"use client"; import { FooterPropsOverrides, GridCellParams, GridRowId, GridRowIdGetter, GridRowModel, GridRowModes, GridRowModesModel, GridToolbarContainer, GridValidRowModel, useGridApiRef, } from "@mui/x-data-grid"; import { Dispatch, MutableRefObject, SetStateAction, useCallback, useEffect, useMemo, useRef, useState, } from "react"; import StyledDataGrid from "../StyledDataGrid"; import { GridColDef } from "@mui/x-data-grid"; import { Box, Button, Grid, Typography, useMediaQuery, useTheme } from "@mui/material"; import { useTranslation } from "react-i18next"; import { Add } from "@mui/icons-material"; import SaveIcon from "@mui/icons-material/Save"; import DeleteIcon from "@mui/icons-material/Delete"; import CancelIcon from "@mui/icons-material/Cancel"; import FactCheckIcon from "@mui/icons-material/FactCheck"; import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; // import { QcItemWithChecks } from "src/app/api/qc"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import { PurchaseOrderLine } from "@/app/api/po"; import { StockInLine } from "@/app/api/stockIn"; import { createStockInLine, deleteStockInLine, updateStockInLine, QcResult } from "@/app/api/stockIn/actions"; import { usePathname, useSearchParams } from "next/navigation"; import { returnWeightUnit, calculateWeight, stockInLineStatusMap, arrayToDateString, } from "@/app/utils/formatUtil"; // import PoQcStockInModal from "./PoQcStockInModal"; import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; import { WarehouseResult } from "@/app/api/warehouse"; import LooksOneIcon from "@mui/icons-material/LooksOne"; import LooksTwoIcon from "@mui/icons-material/LooksTwo"; import Looks3Icon from "@mui/icons-material/Looks3"; import axiosInstance from "@/app/(main)/axios/axiosInstance"; // import axios, { AxiosRequestConfig } from "axios"; import { BASE_API_URL, NEXT_PUBLIC_API_URL } from "@/config/api"; import qs from "qs"; import QrCodeIcon from "@mui/icons-material/QrCode"; import { downloadFile } from "@/app/utils/commonUtil"; import { fetchPoQrcode } from "@/app/api/pdf/actions"; import { fetchQcResult } from "@/app/api/qc/actions"; import DoDisturbIcon from "@mui/icons-material/DoDisturb"; import { useSession } from "next-auth/react"; // import { SessionWithTokens } from "src/config/authConfig"; import QcStockInModal from "../Qc/QcStockInModal"; import { decimalFormatter, integerFormatter } from "@/app/utils/formatUtil"; import { PrinterCombo } from "@/app/api/settings/printer"; import { EscalationResult } from "@/app/api/escalation"; import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions"; import { SessionWithTokens } from "@/config/authConfig"; import { EscalationCombo } from "@/app/api/user"; import { deleteDialog } from "../Swal/CustomAlerts"; import StockInLineRowActions from "./StockInLineRowActions"; import { StockQtyRoundMode, needsPoQcStockQtyRound } from "./stockQtyRound"; // 3 buttons after QC (view + print QR + delete): 176*3 + gap + cell padding const ACTIONS_COLUMN_WIDTH = 580; const PURCHASE_QTY_COLUMN_WIDTH = 72; const UOM_COLUMN_WIDTH = 124; const STOCK_QTY_COLUMN_WIDTH = 110; const STOCK_IN_ROW_HEIGHT = 58; /** Extra table width is shared by text columns; qty / status / actions stay tight. */ const COLUMN_GROW: Record = { dnNo: 1, productLotNo: 1, uom: 1.5, stockQty: 1, stockUom: 1.5, }; /** Tighter horizontal padding for narrow data columns (headers unchanged). */ const COMPACT_STOCK_IN_CELL_FIELDS = [ "dnNo", "productLotNo", "purchaseAcceptedQty", "uom", "stockUom", "status", ] as const; function canDeleteStockInLine(sil: StockInLineRow): boolean { if (sil._isNew || sil.status === "draft") { return true; } const hasPutAway = (sil.putAwayLines ?? []).some( (p) => Number(p.stockQty ?? p.qty ?? 0) > 0, ); if (hasPutAway) return false; const status = (sil.status ?? "").toLowerCase(); return status !== "completed" && status !== "partially_completed"; } interface ResultWithId { id: number; } interface Props { // qc: QcItemWithChecks[]; setRows: Dispatch>; setStockInLine: Dispatch>; setProcessedQty: Dispatch>; itemDetail: PurchaseOrderLine; stockInLine: StockInLine[]; warehouse: WarehouseResult[]; fetchPoDetail: (poId: string, preserveDnNo?: boolean, preferredPolId?: number) => void; handleMailTemplateForStockInLine: (stockInLineId: number) => void; printerCombo: PrinterCombo[]; } export type StockInLineEntryError = { [field in keyof StockInLine]?: string; }; export type StockInLineRow = Partial< StockInLine & { isActive: boolean | undefined; _isNew: boolean; _error: StockInLineEntryError; } & ResultWithId >; class ProcessRowUpdateError extends Error { public readonly row: StockInLineRow; public readonly errors: StockInLineEntryError | undefined; constructor( row: StockInLineRow, message?: string, errors?: StockInLineEntryError, ) { super(message); this.row = row; this.errors = errors; Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); } } /** FP-MTMS Version Checklist | Functions Ref. No. 56 | v1.0.4 | 2026-09-10 */ function PoInputGrid({ // qc, setRows, setStockInLine, setProcessedQty, itemDetail, stockInLine, warehouse, fetchPoDetail, handleMailTemplateForStockInLine, printerCombo, }: Props) { const { t } = useTranslation("purchaseOrder"); const theme = useTheme(); /** Narrow phones: hide low-priority columns. */ const isCompact = useMediaQuery(theme.breakpoints.down("md"), { noSsr: true }); const apiRef = useGridApiRef(); const [rowModesModel, setRowModesModel] = useState({}); const getRowId = useCallback>( (row) => row.id as number, [], ); const [entries, setEntries] = useState(stockInLine || []); useEffect(() => { setEntries(stockInLine); }, [stockInLine]) const [modalInfo, setModalInfo] = useState< StockInLine & { qcResult?: QcResult[] } & { escalationResult?: EscalationResult[] } >(); const pathname = usePathname() const searchParams = useSearchParams(); const [qcOpen, setQcOpen] = useState(false); const [escalOpen, setEscalOpen] = useState(false); const [stockInOpen, setStockInOpen] = useState(false); const [putAwayOpen, setPutAwayOpen] = useState(false); const [rejectOpen, setRejectOpen] = useState(false); const [btnIsLoading, setBtnIsLoading] = useState(false); const [isDeleting, setIsDeleting] = useState(false); const deleteInFlightRef = useRef(false); const roundInFlightRef = useRef(false); const [roundingSilId, setRoundingSilId] = useState(null); const [currQty, setCurrQty] = useState(() => { const total = entries.reduce( // remaining qty (M18 unit) (acc, curr) => acc + (curr.purchaseAcceptedQty || 0), 0, ); return total; }); const { data: session } = useSession(); const sessionToken = session as SessionWithTokens | null; useEffect(() => { const completedList = entries.filter( (e) => stockInLineStatusMap[e.status!] >= 8, ); const processedQty = completedList.reduce( (acc, curr) => acc + (curr.acceptedQty || 0), 0, ); setProcessedQty(processedQty); }, [entries, setProcessedQty]); const handleDelete = useCallback( (id: GridRowId) => () => { setEntries((es) => es.filter((e) => getRowId(e) !== id)); }, [getRowId], ); const handleSoftDelete = useCallback( (row: StockInLineRow) => { if (deleteInFlightRef.current || isDeleting) return; if ( needsPoQcStockQtyRound( row.purchaseOrderLineId, row.status, row.acceptedQty, ) ) { alert("請先在換算庫存數量選擇向上或向下取整"); return; } const rowId = row.id as number; const isDraft = row._isNew || row.status === "draft"; const doDelete = async () => { if (deleteInFlightRef.current) return; deleteInFlightRef.current = true; setIsDeleting(true); try { if (isDraft) { handleDelete(rowId)(); return; } await deleteStockInLine(rowId); await fetchPoDetail( String(itemDetail.purchaseOrderId), true, itemDetail.id, ); } catch (error) { console.error("Failed to delete stock in line:", error); alert(t("Cannot delete put away record")); } finally { setIsDeleting(false); deleteInFlightRef.current = false; } }; void deleteDialog(doDelete, t); }, [fetchPoDetail, handleDelete, isDeleting, itemDetail.id, itemDetail.purchaseOrderId, t], ); const handleRoundStockQty = useCallback( (row: StockInLineRow, mode: StockQtyRoundMode) => { if (roundInFlightRef.current) return; const silId = row.id; const itemId = row.itemId; const purchaseQty = Number(row.purchaseAcceptedQty ?? 0); if (!silId || !itemId || purchaseQty <= 0) return; const doRound = async () => { if (roundInFlightRef.current) return; roundInFlightRef.current = true; setRoundingSilId(silId); try { const res = await updateStockInLine({ id: silId, itemId, purchaseOrderLineId: row.purchaseOrderLineId, acceptedQty: purchaseQty, dnNo: row.dnNo, productLotNo: row.productLotNo, stockQtyRoundMode: mode, stockQtyRoundSource: "CREATE", }); if (res) { await fetchPoDetail( String(itemDetail.purchaseOrderId), true, itemDetail.id, ); } } catch (error) { console.error("Failed to round stock qty:", error); alert(t("Please choose a rounding method")); } finally { setRoundingSilId(null); roundInFlightRef.current = false; } }; void doRound(); }, [fetchPoDetail, itemDetail.id, itemDetail.purchaseOrderId, t], ); const closeQcModal = useCallback(() => { setQcOpen(false); }, []); const openQcModal = useCallback(() => { setQcOpen(true); }, []); const closeStockInModal = useCallback(() => { setStockInOpen(false); }, []); const openStockInModal = useCallback(() => { setStockInOpen(true); }, []); const closePutAwayModal = useCallback(() => { setPutAwayOpen(false); }, []); const openPutAwayModal = useCallback(() => { setPutAwayOpen(true); }, []); const closeEscalationModal = useCallback(() => { setEscalOpen(false); }, []); const openEscalationModal = useCallback(() => { setEscalOpen(true); }, []); const closeRejectModal = useCallback(() => { setRejectOpen(false); }, []); const openRejectModal = useCallback(() => { setRejectOpen(true); }, []); const handleStart = useCallback( // NOTE: Seems unused!!!!!!!! (id: GridRowId, params: any) => () => { setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setTimeout(async () => { // post stock in line const oldId = params.row.id; const postData = { itemId: params.row.itemId, itemNo: params.row.itemNo, itemName: params.row.itemName, // purchaseOrderId: params.row.purchaseOrderId, purchaseOrderLineId: params.row.purchaseOrderLineId, // For PO-origin, backend expects M18 qty and converts it to stock qty. acceptedQty: params.row.purchaseAcceptedQty ?? params.row.acceptedQty, }; const res = await createStockInLine(postData); console.log(res); setEntries((prev) => prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p)), ); setStockInLine( (prev) => prev.map((p) => p.id === oldId ? (res.entity as StockInLine) : p, ) as StockInLine[], ); setBtnIsLoading(false); // do post directly to test // openStartModal(); }, 200); }, [setStockInLine], ); const fetchQcDefaultValue = useCallback(async (stockInLineId: GridRowId) => { return await fetchQcResult(stockInLineId as number); }, []); // const handleQC = useCallback( // UNUSED NOW! // (id: GridRowId, params: any) => async () => { // setBtnIsLoading(true); // setRowModesModel((prev) => ({ // ...prev, // [id]: { mode: GridRowModes.View }, // })); // const qcResult = await fetchQcDefaultValue(id); // // console.log(params.row); // console.log("Fetched QC Result:", qcResult); // setModalInfo({ // ...params.row, // qcResult: qcResult, // }); // // set default values // setTimeout(() => { // // open qc modal // console.log("delayed"); // openQcModal(); // setBtnIsLoading(false); // }, 200); // }, // [fetchQcDefaultValue, openQcModal], // ); const [newOpen, setNewOpen] = useState(false); const stockInLineIdFromNext = searchParams.get("stockInLineId"); const poLineId = searchParams.get("poLineId"); const patchQuery = useCallback( (mutate: (params: URLSearchParams) => void) => { if (typeof window === "undefined") return; const params = new URLSearchParams(window.location.search); mutate(params); const qs = params.toString(); window.history.replaceState( window.history.state, "", qs ? `${pathname}?${qs}` : pathname, ); }, [pathname], ); const getLiveStockInLineId = useCallback((): string | null => { if (typeof window !== "undefined") { return new URLSearchParams(window.location.search).get("stockInLineId"); } return stockInLineIdFromNext; }, [stockInLineIdFromNext]); const closeNewModal = useCallback((updatedStockInLine?: StockInLine) => { patchQuery((params) => { params.delete("stockInLineId"); }); setNewOpen(false); if (updatedStockInLine?.id != null) { setEntries((prev) => prev.map((e) => (e.id === updatedStockInLine.id ? { ...e, ...updatedStockInLine } : e)) ); setStockInLine((prev) => (prev || []).map((p) => (p.id === updatedStockInLine.id ? { ...p, ...updatedStockInLine } : p)) ); } }, [patchQuery, setStockInLine]); // Open modal const openNewModal = useCallback(() => { setNewOpen(() => true); }, []); // Button handler to update the URL and open the modal const handleNewQC = useCallback( (id: GridRowId, params: any) => async() => { if (!params?.row) return; if ( needsPoQcStockQtyRound( params.row.purchaseOrderLineId, params.row.status, params.row.acceptedQty, ) ) { alert("請先在換算庫存數量選擇向上或向下取整"); return; } setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(() => ({ ...params.row, receivedQty: itemDetail.receivedQty, })); // Avoid router.replace — it scrolls the page to top patchQuery((params) => { params.set("stockInLineId", id.toString()); }); openNewModal(); }, [openNewModal, patchQuery, itemDetail.receivedQty], ); // Open modal if `stockInLineId` exists in the live URL (and belongs to current grid) const [firstCheckForSil, setFirstCheckForSil] = useState(false); useEffect(() => { setFirstCheckForSil(false); }, [itemDetail.id]); useEffect(() => { if (!itemDetail || firstCheckForSil) return; const liveStockInLineId = getLiveStockInLineId(); if (!liveStockInLineId) { setFirstCheckForSil(true); return; } const row = apiRef.current.getRow(Number(liveStockInLineId)); if (!row) { // Stale query from another POL: drop it once current entries are known if ( entries.length > 0 && !entries.some((e) => String(e.id) === String(liveStockInLineId)) ) { patchQuery((params) => { params.delete("stockInLineId"); }); setFirstCheckForSil(true); } return; } setFirstCheckForSil(true); void handleNewQC(liveStockInLineId, { row })(); }, [ stockInLineIdFromNext, poLineId, itemDetail, firstCheckForSil, entries, handleNewQC, getLiveStockInLineId, patchQuery, ]); const handleEscalation = useCallback( (id: GridRowId, params: any) => () => { // setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(params.row); setTimeout(() => { // open qc modal console.log("delayed"); openEscalationModal(); // setBtnIsLoading(false); }, 200); }, [openEscalationModal], ); const handleReject = useCallback( (id: GridRowId, params: any) => () => { setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(params.row); setTimeout(() => { // open stock in modal // openPutAwayModal(); // return the record with its status as pending // update layout console.log("delayed"); openRejectModal(); // printQrcode(params.row); }, 200); }, [openRejectModal], ); const handleStockIn = useCallback( (id: GridRowId, params: any) => () => { // setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(params.row); setTimeout(() => { // open stock in modal openStockInModal(); // return the record with its status as pending // update layout console.log("delayed"); // setBtnIsLoading(false); }, 200); }, [openStockInModal], ); const handlePutAway = useCallback( (id: GridRowId, params: any) => () => { // setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(params.row); setTimeout(() => { // open stock in modal openPutAwayModal(); // return the record with its status as pending // update layout console.log("delayed"); // setBtnIsLoading(false); }, 200); }, [openPutAwayModal], ); const printQrcode = useCallback( async (row: any) => { setBtnIsLoading(true); console.log(row.id); const postData = { stockInLineIds: [row.id] }; // const postData = { stockInLineIds: [42,43,44] }; const response = await fetchPoQrcode(postData); if (response) { console.log(response); downloadFile(new Uint8Array(response.blobValue), response.filename!); } setBtnIsLoading(false); }, [], ); const getButtonSx = (sil: StockInLineRow) => { const status = sil?.status?.toLowerCase(); let btnSx = { label: "", color: "" }; switch (status) { case "received": btnSx = { label: t("view putaway"), color: "secondary.main" }; break; case "escalated": if (sessionToken?.id == sil?.handlerId) { btnSx = { label: t("escalation processing"), color: "warning.main" }; break; } btnSx = { label: t("qc processing"), color: "success.main" }; break; case "rejected": case "partially_completed": case "completed": btnSx = { label: t("view stockin"), color: "info.main" }; break; default: btnSx = { label: t("qc processing"), color: "success.main" }; } return btnSx; }; const columnVisibilityModel = useMemo( () => ({ uom: !isCompact, stockUom: !isCompact, }), [isCompact], ); // const handleQrCode = useCallback( // (id: GridRowId, params: any) => () => { // setRowModesModel((prev) => ({ // ...prev, // [id]: { mode: GridRowModes.View }, // })); // setModalInfo(params.row); // setTimeout(() => { // // open stock in modal // // openPutAwayModal(); // // return the record with its status as pending // // update layout // console.log("delayed"); // printQrcode(params.row); // }, 200); // }, // [printQrcode], // ); const columns = useMemo(() => { const baseColumns: GridColDef[] = [ { field: "dnNo", headerName: t("dnNo"), width: 100, minWidth: 100, }, { field: "receiptDate", headerName: t("receiptDate"), width: 125, renderCell: (params) => arrayToDateString(params.value), }, { field: "productLotNo", headerName: t("productLotNo"), width: 110, minWidth: 110, }, { field: "purchaseAcceptedQty", headerName: t("acceptedQty"), width: PURCHASE_QTY_COLUMN_WIDTH, minWidth: PURCHASE_QTY_COLUMN_WIDTH, flex: 0, align: "right", headerAlign: "right", type: "number", renderCell: (params) => { const qty = params.row.purchaseAcceptedQty ?? 0; return integerFormatter.format(qty); }, }, { field: "uom", headerName: t("uom"), width: UOM_COLUMN_WIDTH, minWidth: UOM_COLUMN_WIDTH, flex: 0, renderCell: () => { const text = itemDetail.uom?.udfudesc ?? "-"; return ( {text} ); }, }, { field: "stockQty", headerName: t("Stock In Qty"), width: STOCK_QTY_COLUMN_WIDTH, minWidth: STOCK_QTY_COLUMN_WIDTH, flex: 0, type: "number", align: "left", headerAlign: "left", renderCell: (params) => { const stockQty = Number(params.row.acceptedQty ?? 0); return decimalFormatter.format(stockQty); }, }, { field: "stockUom", headerName: t("Stock UoM"), width: UOM_COLUMN_WIDTH, minWidth: UOM_COLUMN_WIDTH, flex: 0, renderCell: () => { const text = itemDetail.stockUom.stockUomDesc ?? "-"; return ( {text} ); }, }, { field: "status", headerName: t("Status"), width: 88, renderCell: (params) => { const status = params.row.status; return ( {t(`${params.row.status}`)} ); }, }, { field: "actions", headerName: "操作", width: ACTIONS_COLUMN_WIDTH, minWidth: ACTIONS_COLUMN_WIDTH, flex: 0, sortable: false, filterable: false, disableColumnMenu: true, cellClassName: "actions", renderCell: (params) => { const data = params.row as StockInLineRow; const btnSx = getButtonSx(data); const status = (data.status ?? "").toLowerCase(); const canEmail = status === "rejected" || status === "partially_completed"; const canPrint = status === "received"; const canDelete = canDeleteStockInLine(data); const needsStockQtyRound = needsPoQcStockQtyRound( data.purchaseOrderLineId, data.status, data.acceptedQty, ); return ( { void handleNewQC(params.row.id, params)(); }} canEmail={canEmail} canPrint={canPrint} canDelete={canDelete} onEmail={() => handleMailTemplateForStockInLine(params.row.id as number) } onPrint={() => printQrcode(params.row)} onDelete={() => handleSoftDelete(data)} btnIsLoading={btnIsLoading} isDeleting={isDeleting} needsStockQtyRound={needsStockQtyRound} stockQty={Number(data.acceptedQty ?? 0)} isRounding={roundingSilId === data.id} onRound={(mode) => handleRoundStockQty(data, mode)} /> ); }, }, ]; return baseColumns.map((col) => { const grow = COLUMN_GROW[col.field] ?? 0; if (grow > 0) { return { ...col, flex: grow, minWidth: col.minWidth ?? col.width, width: undefined, }; } return { ...col, flex: 0, width: col.width, minWidth: col.minWidth ?? col.width, }; }); }, [ t, itemDetail, handleNewQC, handleMailTemplateForStockInLine, printQrcode, handleSoftDelete, handleRoundStockQty, roundingSilId, btnIsLoading, isDeleting, sessionToken?.id, ]); const unsortableColumns = useMemo(() => columns.map(column => ({ ...column, sortable: false })) , [columns]); const addRow = useCallback(() => { console.log(itemDetail); const newEntry = { id: Date.now(), _isNew: true, itemId: itemDetail.itemId, purchaseOrderId: itemDetail.purchaseOrderId, purchaseOrderLineId: itemDetail.id, itemNo: itemDetail.itemNo, itemName: itemDetail.itemName, // User inputs qty in M18 unit; backend will convert to stock unit on create. purchaseAcceptedQty: itemDetail.qty - currQty, uom: itemDetail.uom, status: "draft", }; setEntries((e) => [...e, newEntry]); setRowModesModel((model) => ({ ...model, [getRowId(newEntry)]: { mode: GridRowModes.Edit, // fieldToFocus: "projectId", }, })); }, [currQty, getRowId, itemDetail]); const validation = useCallback( ( newRow: GridRowModel, // rowModel: GridRowSelectionModel ): StockInLineEntryError | undefined => { const error: StockInLineEntryError = {}; console.log(newRow); console.log(currQty); if ( newRow.purchaseAcceptedQty && newRow.purchaseAcceptedQty > itemDetail.qty ) { error["purchaseAcceptedQty"] = t( "qty cannot be greater than remaining qty", ); } return Object.keys(error).length > 0 ? error : undefined; }, [currQty, itemDetail.qty, t], ); const processRowUpdate = useCallback( ( newRow: GridRowModel, originalRow: GridRowModel, ) => { const errors = validation(newRow); // change to validation if (errors) { throw new ProcessRowUpdateError( originalRow, "validation error", errors, ); } const { _isNew, _error, ...updatedRow } = newRow; const rowToSave = { ...updatedRow, } satisfies StockInLineRow; const newEntries = entries.map((e) => getRowId(e) === getRowId(originalRow) ? rowToSave : e, ); setStockInLine(newEntries as StockInLine[]); console.log("triggered"); setEntries(newEntries); //update remaining qty const total = newEntries.reduce( (acc, curr) => acc + (curr.purchaseAcceptedQty || 0), 0, ); setCurrQty(total); return rowToSave; }, [validation, entries, setStockInLine, getRowId], ); const onProcessRowUpdateError = useCallback( (updateError: ProcessRowUpdateError) => { const errors = updateError.errors; const oldRow = updateError.row; apiRef.current.updateRows([{ ...oldRow, _error: errors }]); }, [apiRef], ); const footer = ( <> {/* */} ); const getRowHeight = useCallback(() => STOCK_IN_ROW_HEIGHT, []); return ( <> [ [ `& .MuiDataGrid-cell[data-field="${field}"]`, { px: 1 }, ], [ `& .MuiDataGrid-columnHeader[data-field="${field}"]`, { px: 1 }, ], ]), ), }} disableColumnMenu editMode="row" rows={entries} rowModesModel={rowModesModel} onRowModesModelChange={setRowModesModel} processRowUpdate={processRowUpdate} onProcessRowUpdateError={onProcessRowUpdateError} columns={unsortableColumns} isCellEditable={(params) => { const status = params.row.status.toLowerCase(); return ( stockInLineStatusMap[status] >= 0 || stockInLineStatusMap[status] <= 1 ); }} getCellClassName={(params: GridCellParams) => { let classname = ""; if (params.row._error) { classname = "hasError"; } return classname; }} slots={{ footer: FooterToolbar, noRowsOverlay: NoRowsOverlay, }} slotProps={{ footer: { child: footer }, }} /> {/* {modalInfo !== undefined && ( */} <> {/* ) } */} ); } const NoRowsOverlay: React.FC = () => { const { t } = useTranslation("purchaseOrder"); return ( {t("Add some entries!")} ); }; const FooterToolbar: React.FC = ({ child }) => { return {child}; }; export default PoInputGrid;