FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

PutAwayModal.tsx 27 KiB

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