| @@ -16,7 +16,6 @@ import TaskSetup from "./TaskSetup"; | |||||
| import StaffAllocation from "./StaffAllocation"; | import StaffAllocation from "./StaffAllocation"; | ||||
| import Milestone from "./Milestone"; | import Milestone from "./Milestone"; | ||||
| import { Task, TaskTemplate } from "@/app/api/tasks"; | import { Task, TaskTemplate } from "@/app/api/tasks"; | ||||
| import AssignIcon from '@mui/icons-material/AssignmentIndOutlined'; | |||||
| import { | import { | ||||
| FieldErrors, | FieldErrors, | ||||
| FormProvider, | FormProvider, | ||||
| @@ -54,6 +53,7 @@ import { | |||||
| } from "../Swal/CustomAlerts"; | } from "../Swal/CustomAlerts"; | ||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import { DELETE_PROJECT } from "@/middleware"; | import { DELETE_PROJECT } from "@/middleware"; | ||||
| import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||||
| export interface Props { | export interface Props { | ||||
| isEditMode: boolean; | isEditMode: boolean; | ||||
| @@ -137,8 +137,8 @@ const CreateProject: React.FC<Props> = ({ | |||||
| title: t("Do you want to submit?"), | title: t("Do you want to submit?"), | ||||
| confirmButtonText: t("Submit"), | confirmButtonText: t("Submit"), | ||||
| successTitle: t("Submit Success"), | successTitle: t("Submit Success"), | ||||
| errorTitle: t("Submit Fail") | |||||
| } | |||||
| errorTitle: t("Submit Fail"), | |||||
| }; | |||||
| const [buttonData, setButtonData] = useState<{ | const [buttonData, setButtonData] = useState<{ | ||||
| buttonName: string; | buttonName: string; | ||||
| title: string; | title: string; | ||||
| @@ -147,26 +147,23 @@ const CreateProject: React.FC<Props> = ({ | |||||
| errorTitle: string; | errorTitle: string; | ||||
| buttonText: string; | buttonText: string; | ||||
| buttonIcon: any; | buttonIcon: any; | ||||
| buttonColor: any | |||||
| }> ({ | |||||
| buttonColor: any; | |||||
| }>({ | |||||
| ...defaultBtn, | ...defaultBtn, | ||||
| buttonText: t("Submit Project"), | buttonText: t("Submit Project"), | ||||
| buttonIcon: <Check />, | buttonIcon: <Check />, | ||||
| buttonColor : "success" | |||||
| }) | |||||
| buttonColor: "success", | |||||
| }); | |||||
| const disableChecking = () => { | const disableChecking = () => { | ||||
| return (loading || | |||||
| formProps.getValues("projectDeleted") === true || | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === | |||||
| "deleted" || | |||||
| // !!formProps.getValues("projectActualStart") && | |||||
| !!( | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === | |||||
| "completed" | |||||
| )) | |||||
| } | |||||
| return ( | |||||
| loading || | |||||
| formProps.getValues("projectDeleted") === true || | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === "deleted" || | |||||
| // !!formProps.getValues("projectActualStart") && | |||||
| !!(formProps.getValues("projectStatus")?.toLowerCase() === "completed") | |||||
| ); | |||||
| }; | |||||
| const handleCancel = () => { | const handleCancel = () => { | ||||
| router.replace("/projects"); | router.replace("/projects"); | ||||
| @@ -200,7 +197,11 @@ const CreateProject: React.FC<Props> = ({ | |||||
| let hasErrors = false; | let hasErrors = false; | ||||
| // Tab - Staff Allocation and Resource | // Tab - Staff Allocation and Resource | ||||
| if (data.totalManhour === null || data.totalManhour <= 0 || Number.isNaN(data.totalManhour)) { | |||||
| if ( | |||||
| data.totalManhour === null || | |||||
| data.totalManhour <= 0 || | |||||
| Number.isNaN(data.totalManhour) | |||||
| ) { | |||||
| formProps.setError("totalManhour", { | formProps.setError("totalManhour", { | ||||
| message: "totalManhour value is not valid", | message: "totalManhour value is not valid", | ||||
| type: "required", | type: "required", | ||||
| @@ -209,7 +210,11 @@ const CreateProject: React.FC<Props> = ({ | |||||
| hasErrors = true; | hasErrors = true; | ||||
| } | } | ||||
| if (data.ratePerManhour === null || data.ratePerManhour <= 0 || Number.isNaN(data.ratePerManhour)) { | |||||
| if ( | |||||
| data.ratePerManhour === null || | |||||
| data.ratePerManhour <= 0 || | |||||
| Number.isNaN(data.ratePerManhour) | |||||
| ) { | |||||
| formProps.setError("ratePerManhour", { | formProps.setError("ratePerManhour", { | ||||
| message: "ratePerManhour value is not valid", | message: "ratePerManhour value is not valid", | ||||
| type: "required", | type: "required", | ||||
| @@ -316,17 +321,28 @@ const CreateProject: React.FC<Props> = ({ | |||||
| data.taskTemplateId = | data.taskTemplateId = | ||||
| data.taskTemplateId === "All" ? undefined : data.taskTemplateId; | data.taskTemplateId === "All" ? undefined : data.taskTemplateId; | ||||
| const response = await saveProject(data); | const response = await saveProject(data); | ||||
| if ( | if ( | ||||
| response.id > 0 && | response.id > 0 && | ||||
| response.message?.toLowerCase() === "success" && | response.message?.toLowerCase() === "success" && | ||||
| response.errorPosition === null | response.errorPosition === null | ||||
| ) { | ) { | ||||
| successDialog(buttonName==="submit"? defaultBtn.successTitle : buttonData.successTitle, t).then(() => { | |||||
| successDialog( | |||||
| buttonName === "submit" | |||||
| ? defaultBtn.successTitle | |||||
| : buttonData.successTitle, | |||||
| t, | |||||
| ).then(() => { | |||||
| router.replace("/projects"); | router.replace("/projects"); | ||||
| }); | }); | ||||
| } else { | } else { | ||||
| errorDialog(response.message ?? (buttonName==="submit"? defaultBtn.errorTitle : buttonData.errorTitle), t).then(() => { | |||||
| errorDialog( | |||||
| response.message ?? | |||||
| (buttonName === "submit" | |||||
| ? defaultBtn.errorTitle | |||||
| : buttonData.errorTitle), | |||||
| t, | |||||
| ).then(() => { | |||||
| if ( | if ( | ||||
| response.errorPosition !== null && | response.errorPosition !== null && | ||||
| response.errorPosition === "projectCode" | response.errorPosition === "projectCode" | ||||
| @@ -337,34 +353,29 @@ const CreateProject: React.FC<Props> = ({ | |||||
| }); | }); | ||||
| setTabIndex(0); | setTabIndex(0); | ||||
| } | } | ||||
| return false; | return false; | ||||
| }); | }); | ||||
| } | } | ||||
| } | |||||
| }; | |||||
| if (buttonName === "complete") { | if (buttonName === "complete") { | ||||
| submitDialogWithWarning( | |||||
| handleSubmit, | |||||
| t, | |||||
| { title: buttonData.title, | |||||
| confirmButtonText: buttonData.confirmButtonText, | |||||
| text: "<b style='color:red'>Completing project will restrict any further changes to the project, are you sure to proceed?</b>" | |||||
| },) | |||||
| submitDialogWithWarning(handleSubmit, t, { | |||||
| title: buttonData.title, | |||||
| confirmButtonText: buttonData.confirmButtonText, | |||||
| text: "<b style='color:red'>Completing project will restrict any further changes to the project, are you sure to proceed?</b>", | |||||
| }); | |||||
| } else if (buttonName === "submit") { | } else if (buttonName === "submit") { | ||||
| submitDialog( | |||||
| handleSubmit, | |||||
| t, | |||||
| { title: defaultBtn.title, confirmButtonText: defaultBtn.confirmButtonText }, | |||||
| ); | |||||
| submitDialog(handleSubmit, t, { | |||||
| title: defaultBtn.title, | |||||
| confirmButtonText: defaultBtn.confirmButtonText, | |||||
| }); | |||||
| } else { | } else { | ||||
| submitDialog( | |||||
| handleSubmit, | |||||
| t, | |||||
| { title: buttonData.title, confirmButtonText: buttonData.confirmButtonText }, | |||||
| ); | |||||
| submitDialog(handleSubmit, t, { | |||||
| title: buttonData.title, | |||||
| confirmButtonText: buttonData.confirmButtonText, | |||||
| }); | |||||
| } | } | ||||
| } catch (e) { | } catch (e) { | ||||
| setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
| } | } | ||||
| @@ -432,77 +443,86 @@ const CreateProject: React.FC<Props> = ({ | |||||
| const errors = formProps.formState.errors; | const errors = formProps.formState.errors; | ||||
| // auto calculate the total project manhour | // auto calculate the total project manhour | ||||
| const expectedProjectFee = formProps.watch("expectedProjectFee") | |||||
| const ratePerManhour = formProps.watch("ratePerManhour") | |||||
| const totalManhour = formProps.watch("totalManhour") | |||||
| const [firstLoaded, setFirstLoaded] = useState(false) | |||||
| const expectedProjectFee = formProps.watch("expectedProjectFee"); | |||||
| const ratePerManhour = formProps.watch("ratePerManhour"); | |||||
| const totalManhour = formProps.watch("totalManhour"); | |||||
| const [firstLoaded, setFirstLoaded] = useState(false); | |||||
| React.useMemo(() => { | React.useMemo(() => { | ||||
| if ((firstLoaded && expectedProjectFee > 0 && ratePerManhour > 0)) { | |||||
| console.log(ratePerManhour, formProps.watch("totalManhour")) | |||||
| formProps.setValue("totalManhour", Math.ceil(expectedProjectFee / ratePerManhour)) | |||||
| if (firstLoaded && expectedProjectFee > 0 && ratePerManhour > 0) { | |||||
| console.log(ratePerManhour, formProps.watch("totalManhour")); | |||||
| formProps.setValue( | |||||
| "totalManhour", | |||||
| Math.ceil(expectedProjectFee / ratePerManhour), | |||||
| ); | |||||
| } else { | } else { | ||||
| setFirstLoaded(true) | |||||
| setFirstLoaded(true); | |||||
| } | } | ||||
| }, [expectedProjectFee, ratePerManhour]) | |||||
| }, [expectedProjectFee, ratePerManhour]); | |||||
| React.useMemo(() => { | React.useMemo(() => { | ||||
| if ((expectedProjectFee > 0 && ratePerManhour > 0) && (totalManhour === null || Number.isNaN(totalManhour) || totalManhour <= 0)) { | |||||
| formProps.setValue("totalManhour", Math.ceil(expectedProjectFee / ratePerManhour)) | |||||
| if ( | |||||
| expectedProjectFee > 0 && | |||||
| ratePerManhour > 0 && | |||||
| (totalManhour === null || Number.isNaN(totalManhour) || totalManhour <= 0) | |||||
| ) { | |||||
| formProps.setValue( | |||||
| "totalManhour", | |||||
| Math.ceil(expectedProjectFee / ratePerManhour), | |||||
| ); | |||||
| } | } | ||||
| }, [totalManhour]) | |||||
| }, [totalManhour]); | |||||
| const updateButtonData = () => { | const updateButtonData = () => { | ||||
| const status = formProps.getValues("projectStatus")?.toLowerCase(); | const status = formProps.getValues("projectStatus")?.toLowerCase(); | ||||
| //Button Parameters// | //Button Parameters// | ||||
| switch (status) { | switch (status) { | ||||
| case "pending to start" : | |||||
| case "pending to start": | |||||
| setButtonData({ | setButtonData({ | ||||
| buttonName: "start", | buttonName: "start", | ||||
| title : t("Do you want to start?"), | |||||
| confirmButtonText : t("Start"), | |||||
| successTitle : t("Start Success"), | |||||
| errorTitle : t("Start Fail"), | |||||
| buttonText : t("Start Project"), | |||||
| buttonIcon : <PlayArrow />, | |||||
| buttonColor: "success" | |||||
| }) | |||||
| title: t("Do you want to start?"), | |||||
| confirmButtonText: t("Start"), | |||||
| successTitle: t("Start Success"), | |||||
| errorTitle: t("Start Fail"), | |||||
| buttonText: t("Start Project"), | |||||
| buttonIcon: <PlayArrow />, | |||||
| buttonColor: "success", | |||||
| }); | |||||
| break; | break; | ||||
| case "on-going" : | |||||
| case "on-going": | |||||
| setButtonData({ | setButtonData({ | ||||
| buttonName: "complete", | buttonName: "complete", | ||||
| title : t("Do you want to complete?"), | |||||
| confirmButtonText : t("Complete"), | |||||
| successTitle : t("Complete Success"), | |||||
| errorTitle : t("Complete Fail"), | |||||
| buttonText : t("Complete Project"), | |||||
| buttonIcon : <DoneIcon />, | |||||
| buttonColor: "info" | |||||
| }) | |||||
| title: t("Do you want to complete?"), | |||||
| confirmButtonText: t("Complete"), | |||||
| successTitle: t("Complete Success"), | |||||
| errorTitle: t("Complete Fail"), | |||||
| buttonText: t("Complete Project"), | |||||
| buttonIcon: <DoneIcon />, | |||||
| buttonColor: "info", | |||||
| }); | |||||
| break; | break; | ||||
| case "completed" : | |||||
| case "completed": | |||||
| setButtonData({ | setButtonData({ | ||||
| buttonName: "reopen", | buttonName: "reopen", | ||||
| title : t("Do you want to reopen?"), | |||||
| confirmButtonText : t("Reopen"), | |||||
| successTitle : t("Reopen Success"), | |||||
| errorTitle : t("Reopen Fail"), | |||||
| buttonText : t("Reopen Project"), | |||||
| buttonIcon : <AutorenewIcon />, | |||||
| buttonColor: "secondary" | |||||
| }) | |||||
| title: t("Do you want to reopen?"), | |||||
| confirmButtonText: t("Reopen"), | |||||
| successTitle: t("Reopen Success"), | |||||
| errorTitle: t("Reopen Fail"), | |||||
| buttonText: t("Reopen Project"), | |||||
| buttonIcon: <AutorenewIcon />, | |||||
| buttonColor: "secondary", | |||||
| }); | |||||
| } | } | ||||
| } | |||||
| }; | |||||
| useEffect(() => { | useEffect(() => { | ||||
| if (!isEditMode) { | if (!isEditMode) { | ||||
| setLoading(false); | setLoading(false); | ||||
| } | |||||
| else if (formProps?.getValues("projectName")) { | |||||
| } else if (formProps?.getValues("projectName")) { | |||||
| setLoading(false); | setLoading(false); | ||||
| updateButtonData(); | updateButtonData(); | ||||
| }}, [formProps] | |||||
| ) | |||||
| } | |||||
| }, [formProps]); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| @@ -512,43 +532,70 @@ const CreateProject: React.FC<Props> = ({ | |||||
| component="form" | component="form" | ||||
| onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | ||||
| > | > | ||||
| {isEditMode && !(formProps.getValues("projectDeleted") === true) && !loading && ( | |||||
| <Grid> | |||||
| <Typography mb={2} variant="h4">{t("Edit Project")}: {`<${defaultInputs?.projectCode}>`}</Typography> | |||||
| <Stack direction="row" gap={1}> | |||||
| {/* {!formProps.getValues("projectActualStart") && ( */} | |||||
| <Button | |||||
| name={buttonData.buttonName} | |||||
| type="submit" | |||||
| variant="contained" | |||||
| startIcon={buttonData.buttonIcon} | |||||
| color={buttonData.buttonColor} | |||||
| > | |||||
| {t(buttonData.buttonText)} | |||||
| </Button> | |||||
| {!( | |||||
| // formProps.getValues("projectActualStart") && | |||||
| // formProps.getValues("projectActualEnd") | |||||
| ( | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === | |||||
| "completed" || | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === | |||||
| "deleted" | |||||
| ) | |||||
| ) && | |||||
| abilities.includes(DELETE_PROJECT) && ( | |||||
| {isEditMode && | |||||
| !(formProps.getValues("projectDeleted") === true) && | |||||
| !loading && ( | |||||
| <Grid> | |||||
| <Typography mb={2} variant="h4"> | |||||
| {t("Edit Project")}: {`<${defaultInputs?.projectCode}>`} | |||||
| </Typography> | |||||
| {(defaultInputs?.projectActualEnd || | |||||
| defaultInputs?.projectActualStart) && ( | |||||
| <Stack mb={2}> | |||||
| {defaultInputs?.projectActualStart && ( | |||||
| <Typography variant="caption"> | |||||
| {t("Project Start Date: {{date}}", { | |||||
| date: dayjs(defaultInputs.projectActualStart).format( | |||||
| OUTPUT_DATE_FORMAT, | |||||
| ), | |||||
| })} | |||||
| </Typography> | |||||
| )} | |||||
| {defaultInputs?.projectActualEnd && ( | |||||
| <Typography variant="caption"> | |||||
| {t("Project End Date: {{date}}", { | |||||
| date: dayjs(defaultInputs.projectActualEnd).format( | |||||
| OUTPUT_DATE_FORMAT, | |||||
| ), | |||||
| })} | |||||
| </Typography> | |||||
| )} | |||||
| </Stack> | |||||
| )} | |||||
| <Stack direction="row" gap={1}> | |||||
| {/* {!formProps.getValues("projectActualStart") && ( */} | |||||
| <Button | <Button | ||||
| variant="outlined" | |||||
| startIcon={<Delete />} | |||||
| color="error" | |||||
| onClick={handleDelete} | |||||
| name={buttonData.buttonName} | |||||
| type="submit" | |||||
| variant="contained" | |||||
| startIcon={buttonData.buttonIcon} | |||||
| color={buttonData.buttonColor} | |||||
| > | > | ||||
| {t("Delete Project")} | |||||
| {t(buttonData.buttonText)} | |||||
| </Button> | </Button> | ||||
| )} | |||||
| </Stack> | |||||
| </Grid> | |||||
| )} | |||||
| {!( | |||||
| // formProps.getValues("projectActualStart") && | |||||
| // formProps.getValues("projectActualEnd") | |||||
| ( | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === | |||||
| "completed" || | |||||
| formProps.getValues("projectStatus")?.toLowerCase() === | |||||
| "deleted" | |||||
| ) | |||||
| ) && | |||||
| abilities.includes(DELETE_PROJECT) && ( | |||||
| <Button | |||||
| variant="outlined" | |||||
| startIcon={<Delete />} | |||||
| color="error" | |||||
| onClick={handleDelete} | |||||
| > | |||||
| {t("Delete Project")} | |||||
| </Button> | |||||
| )} | |||||
| </Stack> | |||||
| </Grid> | |||||
| )} | |||||
| <Tabs | <Tabs | ||||
| value={tabIndex} | value={tabIndex} | ||||
| onChange={handleTabChange} | onChange={handleTabChange} | ||||
| @@ -658,9 +705,7 @@ const CreateProject: React.FC<Props> = ({ | |||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Check />} | startIcon={<Check />} | ||||
| type="submit" | type="submit" | ||||
| disabled={ | |||||
| disableChecking() | |||||
| } | |||||
| disabled={disableChecking()} | |||||
| > | > | ||||
| {isEditMode ? t("Save") : t("Confirm")} | {isEditMode ? t("Save") : t("Confirm")} | ||||
| </Button> | </Button> | ||||