diff --git a/src/app/api/po/actions.ts b/src/app/api/po/actions.ts index a21e333..5c3daa7 100644 --- a/src/app/api/po/actions.ts +++ b/src/app/api/po/actions.ts @@ -12,6 +12,7 @@ export interface PostStockInLiineResponse { id: number | null; name: string; code: string; + type?: string message: string | null; errorPosition: string | keyof T; entity: StockInLine | StockInLine[] @@ -70,22 +71,12 @@ export const testFetch = cache(async (id: number) => { }); }); -export const testFetch2 = cache(async (id: number) => { - return serverFetchJson(`${BASE_API_URL}/qcResult/${id}`, { - next: { tags: ["test"] }, +export const fetchStockInLineInfo = cache(async (stockInLineId: number) => { + return serverFetchJson(`${BASE_API_URL}/stockInLine/${stockInLineId}`, { + next: { tags: ["stockInLine"] }, }); }); -export const test3 = cache(async (id: number) => { - var endpoint = `${BASE_API_URL}/qcResult/${id}` - // if (startDate.length > 0) endpoint += `&startDate=${startDate}` - // if (endDate.length > 0) endpoint += `&endDate=${endDate}` - // if (teamId > 0 ) endpoint += `&teamId=${teamId}` - return serverFetchJson(endpoint, { - next: { tags: ["test"] }, - }); -}) - export const createStockInLine = async (data: StockInLineEntry) => { const stockInLine = await serverFetchJson>(`${BASE_API_URL}/stockInLine/create`, { method: "POST", @@ -104,4 +95,13 @@ export const updateStockInLine = async (data: StockInLineEntry & ModalFormInput) return stockInLine } +export const checkPolAndCompletePo = async (poId: number) => { + const po = await serverFetchJson>(`${BASE_API_URL}/po//check/${poId}`, { + method: "POST", + body: JSON.stringify({ poId }), + headers: { "Content-Type": "application/json" }, + }); + return po +} + diff --git a/src/app/api/po/index.ts b/src/app/api/po/index.ts index c10cbe4..375f98c 100644 --- a/src/app/api/po/index.ts +++ b/src/app/api/po/index.ts @@ -48,6 +48,7 @@ export interface StockInLine { lotNo: string poCode: string uom: Uom + defaultWarehouseId: number // id for now } export const fetchPoList = cache(async () => { diff --git a/src/app/api/qrcode/index.ts b/src/app/api/qrcode/index.ts new file mode 100644 index 0000000..04ce08f --- /dev/null +++ b/src/app/api/qrcode/index.ts @@ -0,0 +1,11 @@ +import { cache } from "react"; +import "server-only"; +import { serverFetchJson } from "@/app/utils/fetchUtil"; +import { BASE_API_URL } from "@/config/api"; + +export interface QrCodeInfo { + stockInLineId?: number; + itemId: number + warehouseId?: number + lotNo?: string + } \ No newline at end of file diff --git a/src/components/InputDataGrid/InputDataGrid.tsx b/src/components/InputDataGrid/InputDataGrid.tsx index dac888c..839db6f 100644 --- a/src/components/InputDataGrid/InputDataGrid.tsx +++ b/src/components/InputDataGrid/InputDataGrid.tsx @@ -26,14 +26,14 @@ import { GridValidRowModel, useGridApiRef, } from "@mui/x-data-grid"; -import { useFormContext } from "react-hook-form"; +import { set, useFormContext } from "react-hook-form"; import SaveIcon from "@mui/icons-material/Save"; import DeleteIcon from "@mui/icons-material/Delete"; import CancelIcon from "@mui/icons-material/Cancel"; import { Add } from "@mui/icons-material"; import { Box, Button, Typography } from "@mui/material"; import { useTranslation } from "react-i18next"; -import { GridApiCommunity } from "@mui/x-data-grid/internals"; +import { GridApiCommunity, GridSlotsComponentsProps } from "@mui/x-data-grid/internals"; interface ResultWithId { id: string | number; @@ -67,6 +67,7 @@ export interface InputDataGridProps { _formKey: keyof T; columns: GridColDef[]; validateRow: (newRow: GridRowModel>) => E; + needAdd?: Boolean }; export interface SelectionInputDataGridProps { // thinking how do @@ -75,6 +76,7 @@ export interface SelectionInputDataGridProps { // thinking how do _formKey: keyof T; columns: GridColDef[]; validateRow: (newRow: GridRowModel>) => E; + needAdd?: Boolean } export type Props = InputDataGridProps | SelectionInputDataGridProps @@ -94,10 +96,11 @@ export class ProcessRowUpdateError extends Error { // E == error function InputDataGrid({ apiRef, - checkboxSelection, + checkboxSelection = false, _formKey, columns, validateRow, + needAdd, }: Props) { const { t, @@ -113,6 +116,7 @@ function InputDataGrid({ [] ); const list: TableRow[] = getValues(formKey); + // console.log(list) const [rows, setRows] = useState[]>(() => { const list: TableRow[] = getValues(formKey); return list && list.length > 0 ? list : []; @@ -348,9 +352,13 @@ function InputDataGrid({ footer: FooterToolbar, noRowsOverlay: NoRowsOverlay, } : undefined} - slotProps={!checkboxSelection ? { + slotProps={!checkboxSelection && Boolean(needAdd) ? { footer: { child: footer }, - } : undefined} + }: undefined + // slotProps={renderFooter ? { + // footer: { child: footer }, + // }: undefined + } /> ) } diff --git a/src/components/PoDetail/PoDetail.tsx b/src/components/PoDetail/PoDetail.tsx index 363c99d..56baf92 100644 --- a/src/components/PoDetail/PoDetail.tsx +++ b/src/components/PoDetail/PoDetail.tsx @@ -28,9 +28,26 @@ import { } from "@mui/material"; import { useTranslation } from "react-i18next"; // import InputDataGrid, { TableRow } from "../InputDataGrid/InputDataGrid"; -import { GridColDef, GridRowModel, useGridApiRef } from "@mui/x-data-grid"; -import { testFetch } from "@/app/api/po/actions"; -import { useCallback, useContext, useEffect, useMemo, useState } from "react"; +import { + GridColDef, + GridRowId, + GridRowModel, + useGridApiRef, +} from "@mui/x-data-grid"; +import { + checkPolAndCompletePo, + fetchStockInLineInfo, + PurchaseQcResult, + testFetch, +} from "@/app/api/po/actions"; +import { + use, + useCallback, + useContext, + useEffect, + useMemo, + useState, +} from "react"; import { FormProvider, useForm } from "react-hook-form"; import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; @@ -46,6 +63,13 @@ import QrCodeScanner from "../QrCodeScanner"; import { CameraDevice, Html5Qrcode } from "html5-qrcode"; import { CameraContext } from "../Cameras/CameraProvider"; import StyledDataGrid from "../StyledDataGrid"; +import { QrCodeInfo } from "@/app/api/qrcode"; +import { fetchQcResult } from "@/app/api/qc/actions"; +import PoQcStockInModal from "./PoQcStockInModal"; +import ReactQrCodeScannerModal, { + ScannerConfig, +} from "../ReactQrCodeScanner/ReactQrCodeScanner"; +import QrModal from "./QrModal"; type Props = { po: PoResult; @@ -72,7 +96,6 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { const [open, setOpen] = useState(false); const [processedQty, setProcessedQty] = useState(row.processed); const [currStatus, setCurrStatus] = useState(row.status); - useEffect(() => { if (processedQty === row.qty) { setCurrStatus("completed".toUpperCase()); @@ -145,6 +168,13 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { ); } + const [tabIndex, setTabIndex] = useState(0); + const handleTabChange = useCallback>( + (_e, newValue) => { + setTabIndex(newValue); + }, + [] + ); const [isOpenScanner, setOpenScanner] = useState(false); const onOpenScanner = useCallback(() => { @@ -155,17 +185,33 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { setOpenScanner(false); }, []); - const handleScanSuccess = useCallback((result: string) => { - console.log(result); + const [itemInfo, setItemInfo] = useState< + StockInLine & { warehouseId?: number } + >(); + const [putAwayOpen, setPutAwayOpen] = useState(false); + // const [scannedInfo, setScannedInfo] = useState({} as QrCodeInfo); + + const closePutAwayModal = useCallback(() => { + setPutAwayOpen(false); + setItemInfo(undefined); + }, []); + const openPutAwayModal = useCallback(() => { + setPutAwayOpen(true); }, []); - const [tabIndex, setTabIndex] = useState(0); - const handleTabChange = useCallback>( - (_e, newValue) => { - setTabIndex(newValue); - }, - [] - ); + const handleComplete = useCallback(async () => { + const res = await checkPolAndCompletePo(po.id) + if (res.type === "completed") { + // toast.success(res.message) + console.log(res) + return + } + if (res.type === "receiving") { + // toast.error(res.message) + console.log(res) + return + } + }, [checkPolAndCompletePo]); return ( <> @@ -180,30 +226,34 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { {po.code} - - {/* go to scanner */} - {/* scan item */} - {/* putaway model form with defaultValues */} - - - - + {/* + + */} + + - + {/* */} - {/* tab 1 */} + {/* */} + {/* scanner */} + {/* */} + + + + + + {/* tab 1 */} @@ -238,6 +288,18 @@ const PoDetail: React.FC = ({ po, qc, warehouse }) => { /> */} + {itemInfo !== undefined && ( + <> + + + )} ); }; diff --git a/src/components/PoDetail/PoDetailWrapper.tsx b/src/components/PoDetail/PoDetailWrapper.tsx index d16a76b..b5e6db6 100644 --- a/src/components/PoDetail/PoDetailWrapper.tsx +++ b/src/components/PoDetail/PoDetailWrapper.tsx @@ -20,23 +20,14 @@ type Props = { }; const PoDetailWrapper: React.FC & SubComponents = async ({ id }) => { - const [ - poWithStockInLine, - warehouse, - qc, - ] = await Promise.all([ + const [poWithStockInLine, warehouse, qc] = await Promise.all([ fetchPoWithStockInLines(id), fetchWarehouseList(), - fetchQcItemCheck() - ]) + fetchQcItemCheck(), + ]); // const poWithStockInLine = await fetchPoWithStockInLines(id) - console.log(poWithStockInLine) - console.log(warehouse) - console.log(qc) - - - return ; + return ; }; PoDetailWrapper.Loading = PoDetailLoading; diff --git a/src/components/PoDetail/PoInputGrid.tsx b/src/components/PoDetail/PoInputGrid.tsx index f2ae97e..ef05e76 100644 --- a/src/components/PoDetail/PoInputGrid.tsx +++ b/src/components/PoDetail/PoInputGrid.tsx @@ -33,17 +33,14 @@ import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; import { QcItemWithChecks } from "src/app/api/qc"; import PlayArrowIcon from "@mui/icons-material/PlayArrow"; import { PurchaseOrderLine, StockInLine } from "@/app/api/po"; -import { - createStockInLine, - PurchaseQcResult, -} from "@/app/api/po/actions"; +import { createStockInLine, PurchaseQcResult } from "@/app/api/po/actions"; import { useSearchParams } from "next/navigation"; import { returnWeightUnit, calculateWeight, stockInLineStatusMap, } from "@/app/utils/formatUtil"; -import PoQcStockInModal from "./PoQcStockInModal"; +// import PoQcStockInModal from "./PoQcStockInModal"; import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; import { WarehouseResult } from "@/app/api/warehouse"; import LooksOneIcon from "@mui/icons-material/LooksOne"; @@ -57,6 +54,8 @@ import QrCodeIcon from "@mui/icons-material/QrCode"; import { downloadFile } from "@/app/utils/commonUtil"; import { fetchPoQrcode } from "@/app/api/pdf/actions"; import { fetchQcResult } from "@/app/api/qc/actions"; +import PoQcStockInModal from "./PoQcStockInModal"; + interface ResultWithId { id: number; } @@ -116,7 +115,9 @@ function PoInputGrid({ ); console.log(stockInLine); const [entries, setEntries] = useState(stockInLine || []); - const [modalInfo, setModalInfo] = useState(); + const [modalInfo, setModalInfo] = useState< + StockInLine & { qcResult?: PurchaseQcResult[] } + >(); const [qcOpen, setQcOpen] = useState(false); const [escalOpen, setEscalOpen] = useState(false); const [stockInOpen, setStockInOpen] = useState(false); @@ -176,19 +177,9 @@ function PoInputGrid({ }, [createStockInLine] ); - const fetchQcDefaultValue = useCallback(async () => { - // const authHeader = axiosInstance.defaults.headers['Authorization']; - // if (!authHeader) { - // return; // Exit the function if the token is not set - // } - // console.log(authHeader) - // console.log(NEXT_PUBLIC_API_URL) - // const res = await axiosInstance.get(`${NEXT_PUBLIC_API_URL}/qcResult/${itemDetail.id}`) - // const res = await testFetch2(itemDetail.id) - const res = await fetchQcResult(itemDetail.id); - console.log(res); - return res - }, [axiosInstance]); + const fetchQcDefaultValue = useCallback(async (stockInLineId: GridRowId) => { + return await fetchQcResult(stockInLineId as number); + }, []); const handleQC = useCallback( (id: GridRowId, params: any) => async () => { @@ -196,11 +187,12 @@ function PoInputGrid({ ...prev, [id]: { mode: GridRowModes.View }, })); - const qcResult = await fetchQcDefaultValue(); - console.log(qcResult) + const qcResult = await fetchQcDefaultValue(id); + console.log(params.row); + console.log(qcResult); setModalInfo({ ...params.row, - qcResult: qcResult + qcResult: qcResult, }); // set default values setTimeout(() => { @@ -414,7 +406,7 @@ function PoInputGrid({ }} disabled={ stockInLineStatusMap[status] <= 0 || - stockInLineStatusMap[status] >= 5 + stockInLineStatusMap[status] >= 4 } // set _isNew to false after posting // or check status @@ -429,7 +421,7 @@ function PoInputGrid({ color: "primary.main", // marginRight: 1, }} - disabled={stockInLineStatusMap[status] !== 6} + disabled={stockInLineStatusMap[status] <= 2 || stockInLineStatusMap[status] >= 7} // set _isNew to false after posting // or check status onClick={handleStockIn(params.row.id, params)} @@ -563,13 +555,6 @@ function PoInputGrid({ [apiRef] ); - // useEffect(() => { - // const total = entries.reduce( - // (acc, curr) => acc + (curr.acceptedQty || 0), - // 0 - // ); - // setDefaultQty(itemDetail.qty - total); - // }, [entries]); const footer = ( - ) : undefined - } + ) : undefined} - - + + ); }; diff --git a/src/components/PoDetail/PutawayForm.tsx b/src/components/PoDetail/PutawayForm.tsx index adc7df9..ee7418f 100644 --- a/src/components/PoDetail/PutawayForm.tsx +++ b/src/components/PoDetail/PutawayForm.tsx @@ -4,10 +4,13 @@ import { PurchaseQcResult, PutawayInput } from "@/app/api/po/actions"; import { Autocomplete, Box, + Button, Card, CardContent, FormControl, Grid, + Modal, + ModalProps, Stack, TextField, Tooltip, @@ -35,8 +38,10 @@ import { GridEditInputCell } from "@mui/x-data-grid"; import { StockInLine } from "@/app/api/po"; import { WarehouseResult } from "@/app/api/warehouse"; import { stockInLineStatusMap } from "@/app/utils/formatUtil"; -import { QRCodeSVG } from 'qrcode.react'; +import { QRCodeSVG } from "qrcode.react"; import { QrCode } from "../QrCode"; +import ReactQrCodeScanner, { ScannerConfig } from "../ReactQrCodeScanner/ReactQrCodeScanner"; +import { QrCodeInfo } from "@/app/api/qrcode"; interface Props { itemDetail: StockInLine; @@ -51,6 +56,18 @@ type EntryError = // type PoQcRow = TableRow, EntryError>; +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + bgcolor: "background.paper", + pt: 5, + px: 5, + pb: 10, + width: "auto", +}; + const PutawayForm: React.FC = ({ itemDetail, warehouse }) => { const { t } = useTranslation(); const apiRef = useGridApiRef(); @@ -108,18 +125,56 @@ const PutawayForm: React.FC = ({ itemDetail, warehouse }) => { [] ); - const qrContent = useMemo(() => ({ - itemId: itemDetail.itemId, - lotNo: itemDetail.lotNo, - // expiryDate: itemDetail.expiryDate, - // productionDate: itemDetail.productionDate, - // supplier: itemDetail.supplier, - // poCode: itemDetail.poCode, - }),[itemDetail]) + + const qrContent = useMemo( + () => ({ + stockInLineId: itemDetail.id, + itemId: itemDetail.itemId, + lotNo: itemDetail.lotNo, + // warehouseId: 1 // for testing + // expiryDate: itemDetail.expiryDate, + // productionDate: itemDetail.productionDate, + // supplier: itemDetail.supplier, + // poCode: itemDetail.poCode, + }), + [itemDetail] + ); + const [isOpenScanner, setOpenScanner] = useState(false); + + const closeHandler = useCallback>( + (...args) => { + setOpenScanner(false); + }, + [] + ); + + const onOpenScanner = useCallback(() => { + setOpenScanner(true); + }, []); + + const onCloseScanner = useCallback(() => { + setOpenScanner(false); + }, []); + const scannerConfig = useMemo( + () => ({ + onUpdate: (err, result) => { + if (result) { + const data: QrCodeInfo = JSON.parse(result.getText()); + console.log(data); + if (data.warehouseId) { + setWarehouseId(data.warehouseId); + onCloseScanner() + } + } else return; + }, + }), + [] + ); useEffect(() => { - setValue("status", "completed") - }, []) + setValue("status", "completed"); + }, []); + return ( @@ -190,7 +245,7 @@ const PutawayForm: React.FC = ({ itemDetail, warehouse }) => { disabled /> - + = ({ itemDetail, warehouse }) => { /> + + o.value === 1)} + // onChange={onChange} + getOptionLabel={(option) => option.label} + options={options} + renderInput={(params) => } + /> + + + = ({ itemDetail, warehouse }) => { helperText={errors.acceptedQty?.message} /> - + + + + = ({ itemDetail, warehouse }) => { /> - - + + - - {/* - - apiRef={apiRef} - checkboxSelection={false} - _formKey={"qcCheck"} - columns={columns} - validateRow={validation} - /> - */} - + + */} + + + + + + ); }; diff --git a/src/components/PoDetail/QcForm.tsx b/src/components/PoDetail/QcForm.tsx index bbff1d6..b9d4337 100644 --- a/src/components/PoDetail/QcForm.tsx +++ b/src/components/PoDetail/QcForm.tsx @@ -31,14 +31,14 @@ import QcSelect from "./QcSelect"; import { GridEditInputCell } from "@mui/x-data-grid"; import { StockInLine } from "@/app/api/po"; import { stockInLineStatusMap } from "@/app/utils/formatUtil"; -import { fetchQcItemCheck } from "@/app/api/qc/actions"; +import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions"; import { QcItemWithChecks } from "@/app/api/qc"; import axios from "@/app/(main)/axios/axiosInstance"; import { NEXT_PUBLIC_API_URL } from "@/config/api"; import axiosInstance from "@/app/(main)/axios/axiosInstance"; interface Props { - itemDetail: StockInLine; + itemDetail: StockInLine qc: QcItemWithChecks[]; } type EntryError = @@ -65,24 +65,8 @@ const QcForm: React.FC = ({ qc, itemDetail }) => { clearErrors, } = useFormContext(); console.log(itemDetail); + console.log(defaultValues); - // const [qc, setQc] = useState([]) - const fetchQcCheck = useCallback(async () => { - const authHeader = axiosInstance.defaults.headers['Authorization']; - if (!authHeader) { - return; // Exit the function if the token is not set - } - const params = { - itemId: itemDetail.itemId - } - console.log(params) - const res = await axios.get(`${NEXT_PUBLIC_API_URL}/qcCheck`, { params }) - console.log(res) - }, [axios]) - useEffect(() => { - fetchQcCheck() - }, [fetchQcCheck]) - const [recordQty, setRecordQty] = useState(0); const columns = useMemo( () => [ @@ -203,7 +187,7 @@ const QcForm: React.FC = ({ qc, itemDetail }) => { spacing={2} sx={{ mt: 0.5 }} > - + = ({ qc, itemDetail }) => { helperText={errors.acceptedQty?.message} /> - + = ({ qc, itemDetail }) => { // helperText={errors.sampleRate?.message} /> - + = ({ qc, itemDetail }) => { helperText={errors.sampleRate?.message} /> - + = ({ qc, itemDetail }) => { helperText={errors.sampleWeight?.message} /> - + = ({ qc, itemDetail }) => { _formKey={"qcResult"} columns={columns} validateRow={validation} + needAdd={itemDetail.status === "qc" || itemDetail.status === "pending"} /> diff --git a/src/components/PoDetail/QrCoderScanner.tsx b/src/components/PoDetail/QrCoderScanner.tsx new file mode 100644 index 0000000..0bd8ba6 --- /dev/null +++ b/src/components/PoDetail/QrCoderScanner.tsx @@ -0,0 +1,42 @@ +"use client" + +import { Box, Modal, ModalProps } from "@mui/material" +import { useCallback } from "react"; + +interface Props extends Omit { + +}; + +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + bgcolor: "background.paper", + pt: 5, + px: 5, + pb: 10, + width: { xs: "80%", sm: "80%", md: "80%" }, + }; + +const QrCodeScanner: React.FC = ({ + open, +}) => { + + const closeHandler = useCallback>( + (...args) => { + // onClose?.(...args); + // reset(); + }, + [] + ); + + return ( + + + + + + ) +} +export default QrCodeScanner \ No newline at end of file diff --git a/src/components/PoDetail/QrModal.tsx b/src/components/PoDetail/QrModal.tsx new file mode 100644 index 0000000..e3b0cbd --- /dev/null +++ b/src/components/PoDetail/QrModal.tsx @@ -0,0 +1,167 @@ +"use client"; + +import { Box, Button, Grid, Modal, ModalProps, Stack } from "@mui/material"; +import { useCallback, useEffect, useMemo, useState } from "react"; +import ReactQrCodeScanner, { + ScannerConfig, +} from "../ReactQrCodeScanner/ReactQrCodeScanner"; +import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; +import { + fetchStockInLineInfo, + ModalFormInput, + StockInLineEntry, + updateStockInLine, +} from "@/app/api/po/actions"; +import PutawayForm from "./PutawayForm"; +import { StockInLine } from "@/app/api/po"; +import { WarehouseResult } from "@/app/api/warehouse"; +import { QrCodeInfo } from "@/app/api/qrcode"; +import { Check } from "@mui/icons-material"; +import { useTranslation } from "react-i18next"; + +interface Props extends Omit { + warehouse: WarehouseResult[]; +} +const style = { + position: "absolute", + top: "50%", + left: "50%", + transform: "translate(-50%, -50%)", + bgcolor: "background.paper", + pt: 5, + px: 5, + pb: 10, + width: "auto", +}; +const QrModal: React.FC = ({ open, onClose, warehouse }) => { + const { t } = useTranslation(); + const [serverError, setServerError] = useState(""); + const formProps = useForm({ + defaultValues: { + // ...itemDetail, + }, + }); + const errors = formProps.formState.errors; + const closeHandler = useCallback>( + (...args) => { + onClose?.(...args); + setItemDetail(undefined); + setStockInLineId(undefined); + // reset(); + }, + [onClose] + ); + const [stockInLineId, setStockInLineId] = useState(); + const scannerConfig = useMemo( + () => ({ + onUpdate: (err, result) => { + if (result) { + const data: QrCodeInfo = JSON.parse(result.getText()); + console.log(data); + if (data.stockInLineId) { + console.log("still got in"); + console.log(data.stockInLineId); + setStockInLineId(data.stockInLineId); + } + } else return; + }, + }), + [] + ); + + const [itemDetail, setItemDetail] = useState(); + + const fetchStockInLine = useCallback( + async (stockInLineId: number) => { + const res = await fetchStockInLineInfo(stockInLineId); + setItemDetail(res); + }, + [fetchStockInLineInfo] + ); + + useEffect(() => { + if (stockInLineId) fetchStockInLine(stockInLineId); + }, [stockInLineId]); + + const onSubmit = useCallback>( + async (data, event) => { + let hasErrors = false; + console.log(errors); + console.log(data); + console.log(itemDetail); + try { + // add checking + // const qty = data.sampleRate + + //////////////////////// modify this mess later ////////////////////// + const args = { + // id: itemDetail.id, + // purchaseOrderId: parseInt(params.get("id")!!), + // purchaseOrderLineId: itemDetail.purchaseOrderLineId, + // itemId: itemDetail.itemId, + // ...data, + // productionDate: productionDate, + } as StockInLineEntry & ModalFormInput; + ////////////////////////////////////////////////////////////////////// + console.log(args); + // return + if (hasErrors) { + setServerError(t("An error has occurred. Please try again later.")); + return false; + } + return; + const res = await updateStockInLine(args); + if (Boolean(res.id)) { + // update entries + console.log(res); + // add loading + // closeHandler({}, "backdropClick"); + } + console.log(res); + // if (res) + } catch (e) { + // server error + setServerError(t("An error has occurred. Please try again later.")); + console.log(e); + } + }, + [t, itemDetail] + ); + + return ( + + + + + + {itemDetail != undefined ? ( + <> + + + + + + ) : ( + + )} + + + + + + ); +}; + +export default QrModal; diff --git a/src/components/QrCodeScanner/QrCodeScanner.tsx b/src/components/QrCodeScanner/QrCodeScanner.tsx index fc7184d..409dfa1 100644 --- a/src/components/QrCodeScanner/QrCodeScanner.tsx +++ b/src/components/QrCodeScanner/QrCodeScanner.tsx @@ -1,173 +1,234 @@ -import { Autocomplete, Box, Button, Card, CardContent, Grid, Modal, ModalProps, Stack, SxProps, TextField, Typography } from "@mui/material"; -import { CameraDevice, Html5Qrcode, Html5QrcodeCameraScanConfig, Html5QrcodeFullConfig, Html5QrcodeResult, Html5QrcodeScanner, Html5QrcodeScannerState, QrcodeErrorCallback, QrcodeSuccessCallback } from "html5-qrcode"; +import { + Autocomplete, + Box, + Button, + Card, + CardContent, + Grid, + Modal, + ModalProps, + Stack, + SxProps, + TextField, + Typography, +} from "@mui/material"; +import { + CameraDevice, + Html5Qrcode, + Html5QrcodeCameraScanConfig, + Html5QrcodeFullConfig, + Html5QrcodeResult, + Html5QrcodeScanner, + Html5QrcodeScannerState, + QrcodeErrorCallback, + QrcodeSuccessCallback, +} from "html5-qrcode"; import { Html5QrcodeError } from "html5-qrcode/esm/core"; import { Html5QrcodeScannerConfig } from "html5-qrcode/esm/html5-qrcode-scanner"; -import React, { RefObject, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import React, { + RefObject, + useCallback, + useEffect, + useMemo, + useRef, + useState, +} from "react"; import { useTranslation } from "react-i18next"; -import QrCodeScannerIcon from '@mui/icons-material/QrCodeScanner'; -import StopCircleOutlinedIcon from '@mui/icons-material/StopCircleOutlined'; -import CloseOutlinedIcon from '@mui/icons-material/CloseOutlined'; +import QrCodeScannerIcon from "@mui/icons-material/QrCodeScanner"; +import StopCircleOutlinedIcon from "@mui/icons-material/StopCircleOutlined"; +import CloseOutlinedIcon from "@mui/icons-material/CloseOutlined"; +import { QrCodeInfo } from "@/app/api/qrcode"; const scannerSx: React.CSSProperties = { - position: "absolute", - // top: "50%", - // left: "50%", - // transform: "translate(-50%, -50%)", - width: "90%", - maxHeight: "10%", - maxWidth: 1400, + position: "absolute", + // top: "50%", + // left: "50%", + // transform: "translate(-50%, -50%)", + width: "90%", + maxHeight: "10%", + maxWidth: 1400, }; type QrCodeScannerProps = { - cameras: CameraDevice[] - title?: string, - contents?: string[], - onScanSuccess: (result: string) => void, - onScanError?: (error: string) => void, - isOpen: boolean, - onClose: () => void -} + cameras: CameraDevice[]; + title?: string; + contents?: string[]; + onScanSuccess: (qrCodeInfo: QrCodeInfo) => void; + onScanError?: (error: string) => void; + isOpen: boolean; + onClose: () => void; +}; const QrCodeScanner: React.FC = ({ - title, - contents, - onScanSuccess, - onScanError, - isOpen, - onClose + title, + contents, + onScanSuccess, + onScanError, + isOpen, + onClose, }) => { - const { t } = useTranslation() - const [isScanned, setIsScanned] = useState(false) - const [scanner, setScanner] = useState(null) - const [cameraList, setCameraList] = useState([]) - const [selectedCameraId, setSelectedCameraId] = useState(null) - const stringList = ["ABC: abc", "123:123", "ABC: abc", "123:123", "ABC: abc", "123:123"] - - const scannerConfig: Html5QrcodeFullConfig = { - verbose: false + const { t } = useTranslation(); + const [isScanned, setIsScanned] = useState(false); + const [scanner, setScanner] = useState(null); + const [cameraList, setCameraList] = useState([]); + const [selectedCameraId, setSelectedCameraId] = useState(null); + const stringList = [ + "ABC: abc", + "123:123", + "ABC: abc", + "123:123", + "ABC: abc", + "123:123", + ]; + + const scannerConfig: Html5QrcodeFullConfig = { + verbose: false, + }; + + const cameraConfig: Html5QrcodeCameraScanConfig = { + fps: 10, + qrbox: { width: 250, height: 250 }, + // aspectRatio: cardRef.current ? (cardRef.current.offsetWidth / cardRef.current.offsetHeight) : 1.78, + aspectRatio: (window.innerWidth / window.innerHeight) * 1.5, // can be better + }; + + // MediaTrackConstraintSet + const mediaTrackConstraintSet = { + facingMode: "environment", + }; + + const handleScanStart = useCallback(() => { + if (scanner && selectedCameraId) { + if (scanner.getState() === Html5QrcodeScannerState.SCANNING) { + console.log("first"); + scanner.stop(); + } + + scanner.start( + selectedCameraId, + cameraConfig, + handleScanSuccess, + handleScanError + ); } + }, [selectedCameraId, scanner]); - const cameraConfig: Html5QrcodeCameraScanConfig = { - fps: 10, - qrbox: { width: 250, height: 250 }, - // aspectRatio: cardRef.current ? (cardRef.current.offsetWidth / cardRef.current.offsetHeight) : 1.78, - aspectRatio: (window.innerWidth / window.innerHeight) * 1.5 // can be better - }; + const handleCameraList = useCallback(async () => { + const cameras = await Html5Qrcode.getCameras(); + setCameraList(cameras); + if (cameras.length > 0) { + handleCameraChange(cameras[cameras.length - 1].id); + } + }, []); + + const handleCameraChange = useCallback((id: string) => { + setSelectedCameraId(id); + }, []); - // MediaTrackConstraintSet - const mediaTrackConstraintSet = { - facingMode: "environment" + const switchScanStatus = useCallback(() => { + if (scanner) { + console.log(isScanned); + switch (isScanned) { + case true: + setIsScanned(false); + scanner.resume(); + break; + case false: + setIsScanned(true); + scanner.pause(true); + break; + } } + }, [scanner, isScanned]); - const handleScanStart = useCallback(() => { - if (scanner && selectedCameraId) { - if (scanner.getState() === Html5QrcodeScannerState.SCANNING) { - console.log("first") - scanner.stop() - } + const handleScanSuccess = useCallback( + (decodedText, result) => { + if (scanner) { + console.log(`Decoded text: ${decodedText}`); + const parseData: QrCodeInfo = JSON.parse(decodedText); + console.log(parseData); + // Handle the decoded text as needed + switchScanStatus(); + onScanSuccess(parseData); + } + }, + [scanner, onScanSuccess] + ); - scanner.start( - selectedCameraId, - cameraConfig, - handleScanSuccess, - handleScanError - ) - } - - }, [selectedCameraId, scanner]) - - const handleCameraList = useCallback(async () => { - const cameras = await Html5Qrcode.getCameras() - setCameraList(cameras) - if (cameras.length > 0) { - handleCameraChange(cameras[cameras.length-1].id) - } - }, []) - - const handleCameraChange = useCallback((id: string) => { - setSelectedCameraId(id) - }, []) - - const switchScanStatus = useCallback(() => { - if (scanner) { - console.log(isScanned) - switch (isScanned) { - case true: - setIsScanned(false); - scanner.resume(); - break; - case false: - setIsScanned(true); - scanner.pause(true); - break; - } - } - }, [scanner, isScanned]) - - const handleScanSuccess = useCallback((decodedText, result) => { - if (scanner) { - console.log(`Decoded text: ${decodedText}`); - const parseData = JSON.parse(decodedText) - console.log(parseData) - // Handle the decoded text as needed - switchScanStatus() - onScanSuccess(decodedText) - } - }, [scanner, onScanSuccess]) - - const handleScanError = useCallback((errorMessage, error) => { - // console.log(`Error: ${errorMessage}`); - - if (onScanError) { - onScanError(errorMessage) - } - }, [scanner, onScanError]) - - const handleScanCloseButton = useCallback(async () => { - if (scanner) { - console.log("Cleaning up scanner..."); - await scanner.stop() - scanner.clear() - onClose() - } - }, [scanner]) - - // close modal without using Cancel Button - const handleScanClose = useCallback(async () => { - if (scanner && !isOpen) { - handleScanCloseButton() - } - }, [scanner, isOpen, handleScanCloseButton]) - - // -------------------------------------------------------// - useEffect(() => { - setScanner(new Html5Qrcode( - "qr-reader", - scannerConfig - )) - - handleCameraList() - }, []) - - useEffect(() => { - handleScanStart() - }, [scanner, selectedCameraId]); - - useEffect(() => { - handleScanClose() - }, [isOpen]) - - return ( - <> - - {title && - {"Title"} - } - - -