| @@ -13,7 +13,7 @@ import { UploadProvider } from "@/components/UploadProvider/UploadProvider"; | |||||
| import SessionProviderWrapper from "@/components/SessionProviderWrapper/SessionProviderWrapper"; | import SessionProviderWrapper from "@/components/SessionProviderWrapper/SessionProviderWrapper"; | ||||
| import QrCodeScannerProvider from "@/components/QrCodeScannerProvider/QrCodeScannerProvider"; | import QrCodeScannerProvider from "@/components/QrCodeScannerProvider/QrCodeScannerProvider"; | ||||
| import { I18nProvider } from "@/i18n"; | import { I18nProvider } from "@/i18n"; | ||||
| import "src/app/global.css" | |||||
| export default async function MainLayout({ | export default async function MainLayout({ | ||||
| children, | children, | ||||
| }: { | }: { | ||||
| @@ -39,6 +39,7 @@ export interface InventoryLotLineResult { | |||||
| uom: string; | uom: string; | ||||
| qtyPerSmallestUnit: number; | qtyPerSmallestUnit: number; | ||||
| baseUom: string; | baseUom: string; | ||||
| stockInLineId: number | |||||
| } | } | ||||
| export interface InventoryLotLineItem { | export interface InventoryLotLineItem { | ||||
| @@ -38,8 +38,10 @@ export interface PurchaseQcResult { | |||||
| } | } | ||||
| export interface StockInInput { | export interface StockInInput { | ||||
| status: string; | status: string; | ||||
| poCode: string; | |||||
| productLotNo?: string; | productLotNo?: string; | ||||
| dnNo?: string; | dnNo?: string; | ||||
| itemName: string; | |||||
| invoiceNo?: string; | invoiceNo?: string; | ||||
| receiptDate: string; | receiptDate: string; | ||||
| acceptedQty: number; | acceptedQty: number; | ||||
| @@ -1,3 +1,7 @@ | |||||
| @tailwind components; | @tailwind components; | ||||
| @tailwind utilities; | |||||
| @tailwind utilities; | |||||
| html, body { | |||||
| overscroll-behavior: none; | |||||
| } | |||||
| @@ -160,6 +160,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||||
| type: "input-number", | type: "input-number", | ||||
| style: { | style: { | ||||
| textAlign: "right", | textAlign: "right", | ||||
| // width: "100px", | |||||
| }, | }, | ||||
| renderCell: (row) => { | renderCell: (row) => { | ||||
| if (typeof row.demandQty == "number") { | if (typeof row.demandQty == "number") { | ||||
| @@ -174,6 +175,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||||
| type: "read-only", | type: "read-only", | ||||
| style: { | style: { | ||||
| textAlign: "left", | textAlign: "left", | ||||
| // width: "100px", | |||||
| }, | }, | ||||
| renderCell: (row) => { | renderCell: (row) => { | ||||
| return row.uomName; | return row.uomName; | ||||
| @@ -185,6 +187,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||||
| type: "read-only", | type: "read-only", | ||||
| style: { | style: { | ||||
| textAlign: "right", | textAlign: "right", | ||||
| // width: "100px", | |||||
| }, | }, | ||||
| renderCell: (row) => { | renderCell: (row) => { | ||||
| return <ProdTimeColumn prodTimeInMinute={row.prodTimeInMinute} /> | return <ProdTimeColumn prodTimeInMinute={row.prodTimeInMinute} /> | ||||
| @@ -196,6 +199,7 @@ const ViewByFGDetails: React.FC<Props> = ({ apiRef, isEdit, type, onReleaseClick | |||||
| type: "read-only", | type: "read-only", | ||||
| style: { | style: { | ||||
| textAlign: "right", | textAlign: "right", | ||||
| // width: "100px", | |||||
| }, | }, | ||||
| // editable: true, | // editable: true, | ||||
| }, | }, | ||||
| @@ -84,9 +84,15 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| const [rows, setRows] = useState<PurchaseOrderLine[]>( | const [rows, setRows] = useState<PurchaseOrderLine[]>( | ||||
| purchaseOrder.pol || [], | purchaseOrder.pol || [], | ||||
| ); | ); | ||||
| const params = useSearchParams(); | |||||
| const searchParams = useSearchParams(); | |||||
| // const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); | // const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); | ||||
| const removeParam = (paramToRemove: string) => { | |||||
| const newParams = new URLSearchParams(searchParams.toString()); | |||||
| newParams.delete(paramToRemove); | |||||
| window.history.replaceState({}, '', `${window.location.pathname}?${newParams}`); | |||||
| }; | |||||
| const handleCompletePo = useCallback(async () => { | const handleCompletePo = useCallback(async () => { | ||||
| const checkRes = await checkPolAndCompletePo(purchaseOrder.id); | const checkRes = await checkPolAndCompletePo(purchaseOrder.id); | ||||
| console.log(checkRes); | console.log(checkRes); | ||||
| @@ -107,6 +113,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| function Row(props: { row: PurchaseOrderLine }) { | function Row(props: { row: PurchaseOrderLine }) { | ||||
| const { row } = props; | const { row } = props; | ||||
| const [firstReceiveQty, setFirstReceiveQty] = useState<number>() | |||||
| const [secondReceiveQty, setSecondReceiveQty] = useState<number>() | |||||
| const [open, setOpen] = useState(false); | const [open, setOpen] = useState(false); | ||||
| const [processedQty, setProcessedQty] = useState(row.processed); | const [processedQty, setProcessedQty] = useState(row.processed); | ||||
| const [currStatus, setCurrStatus] = useState(row.status); | const [currStatus, setCurrStatus] = useState(row.status); | ||||
| @@ -155,11 +163,30 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| <TableCell align="left">{row.price}</TableCell> | <TableCell align="left">{row.price}</TableCell> | ||||
| {/* <TableCell align="left">{row.expiryDate}</TableCell> */} | {/* <TableCell align="left">{row.expiryDate}</TableCell> */} | ||||
| <TableCell align="left">{t(`${currStatus.toLowerCase()}`)}</TableCell> | <TableCell align="left">{t(`${currStatus.toLowerCase()}`)}</TableCell> | ||||
| {/* <TableCell align="left"> | |||||
| 0 | |||||
| </TableCell> | |||||
| <TableCell align="left"> | |||||
| <TextField | |||||
| label="輸入數量" | |||||
| type="text" // Use type="text" to allow validation in the change handler | |||||
| variant="outlined" | |||||
| value={secondReceiveQty} | |||||
| // onChange={handleChange} | |||||
| InputProps={{ | |||||
| inputProps: { | |||||
| min: 0, // Optional: set a minimum value | |||||
| step: 1 // Optional: set the step for the number input | |||||
| } | |||||
| }} | |||||
| /> | |||||
| </TableCell> */} | |||||
| </TableRow> | </TableRow> | ||||
| <TableRow> | <TableRow> | ||||
| {/* <TableCell /> */} | {/* <TableCell /> */} | ||||
| <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={12}> | <TableCell style={{ paddingBottom: 0, paddingTop: 0 }} colSpan={12}> | ||||
| <Collapse in={open} timeout="auto" unmountOnExit> | |||||
| <Collapse in={true} timeout="auto" unmountOnExit> | |||||
| {/* <Collapse in={open} timeout="auto" unmountOnExit> */} | |||||
| <Table> | <Table> | ||||
| <TableBody> | <TableBody> | ||||
| <TableRow> | <TableRow> | ||||
| @@ -260,7 +287,21 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| } | } | ||||
| }, [purchaseOrder.status, t, handleStartPo, handleCompletePo]); | }, [purchaseOrder.status, t, handleStartPo, handleCompletePo]); | ||||
| console.log(window.innerWidth) | |||||
| const FIRST_IN_FIELD = "firstInQty" | |||||
| const SECOND_IN_FIELD = "secondInQty" | |||||
| const renderFieldCondition = useCallback((field: "firstInQty" | "secondInQty"): boolean => { | |||||
| switch (field) { | |||||
| case FIRST_IN_FIELD: | |||||
| return true; | |||||
| case SECOND_IN_FIELD: | |||||
| return true; | |||||
| default: | |||||
| return false; // Default case | |||||
| } | |||||
| }, []); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -277,18 +318,56 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| </Typography> | </Typography> | ||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| <Grid container xs={12} justifyContent="start"> | |||||
| <Grid item> | |||||
| <Button | |||||
| {false ? (<Grid container xs={12} justifyContent="start"> | |||||
| <Grid item xs={3}> | |||||
| <TextField | |||||
| label={t("dnNo")} | |||||
| type="text" // Use type="text" to allow validation in the change handler | |||||
| variant="outlined" | |||||
| // value={secondReceiveQty} | |||||
| // onChange={handleChange} | |||||
| InputProps={{ | |||||
| inputProps: { | |||||
| min: 0, // Optional: set a minimum value | |||||
| step: 1 // Optional: set the step for the number input | |||||
| } | |||||
| }} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={3}> | |||||
| <TextField | |||||
| label={t("dnDate")} | |||||
| type="text" // Use type="text" to allow validation in the change handler | |||||
| variant="outlined" | |||||
| defaultValue={"07/08/2025"} | |||||
| // value={secondReceiveQty} | |||||
| // onChange={handleChange} | |||||
| InputProps={{ | |||||
| inputProps: { | |||||
| min: 0, // Optional: set a minimum value | |||||
| step: 1 // Optional: set the step for the number input | |||||
| } | |||||
| }} | |||||
| /> | |||||
| {/* <Button | |||||
| onClick={buttonData.onClick} | onClick={buttonData.onClick} | ||||
| disabled={buttonData.disabled} | disabled={buttonData.disabled} | ||||
| color={buttonData.buttonColor as ButtonProps["color"]} | color={buttonData.buttonColor as ButtonProps["color"]} | ||||
| startIcon={buttonData.buttonIcon} | startIcon={buttonData.buttonIcon} | ||||
| > | > | ||||
| {buttonData.buttonText} | {buttonData.buttonText} | ||||
| </Button> | |||||
| </Button> */} | |||||
| </Grid> | </Grid> | ||||
| </Grid> | |||||
| <Grid | |||||
| item | |||||
| xs={6} | |||||
| display="flex" | |||||
| justifyContent="end" | |||||
| alignItems="end" | |||||
| > | |||||
| <Button onClick={onOpenScanner}>{t("Accept submit")}</Button> | |||||
| </Grid> | |||||
| </Grid>) : undefined} | |||||
| <Grid container xs={12} justifyContent="space-between"> | <Grid container xs={12} justifyContent="space-between"> | ||||
| <Grid item xs={8}> | <Grid item xs={8}> | ||||
| <Tabs | <Tabs | ||||
| @@ -296,7 +375,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| onChange={handleTabChange} | onChange={handleTabChange} | ||||
| variant="scrollable" | variant="scrollable" | ||||
| > | > | ||||
| <Tab label={t("General")} iconPosition="end" /> | |||||
| {/* <Tab label={t("General")} iconPosition="end" /> */} | |||||
| {/* <Tab label={t("Bind Storage")} iconPosition="end" /> */} | {/* <Tab label={t("Bind Storage")} iconPosition="end" /> */} | ||||
| </Tabs> | </Tabs> | ||||
| </Grid> | </Grid> | ||||
| @@ -315,7 +394,8 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| onClose={onCloseScanner} | onClose={onCloseScanner} | ||||
| warehouse={warehouse} | warehouse={warehouse} | ||||
| /> | /> | ||||
| <Button onClick={onOpenScanner}>{t("bind")}</Button> | |||||
| {/* <Button onClick={onOpenScanner}>{t("Accept submit")}</Button> */} | |||||
| {/* <Button onClick={onOpenScanner}>{t("bind")}</Button> */} | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| {/* tab 1 */} | {/* tab 1 */} | ||||
| @@ -336,6 +416,10 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| <TableCell align="left">{t("price")}</TableCell> | <TableCell align="left">{t("price")}</TableCell> | ||||
| {/* <TableCell align="left">{t("expiryDate")}</TableCell> */} | {/* <TableCell align="left">{t("expiryDate")}</TableCell> */} | ||||
| <TableCell align="left">{t("status")}</TableCell> | <TableCell align="left">{t("status")}</TableCell> | ||||
| {/* start == true && firstInQty == null ? no hide : hide*/} | |||||
| {/* {renderFieldCondition(FIRST_IN_FIELD) ? <TableCell align="left">{t("receivedQty")}</TableCell> : undefined} */} | |||||
| {/* start == true && firstInQty == null ? hide and disabled : no hide*/} | |||||
| {/* {renderFieldCondition(SECOND_IN_FIELD) ? <TableCell align="left">{t("dnQty")}</TableCell> : undefined} */} | |||||
| {/* <TableCell align="left">{"add icon button"}</TableCell> */} | {/* <TableCell align="left">{"add icon button"}</TableCell> */} | ||||
| </TableRow> | </TableRow> | ||||
| </TableHead> | </TableHead> | ||||
| @@ -57,6 +57,7 @@ import { fetchQcResult } from "@/app/api/qc/actions"; | |||||
| import PoQcStockInModal from "./PoQcStockInModal"; | import PoQcStockInModal from "./PoQcStockInModal"; | ||||
| import DoDisturbIcon from "@mui/icons-material/DoDisturb"; | import DoDisturbIcon from "@mui/icons-material/DoDisturb"; | ||||
| import { useSession } from "next-auth/react"; | import { useSession } from "next-auth/react"; | ||||
| import PoQcStockInModalVer2 from "./QcStockInModalVer2"; | |||||
| interface ResultWithId { | interface ResultWithId { | ||||
| id: number; | id: number; | ||||
| @@ -255,6 +256,40 @@ function PoInputGrid({ | |||||
| }, | }, | ||||
| [fetchQcDefaultValue, openQcModal], | [fetchQcDefaultValue, openQcModal], | ||||
| ); | ); | ||||
| const [newOpen, setNewOpen] = useState(false); | |||||
| const closeNewModal = useCallback(() => { | |||||
| setNewOpen(false); | |||||
| }, []); | |||||
| const openNewModal = useCallback(() => { | |||||
| setNewOpen(true); | |||||
| }, []); | |||||
| const handleNewQC = 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"); | |||||
| openNewModal(); | |||||
| setBtnIsLoading(false); | |||||
| }, 200); | |||||
| }, | |||||
| [fetchQcDefaultValue, openNewModal], | |||||
| ); | |||||
| const handleEscalation = useCallback( | const handleEscalation = useCallback( | ||||
| (id: GridRowId, params: any) => () => { | (id: GridRowId, params: any) => () => { | ||||
| // setBtnIsLoading(true); | // setBtnIsLoading(true); | ||||
| @@ -373,20 +408,38 @@ function PoInputGrid({ | |||||
| { | { | ||||
| field: "itemNo", | field: "itemNo", | ||||
| headerName: t("itemNo"), | headerName: t("itemNo"), | ||||
| width: 120, | |||||
| width: 100, | |||||
| // flex: 0.4, | |||||
| }, | |||||
| { | |||||
| field: "dnNo", | |||||
| headerName: t("dnNo"), | |||||
| width: 100, | |||||
| renderCell: () => { | |||||
| return <>DN0000001</> | |||||
| } | |||||
| // flex: 0.4, | |||||
| }, | |||||
| { | |||||
| field: "dnDate", | |||||
| headerName: t("dnDate"), | |||||
| width: 100, | |||||
| renderCell: () => { | |||||
| return <>07/08/2025</> | |||||
| } | |||||
| // flex: 0.4, | // flex: 0.4, | ||||
| }, | }, | ||||
| { | { | ||||
| field: "itemName", | field: "itemName", | ||||
| headerName: t("itemName"), | headerName: t("itemName"), | ||||
| width: 120, | |||||
| width: 100, | |||||
| // flex: 0.6, | // flex: 0.6, | ||||
| }, | }, | ||||
| { | { | ||||
| field: "acceptedQty", | field: "acceptedQty", | ||||
| headerName: t("acceptedQty"), | headerName: t("acceptedQty"), | ||||
| // flex: 0.5, | // flex: 0.5, | ||||
| width: 120, | |||||
| width: 100, | |||||
| type: "number", | type: "number", | ||||
| // editable: true, | // editable: true, | ||||
| // replace with tooltip + content | // replace with tooltip + content | ||||
| @@ -417,7 +470,7 @@ function PoInputGrid({ | |||||
| { | { | ||||
| field: "status", | field: "status", | ||||
| headerName: t("status"), | headerName: t("status"), | ||||
| width: 120, | |||||
| width: 70, | |||||
| // flex: 0.5, | // flex: 0.5, | ||||
| renderCell: (params) => { | renderCell: (params) => { | ||||
| return t(`${params.row.status}`); | return t(`${params.row.status}`); | ||||
| @@ -426,12 +479,13 @@ function PoInputGrid({ | |||||
| { | { | ||||
| field: "actions", | field: "actions", | ||||
| type: "actions", | type: "actions", | ||||
| headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t( | |||||
| "stock in", | |||||
| )} | ${t("putaway")} | ${t("delete")}`, | |||||
| // headerName: `${t("start")} | ${t("qc")} | ${t("escalation")} | ${t( | |||||
| // "stock in", | |||||
| // )} | ${t("putaway")} | ${t("delete")}`, | |||||
| headerName: "動作", | |||||
| // headerName: "start | qc | escalation | stock in | putaway | delete", | // headerName: "start | qc | escalation | stock in | putaway | delete", | ||||
| width: 300, | width: 300, | ||||
| // flex: 1.5, | |||||
| // flex: 2, | |||||
| cellClassName: "actions", | cellClassName: "actions", | ||||
| getActions: (params) => { | getActions: (params) => { | ||||
| // console.log(params.row.status); | // console.log(params.row.status); | ||||
| @@ -440,130 +494,158 @@ function PoInputGrid({ | |||||
| // console.log(session?.user?.abilities?.includes("APPROVAL")); | // console.log(session?.user?.abilities?.includes("APPROVAL")); | ||||
| return [ | return [ | ||||
| <GridActionsCellItem | <GridActionsCellItem | ||||
| icon={<PlayArrowIcon />} | |||||
| icon={<Button variant="contained">{t("qc processing")}</Button>} | |||||
| label="start" | label="start" | ||||
| sx={{ | sx={{ | ||||
| color: "primary.main", | color: "primary.main", | ||||
| // marginRight: 1, | // 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 | |||||
| } | |||||
| // disabled={!(stockInLineStatusMap[status] === 0)} | |||||
| // set _isNew to false after posting | // set _isNew to false after posting | ||||
| // or check status | // or check status | ||||
| onClick={handleEscalation(params.row.id, params)} | |||||
| onClick={handleNewQC(params.row.id, params)} | |||||
| color="inherit" | color="inherit" | ||||
| key="edit" | key="edit" | ||||
| />, | />, | ||||
| <GridActionsCellItem | <GridActionsCellItem | ||||
| icon={<ShoppingCartIcon />} | |||||
| label="stockin" | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| // marginRight: 1, | |||||
| }} | |||||
| disabled={ | |||||
| stockInLineStatusMap[status] === 9 || | |||||
| stockInLineStatusMap[status] <= 2 || | |||||
| stockInLineStatusMap[status] >= 7 || | |||||
| (stockInLineStatusMap[status] >= 3 && | |||||
| stockInLineStatusMap[status] <= 5 && | |||||
| !session?.user?.abilities?.includes("APPROVAL")) | |||||
| } | |||||
| // set _isNew to false after posting | |||||
| // or check status | |||||
| onClick={handleStockIn(params.row.id, params)} | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<ShoppingCartIcon />} | |||||
| label="putaway" | |||||
| icon={<Button variant="contained">{t("putawayBtn")}</Button>} | |||||
| label="start" | |||||
| sx={{ | sx={{ | ||||
| color: "primary.main", | color: "primary.main", | ||||
| // marginRight: 1, | // marginRight: 1, | ||||
| }} | }} | ||||
| disabled={ | |||||
| stockInLineStatusMap[status] === 9 || | |||||
| stockInLineStatusMap[status] < 7 | |||||
| } | |||||
| // disabled={!(stockInLineStatusMap[status] === 0)} | |||||
| // set _isNew to false after posting | // set _isNew to false after posting | ||||
| // or check status | // or check status | ||||
| onClick={handlePutAway(params.row.id, params)} | |||||
| onClick={handleStart(params.row.id, params)} | |||||
| color="inherit" | color="inherit" | ||||
| key="edit" | key="edit" | ||||
| />, | />, | ||||
| // <GridActionsCellItem | // <GridActionsCellItem | ||||
| // icon={<QrCodeIcon />} | |||||
| // icon={<Button variant="contained">{t("qc processing")}</Button>} | |||||
| // 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 || | |||||
| // (stockInLineStatusMap[status] >= 3 && | |||||
| // stockInLineStatusMap[status] <= 5 && | |||||
| // !session?.user?.abilities?.includes("APPROVAL")) | |||||
| // } | |||||
| // // set _isNew to false after posting | |||||
| // // or check status | |||||
| // onClick={handleStockIn(params.row.id, params)} | |||||
| // color="inherit" | |||||
| // key="edit" | |||||
| // />, | |||||
| // <GridActionsCellItem | |||||
| // icon={<ShoppingCartIcon />} | |||||
| // label="putaway" | // label="putaway" | ||||
| // sx={{ | // sx={{ | ||||
| // color: "primary.main", | // color: "primary.main", | ||||
| // // marginRight: 1, | // // marginRight: 1, | ||||
| // }} | // }} | ||||
| // disabled={stockInLineStatusMap[status] === 9 || stockInLineStatusMap[status] !== 8} | |||||
| // disabled={ | |||||
| // stockInLineStatusMap[status] === 9 || | |||||
| // stockInLineStatusMap[status] < 7 | |||||
| // } | |||||
| // // set _isNew to false after posting | // // set _isNew to false after posting | ||||
| // // or check status | // // or check status | ||||
| // onClick={handleQrCode(params.row.id, params)} | |||||
| // 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" | // color="inherit" | ||||
| // key="edit" | // 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" | |||||
| />, | |||||
| ]; | ]; | ||||
| }, | }, | ||||
| }, | }, | ||||
| @@ -594,6 +676,7 @@ function PoInputGrid({ | |||||
| }, | }, | ||||
| })); | })); | ||||
| }, [currQty, getRowId, itemDetail]); | }, [currQty, getRowId, itemDetail]); | ||||
| const validation = useCallback( | const validation = useCallback( | ||||
| ( | ( | ||||
| newRow: GridRowModel<StockInLineRow>, | newRow: GridRowModel<StockInLineRow>, | ||||
| @@ -654,20 +737,22 @@ function PoInputGrid({ | |||||
| ); | ); | ||||
| const footer = ( | 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> | |||||
| <> | |||||
| {/* <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 ( | return ( | ||||
| <> | <> | ||||
| <StyledDataGrid | <StyledDataGrid | ||||
| @@ -715,6 +800,21 @@ function PoInputGrid({ | |||||
| footer: { child: footer }, | footer: { child: footer }, | ||||
| }} | }} | ||||
| /> | /> | ||||
| {modalInfo !== undefined && ( | |||||
| <> | |||||
| <PoQcStockInModalVer2 | |||||
| // setRows={setRows} | |||||
| setEntries={setEntries} | |||||
| setStockInLine={setStockInLine} | |||||
| setItemDetail={setModalInfo} | |||||
| qc={qc} | |||||
| open={newOpen} | |||||
| onClose={closeNewModal} | |||||
| itemDetail={modalInfo} | |||||
| /> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| {modalInfo !== undefined && ( | {modalInfo !== undefined && ( | ||||
| <> | <> | ||||
| <PoQcStockInModal | <PoQcStockInModal | ||||
| @@ -0,0 +1,400 @@ | |||||
| "use client"; | |||||
| import { | |||||
| Dispatch, | |||||
| MutableRefObject, | |||||
| SetStateAction, | |||||
| useCallback, | |||||
| useEffect, | |||||
| useMemo, | |||||
| useState, | |||||
| } from "react"; | |||||
| import StyledDataGrid from "../StyledDataGrid"; | |||||
| import { | |||||
| FooterPropsOverrides, | |||||
| GridActionsCellItem, | |||||
| GridCellParams, | |||||
| GridColDef, | |||||
| GridEventListener, | |||||
| GridRowEditStopReasons, | |||||
| GridRowId, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| GridRowModes, | |||||
| GridRowModesModel, | |||||
| GridRowSelectionModel, | |||||
| GridToolbarContainer, | |||||
| GridValidRowModel, | |||||
| useGridApiRef, | |||||
| } from "@mui/x-data-grid"; | |||||
| import { set, useFormContext } from "react-hook-form"; | |||||
| import SaveIcon from "@mui/icons-material/Save"; | |||||
| import DeleteIcon from "@mui/icons-material/Delete"; | |||||
| import CancelIcon from "@mui/icons-material/Cancel"; | |||||
| import { Add } from "@mui/icons-material"; | |||||
| import { Box, Button, Typography } from "@mui/material"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import { | |||||
| GridApiCommunity, | |||||
| GridSlotsComponentsProps, | |||||
| } 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 { | |||||
| id: string | number; | |||||
| } | |||||
| // export type InputGridProps = { | |||||
| // [key: string]: any | |||||
| // } | |||||
| interface DefaultResult<E> { | |||||
| _isNew: boolean; | |||||
| _error: E; | |||||
| } | |||||
| interface SelectionResult<E> { | |||||
| active: boolean; | |||||
| _isNew: boolean; | |||||
| _error: E; | |||||
| } | |||||
| type Result<E> = DefaultResult<E> | SelectionResult<E>; | |||||
| export type TableRow<V, E> = Partial< | |||||
| V & { | |||||
| isActive: boolean | undefined; | |||||
| _isNew: boolean; | |||||
| _error: E; | |||||
| } & ResultWithId | |||||
| >; | |||||
| export interface InputDataGridProps<T, V, E> { | |||||
| apiRef: MutableRefObject<GridApiCommunity>; | |||||
| checkboxSelection: false | undefined; | |||||
| _formKey: keyof T; | |||||
| columns: GridColDef[]; | |||||
| validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | |||||
| needAdd?: boolean; | |||||
| } | |||||
| export interface SelectionInputDataGridProps<T, V, E> { | |||||
| // thinking how do | |||||
| apiRef: MutableRefObject<GridApiCommunity>; | |||||
| checkboxSelection: true; | |||||
| _formKey: keyof T; | |||||
| columns: GridColDef[]; | |||||
| validateRow: (newRow: GridRowModel<TableRow<V, E>>) => E; | |||||
| needAdd?: boolean; | |||||
| } | |||||
| export type Props<T, V, E> = | |||||
| | InputDataGridProps<T, V, E> | |||||
| | SelectionInputDataGridProps<T, V, E>; | |||||
| export class ProcessRowUpdateError<T, E> extends Error { | |||||
| public readonly row: T; | |||||
| public readonly errors: E | undefined; | |||||
| constructor(row: T, message?: string, errors?: E) { | |||||
| super(message); | |||||
| this.row = row; | |||||
| this.errors = errors; | |||||
| Object.setPrototypeOf(this, ProcessRowUpdateError.prototype); | |||||
| } | |||||
| } | |||||
| // T == CreatexxxInputs map of the form's fields | |||||
| // V == target field input inside CreatexxxInputs, e.g. qcChecks: ItemQc[], V = ItemQc | |||||
| // E == error | |||||
| function QcDatagrid<T, V, E>({ | |||||
| apiRef, | |||||
| checkboxSelection = false, | |||||
| _formKey, | |||||
| columns, | |||||
| validateRow, | |||||
| needAdd, | |||||
| }: Props<T, V, E>) { | |||||
| const { | |||||
| t, | |||||
| // i18n: { language }, | |||||
| } = useTranslation("common"); | |||||
| const formKey = _formKey.toString(); | |||||
| const { setValue, getValues } = useFormContext(); | |||||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||||
| // const apiRef = useGridApiRef(); | |||||
| const getRowId = useCallback<GridRowIdGetter<TableRow<V, E>>>( | |||||
| (row) => row.id! as number, | |||||
| [], | |||||
| ); | |||||
| const list: TableRow<V, E>[] = getValues(formKey); | |||||
| // console.log(list) | |||||
| const [rows, setRows] = useState<TableRow<V, E>[]>(() => { | |||||
| const list: TableRow<V, E>[] = getValues(formKey); | |||||
| return 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 [rowSelectionModel, setRowSelectionModel] = | |||||
| useState<GridRowSelectionModel>(() => { | |||||
| // const rowModel = list.filter((li) => li.isActive).map(i => i.id) as GridRowSelectionModel | |||||
| const rowModel: GridRowSelectionModel = getValues( | |||||
| `${formKey}_active`, | |||||
| ) as GridRowSelectionModel; | |||||
| console.log(rowModel); | |||||
| return rowModel; | |||||
| }); | |||||
| const handleSave = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setRowModesModel((prevRowModesModel) => ({ | |||||
| ...prevRowModesModel, | |||||
| [id]: { mode: GridRowModes.View }, | |||||
| })); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| const onProcessRowUpdateError = useCallback( | |||||
| (updateError: ProcessRowUpdateError<T, E>) => { | |||||
| const errors = updateError.errors; | |||||
| const row = updateError.row; | |||||
| console.log(errors); | |||||
| apiRef.current.updateRows([{ ...row, _error: errors }]); | |||||
| }, | |||||
| [apiRef], | |||||
| ); | |||||
| const processRowUpdate = useCallback( | |||||
| ( | |||||
| newRow: GridRowModel<TableRow<V, E>>, | |||||
| originalRow: GridRowModel<TableRow<V, E>>, | |||||
| ) => { | |||||
| ///////////////// | |||||
| // validation here | |||||
| const errors = validateRow(newRow); | |||||
| console.log(newRow); | |||||
| if (errors) { | |||||
| throw new ProcessRowUpdateError( | |||||
| originalRow, | |||||
| "validation error", | |||||
| errors, | |||||
| ); | |||||
| } | |||||
| ///////////////// | |||||
| const { _isNew, _error, ...updatedRow } = newRow; | |||||
| const rowToSave = { | |||||
| ...updatedRow, | |||||
| } as TableRow<V, E>; /// test | |||||
| console.log(rowToSave); | |||||
| setRows((rw) => | |||||
| rw.map((r) => (getRowId(r) === getRowId(originalRow) ? rowToSave : r)), | |||||
| ); | |||||
| return rowToSave; | |||||
| }, | |||||
| [validateRow, getRowId], | |||||
| ); | |||||
| const addRow = useCallback(() => { | |||||
| const newEntry = { id: Date.now(), _isNew: true } as TableRow<V, E>; | |||||
| setRows((prev) => [...prev, newEntry]); | |||||
| setRowModesModel((model) => ({ | |||||
| ...model, | |||||
| [getRowId(newEntry)]: { | |||||
| mode: GridRowModes.Edit, | |||||
| // fieldToFocus: "team", /// test | |||||
| }, | |||||
| })); | |||||
| }, [getRowId]); | |||||
| const reset = useCallback(() => { | |||||
| setRowModesModel({}); | |||||
| setRows(originalRows); | |||||
| }, [originalRows]); | |||||
| const handleCancel = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setRowModesModel((model) => ({ | |||||
| ...model, | |||||
| [id]: { mode: GridRowModes.View, ignoreModifications: true }, | |||||
| })); | |||||
| const editedRow = rows.find((row) => getRowId(row) === id); | |||||
| if (editedRow?._isNew) { | |||||
| setRows((rw) => rw.filter((r) => getRowId(r) !== id)); | |||||
| } else { | |||||
| setRows((rw) => | |||||
| rw.map((r) => (getRowId(r) === id ? { ...r, _error: undefined } : r)), | |||||
| ); | |||||
| } | |||||
| }, | |||||
| [rows, getRowId], | |||||
| ); | |||||
| const handleDelete = useCallback( | |||||
| (id: GridRowId) => () => { | |||||
| setRows((prevRows) => prevRows.filter((row) => getRowId(row) !== id)); | |||||
| }, | |||||
| [getRowId], | |||||
| ); | |||||
| const _columns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| ...columns, | |||||
| { | |||||
| field: "actions", | |||||
| type: "actions", | |||||
| headerName: "", | |||||
| flex: 0.5, | |||||
| cellClassName: "actions", | |||||
| getActions: ({ id }: { id: GridRowId }) => { | |||||
| const isInEditMode = rowModesModel[id]?.mode === GridRowModes.Edit; | |||||
| if (isInEditMode) { | |||||
| return [ | |||||
| <GridActionsCellItem | |||||
| icon={<SaveIcon />} | |||||
| label="Save" | |||||
| key="edit" | |||||
| sx={{ | |||||
| color: "primary.main", | |||||
| }} | |||||
| onClick={handleSave(id)} | |||||
| />, | |||||
| <GridActionsCellItem | |||||
| icon={<CancelIcon />} | |||||
| label="Cancel" | |||||
| key="edit" | |||||
| onClick={handleCancel(id)} | |||||
| />, | |||||
| ]; | |||||
| } | |||||
| return [ | |||||
| <GridActionsCellItem | |||||
| icon={<DeleteIcon />} | |||||
| label="Delete" | |||||
| sx={{ | |||||
| color: "error.main", | |||||
| }} | |||||
| onClick={handleDelete(id)} | |||||
| color="inherit" | |||||
| key="edit" | |||||
| />, | |||||
| ]; | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| [columns, rowModesModel, handleSave, handleCancel, handleDelete], | |||||
| ); | |||||
| // sync useForm | |||||
| useEffect(() => { | |||||
| // console.log(formKey) | |||||
| // console.log(rows) | |||||
| setValue(formKey, rows); | |||||
| }, [formKey, rows, setValue]); | |||||
| const footer = ( | |||||
| <Box display="flex" gap={2} alignItems="center"> | |||||
| <Button | |||||
| disableRipple | |||||
| variant="outlined" | |||||
| startIcon={<Add />} | |||||
| onClick={addRow} | |||||
| size="small" | |||||
| > | |||||
| {t("Add Record")} | |||||
| </Button> | |||||
| <Button | |||||
| disableRipple | |||||
| variant="outlined" | |||||
| startIcon={<Add />} | |||||
| onClick={reset} | |||||
| size="small" | |||||
| > | |||||
| {t("Clean Record")} | |||||
| </Button> | |||||
| </Box> | |||||
| ); | |||||
| // const handleRowEditStop: GridEventListener<'rowEditStop'> = (params, event) => { | |||||
| // if (params.reason === GridRowEditStopReasons.rowFocusOut) { | |||||
| // event.defaultMuiPrevented = true; | |||||
| // } | |||||
| // }; | |||||
| return ( | |||||
| <StyledDataGrid | |||||
| // {...props} | |||||
| // getRowId={getRowId as GridRowIdGetter<GridValidRowModel>} | |||||
| // checkbox selection | |||||
| checkboxSelection={checkboxSelection} | |||||
| disableRowSelectionOnClick={checkboxSelection} | |||||
| onRowSelectionModelChange={(newRowSelectionModel) => { | |||||
| if (checkboxSelection) { | |||||
| setRowSelectionModel(newRowSelectionModel); | |||||
| setValue("qcChecks_active", newRowSelectionModel); | |||||
| } | |||||
| }} | |||||
| rowSelectionModel={rowSelectionModel} | |||||
| apiRef={apiRef} | |||||
| rows={rows} | |||||
| columns={!checkboxSelection ? _columns : columns} | |||||
| editMode="row" | |||||
| 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 | |||||
| processRowUpdate={processRowUpdate as any} | |||||
| // onRowEditStop={handleRowEditStop} | |||||
| rowModesModel={rowModesModel} | |||||
| onRowModesModelChange={setRowModesModel} | |||||
| onProcessRowUpdateError={onProcessRowUpdateError} | |||||
| getCellClassName={(params: GridCellParams<TableRow<T, E>>) => { | |||||
| let classname = ""; | |||||
| if (params.row._error) { | |||||
| classname = "hasError"; | |||||
| } | |||||
| return classname; | |||||
| }} | |||||
| slots={ | |||||
| !checkboxSelection | |||||
| ? { | |||||
| footer: FooterToolbar, | |||||
| noRowsOverlay: NoRowsOverlay, | |||||
| } | |||||
| : undefined | |||||
| } | |||||
| slotProps={ | |||||
| !checkboxSelection && Boolean(needAdd) | |||||
| ? { | |||||
| footer: { child: footer }, | |||||
| } | |||||
| : undefined | |||||
| // slotProps={renderFooter ? { | |||||
| // footer: { child: footer }, | |||||
| // }: undefined | |||||
| } | |||||
| /> | |||||
| ); | |||||
| } | |||||
| const FooterToolbar: React.FC<FooterPropsOverrides> = ({ child }) => { | |||||
| return <GridToolbarContainer sx={{ p: 2 }}>{child}</GridToolbarContainer>; | |||||
| }; | |||||
| 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> | |||||
| ); | |||||
| }; | |||||
| export default QcDatagrid; | |||||
| @@ -0,0 +1,242 @@ | |||||
| "use client"; | |||||
| import { PurchaseQcResult, PurchaseQCInput } from "@/app/api/po/actions"; | |||||
| import { | |||||
| Box, | |||||
| Card, | |||||
| CardContent, | |||||
| Grid, | |||||
| Stack, | |||||
| Tab, | |||||
| Tabs, | |||||
| TabsProps, | |||||
| TextField, | |||||
| Tooltip, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import StyledDataGrid from "../StyledDataGrid"; | |||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | |||||
| import { | |||||
| GridColDef, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| useGridApiContext, | |||||
| GridRenderCellParams, | |||||
| GridRenderEditCellParams, | |||||
| useGridApiRef, | |||||
| } from "@mui/x-data-grid"; | |||||
| import InputDataGrid from "../InputDataGrid"; | |||||
| import { TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
| import TwoLineCell from "./TwoLineCell"; | |||||
| import QcSelect from "./QcSelect"; | |||||
| import { GridEditInputCell } from "@mui/x-data-grid"; | |||||
| import { StockInLine } from "@/app/api/po"; | |||||
| import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
| import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions"; | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import axios from "@/app/(main)/axios/axiosInstance"; | |||||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | |||||
| import axiosInstance from "@/app/(main)/axios/axiosInstance"; | |||||
| interface Props { | |||||
| itemDetail: StockInLine; | |||||
| qc: QcItemWithChecks[]; | |||||
| disabled: boolean; | |||||
| } | |||||
| type EntryError = | |||||
| | { | |||||
| [field in keyof PurchaseQcResult]?: string; | |||||
| } | |||||
| | undefined; | |||||
| type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | |||||
| // fetchQcItemCheck | |||||
| const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| const { t } = useTranslation("purchaseOrder"); | |||||
| const apiRef = useGridApiRef(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues, touchedFields }, | |||||
| watch, | |||||
| control, | |||||
| setValue, | |||||
| getValues, | |||||
| reset, | |||||
| resetField, | |||||
| setError, | |||||
| clearErrors, | |||||
| } = useFormContext<PurchaseQCInput>(); | |||||
| console.log(itemDetail); | |||||
| console.log(defaultValues); | |||||
| const [tabIndex, setTabIndex] = useState(0); | |||||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||||
| (_e, newValue) => { | |||||
| setTabIndex(newValue); | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| //// validate form | |||||
| const accQty = watch("acceptedQty"); | |||||
| const validateForm = useCallback(() => { | |||||
| console.log(accQty); | |||||
| if (accQty > itemDetail.acceptedQty) { | |||||
| setError("acceptedQty", { | |||||
| message: `${t("acceptedQty must not greater than")} ${ | |||||
| itemDetail.acceptedQty | |||||
| }`, | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| if (accQty < 1) { | |||||
| setError("acceptedQty", { | |||||
| message: t("minimal value is 1"), | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| if (isNaN(accQty)) { | |||||
| setError("acceptedQty", { | |||||
| message: t("value must be a number"), | |||||
| type: "required", | |||||
| }); | |||||
| } | |||||
| }, [accQty]); | |||||
| useEffect(() => { | |||||
| clearErrors(); | |||||
| validateForm(); | |||||
| }, [clearErrors, validateForm]); | |||||
| const columns = useMemo<GridColDef[]>( | |||||
| () => [ | |||||
| { | |||||
| field: "qcItemId", | |||||
| headerName: t("qc Check"), | |||||
| flex: 1, | |||||
| editable: !disabled, | |||||
| valueFormatter(params) { | |||||
| const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | |||||
| if (!row) { | |||||
| return null; | |||||
| } | |||||
| const Qc = qc.find((q) => q.id === row.qcItemId); | |||||
| return Qc ? `${Qc.code} - ${Qc.name}` : t("Please select QC"); | |||||
| }, | |||||
| renderCell(params: GridRenderCellParams<PoQcRow, number>) { | |||||
| console.log(params.value); | |||||
| return <TwoLineCell>{params.formattedValue}</TwoLineCell>; | |||||
| }, | |||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow, number>) { | |||||
| const errorMessage = | |||||
| params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| console.log(errorMessage); | |||||
| const content = ( | |||||
| <QcSelect | |||||
| allQcs={qc} | |||||
| value={params.row.qcItemId} | |||||
| onQcSelect={async (qcItemId) => { | |||||
| await params.api.setEditCellValue({ | |||||
| id: params.id, | |||||
| field: "qcItemId", | |||||
| value: qcItemId, | |||||
| }); | |||||
| // await params.api.setEditCellValue({ | |||||
| // id: params.id, | |||||
| // field: "type", | |||||
| // value: "determine1", | |||||
| // }); | |||||
| }} | |||||
| /> | |||||
| ); | |||||
| return errorMessage ? ( | |||||
| <Tooltip title={errorMessage}> | |||||
| <Box width="100%">{content}</Box> | |||||
| </Tooltip> | |||||
| ) : ( | |||||
| content | |||||
| ); | |||||
| }, | |||||
| }, | |||||
| { | |||||
| field: "failQty", | |||||
| headerName: t("failQty"), | |||||
| flex: 1, | |||||
| editable: !disabled, | |||||
| type: "number", | |||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | |||||
| // const recordQty = params.row.qty | |||||
| // if (recordQty !== undefined) { | |||||
| // setUnrecordQty((prev) => prev - recordQty) | |||||
| // } | |||||
| const errorMessage = | |||||
| params.row._error?.[params.field as keyof PurchaseQcResult]; | |||||
| const content = <GridEditInputCell {...params} />; | |||||
| return errorMessage ? ( | |||||
| <Tooltip title={t(errorMessage)}> | |||||
| <Box width="100%">{content}</Box> | |||||
| </Tooltip> | |||||
| ) : ( | |||||
| content | |||||
| ); | |||||
| }, | |||||
| }, | |||||
| ], | |||||
| [qc], | |||||
| ); | |||||
| /// validate datagrid | |||||
| const validation = useCallback( | |||||
| (newRow: GridRowModel<PoQcRow>): EntryError => { | |||||
| const error: EntryError = {}; | |||||
| const { qcItemId, failQty } = newRow; | |||||
| if (!qcItemId || qcItemId <= 0) { | |||||
| error["qcItemId"] = t("select qc"); | |||||
| } | |||||
| if (!failQty || failQty <= 0) { | |||||
| error["failQty"] = t("enter a failQty"); | |||||
| } | |||||
| if (failQty && failQty > itemDetail.acceptedQty) { | |||||
| error["failQty"] = t("qty too big"); | |||||
| } | |||||
| return Object.keys(error).length > 0 ? error : undefined; | |||||
| }, | |||||
| [], | |||||
| ); | |||||
| useEffect(() => { | |||||
| console.log(itemDetail); | |||||
| const status = "receiving"; | |||||
| // switch (itemDetail.status) { | |||||
| // case 'pending': | |||||
| // status = "receiving" | |||||
| // break; | |||||
| // } | |||||
| setValue("status", status); | |||||
| }, [itemDetail]); | |||||
| return ( | |||||
| <> | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| <Tabs | |||||
| value={tabIndex} | |||||
| onChange={handleTabChange} | |||||
| variant="scrollable" | |||||
| > | |||||
| <Tab label={t("QC Info")} iconPosition="end" /> | |||||
| <Tab label={t("Escalation History")} iconPosition="end" /> | |||||
| </Tabs> | |||||
| </Grid> | |||||
| </Grid> | |||||
| </> | |||||
| ); | |||||
| }; | |||||
| export default QcFormVer2; | |||||
| @@ -0,0 +1,149 @@ | |||||
| "use client"; | |||||
| import { StockInLine } from "@/app/api/po"; | |||||
| import { ModalFormInput, PurchaseQcResult } from "@/app/api/po/actions"; | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import { Box, Button, Grid, Modal, ModalProps, Stack, Typography } from "@mui/material"; | |||||
| import { Dispatch, SetStateAction, useCallback, useState } from "react"; | |||||
| import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | |||||
| import { StockInLineRow } from "./PoInputGrid"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import StockInForm from "./StockInForm"; | |||||
| import StockInFormVer2 from "./StockInFormVer2"; | |||||
| import QcFormVer2 from "./QcFormVer2"; | |||||
| const style = { | |||||
| position: "absolute", | |||||
| top: "50%", | |||||
| left: "50%", | |||||
| transform: "translate(-50%, -50%)", | |||||
| overflowY: "scroll", | |||||
| bgcolor: "background.paper", | |||||
| pt: 5, | |||||
| px: 5, | |||||
| pb: 10, | |||||
| display: "block", | |||||
| width: { xs: "60%", sm: "60%", md: "60%" }, | |||||
| }; | |||||
| interface CommonProps extends Omit<ModalProps, "children"> { | |||||
| // setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>; | |||||
| setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>; | |||||
| setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>; | |||||
| itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | |||||
| setItemDetail: Dispatch< | |||||
| SetStateAction< | |||||
| | (StockInLine & { | |||||
| warehouseId?: number; | |||||
| }) | |||||
| | undefined | |||||
| > | |||||
| >; | |||||
| qc?: QcItemWithChecks[]; | |||||
| warehouse?: any[]; | |||||
| // type: "qc" | "stockIn" | "escalation" | "putaway" | "reject"; | |||||
| } | |||||
| interface Props extends CommonProps{ | |||||
| itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] }; | |||||
| } | |||||
| const PoQcStockInModalVer2: React.FC<Props> = ({ | |||||
| // type, | |||||
| // setRows, | |||||
| setEntries, | |||||
| setStockInLine, | |||||
| open, | |||||
| onClose, | |||||
| itemDetail, | |||||
| setItemDetail, | |||||
| qc, | |||||
| warehouse, | |||||
| }) => { | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation("purchaseOrder"); | |||||
| const formProps = useForm<ModalFormInput>({ | |||||
| defaultValues: { | |||||
| ...itemDetail, | |||||
| // receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT), | |||||
| // warehouseId: itemDetail.defaultWarehouseId || 0 | |||||
| }, | |||||
| }); | |||||
| const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | |||||
| (...args) => { | |||||
| onClose?.(...args); | |||||
| // reset(); | |||||
| }, | |||||
| [onClose], | |||||
| ); | |||||
| const [submissionType, setSubmissionType] = useState<"stockIn" | "qc" | "escalate" | undefined>(undefined) | |||||
| const onSubmit = useCallback<SubmitHandler<ModalFormInput>>( | |||||
| async (data, event) => { | |||||
| console.log(event!.nativeEvent) | |||||
| // divide 3 section for this submition | |||||
| // switch (submissionType) { | |||||
| // submit stock in data | |||||
| // submit qc data | |||||
| // submit putaway | |||||
| // } | |||||
| }, [submissionType]) | |||||
| return ( | |||||
| <> | |||||
| <FormProvider {...formProps}> | |||||
| <Modal open={open} onClose={closeHandler} sx={{ overflowY: "scroll" }}> | |||||
| <Box | |||||
| sx={style} | |||||
| component="form" | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | |||||
| > | |||||
| <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}> | |||||
| <StockInFormVer2 | |||||
| itemDetail={itemDetail} | |||||
| disabled={false} | |||||
| /> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | |||||
| <Button | |||||
| id="stockIn" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="primary" | |||||
| > | |||||
| {t("submitStockIn")} | |||||
| </Button> | |||||
| </Stack> | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| <QcFormVer2 | |||||
| qc={qc!} | |||||
| itemDetail={itemDetail} | |||||
| disabled={false} | |||||
| /> | |||||
| </Grid> | |||||
| <Button | |||||
| id="qc" | |||||
| type="button" | |||||
| variant="contained" | |||||
| color="secondary" | |||||
| > | |||||
| Submit QC | |||||
| </Button> | |||||
| </Box> | |||||
| </Modal> | |||||
| </FormProvider> | |||||
| </> | |||||
| ) | |||||
| } | |||||
| export default PoQcStockInModalVer2 | |||||
| @@ -0,0 +1,323 @@ | |||||
| "use client"; | |||||
| import { | |||||
| PurchaseQcResult, | |||||
| PurchaseQCInput, | |||||
| StockInInput, | |||||
| } from "@/app/api/po/actions"; | |||||
| import { | |||||
| Box, | |||||
| Card, | |||||
| CardContent, | |||||
| Grid, | |||||
| Stack, | |||||
| TextField, | |||||
| Tooltip, | |||||
| Typography, | |||||
| } from "@mui/material"; | |||||
| import { Controller, useFormContext } from "react-hook-form"; | |||||
| import { useTranslation } from "react-i18next"; | |||||
| import StyledDataGrid from "../StyledDataGrid"; | |||||
| import { useCallback, useEffect, useMemo } from "react"; | |||||
| import { | |||||
| GridColDef, | |||||
| GridRowIdGetter, | |||||
| GridRowModel, | |||||
| useGridApiContext, | |||||
| GridRenderCellParams, | |||||
| GridRenderEditCellParams, | |||||
| useGridApiRef, | |||||
| } from "@mui/x-data-grid"; | |||||
| import InputDataGrid from "../InputDataGrid"; | |||||
| import { TableRow } from "../InputDataGrid/InputDataGrid"; | |||||
| import TwoLineCell from "./TwoLineCell"; | |||||
| import QcSelect from "./QcSelect"; | |||||
| import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import { GridEditInputCell } from "@mui/x-data-grid"; | |||||
| import { StockInLine } from "@/app/api/po"; | |||||
| import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers"; | |||||
| import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; | |||||
| import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | |||||
| import dayjs from "dayjs"; | |||||
| // change PurchaseQcResult to stock in entry props | |||||
| interface Props { | |||||
| itemDetail: StockInLine; | |||||
| // qc: QcItemWithChecks[]; | |||||
| disabled: boolean; | |||||
| } | |||||
| type EntryError = | |||||
| | { | |||||
| [field in keyof StockInInput]?: string; | |||||
| } | |||||
| | undefined; | |||||
| // type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | |||||
| const StockInFormVer2: React.FC<Props> = ({ | |||||
| // qc, | |||||
| itemDetail, | |||||
| disabled, | |||||
| }) => { | |||||
| const { | |||||
| t, | |||||
| i18n: { language }, | |||||
| } = useTranslation("purchaseOrder"); | |||||
| const apiRef = useGridApiRef(); | |||||
| const { | |||||
| register, | |||||
| formState: { errors, defaultValues, touchedFields }, | |||||
| watch, | |||||
| control, | |||||
| setValue, | |||||
| getValues, | |||||
| reset, | |||||
| resetField, | |||||
| setError, | |||||
| clearErrors, | |||||
| } = useFormContext<StockInInput>(); | |||||
| console.log(itemDetail); | |||||
| useEffect(() => { | |||||
| console.log("triggered"); | |||||
| // receiptDate default tdy | |||||
| setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT)); | |||||
| setValue("status", "received"); | |||||
| }, [setValue]); | |||||
| useEffect(() => { | |||||
| console.log(errors); | |||||
| }, [errors]); | |||||
| const productionDate = watch("productionDate"); | |||||
| const expiryDate = watch("expiryDate"); | |||||
| useEffect(() => { | |||||
| console.log(productionDate); | |||||
| console.log(expiryDate); | |||||
| if (expiryDate) clearErrors(); | |||||
| if (productionDate) clearErrors(); | |||||
| }, [productionDate, expiryDate, clearErrors]); | |||||
| console.log(itemDetail) | |||||
| return ( | |||||
| <Grid container justifyContent="flex-start" alignItems="flex-start"> | |||||
| {/* <Grid item xs={12}> | |||||
| <Typography variant="h6" display="block" marginBlockEnd={1}> | |||||
| {t("Stock In Detail")} | |||||
| </Typography> | |||||
| </Grid> */} | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("dnNo")} | |||||
| fullWidth | |||||
| {...register("dnNo", { | |||||
| // required: "productLotNo required!", | |||||
| })} | |||||
| disabled={true} | |||||
| // error={Boolean(errors.productLotNo)} | |||||
| // helperText={errors.productLotNo?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("itemName")} | |||||
| fullWidth | |||||
| {...register("itemName", { | |||||
| // required: "productLotNo required!", | |||||
| })} | |||||
| disabled={true} | |||||
| // error={Boolean(errors.productLotNo)} | |||||
| // helperText={errors.productLotNo?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("PO No.")} | |||||
| fullWidth | |||||
| {...register("poCode", { | |||||
| // required: "productLotNo required!", | |||||
| })} | |||||
| disabled={true} | |||||
| // error={Boolean(errors.productLotNo)} | |||||
| // helperText={errors.productLotNo?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <Controller | |||||
| control={control} | |||||
| name="receiptDate" | |||||
| rules={{ required: true }} | |||||
| render={({ field }) => { | |||||
| return ( | |||||
| <LocalizationProvider | |||||
| dateAdapter={AdapterDayjs} | |||||
| adapterLocale={`${language}-hk`} | |||||
| > | |||||
| <DatePicker | |||||
| {...field} | |||||
| sx={{ width: "100%" }} | |||||
| label={t("receiptDate")} | |||||
| value={dayjs(watch("receiptDate"))} | |||||
| disabled={true} | |||||
| onChange={(date) => { | |||||
| if (!date) return; | |||||
| // setValue("receiptDate", date.format(INPUT_DATE_FORMAT)); | |||||
| field.onChange(date); | |||||
| }} | |||||
| inputRef={field.ref} | |||||
| slotProps={{ | |||||
| textField: { | |||||
| // required: true, | |||||
| error: Boolean(errors.receiptDate?.message), | |||||
| helperText: errors.receiptDate?.message, | |||||
| }, | |||||
| }} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| ); | |||||
| }} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <Controller | |||||
| control={control} | |||||
| name="productionDate" | |||||
| // rules={{ required: !Boolean(expiryDate) }} | |||||
| render={({ field }) => { | |||||
| return ( | |||||
| <LocalizationProvider | |||||
| dateAdapter={AdapterDayjs} | |||||
| adapterLocale={`${language}-hk`} | |||||
| > | |||||
| <DatePicker | |||||
| {...field} | |||||
| sx={{ width: "100%" }} | |||||
| label={t("productionDate")} | |||||
| value={productionDate ? dayjs(productionDate) : undefined} | |||||
| disabled={disabled} | |||||
| onChange={(date) => { | |||||
| if (!date) return; | |||||
| setValue( | |||||
| "productionDate", | |||||
| date.format(INPUT_DATE_FORMAT), | |||||
| ); | |||||
| // field.onChange(date); | |||||
| }} | |||||
| inputRef={field.ref} | |||||
| slotProps={{ | |||||
| textField: { | |||||
| // required: true, | |||||
| error: Boolean(errors.productionDate?.message), | |||||
| helperText: errors.productionDate?.message, | |||||
| }, | |||||
| }} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| ); | |||||
| }} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("productLotNo")} | |||||
| fullWidth | |||||
| {...register("productLotNo", { | |||||
| // required: "productLotNo required!", | |||||
| })} | |||||
| disabled={disabled} | |||||
| error={Boolean(errors.productLotNo)} | |||||
| helperText={errors.productLotNo?.message} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <Controller | |||||
| control={control} | |||||
| name="expiryDate" | |||||
| // rules={{ required: !Boolean(productionDate) }} | |||||
| render={({ field }) => { | |||||
| return ( | |||||
| <LocalizationProvider | |||||
| dateAdapter={AdapterDayjs} | |||||
| adapterLocale={`${language}-hk`} | |||||
| > | |||||
| <DatePicker | |||||
| {...field} | |||||
| sx={{ width: "100%" }} | |||||
| label={t("expiryDate")} | |||||
| value={expiryDate ? dayjs(expiryDate) : undefined} | |||||
| disabled={disabled} | |||||
| onChange={(date) => { | |||||
| console.log(date); | |||||
| if (!date) return; | |||||
| console.log(date.format(INPUT_DATE_FORMAT)); | |||||
| setValue("expiryDate", date.format(INPUT_DATE_FORMAT)); | |||||
| // field.onChange(date); | |||||
| }} | |||||
| inputRef={field.ref} | |||||
| slotProps={{ | |||||
| textField: { | |||||
| // required: true, | |||||
| error: Boolean(errors.expiryDate?.message), | |||||
| helperText: errors.expiryDate?.message, | |||||
| }, | |||||
| }} | |||||
| /> | |||||
| </LocalizationProvider> | |||||
| ); | |||||
| }} | |||||
| /> | |||||
| </Grid> | |||||
| <Grid item xs={6}> | |||||
| <TextField | |||||
| label={t("acceptedQty")} | |||||
| fullWidth | |||||
| {...register("acceptedQty", { | |||||
| required: "acceptedQty required!", | |||||
| })} | |||||
| disabled={disabled} | |||||
| error={Boolean(errors.acceptedQty)} | |||||
| helperText={errors.acceptedQty?.message} | |||||
| /> | |||||
| </Grid> | |||||
| {/* <Grid item xs={4}> | |||||
| <TextField | |||||
| label={t("acceptedWeight")} | |||||
| fullWidth | |||||
| // {...register("acceptedWeight", { | |||||
| // required: "acceptedWeight required!", | |||||
| // })} | |||||
| disabled={disabled} | |||||
| error={Boolean(errors.acceptedWeight)} | |||||
| helperText={errors.acceptedWeight?.message} | |||||
| /> | |||||
| </Grid> */} | |||||
| </Grid> | |||||
| <Grid | |||||
| container | |||||
| justifyContent="flex-start" | |||||
| alignItems="flex-start" | |||||
| spacing={2} | |||||
| sx={{ mt: 0.5 }} | |||||
| > | |||||
| {/* <Grid item xs={12}> | |||||
| <InputDataGrid<PurchaseQCInput, PurchaseQcResult, EntryError> | |||||
| apiRef={apiRef} | |||||
| checkboxSelection={false} | |||||
| _formKey={"qcCheck"} | |||||
| columns={columns} | |||||
| validateRow={validationTest} | |||||
| /> | |||||
| </Grid> */} | |||||
| </Grid> | |||||
| </Grid> | |||||
| ); | |||||
| }; | |||||
| export default StockInFormVer2; | |||||
| @@ -0,0 +1,23 @@ | |||||
| const dummyQCData = [ | |||||
| { | |||||
| id: 1, | |||||
| qcItem: "目測", | |||||
| isPassed: undefined, | |||||
| failedQty: undefined, | |||||
| remarks: undefined, | |||||
| }, | |||||
| { | |||||
| id: 2, | |||||
| qcItem: "目測2", | |||||
| isPassed: undefined, | |||||
| failedQty: undefined, | |||||
| remarks: undefined, | |||||
| }, | |||||
| { | |||||
| id: 3, | |||||
| qcItem: "目測3", | |||||
| isPassed: undefined, | |||||
| failedQty: undefined, | |||||
| remarks: undefined, | |||||
| }, | |||||
| ] | |||||
| @@ -253,7 +253,7 @@ const RSOverview: React.FC<Props> = ({ type, defaultInputs }) => { | |||||
| // setFilterObj({}); | // setFilterObj({}); | ||||
| // setTempSelectedValue({}); | // setTempSelectedValue({}); | ||||
| refetchData(defaultInputs, "reset"); | refetchData(defaultInputs, "reset"); | ||||
| }, []); | |||||
| }, [defaultInputs, refetchData]); | |||||
| const testRoughScheduleClick = useCallback(async () => { | const testRoughScheduleClick = useCallback(async () => { | ||||
| try { | try { | ||||
| @@ -17,6 +17,9 @@ const StyledDataGrid = styled(DataGrid)(({ theme }) => ({ | |||||
| "& .MuiDataGrid-columnSeparator": { | "& .MuiDataGrid-columnSeparator": { | ||||
| color: theme.palette.primary.main, | color: theme.palette.primary.main, | ||||
| }, | }, | ||||
| "& .MuiDataGrid-row:nth-of-type(even)": { | |||||
| backgroundColor: theme.palette.grey[200], // Light grey for even rows | |||||
| }, | |||||
| })); | })); | ||||
| export default StyledDataGrid; | export default StyledDataGrid; | ||||
| @@ -82,6 +82,19 @@ | |||||
| "Po Code": "採購訂單編號", | "Po Code": "採購訂單編號", | ||||
| "No Warehouse": "沒有倉庫", | "No Warehouse": "沒有倉庫", | ||||
| "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | "Please scan warehouse qr code.": "請掃描倉庫 QR 碼。", | ||||
| "receivedQty": "已來貨數量", | |||||
| "dnQty": "送貨單數量", | |||||
| "Accept submit": "接受來貨", | |||||
| "qc processing": "處理來貨及品檢", | |||||
| "putawayBtn": "上架", | |||||
| "dnNo": "送貨單編號", | |||||
| "dnDate": "送貨單日期", | |||||
| "submitStockIn": "更新來貨資料", | |||||
| "QC Info": "品檢資料", | |||||
| "Escalation History": "品檢資料", | |||||
| "Reject": "拒絕", | "Reject": "拒絕", | ||||
| "submit": "提交", | "submit": "提交", | ||||