mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-13 16:25:05 -05:00
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.
This commit is contained in:
@@ -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<typeof libraryFolderCreationFormSchema>) {
|
||||
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() {
|
||||
<Form
|
||||
form={form}
|
||||
onSubmit={(values) => onSubmit(values)}
|
||||
className="flex flex-col justify-center gap-4 px-1"
|
||||
className="flex flex-col justify-center px-1 gap-2"
|
||||
>
|
||||
<FormField
|
||||
control={form.control}
|
||||
@@ -123,7 +139,8 @@ export default function LibraryFolderCreationDialog() {
|
||||
id={field.name}
|
||||
label="Folder name"
|
||||
placeholder="Enter folder name"
|
||||
className="w-full"
|
||||
className="w-full !mb-0"
|
||||
wrapperClassName="!mb-0"
|
||||
/>
|
||||
</FormControl>
|
||||
<FormMessage />
|
||||
@@ -153,6 +170,7 @@ export default function LibraryFolderCreationDialog() {
|
||||
/>
|
||||
),
|
||||
}))}
|
||||
wrapperClassName="!mb-0"
|
||||
renderItem={(option) => (
|
||||
<div className="flex items-center gap-2">
|
||||
{option.icon}
|
||||
|
||||
@@ -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<z.infer<typeof editFolderSchema>>({
|
||||
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<typeof editFolderSchema>) {
|
||||
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,
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user