FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 

709 строки
29 KiB

  1. "use client";
  2. import {
  3. Box,
  4. Button,
  5. Grid,
  6. Modal,
  7. ModalProps,
  8. Stack,
  9. TextField,
  10. Typography,
  11. Paper,
  12. Divider,
  13. } from "@mui/material";
  14. import { fetchItemForPutAway } from "@/app/api/stockIn/actions";
  15. import { Result } from "@/app/api/settings/item"; // 只导入类型
  16. import { useCallback, useEffect, useLayoutEffect, useMemo, useState } from "react";
  17. import ReactQrCodeScanner, {
  18. ScannerConfig,
  19. } from "../ReactQrCodeScanner/ReactQrCodeScanner";
  20. import { FormProvider, SubmitHandler, useForm } from "react-hook-form";
  21. import {
  22. fetchStockInLineInfo,
  23. StockInLineEntry,
  24. updateStockInLine,
  25. } from "@/app/api/stockIn/actions";
  26. import { ModalFormInput, StockInLine } from "@/app/api/stockIn";
  27. import { WarehouseResult } from "@/app/api/warehouse";
  28. // import { QrCodeInfo } from "@/app/api/qrcde";
  29. import { Check, QrCode, ErrorOutline, CheckCircle } from "@mui/icons-material";
  30. import { useTranslation } from "react-i18next";
  31. import { useSearchParams } from "next/navigation";
  32. import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
  33. import LoadingComponent from "../General/LoadingComponent";
  34. import StockInForm from "../StockIn/StockInForm";
  35. import { arrayToDateString, INPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  36. import { QrCodeScanner } from "../QrCodeScannerProvider/QrCodeScannerProvider";
  37. import { msg } from "../Swal/CustomAlerts";
  38. import { PutAwayRecord } from ".";
  39. import FgStockInForm from "../StockIn/FgStockInForm";
  40. import Swal from "sweetalert2";
  41. interface Props extends Omit<ModalProps, "children"> {
  42. warehouse: WarehouseResult[];
  43. stockInLineId: number;
  44. warehouseId: number;
  45. scanner: QrCodeScanner;
  46. addPutAwayHistory: (putAwayData: PutAwayRecord) => void;
  47. onSetDefaultWarehouseId?: (warehouseId: number) => void; // 新增回调
  48. }
  49. const style = {
  50. position: "absolute",
  51. top: "50%",
  52. left: "50%",
  53. transform: "translate(-50%, -50%)",
  54. bgcolor: "background.paper",
  55. pt: { xs: 0.5, sm: 1, md: 1.5 },
  56. px: { xs: 1, sm: 1.5, md: 2 },
  57. pb: { xs: 0.5, sm: 1, md: 1.5 },
  58. width: { xs: "95%", sm: "85%", md: "75%", lg: "70%" },
  59. maxWidth: "900px",
  60. maxHeight: { xs: "98vh", sm: "95vh", md: "90vh" },
  61. overflow: "hidden",
  62. display: "flex",
  63. flexDirection: "column",
  64. };
  65. const scannerStyle = {
  66. position: "absolute",
  67. top: "50%",
  68. left: "50%",
  69. transform: "translate(-50%, -50%)",
  70. bgcolor: "background.paper",
  71. pt: { xs: 2, sm: 3, md: 4 },
  72. px: { xs: 2, sm: 3, md: 4 },
  73. pb: { xs: 6, sm: 7, md: 8 },
  74. width: { xs: "85%", sm: "70%", md: "60%" },
  75. maxWidth: "600px",
  76. };
  77. const PutAwayModal: React.FC<Props> = ({ open, onClose, warehouse, stockInLineId, warehouseId, scanner, addPutAwayHistory, onSetDefaultWarehouseId }) => {
  78. const { t } = useTranslation("putAway");
  79. const [serverError, setServerError] = useState("");
  80. const params = useSearchParams();
  81. const [isOpenScanner, setIsOpenScanner] = useState<boolean>(false);
  82. const [firstWarehouseId, setFirstWarehouseId] = useState<number | null>(null);
  83. const [warehouseMismatchError, setWarehouseMismatchError] = useState<string>("");
  84. const [firstWarehouseInfo, setFirstWarehouseInfo] = useState<{name: string, code: string} | null>(null);
  85. const [itemDefaultWarehouseId, setItemDefaultWarehouseId] = useState<number | null>(null);
  86. const [itemDetail, setItemDetail] = useState<StockInLine>();
  87. const [totalPutAwayQty, setTotalPutAwayQty] = useState<number>(0);
  88. const [unavailableText, setUnavailableText] = useState<string | undefined>(
  89. undefined,
  90. );
  91. const [putQty, setPutQty] = useState<number>(itemDetail?.acceptedQty ?? 0);
  92. const [verified, setVerified] = useState<boolean>(false);
  93. const [qtyError, setQtyError] = useState<string>("");
  94. const defaultNewValue = useMemo(() => {
  95. // console.log("%c ItemDetail", "color:purple", itemDetail);
  96. return (
  97. {
  98. ...itemDetail,
  99. // status: itemDetail.status ?? "pending",
  100. // dnDate: arrayToDateString(itemDetail?.dnDate, "input")?? undefined,
  101. // // putAwayLines: dummyPutAwayLine,
  102. // // putAwayLines: itemDetail.putAwayLines.map((line) => (return {...line, printQty: 1})) ?? [],
  103. // putAwayLines: itemDetail.putAwayLines?.map((line) => ({...line, printQty: 1, _isNew: false})) ?? [],
  104. // // qcResult: (itemDetail.qcResult && itemDetail.qcResult?.length > 0) ? itemDetail.qcResult : [],//[...dummyQCData],
  105. // escResult: (itemDetail.escResult && itemDetail.escResult?.length > 0) ? itemDetail.escResult : [],
  106. productionDate: itemDetail?.productionDate ? arrayToDateString(itemDetail?.productionDate, "input") : undefined,
  107. expiryDate: itemDetail?.expiryDate ? arrayToDateString(itemDetail?.expiryDate, "input") : undefined,
  108. receiptDate: itemDetail?.receiptDate ? arrayToDateString(itemDetail?.receiptDate, "input") : undefined,
  109. acceptQty: itemDetail?.acceptedQty ?? 0,
  110. defaultWarehouseId: itemDetail?.defaultWarehouseId ?? 1,
  111. } as ModalFormInput
  112. )
  113. }, [itemDetail])
  114. const formProps = useForm<ModalFormInput>({
  115. defaultValues: {
  116. ...defaultNewValue,
  117. },
  118. });
  119. const errors = formProps.formState.errors;
  120. useEffect(() => {
  121. if (itemDetail) {
  122. startScanner();
  123. }
  124. }, [itemDetail])
  125. const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
  126. (...args) => {
  127. setVerified(false);
  128. setItemDetail(undefined);
  129. setTotalPutAwayQty(0);
  130. setItemDefaultWarehouseId(null);
  131. setFirstWarehouseId(null);
  132. setFirstWarehouseInfo(null);
  133. onClose?.(...args);
  134. // reset();
  135. },
  136. [onClose],
  137. );
  138. const scannerCloseHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
  139. (...args) => {
  140. setIsOpenScanner(false);
  141. scanner.stopScan();
  142. console.log("%c Scanning stopped ", "color:cyan");
  143. },
  144. [],
  145. );
  146. const startScanner = () => {
  147. // setIsOpenScanner(true);
  148. scanner.startScan();
  149. console.log("%c Scanning started ", "color:cyan");
  150. };
  151. const openScanner = () => {
  152. setIsOpenScanner(true);
  153. scanner.startScan();
  154. console.log("%c Scanning started ", "color:cyan");
  155. };
  156. // 根据 item 的 locationCode 设置默认 warehouseId
  157. useEffect(() => {
  158. // 直接使用 fetchStockInLineInfo 返回的 locationCode
  159. // 只在第一次上架时(firstWarehouseId === null)设置默认值
  160. if (itemDetail?.locationCode && warehouse.length > 0 && firstWarehouseId === null) {
  161. const locationCode = itemDetail.locationCode;
  162. if (locationCode) {
  163. // 根据 locationCode 查找对应的 warehouse(通过 code 匹配)
  164. const matchedWarehouse = warehouse.find(
  165. (w) => w.code === locationCode || w.code?.toLowerCase() === locationCode?.toLowerCase()
  166. );
  167. if (matchedWarehouse) {
  168. // 只设置用于显示的默认值,不通知父组件
  169. setItemDefaultWarehouseId(matchedWarehouse.id);
  170. console.log("%c Set default warehouse from item locationCode (from API, display only):", "color:green", {
  171. locationCode,
  172. warehouseId: matchedWarehouse.id,
  173. warehouseCode: matchedWarehouse.code
  174. });
  175. } else {
  176. console.log("%c No warehouse found for locationCode:", "color:yellow", locationCode);
  177. }
  178. }
  179. }
  180. }, [itemDetail?.locationCode, warehouse, firstWarehouseId]);
  181. useEffect(() => {
  182. // 只使用实际扫描的 warehouseId,不使用默认值进行验证
  183. if (warehouseId > 0 && firstWarehouseId !== null) {
  184. // 第二次及后续上架:必须使用第一次的仓库
  185. if (warehouseId !== firstWarehouseId) {
  186. const firstWh = warehouse.find((w) => w.id == firstWarehouseId);
  187. const scannedWh = warehouse.find((w) => w.id == warehouseId);
  188. setWarehouseMismatchError("倉庫不匹配!必須使用首次上架的倉庫");
  189. setVerified(false);
  190. } else {
  191. setWarehouseMismatchError("");
  192. if (scanner.isScanning) {
  193. setIsOpenScanner(false);
  194. setVerified(true);
  195. msg("貨倉掃瞄成功!");
  196. scanner.resetScan();
  197. }
  198. }
  199. } else if (warehouseId > 0 && firstWarehouseId === null) {
  200. // 第一次上架 - 只接受扫描的 warehouseId
  201. if (scanner.isScanning) {
  202. setIsOpenScanner(false);
  203. setVerified(true);
  204. msg("貨倉掃瞄成功!");
  205. scanner.resetScan();
  206. }
  207. }
  208. }, [warehouseId, firstWarehouseId, scanner.isScanning]);
  209. const warehouseDisplay = useMemo(() => {
  210. // 优先使用扫描的 warehouseId,如果没有扫描则显示默认值作为建议
  211. const displayWarehouseId = warehouseId > 0
  212. ? warehouseId
  213. : (itemDefaultWarehouseId || firstWarehouseId || 0);
  214. const wh = warehouse.find((w) => w.id == displayWarehouseId) ?? warehouse.find((w) => w.id == 1);
  215. return <>{wh?.name} <br/> [{wh?.code}]</>;
  216. }, [warehouse, warehouseId, itemDefaultWarehouseId, firstWarehouseId, verified]);
  217. // useEffect(() => { // Restart scanner for changing warehouse
  218. // if (warehouseId > 0) {
  219. // scanner.startScan();
  220. // console.log("%c Scanner restarted", "color:cyan");
  221. // }
  222. // }, [isOpenScanner])
  223. useLayoutEffect(() => {
  224. if (itemDetail !== undefined) {
  225. if (itemDetail.status == "received") {
  226. formProps.reset({
  227. ...defaultNewValue
  228. })
  229. const total = itemDetail.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0;
  230. setPutQty(itemDetail?.acceptedQty - total);
  231. // ✅ Get first warehouse from existing put away lines
  232. const firstPutAwayLine = itemDetail.putAwayLines?.[0];
  233. if (firstPutAwayLine?.warehouseId) {
  234. setFirstWarehouseId(firstPutAwayLine.warehouseId);
  235. // ✅ Store first warehouse info for display
  236. const firstWh = warehouse.find((w) => w.id == firstPutAwayLine.warehouseId);
  237. if (firstWh) {
  238. setFirstWarehouseInfo({
  239. name: firstWh.name || "",
  240. code: firstWh.code || ""
  241. });
  242. }
  243. } else {
  244. setFirstWarehouseId(null);
  245. setFirstWarehouseInfo(null);
  246. }
  247. console.log("%c Loaded data:", "color:lime", defaultNewValue);
  248. } else {
  249. switch (itemDetail.status) {
  250. case "pending": alert("此貨品有待品檢"); break;
  251. case "rejected": alert("此貨品已被拒收"); break;
  252. case "escalated": alert("此貨品已被上報"); break;
  253. case "partially_completed": alert("此貨品已部分上架"); break;
  254. case "completed": alert("此貨品已上架"); break;
  255. default: alert("此貨品暫時無法上架"); break;
  256. }
  257. closeHandler({}, "backdropClick");
  258. }
  259. }
  260. }, [open, itemDetail, defaultNewValue])
  261. const fetchStockInLine = useCallback(
  262. async (stockInLineId: number) => {
  263. setUnavailableText(undefined);
  264. try {
  265. const res = await fetchStockInLineInfo(stockInLineId);
  266. console.log("%c Fetched Stock In Line Info:", "color:gold", res);
  267. const total = res.putAwayLines?.reduce((sum, p) => sum + p.qty, 0) ?? 0;
  268. setTotalPutAwayQty(total);
  269. setItemDetail(res);
  270. } catch (e) {
  271. console.log("%c Error when fetching Stock In Line: ", "color:red", e);
  272. alert("錯誤的二維碼");
  273. closeHandler({}, "backdropClick");
  274. }
  275. },
  276. [formProps, itemDetail, fetchStockInLineInfo],
  277. );
  278. useEffect(() => {
  279. if (stockInLineId) { fetchStockInLine(stockInLineId); }
  280. }, [stockInLineId]);
  281. const validateQty = useCallback((qty : number = putQty) => {
  282. // if (isNaN(putQty) || putQty === undefined || putQty === null || typeof(putQty) != "number") {
  283. // setQtyError(t("value must be a number"));
  284. // } else
  285. if (!Number.isInteger(qty)) {
  286. setQtyError(t("value must be integer"));
  287. }
  288. //if (qty > itemDetail?.demandQty!! - totalPutAwayQty) {
  289. //setQtyError(`${t("putQty must not greater than")} ${
  290. // itemDetail?.demandQty!! - totalPutAwayQty}` );
  291. //}
  292. if (qty > itemDetail?.acceptedQty!! - totalPutAwayQty) {
  293. setQtyError(`${t("putQty must not greater than")} ${
  294. itemDetail?.acceptedQty!! - totalPutAwayQty}` );
  295. }
  296. else
  297. // if (qty > itemDetail?.acceptedQty!!) {
  298. // setQtyError(`${t("putQty must not greater than")} ${
  299. // itemDetail?.acceptedQty!!}` );
  300. // } else
  301. if (qty < 1) {
  302. setQtyError(t("minimal value is 1"));
  303. } else {
  304. setQtyError("");
  305. }
  306. console.log("%c Validated putQty:", "color:yellow", putQty);
  307. },[setQtyError, putQty, itemDetail])
  308. const onSubmit = useCallback<SubmitHandler<ModalFormInput>>(
  309. async (data, event) => {
  310. // console.log("errors", errors);
  311. // const lotLine = {
  312. // warehouseId: warehouseId,
  313. // qty: acceptQty;
  314. // }
  315. try {
  316. // 确定最终使用的 warehouseId
  317. const effectiveWarehouseId = warehouseId > 0
  318. ? warehouseId
  319. : (itemDefaultWarehouseId || 0);
  320. if (firstWarehouseId !== null && effectiveWarehouseId !== firstWarehouseId) {
  321. setWarehouseMismatchError("倉庫不匹配!必須使用首次上架的倉庫");
  322. return;
  323. }
  324. const args = {
  325. // ...itemDetail,
  326. id: itemDetail?.id,
  327. purchaseOrderId: itemDetail?.purchaseOrderId,
  328. purchaseOrderLineId: itemDetail?.purchaseOrderLineId,
  329. itemId: itemDetail?.itemId,
  330. acceptedQty: itemDetail?.acceptedQty,
  331. acceptQty: itemDetail?.acceptedQty,
  332. status: "received",
  333. // purchaseOrderId: parseInt(params.get("id")!),
  334. // purchaseOrderLineId: itemDetail?.purchaseOrderLineId,
  335. // itemId: itemDetail?.itemId,
  336. // acceptedQty: data.acceptedQty,
  337. // status: data.status,
  338. // ...data,
  339. // productionDate: productionDate,
  340. // for putaway data
  341. inventoryLotLines: [{
  342. warehouseId: effectiveWarehouseId,
  343. qty: putQty,
  344. }],
  345. // data.putAwayLines?.filter((line) => line._isNew !== false)
  346. } as StockInLineEntry & ModalFormInput;
  347. console.log(args);
  348. // return
  349. // if (formProps.formState.errors) {
  350. // setServerError(t("An error has occurred. Please try again later."));
  351. // return false;
  352. // }
  353. if (qtyError !== "") {
  354. return;
  355. }
  356. console.log("%c Submitting Data:", "color:blue", args);
  357. const res = await updateStockInLine(args);
  358. if (Boolean(res.id)) {
  359. // update entries
  360. console.log("%c Update Success:", "color:green", res);
  361. // add loading
  362. const putAwayData = {
  363. itemName: itemDetail?.itemName,
  364. itemCode: itemDetail?.itemNo,
  365. poCode: itemDetail?.poCode,
  366. joCode: itemDetail?.joCode,
  367. lotNo: itemDetail?.lotNo,
  368. warehouseCode: warehouse.find((w) => w.id == effectiveWarehouseId)?.code,
  369. warehouse: warehouse.find((w) => w.id == effectiveWarehouseId)?.name,
  370. putQty: putQty,
  371. uom: itemDetail?.uom?.udfudesc,
  372. } as PutAwayRecord;
  373. addPutAwayHistory(putAwayData);
  374. msg("貨品上架成功!");
  375. closeHandler({}, "backdropClick");
  376. }
  377. // console.log(res);
  378. // if (res)
  379. } catch (e) {
  380. // server error
  381. setServerError(t("An error has occurred. Please try again later."));
  382. console.log(e);
  383. }
  384. },
  385. [t, itemDetail, putQty, warehouseId, itemDefaultWarehouseId, firstWarehouseId, warehouse],
  386. );
  387. return (
  388. <FormProvider {...formProps}>
  389. <Modal open={open} onClose={closeHandler}>
  390. <Box
  391. sx={style}
  392. component="form"
  393. onSubmit={formProps.handleSubmit(onSubmit)}
  394. >
  395. <Box sx={{ overflow: "hidden", flex: 1, display: "flex", flexDirection: "column" }}>
  396. <Grid container xs={12}>
  397. <Grid item xs={12}>
  398. {itemDetail != undefined ? (
  399. <>
  400. <Stack direction="column" justifyContent="flex-end" gap={0.25} sx={{ mb: 0.5 }}>
  401. <Typography variant="h4" sx={{ fontSize: { xs: "0.95rem", sm: "1.1rem", md: "1.3rem" }, mb: 0.25, lineHeight: 1.2 }}>
  402. 處理上架
  403. </Typography>
  404. <Box sx={{ "& .MuiFormControl-root": { mb: 0.5 }, "& .MuiTextField-root": { mb: 0.5 }, "& .MuiGrid-item": { mb: 0.25 } }}>
  405. <Grid item xs={12}>
  406. {itemDetail.jobOrderId ? (
  407. <FgStockInForm itemDetail={itemDetail} disabled={true} putawayMode={true}/>
  408. ) : (
  409. <StockInForm itemDetail={itemDetail} disabled={true} putawayMode={true}/>
  410. )}
  411. </Grid>
  412. </Box>
  413. <Paper sx={{ mt: 0.5, padding: { xs: 0.5, sm: 0.75, md: 1 }, width: "100%", backgroundColor: verified ? '#bceb19' : '#FCD34D' }}>
  414. <Grid
  415. container
  416. spacing={{ xs: 0.5, sm: 0.75, md: 1 }}
  417. direction="row"
  418. justifyContent="space-between"
  419. alignItems="stretch"
  420. sx={{ width: '100%' }}
  421. >
  422. <Grid item xs={12} sm={5}>
  423. <Box sx={{
  424. mb: { xs: 0.25, md: 0.5 },
  425. p: { xs: 0.5, sm: 0.75, md: 1 },
  426. backgroundColor: verified ? '#bceb19' : '#FCD34D',
  427. borderRadius: 0,
  428. display: 'flex',
  429. flexDirection: 'row',
  430. gap: 0.25,
  431. flexWrap: 'wrap'
  432. }}
  433. >
  434. <Grid container>
  435. {verified? (
  436. <>
  437. <CheckCircle sx={{
  438. color:"green",
  439. fontSize: { xs: "16px", sm: "20px", md: "24px" },
  440. mr: 0.25
  441. }}/>
  442. <Typography
  443. variant="h5"
  444. component="h2"
  445. sx={{
  446. fontWeight: 'bold',
  447. color: 'black',
  448. fontSize: { xs: "0.75rem", sm: "0.9rem", md: "1rem" },
  449. lineHeight: 1.2
  450. }}
  451. noWrap
  452. >
  453. 掃瞄完成
  454. </Typography>
  455. </>
  456. ) : (
  457. <>
  458. <ErrorOutline sx={{
  459. color:"red",
  460. fontSize: { xs: "16px", sm: "20px", md: "24px" },
  461. mr: 0.25
  462. }}/>
  463. <Typography
  464. variant="h5"
  465. component="h2"
  466. sx={{
  467. fontWeight: 'bold',
  468. color: 'black',
  469. fontSize: { xs: "0.75rem", sm: "0.9rem", md: "1rem" },
  470. lineHeight: 1.2
  471. }}
  472. noWrap
  473. >
  474. {warehouseMismatchError || (firstWarehouseId !== null && warehouseId > 0 && warehouseId !== firstWarehouseId)
  475. ? "倉庫不匹配!請掃瞄首次上架的倉庫"
  476. : "請掃瞄倉庫二維碼"}
  477. </Typography>
  478. </>
  479. )
  480. }
  481. <QrCode sx={{ fontSize: { xs: "16px", sm: "20px", md: "24px" } }}/>
  482. </Grid>
  483. <Grid container>
  484. <Typography
  485. variant="h4"
  486. sx={{
  487. fontWeight: 'bold',
  488. color: 'black',
  489. fontSize: { xs: "0.85rem", sm: "1rem", md: "1.25rem" },
  490. lineHeight: 1.2
  491. }}
  492. noWrap
  493. >
  494. {warehouseDisplay} <Box component="span" sx={{ fontSize: { xs: "0.95rem", sm: "1.2rem", md: "1.5rem" }, color: "black" }}>{verified ? "" : `(建議)`}</Box>
  495. </Typography>
  496. </Grid>
  497. </Box>
  498. </Grid>
  499. <Grid item xs={12} sm={3}>
  500. <Box sx={{
  501. height: '100%',
  502. p: { xs: 0.25, sm: 0.5, md: 0.75 },
  503. textAlign: 'center',
  504. display: "flex",
  505. flexDirection: "column",
  506. justifyContent: "center",
  507. }}>
  508. <TextField
  509. type="number"
  510. label={t("putQty")}
  511. fullWidth
  512. size="small"
  513. sx={{
  514. flex: 1,
  515. "& .MuiInputBase-input": {
  516. padding: { xs: "6px 6px 1px", sm: "8px 8px 2px", md: "10px 10px 3px" },
  517. fontSize: { xs: "16px", sm: "22px", md: "32px" },
  518. fontWeight: "bold",
  519. height: { xs: "32px", sm: "40px", md: "48px" },
  520. },
  521. "& .MuiInputBase-root": {
  522. height: "100%",
  523. borderColor: "black",
  524. },
  525. "& .MuiInputLabel-root": {
  526. fontSize: { xs: "10px", sm: "12px", md: "18px" },
  527. top: "-2px",
  528. color: "black",
  529. },
  530. "& .MuiFormHelperText-root": {
  531. fontSize: { xs: "9px", sm: "10px", md: "14px" },
  532. marginTop: "2px",
  533. lineHeight: "1.1",
  534. },
  535. }}
  536. // defaultValue={itemDetail?.demandQty!! - totalPutAwayQty}
  537. // defaultValue={itemDetail.demandQty}
  538. defaultValue={itemDetail?.acceptedQty!! - totalPutAwayQty}
  539. onChange={(e) => {
  540. const value = e.target.value;
  541. validateQty(Number(value));
  542. setPutQty(Number(value));
  543. }}
  544. onKeyDown={(e) => {
  545. // Prevent non-numeric characters
  546. if (["e", "E", "+", "-", "."].includes(e.key)) {
  547. e.preventDefault();
  548. }
  549. }}
  550. // onBlur={(e) => {
  551. // const value = e.target.value;
  552. // setPutQty(Number(value));
  553. // validateQty(Number(value));
  554. // }}
  555. // disabled={true}
  556. // {...register("acceptedQty", {
  557. // required: "acceptedQty required!",
  558. // })}
  559. error={qtyError !== ""}
  560. helperText={qtyError}
  561. />
  562. </Box>
  563. </Grid>
  564. <Grid item xs={12} sm={4}>
  565. <Box
  566. sx={{
  567. p: { xs: 0.25, sm: 0.5, md: 0.75 },
  568. textAlign: 'center',
  569. height: '100%', // Ensure D stretches to full height
  570. display: 'flex',
  571. alignItems: 'center',
  572. justifyContent: 'center',
  573. }}>
  574. <Button
  575. id="putawaySubmit"
  576. type="submit"
  577. variant="contained"
  578. startIcon={<Check sx={{ fontSize: { xs: "14px", sm: "16px", md: "20px" } }} />}
  579. color="secondary"
  580. size="small"
  581. sx={{
  582. height: "100%",
  583. flex: "0 0 auto",
  584. padding: { xs: "3px 6px", sm: "4px 8px", md: "6px 12px" },
  585. fontSize: { xs: "9px", sm: "11px", md: "18px" },
  586. whiteSpace: "nowrap",
  587. textAlign: "center",
  588. border: "1.5px solid", // Add outline
  589. borderColor: "blue",
  590. minHeight: { xs: "32px", sm: "36px", md: "40px" },
  591. "&:hover": {
  592. borderColor: "grey.200", // Slightly different color on hover
  593. backgroundColor: "secondary.dark", // Darker background on hover
  594. },
  595. "&:disabled": {
  596. borderColor: "grey.400", // Visible outline even when disabled
  597. opacity: 0.7, // Slightly faded but still visible
  598. },
  599. }}
  600. // onClick={formProps.handleSubmit()}
  601. disabled={!verified || qtyError != ""}
  602. >
  603. {t("confirm putaway")}
  604. </Button>
  605. </Box>
  606. {/* <Button
  607. id="scanWarehouse"
  608. variant="contained"
  609. startIcon={<QrCode />}
  610. color="primary"
  611. // sx={{ mx: 3, minWidth : "120px", height: "80px",
  612. // padding: "12px 12px", fontSize: "24px"}}
  613. sx={{
  614. flex: "0 0 auto",
  615. padding: "8px 16px",
  616. fontSize: { xs: "16px", sm: "20px", md: "24px" },
  617. whiteSpace: "nowrap",
  618. textAlign: "center",}}
  619. onClick={openScanner}>
  620. {t("scan warehouse")}
  621. </Button> */}
  622. </Grid>
  623. </Grid>
  624. </Paper>
  625. </Stack>
  626. </>
  627. ) : (
  628. // <ReactQrCodeScanner scannerConfig={scannerConfig} />
  629. <>
  630. <Typography variant="h4" sx={{ fontSize: { xs: "0.95rem", sm: "1.1rem", md: "1.3rem" } }}>
  631. {t("scan loading")}
  632. </Typography>
  633. <LoadingComponent/>
  634. </>
  635. )}
  636. </Grid>
  637. </Grid>
  638. </Box>
  639. <Modal open={isOpenScanner} onClose={scannerCloseHandler}>
  640. <Box sx={scannerStyle}>
  641. <Typography variant="h4" sx={{
  642. display: 'flex',
  643. flexDirection: 'column',
  644. justifyContent: 'center',
  645. margin: 0,
  646. alignItems: 'center',
  647. textAlign: 'center',
  648. fontSize: { xs: "0.95rem", sm: "1.1rem", md: "1.3rem" }
  649. }}
  650. >
  651. {t("Please scan warehouse qr code")}
  652. </Typography>
  653. {/* <ReactQrCodeScanner scannerConfig={scannerConfig} /> */}
  654. </Box>
  655. </Modal>
  656. </Box>
  657. </Modal>
  658. </FormProvider>
  659. );
  660. };
  661. export default PutAwayModal;