| @@ -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)). | 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: | After installing `nvm`, run: | ||||
| ```bash | ```bash | ||||
| @@ -29,4 +32,12 @@ This project uses the following libraries: | |||||
| - [NextJS](https://nextjs.org/docs) | - [NextJS](https://nextjs.org/docs) | ||||
| - [Next-Auth](https://next-auth.js.org/getting-started/example) | - [Next-Auth](https://next-auth.js.org/getting-started/example) | ||||
| - [Material UI](https://mui.com/material-ui/getting-started/) | - [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 { NAVIGATION_CONTENT_WIDTH } from "@/config/uiConfig"; | ||||
| import Stack from "@mui/material/Stack"; | import Stack from "@mui/material/Stack"; | ||||
| import Breadcrumb from "@/components/Breadcrumb"; | 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({ | export default async function MainLayout({ | ||||
| children, | children, | ||||
| @@ -20,30 +21,32 @@ export default async function MainLayout({ | |||||
| redirect("/login"); | redirect("/login"); | ||||
| } | } | ||||
| if(session){ | |||||
| SetupAxiosInterceptors(session?.accessToken); | |||||
| if (session) { | |||||
| SetupAxiosInterceptors(session?.accessToken); | |||||
| } | } | ||||
| return ( | return ( | ||||
| <CameraProvider> | |||||
| <AxiosProvider> | <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> | </AxiosProvider> | ||||
| </CameraProvider> | |||||
| ); | ); | ||||
| } | } | ||||
| @@ -5,6 +5,7 @@ import { revalidateTag } from "next/cache"; | |||||
| import { cache } from "react"; | import { cache } from "react"; | ||||
| import { PoResult, StockInLine } from "."; | import { PoResult, StockInLine } from "."; | ||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | import { serverFetchJson } from "@/app/utils/fetchUtil"; | ||||
| import { QcItemResult } from "../settings/qcItem"; | |||||
| // import { BASE_API_URL } from "@/config/api"; | // import { BASE_API_URL } from "@/config/api"; | ||||
| export interface PostStockInLiineResponse<T> { | 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) => { | export const createStockInLine = async (data: StockInLineEntry) => { | ||||
| const stockInLine = await serverFetchJson<PostStockInLiineResponse<StockInLineEntry>>(`${BASE_API_URL}/stockInLine/create`, { | const stockInLine = await serverFetchJson<PostStockInLiineResponse<StockInLineEntry>>(`${BASE_API_URL}/stockInLine/create`, { | ||||
| method: "POST", | method: "POST", | ||||
| @@ -5,6 +5,10 @@ import { cache } from "react"; | |||||
| import { serverFetchJson } from "@/app/utils/fetchUtil"; | import { serverFetchJson } from "@/app/utils/fetchUtil"; | ||||
| import { QcItemWithChecks } from "."; | import { QcItemWithChecks } from "."; | ||||
| export interface QcResult { | |||||
| } | |||||
| export const fetchQcItemCheck = cache(async (itemId?: number) => { | export const fetchQcItemCheck = cache(async (itemId?: number) => { | ||||
| var url = `${BASE_API_URL}/qcCheck` | var url = `${BASE_API_URL}/qcCheck` | ||||
| if (itemId) url +=`/${itemId}` | if (itemId) url +=`/${itemId}` | ||||
| @@ -12,4 +16,10 @@ export const fetchQcItemCheck = cache(async (itemId?: number) => { | |||||
| next: { tags: ["qc"] }, | 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 | code: string | ||||
| name: string | name: string | ||||
| unit1: string | unit1: string | ||||
| unit1Qty: number | |||||
| unit1Qty: number | |||||
| unit2?: string | unit2?: string | ||||
| unit2Qty: number | unit2Qty: number | ||||
| unit3?: string | unit3?: string | ||||
| @@ -17,4 +17,4 @@ export interface Uom { | |||||
| unit4Qty: number | unit4Qty: number | ||||
| sizeInGram: number | sizeInGram: number | ||||
| gramPerSmallestUnit: number | gramPerSmallestUnit: number | ||||
| } | |||||
| } | |||||
| @@ -6,5 +6,4 @@ export const downloadFile = (blobData: Uint8Array, filename: string) => { | |||||
| link.href = url; | link.href = url; | ||||
| link.setAttribute("download", filename); | link.setAttribute("download", filename); | ||||
| link.click(); | 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 InputDataGrid, { TableRow } from "../InputDataGrid/InputDataGrid"; | ||||
| import { GridColDef, GridRowModel, useGridApiRef } from "@mui/x-data-grid"; | import { GridColDef, GridRowModel, useGridApiRef } from "@mui/x-data-grid"; | ||||
| import { testFetch } from "@/app/api/po/actions"; | 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 { FormProvider, useForm } from "react-hook-form"; | ||||
| import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; | import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; | ||||
| import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; | import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; | ||||
| @@ -42,6 +42,10 @@ import { QcItemWithChecks } from "@/app/api/qc"; | |||||
| import { useSearchParams } from "next/navigation"; | import { useSearchParams } from "next/navigation"; | ||||
| import { WarehouseResult } from "@/app/api/warehouse"; | import { WarehouseResult } from "@/app/api/warehouse"; | ||||
| import { calculateWeight, returnWeightUnit } from "@/app/utils/formatUtil"; | 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 = { | type Props = { | ||||
| po: PoResult; | po: PoResult; | ||||
| @@ -54,9 +58,10 @@ type EntryError = | |||||
| [field in keyof StockInLine]?: string; | [field in keyof StockInLine]?: string; | ||||
| } | } | ||||
| | undefined; | | undefined; | ||||
| // type PolRow = TableRow<Partial<StockInLine>, EntryError>; | // type PolRow = TableRow<Partial<StockInLine>, EntryError>; | ||||
| const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | ||||
| const cameras = useContext(CameraContext); | |||||
| console.log(cameras); | |||||
| const { t } = useTranslation(); | const { t } = useTranslation(); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| const [rows, setRows] = useState<PurchaseOrderLine[]>(po.pol || []); | const [rows, setRows] = useState<PurchaseOrderLine[]>(po.pol || []); | ||||
| @@ -77,8 +82,16 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| setCurrStatus("pending".toUpperCase()); | setCurrStatus("pending".toUpperCase()); | ||||
| } | } | ||||
| }, [processedQty]); | }, [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 ( | return ( | ||||
| <> | <> | ||||
| <TableRow sx={{ "& > *": { borderBottom: "unset" }, color: "black" }}> | <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 ( | return ( | ||||
| <> | <> | ||||
| <Stack | <Stack | ||||
| @@ -140,12 +174,37 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| // component="form" | // component="form" | ||||
| // onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)} | // 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> | |||||
| <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}> | <TableContainer component={Paper}> | ||||
| <Table aria-label="collapsible table"> | <Table aria-label="collapsible table"> | ||||
| <TableHead> | <TableHead> | ||||
| @@ -172,6 +231,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => { | |||||
| </Table> | </Table> | ||||
| </TableContainer> | </TableContainer> | ||||
| </Grid> | </Grid> | ||||
| {/* tab 2 */} | |||||
| <Grid sx={{ display: tabIndex === 1 ? "block" : "none" }}> | |||||
| {/* <StyledDataGrid | |||||
| /> */} | |||||
| </Grid> | |||||
| </Stack> | </Stack> | ||||
| </> | </> | ||||
| ); | ); | ||||
| @@ -33,22 +33,30 @@ import ShoppingCartIcon from "@mui/icons-material/ShoppingCart"; | |||||
| import { QcItemWithChecks } from "src/app/api/qc"; | import { QcItemWithChecks } from "src/app/api/qc"; | ||||
| import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | import PlayArrowIcon from "@mui/icons-material/PlayArrow"; | ||||
| import { PurchaseOrderLine, StockInLine } from "@/app/api/po"; | 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 { 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 PoQcStockInModal from "./PoQcStockInModal"; | ||||
| import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; | import NotificationImportantIcon from "@mui/icons-material/NotificationImportant"; | ||||
| import { WarehouseResult } from "@/app/api/warehouse"; | 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 axiosInstance from "@/app/(main)/axios/axiosInstance"; | ||||
| // import axios, { AxiosRequestConfig } from "axios"; | // 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 { downloadFile } from "@/app/utils/commonUtil"; | ||||
| import { fetchPoQrcode } from "@/app/api/pdf/actions"; | import { fetchPoQrcode } from "@/app/api/pdf/actions"; | ||||
| import { fetchQcResult } from "@/app/api/qc/actions"; | |||||
| interface ResultWithId { | interface ResultWithId { | ||||
| id: number; | id: number; | ||||
| } | } | ||||
| @@ -98,7 +106,7 @@ function PoInputGrid({ | |||||
| stockInLine, | stockInLine, | ||||
| warehouse, | warehouse, | ||||
| }: Props) { | }: Props) { | ||||
| console.log(itemDetail) | |||||
| console.log(itemDetail); | |||||
| const { t } = useTranslation("home"); | const { t } = useTranslation("home"); | ||||
| const apiRef = useGridApiRef(); | const apiRef = useGridApiRef(); | ||||
| const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | const [rowModesModel, setRowModesModel] = useState<GridRowModesModel>({}); | ||||
| @@ -108,7 +116,7 @@ function PoInputGrid({ | |||||
| ); | ); | ||||
| console.log(stockInLine); | console.log(stockInLine); | ||||
| const [entries, setEntries] = useState<StockInLineRow[]>(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 [qcOpen, setQcOpen] = useState(false); | ||||
| const [escalOpen, setEscalOpen] = useState(false); | const [escalOpen, setEscalOpen] = useState(false); | ||||
| const [stockInOpen, setStockInOpen] = useState(false); | const [stockInOpen, setStockInOpen] = useState(false); | ||||
| @@ -122,9 +130,7 @@ function PoInputGrid({ | |||||
| }); | }); | ||||
| useEffect(() => { | useEffect(() => { | ||||
| const completedList = entries.filter( | |||||
| (e) => e.status === "completed" | |||||
| ); | |||||
| const completedList = entries.filter((e) => e.status === "completed"); | |||||
| const processedQty = completedList.reduce( | const processedQty = completedList.reduce( | ||||
| (acc, curr) => acc + (curr.acceptedQty || 0), | (acc, curr) => acc + (curr.acceptedQty || 0), | ||||
| 0 | 0 | ||||
| @@ -150,6 +156,7 @@ function PoInputGrid({ | |||||
| console.log(params); | console.log(params); | ||||
| const oldId = params.row.id; | const oldId = params.row.id; | ||||
| console.log(oldId); | console.log(oldId); | ||||
| console.log(params.row); | |||||
| const postData = { | const postData = { | ||||
| itemId: params.row.itemId, | itemId: params.row.itemId, | ||||
| itemNo: params.row.itemNo, | itemNo: params.row.itemNo, | ||||
| @@ -167,17 +174,21 @@ function PoInputGrid({ | |||||
| // openStartModal(); | // openStartModal(); | ||||
| }, 200); | }, 200); | ||||
| }, | }, | ||||
| [] | |||||
| [createStockInLine] | |||||
| ); | ); | ||||
| const fetchQcDefaultValue = useCallback(async () => { | 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( | const handleQC = useCallback( | ||||
| (id: GridRowId, params: any) => async () => { | (id: GridRowId, params: any) => async () => { | ||||
| @@ -185,15 +196,20 @@ function PoInputGrid({ | |||||
| ...prev, | ...prev, | ||||
| [id]: { mode: GridRowModes.View }, | [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(() => { | setTimeout(() => { | ||||
| // open qc modal | // open qc modal | ||||
| console.log("delayed"); | console.log("delayed"); | ||||
| openQcModal(); | openQcModal(); | ||||
| }, 200); | }, 200); | ||||
| }, | }, | ||||
| [] | |||||
| [fetchQcDefaultValue] | |||||
| ); | ); | ||||
| const handleEscalation = useCallback( | const handleEscalation = useCallback( | ||||
| (id: GridRowId, params: any) => () => { | (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( | const handleQrCode = useCallback( | ||||
| (id: GridRowId, params: any) => () => { | (id: GridRowId, params: any) => () => { | ||||
| @@ -269,7 +289,7 @@ function PoInputGrid({ | |||||
| // return the record with its status as pending | // return the record with its status as pending | ||||
| // update layout | // update layout | ||||
| console.log("delayed"); | console.log("delayed"); | ||||
| printQrcode(params.row) | |||||
| printQrcode(params.row); | |||||
| }, 200); | }, 200); | ||||
| }, | }, | ||||
| [] | [] | ||||
| @@ -334,7 +354,10 @@ function PoInputGrid({ | |||||
| headerName: "weight", | headerName: "weight", | ||||
| flex: 0.5, | flex: 0.5, | ||||
| renderCell: (params) => { | 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); | const weightUnit = returnWeightUnit(params.row.uom); | ||||
| return `${weight} ${weightUnit}`; | return `${weight} ${weightUnit}`; | ||||
| }, | }, | ||||
| @@ -375,9 +398,7 @@ function PoInputGrid({ | |||||
| color: "primary.main", | color: "primary.main", | ||||
| // marginRight: 1, | // marginRight: 1, | ||||
| }} | }} | ||||
| disabled={ | |||||
| stockInLineStatusMap[status] < 1 | |||||
| } | |||||
| disabled={stockInLineStatusMap[status] < 1} | |||||
| // set _isNew to false after posting | // set _isNew to false after posting | ||||
| // or check status | // or check status | ||||
| onClick={handleQC(params.row.id, params)} | onClick={handleQC(params.row.id, params)} | ||||
| @@ -463,7 +484,7 @@ function PoInputGrid({ | |||||
| ); | ); | ||||
| const addRow = useCallback(() => { | const addRow = useCallback(() => { | ||||
| console.log(itemDetail) | |||||
| console.log(itemDetail); | |||||
| const newEntry = { | const newEntry = { | ||||
| id: Date.now(), | id: Date.now(), | ||||
| _isNew: true, | _isNew: true, | ||||
| @@ -1,6 +1,6 @@ | |||||
| "use client"; | "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 { Box, Button, Modal, ModalProps, Stack } from "@mui/material"; | ||||
| import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; | import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; | ||||
| import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | import { FormProvider, SubmitHandler, useForm } from "react-hook-form"; | ||||
| @@ -18,7 +18,7 @@ import { stockInLineStatusMap } from "@/app/utils/formatUtil"; | |||||
| interface CommonProps extends Omit<ModalProps, "children"> { | interface CommonProps extends Omit<ModalProps, "children"> { | ||||
| setEntries: Dispatch<SetStateAction<StockInLineRow[]>> | setEntries: Dispatch<SetStateAction<StockInLineRow[]>> | ||||
| itemDetail: StockInLine; | |||||
| itemDetail: StockInLine & {qcResult?: PurchaseQcResult[]}; | |||||
| qc?: QcItemWithChecks[]; | qc?: QcItemWithChecks[]; | ||||
| warehouse?: any[]; | warehouse?: any[]; | ||||
| type: "qc" | "stockIn" | "escalation" | "putaway" | type: "qc" | "stockIn" | "escalation" | "putaway" | ||||
| @@ -58,7 +58,7 @@ const style = { | |||||
| const PoQcStockInModal: React.FC<Props> = ({ | const PoQcStockInModal: React.FC<Props> = ({ | ||||
| type, | type, | ||||
| setEntries, | setEntries, | ||||
| open, | |||||
| open, | |||||
| onClose, | onClose, | ||||
| itemDetail, | itemDetail, | ||||
| qc, | qc, | ||||
| @@ -70,8 +70,19 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| const params = useSearchParams() | const params = useSearchParams() | ||||
| console.log(params.get("id")) | console.log(params.get("id")) | ||||
| const [defaultValues, setDefaultValues] = useState({}); | 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>({ | const formProps = useForm<ModalFormInput>({ | ||||
| defaultValues: defaultValues ? defaultValues : {}, | |||||
| defaultValues: defaultValue | |||||
| }); | }); | ||||
| const errors = formProps.formState.errors; | const errors = formProps.formState.errors; | ||||
| const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>( | ||||
| @@ -155,17 +166,22 @@ const PoQcStockInModal: React.FC<Props> = ({ | |||||
| const renderSubmitButton = useMemo((): Boolean => { | const renderSubmitButton = useMemo((): Boolean => { | ||||
| if (itemDetail) { | if (itemDetail) { | ||||
| const status = itemDetail.status | const status = itemDetail.status | ||||
| console.log(status) | |||||
| switch (type) { | switch (type) { | ||||
| case "qc": | case "qc": | ||||
| return stockInLineStatusMap[status] === 1 | return stockInLineStatusMap[status] === 1 | ||||
| case "putaway": | case "putaway": | ||||
| return stockInLineStatusMap[status] === 7 | return stockInLineStatusMap[status] === 7 | ||||
| case "stockIn": | |||||
| return stockInLineStatusMap[status] === 6 | |||||
| default: | default: | ||||
| return false; // Handle unexpected type | return false; // Handle unexpected type | ||||
| } | } | ||||
| } else return false | } else return false | ||||
| }, [type, itemDetail]) | }, [type, itemDetail]) | ||||
| renderSubmitButton | |||||
| useEffect(() => { | |||||
| console.log(renderSubmitButton) | |||||
| }, [renderSubmitButton]) | |||||
| return ( | return ( | ||||
| <> | <> | ||||
| <Modal open={open} onClose={closeHandler}> | <Modal open={open} onClose={closeHandler}> | ||||
| @@ -19,6 +19,7 @@ const scannerSx: React.CSSProperties = { | |||||
| }; | }; | ||||
| type QrCodeScannerProps = { | type QrCodeScannerProps = { | ||||
| cameras: CameraDevice[] | |||||
| title?: string, | title?: string, | ||||
| contents?: string[], | contents?: string[], | ||||
| onScanSuccess: (result: string) => void, | onScanSuccess: (result: string) => void, | ||||
| @@ -79,7 +80,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||||
| const cameras = await Html5Qrcode.getCameras() | const cameras = await Html5Qrcode.getCameras() | ||||
| setCameraList(cameras) | setCameraList(cameras) | ||||
| if (cameras.length > 0) { | 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) => { | const handleScanSuccess = useCallback<QrcodeSuccessCallback>((decodedText, result) => { | ||||
| if (scanner) { | if (scanner) { | ||||
| console.log(`Decoded text: ${decodedText}`); | console.log(`Decoded text: ${decodedText}`); | ||||
| const parseData = JSON.parse(decodedText) | |||||
| console.log(parseData) | |||||
| // Handle the decoded text as needed | // Handle the decoded text as needed | ||||
| switchScanStatus() | switchScanStatus() | ||||
| onScanSuccess(decodedText) | onScanSuccess(decodedText) | ||||
| @@ -113,20 +116,18 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||||
| }, [scanner, onScanSuccess]) | }, [scanner, onScanSuccess]) | ||||
| const handleScanError = useCallback<QrcodeErrorCallback>((errorMessage, error) => { | const handleScanError = useCallback<QrcodeErrorCallback>((errorMessage, error) => { | ||||
| console.log(`Error: ${errorMessage}`); | |||||
| // console.log(`Error: ${errorMessage}`); | |||||
| if (onScanError) { | if (onScanError) { | ||||
| onScanError(errorMessage) | onScanError(errorMessage) | ||||
| } | } | ||||
| }, [scanner, onScanError]) | }, [scanner, onScanError]) | ||||
| const handleScanCloseButton = useCallback(async () => { | const handleScanCloseButton = useCallback(async () => { | ||||
| if (scanner) { | if (scanner) { | ||||
| console.log("Cleaning up scanner..."); | console.log("Cleaning up scanner..."); | ||||
| await scanner.stop() | await scanner.stop() | ||||
| await scanner.clear() | |||||
| scanner.clear() | |||||
| onClose() | onClose() | ||||
| } | } | ||||
| }, [scanner]) | }, [scanner]) | ||||
| @@ -166,7 +167,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||||
| <Grid item xs={12}> | <Grid item xs={12}> | ||||
| <div style={{ textAlign: "center", margin: "auto", justifyContent: "center" }} id="qr-reader" hidden={isScanned} /> | <div style={{ textAlign: "center", margin: "auto", justifyContent: "center" }} id="qr-reader" hidden={isScanned} /> | ||||
| </Grid> | </Grid> | ||||
| {cameraList.length > 0 && <Grid item xs={6} > | |||||
| {/* {cameraList.length > 0 && <Grid item xs={6} > | |||||
| <Autocomplete | <Autocomplete | ||||
| disableClearable | disableClearable | ||||
| noOptionsText={t("No Options")} | noOptionsText={t("No Options")} | ||||
| @@ -183,7 +184,7 @@ const QrCodeScanner: React.FC<QrCodeScannerProps> = ({ | |||||
| /> | /> | ||||
| )} | )} | ||||
| /> | /> | ||||
| </Grid>} | |||||
| </Grid>} */} | |||||
| { | { | ||||
| contents && contents.map((string) => { | contents && contents.map((string) => { | ||||
| return <Grid item xs={8}>{string}</Grid> | 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 QrCodeScanner from "./QrCodeScanner"; | ||||
| import { useCallback, useEffect, useRef, useState } from "react"; | import { useCallback, useEffect, useRef, useState } from "react"; | ||||
| import { CameraDevice } from "html5-qrcode"; | |||||
| const modalSx: SxProps = { | 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 = { | 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> = ({ | 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; | |||||