Browse Source

bug fix + ui

master
MSI\derek 1 month ago
parent
commit
ff13aea37f
8 changed files with 150 additions and 150 deletions
  1. +7
    -0
      src/app/utils/commonUtil.ts
  2. +14
    -8
      src/components/InputDataGrid/InputDataGrid.tsx
  3. +2
    -1
      src/components/PickOrderSearch/ConsolidatedPickOrders.tsx
  4. +39
    -36
      src/components/PickOrderSearch/CreateForm.tsx
  5. +2
    -2
      src/components/PickOrderSearch/CreatePickOrderModal.tsx
  6. +5
    -30
      src/components/PoDetail/PoDetail.tsx
  7. +80
    -72
      src/components/PoDetail/PoInputGrid.tsx
  8. +1
    -1
      src/i18n/zh/purchaseOrder.json

+ 7
- 0
src/app/utils/commonUtil.ts View File

@@ -26,3 +26,10 @@ export const convertObjToURLSearchParams = <T extends object>(


return params.toString(); return params.toString();
}; };

export const getCustomWidth = (): number => {
const LIMIT_WIDTH = 1000
const CUSTOM_WIDTH = 1100
if (window.innerWidth < LIMIT_WIDTH) return CUSTOM_WIDTH
return window.innerWidth
}

+ 14
- 8
src/components/InputDataGrid/InputDataGrid.tsx View File

@@ -37,7 +37,9 @@ import {
GridApiCommunity, GridApiCommunity,
GridSlotsComponentsProps, GridSlotsComponentsProps,
} from "@mui/x-data-grid/internals"; } from "@mui/x-data-grid/internals";

