您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 

798 行
24 KiB

  1. "use client";
  2. import AutorenewIcon from "@mui/icons-material/Autorenew";
  3. import DoneIcon from "@mui/icons-material/Done";
  4. import Check from "@mui/icons-material/Check";
  5. import Close from "@mui/icons-material/Close";
  6. import Button, { ButtonProps } from "@mui/material/Button";
  7. import Stack from "@mui/material/Stack";
  8. import Tab from "@mui/material/Tab";
  9. import Tabs, { TabsProps } from "@mui/material/Tabs";
  10. import { useRouter } from "next/navigation";
  11. import React, {
  12. useCallback,
  13. useEffect,
  14. useMemo,
  15. useRef,
  16. useState,
  17. } from "react";
  18. import { useTranslation } from "react-i18next";
  19. import ProjectClientDetails from "./ProjectClientDetails";
  20. import TaskSetup from "./TaskSetup";
  21. import StaffAllocation from "./StaffAllocation";
  22. import Milestone from "./Milestone";
  23. import { Task, TaskTemplate } from "@/app/api/tasks";
  24. import {
  25. FieldErrors,
  26. FormProvider,
  27. SubmitErrorHandler,
  28. SubmitHandler,
  29. useForm,
  30. } from "react-hook-form";
  31. import {
  32. CreateProjectInputs,
  33. deleteProject,
  34. saveProject,
  35. } from "@/app/api/projects/actions";
  36. import { Delete, EditNote, Error, PlayArrow } from "@mui/icons-material";
  37. import {
  38. BuildingType,
  39. ContractType,
  40. FundingType,
  41. LocationType,
  42. MainProject,
  43. ProjectCategory,
  44. ServiceType,
  45. WorkNature,
  46. } from "@/app/api/projects";
  47. import { StaffResult } from "@/app/api/staff";
  48. import { Box, Grid, Typography } from "@mui/material";
  49. import { Grade } from "@/app/api/grades";
  50. import { Customer, CustomerType, Subsidiary } from "@/app/api/customer";
  51. import { isEmpty } from "lodash";
  52. import {
  53. deleteDialog,
  54. errorDialog,
  55. submitDialog,
  56. submitDialogWithWarning,
  57. successDialog,
  58. } from "../Swal/CustomAlerts";
  59. import dayjs from "dayjs";
  60. import { DELETE_PROJECT } from "@/middleware";
  61. import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  62. import { deleteDraft, loadDraft, saveToLocalStorage } from "@/app/utils/draftUtils";
  63. export interface Props {
  64. isEditMode: boolean;
  65. isCopyMode: boolean;
  66. draftId?: number;
  67. isSubProject: boolean;
  68. mainProjects?: MainProject[];
  69. defaultInputs?: CreateProjectInputs;
  70. allTasks: Task[];
  71. projectCategories: ProjectCategory[];
  72. taskTemplates: TaskTemplate[];
  73. teamLeads: StaffResult[];
  74. allCustomers: Customer[];
  75. allSubsidiaries: Subsidiary[];
  76. fundingTypes: FundingType[];
  77. serviceTypes: ServiceType[];
  78. contractTypes: ContractType[];
  79. locationTypes: LocationType[];
  80. buildingTypes: BuildingType[];
  81. workNatures: WorkNature[];
  82. allStaffs: StaffResult[];
  83. customerTypes: CustomerType[];
  84. grades: Grade[];
  85. abilities: string[];
  86. }
  87. const hasErrorsInTab = (
  88. tabIndex: number,
  89. errors: FieldErrors<CreateProjectInputs>,
  90. ) => {
  91. switch (tabIndex) {
  92. case 0:
  93. return (
  94. errors.projectName ||
  95. errors.projectDescription ||
  96. errors.clientId ||
  97. errors.projectCode ||
  98. errors.projectPlanStart ||
  99. errors.projectPlanEnd
  100. );
  101. case 2:
  102. return (
  103. errors.totalManhour ||
  104. errors.manhourPercentageByGrade ||
  105. errors.taskGroups ||
  106. errors.ratePerManhour
  107. );
  108. case 3:
  109. return errors.milestones;
  110. default:
  111. false;
  112. }
  113. };
  114. const CreateProject: React.FC<Props> = ({
  115. isEditMode,
  116. isCopyMode,
  117. draftId,
  118. isSubProject,
  119. mainProjects,
  120. defaultInputs,
  121. allTasks,
  122. projectCategories,
  123. taskTemplates,
  124. teamLeads,
  125. grades,
  126. allCustomers,
  127. allSubsidiaries,
  128. contractTypes,
  129. fundingTypes,
  130. locationTypes,
  131. serviceTypes,
  132. buildingTypes,
  133. workNatures,
  134. allStaffs,
  135. customerTypes,
  136. abilities,
  137. }) => {
  138. const [serverError, setServerError] = useState("");
  139. const [tabIndex, setTabIndex] = useState(0);
  140. const { t } = useTranslation();
  141. const router = useRouter();
  142. const formProps = useForm<CreateProjectInputs>({
  143. defaultValues: {
  144. taskGroups: {},
  145. allocatedStaffIds: [],
  146. milestones: {},
  147. totalManhour: 0,
  148. taskTemplateId: "All",
  149. projectName:
  150. mainProjects !== undefined ? mainProjects[0].projectName : undefined,
  151. projectDescription:
  152. mainProjects !== undefined
  153. ? mainProjects[0].projectDescription
  154. : undefined,
  155. expectedProjectFee:
  156. mainProjects !== undefined
  157. ? mainProjects[0].expectedProjectFee
  158. : undefined,
  159. subContractFee:
  160. mainProjects !== undefined ? mainProjects[0].subContractFee : undefined,
  161. clientId: allCustomers !== undefined ? allCustomers[0].id : undefined,
  162. ratePerManhour: 250,
  163. ...defaultInputs,
  164. // manhourPercentageByGrade should have a sensible default
  165. manhourPercentageByGrade: isEmpty(defaultInputs?.manhourPercentageByGrade)
  166. ? grades.reduce((acc, grade) => {
  167. return { ...acc, [grade.id]: 100 / grades.length };
  168. }, {})
  169. : defaultInputs?.manhourPercentageByGrade,
  170. },
  171. });
  172. const projectName = formProps.watch("projectName");
  173. const projectDeleted = formProps.watch("projectDeleted");
  174. const projectStatus = formProps.watch("projectStatus") || "";
  175. const defaultBtn = {
  176. buttonName: "submit",
  177. title: t("Do you want to submit?"),
  178. confirmButtonText: t("Submit"),
  179. successTitle: t("Submit Success"),
  180. errorTitle: t("Submit Fail"),
  181. };
  182. const buttonData = useMemo<{
  183. buttonName: string;
  184. title: string;
  185. confirmButtonText: string;
  186. successTitle: string;
  187. errorTitle: string;
  188. buttonText: string;
  189. buttonIcon: React.ReactNode;
  190. buttonColor: ButtonProps["color"];
  191. }>(() => {
  192. //Button Parameters//
  193. switch (projectStatus.toLowerCase()) {
  194. case "pending to start":
  195. return {
  196. buttonName: "start",
  197. title: t("Do you want to start?"),
  198. confirmButtonText: t("Start"),
  199. successTitle: t("Start Success"),
  200. errorTitle: t("Start Fail"),
  201. buttonText: t("Start Project"),
  202. buttonIcon: <PlayArrow />,
  203. buttonColor: "success",
  204. };
  205. case "on-going":
  206. return {
  207. buttonName: "complete",
  208. title: t("Do you want to complete?"),
  209. confirmButtonText: t("Complete"),
  210. successTitle: t("Complete Success"),
  211. errorTitle: t("Complete Fail"),
  212. buttonText: t("Complete Project"),
  213. buttonIcon: <DoneIcon />,
  214. buttonColor: "info",
  215. };
  216. case "completed":
  217. return {
  218. buttonName: "reopen",
  219. title: t("Do you want to reopen?"),
  220. confirmButtonText: t("Reopen"),
  221. successTitle: t("Reopen Success"),
  222. errorTitle: t("Reopen Fail"),
  223. buttonText: t("Reopen Project"),
  224. buttonIcon: <AutorenewIcon />,
  225. buttonColor: "secondary",
  226. };
  227. default:
  228. return {
  229. buttonName: "submit",
  230. title: t("Do you want to submit?"),
  231. confirmButtonText: t("Submit"),
  232. successTitle: t("Submit Success"),
  233. errorTitle: t("Submit Fail"),
  234. buttonText: t("Submit Project"),
  235. buttonIcon: <Check />,
  236. buttonColor: "success",
  237. };
  238. }
  239. }, [projectStatus, t]);
  240. const handleCancel = () => {
  241. router.replace("/projects");
  242. };
  243. const handleDelete = () => {
  244. deleteDialog(async () => {
  245. await deleteProject(formProps.getValues("projectId")!);
  246. const clickSuccessDialog = await successDialog("Delete Success", t);
  247. if (clickSuccessDialog) {
  248. router.replace("/projects");
  249. }
  250. }, t);
  251. };
  252. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  253. (_e, newValue) => {
  254. setTabIndex(newValue);
  255. },
  256. [],
  257. );
  258. const onSubmit = useCallback<SubmitHandler<CreateProjectInputs>>(
  259. async (data, event) => {
  260. try {
  261. console.log(data);
  262. // detect errors
  263. let hasErrors = false;
  264. if(
  265. !data.projectPlanStart || !data.projectPlanEnd
  266. ){
  267. formProps.setError("projectPlanStart", {
  268. message: "projectPlanStart is not valid",
  269. type: "required",
  270. });
  271. setTabIndex(0);
  272. hasErrors = true;
  273. }
  274. // Tab - Staff Allocation and Resource
  275. if (
  276. data.totalManhour === null ||
  277. data.totalManhour <= 0 ||
  278. Number.isNaN(data.totalManhour)
  279. ) {
  280. formProps.setError("totalManhour", {
  281. message: "totalManhour value is not valid",
  282. type: "required",
  283. });
  284. setTabIndex(2);
  285. hasErrors = true;
  286. }
  287. if (
  288. data.ratePerManhour === null ||
  289. data.ratePerManhour <= 0 ||
  290. Number.isNaN(data.ratePerManhour)
  291. ) {
  292. formProps.setError("ratePerManhour", {
  293. message: "ratePerManhour value is not valid",
  294. type: "required",
  295. });
  296. setTabIndex(2);
  297. hasErrors = true;
  298. }
  299. const manhourPercentageByGradeKeys = Object.keys(
  300. data.manhourPercentageByGrade,
  301. );
  302. if (
  303. manhourPercentageByGradeKeys.filter(
  304. (k) => data.manhourPercentageByGrade[k as any] < 0,
  305. ).length > 0 ||
  306. manhourPercentageByGradeKeys.reduce(
  307. (acc, value) => acc + data.manhourPercentageByGrade[value as any],
  308. 0,
  309. ) !== 100
  310. ) {
  311. formProps.setError("manhourPercentageByGrade", {
  312. message: "manhourPercentageByGrade value is not valid",
  313. type: "invalid",
  314. });
  315. setTabIndex(2);
  316. hasErrors = true;
  317. }
  318. const taskGroupKeys = Object.keys(data.taskGroups);
  319. if (
  320. taskGroupKeys.filter(
  321. (k) => data.taskGroups[k as any].percentAllocation < 0,
  322. ).length > 0 ||
  323. taskGroupKeys.reduce(
  324. (acc, value) =>
  325. acc + data.taskGroups[value as any].percentAllocation,
  326. 0,
  327. ) !== 100
  328. ) {
  329. formProps.setError("taskGroups", {
  330. message: "Task Groups value is not invalid",
  331. type: "invalid",
  332. });
  333. setTabIndex(2);
  334. hasErrors = true;
  335. }
  336. // Tab - Milestone
  337. let projectTotal = 0;
  338. const milestonesKeys = Object.keys(data.milestones).filter((key) =>
  339. taskGroupKeys.includes(key),
  340. );
  341. milestonesKeys
  342. .filter((key) => Object.keys(data.taskGroups).includes(key))
  343. .forEach((key) => {
  344. const { startDate, endDate, payments } =
  345. data.milestones[parseFloat(key)];
  346. if (
  347. !Boolean(startDate) ||
  348. startDate === "Invalid Date" ||
  349. !Boolean(endDate) ||
  350. endDate === "Invalid Date"
  351. ){
  352. data.milestones[parseFloat(key)].startDate = null
  353. data.milestones[parseFloat(key)].endDate = null
  354. }
  355. // if (
  356. // !Boolean(startDate) ||
  357. // startDate === "Invalid Date" ||
  358. // !Boolean(endDate) ||
  359. // endDate === "Invalid Date" ||
  360. // new Date(startDate) > new Date(endDate)
  361. // ) {
  362. // formProps.setError("milestones", {
  363. // message: "milestones is not valid",
  364. // type: "invalid",
  365. // });
  366. // setTabIndex(3);
  367. // hasErrors = true;
  368. // }
  369. projectTotal += payments.reduce(
  370. (acc, payment) => acc + payment.amount,
  371. 0,
  372. );
  373. });
  374. console.log(milestonesKeys)
  375. if (
  376. projectTotal !== data.expectedProjectFee
  377. // || milestonesKeys.length !== taskGroupKeys.length
  378. ) {
  379. formProps.setError("milestones", {
  380. message: "milestones is not valid",
  381. type: "invalid",
  382. });
  383. setTabIndex(3);
  384. hasErrors = true;
  385. }
  386. if (hasErrors) return false;
  387. // save project
  388. setServerError("");
  389. const buttonName = (event?.nativeEvent as any).submitter.name;
  390. const handleSubmit = async () => {
  391. if (buttonName === "start") {
  392. data.projectActualStart = dayjs().format("YYYY-MM-DD");
  393. } else if (buttonName === "complete") {
  394. data.projectActualEnd = dayjs().format("YYYY-MM-DD");
  395. }
  396. data.taskTemplateId =
  397. data.taskTemplateId === "All" ? undefined : data.taskTemplateId;
  398. const response = await saveProject(data);
  399. if (
  400. response.id > 0 &&
  401. response.message?.toLowerCase() === "success" &&
  402. response.errorPosition === null
  403. ) {
  404. successDialog(
  405. buttonName === "submit"
  406. ? defaultBtn.successTitle
  407. : buttonData.successTitle,
  408. t,
  409. ).then(() => {
  410. if (draftId) {
  411. deleteDraft(draftId);
  412. }
  413. router.replace("/projects");
  414. });
  415. } else {
  416. errorDialog(
  417. response.message ??
  418. (buttonName === "submit"
  419. ? defaultBtn.errorTitle
  420. : buttonData.errorTitle),
  421. t,
  422. ).then(() => {
  423. if (
  424. response.errorPosition !== null &&
  425. response.errorPosition === "projectCode"
  426. ) {
  427. formProps.setError("projectCode", {
  428. message: response.message,
  429. type: "invalid",
  430. });
  431. setTabIndex(0);
  432. }
  433. return false;
  434. });
  435. }
  436. };
  437. if (buttonName === "complete") {
  438. submitDialogWithWarning(handleSubmit, t, {
  439. title: buttonData.title,
  440. confirmButtonText: buttonData.confirmButtonText,
  441. text: "<b style='color:red'>Completing project will restrict any further changes to the project, are you sure to proceed?</b>",
  442. });
  443. } else if (buttonName === "submit") {
  444. submitDialog(handleSubmit, t, {
  445. title: defaultBtn.title,
  446. confirmButtonText: defaultBtn.confirmButtonText,
  447. });
  448. } else {
  449. submitDialog(handleSubmit, t, {
  450. title: buttonData.title,
  451. confirmButtonText: buttonData.confirmButtonText,
  452. });
  453. }
  454. } catch (e) {
  455. setServerError(t("An error has occurred. Please try again later."));
  456. }
  457. },
  458. [router, t],
  459. );
  460. const onSubmitError = useCallback<SubmitErrorHandler<CreateProjectInputs>>(
  461. (errors) => {
  462. console.log(errors);
  463. // Set the tab so that the focus will go there
  464. if (
  465. errors.projectName ||
  466. errors.projectDescription ||
  467. errors.projectCode ||
  468. errors.clientId ||
  469. errors.projectPlanStart ||
  470. errors.projectPlanEnd
  471. ) {
  472. setTabIndex(0);
  473. } else if (
  474. errors.totalManhour ||
  475. errors.manhourPercentageByGrade ||
  476. errors.taskGroups ||
  477. errors.ratePerManhour
  478. ) {
  479. setTabIndex(2);
  480. } else if (errors.milestones) {
  481. setTabIndex(3);
  482. }
  483. },
  484. [],
  485. );
  486. const errors = formProps.formState.errors;
  487. // auto calculate the total project manhour
  488. const expectedProjectFee = formProps.watch("expectedProjectFee");
  489. const ratePerManhour = formProps.watch("ratePerManhour");
  490. const totalManhour = formProps.watch("totalManhour");
  491. const firstLoadedRef = useRef(false);
  492. useEffect(() => {
  493. if (
  494. firstLoadedRef.current &&
  495. expectedProjectFee > 0 &&
  496. ratePerManhour > 0
  497. ) {
  498. formProps.setValue(
  499. "totalManhour",
  500. Math.ceil(expectedProjectFee / ratePerManhour),
  501. );
  502. } else {
  503. firstLoadedRef.current = true;
  504. }
  505. }, [expectedProjectFee, ratePerManhour]);
  506. useEffect(() => {
  507. if (
  508. expectedProjectFee > 0 &&
  509. ratePerManhour > 0 &&
  510. (totalManhour === null || Number.isNaN(totalManhour) || totalManhour <= 0)
  511. ) {
  512. formProps.setValue(
  513. "totalManhour",
  514. Math.ceil(expectedProjectFee / ratePerManhour),
  515. );
  516. }
  517. }, [totalManhour]);
  518. const loading = isEditMode || isCopyMode ? !Boolean(projectName) : false;
  519. const submitDisabled =
  520. loading ||
  521. projectDeleted === true ||
  522. projectStatus.toLowerCase() === "deleted" ||
  523. // !!formProps.getValues("projectActualStart") &&
  524. !!(projectStatus.toLowerCase() === "completed");
  525. useEffect(() => {
  526. const draftInputs = draftId ? loadDraft(draftId) : undefined;
  527. formProps.reset(draftInputs);
  528. }, [draftId, formProps]);
  529. const saveDraft = useCallback(() => {
  530. saveToLocalStorage(draftId || Date.now(), formProps.getValues());
  531. router.replace("/projects");
  532. }, [draftId, formProps, router]);
  533. const handleDeleteDraft = useCallback(() => {
  534. deleteDialog(async () => {
  535. deleteDraft(Number(draftId));
  536. const clickSuccessDialog = await successDialog("Delete Success", t);
  537. if (clickSuccessDialog) {
  538. router.replace("/projects");
  539. }
  540. }, t);
  541. }, [draftId, router]);
  542. return (
  543. <>
  544. <FormProvider {...formProps}>
  545. <Stack
  546. spacing={2}
  547. component="form"
  548. onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
  549. >
  550. {isEditMode &&
  551. !(formProps.getValues("projectDeleted") === true) &&
  552. !loading && (
  553. <Grid>
  554. <Typography mb={2} variant="h4">
  555. {t("Edit Project")}: {`<${defaultInputs?.projectCode}>`}
  556. </Typography>
  557. {(defaultInputs?.projectActualEnd ||
  558. defaultInputs?.projectActualStart) && (
  559. <Stack mb={2}>
  560. {defaultInputs?.projectActualStart && (
  561. <Typography variant="caption">
  562. {t("Project Start Date: {{date}}", {
  563. date: dayjs(defaultInputs.projectActualStart).format(
  564. OUTPUT_DATE_FORMAT,
  565. ),
  566. })}
  567. </Typography>
  568. )}
  569. {defaultInputs?.projectActualEnd && (
  570. <Typography variant="caption">
  571. {t("Project End Date: {{date}}", {
  572. date: dayjs(defaultInputs.projectActualEnd).format(
  573. OUTPUT_DATE_FORMAT,
  574. ),
  575. })}
  576. </Typography>
  577. )}
  578. </Stack>
  579. )}
  580. <Stack direction="row" gap={1}>
  581. {/* {!formProps.getValues("projectActualStart") && ( */}
  582. <Button
  583. name={buttonData.buttonName}
  584. type="submit"
  585. variant="contained"
  586. startIcon={buttonData.buttonIcon}
  587. color={buttonData.buttonColor}
  588. >
  589. {t(buttonData.buttonText)}
  590. </Button>
  591. {!(
  592. // formProps.getValues("projectActualStart") &&
  593. // formProps.getValues("projectActualEnd")
  594. (
  595. projectStatus.toLowerCase() === "completed" ||
  596. projectStatus.toLowerCase() === "deleted"
  597. )
  598. ) &&
  599. abilities.includes(DELETE_PROJECT) && (
  600. <Button
  601. variant="outlined"
  602. startIcon={<Delete />}
  603. color="error"
  604. onClick={handleDelete}
  605. >
  606. {t("Delete Project")}
  607. </Button>
  608. )}
  609. </Stack>
  610. </Grid>
  611. )}
  612. <Tabs
  613. value={tabIndex}
  614. onChange={handleTabChange}
  615. variant="scrollable"
  616. >
  617. <Tab
  618. label={t("Project and Client Details")}
  619. sx={{
  620. marginInlineEnd:
  621. !hasErrorsInTab(1, errors) &&
  622. (hasErrorsInTab(2, errors) || hasErrorsInTab(3, errors))
  623. ? 1
  624. : undefined,
  625. }}
  626. icon={
  627. hasErrorsInTab(0, errors) ? (
  628. <Error sx={{ marginInlineEnd: 1 }} color="error" />
  629. ) : undefined
  630. }
  631. iconPosition="end"
  632. />
  633. <Tab
  634. label={t("Project Task Setup")}
  635. sx={{
  636. marginInlineEnd:
  637. hasErrorsInTab(2, errors) || hasErrorsInTab(3, errors)
  638. ? 1
  639. : undefined,
  640. }}
  641. iconPosition="end"
  642. />
  643. <Tab
  644. label={t("Staff Allocation and Resource")}
  645. sx={{
  646. marginInlineEnd:
  647. !hasErrorsInTab(2, errors) && hasErrorsInTab(3, errors)
  648. ? 1
  649. : undefined,
  650. }}
  651. icon={
  652. hasErrorsInTab(2, errors) ? (
  653. <Error sx={{ marginInlineEnd: 1 }} color="error" />
  654. ) : undefined
  655. }
  656. iconPosition="end"
  657. />
  658. <Tab
  659. label={t("Milestone")}
  660. icon={
  661. hasErrorsInTab(3, errors) ? (
  662. <Error sx={{ marginInlineEnd: 1 }} color="error" />
  663. ) : undefined
  664. }
  665. iconPosition="end"
  666. />
  667. </Tabs>
  668. {
  669. <ProjectClientDetails
  670. isSubProject={isSubProject}
  671. mainProjects={mainProjects}
  672. buildingTypes={buildingTypes}
  673. workNatures={workNatures}
  674. contractTypes={contractTypes}
  675. fundingTypes={fundingTypes}
  676. locationTypes={locationTypes}
  677. serviceTypes={serviceTypes}
  678. allCustomers={allCustomers}
  679. allSubsidiaries={allSubsidiaries}
  680. projectCategories={projectCategories}
  681. customerTypes={customerTypes}
  682. teamLeads={teamLeads}
  683. isActive={tabIndex === 0}
  684. isEditMode={isEditMode}
  685. />
  686. }
  687. {
  688. <TaskSetup
  689. allTasks={allTasks}
  690. taskTemplates={taskTemplates}
  691. isActive={tabIndex === 1}
  692. />
  693. }
  694. {
  695. <StaffAllocation
  696. isActive={tabIndex === 2}
  697. allTasks={allTasks}
  698. grades={grades}
  699. allStaffs={allStaffs}
  700. teamLeads={teamLeads}
  701. />
  702. }
  703. {<Milestone allTasks={allTasks} isActive={tabIndex === 3} />}
  704. {serverError && (
  705. <Typography variant="body2" color="error" alignSelf="flex-end">
  706. {serverError}
  707. </Typography>
  708. )}
  709. <Stack direction="row" justifyContent="flex-end" gap={1}>
  710. {!isEditMode && (
  711. <>
  712. <Button
  713. variant="outlined"
  714. color="secondary"
  715. startIcon={<EditNote />}
  716. onClick={saveDraft}
  717. >
  718. {t("Save Draft")}
  719. </Button>
  720. {draftId &&
  721. <Button
  722. variant="outlined"
  723. color="error"
  724. startIcon={<Delete />}
  725. onClick={handleDeleteDraft}
  726. >
  727. {t("Delete Draft")}
  728. </Button>
  729. }
  730. <Box sx={{ flex: 1, pointerEvents: "none" }} />
  731. </>
  732. )}
  733. <Button
  734. variant="outlined"
  735. startIcon={<Close />}
  736. onClick={handleCancel}
  737. >
  738. {t("Cancel")}
  739. </Button>
  740. <Button
  741. name="submit"
  742. variant="contained"
  743. startIcon={<Check />}
  744. type="submit"
  745. disabled={submitDisabled}
  746. >
  747. {isEditMode ? t("Save") : t("Confirm")}
  748. </Button>
  749. </Stack>
  750. </Stack>
  751. </FormProvider>
  752. </>
  753. );
  754. };
  755. export default CreateProject;