|
- "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, PurchaseQcResult } 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 { 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 PoQcStockInModal from "./PoQcStockInModal"
- import DoDisturbIcon from "@mui/icons-material/DoDisturb";
-
- interface ResultWithId {
- id: number;
- }
-
- interface Props {
- qc: QcItemWithChecks[];
- setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
- setStockInLine: Dispatch<SetStateAction<StockInLine[]>>;
- setProcessedQty: Dispatch<SetStateAction<number>>;
- 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,
- setStockInLine,
- setProcessedQty,
- itemDetail,
- stockInLine,
- warehouse,
- }: Props) {
- console.log(itemDetail);
- const { t } = useTranslation("home");
- const apiRef = useGridApiRef();
- const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({});
- const getRowId = useCallback<GridRowIdGetter<StockInLineRow>>(
- (row) => row.id as number,
- []
- );
- console.log(stockInLine);
- const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []);
- const [modalInfo, setModalInfo] = useState<
- StockInLine & { qcResult?: PurchaseQcResult[] }
- >();
- 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 [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) => () => {
- 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,
- 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))
- );
- setStockInLine(
- (prev) =>
- prev.map((p) =>
- p.id === oldId ? (res.entity as StockInLine) : p
- ) as StockInLine[]
- );
- setBtnIsLoading(false);
- // do post directly to test
- // openStartModal();
- }, 200);
- },
- [createStockInLine]
- );
- const fetchQcDefaultValue = useCallback(async (stockInLineId: GridRowId) => {
- return await fetchQcResult(stockInLineId as number);
- }, []);
-
- const handleQC = useCallback(
- (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(qcResult);
- setModalInfo({
- ...params.row,
- qcResult: qcResult,
- });
- // set default values
- setTimeout(() => {
- // open qc modal
- console.log("delayed");
- openQcModal();
- setBtnIsLoading(false);
- }, 200);
- },
- [fetchQcDefaultValue]
- );
- 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);
- },
- []
- );
-
- 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);
- },
- []
- );
-
- 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);
- },
- []
- );
-
- 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);
- },
- []
- );
-
- 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);
- },
- [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 closeRejectModal = useCallback(() => {
- setRejectOpen(false);
- }, []);
- const openRejectModal = useCallback(() => {
- setRejectOpen(true);
- }, []);
-
- const columns = useMemo<GridColDef[]>(
- () => [
- {
- field: "itemNo",
- flex: 0.4,
- },
- {
- field: "itemName",
- flex: 0.6,
- },
- {
- 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.5,
- cellClassName: "actions",
- getActions: (params) => {
- console.log(params.row.status);
- const status = params.row.status.toLowerCase();
- return [
- <GridActionsCellItem
- icon={<PlayArrowIcon />}
- 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"
- />,
- <GridActionsCellItem
- icon={<FactCheckIcon />}
- label="qc"
- sx={{
- color: "primary.main",
- // marginRight: 1,
- }}
- disabled={
- // stockInLineStatusMap[status] === 9 ||
- stockInLineStatusMap[status] < 1
- }
- // set _isNew to false after posting
- // or check status
- onClick={handleQC(params.row.id, params)}
- color="inherit"
- key="edit"
- />,
- <GridActionsCellItem
- icon={<NotificationImportantIcon />}
- label="escalation"
- sx={{
- color: "primary.main",
- // marginRight: 1,
- }}
- disabled={
- stockInLineStatusMap[status] === 9 ||
- 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"
- />,
- <GridActionsCellItem
- icon={<ShoppingCartIcon />}
- label="stockin"
- sx={{
- color: "primary.main",
- // marginRight: 1,
- }}
- disabled={
- stockInLineStatusMap[status] === 9 ||
- stockInLineStatusMap[status] <= 2 ||
- stockInLineStatusMap[status] >= 7
- }
- // set _isNew to false after posting
- // or check status
- onClick={handleStockIn(params.row.id, params)}
- color="inherit"
- key="edit"
- />,
- <GridActionsCellItem
- icon={<ShoppingCartIcon />}
- label="putaway"
- sx={{
- color: "primary.main",
- // marginRight: 1,
- }}
- disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] < 7}
- // set _isNew to false after posting
- // or check status
- onClick={handlePutAway(params.row.id, params)}
- color="inherit"
- key="edit"
- />,
- <GridActionsCellItem
- icon={<QrCodeIcon />}
- label="putaway"
- sx={{
- color: "primary.main",
- // marginRight: 1,
- }}
- disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8}
- // set _isNew to false after posting
- // or check status
- onClick={handleQrCode(params.row.id, params)}
- color="inherit"
- key="edit"
- />,
- <GridActionsCellItem
- icon={
- stockInLineStatusMap[status] >= 1 ? (
- <DoDisturbIcon />
- ) : (
- <DeleteIcon />
- )
- }
- label="Delete"
- sx={{
- color: "error.main",
- }}
- disabled={
- stockInLineStatusMap[status] >= 7 &&
- stockInLineStatusMap[status] <= 9
- }
- onClick={
- stockInLineStatusMap[status] === 0
- ? handleDelete(params.row.id)
- : handleReject(params.row.id, params)
- }
- color="inherit"
- key="edit"
- />,
- ];
- },
- },
- ],
- [stockInLineStatusMap, btnIsLoading, handleQrCode, handleReject]
- );
-
- 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<StockInLineRow>
- // 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<StockInLineRow>,
- originalRow: GridRowModel<StockInLineRow>
- ) => {
- 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.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]
- );
-
- const footer = (
- <Box display="flex" gap={2} alignItems="center">
- <Button
- disableRipple
- variant="outlined"
- startIcon={<Add />}
- disabled={itemDetail.qty - currQty <= 0}
- onClick={addRow}
- size="small"
- >
- {t("Record pol")}
- </Button>
- </Box>
- );
-
- return (
- <>
- <StyledDataGrid
- getRowId={getRowId}
- apiRef={apiRef}
- autoHeight
- sx={{
- "--DataGrid-overlayHeight": "100px",
- ".MuiDataGrid-row .MuiDataGrid-cell.hasError": {
- border: "1px solid",
- borderColor: "error.main",
- },
- ".MuiDataGrid-row .MuiDataGrid-cell.hasWarning": {
- border: "1px solid",
- borderColor: "warning.main",
- },
- }}
- disableColumnMenu
- editMode="row"
- rows={entries}
- rowModesModel={rowModesModel}
- onRowModesModelChange={setRowModesModel}
- processRowUpdate={processRowUpdate}
- onProcessRowUpdateError={onProcessRowUpdateError}
- columns={columns}
- isCellEditable={(params) => {
- const status = params.row.status.toLowerCase();
- return (
- stockInLineStatusMap[status] >= 0 ||
- stockInLineStatusMap[status] <= 1
- );
- }}
- getCellClassName={(params: GridCellParams<StockInLineRow>) => {
- let classname = "";
- if (params.row._error) {
- classname = "hasError";
- }
- return classname;
- }}
- slots={{
- footer: FooterToolbar,
- noRowsOverlay: NoRowsOverlay,
- }}
- slotProps={{
- footer: { child: footer },
- }}
- />
- {modalInfo !== undefined && (
- <>
- <PoQcStockInModal
- type={"qc"}
- // setRows={setRows}
- setEntries={setEntries}
- setStockInLine={setStockInLine}
- setItemDetail={setModalInfo}
- qc={qc}
- open={qcOpen}
- onClose={closeQcModal}
- itemDetail={modalInfo}
- />
- </>
- )}
- {modalInfo !== undefined && (
- <>
- <PoQcStockInModal
- type={"escalation"}
- // setRows={setRows}
- setEntries={setEntries}
- setStockInLine={setStockInLine}
- setItemDetail={setModalInfo}
- // qc={qc}
- open={escalOpen}
- onClose={closeEscalationModal}
- itemDetail={modalInfo}
- />
- </>
- )}
- {modalInfo !== undefined && (
- <>
- <PoQcStockInModal
- type={"reject"}
- // setRows={setRows}
- setEntries={setEntries}
- setStockInLine={setStockInLine}
- setItemDetail={setModalInfo}
- // qc={qc}
- open={rejectOpen}
- onClose={closeRejectModal}
- itemDetail={modalInfo}
- />
- </>
- )}
- {modalInfo !== undefined && (
- <>
- <PoQcStockInModal
- type={"stockIn"}
- // setRows={setRows}
- setEntries={setEntries}
- setStockInLine={setStockInLine}
- // qc={qc}
- setItemDetail={setModalInfo}
- open={stockInOpen}
- onClose={closeStockInModal}
- itemDetail={modalInfo}
- />
- </>
- )}
- {modalInfo !== undefined && (
- <>
- <PoQcStockInModal
- type={"putaway"}
- // setRows={setRows}
- setEntries={setEntries}
- setStockInLine={setStockInLine}
- setItemDetail={setModalInfo}
- open={putAwayOpen}
- warehouse={warehouse}
- onClose={closePutAwayModal}
- itemDetail={modalInfo}
- />
- </>
- )}
- </>
- );
- }
- const NoRowsOverlay: React.FC = () => {
- const { t } = useTranslation("home");
- return (
- <Box
- display="flex"
- justifyContent="center"
- alignItems="center"
- height="100%"
- >
- <Typography variant="caption">{t("Add some entries!")}</Typography>
- </Box>
- );
- };
-
- const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => {
- return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>;
- };
- export default PoInputGrid;
|