|
- import { LeaveType } from "@/app/api/timesheets";
- import { LeaveEntry } from "@/app/api/timesheets/actions";
- import {
- DAILY_NORMAL_MAX_HOURS,
- TIMESHEET_DAILY_MAX_HOURS,
- } from "@/app/api/timesheets/utils";
- import { shortDateFormatter } from "@/app/utils/formatUtil";
- import { roundToNearestQuarter } from "@/app/utils/manhourUtils";
- import { Check, Delete, Close } from "@mui/icons-material";
- import {
- Box,
- Button,
- FormControl,
- InputLabel,
- MenuItem,
- Modal,
- ModalProps,
- Paper,
- Select,
- SxProps,
- TextField,
- Typography,
- } from "@mui/material";
- import React, { useCallback, useEffect } from "react";
- import { Controller, useForm } from "react-hook-form";
- import { useTranslation } from "react-i18next";
-
- export interface Props extends Omit<ModalProps, "children"> {
- onSave: (leaveEntry: LeaveEntry, recordDate?: string) => Promise<void>;
- onDelete?: () => Promise<void>;
- leaveTypes: LeaveType[];
- defaultValues?: Partial<LeaveEntry>;
- modalSx?: SxProps;
- recordDate?: string;
- isHoliday?: boolean;
- }
-
- const modalSx: SxProps = {
- position: "absolute",
- top: "50%",
- left: "50%",
- transform: "translate(-50%, -50%)",
- width: "90%",
- maxHeight: "90%",
- padding: 3,
- display: "flex",
- flexDirection: "column",
- gap: 2,
- };
- const LeaveEditModal: React.FC<Props> = ({
- onSave,
- onDelete,
- open,
- onClose,
- leaveTypes,
- defaultValues,
- recordDate,
- modalSx: mSx,
- isHoliday,
- }) => {
- const {
- t,
- i18n: { language },
- } = useTranslation("home");
- const { register, control, reset, getValues, trigger, formState, setError } =
- useForm<LeaveEntry>({
- defaultValues: {
- leaveTypeId: leaveTypes[0].id,
- },
- });
-
- useEffect(() => {
- reset(defaultValues ?? { leaveTypeId: leaveTypes[0].id, id: Date.now() });
- }, [defaultValues, leaveTypes, reset]);
-
- const saveHandler = useCallback(async () => {
- const valid = await trigger();
- if (valid) {
- try {
- await onSave(getValues(), recordDate);
- reset({ id: Date.now() });
- } catch (e) {
- setError("root", {
- message: e instanceof Error ? e.message : "Unknown error",
- });
- }
- }
- }, [getValues, onSave, recordDate, reset, setError, trigger]);
-
- const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
- (...args) => {
- onClose?.(...args);
- reset({ id: Date.now() });
- },
- [onClose, reset],
- );
-
- return (
- <Modal open={open} onClose={closeHandler}>
- <Paper sx={{ ...modalSx, ...mSx }}>
- {recordDate && (
- <Typography
- variant="h6"
- marginBlockEnd={2}
- color={isHoliday ? "error.main" : undefined}
- >
- {shortDateFormatter(language).format(new Date(recordDate))}
- </Typography>
- )}
- <FormControl fullWidth>
- <InputLabel>{t("Leave Types")}</InputLabel>
- <Controller
- defaultValue={leaveTypes[0].id}
- control={control}
- name="leaveTypeId"
- render={({ field }) => (
- <Select label={t("Leave Types")} {...field}>
- {leaveTypes.map((type, index) => (
- <MenuItem key={`${type.id}-${index}`} value={type.id}>
- {t(type.name)}
- </MenuItem>
- ))}
- </Select>
- )}
- />
- </FormControl>
- <TextField
- type="number"
- label={t("Hours")}
- fullWidth
- {...register("inputHours", {
- setValueAs: (value) => roundToNearestQuarter(parseFloat(value)),
- validate: (value) => {
- if (isHoliday) {
- return t("Cannot input normal hours on holidays");
- }
-
- return (
- (0 < value && value <= DAILY_NORMAL_MAX_HOURS) ||
- t(
- "Input hours should be between 0 and {{DAILY_NORMAL_MAX_HOURS}}",
- { DAILY_NORMAL_MAX_HOURS },
- )
- );
- },
- })}
- error={Boolean(formState.errors.inputHours)}
- helperText={formState.errors.inputHours?.message}
- />
- <TextField
- label={t("Remarks")}
- fullWidth
- multiline
- rows={2}
- {...register("remark")}
- />
- {formState.errors.root?.message && (
- <Typography variant="caption" color="error">
- {t(formState.errors.root.message, {
- DAILY_NORMAL_MAX_HOURS,
- TIMESHEET_DAILY_MAX_HOURS,
- })}
- </Typography>
- )}
- <Box display="flex" justifyContent="flex-end" gap={1}>
- {onDelete && (
- <Button
- variant="outlined"
- startIcon={<Delete />}
- color="error"
- onClick={onDelete}
- >
- {t("Delete")}
- </Button>
- )}
- <Button
- variant="outlined"
- startIcon={<Close />}
- onClick={(event) => closeHandler(event, "backdropClick")}
- >
- {t("Close")}
- </Button>
- <Button
- variant="contained"
- startIcon={<Check />}
- onClick={saveHandler}
- >
- {t("Save")}
- </Button>
- </Box>
- </Paper>
- </Modal>
- );
- };
-
- export default LeaveEditModal;
|