| @@ -4,6 +4,9 @@ This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next | |||
| It is recommended to use the same node and npm versions for development. An easy way to do so would be to use `nvm` ([Linux/MacOS](https://github.com/nvm-sh/nvm), [Windows](https://github.com/coreybutler/nvm-windows)). | |||
| ## Version | |||
| nvm: 1.1.12 | |||
| After installing `nvm`, run: | |||
| ```bash | |||
| @@ -29,4 +32,12 @@ This project uses the following libraries: | |||
| - [NextJS](https://nextjs.org/docs) | |||
| - [Next-Auth](https://next-auth.js.org/getting-started/example) | |||
| - [Material UI](https://mui.com/material-ui/getting-started/) | |||
| - [i18next](https://www.i18next.com/overview/getting-started) | |||
| - [i18next](https://www.i18next.com/overview/getting-started) | |||
| ## Qrcode Testing | |||
| https://stackoverflow.com/questions/16835421/how-to-allow-chrome-to-access-my-camera-on-localhost | |||
| Local Qrcode testing require setting tweak | |||
| Steps: | |||
| 1. Navigate to chrome://flags/#unsafely-treat-insecure-origin-as-secure in Chrome. | |||
| 2. Find and enable the Insecure origins treated as secure section (see below). | |||
| 3. Add any addresses you want to ignore the secure origin policy for. Remember to include the port number too (if required). | |||
| @@ -6,8 +6,9 @@ import Box from "@mui/material/Box"; | |||
| import { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig"; | |||
| import Stack from "@mui/material/Stack"; | |||
| import Breadcrumb from "@/components/Breadcrumb"; | |||
| import {AxiosProvider} from "@/app/(main)/axios/AxiosProvider"; | |||
| import {SetupAxiosInterceptors} from "@/app/(main)/axios/axiosInstance"; | |||
| import { AxiosProvider } from "@/app/(main)/axios/AxiosProvider"; | |||
| import { SetupAxiosInterceptors } from "@/app/(main)/axios/axiosInstance"; | |||
| import { CameraProvider } from "@/components/Cameras/CameraProvider"; | |||
| export default async function MainLayout({ | |||
| children, | |||
| @@ -20,30 +21,32 @@ export default async function MainLayout({ | |||
| redirect("/login"); | |||
| } | |||
| if(session){ | |||
| SetupAxiosInterceptors(session?.accessToken); | |||
| if (session) { | |||
| SetupAxiosInterceptors(session?.accessToken); | |||
| } | |||
| return ( | |||
| <CameraProvider> | |||
| <AxiosProvider> | |||
| <> | |||
| <AppBar | |||
| profileName={session.user.name!} | |||
| avatarImageSrc={session.user.image || undefined} | |||
| /> | |||
| <Box | |||
| component="main" | |||
| sx={{ | |||
| marginInlineStart: { xs: 0, xl: NAVIGATION_CONTENT_WIDTH }, | |||
| padding: { xs: "1rem", sm: "1.5rem", lg: "3rem" }, | |||
| }} | |||
| > | |||
| <Stack spacing={2}> | |||
| <Breadcrumb /> | |||
| {children} | |||
| </Stack> | |||
| </Box> | |||
| </> | |||
| <> | |||
| <AppBar | |||
| profileName={session.user.name!} | |||
| avatarImageSrc={session.user.image || undefined} | |||
| /> | |||
| <Box | |||
| component="main" | |||
| sx={{ | |||
| marginInlineStart: { xs: 0, xl: NAVIGATION_CONTENT_WIDTH }, | |||
| padding: { xs: "1rem", sm: "1.5rem", lg: "3rem" }, | |||
| }} | |||
| > | |||
| <Stack spacing={2}> | |||
| <Breadcrumb /> | |||
| {children} | |||
| </Stack> | |||
| </Box> | |||
| </> | |||
| </AxiosProvider> | |||
| </CameraProvider> | |||
| ); | |||
| } | |||
| @@ -5,6 +5,7 @@ import { revalidateTag } from "next/cache"; | |||
| import { cache } from "react"; | |||
| import { PoResult, StockInLine } from "."; | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { QcItemResult } from "../settings/qcItem"; | |||
| // import { BASE_API_URL } from "@/config/api"; | |||
| export interface PostStockInLiineResponse<T> { | |||
| @@ -69,6 +70,22 @@ export const testFetch = cache(async (id: number) => { | |||
| }); | |||
| }); | |||
| export const testFetch2 = cache(async (id: number) => { | |||
| return serverFetchJson<any[]>(`${BASE_API_URL}/qcResult/${id}`, { | |||
| next: { tags: ["test"] }, | |||
| }); | |||
| }); | |||
| 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<any[]>(endpoint, { | |||
| next: { tags: ["test"] }, | |||
| }); | |||
| }) | |||
| export const createStockInLine = async (data: StockInLineEntry) => { | |||
| const stockInLine = await serverFetchJson<PostStockInLiineResponse<StockInLineEntry>>(`${BASE_API_URL}/stockInLine/create`, { | |||
| method: "POST", | |||
| @@ -5,6 +5,10 @@ import { cache } from "react"; | |||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | |||
| import { QcItemWithChecks } from "."; | |||
| export interface QcResult { | |||
| } | |||
| export const fetchQcItemCheck = cache(async (itemId?: number) => { | |||
| var url = `${BASE_API_URL}/qcCheck` | |||
| if (itemId) url +=`/${itemId}` | |||
| @@ -12,4 +16,10 @@ export const fetchQcItemCheck = cache(async (itemId?: number) => { | |||
| next: { tags: ["qc"] }, | |||
| }); | |||
| }); | |||
| export const fetchQcResult = cache(async (id: number) => { | |||
| return serverFetchJson<any[]>(`${BASE_API_URL}/qcResult/${id}`, { | |||
| next: { tags: ["qc"] }, | |||
| }); | |||
| }); | |||
| @@ -8,7 +8,7 @@ export interface Uom { | |||
| code: string | |||
| name: string | |||
| unit1: string | |||
| unit1Qty: number | |||
| unit1Qty: number | |||
| unit2?: string | |||
| unit2Qty: number | |||
| unit3?: string | |||
| @@ -17,4 +17,4 @@ export interface Uom { | |||
| unit4Qty: number | |||
| sizeInGram: number | |||
| gramPerSmallestUnit: number | |||
| } | |||
| } | |||
| @@ -6,5 +6,4 @@ export const downloadFile = (blobData: Uint8Array, filename: string) => { | |||
| link.href = url; | |||
| link.setAttribute("download", filename); | |||
| link.click(); | |||
| } | |||
| } | |||
| @@ -0,0 +1,26 @@ | |||
| "use client"; | |||
| import { CameraDevice, Html5Qrcode } from "html5-qrcode"; | |||
| import React, { createContext, useContext, useEffect, useState } from "react"; | |||
| export const CameraContext = createContext<CameraDevice[]>([]); | |||
| export const CameraProvider: React.FC<{ children: React.ReactNode }> = ({ | |||
| children, | |||
| }) => { | |||
| const [cameras, setCameras] = useState<CameraDevice[]>([]); | |||
| useEffect(() => { | |||
| const fetchCameras = async () => { | |||
| const res = await Html5Qrcode.getCameras(); | |||
| if (res) { | |||
| setCameras(res); | |||
| } | |||
| }; | |||
| fetchCameras(); | |||
| }, []); | |||
| return ( | |||
| <CameraContext.Provider value={cameras}>{children}</CameraContext.Provider> | |||
| ); | |||
| }; | |||
| @@ -30,7 +30,7 @@ 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, useEffect, useMemo, useState } from "react"; | |||
| import { 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"; | |||
| @@ -42,6 +42,10 @@ import { QcItemWithChecks } from "@/app/api/qc"; | |||
| import { useSearchParams } from "next/navigation"; | |||
| import { WarehouseResult } from "@/app/api/warehouse"; | |||
| import { calculateWeight, returnWeightUnit } from "@/app/utils/formatUtil"; | |||
| import QrCodeScanner from "../QrCodeScanner"; | |||
| import { CameraDevice, Html5Qrcode } from "html5-qrcode"; | |||
| import { CameraContext } from "../Cameras/CameraProvider"; | |||
| import StyledDataGrid from "../StyledDataGrid"; | |||
| type Props = { | |||
| po: PoResult; | |||
| @@ -54,9 +58,10 @@ type EntryError = | |||
| [field in keyof StockInLine]?: string; | |||
| } | |||
| | undefined; | |||
| // type PolRow = TableRow<Partial<StockInLine>, EntryError>; | |||
| const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
| const cameras = useContext(CameraContext); | |||
| console.log(cameras); | |||
| const { t } = useTranslation(); | |||
| const apiRef = useGridApiRef(); | |||
| const [rows, setRows] = useState<PurchaseOrderLine[]>(po.pol || []); | |||
| @@ -77,8 +82,16 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
| setCurrStatus("pending".toUpperCase()); | |||
| } | |||
| }, [processedQty]); | |||
| const totalWeight = useMemo(() => calculateWeight(row.qty, row.uom), []); | |||
| const weightUnit = useMemo(() => returnWeightUnit(row.uom), []); | |||
| const totalWeight = useMemo( | |||
| () => calculateWeight(row.qty, row.uom), | |||
| [calculateWeight] | |||
| ); | |||
| const weightUnit = useMemo( | |||
| () => returnWeightUnit(row.uom), | |||
| [returnWeightUnit] | |||
| ); | |||
| return ( | |||
| <> | |||
| <TableRow sx={{ "& > *": { borderBottom: "unset" }, color: "black" }}> | |||
| @@ -133,6 +146,27 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
| ); | |||
| } | |||
| const [isOpenScanner, setOpenScanner] = useState(false); | |||
| const onOpenScanner = useCallback(() => { | |||
| setOpenScanner(true); | |||
| }, []); | |||
| const onCloseScanner = useCallback(() => { | |||
| setOpenScanner(false); | |||
| }, []); | |||
| const handleScanSuccess = useCallback((result: string) => { | |||
| console.log(result); | |||
| }, []); | |||
| const [tabIndex, setTabIndex] = useState(0); | |||
| const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>( | |||
| (_e, newValue) => { | |||
| setTabIndex(newValue); | |||
| }, | |||
| [] | |||
| ); | |||
| return ( | |||
| <> | |||
| <Stack | |||
| @@ -140,12 +174,37 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
| // component="form" | |||
| // onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | |||
| > | |||
| <Grid> | |||
| <Typography mb={2} variant="h4"> | |||
| {po.code} | |||
| </Typography> | |||
| <Grid container xs={12} justifyContent="space-between"> | |||
| <Grid item> | |||
| <Typography mb={2} variant="h4"> | |||
| {po.code} | |||
| </Typography> | |||
| </Grid> | |||
| <Grid item> | |||
| {/* go to scanner */} | |||
| {/* scan item */} | |||
| {/* putaway model form with defaultValues */} | |||
| <QrCodeScanner | |||
| cameras={cameras} | |||
| isOpen={isOpenScanner} | |||
| onClose={onCloseScanner} | |||
| onScanSuccess={handleScanSuccess} | |||
| /> | |||
| <Button onClick={onOpenScanner}>bind</Button> | |||
| </Grid> | |||
| </Grid> | |||
| <Grid> | |||
| <Grid container xs={12}> | |||
| <Tabs | |||
| value={tabIndex} | |||
| onChange={handleTabChange} | |||
| variant="scrollable" | |||
| > | |||
| <Tab label={t("General")} iconPosition="end" /> | |||
| <Tab label={t("Bind Storage")} iconPosition="end" /> | |||
| </Tabs> | |||
| </Grid> | |||
| {/* tab 1 */} | |||
| <Grid sx={{ display: tabIndex === 0 ? "block" : "none" }}> | |||
| <TableContainer component={Paper}> | |||
| <Table aria-label="collapsible table"> | |||
| <TableHead> | |||
| @@ -172,6 +231,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||
| </Table> | |||
| </TableContainer> | |||
| </Grid> | |||
| {/* tab 2 */} | |||
| <Grid sx={{ display: tabIndex === 1 ? "block" : "none" }}> | |||
| {/* <StyledDataGrid | |||
| /> */} | |||
| </Grid> | |||
| </Stack> | |||
| </> | |||
| ); | |||
| @@ -33,22 +33,30 @@ 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, testFetch } 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 { | |||
| returnWeightUnit, | |||
| calculateWeight, | |||
| stockInLineStatusMap, | |||
| } from "@/app/utils/formatUtil"; | |||
| import PoQcStockInModal from "./PoQcStockInModal"; | |||
| import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; | |||
| import { WarehouseResult } from "@/app/api/warehouse"; | |||
| import LooksOneIcon from '@mui/icons-material/LooksOne'; | |||
| import LooksTwoIcon from '@mui/icons-material/LooksTwo'; | |||
| import Looks3Icon from '@mui/icons-material/Looks3'; | |||
| import LooksOneIcon from "@mui/icons-material/LooksOne"; | |||
| import LooksTwoIcon from "@mui/icons-material/LooksTwo"; | |||
| import Looks3Icon from "@mui/icons-material/Looks3"; | |||
| import axiosInstance from "@/app/(main)/axios/axiosInstance"; | |||
| // import axios, { AxiosRequestConfig } from "axios"; | |||
| import { NEXT_PUBLIC_API_URL } from "@/config/api"; | |||
| import qs from 'qs'; | |||
| import QrCodeIcon from '@mui/icons-material/QrCode'; | |||
| import { BASE_API_URL, NEXT_PUBLIC_API_URL } from "@/config/api"; | |||
| import qs from "qs"; | |||
| 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"; | |||
| interface ResultWithId { | |||
| id: number; | |||
| } | |||
| @@ -98,7 +106,7 @@ function PoInputGrid({ | |||
| stockInLine, | |||
| warehouse, | |||
| }: Props) { | |||
| console.log(itemDetail) | |||
| console.log(itemDetail); | |||
| const { t } = useTranslation("home"); | |||
| const apiRef = useGridApiRef(); | |||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | |||
| @@ -108,7 +116,7 @@ function PoInputGrid({ | |||
| ); | |||
| console.log(stockInLine); | |||
| const [entries, setEntries] = useState<StockInLineRow[]>(stockInLine || []); | |||
| const [modalInfo, setModalInfo] = useState<StockInLine>(); | |||
| const [modalInfo, setModalInfo] = useState<StockInLine & {qcResult?: PurchaseQcResult[]}>(); | |||
| const [qcOpen, setQcOpen] = useState(false); | |||
| const [escalOpen, setEscalOpen] = useState(false); | |||
| const [stockInOpen, setStockInOpen] = useState(false); | |||
| @@ -122,9 +130,7 @@ function PoInputGrid({ | |||
| }); | |||
| useEffect(() => { | |||
| const completedList = entries.filter( | |||
| (e) => e.status === "completed" | |||
| ); | |||
| const completedList = entries.filter((e) => e.status === "completed"); | |||
| const processedQty = completedList.reduce( | |||
| (acc, curr) => acc + (curr.acceptedQty || 0), | |||
| 0 | |||
| @@ -150,6 +156,7 @@ function PoInputGrid({ | |||
| console.log(params); | |||
| const oldId = params.row.id; | |||
| console.log(oldId); | |||
| console.log(params.row); | |||
| const postData = { | |||
| itemId: params.row.itemId, | |||
| itemNo: params.row.itemNo, | |||
| @@ -167,17 +174,21 @@ function PoInputGrid({ | |||
| // openStartModal(); | |||
| }, 200); | |||
| }, | |||
| [] | |||
| [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) | |||
| const res = await axiosInstance.get<QcItemWithChecks[]>(`${NEXT_PUBLIC_API_URL}/qcResult/${itemDetail.id}`) | |||
| console.log(res) | |||
| }, [axiosInstance]) | |||
| // 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<QcItemWithChecks[]>(`${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 handleQC = useCallback( | |||
| (id: GridRowId, params: any) => async () => { | |||
| @@ -185,15 +196,20 @@ function PoInputGrid({ | |||
| ...prev, | |||
| [id]: { mode: GridRowModes.View }, | |||
| })); | |||
| setModalInfo(params.row); | |||
| // await fetchQcDefaultValue() | |||
| const qcResult = await fetchQcDefaultValue(); | |||
| console.log(qcResult) | |||
| setModalInfo({ | |||
| ...params.row, | |||
| qcResult: qcResult | |||
| }); | |||
| // set default values | |||
| setTimeout(() => { | |||
| // open qc modal | |||
| console.log("delayed"); | |||
| openQcModal(); | |||
| }, 200); | |||
| }, | |||
| [] | |||
| [fetchQcDefaultValue] | |||
| ); | |||
| const handleEscalation = useCallback( | |||
| (id: GridRowId, params: any) => () => { | |||
| @@ -246,15 +262,19 @@ function PoInputGrid({ | |||
| [] | |||
| ); | |||
| const printQrcode = useCallback(async (row: any) => { | |||
| console.log(row.id) | |||
| const postData = {stockInLineIds: [row.id]} | |||
| const response = await fetchPoQrcode(postData) | |||
| if (response) { | |||
| console.log(response) | |||
| downloadFile(new Uint8Array(response.blobValue), response.filename!!) | |||
| } | |||
| }, [fetchPoQrcode, downloadFile]) | |||
| const printQrcode = useCallback( | |||
| async (row: any) => { | |||
| console.log(row.id); | |||
| const postData = { stockInLineIds: [row.id] }; | |||
| // const postData = { stockInLineIds: [42,43,44] }; | |||
| const response = await fetchPoQrcode(postData); | |||
| if (response) { | |||
| console.log(response); | |||
| downloadFile(new Uint8Array(response.blobValue), response.filename!!); | |||
| } | |||
| }, | |||
| [fetchPoQrcode, downloadFile] | |||
| ); | |||
| const handleQrCode = useCallback( | |||
| (id: GridRowId, params: any) => () => { | |||
| @@ -269,7 +289,7 @@ function PoInputGrid({ | |||
| // return the record with its status as pending | |||
| // update layout | |||
| console.log("delayed"); | |||
| printQrcode(params.row) | |||
| printQrcode(params.row); | |||
| }, 200); | |||
| }, | |||
| [] | |||
| @@ -334,7 +354,10 @@ function PoInputGrid({ | |||
| headerName: "weight", | |||
| flex: 0.5, | |||
| renderCell: (params) => { | |||
| const weight = calculateWeight(params.row.acceptedQty, params.row.uom); | |||
| const weight = calculateWeight( | |||
| params.row.acceptedQty, | |||
| params.row.uom | |||
| ); | |||
| const weightUnit = returnWeightUnit(params.row.uom); | |||
| return `${weight} ${weightUnit}`; | |||
| }, | |||
| @@ -375,9 +398,7 @@ function PoInputGrid({ | |||
| color: "primary.main", | |||
| // marginRight: 1, | |||
| }} | |||
| disabled={ | |||
| stockInLineStatusMap[status] < 1 | |||
| } | |||
| disabled={stockInLineStatusMap[status] < 1} | |||
| // set _isNew to false after posting | |||
| // or check status | |||
| onClick={handleQC(params.row.id, params)} | |||
| @@ -463,7 +484,7 @@ function PoInputGrid({ | |||
| ); | |||
| const addRow = useCallback(() => { | |||
| console.log(itemDetail) | |||
| console.log(itemDetail); | |||
| const newEntry = { | |||
| id: Date.now(), | |||
| _isNew: true, | |||
| @@ -1,6 +1,6 @@ | |||
| "use client"; | |||
| import { ModalFormInput, PurchaseQCInput, StockInInput, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions"; | |||
| import { ModalFormInput, PurchaseQCInput, PurchaseQcResult, StockInInput, StockInLineEntry, updateStockInLine } from "@/app/api/po/actions"; | |||
| import { Box, Button, Modal, ModalProps, Stack } from "@mui/material"; | |||
| import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; | |||
| import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | |||
| @@ -18,7 +18,7 @@ import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||
| interface CommonProps extends Omit<ModalProps, "children"> { | |||
| setEntries: Dispatch<SetStateAction<StockInLineRow[]>> | |||
| itemDetail: StockInLine; | |||
| itemDetail: StockInLine & {qcResult?: PurchaseQcResult[]}; | |||
| qc?: QcItemWithChecks[]; | |||
| warehouse?: any[]; | |||
| type: "qc" | "stockIn" | "escalation" | "putaway" | |||
| @@ -58,7 +58,7 @@ const style = { | |||
| const PoQcStockInModal: React.FC<Props> = ({ | |||
| type, | |||
| setEntries, | |||
| open, | |||
| open, | |||
| onClose, | |||
| itemDetail, | |||
| qc, | |||
| @@ -70,8 +70,19 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||
| const params = useSearchParams() | |||
| console.log(params.get("id")) | |||
| const [defaultValues, setDefaultValues] = useState({}); | |||
| const defaultValue = useMemo(() => { | |||
| // switch (type) { | |||
| // case "qc": | |||
| // return {qcResult: itemDetail.qcResult} | |||
| // } | |||
| // return {} | |||
| return {...itemDetail} | |||
| }, []) | |||
| // const formProps = useForm<ModalFormInput>({ | |||
| // defaultValues: defaultValues ? defaultValues : {}, | |||
| // }); | |||
| const formProps = useForm<ModalFormInput>({ | |||
| defaultValues: defaultValues ? defaultValues : {}, | |||
| defaultValues: defaultValue | |||
| }); | |||
| const errors = formProps.formState.errors; | |||
| const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | |||
| @@ -155,17 +166,22 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||
| const renderSubmitButton = useMemo((): Boolean => { | |||
| if (itemDetail) { | |||
| const status = itemDetail.status | |||
| console.log(status) | |||
| switch (type) { | |||
| case "qc": | |||
| return stockInLineStatusMap[status] === 1 | |||
| case "putaway": | |||
| return stockInLineStatusMap[status] === 7 | |||
| case "stockIn": | |||
| return stockInLineStatusMap[status] === 6 | |||
| default: | |||
| return false; // Handle unexpected type | |||
| } | |||
| } else return false | |||
| }, [type, itemDetail]) | |||
| renderSubmitButton | |||
| useEffect(() => { | |||
| console.log(renderSubmitButton) | |||
| }, [renderSubmitButton]) | |||
| return ( | |||
| <> | |||
| <Modal open={open} onClose={closeHandler}> | |||
| @@ -19,6 +19,7 @@ const scannerSx: React.CSSProperties = { | |||
| }; | |||
| type QrCodeScannerProps = { | |||
| cameras: CameraDevice[] | |||
| title?: string, | |||
| contents?: string[], | |||
| onScanSuccess: (result: string) => void, | |||
| @@ -79,7 +80,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||
| const cameras = await Html5Qrcode.getCameras() | |||
| setCameraList(cameras) | |||
| if (cameras.length > 0) { | |||
| handleCameraChange(cameras[0].id) | |||
| handleCameraChange(cameras[cameras.length-1].id) | |||
| } | |||
| }, []) | |||
| @@ -106,6 +107,8 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||
| const handleScanSuccess = useCallback<QrcodeSuccessCallback>((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) | |||
| @@ -113,20 +116,18 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||
| }, [scanner, onScanSuccess]) | |||
| const handleScanError = useCallback<QrcodeErrorCallback>((errorMessage, error) => { | |||
| console.log(`Error: ${errorMessage}`); | |||
| // console.log(`Error: ${errorMessage}`); | |||
| if (onScanError) { | |||
| onScanError(errorMessage) | |||
| } | |||
| }, [scanner, onScanError]) | |||
| const handleScanCloseButton = useCallback(async () => { | |||
| if (scanner) { | |||
| console.log("Cleaning up scanner..."); | |||
| await scanner.stop() | |||
| await scanner.clear() | |||
| scanner.clear() | |||
| onClose() | |||
| } | |||
| }, [scanner]) | |||
| @@ -166,7 +167,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||
| <Grid item xs={12}> | |||
| <div style={{ textAlign: "center", margin: "auto", justifyContent: "center" }} id="qr-reader" hidden={isScanned} /> | |||
| </Grid> | |||
| {cameraList.length > 0 && <Grid item xs={6} > | |||
| {/* {cameraList.length > 0 && <Grid item xs={6} > | |||
| <Autocomplete | |||
| disableClearable | |||
| noOptionsText={t("No Options")} | |||
| @@ -183,7 +184,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||
| /> | |||
| )} | |||
| /> | |||
| </Grid>} | |||
| </Grid>} */} | |||
| { | |||
| contents && contents.map((string) => { | |||
| return <Grid item xs={8}>{string}</Grid> | |||
| @@ -1,65 +1,86 @@ | |||
| import { Button, CardContent, Card, Modal, SxProps, ModalProps, Box, CardActions } from "@mui/material"; | |||
| import { | |||
| Button, | |||
| CardContent, | |||
| Card, | |||
| Modal, | |||
| SxProps, | |||
| ModalProps, | |||
| Box, | |||
| CardActions, | |||
| } from "@mui/material"; | |||
| import QrCodeScanner from "./QrCodeScanner"; | |||
| import { useCallback, useEffect, useRef, useState } from "react"; | |||
| import { CameraDevice } from "html5-qrcode"; | |||
| const modalSx: SxProps = { | |||
| position: "absolute", | |||
| top: "50%", | |||
| left: "50%", | |||
| transform: "translate(-50%, -50%)", | |||
| width: { xs: "calc(100% - 2rem)", sm: "90%" }, | |||
| maxHeight: "90%", | |||
| maxWidth: 1400, | |||
| position: "absolute", | |||
| top: "50%", | |||
| left: "50%", | |||
| transform: "translate(-50%, -50%)", | |||
| width: { xs: "calc(100% - 2rem)", sm: "90%" }, | |||
| maxHeight: "90%", | |||
| maxWidth: 1400, | |||
| }; | |||
| type QrCodeScannerModalProps = { | |||
| title?: string, | |||
| contents?: string[], | |||
| isOpen: boolean, | |||
| onClose: () => void, | |||
| onScanSuccess: (result: string) => void, | |||
| onScanError?: (error: string) => void | |||
| } | |||
| cameras: CameraDevice[]; | |||
| title?: string; | |||
| contents?: string[]; | |||
| isOpen: boolean; | |||
| onClose: () => void; | |||
| onScanSuccess: (result: string) => void; | |||
| onScanError?: (error: string) => void; | |||
| }; | |||
| const QrCodeScannerModal: React.FC<QrCodeScannerModalProps> = ({ | |||
| title, | |||
| contents, | |||
| isOpen, | |||
| onClose, | |||
| onScanSuccess, | |||
| onScanError, | |||
| cameras, | |||
| title, | |||
| contents, | |||
| isOpen, | |||
| onClose, | |||
| onScanSuccess, | |||
| onScanError, | |||
| }) => { | |||
| const [modalOpen, setModalOpen] = useState(isOpen); // pass to qr code scanner | |||
| useEffect(() => { | |||
| setModalOpen(isOpen); | |||
| }, [isOpen]); | |||
| const [modalOpen, setModalOpen] = useState(isOpen) // pass to qr code scanner | |||
| useEffect(() => { | |||
| setModalOpen(isOpen) | |||
| }, [isOpen]) | |||
| const onModalClose = useCallback<NonNullable<ModalProps["onClose"]>>( | |||
| (_, reason) => { | |||
| if (reason !== "backdropClick") { | |||
| setModalOpen(false); | |||
| } | |||
| }, | |||
| [] | |||
| ); | |||
| const onModalClose = useCallback<NonNullable<ModalProps["onClose"]>>( | |||
| (_, reason) => { | |||
| if (reason !== "backdropClick") { | |||
| setModalOpen(false) | |||
| return ( | |||
| <Modal open={isOpen} onClose={onModalClose}> | |||
| <Card sx={modalSx}> | |||
| <CardContent sx={{ overflow: "auto", maxHeight: "90vh" }}> | |||
| <Box | |||
| sx={ | |||
| { | |||
| // marginInline: -1, | |||
| // marginBlock: 1, | |||
| } | |||
| } | |||
| }, | |||
| [], | |||
| ); | |||
| return ( | |||
| <Modal open={isOpen} onClose={onModalClose}> | |||
| <Card sx={modalSx}> | |||
| <CardContent sx={{ overflow: "auto", maxHeight: "90vh" }} > | |||
| <Box | |||
| sx={{ | |||
| // marginInline: -1, | |||
| // marginBlock: 1, | |||
| }} | |||
| > | |||
| <QrCodeScanner title={title} contents={contents} onScanSuccess={onScanSuccess} onScanError={onScanError} isOpen={modalOpen} onClose={onClose} /> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| </Modal> | |||
| ) | |||
| } | |||
| > | |||
| <QrCodeScanner | |||
| cameras={cameras} | |||
| title={title} | |||
| contents={contents} | |||
| onScanSuccess={onScanSuccess} | |||
| onScanError={onScanError} | |||
| isOpen={modalOpen} | |||
| onClose={onClose} | |||
| /> | |||
| </Box> | |||
| </CardContent> | |||
| </Card> | |||
| </Modal> | |||
| ); | |||
| }; | |||
| export default QrCodeScannerModal | |||
| export default QrCodeScannerModal; | |||