From a6c2f645f16112d9abedd6973a35e11e6a319d0d Mon Sep 17 00:00:00 2001 From: abhi1992002 Date: Fri, 13 Feb 2026 14:21:50 +0530 Subject: [PATCH] feat(library): enhance folder management with validation and UI improvements - Integrated folder existence validation in both `LibraryFolderCreationDialog` and `LibraryFolderEditDialog` to prevent duplicate folder names. - Updated folder name handling to trim whitespace before submission. - Improved UI spacing in folder dialogs for better user experience. - Added API integration for fetching existing folders to support validation. These changes enhance the usability and reliability of folder management in the library, ensuring users can create and edit folders without naming conflicts. --- .../LibraryFolderCreationDialog.tsx | 24 ++++++++++-- .../LibraryFolderEditDialog.tsx | 37 ++++++++++++++++++- 2 files changed, 56 insertions(+), 5 deletions(-) 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, },