diff --git a/frontend/app/projects/[projectId]/folders/folder-dialog.tsx b/frontend/app/projects/[projectId]/folders/FolderDialog.tsx similarity index 98% rename from frontend/app/projects/[projectId]/folders/folder-dialog.tsx rename to frontend/app/projects/[projectId]/folders/FolderDialog.tsx index 2483775..9a8e0f7 100644 --- a/frontend/app/projects/[projectId]/folders/folder-dialog.tsx +++ b/frontend/app/projects/[projectId]/folders/FolderDialog.tsx @@ -20,7 +20,7 @@ type Props = { onSubmit: (name: string, detail: string) => void; }; -export function FolderDialog({ +export default function FolderDialog({ isOpen, editingFolder, onCancel, diff --git a/frontend/app/projects/[projectId]/folders/folder-edit-menu.tsx b/frontend/app/projects/[projectId]/folders/FolderEditMenu.tsx similarity index 100% rename from frontend/app/projects/[projectId]/folders/folder-edit-menu.tsx rename to frontend/app/projects/[projectId]/folders/FolderEditMenu.tsx diff --git a/frontend/app/projects/[projectId]/folders/FoldersPane.tsx b/frontend/app/projects/[projectId]/folders/FoldersPane.tsx new file mode 100644 index 0000000..3a7e8c6 --- /dev/null +++ b/frontend/app/projects/[projectId]/folders/FoldersPane.tsx @@ -0,0 +1,150 @@ +"use client"; +import React from "react"; +import { FolderType } from "@/types/folder"; +import { useEffect, useState } from "react"; +import { Button, Listbox, ListboxItem } from "@nextui-org/react"; +import { Folder, Plus } from "lucide-react"; +import { useRouter, usePathname } from "next/navigation"; +import useGetCurrentIds from "@/utils/useGetCurrentIds"; +import FolderDialog from "./FolderDialog"; +import FolderEditMenu from "./FolderEditMenu"; + +import { + fetchFolders, + createFolder, + updateFolder, + deleteFolder, +} from "./foldersControl"; + +type Props = { + projectId: string; +}; + +export default function FoldersPane({ projectId }: Props) { + const router = useRouter(); + const pathname = usePathname(); + const [folders, setFolders] = useState([]); + const [selectedFolder, setSelectedFolder] = useState({}); + + const { folderId } = useGetCurrentIds(); + + const [isFolderDialogOpen, setIsFolderDialogOpen] = useState(false); + const [editingFolder, setEditingProject] = useState(null); + + const openDialogForCreate = () => { + setIsFolderDialogOpen(true); + setEditingProject(null); + }; + + const closeDialog = () => { + setIsFolderDialogOpen(false); + setEditingProject(null); + }; + + const onSubmit = async (name: string, detail: string) => { + if (editingFolder) { + const updatedProject = await updateFolder( + editingFolder.id, + name, + detail, + projectId, + null + ); + const updatedProjects = folders.map((project) => + project.id === updatedProject.id ? updatedProject : project + ); + setFolders(updatedProjects); + } else { + const newProject = await createFolder(name, detail, projectId, null); + setFolders([...folders, newProject]); + } + closeDialog(); + }; + + const onEditClick = (folder: FolderType) => { + setEditingProject(folder); + setIsFolderDialogOpen(true); + }; + + const onDeleteClick = async (folderId: number) => { + await deleteFolder(folderId); + router.push(`/projects/${projectId}/folders`); + }; + + useEffect(() => { + async function fetchDataEffect() { + try { + const data = await fetchFolders(projectId); + setFolders(data); + + const selectedFolderFromUrl = data.find( + (folder) => folder.id === folderId + ); + setSelectedFolder(selectedFolderFromUrl); + + // Redirect to the smallest folder ID page if the path is "projects/[projectId]/folders + if (pathname === `/projects/${projectId}/folders`) { + const smallestFolderId = Math.min(...data.map((folder) => folder.id)); + router.push( + `/projects/${projectId}/folders/${smallestFolderId}/cases` + ); + } + } catch (error) { + console.error("Error in effect:", error.message); + } + } + + fetchDataEffect(); + }, [folderId]); + + const baseClass = "px-3 py-2 rounded-none"; + const selectedClass = `${baseClass} bg-neutral-200 dark:bg-neutral-700`; + + return ( + <> +
+ + + {folders.map((folder, index) => ( + + router.push(`/projects/${projectId}/folders/${folder.id}/cases`) + } + startContent={} + className={ + selectedFolder && folder.id === selectedFolder.id + ? selectedClass + : baseClass + } + endContent={ + + } + > + {folder.name} + + ))} + +
+ + + + ); +} diff --git a/frontend/app/projects/[projectId]/folders/foldersControl.ts b/frontend/app/projects/[projectId]/folders/foldersControl.ts new file mode 100644 index 0000000..603ecf6 --- /dev/null +++ b/frontend/app/projects/[projectId]/folders/foldersControl.ts @@ -0,0 +1,131 @@ +import Config from "@/config/config"; +const apiServer = Config.apiServer; + +/** + * fetch folder records + */ +async function fetchFolders(projectId: string) { + try { + const url = `${apiServer}/folders?projectId=${projectId}`; + const response = await fetch(url, { + method: "GET", + headers: { + "Content-Type": "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + + const data = await response.json(); + return data; + } catch (error) { + console.error("Error fetching data:", error.message); + } +} + +/** + * Create project + */ +async function createFolder( + name: string, + detail: string, + projectId: strting, + parentFolderId: number +) { + const newFolderData = { + name: name, + detail: detail, + projectId: projectId, + parentFolderId: parentFolderId, + }; + + const fetchOptions = { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(newFolderData), + }; + + const url = `${apiServer}/folders`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error creating new project:", error); + throw error; + } +} + +/** + * Update folder + */ +async function updateFolder( + folderId: number, + name: string, + detail: string, + projectId: string, + parentFolderId: number +) { + const updateFolderData = { + name: name, + detail: detail, + projectId: projectId, + parentFolderId: parentFolderId, + }; + + const fetchOptions = { + method: "PUT", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify(updateFolderData), + }; + + const url = `${apiServer}/folders/${folderId}`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + const data = await response.json(); + return data; + } catch (error) { + console.error("Error updating project:", error); + throw error; + } +} + +/** + * Delete folder + */ +async function deleteFolder(folderId: number) { + const fetchOptions = { + method: "DELETE", + headers: { + "Content-Type": "application/json", + }, + }; + + const url = `${apiServer}/folders/${folderId}`; + + try { + const response = await fetch(url, fetchOptions); + if (!response.ok) { + throw new Error(`HTTP error! Status: ${response.status}`); + } + } catch (error) { + console.error("Error deleting project:", error); + throw error; + } +} + +export { fetchFolders, createFolder, updateFolder, deleteFolder }; diff --git a/frontend/app/projects/[projectId]/folders/layout.tsx b/frontend/app/projects/[projectId]/folders/layout.tsx index bb95eaa..623471c 100644 --- a/frontend/app/projects/[projectId]/folders/layout.tsx +++ b/frontend/app/projects/[projectId]/folders/layout.tsx @@ -1,13 +1,8 @@ "use client"; import { useEffect, useState } from "react"; -import Config from "@/config/config"; -const apiServer = Config.apiServer; -import { Button, Listbox, ListboxItem } from "@nextui-org/react"; -import { Folder, Plus } from "lucide-react"; -import { FolderDialog } from "./folder-dialog"; -import FolderEditMenu from "./folder-edit-menu"; import { useRouter, usePathname } from "next/navigation"; import useGetCurrentIds from "@/utils/useGetCurrentIds"; +import FoldersPane from "./FoldersPane"; export type FolderType = { id: number; @@ -19,140 +14,12 @@ export type FolderType = { updatedAt: string; }; -/** - * fetch folder records - */ -async function fetchFolders(projectId: string) { - // console.log("fetch folders", url) - try { - const url = `${apiServer}/folders?projectId=${projectId}`; - const response = await fetch(url, { - method: "GET", - headers: { - "Content-Type": "application/json", - }, - }); - - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - - const data = await response.json(); - return data; - } catch (error) { - console.error("Error fetching data:", error.message); - } -} - -/** - * Create project - */ -async function createFolder( - name: string, - detail: string, - projectId: number, - parentFolderId: number -) { - const newFolderData = { - name: name, - detail: detail, - projectId: projectId, - parentFolderId: parentFolderId, - }; - - const fetchOptions = { - method: "POST", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(newFolderData), - }; - - const url = `${apiServer}/folders`; - - try { - const response = await fetch(url, fetchOptions); - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - const data = await response.json(); - return data; - } catch (error) { - console.error("Error creating new project:", error); - throw error; - } -} - -/** - * Update folder - */ -async function updateFolder( - folderId: number, - name: string, - detail: string, - projectId: number, - parentFolderId: number -) { - const updateFolderData = { - name: name, - detail: detail, - projectId: projectId, - parentFolderId: parentFolderId, - }; - - const fetchOptions = { - method: "PUT", - headers: { - "Content-Type": "application/json", - }, - body: JSON.stringify(updateFolderData), - }; - - const url = `${apiServer}/folders/${folderId}`; - - try { - const response = await fetch(url, fetchOptions); - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - const data = await response.json(); - return data; - } catch (error) { - console.error("Error updating project:", error); - throw error; - } -} - -/** - * Delete folder - */ -async function deleteFolder(folderId: number) { - const fetchOptions = { - method: "DELETE", - headers: { - "Content-Type": "application/json", - }, - }; - - const url = `${apiServer}/folders/${folderId}`; - - try { - const response = await fetch(url, fetchOptions); - if (!response.ok) { - throw new Error(`HTTP error! Status: ${response.status}`); - } - } catch (error) { - console.error("Error deleting project:", error); - throw error; - } -} - export default function FoldersLayout({ children, params, }: { children: React.ReactNode; - params: { projectId: number }; + params: { projectId: string }; }) { const router = useRouter(); const pathname = usePathname(); @@ -235,57 +102,10 @@ export default function FoldersLayout({ fetchDataEffect(); }, [folderId]); - const baseClass = "px-3 py-2 rounded-none"; - const selectedClass = `${baseClass} bg-neutral-200 dark:bg-neutral-700`; - return (
-
- - - {folders.map((folder, index) => ( - - router.push( - `/projects/${params.projectId}/folders/${folder.id}/cases` - ) - } - startContent={} - className={ - selectedFolder && folder.id === selectedFolder.id - ? selectedClass - : baseClass - } - endContent={ - - } - > - {folder.name} - - ))} - -
+
{children}
- -
); } diff --git a/frontend/types/folder.ts b/frontend/types/folder.ts new file mode 100644 index 0000000..253eb5e --- /dev/null +++ b/frontend/types/folder.ts @@ -0,0 +1,9 @@ +export type FolderType = { + id: number; + name: string; + detail: string; + projectId: number; + parentFolderId: number | null; + createdAt: string; + updatedAt: string; +}; \ No newline at end of file