"use client"; import { FooterPropsOverrides, GridActionsCellItem, GridCellParams, GridRowId, GridRowIdGetter, GridRowModel, GridRowModes, GridRowModesModel, GridToolbarContainer, useGridApiRef, } from "@mui/x-data-grid"; import { Dispatch, MutableRefObject, SetStateAction, useCallback, useEffect, useMemo, useState, } from "react"; import StyledDataGrid from "../StyledDataGrid"; import { GridColDef } from "@mui/x-data-grid"; import { Box, Button, Grid, Typography } 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, StockInLine } from "@/app/api/po"; import { createStockInLine, testFetch } from "@/app/api/po/actions"; import { useSearchParams } from "next/navigation"; import { returnWeightUnit, calculateWeight, stockInLineStatusMap } 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 { 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"; interface ResultWithId { id: number; } interface Props { qc: QcItemWithChecks[]; setRows: Dispatch>; setProcessedQty: Dispatch>; itemDetail: PurchaseOrderLine; stockInLine: StockInLine[]; warehouse: WarehouseResult[]; } 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); } } function PoInputGrid({ qc, setRows, setProcessedQty, itemDetail, stockInLine, warehouse, }: Props) { console.log(itemDetail) const { t } = useTranslation("home"); const apiRef = useGridApiRef(); const [rowModesModel, setRowModesModel] = useState({}); const getRowId = useCallback>( (row) => row.id as number, [] ); console.log(stockInLine); const [entries, setEntries] = useState(stockInLine || []); const [modalInfo, setModalInfo] = useState(); const [qcOpen, setQcOpen] = useState(false); const [escalOpen, setEscalOpen] = useState(false); const [stockInOpen, setStockInOpen] = useState(false); const [putAwayOpen, setPutAwayOpen] = useState(false); const [currQty, setCurrQty] = useState(() => { const total = entries.reduce( (acc, curr) => acc + (curr.acceptedQty || 0), 0 ); return total; }); useEffect(() => { const completedList = entries.filter( (e) => e.status === "completed" ); const processedQty = completedList.reduce( (acc, curr) => acc + (curr.acceptedQty || 0), 0 ); setProcessedQty(processedQty); }, [entries]); const handleDelete = useCallback( (id: GridRowId) => () => { setEntries((es) => es.filter((e) => getRowId(e) !== id)); }, [getRowId] ); const handleStart = useCallback( (id: GridRowId, params: any) => () => { setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setTimeout(async () => { // post stock in line console.log("delayed"); console.log(params); const oldId = params.row.id; console.log(oldId); const postData = { itemId: params.row.itemId, itemNo: params.row.itemNo, itemName: params.row.itemName, purchaseOrderId: params.row.purchaseOrderId, purchaseOrderLineId: params.row.purchaseOrderLineId, acceptedQty: params.row.acceptedQty, }; const res = await createStockInLine(postData); console.log(res); setEntries((prev) => prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p)) ); // do post directly to test // openStartModal(); }, 200); }, [] ); const fetchQcDefaultValue = useCallback(async () => { const authHeader = axiosInstance.defaults.headers['Authorization']; if (!authHeader) { return; // Exit the function if the token is not set } console.log(authHeader) const res = await axiosInstance.get(`${NEXT_PUBLIC_API_URL}/qcResult/${itemDetail.id}`) console.log(res) }, [axiosInstance]) const handleQC = useCallback( (id: GridRowId, params: any) => async () => { setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(params.row); // await fetchQcDefaultValue() setTimeout(() => { // open qc modal console.log("delayed"); openQcModal(); }, 200); }, [] ); const handleEscalation = useCallback( (id: GridRowId, params: any) => () => { setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setModalInfo(params.row); setTimeout(() => { // open qc modal console.log("delayed"); openEscalationModal(); }, 200); }, [] ); const handleStockIn = useCallback( (id: GridRowId, params: any) => () => { 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"); }, 200); }, [] ); const handlePutAway = 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"); }, 200); }, [] ); const printQrcode = useCallback(async (row: any) => { console.log(row.id) const postData = {stockInLineIds: [row.id]} const response = await fetchPoQrcode(postData) if (response) { console.log(response) downloadFile(new Uint8Array(response.blobValue), response.filename!!) } }, [fetchPoQrcode, downloadFile]) 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); }, [] ); 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 columns = useMemo( () => [ { field: "itemNo", flex: 0.8, }, { field: "itemName", flex: 1, }, { field: "acceptedQty", headerName: "qty", flex: 0.5, type: "number", editable: true, // replace with tooltip + content }, { field: "uom", headerName: "uom", flex: 0.5, renderCell: (params) => { return params.row.uom.code; }, }, { field: "weight", headerName: "weight", flex: 0.5, renderCell: (params) => { const weight = calculateWeight(params.row.acceptedQty, params.row.uom); const weightUnit = returnWeightUnit(params.row.uom); return `${weight} ${weightUnit}`; }, }, { field: "status", flex: 0.5, // editable: true, }, { field: "actions", type: "actions", headerName: "start | qc | escalation | stock in | putaway | delete", flex: 1, cellClassName: "actions", getActions: (params) => { console.log(params.row.status); const status = params.row.status.toLowerCase(); return [ } label="start" sx={{ color: "primary.main", // marginRight: 1, }} disabled={!(stockInLineStatusMap[status] === 0)} // set _isNew to false after posting // or check status onClick={handleStart(params.row.id, params)} color="inherit" key="edit" />, } label="qc" sx={{ color: "primary.main", // marginRight: 1, }} disabled={ stockInLineStatusMap[status] < 1 } // set _isNew to false after posting // or check status onClick={handleQC(params.row.id, params)} color="inherit" key="edit" />, } label="escalation" sx={{ color: "primary.main", // marginRight: 1, }} disabled={ stockInLineStatusMap[status] <= 0 || stockInLineStatusMap[status] >= 5 } // set _isNew to false after posting // or check status onClick={handleEscalation(params.row.id, params)} color="inherit" key="edit" />, } label="stockin" sx={{ color: "primary.main", // marginRight: 1, }} disabled={stockInLineStatusMap[status] !== 6} // set _isNew to false after posting // or check status onClick={handleStockIn(params.row.id, params)} color="inherit" key="edit" />, } label="putaway" sx={{ color: "primary.main", // marginRight: 1, }} disabled={stockInLineStatusMap[status] < 7} // set _isNew to false after posting // or check status onClick={handlePutAway(params.row.id, params)} color="inherit" key="edit" />, } label="putaway" sx={{ color: "primary.main", // marginRight: 1, }} disabled={stockInLineStatusMap[status] !== 8} // set _isNew to false after posting // or check status onClick={handleQrCode(params.row.id, params)} color="inherit" key="edit" />, } label="Delete" sx={{ color: "error.main", }} disabled={stockInLineStatusMap[status] !== 0} // disabled={Boolean(params.row.status)} onClick={handleDelete(params.row.id)} color="inherit" key="edit" />, ]; }, }, ], [] ); 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, acceptedQty: itemDetail.qty - currQty, // this bug uom: itemDetail.uom, status: "draft", }; setEntries((e) => [...e, newEntry]); setRowModesModel((model) => ({ ...model, [getRowId(newEntry)]: { mode: GridRowModes.Edit, // fieldToFocus: "projectId", }, })); }, [currQty, getRowId]); const validation = useCallback( ( newRow: GridRowModel // rowModel: GridRowSelectionModel ): StockInLineEntryError | undefined => { const error: StockInLineEntryError = {}; console.log(newRow); console.log(currQty); if (newRow.acceptedQty && newRow.acceptedQty > itemDetail.qty) { error["acceptedQty"] = "qty cannot be greater than remaining qty"; } return Object.keys(error).length > 0 ? error : undefined; }, [currQty] ); 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 ); setEntries(newEntries); //update remaining qty const total = newEntries.reduce( (acc, curr) => acc + (curr.acceptedQty || 0), 0 ); setCurrQty(total); return rowToSave; }, [getRowId, entries] ); const onProcessRowUpdateError = useCallback( (updateError: ProcessRowUpdateError) => { const errors = updateError.errors; const oldRow = updateError.row; apiRef.current.updateRows([{ ...oldRow, _error: errors }]); }, [apiRef] ); // useEffect(() => { // const total = entries.reduce( // (acc, curr) => acc + (curr.acceptedQty || 0), // 0 // ); // setDefaultQty(itemDetail.qty - total); // }, [entries]); const footer = ( ); return ( <> { 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 }, }} /> <> <> <> <> ); } const NoRowsOverlay: React.FC = () => { const { t } = useTranslation("home"); return ( {t("Add some entries!")} ); }; const FooterToolbar: React.FC = ({ child }) => { return {child}; }; export default PoInputGrid;