From 966d7d0c65f37a801cd70b1aeb4d5342bb647356 Mon Sep 17 00:00:00 2001 From: Wayne Date: Wed, 1 May 2024 23:30:37 +0900 Subject: [PATCH] Add not-found page for edit project --- src/app/(main)/projects/edit/not-found.tsx | 17 ++++++++++++ .../projects/edit/{[projectId] => }/page.tsx | 26 ++++++++++++++----- src/app/utils/fetchUtil.ts | 15 ++++++++++- src/components/Breadcrumb/Breadcrumb.tsx | 1 + .../CreateProject/CreateProject.tsx | 22 +++++++++++----- .../ProjectSearch/ProjectSearch.tsx | 2 +- 6 files changed, 68 insertions(+), 15 deletions(-) create mode 100644 src/app/(main)/projects/edit/not-found.tsx rename src/app/(main)/projects/edit/{[projectId] => }/page.tsx (69%) diff --git a/src/app/(main)/projects/edit/not-found.tsx b/src/app/(main)/projects/edit/not-found.tsx new file mode 100644 index 0000000..14e0e6d --- /dev/null +++ b/src/app/(main)/projects/edit/not-found.tsx @@ -0,0 +1,17 @@ +import { getServerI18n } from "@/i18n"; +import { Stack, Typography, Link } from "@mui/material"; +import NextLink from "next/link"; + +export default async function NotFound() { + const { t } = await getServerI18n("projects", "common"); + + return ( + + {t("Not Found")} + {t("The project was not found!")} + + {t("Return to all projects")} + + + ); +} diff --git a/src/app/(main)/projects/edit/[projectId]/page.tsx b/src/app/(main)/projects/edit/page.tsx similarity index 69% rename from src/app/(main)/projects/edit/[projectId]/page.tsx rename to src/app/(main)/projects/edit/page.tsx index dbad026..78e0ed1 100644 --- a/src/app/(main)/projects/edit/[projectId]/page.tsx +++ b/src/app/(main)/projects/edit/page.tsx @@ -12,23 +12,30 @@ import { } from "@/app/api/projects"; import { preloadStaff, preloadTeamLeads } from "@/app/api/staff"; import { fetchAllTasks, fetchTaskTemplates } from "@/app/api/tasks"; +import { ServerFetchError } from "@/app/utils/fetchUtil"; import CreateProject from "@/components/CreateProject"; import { I18nProvider, getServerI18n } from "@/i18n"; import Typography from "@mui/material/Typography"; +import { isArray } from "lodash"; import { Metadata } from "next"; +import { notFound } from "next/navigation"; interface Props { - params: { - projectId: string; - }; + searchParams: { [key: string]: string | string[] | undefined }; } export const metadata: Metadata = { title: "Edit Project", }; -const Projects: React.FC = async ({ params }) => { +const Projects: React.FC = async ({ searchParams }) => { const { t } = await getServerI18n("projects"); + // Assume projectId is string here + const projectId = searchParams["id"]; + + if (!projectId || isArray(projectId)) { + notFound(); + } // Preload necessary dependencies fetchAllTasks(); @@ -46,14 +53,19 @@ const Projects: React.FC = async ({ params }) => { preloadTeamLeads(); preloadStaff(); - // TODO: Handle not found - const fetchedProject = await fetchProjectDetails(params.projectId); + try { + await fetchProjectDetails(projectId); + } catch (e) { + if (e instanceof ServerFetchError && e.response?.status === 404) { + notFound(); + } + } return ( <> {t("Edit Project")} - + ); diff --git a/src/app/utils/fetchUtil.ts b/src/app/utils/fetchUtil.ts index 5060991..a519164 100644 --- a/src/app/utils/fetchUtil.ts +++ b/src/app/utils/fetchUtil.ts @@ -3,6 +3,16 @@ import { getServerSession } from "next-auth"; import { headers } from "next/headers"; import { redirect } from "next/navigation"; +export class ServerFetchError extends Error { + public readonly response: Response | undefined; + constructor(message?: string, response?: Response) { + super(message); + this.response = response; + + Object.setPrototypeOf(this, ServerFetchError.prototype); + } +} + export const serverFetch: typeof fetch = async (input, init) => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const session = await getServerSession(authOptions); @@ -37,7 +47,10 @@ export async function serverFetchJson(...args: FetchParams) { signOutUser(); default: console.error(await response.text()); - throw Error("Something went wrong fetching data in server."); + throw new ServerFetchError( + "Something went wrong fetching data in server.", + response, + ); } } } diff --git a/src/components/Breadcrumb/Breadcrumb.tsx b/src/components/Breadcrumb/Breadcrumb.tsx index 3d6123a..a94670a 100644 --- a/src/components/Breadcrumb/Breadcrumb.tsx +++ b/src/components/Breadcrumb/Breadcrumb.tsx @@ -12,6 +12,7 @@ const pathToLabelMap: { [path: string]: string } = { "/home": "User Workspace", "/projects": "Projects", "/projects/create": "Create Project", + "/projects/edit": "Edit Project", "/tasks": "Task Template", "/tasks/create": "Create Task Template", "/staffReimbursement": "Staff Reimbursement", diff --git a/src/components/CreateProject/CreateProject.tsx b/src/components/CreateProject/CreateProject.tsx index 175d712..3a91a8f 100644 --- a/src/components/CreateProject/CreateProject.tsx +++ b/src/components/CreateProject/CreateProject.tsx @@ -36,6 +36,7 @@ import { StaffResult } from "@/app/api/staff"; import { Typography } from "@mui/material"; import { Grade } from "@/app/api/grades"; import { Customer, Subsidiary } from "@/app/api/customer"; +import { isEmpty } from "lodash"; export interface Props { isEditMode: boolean; @@ -108,13 +109,17 @@ const CreateProject: React.FC = ({ async (data) => { try { setServerError(""); - await saveProject(data); + if (isEditMode) { + console.log("edit project", data); + } else { + await saveProject(data); + } router.replace("/projects"); } catch (e) { setServerError(t("An error has occurred. Please try again later.")); } }, - [router, t], + [router, t, isEditMode], ); const onSubmitError = useCallback>( @@ -132,14 +137,19 @@ const CreateProject: React.FC = ({ ); const formProps = useForm({ - defaultValues: defaultInputs ?? { + defaultValues: { taskGroups: {}, allocatedStaffIds: [], milestones: {}, totalManhour: 0, - manhourPercentageByGrade: grades.reduce((acc, grade) => { - return { ...acc, [grade.id]: 1 / grades.length }; - }, {}), + ...defaultInputs, + + // manhourPercentageByGrade should have a sensible default + manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade) + ? grades.reduce((acc, grade) => { + return { ...acc, [grade.id]: 1 / grades.length }; + }, {}) + : defaultInputs.manhourPercentageByGrade, }, }); diff --git a/src/components/ProjectSearch/ProjectSearch.tsx b/src/components/ProjectSearch/ProjectSearch.tsx index d33ce14..1937fe2 100644 --- a/src/components/ProjectSearch/ProjectSearch.tsx +++ b/src/components/ProjectSearch/ProjectSearch.tsx @@ -55,7 +55,7 @@ const ProjectSearch: React.FC = ({ projects, projectCategories }) => { const onProjectClick = useCallback( (project: ProjectResult) => { - router.push(`/projects/edit/${project.id}`); + router.push(`/projects/edit?id=${project.id}`); }, [router], );