소스 검색

update po

create_edit_user
MSI\derek 3 달 전
부모
커밋
71210e0cea
13개의 변경된 파일330개의 추가작업 그리고 140개의 파일을 삭제
  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 파일 보기

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

+ 25
- 22
src/app/(main)/layout.tsx 파일 보기

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

+ 17
- 0
src/app/api/po/actions.ts 파일 보기

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


+ 11
- 1
src/app/api/qc/actions.ts 파일 보기

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

+ 2
- 2
src/app/api/settings/uom/index.ts 파일 보기

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

+ 1
- 2
src/app/utils/commonUtil.ts 파일 보기

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

+ 26
- 0
src/components/Cameras/CameraProvider.tsx 파일 보기

@@ -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 파일 보기

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


+ 61
- 40
src/components/PoDetail/PoInputGrid.tsx 파일 보기

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


+ 21
- 5
src/components/PoDetail/PoQcStockInModal.tsx 파일 보기

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


+ 0
- 0
src/components/PoDetail/PoStockInModal.tsx 파일 보기


+ 8
- 7
src/components/QrCodeScanner/QrCodeScanner.tsx 파일 보기

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


+ 72
- 51
src/components/QrCodeScanner/QrCodeScannerModal.tsx 파일 보기

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

불러오는 중...
취소
저장