diff --git a/src/app/(main)/tasks/edit/not-found.tsx b/src/app/(main)/tasks/edit/not-found.tsx new file mode 100644 index 0000000..b99e85e --- /dev/null +++ b/src/app/(main)/tasks/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("tasks", "common"); + + return ( + + {t("Not Found")} + {t("The task template was not found!")} + + {t("Return to all task templates")} + + + ); +} diff --git a/src/app/(main)/tasks/edit/page.tsx b/src/app/(main)/tasks/edit/page.tsx index 2b2c02c..4872daf 100644 --- a/src/app/(main)/tasks/edit/page.tsx +++ b/src/app/(main)/tasks/edit/page.tsx @@ -1,23 +1,44 @@ -import { preloadAllTasks } from "@/app/api/tasks"; +import { fetchTaskTemplate, preloadAllTasks } from "@/app/api/tasks"; import CreateTaskTemplate from "@/components/CreateTaskTemplate"; import { getServerI18n } from "@/i18n"; import Typography from "@mui/material/Typography"; import { Metadata } from "next"; import { I18nProvider } from "@/i18n"; +import { ServerFetchError } from "@/app/utils/fetchUtil"; +import { isArray } from "lodash"; +import { notFound } from "next/navigation"; export const metadata: Metadata = { title: "Edit Task Template", }; -const TaskTemplates: React.FC = async () => { +interface Props { + searchParams: { [key: string]: string | string[] | undefined }; +} + +const TaskTemplates: React.FC = async ({ searchParams }) => { const { t } = await getServerI18n("tasks"); + const taskTemplateId = searchParams["id"]; + + if (!taskTemplateId || isArray(taskTemplateId)) { + notFound(); + } + preloadAllTasks(); + try { + await fetchTaskTemplate(taskTemplateId); + } catch (e) { + if (e instanceof ServerFetchError && e.response?.status === 404) { + notFound(); + } + } + return ( <> {t("Edit Task Template")} - + ); diff --git a/src/app/api/tasks/actions.ts b/src/app/api/tasks/actions.ts index 2c043be..3414bf6 100644 --- a/src/app/api/tasks/actions.ts +++ b/src/app/api/tasks/actions.ts @@ -4,13 +4,26 @@ import { serverFetchJson, serverFetchWithNoContent } from "@/app/utils/fetchUtil import { BASE_API_URL } from "@/config/api"; import { TaskTemplate } from "."; import { revalidateTag } from "next/cache"; +import { ManhourAllocation } from "@/app/api/projects/actions"; +import { Task, TaskGroup } from '@/app/api/tasks'; export interface NewTaskTemplateFormInputs { + + // task template code: string; name: string; taskIds: number[]; id: number | null; + + // resource allocation template + manhourPercentageByGrade: ManhourAllocation; + taskGroups: { + [taskGroup: TaskGroup["id"]]: { + taskIds: Task["id"][]; + percentAllocation: number; + }; + }; } export const saveTaskTemplate = async (data: NewTaskTemplateFormInputs) => { diff --git a/src/app/api/tasks/index.ts b/src/app/api/tasks/index.ts index f701cca..ba993f0 100644 --- a/src/app/api/tasks/index.ts +++ b/src/app/api/tasks/index.ts @@ -39,3 +39,15 @@ export const preloadAllTasks = () => { export const fetchAllTasks = cache(async () => { return serverFetchJson(`${BASE_API_URL}/tasks`); }); + +export const fetchTaskTemplate = cache(async (id: string) => { + const taskTemplate = await serverFetchJson( + `${BASE_API_URL}/tasks/templatesDetails/${id}`, + { + method: "GET", + headers: { "Content-Type": "application/json" }, + }, + ); + + return taskTemplate; +}); diff --git a/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx b/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx index f7d5912..856f66e 100644 --- a/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx +++ b/src/components/CreateTaskTemplate/CreateTaskTemplate.tsx @@ -13,7 +13,7 @@ import Close from "@mui/icons-material/Close"; import { useRouter, useSearchParams } from "next/navigation"; import React from "react"; import Stack from "@mui/material/Stack"; -import { Task } from "@/app/api/tasks"; +import { Task, TaskTemplate } from "@/app/api/tasks"; import { NewTaskTemplateFormInputs, fetchTaskTemplate, @@ -24,9 +24,10 @@ import { errorDialog, submitDialog, successDialog } from "../Swal/CustomAlerts"; interface Props { tasks: Task[]; + defaultInputs?: TaskTemplate; } -const CreateTaskTemplate: React.FC = ({ tasks }) => { +const CreateTaskTemplate: React.FC = ({ tasks, defaultInputs }) => { const { t } = useTranslation(); const searchParams = useSearchParams() @@ -54,45 +55,45 @@ const CreateTaskTemplate: React.FC = ({ tasks }) => { watch, resetField, formState: { errors, isSubmitting }, - } = useForm({ defaultValues: { taskIds: [] } }); + } = useForm({ defaultValues: defaultInputs }); const currentTaskIds = watch("taskIds"); const selectedItems = React.useMemo(() => { return items.filter((item) => currentTaskIds.includes(item.id)); }, [currentTaskIds, items]); - const [refTaskTemplate, setRefTaskTemplate] = React.useState() - const id = searchParams.get('id') - - const fetchCurrentTaskTemplate = async () => { - try { - const taskTemplate = await fetchTaskTemplate(parseInt(id!!)) - - const defaultValues = { - id: parseInt(id!!), - code: taskTemplate.code ?? null, - name: taskTemplate.name ?? null, - taskIds: taskTemplate.tasks.map(task => task.id) ?? [], - } - - setRefTaskTemplate(defaultValues) - } catch (e) { - console.log(e) - } - } - - React.useLayoutEffect(() => { - if (id !== null && parseInt(id) > 0) fetchCurrentTaskTemplate() - }, [id]) - - React.useEffect(() => { - if (refTaskTemplate) { - setValue("taskIds", refTaskTemplate.taskIds) - resetField("code", { defaultValue: refTaskTemplate.code }) - resetField("name", { defaultValue: refTaskTemplate.name }) - setValue("id", refTaskTemplate.id) - } - }, [refTaskTemplate]) + // const [refTaskTemplate, setRefTaskTemplate] = React.useState() + // const id = searchParams.get('id') + + // const fetchCurrentTaskTemplate = async () => { + // try { + // const taskTemplate = await fetchTaskTemplate(parseInt(id!!)) + + // const defaultValues = { + // id: parseInt(id!!), + // code: taskTemplate.code ?? null, + // name: taskTemplate.name ?? null, + // taskIds: taskTemplate.tasks.map(task => task.id) ?? [], + // } + + // setRefTaskTemplate(defaultValues) + // } catch (e) { + // console.log(e) + // } + // } + + // React.useLayoutEffect(() => { + // if (id !== null && parseInt(id) > 0) fetchCurrentTaskTemplate() + // }, [id]) + + // React.useEffect(() => { + // if (refTaskTemplate) { + // setValue("taskIds", refTaskTemplate.taskIds) + // resetField("code", { defaultValue: refTaskTemplate.code }) + // resetField("name", { defaultValue: refTaskTemplate.name }) + // setValue("id", refTaskTemplate.id) + // } + // }, [refTaskTemplate]) const onSubmit: SubmitHandler = React.useCallback( async (data) => { @@ -120,9 +121,9 @@ const CreateTaskTemplate: React.FC = ({ tasks }) => { return ( <> - { - (id === null || refTaskTemplate !== undefined) && + + {/* Task List Setup */} {t("Task List Setup")} = ({ tasks }) => { allItemsLabel={t("Task Pool")} selectedItemsLabel={t("Task List Template")} /> + {/* Task List Setup */} + {/* Task List Setup */} + { @@ -188,7 +192,7 @@ const CreateTaskTemplate: React.FC = ({ tasks }) => { {t("Confirm")} - } + ); }; diff --git a/src/components/CreateTaskTemplate/CreateTaskTemplateWrapper.tsx b/src/components/CreateTaskTemplate/CreateTaskTemplateWrapper.tsx index 77888a2..50a131b 100644 --- a/src/components/CreateTaskTemplate/CreateTaskTemplateWrapper.tsx +++ b/src/components/CreateTaskTemplate/CreateTaskTemplateWrapper.tsx @@ -1,11 +1,18 @@ import React from "react"; import CreateTaskTemplate from "./CreateTaskTemplate"; -import { fetchAllTasks } from "@/app/api/tasks"; +import { fetchAllTasks, fetchTaskTemplate } from "@/app/api/tasks"; -const CreateTaskTemplateWrapper: React.FC = async () => { - const tasks = await fetchAllTasks(); +interface Props { + taskTemplateId?: string; +} - return ; +const CreateTaskTemplateWrapper: React.FC = async (props) => { + const [tasks] = await Promise.all([ + fetchAllTasks(), + ]); + + const taskTemplateInfo = props.taskTemplateId ? await fetchTaskTemplate(props.taskTemplateId) : undefined + return ; }; export default CreateTaskTemplateWrapper; diff --git a/src/components/Swal/CustomAlerts.js b/src/components/Swal/CustomAlerts.js index ad1aec6..5780460 100644 --- a/src/components/Swal/CustomAlerts.js +++ b/src/components/Swal/CustomAlerts.js @@ -51,13 +51,17 @@ export const warningDialog = (text, t) => { }) } -export const submitDialog = async (confirmAction, t, {...props}) => { +export const submitDialog = async (confirmAction, t, {...props} = { + title: null, + confirmButtonText: null +}) => { + // console.log(props) // const { t } = useTranslation("common") const result = await Swal.fire({ icon: "question", - title: props.title ?? t("Do you want to submit?"), + title: props?.title ?? t("Do you want to submit?"), cancelButtonText: t("Cancel"), - confirmButtonText: props.confirmButtonText ?? t("Submit"), + confirmButtonText: props?.confirmButtonText ?? t("Submit"), showCancelButton: true, showConfirmButton: true, customClass: {