Bläddra i källkod

update po

create_edit_user
MSI\derek 3 månader sedan
förälder
incheckning
71210e0cea
13 ändrade filer med 330 tillägg och 140 borttagningar
  1. +12
    -1
      README.md
  2. +25
    -22
      src/app/(main)/layout.tsx
  3. +17
    -0
      src/app/api/po/actions.ts
  4. +11
    -1
      src/app/api/qc/actions.ts
  5. +2
    -2
      src/app/api/settings/uom/index.ts
  6. +1
    -2
      src/app/utils/commonUtil.ts
  7. +26
    -0
      src/components/Cameras/CameraProvider.tsx
  8. +74
    -9
      src/components/PoDetail/PoDetail.tsx
  9. +61
    -40
      src/components/PoDetail/PoInputGrid.tsx
  10. +21
    -5
      src/components/PoDetail/PoQcStockInModal.tsx
  11. +0
    -0
      src/components/PoDetail/PoStockInModal.tsx
  12. +8
    -7
      src/components/QrCodeScanner/QrCodeScanner.tsx
  13. +72
    -51
      src/components/QrCodeScanner/QrCodeScannerModal.tsx

+ 12
- 1
README.md Visa fil

@@ -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).

+ 25
- 22
src/app/(main)/layout.tsx Visa fil

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

+ 17
- 0
src/app/api/po/actions.ts Visa fil

@@ -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",


+ 11
- 1
src/app/api/qc/actions.ts Visa fil

@@ -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"] },
});
});

+ 2
- 2
src/app/api/settings/uom/index.ts Visa fil

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

+ 1
- 2
src/app/utils/commonUtil.ts Visa fil

@@ -6,5 +6,4 @@ export const downloadFile = (blobData: Uint8Array, filename: string) => {
link.href = url;
link.setAttribute("download", filename);
link.click();
}
}

+ 26
- 0
src/components/Cameras/CameraProvider.tsx Visa fil

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

+ 74
- 9
src/components/PoDetail/PoDetail.tsx Visa fil

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


+ 61
- 40
src/components/PoDetail/PoInputGrid.tsx Visa fil

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


+ 21
- 5
src/components/PoDetail/PoQcStockInModal.tsx Visa fil

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


+ 0
- 0
src/components/PoDetail/PoStockInModal.tsx Visa fil


+ 8
- 7
src/components/QrCodeScanner/QrCodeScanner.tsx Visa fil

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


+ 72
- 51
src/components/QrCodeScanner/QrCodeScannerModal.tsx Visa fil

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

Laddar…
Avbryt
Spara