Ver a proveniência

update po with checking

create_edit_user
MSI\derek há 2 meses
ascendente
cometimento
7a2ddc94dc
13 ficheiros alterados com 662 adições e 197 eliminações
  1. +3
    -3
      src/app/(main)/po/page.tsx
  2. +22
    -2
      src/app/api/po/actions.ts
  3. +4
    -2
      src/app/api/po/index.ts
  4. +6
    -0
      src/app/utils/formatUtil.ts
  5. +61
    -29
      src/components/PoDetail/PoDetail.tsx
  6. +48
    -18
      src/components/PoDetail/PoInputGrid.tsx
  7. +168
    -39
      src/components/PoDetail/PoQcStockInModal.tsx
  8. +111
    -26
      src/components/PoDetail/PutawayForm.tsx
  9. +53
    -16
      src/components/PoDetail/QcForm.tsx
  10. +134
    -34
      src/components/PoDetail/StockInForm.tsx
  11. +8
    -0
      src/components/PoSearch/PoSearch.tsx
  12. +12
    -2
      src/components/PoSearch/PoSearchWrapper.tsx
  13. +32
    -26
      src/components/ReactQrCodeScanner/ReactQrCodeScanner.tsx

+ 3
- 3
src/app/(main)/po/page.tsx Ver ficheiro

@@ -14,8 +14,8 @@ export const metadata: Metadata = {
title: "Purchase Order",
};

const production: React.FC = async () => {
const { t } = await getServerI18n("claims");
const PurchaseOrder: React.FC = async () => {
const { t } = await getServerI18n("purchaseOrder");
// preloadClaims();

return (
@@ -45,4 +45,4 @@ const production: React.FC = async () => {
);
};

export default production;
export default PurchaseOrder;

+ 22
- 2
src/app/api/po/actions.ts Ver ficheiro

@@ -15,7 +15,8 @@ export interface PostStockInLiineResponse<T> {
type?: string
message: string | null;
errorPosition: string | keyof T;
entity: StockInLine | StockInLine[]
entity: T | T[]
// entity: StockInLine | StockInLine[]
}

export interface StockInLineEntry {
@@ -83,6 +84,7 @@ export const createStockInLine = async (data: StockInLineEntry) => {
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
// revalidateTag("po");
return stockInLine
}

@@ -92,16 +94,34 @@ export const updateStockInLine = async (data: StockInLineEntry & ModalFormInput)
body: JSON.stringify(data),
headers: { "Content-Type": "application/json" },
});
// revalidateTag("po");
return stockInLine
}

export const startPo = async (poId: number) => {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(`${BASE_API_URL}/po/start/${poId}`, {
method: "POST",
body: JSON.stringify({ poId }),
headers: { "Content-Type": "application/json" },
});
revalidateTag("po");
return po
}

