浏览代码

update escalation tab

update stock in modal
master
kelvinsuen 3 个月前
父节点
当前提交
ed419e0953
共有 9 个文件被更改,包括 229 次插入183 次删除
  1. +56
    -29
      src/components/CollapsibleCard/CollapsibleCard.tsx
  2. +2
    -0
      src/components/DashboardPage/DashboardPage.tsx
  3. +17
    -2
      src/components/DashboardPage/escalation/EscalationLogTable.tsx
  4. +1
    -1
      src/components/PoDetail/PoInputGrid.tsx
  5. +1
    -51
      src/components/PoDetail/PutAwayForm.tsx
  6. +22
    -11
      src/components/PoDetail/QcComponent.tsx
  7. +122
    -86
      src/components/PoDetail/QcStockInModal.tsx
  8. +2
    -1
      src/i18n/zh/dashboard.json
  9. +6
    -2
      src/i18n/zh/purchaseOrder.json

+ 56
- 29
src/components/CollapsibleCard/CollapsibleCard.tsx 查看文件

@@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { createContext, useContext, useState, useCallback, Dispatch, SetStateAction } from "react";
import {
Card,
CardHeader,
@@ -7,6 +7,7 @@ import {
Collapse,
Checkbox,
Box,
FormControlLabel,
} from "@mui/material";
import ExpandMoreIcon from "@mui/icons-material/ExpandMore";
import ExpandLessIcon from "@mui/icons-material/ExpandLess";
@@ -18,6 +19,19 @@ interface CollapsibleCardProps {
showFilter?: boolean;
filterText?: string;
}
interface CardFilterContextType {
filter: boolean;
onFilterChange: (checked: boolean) => void;
filterText: string;
setOnFilterChange: (override: () => void) => void;
}

export const CardFilterContext = createContext<CardFilterContextType>({
filter: false,
onFilterChange: (checked: boolean) => {},
filterText: "FilterText",
setOnFilterChange: () => {}, //Not working yet
});

const CollapsibleCard: React.FC<CollapsibleCardProps> = ({
title,
@@ -28,38 +42,51 @@ const CollapsibleCard: React.FC<CollapsibleCardProps> = ({
}) => {

const [filter, setFilter] = useState(false);
const [open, setOpen] = useState(defaultOpen);
const [onFilterChange, setOnFilterChange] = useState<() => void>();

const onFilterChange = (bool : boolean) => {
setFilter(bool);
const handleFilterChange = useCallback((checked : boolean) => {
if (onFilterChange) {
onFilterChange();
}

setFilter(checked);
setOpen(true);
}
const [open, setOpen] = useState(defaultOpen);
}, [onFilterChange]);

return (
<Card>
<CardHeader
title={title}
action={
<Box display="flex" justifyContent="flex-end" alignItems="center" gap={1}>
{showFilter && (
<>
<Checkbox
sx={{textAlign: "left"}}
checked={filter}
onChange={(e) => {onFilterChange(e.target.checked);}}
// disabled={!isEmpty(pickOrder.consoCode)}
/> <span style={{paddingRight: 20}}>{filterText}</span>
</>)}
<IconButton onClick={() => setOpen((v) => !v)}>
{open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
</Box>
}
/>
<Collapse in={open}>
<CardContent>{children}</CardContent>
</Collapse>
</Card>
<CardFilterContext.Provider value={{
filter, onFilterChange: handleFilterChange, filterText, setOnFilterChange
}}>
<Card>
<CardHeader
title={title}
action={
<Box display="flex" justifyContent="flex-end" alignItems="center" gap={1}>
{showFilter && (
<>
<FormControlLabel
control={
<Checkbox
checked={filter}
onChange={(e) => handleFilterChange(e.target.checked)}
/>
}
label={filterText} // Use filterText as the label
sx={{ marginRight: 2 }} // Optional: Adjust spacing
/>
</>)}
<IconButton onClick={() => setOpen((v) => !v)}>
{open ? <ExpandLessIcon /> : <ExpandMoreIcon />}
</IconButton>
</Box>
}
/>
<Collapse in={open}>
<CardContent>{children}</CardContent>
</Collapse>
</Card>
</CardFilterContext.Provider>
);
};



+ 2
- 0
src/components/DashboardPage/DashboardPage.tsx 查看文件

@@ -46,6 +46,8 @@ const DashboardPage: React.FC<Props> = ({
<CollapsibleCard
title={`${t("Responsible Escalation List")} (${t("pending")} : ${
getPendingLog().length > 0 ? getPendingLog().length : t("No")})`}
showFilter={true}
filterText={t("show completed logs")}
// defaultOpen={getPendingLog().length > 0} // TODO Fix default not opening
>
<CardContent>


+ 17
- 2
src/components/DashboardPage/escalation/EscalationLogTable.tsx 查看文件

@@ -2,13 +2,14 @@

import { Box, Card, CardActionArea, CardContent, CardHeader, Grid, Paper, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Typography } from "@mui/material";
import { useRouter } from "next/navigation";
import { useCallback, useMemo, useState } from "react";
import { useCallback, useContext, useEffect, useMemo, useState } from "react";
import { usePathname } from "next/navigation";
import { useTranslation } from "react-i18next";
import { EscalationResult } from "@/app/api/escalation";
import { Column } from "@/components/SearchResults";
import SearchResults from "@/components/SearchResults/SearchResults";
import { arrayToDateString, arrayToDateTimeString } from "@/app/utils/formatUtil";
import { CardFilterContext } from "@/components/CollapsibleCard/CollapsibleCard";

export type IQCItems = {
id: number;
@@ -36,6 +37,20 @@ const EscalationLogTable: React.FC<Props> = ({
const router = useRouter();
const [selectedId, setSelectedId] = useState<number | null>(null);

const [escalationLogs, setEscalationLogs] = useState<EscalationResult[]>([]);
const useCardFilter = useContext(CardFilterContext);
const showCompleted = useCardFilter.filter;
useEffect(() => {
if (showCompleted) {
setEscalationLogs(items);
} else {
const filteredEscLog = items.filter(log => log.status == "pending");
setEscalationLogs(filteredEscLog);
}
}, [showCompleted, items])
const navigateTo = useCallback(
(item: EscalationResult) => {
setSelectedId(item.id);
@@ -237,7 +252,7 @@ const EscalationLogTable: React.FC<Props> = ({
return (
<SearchResults
onRowClick={onRowClick}
items={items}
items={escalationLogs}
columns={getColumnByType(type)}
isAutoPaging={false}
/>


+ 1
- 1
src/components/PoDetail/PoInputGrid.tsx 查看文件

@@ -443,7 +443,7 @@ const closeNewModal = useCallback(() => {
const status = sil?.status?.toLowerCase();
let btnSx = {label:"", color:""};
switch (status) {
case "received": btnSx = {label: t("putaway processing"), color:"secondary.main"}; break;
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;}


+ 1
- 51
src/components/PoDetail/PutAwayForm.tsx 查看文件

@@ -198,56 +198,6 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled, setRowM
[],
);

const onOpenScanner = useCallback(() => {
setOpenScanner(true);
}, []);

const onCloseScanner = useCallback(() => {
setOpenScanner(false);
}, []);
const scannerConfig = useMemo<ScannerConfig>(
() => ({
onUpdate: (err, result) => {
console.log(result);
console.log(Boolean(result));
if (result) {
const data: QrCodeInfo = JSON.parse(result.getText());
console.log(data);
if (data.warehouseId) {
console.log(data.warehouseId);
setWarehouseId(data.warehouseId);
onCloseScanner();
}
} else return;
},
}),
[onCloseScanner],
);

// QR Code Scanner
const scanner = useQrCodeScannerContext();
useEffect(() => {
if (isOpenScanner) {
scanner.startScan();
} else if (!isOpenScanner) {
scanner.stopScan();
}
}, [isOpenScanner]);

useEffect(() => {
if (scanner.values.length > 0) {
console.log(scanner.values[0]);
const data: QrCodeInfo = JSON.parse(scanner.values[0]);
console.log(data);
if (data.warehouseId) {
console.log(data.warehouseId);
setWarehouseId(data.warehouseId);
onCloseScanner();
}
scanner.resetScan();
}
}, [scanner.values]);

useEffect(() => {
setValue("status", "received");
// setValue("status", "completed");
@@ -536,7 +486,7 @@ const PutAwayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled, setRowM
{/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */}
<InputDataGrid<PutAwayInput, PutAwayLine, EntryError>
apiRef={apiRef}
checkboxSelection={true}
checkboxSelection={false}
_formKey={"putAwayLines"}
columns={columns}
validateRow={validation}


+ 22
- 11
src/components/PoDetail/QcComponent.tsx 查看文件

@@ -239,13 +239,19 @@ const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => {
{
field: "code",
headerName: t("qcItem"),
wrapText: true,
flex: 2,
renderCell: (params) => {
const index = params.api.getRowIndexRelativeToVisibleRows(params.id) + 1;
return (
<Box>
<Box
sx={{
lineHeight: 1.5,
padding: "4px",
}}
>
<b>{`${index}. ${params.value}`}</b><br/>
{params.row.name}<br/>
{params.row.name}
</Box>
)},
},
@@ -433,7 +439,7 @@ const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => {
const setDefaultQcDecision = (status : string | undefined) => {
const param = status?.toLowerCase();
if (param !== undefined && param !== null) {
if (param == "completed" || param == "partially_completed") {
if (param == "received" || param == "completed" || param == "partially_completed") {
return 1;
} else if (param == "rejected") {
return 2;
@@ -456,6 +462,13 @@ const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => {
// return row.id || `${row.name}-${Math.random().toString(36).substr(2, 9)}`;
};

const getRowHeight = (row :any) => { // Not used?
console.log("row", row);
if (!row.model.name) {
return (row.model.name.length ?? 10) * 1.2 + 30;
} else { return 60}
};

return (
<>
<Grid container justifyContent="flex-start" alignItems="flex-start">
@@ -504,8 +517,10 @@ const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => {
rows={qcResult}
// rows={qcResult && qcResult.length > 0 ? qcResult : qcItems}
// rows={disabled? qcResult:qcItems}
autoHeight
// autoHeight
sortModel={[]}
// getRowHeight={getRowHeight}
getRowHeight={() => 'auto'}
getRowId={getRowId}
/>
</Grid>
@@ -548,17 +563,13 @@ const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => {
sortModel={[]}
/>
</CollapsibleCard>
{/* <StyledDataGrid
rows={escalationHistory}
columns={columns}
onRowSelectionModelChange={(newRowSelectionModel) => {
setRowSelectionModel(newRowSelectionModel);
}}
/> */}
</Grid>
</>
)}
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Qc Decision")}
</Typography>
<FormControl>
<Controller
name="qcDecision"


+ 122
- 86
src/components/PoDetail/QcStockInModal.tsx 查看文件

@@ -6,10 +6,14 @@ import {
Autocomplete,
Box,
Button,
Divider,
Grid,
Modal,
ModalProps,
Stack,
Tab,
Tabs,
TabsProps,
TextField,
Typography,
} from "@mui/material";
@@ -34,6 +38,7 @@ import { GridRowModesModel } from "@mui/x-data-grid";
import { isEmpty } from "lodash";
import { EscalationCombo } from "@/app/api/user";
import { truncateSync } from "fs";
import PutAwayGrid from "./PutAwayGrid";


const style = {
@@ -47,7 +52,7 @@ const style = {
pb: 10,
display: "block",
width: { xs: "90%", sm: "90%", md: "90%" },
// height: { xs: "60%", sm: "60%", md: "60%" },
height: { xs: "90%", sm: "90%", md: "90%" },
};
interface CommonProps extends Omit<ModalProps, "children"> {
// setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
@@ -95,6 +100,14 @@ const PoQcStockInModalVer2: React.FC<Props> = ({

// Select Printer
const [selectedPrinter, setSelectedPrinter] = useState(printerCombo[0]);
const [tabIndex, setTabIndex] = useState(0);

const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
(_e, newValue) => {
setTabIndex(newValue);
},
[],
);

const defaultNewValue = useMemo(() => {
return (
@@ -150,6 +163,8 @@ const [qcItems, setQcItems] = useState(dummyQCData)
setViewOnly(isViewOnly)
}
console.log("Modal ItemDetail updated:", itemDetail);
console.log("%c SHOW PUTAWAY? ", "color:lime", showPutaway);
if (showPutaway) { setTabIndex(1); } else { setTabIndex(0); }
}, [itemDetail]);

useEffect(() => {
@@ -158,8 +173,7 @@ const [qcItems, setQcItems] = useState(dummyQCData)
})

setQcItems(dummyQCData);
setOpenPutaway(isPutaway);
// setOpenPutaway(isPutaway);
}, [open])

const [openPutaway, setOpenPutaway] = useState(false);
@@ -322,7 +336,9 @@ const [qcItems, setQcItems] = useState(dummyQCData)
if (qcData.qcAccept) {
// submitDialogWithWarning(onOpenPutaway, t, {title:"Save success, confirm to proceed?",
// confirmButtonText: t("confirm putaway"), html: ""});
onOpenPutaway();
// onOpenPutaway();
closeHandler({}, "backdropClick");
// setTabIndex(1); // Need to go Putaway tab?
} else {
closeHandler({}, "backdropClick");
}
@@ -463,6 +479,11 @@ const [qcItems, setQcItems] = useState(dummyQCData)
const acceptQty = formProps.watch("acceptedQty")

const showPutaway = useMemo(() => {
const status = itemDetail.status;
return status !== "pending" && status !== "escalated" && status !== "rejected";
}, [itemDetail]);

const checkQcIsPassed = useCallback((qcItems: PurchaseQcResult[]) => {
const isPassed = qcItems.every((qc) => qc.qcPassed);
console.log(isPassed)
@@ -492,88 +513,36 @@ const [qcItems, setQcItems] = useState(dummyQCData)
overflowY: "auto",
marginLeft: 3,
marginRight: 3,
// overflow: "hidden",
}}
>
{openPutaway ? (
<Box
component="form"
onSubmit={formProps.handleSubmit(onSubmitPutaway)}
<Box sx={{ position: 'sticky', top: 0, bgcolor: 'background.paper',
zIndex: 5, borderBottom: 2, borderColor: 'divider', width: "100%" }}>
<Tabs
value={tabIndex}
onChange={handleTabChange}
variant="scrollable"
>
<PutAwayForm
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={viewOnly}
setRowModesModel={setPafRowModesModel}
setRowSelectionModel={setPafRowSelectionModel}
/>
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Autocomplete
disableClearable
options={printerCombo}
defaultValue={selectedPrinter}
onChange={(event, value) => {
setSelectedPrinter(value)
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={t("Printer")}
sx={{ width: 300}}
/>
)}
/>
<Button
id="printButton"
type="button"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={handlePrint}
disabled={isPrinting || printerCombo.length <= 0 || pafSubmitDisable}
>
{isPrinting ? t("Printing") : t("print")}
</Button>
<Button
id="putawaySubmit"
type="submit"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitPutaway)}
disabled={pafSubmitDisable}
>
{t("confirm putaway")}
</Button>
</Stack>
</Box>
) : (
<>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("qc processing")}
</Typography>
</Grid>
<Grid item xs={12}>
<StockInForm itemDetail={itemDetail} disabled={viewOnly} />
</Grid>
<Tab label={
showPutaway ? t("dn and qc info") : t("qc processing")
} iconPosition="end" />
{showPutaway && <Tab label={t("putaway processing")} iconPosition="end" />}
</Tabs>
</Box>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
>
<Grid item xs={12}>
{tabIndex === 0 && <>
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Delivery Detail")}
</Typography>
</Grid>
{/* <Stack direction="row" justifyContent="flex-end" gap={1}>
<Button
id="stockInSubmit"
type="button"
variant="contained"
color="primary"
onClick={formProps.handleSubmit(onSubmitStockIn)}
>
{t("submitStockIn")}
</Button>
</Stack> */}
<StockInForm itemDetail={itemDetail} disabled={viewOnly || showPutaway} />

<Grid
container
justifyContent="flex-start"
@@ -582,13 +551,13 @@ const [qcItems, setQcItems] = useState(dummyQCData)
<QcComponent
// qc={qc!}
itemDetail={itemDetail}
disabled={viewOnly}
disabled={viewOnly || showPutaway}
// qcItems={qcItems}
// setQcItems={setQcItems}
/>
</Grid>
<Stack direction="row" justifyContent="flex-end" gap={1}>
{!viewOnly && (<Button
{(!viewOnly && !showPutaway) && (<Button
id="qcSubmit"
type="button"
variant="contained"
@@ -599,8 +568,75 @@ const [qcItems, setQcItems] = useState(dummyQCData)
{t("confirm qc result")}
</Button>)}
</Stack>
</>
)}
</>}

{tabIndex === 1 &&
<Box
// component="form"
// onSubmit={formProps.handleSubmit(onSubmitPutaway)}
>
<Grid
container
justifyContent="flex-start"
alignItems="flex-start"
>
<PutAwayForm
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={viewOnly}
setRowModesModel={setPafRowModesModel}
setRowSelectionModel={setPafRowSelectionModel}
/>
{/* <PutAwayGrid
itemDetail={itemDetail}
warehouse={warehouse!}
disabled={viewOnly}
/> */}
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Autocomplete
disableClearable
options={printerCombo}
defaultValue={selectedPrinter}
onChange={(event, value) => {
setSelectedPrinter(value)
}}
renderInput={(params) => (
<TextField
{...params}
variant="outlined"
label={t("Printer")}
sx={{ width: 300}}
/>
)}
/>
<Button
id="printButton"
type="button"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={handlePrint}
disabled={isPrinting || printerCombo.length <= 0 || pafSubmitDisable}
>
{isPrinting ? t("Printing") : t("print")}
</Button>
{/* <Button
id="putawaySubmit"
type="submit"
variant="contained"
color="primary"
sx={{ mt: 1 }}
onClick={formProps.handleSubmit(onSubmitPutaway)}
disabled={pafSubmitDisable}
>
{t("confirm putaway")}
</Button> */}
</Stack>
</Grid>
</Box>
}
</Grid>
</Grid>
</Box>
</Modal>
</FormProvider>


+ 2
- 1
src/i18n/zh/dashboard.json 查看文件

@@ -54,5 +54,6 @@
"escalated datetime": "上報時間",
"escalateFrom": "上報同事",
"No": "無",
"Responsible Escalation List": "負責的上報列表"
"Responsible Escalation List": "負責的上報列表",
"show completed logs": "顯示已完成上報"
}

+ 6
- 2
src/i18n/zh/purchaseOrder.json 查看文件

@@ -100,6 +100,7 @@
"Default Warehouse": "預設倉庫",
"Select warehouse": "選擇倉庫",
"Putaway Detail": "上架詳情",
"Delivery Detail": "來貨詳情",
"LotNo": "批號",
"Po Code": "採購訂單編號",
"No Warehouse": "沒有倉庫",
@@ -109,7 +110,8 @@
"Accept submit": "接受來貨",
"qc processing": "處理來貨及品檢",
"putaway processing": "處理來貨及上架",
"view stockin": "查看收貨及品檢",
"view stockin": "查看收貨詳情",
"view putaway": "查看上架詳情",
"putawayBtn": "上架",
"dnNo": "送貨單編號",
"dnDate": "送貨單日期",
@@ -144,5 +146,7 @@
"No Option": "沒有選項",
"receivedTotal": "已來貨總數",
"QC Record": "品檢記錄",
"value must be integer": "請輸入整數"
"value must be integer": "請輸入整數",
"dn and qc info": "來貨及品檢詳情",
"Qc Decision": "品檢詳情"
}

正在加载...
取消
保存