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],
);