"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")}:
}
onClick={handleAddClick}
sx={{ margin: "20px" }}
>
{t("Add Record")}
);
};
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}
*/}
) : (
);
},
},
], [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;