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