Não pode escolher mais do que 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.
 
 

146 linhas
3.8 KiB

  1. import React, { useCallback, useMemo } from "react";
  2. import {
  3. Box,
  4. Button,
  5. Card,
  6. CardActions,
  7. CardContent,
  8. Modal,
  9. ModalProps,
  10. SxProps,
  11. Typography,
  12. } from "@mui/material";
  13. import TimesheetTable from "../TimesheetTable";
  14. import { useTranslation } from "react-i18next";
  15. import { Check, Close } from "@mui/icons-material";
  16. import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
  17. import {
  18. RecordTimesheetInput,
  19. saveTimesheet,
  20. } from "@/app/api/timesheets/actions";
  21. import dayjs from "dayjs";
  22. import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  23. import { AssignedProject, ProjectWithTasks } from "@/app/api/projects";
  24. interface Props {
  25. isOpen: boolean;
  26. onClose: () => void;
  27. allProjects: ProjectWithTasks[];
  28. assignedProjects: AssignedProject[];
  29. username: string;
  30. defaultTimesheets?: RecordTimesheetInput;
  31. }
  32. const modalSx: SxProps = {
  33. position: "absolute",
  34. top: "50%",
  35. left: "50%",
  36. transform: "translate(-50%, -50%)",
  37. width: { xs: "calc(100% - 2rem)", sm: "90%" },
  38. maxHeight: "90%",
  39. maxWidth: 1200,
  40. };
  41. const TimesheetModal: React.FC<Props> = ({
  42. isOpen,
  43. onClose,
  44. allProjects,
  45. assignedProjects,
  46. username,
  47. defaultTimesheets,
  48. }) => {
  49. const { t } = useTranslation("home");
  50. const defaultValues = useMemo(() => {
  51. const today = dayjs();
  52. return Array(7)
  53. .fill(undefined)
  54. .reduce<RecordTimesheetInput>((acc, _, index) => {
  55. const date = today.subtract(index, "day").format(INPUT_DATE_FORMAT);
  56. return {
  57. ...acc,
  58. [date]: defaultTimesheets?.[date] ?? [],
  59. };
  60. }, {});
  61. }, [defaultTimesheets]);
  62. const formProps = useForm<RecordTimesheetInput>({ defaultValues });
  63. const onSubmit = useCallback<SubmitHandler<RecordTimesheetInput>>(
  64. async (data) => {
  65. const savedRecords = await saveTimesheet(data, username);
  66. const today = dayjs();
  67. const newFormValues = Array(7)
  68. .fill(undefined)
  69. .reduce<RecordTimesheetInput>((acc, _, index) => {
  70. const date = today.subtract(index, "day").format(INPUT_DATE_FORMAT);
  71. return {
  72. ...acc,
  73. [date]: savedRecords[date] ?? [],
  74. };
  75. }, {});
  76. formProps.reset(newFormValues);
  77. onClose();
  78. },
  79. [formProps, onClose, username],
  80. );
  81. const onCancel = useCallback(() => {
  82. formProps.reset(defaultValues);
  83. onClose();
  84. }, [defaultValues, formProps, onClose]);
  85. const onModalClose = useCallback<NonNullable<ModalProps["onClose"]>>(
  86. (_, reason) => {
  87. if (reason !== "backdropClick") {
  88. onClose();
  89. }
  90. },
  91. [onClose],
  92. );
  93. return (
  94. <Modal open={isOpen} onClose={onModalClose}>
  95. <Card sx={modalSx}>
  96. <FormProvider {...formProps}>
  97. <CardContent
  98. component="form"
  99. onSubmit={formProps.handleSubmit(onSubmit)}
  100. >
  101. <Typography variant="overline" display="block" marginBlockEnd={1}>
  102. {t("Timesheet Input")}
  103. </Typography>
  104. <Box
  105. sx={{
  106. marginInline: -3,
  107. marginBlock: 4,
  108. }}
  109. >
  110. <TimesheetTable
  111. assignedProjects={assignedProjects}
  112. allProjects={allProjects}
  113. />
  114. </Box>
  115. <CardActions sx={{ justifyContent: "flex-end" }}>
  116. <Button
  117. variant="outlined"
  118. startIcon={<Close />}
  119. onClick={onCancel}
  120. >
  121. {t("Cancel")}
  122. </Button>
  123. <Button variant="contained" startIcon={<Check />} type="submit">
  124. {t("Save")}
  125. </Button>
  126. </CardActions>
  127. </CardContent>
  128. </FormProvider>
  129. </Card>
  130. </Modal>
  131. );
  132. };
  133. export default TimesheetModal;