// T == CreatexxxInputs map of the form's fields
// V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc
// E == error
interface ResultWithId { interface ResultWithId {
id: string | number; id: string | number;
} }
@@ -97,7 +99,7 @@ export class ProcessRowUpdateError<T, E> extends Error {
Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); Object.setPrototypeOf(this, ProcessRowUpdateError.prototype);
} }
} }
// T == CreatexxxInputs
// T == CreatexxxInputs map of the form's fields
// V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc // V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc
// E == error // E == error
function InputDataGrid<T, V, E>({ function InputDataGrid<T, V, E>({
@@ -126,7 +128,11 @@ function InputDataGrid<T, V, E>({
const list: TableRow<V, E>[] = getValues(formKey); const list: TableRow<V, E>[] = getValues(formKey);
return list && list.length > 0 ? list : []; return list && list.length > 0 ? list : [];
}); });
const originalRows = list && list.length > 0 ? list : [];
// const originalRows = list && list.length > 0 ? list : [];
const originalRows = useMemo(() => (
list && list.length > 0 ? list : []
), [list])
// const originalRowModel = originalRows.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel // const originalRowModel = originalRows.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel
const [rowSelectionModel, setRowSelectionModel] = const [rowSelectionModel, setRowSelectionModel] =
useState<GridRowSelectionModel>(() => { useState<GridRowSelectionModel>(() => {
@@ -154,7 +160,7 @@ function InputDataGrid<T, V, E>({
console.log(errors); console.log(errors);
apiRef.current.updateRows([{ ...row, _error: errors }]); apiRef.current.updateRows([{ ...row, _error: errors }]);
}, },
[apiRef, rowModesModel],
[apiRef],
); );


const processRowUpdate = useCallback( const processRowUpdate = useCallback(
@@ -202,7 +208,7 @@ function InputDataGrid<T, V, E>({
const reset = useCallback(() => { const reset = useCallback(() => {
setRowModesModel({}); setRowModesModel({});
setRows(originalRows); setRows(originalRows);
}, []);
}, [originalRows]);


const handleCancel = useCallback( const handleCancel = useCallback(
(id: GridRowId) => () => { (id: GridRowId) => () => {
@@ -219,14 +225,14 @@ function InputDataGrid<T, V, E>({
); );
} }
}, },
[setRowModesModel, rows],
[rows, getRowId],
); );


const handleDelete = useCallback( const handleDelete = useCallback(
(id: GridRowId) => () => { (id: GridRowId) => () => {
setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id)); setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id));
}, },
[],
[getRowId],
); );


const _columns = useMemo<GridColDef[]>( const _columns = useMemo<GridColDef[]>(
@@ -281,7 +287,7 @@ function InputDataGrid<T, V, E>({
// console.log(formKey) // console.log(formKey)
// console.log(rows) // console.log(rows)
setValue(formKey, rows); setValue(formKey, rows);
}, [formKey, rows]);
}, [formKey, rows, setValue]);


const footer = ( const footer = (
<Box display="flex" gap={2} alignItems="center"> <Box display="flex" gap={2} alignItems="center">


+ 2
- 1
src/components/PickOrderSearch/ConsolidatedPickOrders.tsx View File

@@ -64,7 +64,8 @@ const style = {
pt: 5, pt: 5,
px: 5, px: 5,
pb: 10, pb: 10,
width: 1500,
// width: 1500,
width: { xs: "100%", sm: "100%", md: "100%" },
}; };
interface DisableButton { interface DisableButton {
releaseBtn: boolean; releaseBtn: boolean;


+ 39
- 36
src/components/PickOrderSearch/CreateForm.tsx View File

@@ -107,6 +107,7 @@ const CreateForm: React.FC<Props> = ({ items }) => {
{ {
field: "itemId", field: "itemId",
headerName: t("Item"), headerName: t("Item"),
// width: 100,
flex: 1, flex: 1,
editable: true, editable: true,
valueFormatter(params) { valueFormatter(params) {
@@ -162,6 +163,7 @@ const CreateForm: React.FC<Props> = ({ items }) => {
{ {
field: "qty", field: "qty",
headerName: t("qty"), headerName: t("qty"),
// width: 100,
flex: 1, flex: 1,
type: "number", type: "number",
editable: true, editable: true,
@@ -181,6 +183,7 @@ const CreateForm: React.FC<Props> = ({ items }) => {
{ {
field: "uom", field: "uom",
headerName: t("uom"), headerName: t("uom"),
// width: 100,
flex: 1, flex: 1,
editable: true, editable: true,
// renderEditCell(params: GridRenderEditCellParams<PolRow>) { // renderEditCell(params: GridRenderEditCellParams<PolRow>) {
@@ -257,42 +260,42 @@ const CreateForm: React.FC<Props> = ({ items }) => {
</FormControl> </FormControl>
</Grid> </Grid>
<Grid item xs={6}> <Grid item xs={6}>
<Controller
control={control}
name="targetDate"
// rules={{ required: !Boolean(productionDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("targetDate")}
value={targetDate ? dayjs(targetDate) : undefined}
onChange={(date) => {
console.log(date);
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("targetDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.targetDate?.message),
helperText: errors.targetDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Controller
control={control}
name="targetDate"
// rules={{ required: !Boolean(productionDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("targetDate")}
value={targetDate ? dayjs(targetDate) : undefined}
onChange={(date) => {
console.log(date);
if (!date) return;
console.log(date.format(INPUT_DATE_FORMAT));
setValue("targetDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.targetDate?.message),
helperText: errors.targetDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
</Grid> </Grid>
<Grid <Grid
container container


+ 2
- 2
src/components/PickOrderSearch/CreatePickOrderModal.tsx View File

@@ -21,7 +21,7 @@ const style = {
px: 5, px: 5,
pb: 10, pb: 10,
display: "block", display: "block",
width: { xs: "60%", sm: "60%", md: "60%" },
width: { xs: "100%", sm: "100%", md: "100%" },
}; };


interface Props extends Omit<ModalProps, "children"> { interface Props extends Omit<ModalProps, "children"> {
@@ -62,7 +62,7 @@ const CreatePickOrderModal: React.FC<Props> = ({
return ( return (
<> <>
<FormProvider {...formProps}> <FormProvider {...formProps}>
<Modal open={open} onClose={closeHandler} sx={{ overflowY: "scroll" }}>
<Modal open={open} onClose={closeHandler}>
<Box <Box
sx={style} sx={style}
component="form" component="form"


+ 5
- 30
src/components/PoDetail/PoDetail.tsx View File

@@ -41,41 +41,27 @@ import {
fetchStockInLineInfo, fetchStockInLineInfo,
PurchaseQcResult, PurchaseQcResult,
startPo, startPo,
testFetch,
} from "@/app/api/po/actions"; } from "@/app/api/po/actions";
import { import {
use,
useCallback, useCallback,
useContext, useContext,
useEffect, useEffect,
useMemo, useMemo,
useState, useState,
} from "react"; } from "react";
import { FormProvider, useForm } from "react-hook-form";
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown";
import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp";
import InputDataGrid, {
TableRow as InputTableRow,
} from "../InputDataGrid/InputDataGrid";
import PoInputGrid from "./PoInputGrid"; import PoInputGrid from "./PoInputGrid";
import { QcItemWithChecks } from "@/app/api/qc"; import { QcItemWithChecks } from "@/app/api/qc";
import { useSearchParams } from "next/navigation"; import { useSearchParams } from "next/navigation";
import { WarehouseResult } from "@/app/api/warehouse"; import { WarehouseResult } from "@/app/api/warehouse";
import { calculateWeight, returnWeightUnit } from "@/app/utils/formatUtil"; import { calculateWeight, returnWeightUnit } from "@/app/utils/formatUtil";
import QrCodeScanner from "../QrCodeScanner";
import { CameraDevice, Html5Qrcode } from "html5-qrcode";
import { CameraContext } from "../Cameras/CameraProvider"; import { CameraContext } from "../Cameras/CameraProvider";
import StyledDataGrid from "../StyledDataGrid";
import { QrCodeInfo } from "@/app/api/qrcode";
import { fetchQcResult } from "@/app/api/qc/actions";
import PoQcStockInModal from "./PoQcStockInModal"; import PoQcStockInModal from "./PoQcStockInModal";
import ReactQrCodeScannerModal, {
ScannerConfig,
} from "../ReactQrCodeScanner/ReactQrCodeScanner";
import QrModal from "./QrModal"; import QrModal from "./QrModal";
import { PlayArrow } from "@mui/icons-material"; import { PlayArrow } from "@mui/icons-material";
import DoneIcon from "@mui/icons-material/Done"; import DoneIcon from "@mui/icons-material/Done";
import { QrCode } from "../QrCode";
import { getCustomWidth } from "@/app/utils/commonUtil";


type Props = { type Props = {
po: PoResult; po: PoResult;
@@ -272,8 +258,9 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
}; };
// break; // break;
} }
}, [purchaseOrder, handleStartPo, handleCompletePo]);
}, [purchaseOrder.status, t, handleStartPo, handleCompletePo]);


console.log(window.innerWidth)
return ( return (
<> <>
<Stack <Stack
@@ -301,20 +288,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
{buttonData.buttonText} {buttonData.buttonText}
</Button> </Button>
</Grid> </Grid>
{/* {purchaseOrder.status.toLowerCase() === "pending" && (
<Grid item>
<Button onClick={handleStartPo}>Start</Button>
</Grid>
)}
{purchaseOrder.status.toLowerCase() === "receiving" && (
<Grid item>
<Button onClick={handleCompletePo}>Complete</Button>
</Grid>
)} */}
</Grid> </Grid>
{/* <Grid container xs={12} justifyContent="space-between">
<Button onClick={handleCompletePo}>Complete</Button>
</Grid> */}
<Grid container xs={12} justifyContent="space-between"> <Grid container xs={12} justifyContent="space-between">
<Grid item xs={8}> <Grid item xs={8}>
<Tabs <Tabs
@@ -346,7 +320,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
</Grid> </Grid>
{/* tab 1 */} {/* tab 1 */}
<Grid sx={{ display: tabIndex === 0 ? "block" : "none" }}> <Grid sx={{ display: tabIndex === 0 ? "block" : "none" }}>
<TableContainer component={Paper}>
<TableContainer component={Paper} sx={{ width: 'fit-content', overflow: 'auto' }}>
{/* <TableContainer component={Paper} sx={{width: getCustomWidth(), overflow: 'auto' }}> */}
<Table aria-label="collapsible table" stickyHeader> <Table aria-label="collapsible table" stickyHeader>
<TableHead> <TableHead>
<TableRow> <TableRow>


+ 80
- 72
src/components/PoDetail/PoInputGrid.tsx View File

@@ -147,7 +147,7 @@ function PoInputGrid({
0, 0,
); );
setProcessedQty(processedQty); setProcessedQty(processedQty);
}, [entries]);
}, [entries, setProcessedQty]);


const handleDelete = useCallback( const handleDelete = useCallback(
(id: GridRowId) => () => { (id: GridRowId) => () => {
@@ -155,6 +155,42 @@ function PoInputGrid({
}, },
[getRowId], [getRowId],
); );
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( const handleStart = useCallback(
(id: GridRowId, params: any) => () => { (id: GridRowId, params: any) => () => {
setBtnIsLoading(true); setBtnIsLoading(true);
@@ -189,7 +225,7 @@ function PoInputGrid({
// openStartModal(); // openStartModal();
}, 200); }, 200);
}, },
[createStockInLine],
[setStockInLine],
); );
const fetchQcDefaultValue = useCallback(async (stockInLineId: GridRowId) => { const fetchQcDefaultValue = useCallback(async (stockInLineId: GridRowId) => {
return await fetchQcResult(stockInLineId as number); return await fetchQcResult(stockInLineId as number);
@@ -217,7 +253,7 @@ function PoInputGrid({
setBtnIsLoading(false); setBtnIsLoading(false);
}, 200); }, 200);
}, },
[fetchQcDefaultValue],
[fetchQcDefaultValue, openQcModal],
); );
const handleEscalation = useCallback( const handleEscalation = useCallback(
(id: GridRowId, params: any) => () => { (id: GridRowId, params: any) => () => {
@@ -234,7 +270,7 @@ function PoInputGrid({
// setBtnIsLoading(false); // setBtnIsLoading(false);
}, 200); }, 200);
}, },
[],
[openEscalationModal],
); );


const handleReject = useCallback( const handleReject = useCallback(
@@ -254,7 +290,7 @@ function PoInputGrid({
// printQrcode(params.row); // printQrcode(params.row);
}, 200); }, 200);
}, },
[],
[openRejectModal],
); );


const handleStockIn = useCallback( const handleStockIn = useCallback(
@@ -274,7 +310,7 @@ function PoInputGrid({
// setBtnIsLoading(false); // setBtnIsLoading(false);
}, 200); }, 200);
}, },
[],
[openStockInModal],
); );


const handlePutAway = useCallback( const handlePutAway = useCallback(
@@ -294,7 +330,7 @@ function PoInputGrid({
// setBtnIsLoading(false); // setBtnIsLoading(false);
}, 200); }, 200);
}, },
[],
[openPutAwayModal],
); );


const printQrcode = useCallback( const printQrcode = useCallback(
@@ -310,79 +346,47 @@ function PoInputGrid({
} }
setBtnIsLoading(false); 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 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<GridColDef[]>( const columns = useMemo<GridColDef[]>(
() => [ () => [
{ {
field: "itemNo", field: "itemNo",
headerName: t("itemNo"), headerName: t("itemNo"),
flex: 0.4,
width: 120,
// flex: 0.4,
}, },
{ {
field: "itemName", field: "itemName",
headerName: t("itemName"), headerName: t("itemName"),
flex: 0.6,
width: 120,
// flex: 0.6,
}, },
{ {
field: "acceptedQty", field: "acceptedQty",
headerName: t("acceptedQty"), headerName: t("acceptedQty"),
flex: 0.5,
// flex: 0.5,
width: 120,
type: "number", type: "number",
// editable: true, // editable: true,
// replace with tooltip + content // replace with tooltip + content
@@ -390,7 +394,8 @@ function PoInputGrid({
{ {
field: "uom", field: "uom",
headerName: t("uom"), headerName: t("uom"),
flex: 0.5,
width: 120,
// flex: 0.5,
renderCell: (params) => { renderCell: (params) => {
return params.row.uom.code; return params.row.uom.code;
}, },
@@ -398,7 +403,8 @@ function PoInputGrid({
{ {
field: "weight", field: "weight",
headerName: t("weight"), headerName: t("weight"),
flex: 0.5,
width: 120,
// flex: 0.5,
renderCell: (params) => { renderCell: (params) => {
const weight = calculateWeight( const weight = calculateWeight(
params.row.acceptedQty, params.row.acceptedQty,
@@ -411,7 +417,8 @@ function PoInputGrid({
{ {
field: "status", field: "status",
headerName: t("status"), headerName: t("status"),
flex: 0.5,
width: 120,
// flex: 0.5,
renderCell: (params) => { renderCell: (params) => {
return t(`${params.row.status}`); return t(`${params.row.status}`);
}, },
@@ -423,7 +430,8 @@ function PoInputGrid({
"stock in", "stock in",
)} | ${t("putaway")} | ${t("delete")}`, )} | ${t("putaway")} | ${t("delete")}`,
// headerName: "start | qc | escalation | stock in | putaway | delete", // headerName: "start | qc | escalation | stock in | putaway | delete",
flex: 1.5,
width: 300,
// flex: 1.5,
cellClassName: "actions", cellClassName: "actions",
getActions: (params) => { getActions: (params) => {
// console.log(params.row.status); // console.log(params.row.status);
@@ -494,7 +502,7 @@ function PoInputGrid({
(stockInLineStatusMap[status] >= 3 && (stockInLineStatusMap[status] >= 3 &&
stockInLineStatusMap[status] <= 5 && stockInLineStatusMap[status] <= 5 &&
!session?.user?.abilities?.includes("APPROVAL")) !session?.user?.abilities?.includes("APPROVAL"))
}
}
// set _isNew to false after posting // set _isNew to false after posting
// or check status // or check status
onClick={handleStockIn(params.row.id, params)} onClick={handleStockIn(params.row.id, params)}
@@ -560,7 +568,7 @@ function PoInputGrid({
}, },
}, },
], ],
[stockInLineStatusMap, btnIsLoading, handleQrCode, handleReject],
[t, handleStart, handleQC, handleEscalation, session?.user?.abilities, handleStockIn, handlePutAway, handleDelete, handleReject],
); );


const addRow = useCallback(() => { const addRow = useCallback(() => {
@@ -585,7 +593,7 @@ function PoInputGrid({
// fieldToFocus: "projectId", // fieldToFocus: "projectId",
}, },
})); }));
}, [currQty, getRowId]);
}, [currQty, getRowId, itemDetail]);
const validation = useCallback( const validation = useCallback(
( (
newRow: GridRowModel<StockInLineRow>, newRow: GridRowModel<StockInLineRow>,
@@ -599,7 +607,7 @@ function PoInputGrid({
} }
return Object.keys(error).length > 0 ? error : undefined; return Object.keys(error).length > 0 ? error : undefined;
}, },
[currQty],
[currQty, itemDetail.qty, t],
); );
const processRowUpdate = useCallback( const processRowUpdate = useCallback(
( (
@@ -632,7 +640,7 @@ function PoInputGrid({
setCurrQty(total); setCurrQty(total);
return rowToSave; return rowToSave;
}, },
[getRowId, entries],
[validation, entries, setStockInLine, getRowId],
); );


const onProcessRowUpdateError = useCallback( const onProcessRowUpdateError = useCallback(


+ 1
- 1
src/i18n/zh/purchaseOrder.json View File

@@ -27,7 +27,7 @@
"total weight": "總重量", "total weight": "總重量",
"weight unit": "重量單位", "weight unit": "重量單位",
"price": "價格", "price": "價格",
"processed": "已入倉",
"processed": "已處理",
"expiryDate": "到期日", "expiryDate": "到期日",
"acceptedQty": "接受數量", "acceptedQty": "接受數量",
"weight": "重量", "weight": "重量",


Loading…
Cancel
Save