"use client"; import Grid from "@mui/material/Grid"; import Paper from "@mui/material/Paper"; import { useState, useEffect } from "react"; import { useTranslation } from "react-i18next"; import PageTitle from "../PageTitle/PageTitle"; import { Suspense } from "react"; import Button from "@mui/material/Button"; import Stack from "@mui/material/Stack"; import Link from "next/link"; import { Box, Card, Typography, } from "@mui/material"; import AddIcon from "@mui/icons-material/Add"; import EditIcon from "@mui/icons-material/Edit"; import DeleteIcon from "@mui/icons-material/DeleteOutlined"; import SaveIcon from "@mui/icons-material/Save"; import CancelIcon from "@mui/icons-material/Close"; import AddPhotoAlternateOutlinedIcon from "@mui/icons-material/AddPhotoAlternateOutlined"; import ImageNotSupportedOutlinedIcon from "@mui/icons-material/ImageNotSupportedOutlined"; import React from "react"; import { GridRowsProp, GridRowModesModel, GridRowModes, DataGrid, GridColDef, GridActionsCellItem, GridEventListener, GridRowId, GridRowModel, GridRowEditStopReasons, GridEditInputCell, GridTreeNodeWithRender, GridRenderCellParams, } from "@mui/x-data-grid"; import dayjs from "dayjs"; import { Props } from "react-intl/src/components/relative"; import palette from "@/theme/devias-material-kit/palette"; import { ProjectCombo } from "@/app/api/claims"; import { ClaimDetailTable, ClaimInputFormByStaff } from "@/app/api/claims/actions"; import { useFieldArray, useFormContext } from "react-hook-form"; import { GridRenderEditCellParams } from "@mui/x-data-grid"; import { convertDateToString, moneyFormatter } from "@/app/utils/formatUtil"; interface BottomBarProps { getCostTotal: () => number; setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; setRowModesModel: ( newModel: (oldModel: GridRowModesModel) => GridRowModesModel, ) => void; } interface EditFooterProps { setRows: (newRows: (oldRows: GridRowsProp) => GridRowsProp) => void; setRowModesModel: ( newModel: (oldModel: GridRowModesModel) => GridRowModesModel, ) => void; } const BottomBar = (props: BottomBarProps) => { const { t } = useTranslation("claim") const { setRows, setRowModesModel, getCostTotal } = props; // const getCostTotal = props.getCostTotal; const [newId, setNewId] = useState(-1); const handleAddClick = () => { const id = newId; setNewId(newId - 1); setRows((oldRows) => [ ...oldRows, { id, invoiceDate: new Date(), project: null, description: null, amount: null, newSupportingDocument: null, supportingDocumentName: null, isNew: true }, ]); setRowModesModel((oldModel) => ({ ...oldModel, [id]: { mode: GridRowModes.Edit, fieldToFocus: "projectCode" }, })); }; const TotalCell = ({ value }: Props) => { const [invalid, setInvalid] = useState(false); useEffect(() => { const newInvalid = (value ?? 0) < 0; setInvalid(newInvalid); }, [value]); return ( $ {value} ); }; return (
{t("Total")}:
); }; const EditFooter = (props: EditFooterProps) => { return (
Total: test
); }; interface ClaimFormInputGridProps { // onClose?: () => void; projectCombo: ProjectCombo[] } const initialRows: GridRowsProp = [ { id: 1, invoiceDate: new Date(), description: "Taxi to client office", amount: 169.5, supportingDocumentName: "taxi_receipt.jpg", }, { id: 2, invoiceDate: dayjs().add(-14, "days").toDate(), description: "MTR fee to Kowloon Bay Office", amount: 15.5, supportingDocumentName: "octopus_invoice.jpg", }, { id: 3, invoiceDate: dayjs().add(-44, "days").toDate(), description: "Starbucks", amount: 504, }, ]; const ClaimFormInputGrid: React.FC = ({ // onClose, projectCombo, }) => { const { t } = useTranslation() const { control, setValue, getValues, formState: { errors }, clearErrors, setError } = useFormContext(); const { fields } = useFieldArray({ control, name: "addClaimDetails" }) const [rows, setRows] = useState([]); const [rowModesModel, setRowModesModel] = React.useState( {}, ); // Row function const handleRowEditStop: GridEventListener<"rowEditStop"> = ( params, event, ) => { if (params.reason === GridRowEditStopReasons.rowFocusOut) { event.defaultMuiPrevented = true; } }; const handleEditClick = (id: GridRowId) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.Edit } }); }; const handleSaveClick = (id: GridRowId) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View } }); }; const handleDeleteClick = (id: GridRowId) => () => { setRows(rows.filter((row) => row.id !== id)); }; const handleCancelClick = (id: GridRowId) => () => { setRowModesModel({ ...rowModesModel, [id]: { mode: GridRowModes.View, ignoreModifications: true }, }); const editedRow = rows.find((row) => row.id === id); if (editedRow!.isNew) { setRows(rows.filter((row) => row.id !== id)); } }; const processRowUpdate = React.useCallback((newRow: GridRowModel) => { const updatedRow = { ...newRow }; const updatedRows = rows.map((row) => (row.id === newRow.id ? { ...updatedRow, supportingDocumentName: row.supportingDocumentName } : row)) setRows(updatedRows); setValue("addClaimDetails", updatedRows as ClaimDetailTable[]) return updatedRows.find((row) => row.id === newRow.id) as GridRowModel; }, [rows, rowModesModel, t]); const handleRowModesModelChange = (newRowModesModel: GridRowModesModel) => { setRowModesModel(newRowModesModel); }; // File Upload function const fileInputRef: React.RefObject> = React.useRef({}) const setFileInputRefs = (ele: HTMLInputElement | null, key: string) => { if (fileInputRef.current !== null) { fileInputRef.current[key] = ele } } useEffect(() => { }, []) const handleFileSelect = (key: string) => { if (fileInputRef !== null && fileInputRef.current !== null && fileInputRef.current[key] !== null) { fileInputRef.current[key]?.click() } } const handleFileChange = (event: React.ChangeEvent, params: GridRenderEditCellParams) => { const file = event.target.files?.[0] ?? null if (file !== null) { console.log(file) console.log(typeof file) const updatedRows = rows.map((row) => (row.id === params.row.id ? { ...row, supportingDocumentName: file.name, newSupportingDocument: file } : row)) setRows(updatedRows); setValue("addClaimDetails", updatedRows as ClaimDetailTable[]) // const url = URL.createObjectURL(new Blob([file])); // const link = document.createElement("a"); // link.href = url; // link.setAttribute("download", file.name); // link.click(); } } const handleFileDelete = (id: number) => { const updatedRows = rows.map((row) => (row.id === id ? { ...row, supportingDocumentName: null, newSupportingDocument: null } : row)) setRows(updatedRows); setValue("addClaimDetails", updatedRows as ClaimDetailTable[]) } const handleLinkClick = (params: GridRenderEditCellParams) => { const url = URL.createObjectURL(new Blob([params.row.newSupportingDocument])); const link = document.createElement("a"); link.href = url; link.setAttribute("download", params.row.supportingDocumentName); link.click(); // console.log(params) // console.log(rows) } // columns const getCostTotal = () => { let sum = 0; rows.forEach((row) => { sum += row["amount"] ?? 0; }); return sum; }; const commonGridColConfig: any = { type: "number", // sortable: false, //width: 100, flex: 1, align: "left", headerAlign: "left", // headerClassName: 'header', editable: true, renderEditCell: (value: any) => ( ), }; const columns: GridColDef[] = React.useMemo(() => [ { field: "actions", type: "actions", headerName: t("Actions"), width: 100, cellClassName: "actions", getActions: ({ id }) => { const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; if (isInEditMode) { return [ } title="Save" label="Save" sx={{ color: "primary.main", }} onClick={handleSaveClick(id)} />, } title="Cancel" label="Cancel" className="textPrimary" onClick={handleCancelClick(id)} color="inherit" />, ]; } return [ } title="Edit" label="Edit" className="textPrimary" onClick={handleEditClick(id)} color="inherit" />, } onClick={handleDeleteClick(id)} sx={{ color: "red" }} />, ]; }, }, { field: "invoiceDate", headerName: t("Invoice Date"), // width: 220, flex: 1, editable: true, type: "date", renderCell: (params: GridRenderCellParams) => { return convertDateToString(params.value!!) }, }, { field: "project", headerName: t("Project"), // width: 220, flex: 1, editable: true, type: "singleSelect", getOptionLabel: (value: ProjectCombo) => { return !value?.code || value?.code.length === 0 ? `${value?.name}` : `${value?.code} - ${value?.name}`; }, getOptionValue: (value: ProjectCombo) => value.id, valueOptions: () => { const options = projectCombo ?? [] if (options.length === 0) { options.push({ id: -1, code: "", name: "No Projects" }) } return options as ProjectCombo[]; }, valueGetter: (params) => { return params.value ?? projectCombo[0] ?? { id: -1, code: "", name: "No Projects" } as ProjectCombo }, }, { field: "description", headerName: t("Description"), // width: 220, flex: 2, editable: true, type: "string", }, { field: "amount", headerName: t("Amount"), editable: true, type: "number", align: "right", valueFormatter: (params) => { return moneyFormatter.format(params.value ?? 0); }, }, { field: "supportingDocumentName", headerName: t("Supporting Document"), // type: "string", editable: true, flex: 2, renderCell: (params) => { return params.value ? ( handleLinkClick(params)} href="#">{params.value} {/* {params.value} */} ) : ( No Documents ); }, renderEditCell: (params) => { // const currentRow = rows.find(row => row.id === params.row.id); return params.formattedValue ? ( handleLinkClick(params)} href="#">{params.formattedValue} {/* {params.formattedValue} */} ) : (
setFileInputRefs(ele, params.row.id)} accept="image/jpg, image/jpeg, image/png, .doc, .docx, .pdf" style={{ display: 'none' }} onChange={(event) => handleFileChange(event, params)} />
); }, }, ], [rows, rowModesModel, t],); // check error useEffect(() => { if (getValues("addClaimDetails") === undefined || getValues("addClaimDetails") === null) { return; } if (getValues("addClaimDetails").length === 0) { clearErrors("addClaimDetails") } else { console.log(rows) if (rows.filter(row => String(row.description).trim().length === 0 || String(row.amount).trim().length === 0 || row.project === null || row.project === undefined || ((row.oldSupportingDocument === null || row.oldSupportingDocument === undefined) && (row.newSupportingDocument === null || row.newSupportingDocument === undefined))).length > 0) { setError("addClaimDetails", { message: "Claim details include empty fields", type: "required" }) } else { let haveError = false if (rows.filter(row => row.invoiceDate.getTime() > new Date().getTime()).length > 0) { haveError = true setError("addClaimDetails", { message: "Claim details include invalid invoice date", type: "invalid_date" }) } if (rows.filter(row => row.project === null || row.project === undefined).length > 0) { haveError = true setError("addClaimDetails", { message: "Claim details include empty project", type: "invalid_project" }) } if (rows.filter(row => row.amount <= 0).length > 0) { haveError = true setError("addClaimDetails", { message: "Claim details include invalid amount", type: "invalid_amount" }) } if (!haveError) { clearErrors("addClaimDetails") } } } }, [rows, rowModesModel]) // check editing useEffect(() => { const filteredByKey = Object.fromEntries( Object.entries(rowModesModel).filter(([key, value]) => rowModesModel[key].mode === 'edit')) if (Object.keys(filteredByKey).length > 0) { setValue("isGridEditing", true) } else { setValue("isGridEditing", false) } }, [rowModesModel]) return ( {Boolean(errors.addClaimDetails?.type === "required") && ({ color: theme.palette.error.main, ml: 3, mt: 1 })} variant="overline" display='inline-block' noWrap> {t("Please ensure at least one row is created, and all the fields are inputted and saved")} } {Boolean(errors.addClaimDetails?.type === "invalid_date") && ({ color: theme.palette.error.main, ml: 3, mt: 1 })} variant="overline" display='inline-block' noWrap> {t("Please ensure the date are correct")} } {Boolean(errors.addClaimDetails?.type === "invalid_project") && ({ color: theme.palette.error.main, ml: 3, mt: 1 })} variant="overline" display='inline-block' noWrap> {t("Please ensure the projects are selected")} } {Boolean(errors.addClaimDetails?.type === "invalid_amount") && ({ color: theme.palette.error.main, ml: 3, mt: 1 })} variant="overline" display='inline-block' noWrap> {t("Please ensure the amount are correct")} }
); }; export default ClaimFormInputGrid;