export const checkPolAndCompletePo = async (poId: number) => {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(`${BASE_API_URL}/po//check/${poId}`, {
const po = await serverFetchJson<PostStockInLiineResponse<PoResult>>(`${BASE_API_URL}/po/check/${poId}`, {
method: "POST",
body: JSON.stringify({ poId }),
headers: { "Content-Type": "application/json" },
});
revalidateTag("po");
return po
}

export const fetchPoInClient = cache(async (id: number) => {
return serverFetchJson<PoResult>(`${BASE_API_URL}/po/detail/${id}`, {
next: { tags: ["po"] },
});
});



+ 4
- 2
src/app/api/po/index.ts Ver ficheiro

@@ -41,8 +41,10 @@ export interface StockInLine {
acceptedQty: number
price: number
priceUnit: string
productionDate: string
expiryDate: string
shelfLife?: number,
receiptDate?: string
productionDate?: string
expiryDate?: string
status: string
supplier: string
lotNo: string


+ 6
- 0
src/app/utils/formatUtil.ts Ver ficheiro

@@ -18,6 +18,12 @@ export const integerFormatter = new Intl.NumberFormat("en-HK", {

})

export const INPUT_DATE_FORMAT = "YYYY-MM-DD";

export const OUTPUT_DATE_FORMAT = "YYYY/MM/DD";

export const OUTPUT_TIME_FORMAT = "HH:mm:ss";

export const stockInLineStatusMap: { [status: string]: number } = {
"draft": 0,
"pending": 1,


+ 61
- 29
src/components/PoDetail/PoDetail.tsx Ver ficheiro

@@ -36,8 +36,10 @@ import {
} from "@mui/x-data-grid";
import {
checkPolAndCompletePo,
fetchPoInClient,
fetchStockInLineInfo,
PurchaseQcResult,
startPo,
testFetch,
} from "@/app/api/po/actions";
import {
@@ -88,14 +90,46 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
console.log(cameras);
const { t } = useTranslation();
const apiRef = useGridApiRef();
const [rows, setRows] = useState<PurchaseOrderLine[]>(po.pol || []);
const [purchaseOrder, setPurchaseOrder] = useState({ ...po });
const [rows, setRows] = useState<PurchaseOrderLine[]>(
purchaseOrder.pol || []
);
const params = useSearchParams();
const [currPoStatus, setCurrPoStatus] = useState(purchaseOrder.status);

const handleComplete = useCallback(async () => {
const checkRes = await checkPolAndCompletePo(purchaseOrder.id);
console.log(checkRes);
const newPo = await fetchPoInClient(purchaseOrder.id);
setPurchaseOrder(newPo);
}, [checkPolAndCompletePo, fetchPoInClient]);

const handleStartPo = useCallback(async () => {
const startRes = await startPo(purchaseOrder.id);
console.log(startRes);
const newPo = await fetchPoInClient(purchaseOrder.id);
setPurchaseOrder(newPo);
}, [startPo, fetchPoInClient]);

useEffect(() => {
setRows(purchaseOrder.pol || []);
}, [purchaseOrder]);

function Row(props: { row: PurchaseOrderLine }) {
const { row } = props;
const [open, setOpen] = useState(false);
const [processedQty, setProcessedQty] = useState(row.processed);
const [currStatus, setCurrStatus] = useState(row.status);
const [stockInLine, setStockInLine] = useState(row.stockInLine);
const totalWeight = useMemo(
() => calculateWeight(row.qty, row.uom),
[calculateWeight]
);
const weightUnit = useMemo(
() => returnWeightUnit(row.uom),
[returnWeightUnit]
);

useEffect(() => {
if (processedQty === row.qty) {
setCurrStatus("completed".toUpperCase());
@@ -106,20 +140,12 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
}
}, [processedQty]);

const totalWeight = useMemo(
() => calculateWeight(row.qty, row.uom),
[calculateWeight]
);
const weightUnit = useMemo(
() => returnWeightUnit(row.uom),
[returnWeightUnit]
);

return (
<>
<TableRow sx={{ "& > *": { borderBottom: "unset" }, color: "black" }}>
<TableCell>
<IconButton
disabled={purchaseOrder.status.toLowerCase() === "pending"}
aria-label="expand row"
size="small"
onClick={() => setOpen(!open)}
@@ -152,9 +178,10 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
<PoInputGrid
qc={qc}
setRows={setRows}
stockInLine={stockInLine}
setStockInLine={setStockInLine}
setProcessedQty={setProcessedQty}
itemDetail={row}
stockInLine={row.stockInLine}
warehouse={warehouse}
/>
</Box>
@@ -199,20 +226,6 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
setPutAwayOpen(true);
}, []);

const handleComplete = useCallback(async () => {
const res = await checkPolAndCompletePo(po.id)
if (res.type === "completed") {
// toast.success(res.message)
console.log(res)
return
}
if (res.type === "receiving") {
// toast.error(res.message)
console.log(res)
return
}
}, [checkPolAndCompletePo]);

return (
<>
<Stack
@@ -220,13 +233,26 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
// component="form"
// onSubmit={formProps.handleSubmit(onSubmit, onSubmitError)}
>
<Grid container xs={12} justifyContent="space-between">
<Grid container xs={12} justifyContent="start">
<Grid item>
<Typography mb={2} variant="h4">
{po.code}
{/* {purchaseOrder.code} - {currPoStatus} */}
{purchaseOrder.code} - {purchaseOrder.status}
</Typography>
</Grid>
</Grid>
<Grid container xs={12} justifyContent="start">
{purchaseOrder.status.toLowerCase() === "pending" && (
<Grid item>
<Button onClick={handleStartPo}>Start</Button>
</Grid>
)}
{purchaseOrder.status.toLowerCase() === "receiving" && (
<Grid item>
<Button onClick={handleComplete}>Complete</Button>
</Grid>
)}
</Grid>
{/* <Grid container xs={12} justifyContent="space-between">
<Button onClick={handleComplete}>Complete</Button>
</Grid> */}
@@ -242,9 +268,15 @@ const PoDetail: React.FC<Props> = ({ po, qc, warehouse }) => {
</Tabs>
</Grid>
{/* <Grid item xs={4}> */}
{/* scanner */}
{/* scanner */}
{/* </Grid> */}
<Grid item xs={4} display="flex" justifyContent="end" alignItems="end">
<Grid
item
xs={4}
display="flex"
justifyContent="end"
alignItems="end"
>
<QrModal
open={isOpenScanner}
onClose={onCloseScanner}


+ 48
- 18
src/components/PoDetail/PoInputGrid.tsx Ver ficheiro

@@ -63,6 +63,7 @@ interface ResultWithId {
interface Props {
qc: QcItemWithChecks[];
setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
setStockInLine: Dispatch<SetStateAction<StockInLine[]>>;
setProcessedQty: Dispatch<SetStateAction<number>>;
itemDetail: PurchaseOrderLine;
stockInLine: StockInLine[];
@@ -100,6 +101,7 @@ class ProcessRowUpdateError extends Error {
function PoInputGrid({
qc,
setRows,
setStockInLine,
setProcessedQty,
itemDetail,
stockInLine,
@@ -122,6 +124,7 @@ function PoInputGrid({
const [escalOpen, setEscalOpen] = useState(false);
const [stockInOpen, setStockInOpen] = useState(false);
const [putAwayOpen, setPutAwayOpen] = useState(false);
const [btnIsLoading, setBtnIsLoading] = useState(false);
const [currQty, setCurrQty] = useState(() => {
const total = entries.reduce(
(acc, curr) => acc + (curr.acceptedQty || 0),
@@ -147,17 +150,14 @@ function PoInputGrid({
);
const handleStart = useCallback(
(id: GridRowId, params: any) => () => {
setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
}));
setTimeout(async () => {
// post stock in line
console.log("delayed");
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,
@@ -171,6 +171,13 @@ function PoInputGrid({
setEntries((prev) =>
prev.map((p) => (p.id === oldId ? (res.entity as StockInLine) : p))
);
setStockInLine(
(prev) =>
prev.map((p) =>
p.id === oldId ? (res.entity as StockInLine) : p
) as StockInLine[]
);
setBtnIsLoading(false);
// do post directly to test
// openStartModal();
}, 200);
@@ -183,6 +190,7 @@ function PoInputGrid({

const handleQC = useCallback(
(id: GridRowId, params: any) => async () => {
setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
@@ -199,12 +207,14 @@ function PoInputGrid({
// open qc modal
console.log("delayed");
openQcModal();
setBtnIsLoading(false);
}, 200);
},
[fetchQcDefaultValue]
);
const handleEscalation = useCallback(
(id: GridRowId, params: any) => () => {
// setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
@@ -214,12 +224,14 @@ function PoInputGrid({
// open qc modal
console.log("delayed");
openEscalationModal();
// setBtnIsLoading(false);
}, 200);
},
[]
);
const handleStockIn = useCallback(
(id: GridRowId, params: any) => () => {
// setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
@@ -231,6 +243,7 @@ function PoInputGrid({
// return the record with its status as pending
// update layout
console.log("delayed");
// setBtnIsLoading(false);
}, 200);
},
[]
@@ -238,6 +251,7 @@ function PoInputGrid({

const handlePutAway = useCallback(
(id: GridRowId, params: any) => () => {
// setBtnIsLoading(true);
setRowModesModel((prev) => ({
...prev,
[id]: { mode: GridRowModes.View },
@@ -249,6 +263,7 @@ function PoInputGrid({
// return the record with its status as pending
// update layout
console.log("delayed");
// setBtnIsLoading(false);
}, 200);
},
[]
@@ -256,6 +271,7 @@ function PoInputGrid({

const printQrcode = useCallback(
async (row: any) => {
setBtnIsLoading(true);
console.log(row.id);
const postData = { stockInLineIds: [row.id] };
// const postData = { stockInLineIds: [42,43,44] };
@@ -264,6 +280,7 @@ function PoInputGrid({
console.log(response);
downloadFile(new Uint8Array(response.blobValue), response.filename!!);
}
setBtnIsLoading(false);
},
[fetchPoQrcode, downloadFile]
);
@@ -319,11 +336,11 @@ function PoInputGrid({
() => [
{
field: "itemNo",
flex: 0.8,
flex: 0.4,
},
{
field: "itemName",
flex: 1,
flex: 0.6,
},
{
field: "acceptedQty",
@@ -363,7 +380,7 @@ function PoInputGrid({
field: "actions",
type: "actions",
headerName: "start | qc | escalation | stock in | putaway | delete",
flex: 1,
flex: 1.5,
cellClassName: "actions",
getActions: (params) => {
console.log(params.row.status);
@@ -376,7 +393,7 @@ function PoInputGrid({
color: "primary.main",
// marginRight: 1,
}}
disabled={!(stockInLineStatusMap[status] === 0)}
disabled={btnIsLoading || !(stockInLineStatusMap[status] === 0)}
// set _isNew to false after posting
// or check status
onClick={handleStart(params.row.id, params)}
@@ -390,7 +407,7 @@ function PoInputGrid({
color: "primary.main",
// marginRight: 1,
}}
disabled={stockInLineStatusMap[status] < 1}
disabled={btnIsLoading || stockInLineStatusMap[status] < 1}
// set _isNew to false after posting
// or check status
onClick={handleQC(params.row.id, params)}
@@ -405,8 +422,9 @@ function PoInputGrid({
// marginRight: 1,
}}
disabled={
btnIsLoading ||
stockInLineStatusMap[status] <= 0 ||
stockInLineStatusMap[status] >= 4
stockInLineStatusMap[status] >= 5
}
// set _isNew to false after posting
// or check status
@@ -421,7 +439,11 @@ function PoInputGrid({
color: "primary.main",
// marginRight: 1,
}}
disabled={stockInLineStatusMap[status] <= 2 || stockInLineStatusMap[status] >= 7}
disabled={
btnIsLoading ||
stockInLineStatusMap[status] <= 2 ||
stockInLineStatusMap[status] >= 7
}
// set _isNew to false after posting
// or check status
onClick={handleStockIn(params.row.id, params)}
@@ -435,7 +457,7 @@ function PoInputGrid({
color: "primary.main",
// marginRight: 1,
}}
disabled={stockInLineStatusMap[status] < 7}
disabled={btnIsLoading || stockInLineStatusMap[status] < 7}
// set _isNew to false after posting
// or check status
onClick={handlePutAway(params.row.id, params)}
@@ -449,7 +471,7 @@ function PoInputGrid({
color: "primary.main",
// marginRight: 1,
}}
disabled={stockInLineStatusMap[status] !== 8}
disabled={btnIsLoading || stockInLineStatusMap[status] !== 8}
// set _isNew to false after posting
// or check status
onClick={handleQrCode(params.row.id, params)}
@@ -462,7 +484,7 @@ function PoInputGrid({
sx={{
color: "error.main",
}}
disabled={stockInLineStatusMap[status] !== 0}
disabled={btnIsLoading || stockInLineStatusMap[status] !== 0}
// disabled={Boolean(params.row.status)}
onClick={handleDelete(params.row.id)}
color="inherit"
@@ -472,7 +494,7 @@ function PoInputGrid({
},
},
],
[]
[btnIsLoading]
);

const addRow = useCallback(() => {
@@ -533,6 +555,8 @@ function PoInputGrid({
const newEntries = entries.map((e) =>
getRowId(e) === getRowId(originalRow) ? rowToSave : e
);
setStockInLine(newEntries as StockInLine[]);
console.log("triggered");
setEntries(newEntries);
//update remaining qty
const total = newEntries.reduce(
@@ -569,9 +593,7 @@ function PoInputGrid({
</Button>
</Box>
);
useEffect(() => {
console.log(modalInfo);
}, [modalInfo]);

return (
<>
<StyledDataGrid
@@ -623,7 +645,9 @@ function PoInputGrid({
<>
<PoQcStockInModal
type={"qc"}
// setRows={setRows}
setEntries={setEntries}
setStockInLine={setStockInLine}
setItemDetail={setModalInfo}
qc={qc}
open={qcOpen}
@@ -636,7 +660,9 @@ function PoInputGrid({
<>
<PoQcStockInModal
type={"escalation"}
// setRows={setRows}
setEntries={setEntries}
setStockInLine={setStockInLine}
setItemDetail={setModalInfo}
// qc={qc}
open={escalOpen}
@@ -649,7 +675,9 @@ function PoInputGrid({
<>
<PoQcStockInModal
type={"stockIn"}
// setRows={setRows}
setEntries={setEntries}
setStockInLine={setStockInLine}
// qc={qc}
setItemDetail={setModalInfo}
open={stockInOpen}
@@ -662,7 +690,9 @@ function PoInputGrid({
<>
<PoQcStockInModal
type={"putaway"}
// setRows={setRows}
setEntries={setEntries}
setStockInLine={setStockInLine}
setItemDetail={setModalInfo}
open={putAwayOpen}
warehouse={warehouse}


+ 168
- 39
src/components/PoDetail/PoQcStockInModal.tsx Ver ficheiro

@@ -22,23 +22,35 @@ import { useTranslation } from "react-i18next";
import QcForm from "./QcForm";
import { QcItemWithChecks } from "@/app/api/qc";
import { Check, CurrencyYuanRounded, TtyTwoTone } from "@mui/icons-material";
import { StockInLine } from "@/app/api/po";
import { PurchaseOrderLine, StockInLine } from "@/app/api/po";
import { useSearchParams } from "next/navigation";
import { StockInLineRow } from "./PoInputGrid";
import EscalationForm from "./EscalationForm";
import StockInForm from "./StockInForm";
import PutawayForm from "./PutawayForm";
import { stockInLineStatusMap } from "@/app/utils/formatUtil";
import {
INPUT_DATE_FORMAT,
stockInLineStatusMap,
} from "@/app/utils/formatUtil";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import { downloadFile } from "@/app/utils/commonUtil";
import { fetchPoQrcode } from "@/app/api/pdf/actions";

dayjs.extend(arraySupport)
dayjs.extend(arraySupport);
interface CommonProps extends Omit<ModalProps, "children"> {
// setRows: Dispatch<SetStateAction<PurchaseOrderLine[]>>;
setEntries?: Dispatch<SetStateAction<StockInLineRow[]>>;
setStockInLine?: Dispatch<SetStateAction<StockInLine[]>>;
itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] };
setItemDetail: Dispatch<SetStateAction<(StockInLine & {
warehouseId?: number;
}) | undefined>>
setItemDetail: Dispatch<
SetStateAction<
| (StockInLine & {
warehouseId?: number;
})
| undefined
>
>;
qc?: QcItemWithChecks[];
warehouse?: any[];
type: "qc" | "stockIn" | "escalation" | "putaway";
@@ -75,7 +87,9 @@ const style = {

const PoQcStockInModal: React.FC<Props> = ({
type,
// setRows,
setEntries,
setStockInLine,
open,
onClose,
itemDetail,
@@ -86,12 +100,16 @@ const PoQcStockInModal: React.FC<Props> = ({
const [serverError, setServerError] = useState("");
const { t } = useTranslation();
const params = useSearchParams();
const [btnIsLoading, setBtnIsLoading] = useState(false);

console.log(params.get("id"));
console.log(itemDetail);
console.log(itemDetail.qcResult);
const formProps = useForm<ModalFormInput>({
defaultValues: {
...itemDetail,
// receiptDate: itemDetail.receiptDate || dayjs().add(-1, "month").format(INPUT_DATE_FORMAT),
// warehouseId: itemDetail.defaultWarehouseId || 0
},
});
// console.log(formProps);
@@ -111,40 +129,95 @@ const PoQcStockInModal: React.FC<Props> = ({
}
}, [itemDetail]);

const fix0IndexedDate = useCallback((date: string | number[] | undefined) => {
if (Array.isArray(date)) {
console.log(date)
return dayjs([date[0], date[1] - 1, date[2]]).format("YYYY-MM-DD")
}
return date
}, [])
// const fix0IndexedDate = useCallback((date: string | number[] | undefined) => {
// if (Array.isArray(date)) {
// console.log(date);
// return dayjs([date[0], date[1] - 1, date[2]]).format("YYYY-MM-DD");
// }
// return date;
// }, []);
const checkStockIn = useCallback(
(data: ModalFormInput): boolean => {
let hasErrors = false;
if (itemDetail.shelfLife && !data.productionDate && !data.expiryDate) {
formProps.setError("productionDate", {
message: "Please provide at least one",
type: "invalid",
});
formProps.setError("expiryDate", {
message: "Please provide at least one",
type: "invalid",
});
hasErrors = true;
}
if (!itemDetail.shelfLife && !data.expiryDate) {
formProps.setError("expiryDate", {
message: "Please provide expiry date",
type: "invalid",
});
hasErrors = true;
}

if (data.expiryDate && data.expiryDate < data.receiptDate!!) {
formProps.setError("expiryDate", {
message: "Expired",
type: "invalid",
});
hasErrors = true;
}
return hasErrors;
},
[itemDetail, formProps]
);

const checkPutaway = useCallback(
(data: ModalFormInput): boolean => {
let hasErrors = false;
console.log(data.warehouseId);
if (!data.warehouseId || data.warehouseId <= 0) {
formProps.setError("warehouseId", {
message: "Please provide warehouseId",
type: "invalid",
});
hasErrors = true;
}
return hasErrors;
},
[itemDetail, formProps]
);

const onSubmit = useCallback<SubmitHandler<ModalFormInput & {}>>(
async (data, event) => {
formProps.clearErrors();
let hasErrors = false;
setBtnIsLoading(true);
console.log(errors);
console.log(data);
console.log(itemDetail);
console.log(data.receiptDate)
console.log(fix0IndexedDate(data.receiptDate))
// console.log(fix0IndexedDate(data.receiptDate));
try {
// add checking
// const qty = data.sampleRate

if (type === "stockIn") {
hasErrors = checkStockIn(data)
console.log(hasErrors)
}
if (type === "putaway") {
hasErrors = checkPutaway(data);
console.log(hasErrors)
}
//////////////////////// modify this mess later //////////////////////
var productionDate = null;
var expiryDate = null;
var receiptDate = null;
var acceptedQty = null;
if (data.productionDate && data.productionDate.length > 0) {
productionDate = fix0IndexedDate(data.productionDate);
if (data.productionDate) {
productionDate = dayjs(data.productionDate).format(INPUT_DATE_FORMAT);
}
if (data.expiryDate && data.expiryDate.length > 0) {
expiryDate = fix0IndexedDate(data.expiryDate);
if (data.expiryDate) {
expiryDate = dayjs(data.expiryDate).format(INPUT_DATE_FORMAT);
}
if (data.receiptDate && data.receiptDate.length > 0) {
receiptDate = fix0IndexedDate(data.receiptDate);
if (data.receiptDate) {
receiptDate = dayjs(data.receiptDate).format(INPUT_DATE_FORMAT);
}
// if ()
if (data.qcResult) {
@@ -163,17 +236,20 @@ const PoQcStockInModal: React.FC<Props> = ({
receiptDate: receiptDate,
} as StockInLineEntry & ModalFormInput;
//////////////////////////////////////////////////////////////////////
console.log(args);
// return
if (hasErrors) {
console.log(args);
setServerError(t("An error has occurred. Please try again later."));
return false;
setBtnIsLoading(false);
return;
}
console.log(args);
// setBtnIsLoading(false);
// return
const res = await updateStockInLine(args);
if (Boolean(res.id)) {
// update entries
const newEntries = res.entity as StockInLine[];
console.log(newEntries)
console.log(newEntries);
if (setEntries) {
setEntries((prev) => {
const updatedEntries = [...prev]; // Create a new array
@@ -181,7 +257,24 @@ const PoQcStockInModal: React.FC<Props> = ({
const index = updatedEntries.findIndex((p) => p.id === item.id);
if (index !== -1) {
// Update existing item
console.log(item)
console.log(item);
updatedEntries[index] = item;
} else {
// Add new item
updatedEntries.push(item);
}
});
return updatedEntries; // Return the new array
});
}
if (setStockInLine) {
setStockInLine((prev) => {
const updatedEntries = [...prev]; // Create a new array
newEntries.forEach((item) => {
const index = updatedEntries.findIndex((p) => p.id === item.id);
if (index !== -1) {
// Update existing item
console.log(item);
updatedEntries[index] = item;
} else {
// Add new item
@@ -192,31 +285,56 @@ const PoQcStockInModal: React.FC<Props> = ({
});
}
// add loading
setItemDetail(undefined)
setBtnIsLoading(false);

setItemDetail(undefined);
closeHandler({}, "backdropClick");
}
console.log(res);
// if (res)
} catch (e) {
// server error
setBtnIsLoading(false);
setServerError(t("An error has occurred. Please try again later."));
console.log(e);
}
},
[t, itemDetail]
[t, itemDetail, checkStockIn, checkPutaway]
);

const printQrcode = useCallback(async () => {
setBtnIsLoading(true);
const postData = { stockInLineIds: [itemDetail.id] };
// const postData = { stockInLineIds: [42,43,44] };
const response = await fetchPoQrcode(postData);
if (response) {
console.log(response);
downloadFile(new Uint8Array(response.blobValue), response.filename!!);
}
setBtnIsLoading(false);
}, [itemDetail, fetchPoQrcode, downloadFile]);

const renderSubmitButton = useMemo((): Boolean => {
if (itemDetail) {
const status = itemDetail.status;
console.log(status);
switch (type) {
case "qc":
return stockInLineStatusMap[status] >= 1 && stockInLineStatusMap[status] <= 2;
return (
stockInLineStatusMap[status] >= 1 &&
stockInLineStatusMap[status] <= 2
);
case "escalation":
return stockInLineStatusMap[status] === 1 || stockInLineStatusMap[status] >= 3 || stockInLineStatusMap[status] <= 5;
return (
stockInLineStatusMap[status] === 1 ||
stockInLineStatusMap[status] >= 3 ||
stockInLineStatusMap[status] <= 5
);
case "stockIn":
return stockInLineStatusMap[status] >= 3 && stockInLineStatusMap[status] <= 6;
return (
stockInLineStatusMap[status] >= 3 &&
stockInLineStatusMap[status] <= 6
);
case "putaway":
return stockInLineStatusMap[status] === 7;
default:
@@ -248,19 +366,30 @@ const PoQcStockInModal: React.FC<Props> = ({
{itemDetail !== undefined && type === "putaway" && (
<PutawayForm itemDetail={itemDetail} warehouse={warehouse!!} />
)}
{renderSubmitButton ? (
<Stack direction="row" justifyContent="flex-end" gap={1}>
<Stack direction="row" justifyContent="flex-end" gap={1}>
{renderSubmitButton ? (
<Button
name="submit"
variant="contained"
startIcon={<Check />}
type="submit"
// disabled={submitDisabled}
disabled={btnIsLoading}
>
{t("submit")}
</Button>
</Stack>
) : undefined}
) : undefined}
{itemDetail !== undefined && type === "putaway" && (
<Button
name="print"
variant="contained"
// startIcon={<Check />}
onClick={printQrcode}
disabled={btnIsLoading}
>
{t("print")}
</Button>
)}
</Stack>
</Box>
</Modal>
</FormProvider>


+ 111
- 26
src/components/PoDetail/PutawayForm.tsx Ver ficheiro

@@ -16,7 +16,7 @@ import {
Tooltip,
Typography,
} from "@mui/material";
import { useFormContext } from "react-hook-form";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo, useState } from "react";
@@ -40,7 +40,9 @@ import { WarehouseResult } from "@/app/api/warehouse";
import { stockInLineStatusMap } from "@/app/utils/formatUtil";
import { QRCodeSVG } from "qrcode.react";
import { QrCode } from "../QrCode";
import ReactQrCodeScanner, { ScannerConfig } from "../ReactQrCodeScanner/ReactQrCodeScanner";
import ReactQrCodeScanner, {
ScannerConfig,
} from "../ReactQrCodeScanner/ReactQrCodeScanner";
import { QrCodeInfo } from "@/app/api/qrcode";

interface Props {
@@ -84,19 +86,24 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
clearErrors,
} = useFormContext<PutawayInput>();
console.log(itemDetail);
const [recordQty, setRecordQty] = useState(0);
const [warehouseId, setWarehouseId] = useState(0);
// const [recordQty, setRecordQty] = useState(0);
const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId);
const filteredWarehouse = useMemo(() => {
// do filtering here if any
return warehouse;
}, []);
const defaultOption = {
value: 0, // think think sin
label: t("Select warehouse"),
group: "default",
}
const options = useMemo(() => {
return [
{
value: -1, // think think sin
label: t("Select warehouse"),
group: "default",
},
// {
// value: 0, // think think sin
// label: t("Select warehouse"),
// group: "default",
// },
...filteredWarehouse.map((w) => ({
value: w.id,
label: `${w.code} - ${w.name}`,
@@ -107,8 +114,8 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
const currentValue =
warehouseId > 0
? options.find((o) => o.value === warehouseId)
: options.find((o) => o.value === getValues("warehouseId")) || options[0];
: options.find((o) => o.value === getValues("warehouseId")) || defaultOption;
const onChange = useCallback(
(
event: React.SyntheticEvent,
@@ -119,19 +126,47 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
group: string;
};
console.log(singleNewVal);
setValue("warehouseId", singleNewVal.value);
console.log("onChange");
// setValue("warehouseId", singleNewVal.value);
setWarehouseId(singleNewVal.value);
},
[]
);

// const accQty = watch("acceptedQty");
// const validateForm = useCallback(() => {
// console.log(accQty);
// if (accQty > itemDetail.acceptedQty) {
// setError("acceptedQty", {
// message: `acceptedQty must not greater than ${itemDetail.acceptedQty}`,
// type: "required",
// });
// }
// if (accQty < 1) {
// setError("acceptedQty", {
// message: `minimal value is 1`,
// type: "required",
// });
// }
// if (isNaN(accQty)) {
// setError("acceptedQty", {
// message: `value must be a number`,
// type: "required",
// });
// }
// }, [accQty]);

// useEffect(() => {
// clearErrors();
// validateForm();
// }, [validateForm]);

const qrContent = useMemo(
() => ({
stockInLineId: itemDetail.id,
itemId: itemDetail.itemId,
lotNo: itemDetail.lotNo,
// warehouseId: 1 // for testing
// warehouseId: 2 // for testing
// expiryDate: itemDetail.expiryDate,
// productionDate: itemDetail.productionDate,
// supplier: itemDetail.supplier,
@@ -140,7 +175,7 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
[itemDetail]
);
const [isOpenScanner, setOpenScanner] = useState(false);
const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
(...args) => {
setOpenScanner(false);
@@ -158,23 +193,33 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
const scannerConfig = useMemo<ScannerConfig>(
() => ({
onUpdate: (err, result) => {
console.log(result);
console.log(Boolean(result));
if (result) {
const data: QrCodeInfo = JSON.parse(result.getText());
console.log(data);
if (data.warehouseId) {
console.log(data.warehouseId);
setWarehouseId(data.warehouseId);
onCloseScanner()
onCloseScanner();
}
} else return;
},
}),
[]
[onCloseScanner]
);

useEffect(() => {
setValue("status", "completed");
}, []);

useEffect(() => {
if (warehouseId > 0) {
setValue("warehouseId", warehouseId);
clearErrors("warehouseId")
}
}, [warehouseId]);

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
@@ -260,11 +305,13 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
disableClearable
disabled
fullWidth
defaultValue={options.find((o) => o.value === 1)}
defaultValue={options.find((o) => o.value === 1)} /// modify this later
// onChange={onChange}
getOptionLabel={(option) => option.label}
options={options}
renderInput={(params) => <TextField {...params} label="Default Warehouse"/>}
renderInput={(params) => (
<TextField {...params} label="Default Warehouse" />
)}
/>
</FormControl>
</Grid>
@@ -278,25 +325,63 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
max: itemDetail.acceptedQty,
valueAsNumber: true,
})}
defaultValue={itemDetail.acceptedQty}
// defaultValue={itemDetail.acceptedQty}
error={Boolean(errors.acceptedQty)}
helperText={errors.acceptedQty?.message}
/>
</Grid>
<Grid item xs={1}>
<Button onClick={onOpenScanner}>bind</Button>
<Button onClick={onOpenScanner}>bind</Button>
</Grid>
<Grid item xs={5.5}>
{/* <Controller
control={control}
name="warehouseId"
render={({ field }) => {
console.log(field);
return (
<Autocomplete
noOptionsText={t("No Warehouse")}
disableClearable
fullWidth
value={options.find((o) => o.value == field.value)}
onChange={onChange}
getOptionLabel={(option) => option.label}
options={options}
renderInput={(params) => (
<TextField
{...params}
label={"Select warehouse"}
error={Boolean(errors.warehouseId?.message)}
helperText={warehouseHelperText}
// helperText={errors.warehouseId?.message}
/>
)}
/>
);
}}
/> */}
<FormControl fullWidth>
<Autocomplete
noOptionsText={t("No Warehouse")}
disableClearable
fullWidth
// value={warehouseId > 0
// ? options.find((o) => o.value === warehouseId)
// : undefined}
value={currentValue}
onChange={onChange}
getOptionLabel={(option) => option.label}
options={options}
renderInput={(params) => <TextField {...params} />}
renderInput={(params) => (
<TextField
{...params}
// label={"Select warehouse"}
error={Boolean(errors.warehouseId?.message)}
helperText={errors.warehouseId?.message}
// helperText={warehouseHelperText}
/>
)}
/>
</FormControl>
</Grid>
@@ -318,11 +403,11 @@ const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse }) => {
<Button onClick={onOpenScanner}>bind</Button>
</Grid> */}

<Modal open={isOpenScanner} onClose={closeHandler}>
<Box sx={style}>
<ReactQrCodeScanner scannerConfig={scannerConfig} />
</Box>
</Modal>
<Modal open={isOpenScanner} onClose={closeHandler}>
<Box sx={style}>
<ReactQrCodeScanner scannerConfig={scannerConfig} />
</Box>
</Modal>
</Grid>
);
};


+ 53
- 16
src/components/PoDetail/QcForm.tsx Ver ficheiro

@@ -38,7 +38,7 @@ import { NEXT_PUBLIC_API_URL } from "@/config/api";
import axiosInstance from "@/app/(main)/axios/axiosInstance";

interface Props {
itemDetail: StockInLine
itemDetail: StockInLine;
qc: QcItemWithChecks[];
}
type EntryError =
@@ -65,9 +65,37 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
clearErrors,
} = useFormContext<PurchaseQCInput>();
console.log(itemDetail);
console.log(defaultValues);
const [recordQty, setRecordQty] = useState(0);
console.log(defaultValues);

//// validate form
const accQty = watch("acceptedQty");
const validateForm = useCallback(() => {
console.log(accQty);
if (accQty > itemDetail.acceptedQty) {
setError("acceptedQty", {
message: `acceptedQty must not greater than ${itemDetail.acceptedQty}`,
type: "required",
});
}
if (accQty < 1) {
setError("acceptedQty", {
message: `minimal value is 1`,
type: "required",
});
}
if (isNaN(accQty)) {
setError("acceptedQty", {
message: `value must be a number`,
type: "required",
});
}
}, [accQty]);

useEffect(() => {
clearErrors();
validateForm();
}, [validateForm]);
// const [recordQty, setRecordQty] = useState(0);
const columns = useMemo<GridColDef[]>(
() => [
{
@@ -105,7 +133,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
// id: params.id,
// field: "type",
// value: "determine1",
// });
// });
}}
/>
);
@@ -144,6 +172,7 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
],
[qc]
);
/// validate datagrid
const validation = useCallback(
(newRow: GridRowModel<PoQcRow>): EntryError => {
const error: EntryError = {};
@@ -161,17 +190,17 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
},
[]
);
useEffect(() => {
console.log(itemDetail)
var status = "receiving"
console.log(itemDetail);
var status = "receiving";
// switch (itemDetail.status) {
// case 'pending':
// status = "receiving"
// break;
// }
setValue("status", status)
}, [itemDetail])
setValue("status", status);
}, [itemDetail]);

return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
@@ -187,21 +216,22 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
spacing={2}
sx={{ mt: 0.5 }}
>
<Grid item xs={12} lg={6}>
<Grid item xs={12} lg={12}>
<TextField
label={t("accepted Qty")}
fullWidth
// value={itemDetail.acceptedQty}
{...register("acceptedQty", {
required: "acceptedQty required!",
valueAsNumber: true
valueAsNumber: true,
max: itemDetail.acceptedQty,
})}
// disabled
error={Boolean(errors.acceptedQty)}
helperText={errors.acceptedQty?.message}
/>
</Grid>
<Grid item xs={12} lg={6}>
{/* <Grid item xs={12} lg={6}>
<TextField
label={t("Total record qty")}
fullWidth
@@ -213,14 +243,15 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
// error={Boolean(errors.sampleRate)}
// helperText={errors.sampleRate?.message}
/>
</Grid>
</Grid> */}
<Grid item xs={12} lg={6}>
<TextField
label={t("sampleRate")}
fullWidth
defaultValue={1}
{...register("sampleRate", {
required: "sampleRate required!",
valueAsNumber: true
valueAsNumber: true,
})}
error={Boolean(errors.sampleRate)}
helperText={errors.sampleRate?.message}
@@ -230,8 +261,10 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
<TextField
label={t("sampleWeight")}
fullWidth
defaultValue={1}
{...register("sampleWeight", {
required: "sampleWeight required!",
valueAsNumber: true,
})}
error={Boolean(errors.sampleWeight)}
helperText={errors.sampleWeight?.message}
@@ -241,8 +274,10 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
<TextField
label={t("totalWeight")}
fullWidth
defaultValue={1}
{...register("totalWeight", {
required: "totalWeight required!",
valueAsNumber: true,
})}
error={Boolean(errors.totalWeight)}
helperText={errors.totalWeight?.message}
@@ -263,7 +298,9 @@ const QcForm: React.FC<Props> = ({ qc, itemDetail }) => {
_formKey={"qcResult"}
columns={columns}
validateRow={validation}
needAdd={itemDetail.status === "qc" || itemDetail.status === "pending"}
needAdd={
itemDetail.status === "qc" || itemDetail.status === "pending"
}
/>
</Grid>
</Grid>


+ 134
- 34
src/components/PoDetail/StockInForm.tsx Ver ficheiro

@@ -1,6 +1,10 @@
"use client";

import { PurchaseQcResult, PurchaseQCInput, StockInInput } from "@/app/api/po/actions";
import {
PurchaseQcResult,
PurchaseQCInput,
StockInInput,
} from "@/app/api/po/actions";
import {
Box,
Card,
@@ -11,7 +15,7 @@ import {
Tooltip,
Typography,
} from "@mui/material";
import { useFormContext } from "react-hook-form";
import { Controller, useFormContext } from "react-hook-form";
import { useTranslation } from "react-i18next";
import StyledDataGrid from "../StyledDataGrid";
import { useCallback, useEffect, useMemo } from "react";
@@ -31,6 +35,10 @@ import QcSelect from "./QcSelect";
import { QcItemWithChecks } from "@/app/api/qc";
import { GridEditInputCell } from "@mui/x-data-grid";
import { StockInLine } from "@/app/api/po";
import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
import { INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
import dayjs from "dayjs";
// change PurchaseQcResult to stock in entry props
interface Props {
itemDetail: StockInLine;
@@ -44,11 +52,14 @@ type EntryError =

// type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>;

const StockInForm: React.FC<Props> = ({
const StockInForm: React.FC<Props> = ({
// qc,
itemDetail,
}) => {
const { t } = useTranslation();
}) => {
const {
t,
i18n: { language },
} = useTranslation();
const apiRef = useGridApiRef();
const {
register,
@@ -62,18 +73,33 @@ const StockInForm: React.FC<Props> = ({
setError,
clearErrors,
} = useFormContext<StockInInput>();
console.log(itemDetail)
console.log(itemDetail);

useEffect(() => {
console.log("triggered");
// receiptDate default tdy
setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
setValue("status", "received");
}, []);

useEffect(() => {
console.log("triggered")
setValue("status", "received")
}, [])
console.log(errors);
}, [errors]);

const productionDate = watch("productionDate");
const expiryDate = watch("expiryDate");

useEffect(() => {
console.log(productionDate)
console.log(expiryDate)
if (expiryDate) clearErrors()
if (productionDate) clearErrors()
}, [productionDate, expiryDate])
return (
<Grid container justifyContent="flex-start" alignItems="flex-start">
<Grid item xs={12}>
<Typography variant="h6" display="block" marginBlockEnd={1}>
{t("Qc Detail")}
{t("Stock In Detail")}
</Typography>
</Grid>
<Grid
@@ -95,14 +121,38 @@ const StockInForm: React.FC<Props> = ({
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("receiptDate")}
fullWidth
{...register("receiptDate", {
required: "receiptDate required!",
})}
error={Boolean(errors.receiptDate)}
helperText={errors.receiptDate?.message}
<Controller
control={control}
name="receiptDate"
rules={{ required: true }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("receiptDate")}
value={dayjs(watch("receiptDate"))}
onChange={(date) => {
if (!date) return
// setValue("receiptDate", date.format(INPUT_DATE_FORMAT));
field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.receiptDate?.message),
helperText: errors.receiptDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={4}>
@@ -128,25 +178,75 @@ const StockInForm: React.FC<Props> = ({
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("productionDate")}
fullWidth
{...register("productionDate", {
// required: "productionDate required!",
})}
// error={Boolean(errors.productionDate)}
// helperText={errors.productionDate?.message}
<Controller
control={control}
name="productionDate"
// rules={{ required: !Boolean(expiryDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("productionDate")}
value={productionDate ? dayjs(productionDate) : undefined}
onChange={(date) => {
if (!date) return
setValue("productionDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.productionDate?.message),
helperText: errors.productionDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
<Grid item xs={4}>
<TextField
label={t("expiryDate")}
fullWidth
{...register("expiryDate", {
required: "expiryDate required!",
})}
error={Boolean(errors.expiryDate)}
helperText={errors.expiryDate?.message}
<Controller
control={control}
name="expiryDate"
// rules={{ required: !Boolean(productionDate) }}
render={({ field }) => {
return (
<LocalizationProvider
dateAdapter={AdapterDayjs}
adapterLocale={`${language}-hk`}
>
<DatePicker
{...field}
sx={{ width: "100%" }}
label={t("expiryDate")}
value={expiryDate ? dayjs(expiryDate) : undefined}
onChange={(date) => {
console.log(date)
if (!date) return
console.log(date.format(INPUT_DATE_FORMAT))
setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
// field.onChange(date);
}}
inputRef={field.ref}
slotProps={{
textField: {
// required: true,
error: Boolean(errors.expiryDate?.message),
helperText: errors.expiryDate?.message,
},
}}
/>
</LocalizationProvider>
);
}}
/>
</Grid>
</Grid>


+ 8
- 0
src/components/PoSearch/PoSearch.tsx Ver ficheiro

@@ -52,6 +52,14 @@ const PoSearch: React.FC<Props> = ({ po }) => {
name: "code",
label: t("Code"),
},
{
name: "orderDate",
label: t("OrderDate"),
},
{
name: "status",
label: t("Status"),
},
// {
// name: "name",
// label: t("Name"),


+ 12
- 2
src/components/PoSearch/PoSearchWrapper.tsx Ver ficheiro

@@ -7,6 +7,10 @@ import { notFound } from "next/navigation";
import PoSearchLoading from "./PoSearchLoading";
import PoSearch from "./PoSearch";
import { fetchPoList, PoResult } from "@/app/api/po";
import dayjs from "dayjs";
import arraySupport from "dayjs/plugin/arraySupport";
import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
dayjs.extend(arraySupport);

interface SubComponents {
Loading: typeof PoSearchLoading;
@@ -26,8 +30,14 @@ const PoSearchWrapper: React.FC<Props> & SubComponents = async (
] = await Promise.all([
fetchPoList()
]);

return <PoSearch po={po} />;
console.log(po)
const fixPoDate = po.map((p) => {
return ({
...p,
orderDate: dayjs(p.orderDate).format(OUTPUT_DATE_FORMAT)
})
})
return <PoSearch po={fixPoDate} />;
};

PoSearchWrapper.Loading = PoSearchLoading;


+ 32
- 26
src/components/ReactQrCodeScanner/ReactQrCodeScanner.tsx Ver ficheiro

@@ -29,7 +29,7 @@ const style = {
export var defaultScannerConfig: ScannerConfig = {
onUpdate: (err, result) => {
if (result) {
const data = JSON.parse(result.getText())
const data = JSON.parse(result.getText());
console.log(data);
} else return;
},
@@ -48,19 +48,27 @@ export interface ScannerConfig {
delay?: number; // Delay between scans in milliseconds. Default is 500ms.
videoConstraints?: MediaTrackConstraints; // Video constraints to pass to the webcam. If not provided, the default constraints will be used.
formats?: BarcodeFormat[] | BarcodeStringFormat[]; // Array of barcode formats to decode. If not provided, all formats will be used. A smaller list may improve the speed of the scan.
stopStream?: boolean
stopStream?: boolean;
}

const ReactQrCodeScanner: React.FC<Props> = ({
scannerConfig,
}) => {
const [stopStream, setStopStream] = useState(scannerConfig.stopStream || defaultScannerConfig.stopStream || false);
const ReactQrCodeScanner: React.FC<Props> = ({ scannerConfig }) => {
const [stopStream, setStopStream] = useState(
scannerConfig.stopStream || defaultScannerConfig.stopStream || false
);
const [torchEnabled, setTorchEnabled] = useState<boolean>(false);
const _scannerConfig = useMemo(() => ({
...defaultScannerConfig,
...scannerConfig,
}),[])

// const _scannerConfig = useMemo(() => ({
// ...defaultScannerConfig,
// ...scannerConfig,
// }),[])
const [_scannerConfig, setScannerConfig] = useState<ScannerConfig>({
...defaultScannerConfig
});
useEffect(() => {
setScannerConfig({
...defaultScannerConfig,
...scannerConfig,
});
}, []);
const SwitchOnOffScanner = useCallback(() => {
// Stop the QR Reader stream (fixes issue where the browser freezes when closing the modal) and then dismiss the modal one tick later
setStopStream((prev) => !prev);
@@ -71,21 +79,19 @@ const ReactQrCodeScanner: React.FC<Props> = ({
}, []);

return (
<>
{!stopStream ? (
<BarcodeScanner
stopStream={stopStream}
torch={torchEnabled}
{..._scannerConfig}
/>
) : undefined}
<Button onClick={SwitchOnOffTorch}>
{torchEnabled ? "off" : "on"}
</Button>
<Button onClick={SwitchOnOffScanner}>
{stopStream ? "start" : "stop"}
</Button>
</>
<>
{!stopStream ? (
<BarcodeScanner
stopStream={stopStream}
torch={torchEnabled}
{..._scannerConfig}
/>
) : undefined}
<Button onClick={SwitchOnOffTorch}>{torchEnabled ? "off" : "on"}</Button>
<Button onClick={SwitchOnOffScanner}>
{stopStream ? "start" : "stop"}
</Button>
</>
);
};
export default ReactQrCodeScanner;

Carregando…
Cancelar
Guardar