| @@ -41,6 +41,7 @@ | |||||
| "react-intl": "^6.5.5", | "react-intl": "^6.5.5", | ||||
| "react-qr-barcode-scanner": "^2.1.5", | "react-qr-barcode-scanner": "^2.1.5", | ||||
| "react-select": "^5.8.0", | "react-select": "^5.8.0", | ||||
| "react-toastify": "^11.0.5", | |||||
| "reactstrap": "^9.2.2", | "reactstrap": "^9.2.2", | ||||
| "styled-components": "^6.1.8", | "styled-components": "^6.1.8", | ||||
| "sweetalert2": "^11.10.3" | "sweetalert2": "^11.10.3" | ||||
| @@ -6,17 +6,18 @@ import { I18nProvider, getServerI18n } from "@/i18n"; | |||||
| import { Typography } from "@mui/material"; | import { Typography } from "@mui/material"; | ||||
| import isString from "lodash/isString"; | import isString from "lodash/isString"; | ||||
| import { notFound } from "next/navigation"; | import { notFound } from "next/navigation"; | ||||
| import { Suspense } from "react"; | |||||
| type Props = {} & SearchParams; | type Props = {} & SearchParams; | ||||
| const PoEdit: React.FC<Props> = async ({ searchParams }) => { | const PoEdit: React.FC<Props> = async ({ searchParams }) => { | ||||
| const type = "po"; | const type = "po"; | ||||
| const { t } = await getServerI18n(type); | const { t } = await getServerI18n(type); | ||||
| console.log(searchParams["id"]) | |||||
| console.log(searchParams["id"]); | |||||
| const id = isString(searchParams["id"]) | const id = isString(searchParams["id"]) | ||||
| ? parseInt(searchParams["id"]) | ? parseInt(searchParams["id"]) | ||||
| : undefined; | : undefined; | ||||
| console.log(id) | |||||
| console.log(id); | |||||
| if (!id) { | if (!id) { | ||||
| notFound(); | notFound(); | ||||
| } | } | ||||
| @@ -24,7 +25,9 @@ const PoEdit: React.FC<Props> = async ({ searchParams }) => { | |||||
| <> | <> | ||||
| {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | {/* <Typography variant="h4">{t("Create Material")}</Typography> */} | ||||
| <I18nProvider namespaces={[type]}> | <I18nProvider namespaces={[type]}> | ||||
| <PoDetail id={id} /> | |||||
| <Suspense fallback={<PoDetail.Loading />}> | |||||
| <PoDetail id={id} /> | |||||
| </Suspense> | |||||
| </I18nProvider> | </I18nProvider> | ||||
| </> | </> | ||||
| ); | ); | ||||
| @@ -26,9 +26,6 @@ const PurchaseOrder: React.FC = async () => { | |||||
| flexWrap="wrap" | flexWrap="wrap" | ||||
| rowGap={2} | rowGap={2} | ||||
| > | > | ||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Purchase Order")} | |||||
| </Typography> | |||||
| {/* <Button | {/* <Button | ||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Add />} | startIcon={<Add />} | ||||
| @@ -36,6 +36,7 @@ import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
| // qc: QcItemWithChecks[]; | // qc: QcItemWithChecks[]; | ||||
| disabled: boolean | |||||
| } | } | ||||
| type EntryError = | type EntryError = | ||||
| | { | | { | ||||
| @@ -46,6 +47,7 @@ type EntryError = | |||||
| const EscalationForm: React.FC<Props> = ({ | const EscalationForm: React.FC<Props> = ({ | ||||
| // qc, | // qc, | ||||
| itemDetail, | itemDetail, | ||||
| disabled | |||||
| }) => { | }) => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| @@ -116,6 +118,7 @@ const EscalationForm: React.FC<Props> = ({ | |||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| max: itemDetail.acceptedQty | max: itemDetail.acceptedQty | ||||
| })} | })} | ||||
| disabled={disabled} | |||||
| defaultValue={itemDetail.acceptedQty} | defaultValue={itemDetail.acceptedQty} | ||||
| error={Boolean(errors.acceptedQty)} | error={Boolean(errors.acceptedQty)} | ||||
| helperText={errors.acceptedQty?.message} | helperText={errors.acceptedQty?.message} | ||||
| @@ -9,6 +9,7 @@ import { | |||||
| import { | import { | ||||
| Box, | Box, | ||||
| Button, | Button, | ||||
| ButtonProps, | |||||
| Collapse, | Collapse, | ||||
| Grid, | Grid, | ||||
| IconButton, | IconButton, | ||||
| @@ -72,6 +73,8 @@ import ReactQrCodeScannerModal, { | |||||
| ScannerConfig, | ScannerConfig, | ||||
| } from "../ReactQrCodeScanner/ReactQrCodeScanner"; | } from "../ReactQrCodeScanner/ReactQrCodeScanner"; | ||||
| import QrModal from "./QrModal"; | import QrModal from "./QrModal"; | ||||
| import { PlayArrow } from "@mui/icons-material"; | |||||
| import DoneIcon from "@mui/icons-material/Done"; | |||||
| type Props = { | type Props = { | ||||
| po: PoResult; | po: PoResult; | ||||
| @@ -97,7 +100,7 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| const params = useSearchParams(); | const params = useSearchParams(); | ||||
| const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); | const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status); | ||||
| const handleComplete = useCallback(async () => { | |||||
| const handleCompletePo = useCallback(async () => { | |||||
| const checkRes = await checkPolAndCompletePo(purchaseOrder.id); | const checkRes = await checkPolAndCompletePo(purchaseOrder.id); | ||||
| console.log(checkRes); | console.log(checkRes); | ||||
| const newPo = await fetchPoInClient(purchaseOrder.id); | const newPo = await fetchPoInClient(purchaseOrder.id); | ||||
| @@ -226,6 +229,49 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| setPutAwayOpen(true); | setPutAwayOpen(true); | ||||
| }, []); | }, []); | ||||
| const buttonData = useMemo(() => { | |||||
| switch (purchaseOrder.status.toLowerCase()) { | |||||
| case "pending": | |||||
| return { | |||||
| buttonName: "start", | |||||
| title: t("Do you want to start?"), | |||||
| confirmButtonText: t("Start"), | |||||
| successTitle: t("Start Success"), | |||||
| errorTitle: t("Start Fail"), | |||||
| buttonText: t("Start PO"), | |||||
| buttonIcon: <PlayArrow />, | |||||
| buttonColor: "success", | |||||
| disabled: false, | |||||
| onClick: handleStartPo, | |||||
| }; | |||||
| case "receiving": | |||||
| return { | |||||
| buttonName: "complete", | |||||
| title: t("Do you want to complete?"), | |||||
| confirmButtonText: t("Complete"), | |||||
| successTitle: t("Complete Success"), | |||||
| errorTitle: t("Complete Fail"), | |||||
| buttonText: t("Complete PO"), | |||||
| buttonIcon: <DoneIcon />, | |||||
| buttonColor: "info", | |||||
| disabled: false, | |||||
| onClick: handleCompletePo, | |||||
| }; | |||||
| default: | |||||
| return { | |||||
| buttonName: "complete", | |||||
| title: t("Do you want to complete?"), | |||||
| confirmButtonText: t("Complete"), | |||||
| successTitle: t("Complete Success"), | |||||
| errorTitle: t("Complete Fail"), | |||||
| buttonText: t("Complete PO"), | |||||
| buttonIcon: <DoneIcon />, | |||||
| buttonColor: "info", | |||||
| disabled: true, | |||||
| }; | |||||
| // break; | |||||
| } | |||||
| }, [purchaseOrder, handleStartPo, handleCompletePo]); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -242,19 +288,29 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| </Grid> | </Grid> | ||||
| </Grid> | </Grid> | ||||
| <Grid container xs={12} justifyContent="start"> | <Grid container xs={12} justifyContent="start"> | ||||
| {purchaseOrder.status.toLowerCase() === "pending" && ( | |||||
| <Grid item> | |||||
| <Button | |||||
| onClick={buttonData.onClick} | |||||
| disabled={buttonData.disabled} | |||||
| color={buttonData.buttonColor as ButtonProps["color"]} | |||||
| startIcon={buttonData.buttonIcon} | |||||
| > | |||||
| {t(buttonData.buttonText)} | |||||
| </Button> | |||||
| </Grid> | |||||
| {/* {purchaseOrder.status.toLowerCase() === "pending" && ( | |||||
| <Grid item> | <Grid item> | ||||
| <Button onClick={handleStartPo}>Start</Button> | <Button onClick={handleStartPo}>Start</Button> | ||||
| </Grid> | </Grid> | ||||
| )} | )} | ||||
| {purchaseOrder.status.toLowerCase() === "receiving" && ( | {purchaseOrder.status.toLowerCase() === "receiving" && ( | ||||
| <Grid item> | <Grid item> | ||||
| <Button onClick={handleComplete}>Complete</Button> | |||||
| <Button onClick={handleCompletePo}>Complete</Button> | |||||
| </Grid> | </Grid> | ||||
| )} | |||||
| )} */} | |||||
| </Grid> | </Grid> | ||||
| {/* <Grid container xs={12} justifyContent="space-between"> | {/* <Grid container xs={12} justifyContent="space-between"> | ||||
| <Button onClick={handleComplete}>Complete</Button> | |||||
| <Button onClick={handleCompletePo}>Complete</Button> | |||||
| </Grid> */} | </Grid> */} | ||||
| <Grid container xs={12} justifyContent="space-between"> | <Grid container xs={12} justifyContent="space-between"> | ||||
| <Grid item xs={8}> | <Grid item xs={8}> | ||||
| @@ -13,6 +13,7 @@ import { | |||||
| Dispatch, | Dispatch, | ||||
| SetStateAction, | SetStateAction, | ||||
| useCallback, | useCallback, | ||||
| useContext, | |||||
| useEffect, | useEffect, | ||||
| useMemo, | useMemo, | ||||
| useState, | useState, | ||||
| @@ -36,6 +37,8 @@ import dayjs from "dayjs"; | |||||
| import arraySupport from "dayjs/plugin/arraySupport"; | import arraySupport from "dayjs/plugin/arraySupport"; | ||||
| import { downloadFile } from "@/app/utils/commonUtil"; | import { downloadFile } from "@/app/utils/commonUtil"; | ||||
| import { fetchPoQrcode } from "@/app/api/pdf/actions"; | import { fetchPoQrcode } from "@/app/api/pdf/actions"; | ||||
| import UploadContext from "../UploadProvider/UploadProvider"; | |||||
| import useUploadContext from "../UploadProvider/useUploadContext"; | |||||
| dayjs.extend(arraySupport); | dayjs.extend(arraySupport); | ||||
| interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
| @@ -97,6 +100,8 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| qc, | qc, | ||||
| warehouse, | warehouse, | ||||
| }) => { | }) => { | ||||
| const { setIsUploading } = useUploadContext(); | |||||
| const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const params = useSearchParams(); | const params = useSearchParams(); | ||||
| @@ -188,9 +193,10 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | ||||
| async (data, event) => { | async (data, event) => { | ||||
| setBtnIsLoading(true); | |||||
| setIsUploading(true) | |||||
| formProps.clearErrors(); | formProps.clearErrors(); | ||||
| let hasErrors = false; | let hasErrors = false; | ||||
| setBtnIsLoading(true); | |||||
| console.log(errors); | console.log(errors); | ||||
| console.log(data); | console.log(data); | ||||
| console.log(itemDetail); | console.log(itemDetail); | ||||
| @@ -240,10 +246,12 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| console.log(args); | console.log(args); | ||||
| setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
| setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
| setIsUploading(false) | |||||
| return; | return; | ||||
| } | } | ||||
| console.log(args); | console.log(args); | ||||
| // setBtnIsLoading(false); | // setBtnIsLoading(false); | ||||
| // setIsUploading(false) | |||||
| // return | // return | ||||
| const res = await updateStockInLine(args); | const res = await updateStockInLine(args); | ||||
| if (Boolean(res.id)) { | if (Boolean(res.id)) { | ||||
| @@ -286,7 +294,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| } | } | ||||
| // add loading | // add loading | ||||
| setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
| setIsUploading(false) | |||||
| setItemDetail(undefined); | setItemDetail(undefined); | ||||
| closeHandler({}, "backdropClick"); | closeHandler({}, "backdropClick"); | ||||
| } | } | ||||
| @@ -295,6 +303,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| } catch (e) { | } catch (e) { | ||||
| // server error | // server error | ||||
| setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
| setIsUploading(false) | |||||
| setServerError(t("An error has occurred. Please try again later.")); | setServerError(t("An error has occurred. Please try again later.")); | ||||
| console.log(e); | console.log(e); | ||||
| } | } | ||||
| @@ -304,6 +313,7 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| const printQrcode = useCallback(async () => { | const printQrcode = useCallback(async () => { | ||||
| setBtnIsLoading(true); | setBtnIsLoading(true); | ||||
| setIsUploading(true) | |||||
| const postData = { stockInLineIds: [itemDetail.id] }; | const postData = { stockInLineIds: [itemDetail.id] }; | ||||
| // const postData = { stockInLineIds: [42,43,44] }; | // const postData = { stockInLineIds: [42,43,44] }; | ||||
| const response = await fetchPoQrcode(postData); | const response = await fetchPoQrcode(postData); | ||||
| @@ -312,9 +322,10 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| downloadFile(new Uint8Array(response.blobValue), response.filename!!); | downloadFile(new Uint8Array(response.blobValue), response.filename!!); | ||||
| } | } | ||||
| setBtnIsLoading(false); | setBtnIsLoading(false); | ||||
| setIsUploading(false) | |||||
| }, [itemDetail, fetchPoQrcode, downloadFile]); | }, [itemDetail, fetchPoQrcode, downloadFile]); | ||||
| const renderSubmitButton = useMemo((): Boolean => { | |||||
| const renderSubmitButton = useMemo((): boolean => { | |||||
| if (itemDetail) { | if (itemDetail) { | ||||
| const status = itemDetail.status; | const status = itemDetail.status; | ||||
| console.log(status); | console.log(status); | ||||
| @@ -355,16 +366,16 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| onSubmit={formProps.handleSubmit(onSubmit)} | onSubmit={formProps.handleSubmit(onSubmit)} | ||||
| > | > | ||||
| {itemDetail !== undefined && type === "qc" && ( | {itemDetail !== undefined && type === "qc" && ( | ||||
| <QcForm qc={qc!!} itemDetail={itemDetail} /> | |||||
| <QcForm qc={qc!!} itemDetail={itemDetail} disabled={renderSubmitButton}/> | |||||
| )} | )} | ||||
| {itemDetail !== undefined && type === "stockIn" && ( | {itemDetail !== undefined && type === "stockIn" && ( | ||||
| <StockInForm itemDetail={itemDetail} /> | |||||
| <StockInForm itemDetail={itemDetail} disabled={renderSubmitButton}/> | |||||
| )} | )} | ||||
| {itemDetail !== undefined && type === "escalation" && ( | {itemDetail !== undefined && type === "escalation" && ( | ||||
| <EscalationForm itemDetail={itemDetail} /> | |||||
| <EscalationForm itemDetail={itemDetail} disabled={renderSubmitButton}/> | |||||
| )} | )} | ||||
| {itemDetail !== undefined && type === "putaway" && ( | {itemDetail !== undefined && type === "putaway" && ( | ||||
| <PutawayForm itemDetail={itemDetail} warehouse={warehouse!!} /> | |||||
| <PutawayForm itemDetail={itemDetail} warehouse={warehouse!!} disabled={renderSubmitButton}/> | |||||
| )} | )} | ||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
| {renderSubmitButton ? ( | {renderSubmitButton ? ( | ||||
| @@ -48,6 +48,7 @@ import { QrCodeInfo } from "@/app/api/qrcode"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
| warehouse: WarehouseResult[]; | warehouse: WarehouseResult[]; | ||||
| disabled: boolean | |||||
| // qc: QcItemWithChecks[]; | // qc: QcItemWithChecks[]; | ||||
| } | } | ||||
| type EntryError = | type EntryError = | ||||
| @@ -70,7 +71,7 @@ const style = { | |||||
| width: "auto", | width: "auto", | ||||
| }; | }; | ||||
| const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => { | |||||
| const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| const { | const { | ||||
| @@ -326,12 +327,13 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => { | |||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| })} | })} | ||||
| // defaultValue={itemDetail.acceptedQty} | // defaultValue={itemDetail.acceptedQty} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.acceptedQty)} | error={Boolean(errors.acceptedQty)} | ||||
| helperText={errors.acceptedQty?.message} | helperText={errors.acceptedQty?.message} | ||||
| /> | /> | ||||
| </Grid> | </Grid> | ||||
| <Grid item xs={1}> | <Grid item xs={1}> | ||||
| <Button onClick={onOpenScanner}>bind</Button> | |||||
| <Button disabled={disabled} onClick={onOpenScanner}>bind</Button> | |||||
| </Grid> | </Grid> | ||||
| <Grid item xs={5.5}> | <Grid item xs={5.5}> | ||||
| {/* <Controller | {/* <Controller | ||||
| @@ -377,6 +379,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => { | |||||
| <TextField | <TextField | ||||
| {...params} | {...params} | ||||
| // label={"Select warehouse"} | // label={"Select warehouse"} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.warehouseId?.message)} | error={Boolean(errors.warehouseId?.message)} | ||||
| helperText={errors.warehouseId?.message} | helperText={errors.warehouseId?.message} | ||||
| // helperText={warehouseHelperText} | // helperText={warehouseHelperText} | ||||
| @@ -40,6 +40,7 @@ import axiosInstance from "@/app/(main)/axios/axiosInstance"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
| qc: QcItemWithChecks[]; | qc: QcItemWithChecks[]; | ||||
| disabled: boolean | |||||
| } | } | ||||
| type EntryError = | type EntryError = | ||||
| | { | | { | ||||
| @@ -49,7 +50,7 @@ type EntryError = | |||||
| type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>; | ||||
| // fetchQcItemCheck | // fetchQcItemCheck | ||||
| const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| const QcForm: React.FC<Props> = ({ qc, itemDetail, disabled }) => { | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| const { | const { | ||||
| @@ -102,7 +103,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| field: "qcItemId", | field: "qcItemId", | ||||
| headerName: "qc Check", | headerName: "qc Check", | ||||
| flex: 1, | flex: 1, | ||||
| editable: true, | |||||
| editable: !disabled, | |||||
| valueFormatter(params) { | valueFormatter(params) { | ||||
| const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | const row = params.id ? params.api.getRow<PoQcRow>(params.id) : null; | ||||
| if (!row) { | if (!row) { | ||||
| @@ -150,7 +151,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| field: "failQty", | field: "failQty", | ||||
| headerName: "failQty", | headerName: "failQty", | ||||
| flex: 1, | flex: 1, | ||||
| editable: true, | |||||
| editable: !disabled, | |||||
| type: "number", | type: "number", | ||||
| renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | renderEditCell(params: GridRenderEditCellParams<PoQcRow>) { | ||||
| // const recordQty = params.row.qty | // const recordQty = params.row.qty | ||||
| @@ -226,7 +227,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| max: itemDetail.acceptedQty, | max: itemDetail.acceptedQty, | ||||
| })} | })} | ||||
| // disabled | |||||
| disabled={disabled} | |||||
| error={Boolean(errors.acceptedQty)} | error={Boolean(errors.acceptedQty)} | ||||
| helperText={errors.acceptedQty?.message} | helperText={errors.acceptedQty?.message} | ||||
| /> | /> | ||||
| @@ -253,6 +254,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| required: "sampleRate required!", | required: "sampleRate required!", | ||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| })} | })} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.sampleRate)} | error={Boolean(errors.sampleRate)} | ||||
| helperText={errors.sampleRate?.message} | helperText={errors.sampleRate?.message} | ||||
| /> | /> | ||||
| @@ -266,6 +268,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| required: "sampleWeight required!", | required: "sampleWeight required!", | ||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| })} | })} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.sampleWeight)} | error={Boolean(errors.sampleWeight)} | ||||
| helperText={errors.sampleWeight?.message} | helperText={errors.sampleWeight?.message} | ||||
| /> | /> | ||||
| @@ -279,6 +282,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => { | |||||
| required: "totalWeight required!", | required: "totalWeight required!", | ||||
| valueAsNumber: true, | valueAsNumber: true, | ||||
| })} | })} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.totalWeight)} | error={Boolean(errors.totalWeight)} | ||||
| helperText={errors.totalWeight?.message} | helperText={errors.totalWeight?.message} | ||||
| /> | /> | ||||
| @@ -1,6 +1,6 @@ | |||||
| "use client"; | "use client"; | ||||
| import { Box, Button, Grid, Modal, ModalProps, Stack } from "@mui/material"; | |||||
| import { Box, Button, Grid, Modal, ModalProps, Stack, Typography } from "@mui/material"; | |||||
| import { useCallback, useEffect, useMemo, useState } from "react"; | import { useCallback, useEffect, useMemo, useState } from "react"; | ||||
| import ReactQrCodeScanner, { | import ReactQrCodeScanner, { | ||||
| ScannerConfig, | ScannerConfig, | ||||
| @@ -18,6 +18,7 @@ import { WarehouseResult } from "@/app/api/warehouse"; | |||||
| import { QrCodeInfo } from "@/app/api/qrcode"; | import { QrCodeInfo } from "@/app/api/qrcode"; | ||||
| import { Check } from "@mui/icons-material"; | import { Check } from "@mui/icons-material"; | ||||
| import { useTranslation } from "react-i18next"; | import { useTranslation } from "react-i18next"; | ||||
| import { useSearchParams } from "next/navigation"; | |||||
| interface Props extends Omit<ModalProps, "children"> { | interface Props extends Omit<ModalProps, "children"> { | ||||
| warehouse: WarehouseResult[]; | warehouse: WarehouseResult[]; | ||||
| @@ -36,8 +37,10 @@ const style = { | |||||
| const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => { | const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => { | ||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const [serverError, setServerError] = useState(""); | const [serverError, setServerError] = useState(""); | ||||
| const params = useSearchParams(); | |||||
| const formProps = useForm<ModalFormInput>({ | const formProps = useForm<ModalFormInput>({ | ||||
| defaultValues: { | defaultValues: { | ||||
| // acceptedQty | |||||
| // ...itemDetail, | // ...itemDetail, | ||||
| }, | }, | ||||
| }); | }); | ||||
| @@ -70,13 +73,27 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => { | |||||
| ); | ); | ||||
| const [itemDetail, setItemDetail] = useState<StockInLine>(); | const [itemDetail, setItemDetail] = useState<StockInLine>(); | ||||
| const [disabledSubmit, setDisabledSubmit] = useState(false); | |||||
| const [unavailableText, setUnavailableText] = useState<string | undefined>(undefined) | |||||
| const fetchStockInLine = useCallback( | const fetchStockInLine = useCallback( | ||||
| async (stockInLineId: number) => { | async (stockInLineId: number) => { | ||||
| setUnavailableText(undefined) | |||||
| const res = await fetchStockInLineInfo(stockInLineId); | const res = await fetchStockInLineInfo(stockInLineId); | ||||
| setItemDetail(res); | |||||
| if (res.status.toLowerCase() === "received") { | |||||
| console.log(res.acceptedQty) | |||||
| formProps.setValue("acceptedQty", res.acceptedQty) | |||||
| setDisabledSubmit(false) | |||||
| setItemDetail(res); | |||||
| } else if (res.status.toLowerCase() === "completed") { | |||||
| setDisabledSubmit(true) | |||||
| } else { | |||||
| // | |||||
| setUnavailableText("Item Not Available") | |||||
| setDisabledSubmit(true) | |||||
| } | |||||
| // return | |||||
| }, | }, | ||||
| [fetchStockInLineInfo] | |||||
| [formProps, itemDetail, fetchStockInLineInfo] | |||||
| ); | ); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| @@ -86,8 +103,11 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => { | |||||
| const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>( | ||||
| async (data, event) => { | async (data, event) => { | ||||
| let hasErrors = false; | let hasErrors = false; | ||||
| console.log("errors"); | |||||
| console.log(errors); | console.log(errors); | ||||
| console.log("data"); | |||||
| console.log(data); | console.log(data); | ||||
| console.log("itemDetail"); | |||||
| console.log(itemDetail); | console.log(itemDetail); | ||||
| try { | try { | ||||
| // add checking | // add checking | ||||
| @@ -95,11 +115,13 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => { | |||||
| //////////////////////// modify this mess later ////////////////////// | //////////////////////// modify this mess later ////////////////////// | ||||
| const args = { | const args = { | ||||
| // id: itemDetail.id, | |||||
| // purchaseOrderId: parseInt(params.get("id")!!), | |||||
| // purchaseOrderLineId: itemDetail.purchaseOrderLineId, | |||||
| // itemId: itemDetail.itemId, | |||||
| // ...data, | |||||
| id: itemDetail?.id, | |||||
| purchaseOrderId: parseInt(params.get("id")!!), | |||||
| purchaseOrderLineId: itemDetail?.purchaseOrderLineId, | |||||
| itemId: itemDetail?.itemId, | |||||
| acceptedQty: data.acceptedQty, | |||||
| warehouseId: data.warehouseId, | |||||
| // ...data, | |||||
| // productionDate: productionDate, | // productionDate: productionDate, | ||||
| } as StockInLineEntry & ModalFormInput; | } as StockInLineEntry & ModalFormInput; | ||||
| ////////////////////////////////////////////////////////////////////// | ////////////////////////////////////////////////////////////////////// | ||||
| @@ -139,20 +161,24 @@ const QrModal: React.FC<Props> = ({ open, onClose, warehouse }) => { | |||||
| <Grid container xs={12}> | <Grid container xs={12}> | ||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| {itemDetail != undefined ? ( | {itemDetail != undefined ? ( | ||||
| unavailableText != undefined ? <Typography variant="h4" marginInlineEnd={2}>{unavailableText}</Typography> | |||||
| : ( | |||||
| <> | <> | ||||
| <PutawayForm itemDetail={itemDetail} warehouse={warehouse} /> | |||||
| <PutawayForm itemDetail={itemDetail} warehouse={warehouse} disabled={false}/> | |||||
| <Stack direction="row" justifyContent="flex-end" gap={1}> | <Stack direction="row" justifyContent="flex-end" gap={1}> | ||||
| <Button | <Button | ||||
| name="submit" | name="submit" | ||||
| variant="contained" | variant="contained" | ||||
| startIcon={<Check />} | startIcon={<Check />} | ||||
| type="submit" | type="submit" | ||||
| // disabled={submitDisabled} | |||||
| disabled={disabledSubmit} | |||||
| > | > | ||||
| {t("submit")} | {t("submit")} | ||||
| </Button> | </Button> | ||||
| </Stack> | </Stack> | ||||
| </> | </> | ||||
| ) | |||||
| ) : ( | ) : ( | ||||
| <ReactQrCodeScanner scannerConfig={scannerConfig} /> | <ReactQrCodeScanner scannerConfig={scannerConfig} /> | ||||
| )} | )} | ||||
| @@ -43,6 +43,7 @@ import dayjs from "dayjs"; | |||||
| interface Props { | interface Props { | ||||
| itemDetail: StockInLine; | itemDetail: StockInLine; | ||||
| // qc: QcItemWithChecks[]; | // qc: QcItemWithChecks[]; | ||||
| disabled: boolean | |||||
| } | } | ||||
| type EntryError = | type EntryError = | ||||
| | { | | { | ||||
| @@ -55,6 +56,7 @@ type EntryError = | |||||
| const StockInForm: React.FC<Props> = ({ | const StockInForm: React.FC<Props> = ({ | ||||
| // qc, | // qc, | ||||
| itemDetail, | itemDetail, | ||||
| disabled | |||||
| }) => { | }) => { | ||||
| const { | const { | ||||
| t, | t, | ||||
| @@ -116,6 +118,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| {...register("productLotNo", { | {...register("productLotNo", { | ||||
| // required: "productLotNo required!", | // required: "productLotNo required!", | ||||
| })} | })} | ||||
| disabled={disabled} | |||||
| // error={Boolean(errors.productLotNo)} | // error={Boolean(errors.productLotNo)} | ||||
| // helperText={errors.productLotNo?.message} | // helperText={errors.productLotNo?.message} | ||||
| /> | /> | ||||
| @@ -136,6 +139,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| sx={{ width: "100%" }} | sx={{ width: "100%" }} | ||||
| label={t("receiptDate")} | label={t("receiptDate")} | ||||
| value={dayjs(watch("receiptDate"))} | value={dayjs(watch("receiptDate"))} | ||||
| disabled={disabled} | |||||
| onChange={(date) => { | onChange={(date) => { | ||||
| if (!date) return | if (!date) return | ||||
| // setValue("receiptDate", date.format(INPUT_DATE_FORMAT)); | // setValue("receiptDate", date.format(INPUT_DATE_FORMAT)); | ||||
| @@ -162,6 +166,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| {...register("acceptedQty", { | {...register("acceptedQty", { | ||||
| required: "acceptedQty required!", | required: "acceptedQty required!", | ||||
| })} | })} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.acceptedQty)} | error={Boolean(errors.acceptedQty)} | ||||
| helperText={errors.acceptedQty?.message} | helperText={errors.acceptedQty?.message} | ||||
| /> | /> | ||||
| @@ -173,6 +178,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| // {...register("acceptedWeight", { | // {...register("acceptedWeight", { | ||||
| // required: "acceptedWeight required!", | // required: "acceptedWeight required!", | ||||
| // })} | // })} | ||||
| disabled={disabled} | |||||
| error={Boolean(errors.acceptedWeight)} | error={Boolean(errors.acceptedWeight)} | ||||
| helperText={errors.acceptedWeight?.message} | helperText={errors.acceptedWeight?.message} | ||||
| /> | /> | ||||
| @@ -193,6 +199,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| sx={{ width: "100%" }} | sx={{ width: "100%" }} | ||||
| label={t("productionDate")} | label={t("productionDate")} | ||||
| value={productionDate ? dayjs(productionDate) : undefined} | value={productionDate ? dayjs(productionDate) : undefined} | ||||
| disabled={disabled} | |||||
| onChange={(date) => { | onChange={(date) => { | ||||
| if (!date) return | if (!date) return | ||||
| setValue("productionDate", date.format(INPUT_DATE_FORMAT)); | setValue("productionDate", date.format(INPUT_DATE_FORMAT)); | ||||
| @@ -228,6 +235,7 @@ const StockInForm: React.FC<Props> = ({ | |||||
| sx={{ width: "100%" }} | sx={{ width: "100%" }} | ||||
| label={t("expiryDate")} | label={t("expiryDate")} | ||||
| value={expiryDate ? dayjs(expiryDate) : undefined} | value={expiryDate ? dayjs(expiryDate) : undefined} | ||||
| disabled={disabled} | |||||
| onChange={(date) => { | onChange={(date) => { | ||||
| console.log(date) | console.log(date) | ||||
| if (!date) return | if (!date) return | ||||
| @@ -7,14 +7,18 @@ import { useRouter, useSearchParams } from "next/navigation"; | |||||
| import SearchBox, { Criterion } from "../SearchBox"; | import SearchBox, { Criterion } from "../SearchBox"; | ||||
| import SearchResults, { Column } from "../SearchResults"; | import SearchResults, { Column } from "../SearchResults"; | ||||
| import { EditNote } from "@mui/icons-material"; | import { EditNote } from "@mui/icons-material"; | ||||
| import { Button, Grid, Tab, Tabs, TabsProps, Typography } from "@mui/material"; | |||||
| import QrModal from "../PoDetail/QrModal"; | |||||
| import { WarehouseResult } from "@/app/api/warehouse"; | |||||
| type Props = { | type Props = { | ||||
| po: PoResult[]; | po: PoResult[]; | ||||
| warehouse: WarehouseResult[]; | |||||
| }; | }; | ||||
| type SearchQuery = Partial<Omit<PoResult, "id">>; | type SearchQuery = Partial<Omit<PoResult, "id">>; | ||||
| type SearchParamNames = keyof SearchQuery; | type SearchParamNames = keyof SearchQuery; | ||||
| const PoSearch: React.FC<Props> = ({ po }) => { | |||||
| const PoSearch: React.FC<Props> = ({ po, warehouse }) => { | |||||
| const [filteredPo, setFilteredPo] = useState<PoResult[]>(po); | const [filteredPo, setFilteredPo] = useState<PoResult[]>(po); | ||||
| const { t } = useTranslation("po"); | const { t } = useTranslation("po"); | ||||
| const router = useRouter(); | const router = useRouter(); | ||||
| @@ -27,7 +31,6 @@ const PoSearch: React.FC<Props> = ({ po }) => { | |||||
| return searchCriteria; | return searchCriteria; | ||||
| }, [t, po]); | }, [t, po]); | ||||
| const onDetailClick = useCallback( | const onDetailClick = useCallback( | ||||
| (po: PoResult) => { | (po: PoResult) => { | ||||
| router.push(`/po/edit?id=${po.id}`); | router.push(`/po/edit?id=${po.id}`); | ||||
| @@ -35,10 +38,7 @@ const PoSearch: React.FC<Props> = ({ po }) => { | |||||
| [router] | [router] | ||||
| ); | ); | ||||
| const onDeleteClick = useCallback( | |||||
| (po: PoResult) => {}, | |||||
| [router] | |||||
| ); | |||||
| const onDeleteClick = useCallback((po: PoResult) => {}, [router]); | |||||
| const columns = useMemo<Column<PoResult>[]>( | const columns = useMemo<Column<PoResult>[]>( | ||||
| () => [ | () => [ | ||||
| @@ -68,12 +68,12 @@ const PoSearch: React.FC<Props> = ({ po }) => { | |||||
| // name: "name", | // name: "name", | ||||
| // label: t("Name"), | // label: t("Name"), | ||||
| // }, | // }, | ||||
| // { | |||||
| // name: "action", | |||||
| // label: t(""), | |||||
| // buttonIcon: <GridDeleteIcon />, | |||||
| // onClick: onDeleteClick, | |||||
| // }, | |||||
| // { | |||||
| // name: "action", | |||||
| // label: t(""), | |||||
| // buttonIcon: <GridDeleteIcon />, | |||||
| // onClick: onDeleteClick, | |||||
| // }, | |||||
| ], | ], | ||||
| [filteredPo] | [filteredPo] | ||||
| ); | ); | ||||
| @@ -82,23 +82,56 @@ const PoSearch: React.FC<Props> = ({ po }) => { | |||||
| setFilteredPo(po); | setFilteredPo(po); | ||||
| }, [po]); | }, [po]); | ||||
| const [isOpenScanner, setOpenScanner] = useState(false); | |||||
| const onOpenScanner = useCallback(() => { | |||||
| setOpenScanner(true); | |||||
| }, []); | |||||
| const onCloseScanner = useCallback(() => { | |||||
| setOpenScanner(false); | |||||
| }, []); | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <SearchBox | |||||
| criteria={searchCriteria} | |||||
| onSearch={(query) => { | |||||
| setFilteredPo( | |||||
| po.filter((p) => { | |||||
| return ( | |||||
| p.code.toLowerCase().includes(query.code.toLowerCase()) | |||||
| // p.name.toLowerCase().includes(query.name.toLowerCase()) | |||||
| <Grid container> | |||||
| <Grid item xs={8}> | |||||
| <Typography variant="h4" marginInlineEnd={2}> | |||||
| {t("Purchase Order")} | |||||
| </Typography> | |||||
| </Grid> | |||||
| <Grid | |||||
| item | |||||
| xs={4} | |||||
| display="flex" | |||||
| justifyContent="end" | |||||
| alignItems="end" | |||||
| > | |||||
| <QrModal | |||||
| open={isOpenScanner} | |||||
| onClose={onCloseScanner} | |||||
| warehouse={warehouse} | |||||
| /> | |||||
| <Button onClick={onOpenScanner}>bind</Button> | |||||
| </Grid> | |||||
| </Grid> | |||||
| <> | |||||
| <SearchBox | |||||
| criteria={searchCriteria} | |||||
| onSearch={(query) => { | |||||
| setFilteredPo( | |||||
| po.filter((p) => { | |||||
| return p.code | |||||
| .toLowerCase() | |||||
| .includes(query.code.toLowerCase()); | |||||
| // p.name.toLowerCase().includes(query.name.toLowerCase()) | |||||
| }) | |||||
| ); | ); | ||||
| }) | |||||
| ); | |||||
| }} | |||||
| onReset={onReset} | |||||
| /> | |||||
| <SearchResults<PoResult> items={filteredPo} columns={columns}/> | |||||
| }} | |||||
| onReset={onReset} | |||||
| /> | |||||
| <SearchResults<PoResult> items={filteredPo} columns={columns} /> | |||||
| </> | |||||
| </> | </> | ||||
| ); | ); | ||||
| }; | }; | ||||
| @@ -10,6 +10,7 @@ import { fetchPoList, PoResult } from "@/app/api/po"; | |||||
| import dayjs from "dayjs"; | import dayjs from "dayjs"; | ||||
| import arraySupport from "dayjs/plugin/arraySupport"; | import arraySupport from "dayjs/plugin/arraySupport"; | ||||
| import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; | ||||
| import { fetchWarehouseList } from "@/app/api/warehouse"; | |||||
| dayjs.extend(arraySupport); | dayjs.extend(arraySupport); | ||||
| interface SubComponents { | interface SubComponents { | ||||
| @@ -26,9 +27,11 @@ const PoSearchWrapper: React.FC<Props> & SubComponents = async ( | |||||
| } | } | ||||
| ) => { | ) => { | ||||
| const [ | const [ | ||||
| po | |||||
| po, | |||||
| warehouse, | |||||
| ] = await Promise.all([ | ] = await Promise.all([ | ||||
| fetchPoList() | |||||
| fetchPoList(), | |||||
| fetchWarehouseList(), | |||||
| ]); | ]); | ||||
| console.log(po) | console.log(po) | ||||
| const fixPoDate = po.map((p) => { | const fixPoDate = po.map((p) => { | ||||
| @@ -37,7 +40,7 @@ const PoSearchWrapper: React.FC<Props> & SubComponents = async ( | |||||
| orderDate: dayjs(p.orderDate).format(OUTPUT_DATE_FORMAT) | orderDate: dayjs(p.orderDate).format(OUTPUT_DATE_FORMAT) | ||||
| }) | }) | ||||
| }) | }) | ||||
| return <PoSearch po={fixPoDate} />; | |||||
| return <PoSearch po={fixPoDate} warehouse={warehouse}/>; | |||||
| }; | }; | ||||
| PoSearchWrapper.Loading = PoSearchLoading; | PoSearchWrapper.Loading = PoSearchLoading; | ||||