diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderCreationDialog/LibraryFolderCreationDialog.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderCreationDialog/LibraryFolderCreationDialog.tsx index ec3b41296f..7ac9fca52c 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderCreationDialog/LibraryFolderCreationDialog.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderCreationDialog/LibraryFolderCreationDialog.tsx @@ -19,8 +19,10 @@ import { z } from "zod"; import { EmojiPicker } from "@ferrucc-io/emoji-picker"; import { usePostV2CreateFolder, + useGetV2ListLibraryFolders, getGetV2ListLibraryFoldersQueryKey, } from "@/app/api/__generated__/endpoints/folders/folders"; +import { okData } from "@/app/api/helpers"; import { useToast } from "@/components/molecules/Toast/use-toast"; import { useQueryClient } from "@tanstack/react-query"; @@ -43,6 +45,10 @@ export default function LibraryFolderCreationDialog() { const queryClient = useQueryClient(); const { toast } = useToast(); + const { data: foldersData } = useGetV2ListLibraryFolders(undefined, { + query: { select: okData }, + }); + const { mutate: createFolder, isPending } = usePostV2CreateFolder({ mutation: { onSuccess: () => { @@ -74,9 +80,19 @@ export default function LibraryFolderCreationDialog() { }); function onSubmit(values: z.infer) { + const existingNames = (foldersData?.folders ?? []).map((f) => + f.name.toLowerCase(), + ); + if (existingNames.includes(values.folderName.trim().toLowerCase())) { + form.setError("folderName", { + message: "A folder with this name already exists", + }); + return; + } + createFolder({ data: { - name: values.folderName, + name: values.folderName.trim(), color: values.folderColor, icon: values.folderIcon, }, @@ -110,7 +126,7 @@ export default function LibraryFolderCreationDialog() {
onSubmit(values)} - className="flex flex-col justify-center gap-4 px-1" + className="flex flex-col justify-center px-1 gap-2" > @@ -153,6 +170,7 @@ export default function LibraryFolderCreationDialog() { /> ), }))} + wrapperClassName="!mb-0" renderItem={(option) => (
{option.icon} diff --git a/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderEditDialog/LibraryFolderEditDialog.tsx b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderEditDialog/LibraryFolderEditDialog.tsx index c846d94962..3cfb69ff3d 100644 --- a/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderEditDialog/LibraryFolderEditDialog.tsx +++ b/autogpt_platform/frontend/src/app/(platform)/library/components/LibraryFolderEditDialog/LibraryFolderEditDialog.tsx @@ -20,8 +20,10 @@ import { z } from "zod"; import { EmojiPicker } from "@ferrucc-io/emoji-picker"; import { usePatchV2UpdateFolder, + useGetV2ListLibraryFolders, getGetV2ListLibraryFoldersQueryKey, } from "@/app/api/__generated__/endpoints/folders/folders"; +import { okData } from "@/app/api/helpers"; import { useQueryClient } from "@tanstack/react-query"; import type { LibraryFolder } from "@/app/api/__generated__/models/libraryFolder"; import type { getV2ListLibraryFoldersResponseSuccess } from "@/app/api/__generated__/endpoints/folders/folders"; @@ -50,6 +52,10 @@ export function LibraryFolderEditDialog({ folder, isOpen, setIsOpen }: Props) { const queryClient = useQueryClient(); const { toast } = useToast(); + const { data: foldersData } = useGetV2ListLibraryFolders(undefined, { + query: { select: okData }, + }); + const form = useForm>({ resolver: zodResolver(editFolderSchema), defaultValues: { @@ -107,12 +113,27 @@ export function LibraryFolderEditDialog({ folder, isOpen, setIsOpen }: Props) { return { previousData }; }, - onError: (_error, _variables, context) => { + onError: ( + error: { detail?: string; response?: { detail?: string } }, + _variables, + context, + ) => { if (context?.previousData) { for (const [queryKey, data] of context.previousData) { queryClient.setQueryData(queryKey, data); } } + const detail = + error.detail ?? error.response?.detail ?? ""; + if ( + typeof detail === "string" && + detail.toLowerCase().includes("already exists") + ) { + form.setError("folderName", { + message: "A folder with this name already exists", + }); + return; + } toast({ title: "Error", description: "Failed to update folder. Please try again.", @@ -135,10 +156,22 @@ export function LibraryFolderEditDialog({ folder, isOpen, setIsOpen }: Props) { }); function onSubmit(values: z.infer) { + const trimmedName = values.folderName.trim(); + const existingNames = (foldersData?.folders ?? []) + .filter((f) => f.id !== folder.id) + .map((f) => f.name.toLowerCase()); + + if (existingNames.includes(trimmedName.toLowerCase())) { + form.setError("folderName", { + message: "A folder with this name already exists", + }); + return; + } + updateFolder({ folderId: folder.id, data: { - name: values.folderName, + name: trimmedName, color: values.folderColor, icon: values.folderIcon, },