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