@@ -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; | ||||