"use client"; import { Box, Button, ButtonProps, Card, CardContent, CardHeader, CircularProgress, Grid, Stack, Tooltip, Typography, } from "@mui/material"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import { useTranslation } from "react-i18next"; import StyledDataGrid from "../StyledDataGrid"; import { useCallback, useEffect, useMemo, useState } from "react"; import { GridColDef, GridRowId, GridRowIdGetter, GridRowModel, GridRowModes, useGridApiRef, GridRenderEditCellParams, GridEditInputCell, GridRowParams, } from "@mui/x-data-grid"; import DoneIcon from "@mui/icons-material/Done"; import { GridRowSelectionModel } from "@mui/x-data-grid"; import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider"; import { completeConsoPickOrder, CreateStockOutLine, createStockOutLine, fetchConsoStatus, fetchPickOrderLineClient, fetchStockOutLineClient, PickOrderApprovalInput, PickOrderQcInput, } from "@/app/api/pickOrder/actions"; import { PickOrderLineWithSuggestedLot, StockOutLine, } from "@/app/api/pickOrder"; import { Pageable } from "@/app/utils/fetchUtil"; import { QrCodeInfo } from "@/app/api/qrcode"; import { QrCode } from "../QrCode"; import { fetchLotDetail, LotLineInfo } from "@/app/api/inventory/actions"; import { GridRowModesModel } from "@mui/x-data-grid"; import { stockOutLineStatusMap } from "@/app/utils/formatUtil"; import { GridActionsCellItem } from "@mui/x-data-grid"; import DoDisturbIcon from "@mui/icons-material/DoDisturb"; import useUploadContext from "../UploadProvider/useUploadContext"; import { FitAllCell } from "@/app/utils/gridUtil"; import { QcItemWithChecks } from "@/app/api/qc"; import QcForm from "./QcForm"; import { fetchPickOrderQcResult, QcResult } from "@/app/api/qc/actions"; import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; import AutoFixNormalIcon from "@mui/icons-material/AutoFixNormal"; import ApprovalForm from "./ApprovalForm"; import InfoIcon from "@mui/icons-material/Info"; import VerifiedIcon from "@mui/icons-material/Verified"; import { isNullOrUndefined } from "html5-qrcode/esm/core"; interface Props { qc: QcItemWithChecks[]; consoCode: string; } interface IsLoadingModel { pickOrderLineTable: boolean; stockOutLineTable: boolean; } export type StockOutLineEntryError = { [field in keyof StockOutLine]?: string; }; export type StockOutLineRow = Partial< StockOutLine & { id: number; isActive: boolean | undefined; _isNew: boolean; _error: StockOutLineEntryError; } >; class ProcessRowUpdateError extends Error { public readonly row: StockOutLineRow; public readonly errors: StockOutLineEntryError | undefined; constructor( row: StockOutLineRow, message?: string, errors?: StockOutLineEntryError, ) { super(message); this.row = row; this.errors = errors; Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); } } export type formDefaultValues = StockOutLine & (PickOrderQcInput | PickOrderApprovalInput); const PickOrderDetail: React.FC = ({ consoCode, qc }) => { const { t } = useTranslation("pickOrder"); const apiRef = useGridApiRef(); const [qcResult, setQcResult] = useState([] as QcResult[]); const [selectedRow, setSelectedRow] = useState([]); const [currPol, setCurrPol] = useState(); const [isChangeLotSolId, setIsChangeLotSolId] = useState( undefined, ); const [formDefaultValues, setFormDefaultValues] = useState(); const [isLoadingModel, setIsLoadingModel] = useState({ pickOrderLineTable: false, stockOutLineTable: false, }); const [polCriteriaArgs, setPolCriteriaArgs] = useState({ pageNum: 1, pageSize: 10, }); const [solCriteriaArgs, setSolCriteriaArgs] = useState({ pageNum: 1, pageSize: 10, }); const [polTotalCount, setPolTotalCount] = useState(0); const [solTotalCount, setSolTotalCount] = useState(0); const [rowModesModel, setRowModesModel] = useState({}); const [btnIsLoading, setBtnIsLoading] = useState(false); const { setIsUploading } = useUploadContext(); const [pickOrderLine, setPickOrderLine] = useState< PickOrderLineWithSuggestedLot[] >([]); const pickOrderLineColumns = useMemo( () => [ { field: "id", headerName: "pickOrderLineId", flex: 1, }, { field: "itemName", headerName: "itemId", flex: 1, }, { field: "qty", headerName: "qty", flex: 1, }, { field: "uom", headerName: "uom", flex: 1, }, // { // field: "lotLineId", // headerName: "lotLineId", // flex: 1, // }, { field: "warehouse", headerName: "location", flex: 1, renderCell: (params) => { // console.log(params.row.warehouse) return params.row.warehouse; if (isNullOrUndefined(params.row.warehouse)) { return <>; } else { const warehouseList = JSON.parse( `{${params.row.warehouse}}`, ) as string[]; return FitAllCell(warehouseList); } }, }, { field: "suggestedLotNo", headerName: "suggestedLotNo", flex: 1.2, renderCell: (params) => { return params.row.suggestedLotNo; if (isNullOrUndefined(params.row.suggestedLotNo)) return <>; const suggestedLotNoList = JSON.parse( params.row.suggestedLotNo, ) as string[]; return FitAllCell(suggestedLotNoList); }, }, { field: "status", headerName: t("status"), flex: 0.5, renderCell: (params) => { return t(`${params.row.status}`); }, }, ], [], ); const [isDisableComplete, setIsDisableComplete] = useState(true); const [status, setStatus] = useState(""); const getConsoStatus = useCallback(async () => { const status = await fetchConsoStatus(consoCode); console.log(status); setStatus(status.status); }, [fetchConsoStatus]); const fetchPickOrderLine = useCallback( async (params: Record) => { setIsLoadingModel((prev) => ({ ...prev, pickOrderLineTable: true, })); const res = await fetchPickOrderLineClient({ ...params, consoCode: consoCode, }); if (res) { console.log(res); console.log(res.records.every((line) => line.status == "completed")); setIsDisableComplete(res.records[0].poStatus === "completed"); // setIsDisableComplete(() => // res.records.every((line) => line.status !== "completed"), // ); setPickOrderLine(res.records); setPolTotalCount(res.total); } else { console.log("error"); console.log(res); } setIsLoadingModel((prev) => ({ ...prev, pickOrderLineTable: false, })); }, [fetchPickOrderLineClient, consoCode], ); const [stockOutLine, setStockOutLine] = useState([]); const getRowId = useCallback>( (row) => row.id as number, [], ); const [qcOpen, setQcOpen] = useState(false); const [approvalOpen, setApprovalOpen] = useState(false); const closeQcModal = useCallback(() => { setQcOpen(false); }, []); const openQcModal = useCallback(() => { setQcOpen(true); }, []); const closeApprovalModal = useCallback(() => { setApprovalOpen(false); }, []); const openApprovalModal = useCallback(() => { setApprovalOpen(true); }, []); const triggerRefetch = useCallback(() => { setSelectedRow((prev) => prev); }, []); const handleDelete = useCallback( (id: GridRowId) => () => { setStockOutLine((prev) => prev.filter((e) => getRowId(e) !== id)); }, [getRowId], ); const handleStart = useCallback( (id: GridRowId, params: any) => () => { setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); setTimeout(async () => { // post stock in line const oldId = params.row.id; console.log(params.row); // console.log(currPol); const postData = { consoCode: consoCode, pickOrderLineId: params.row.pickOrderLineId, inventoryLotLineId: params.row.inventoryLotLineId, qty: params.row.qty, } as CreateStockOutLine; console.log(postData); // return console.log("triggering"); const res = await createStockOutLine(postData); if (res) { console.log(res); setStockOutLine((prev) => prev.map((p) => p.id === oldId ? (res.entity as StockOutLine) : p, ), ); } setBtnIsLoading(false); // do post directly to test // openStartModal(); }, 500); }, [createStockOutLine], ); useEffect(() => { console.log(stockOutLine); }, [stockOutLine]); const handleApproval = useCallback( (id: GridRowId, params: any) => async () => { setBtnIsLoading(true); console.log(params.row.qty); console.log(params.row); setFormDefaultValues({ ...(params.row as StockOutLine), status: "lot-change", } as StockOutLine & PickOrderApprovalInput); setTimeout(() => { // open qc modal console.log("delayed"); openApprovalModal(); setBtnIsLoading(false); }, 200); }, [], ); const handleLotChange = useCallback( (id: GridRowId, params: GridRowParams) => async () => { setOpenScanner((prev) => !prev); console.log(id); setIsChangeLotSolId((prev) => { if (prev != undefined) return undefined; return id as number; }); }, [], ); useEffect(() => { console.log(isChangeLotSolId); }, [isChangeLotSolId]); const handleComplete = useCallback( (id: GridRowId, params: any) => async () => { setBtnIsLoading(true); setRowModesModel((prev) => ({ ...prev, [id]: { mode: GridRowModes.View }, })); getQcResult(id as number).then((qcResult) => { setQcResult(qcResult); }); console.log(params.row.qty); console.log(params.row); setFormDefaultValues({ ...(params.row as StockOutLine), qty: params.row.qty, status: "completed", } as StockOutLine & PickOrderQcInput); setTimeout(() => { // open qc modal console.log("delayed"); openQcModal(); setBtnIsLoading(false); }, 200); }, [], ); const stockOutLineColumns = useMemo( () => [ { field: "itemName", headerName: "item name", flex: 1, }, { field: "qty", headerName: "qty", editable: true, flex: 1, type: "number", // renderEditCell(params: GridRenderEditCellParams) { // const errorMessage = // params.row._error?.[params.field as keyof StockOutLineEntryError]; // const content = ( // // ); // return errorMessage ? ( // // {content} // // ) : ( // content // ); // }, }, { field: "lotNo", headerName: "lotNo", flex: 1, }, { field: "status", headerName: t("status"), flex: 0.5, renderCell: (params) => { return t(`${params.row.status}`); }, }, { field: "actions", type: "actions", headerName: `${t("start")} | ${t("approval")} | ${t("lot change")} | ${t("checkout")} | ${t("delete")}`, flex: 1.5, cellClassName: "actions", getActions: (params) => { const status = params.row.status.toLowerCase(); return [ } label="start" sx={{ color: "primary.main", // marginRight: 1, }} disabled={!(stockOutLineStatusMap[status] === 0)} // set _isNew to false after posting // or check status onClick={handleStart(params.row.id, params)} color="inherit" key="edit" />, } label="approval" sx={{ color: "primary.main", // marginRight: 1, }} disabled={stockOutLineStatusMap[status] !== 2} // set _isNew to false after posting // or check status onClick={handleApproval(params.row.id, params)} // start scanning for that row color="inherit" key="edit" />, } label="lot change" /// sx={{ color: "primary.main", // marginRight: 1, }} disabled={stockOutLineStatusMap[status] !== 3} // set _isNew to false after posting // or check status onClick={handleLotChange(params.row.id, params)} // start scanning for that row color="inherit" key="edit" />, } label="qcAndPick" sx={{ color: "primary.main", // marginRight: 1, }} disabled={ !params.row.inventoryLotLineId || stockOutLineStatusMap[status] === 2 || stockOutLineStatusMap[status] >= 4 || stockOutLineStatusMap[status] === 0 } // set _isNew to false after posting // or check status onClick={handleComplete(params.row.id, params)} color="inherit" key="edit" />, } label="Delete" sx={{ color: "error.main", }} disabled={stockOutLineStatusMap[status] > 0} onClick={handleDelete(params.row.id)} />, // } // label="debug button" // sx={{ // color: "error.main", // }} // onClick={() => console.log(params.row)} // />, ]; }, }, ], [stockOutLineStatusMap, handleStart, handleDelete], ); const fetchStockOutLine = useCallback( async (params: Record, selectedRow: GridRowSelectionModel) => { const _selectedRow = selectedRow as number[]; console.log(params); console.log(_selectedRow); // fetch const res = await fetchStockOutLineClient(_selectedRow[0]); console.log(res); // set state setStockOutLine(res); }, [], ); const addRow = useCallback( (qrcode: LotLineInfo) => { const newEntry = { id: Date.now(), _isNew: true, itemId: qrcode.itemId, itemName: qrcode.itemName, itemNo: qrcode.itemNo, lotNo: qrcode.lotNo, inventoryLotLineId: qrcode.inventoryLotLineId, qty: 0, pickOrderLineId: selectedRow[0] as number, status: "draft", }; setStockOutLine((prev) => [...prev, newEntry]); setRowModesModel((model) => ({ ...model, [getRowId(newEntry)]: { mode: GridRowModes.Edit, }, })); }, [getRowId, selectedRow], ); // need modify this later const changeRow = useCallback( (id: number, qrcode: LotLineInfo) => { console.log(stockOutLine); console.log(stockOutLine.find((line) => line.id === id)); const rowToSave = { ...stockOutLine.find((line) => line.id === id), itemId: qrcode.itemId, itemName: qrcode.itemName, itemNo: qrcode.itemNo, lotNo: qrcode.lotNo, inventoryLotLineId: qrcode.inventoryLotLineId, }; console.log(rowToSave); const newEntries = stockOutLine.map((e) => getRowId(e) === id ? rowToSave : e, ); setStockOutLine(newEntries as StockOutLine[]); }, [stockOutLine, getRowId], ); /// handle refetch all data useEffect(() => { if (!qcOpen || !approvalOpen) { triggerRefetch(); getConsoStatus(); fetchPickOrderLine(polCriteriaArgs); // getConsoStatus() } if (selectedRow.length > 0) fetchStockOutLine(solCriteriaArgs, selectedRow); }, [ qcOpen, approvalOpen, solCriteriaArgs, selectedRow, triggerRefetch, polCriteriaArgs, getConsoStatus, ]); const getLotDetail = useCallback( async (stockInLineId: number): Promise => { const res = await fetchLotDetail(stockInLineId); console.log("res"); console.log(res); return res; }, [fetchLotDetail], ); const getQcResult = useCallback( async (stockOutLineId: number): Promise => { const res = await fetchPickOrderQcResult(stockOutLineId); console.log("res"); console.log(res); return res; }, [fetchPickOrderQcResult], ); const [isOpenScanner, setOpenScanner] = useState(false); const onOpenScanner = useCallback(() => { setOpenScanner((prev) => !prev); }, []); const scanner = useQrCodeScannerContext(); useEffect(() => { if (isOpenScanner && !scanner.isScanning) { scanner.startScan(); } else if (!isOpenScanner && scanner.isScanning) { scanner.stopScan(); } }, [isOpenScanner]); const homemade_Qrcode = { // stockInLineId: 156, // eggs // stockInLineId: 162, // chicken wings stockInLineId: 168, // sesame }; useEffect(() => { if (scanner.values.length > 0) { console.log(scanner.values[0]); const data: QrCodeInfo = JSON.parse(scanner.values[0]); console.log(data); if (data.stockInLineId) { setIsUploading(true); // fetch getLotDetail(data.stockInLineId).then((qrcode) => { // add row if (isChangeLotSolId) { changeRow(isChangeLotSolId, qrcode); } else { addRow(qrcode); } }); setIsUploading(false); } scanner.resetScan(); } }, [ isChangeLotSolId, scanner.values, selectedRow, changeRow, addRow, getLotDetail, ]); const mannuallyAddRow = useCallback(() => { getLotDetail(homemade_Qrcode.stockInLineId).then((qrcode) => { addRow(qrcode); // scanner.resetScan(); }); }, [addRow, homemade_Qrcode]); const validation = useCallback( ( newRow: GridRowModel, // rowModel: GridRowSelectionModel ): StockOutLineEntryError | undefined => { const error: StockOutLineEntryError = {}; const checkQty = currPol?.qty; console.log(newRow); if (!newRow.qty || newRow.qty <= 0) { error["qty"] = t("illegal qty"); } return Object.keys(error).length > 0 ? error : undefined; }, [currPol], ); const processRowUpdate = useCallback( ( newRow: GridRowModel, originalRow: GridRowModel, ) => { const errors = validation(newRow); // change to validation console.log(newRow); if (errors) { throw new ProcessRowUpdateError( originalRow, "validation error", errors, ); } const { _isNew, _error, ...updatedRow } = newRow; const rowToSave = { ...updatedRow, } satisfies StockOutLineRow; console.log(rowToSave); const newEntries = stockOutLine.map((e) => getRowId(e) === getRowId(originalRow) ? rowToSave : e, ); console.log(newEntries); setStockOutLine(newEntries as StockOutLine[]); return rowToSave; }, [stockOutLine, validation], ); const onProcessRowUpdateError = useCallback( (updateError: ProcessRowUpdateError) => { const errors = updateError.errors; const oldRow = updateError.row; apiRef.current.updateRows([{ ...oldRow, _error: errors }]); }, [apiRef], ); const handleCompleteOrder = useCallback(async () => { const res = await completeConsoPickOrder(consoCode); if (res.message === "completed") { console.log(res); // completed triggerRefetch(); // setIsCompletedOrder(false) } else { // not completed triggerRefetch(); } }, [consoCode, triggerRefetch, completeConsoPickOrder]); return ( <> {consoCode} - {status} {/* */} {/* homemade qrcode for testing purpose */} {/* */} {isLoadingModel.pickOrderLineTable && pickOrderLine == undefined ? ( ) : ( { setSelectedRow(newRowSelectionModel); if (newRowSelectionModel && newRowSelectionModel.length > 0) { const pol = pickOrderLine.find( (item) => item.id === newRowSelectionModel[0], ); console.log(pol); setCurrPol(pol); } }} initialState={{ pagination: { paginationModel: { pageSize: 10, page: 0 }, }, }} pageSizeOptions={[10, 25, 50, 100]} onPaginationModelChange={async (model, details) => { setPolCriteriaArgs({ pageNum: model.page + 1, pageSize: model.pageSize, }); }} rowCount={polTotalCount} /> )} {isChangeLotSolId} { const status = params.row.status.toLowerCase(); return ( stockOutLineStatusMap[status] === 0 || stockOutLineStatusMap[status] === 3 ); }} initialState={{ pagination: { paginationModel: { pageSize: 10, page: 0 }, }, }} pageSizeOptions={[10, 25, 50, 100]} onPaginationModelChange={async (model, details) => { setSolCriteriaArgs({ pageNum: model.page + 1, pageSize: model.pageSize, }); }} rowCount={solTotalCount} /> {/* modals */} {qcOpen && formDefaultValues != undefined && ( )} {approvalOpen && formDefaultValues != undefined && ( )} ); }; export default PickOrderDetail;