FPSMS-frontend
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符
 
 
 

473 行
13 KiB

  1. "use client";
  2. import { PurchaseQcResult, PutawayInput } from "@/app/api/po/actions";
  3. import {
  4. Autocomplete,
  5. Box,
  6. Button,
  7. Card,
  8. CardContent,
  9. FormControl,
  10. Grid,
  11. Modal,
  12. ModalProps,
  13. Stack,
  14. TextField,
  15. Tooltip,
  16. Typography,
  17. } from "@mui/material";
  18. import { Controller, useFormContext } from "react-hook-form";
  19. import { useTranslation } from "react-i18next";
  20. import StyledDataGrid from "../StyledDataGrid";
  21. import { useCallback, useEffect, useMemo, useState } from "react";
  22. import {
  23. GridColDef,
  24. GridRowIdGetter,
  25. GridRowModel,
  26. useGridApiContext,
  27. GridRenderCellParams,
  28. GridRenderEditCellParams,
  29. useGridApiRef,
  30. } from "@mui/x-data-grid";
  31. import InputDataGrid from "../InputDataGrid";
  32. import { TableRow } from "../InputDataGrid/InputDataGrid";
  33. import TwoLineCell from "./TwoLineCell";
  34. import QcSelect from "./QcSelect";
  35. import { QcItemWithChecks } from "@/app/api/qc";
  36. import { GridEditInputCell } from "@mui/x-data-grid";
  37. import { StockInLine } from "@/app/api/po";
  38. import { WarehouseResult } from "@/app/api/warehouse";
  39. import {
  40. OUTPUT_DATE_FORMAT,
  41. stockInLineStatusMap,
  42. } from "@/app/utils/formatUtil";
  43. import { QRCodeSVG } from "qrcode.react";
  44. import { QrCode } from "../QrCode";
  45. import ReactQrCodeScanner, {
  46. ScannerConfig,
  47. } from "../ReactQrCodeScanner/ReactQrCodeScanner";
  48. import { QrCodeInfo } from "@/app/api/qrcode";
  49. import { useQrCodeScannerContext } from "../QrCodeScannerProvider/QrCodeScannerProvider";
  50. import dayjs from "dayjs";
  51. import arraySupport from "dayjs/plugin/arraySupport";
  52. dayjs.extend(arraySupport);
  53. interface Props {
  54. itemDetail: StockInLine;
  55. warehouse: WarehouseResult[];
  56. disabled: boolean;
  57. // qc: QcItemWithChecks[];
  58. }
  59. type EntryError =
  60. | {
  61. [field in keyof PurchaseQcResult]?: string;
  62. }
  63. | undefined;
  64. // type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>;
  65. const style = {
  66. position: "absolute",
  67. top: "50%",
  68. left: "50%",
  69. transform: "translate(-50%, -50%)",
  70. bgcolor: "background.paper",
  71. pt: 5,
  72. px: 5,
  73. pb: 10,
  74. width: "auto",
  75. };
  76. const PutawayForm: React.FC<Props> = ({ itemDetail, warehouse, disabled }) => {
  77. const { t } = useTranslation("purchaseOrder");
  78. const apiRef = useGridApiRef();
  79. const {
  80. register,
  81. formState: { errors, defaultValues, touchedFields },
  82. watch,
  83. control,
  84. setValue,
  85. getValues,
  86. reset,
  87. resetField,
  88. setError,
  89. clearErrors,
  90. } = useFormContext<PutawayInput>();
  91. console.log(itemDetail);
  92. // const [recordQty, setRecordQty] = useState(0);
  93. const [warehouseId, setWarehouseId] = useState(itemDetail.defaultWarehouseId);
  94. const filteredWarehouse = useMemo(() => {
  95. // do filtering here if any
  96. return warehouse;
  97. }, []);
  98. const defaultOption = {
  99. value: 0, // think think sin
  100. label: t("Select warehouse"),
  101. group: "default",
  102. };
  103. const options = useMemo(() => {
  104. return [
  105. // {
  106. // value: 0, // think think sin
  107. // label: t("Select warehouse"),
  108. // group: "default",
  109. // },
  110. ...filteredWarehouse.map((w) => ({
  111. value: w.id,
  112. label: `${w.code} - ${w.name}`,
  113. group: "existing",
  114. })),
  115. ];
  116. }, [filteredWarehouse]);
  117. const currentValue =
  118. warehouseId > 0
  119. ? options.find((o) => o.value === warehouseId)
  120. : options.find((o) => o.value === getValues("warehouseId")) ||
  121. defaultOption;
  122. const onChange = useCallback(
  123. (
  124. event: React.SyntheticEvent,
  125. newValue: { value: number; group: string } | { value: number }[],
  126. ) => {
  127. const singleNewVal = newValue as {
  128. value: number;
  129. group: string;
  130. };
  131. console.log(singleNewVal);
  132. console.log("onChange");
  133. // setValue("warehouseId", singleNewVal.value);
  134. setWarehouseId(singleNewVal.value);
  135. },
  136. [],
  137. );
  138. // const accQty = watch("acceptedQty");
  139. // const validateForm = useCallback(() => {
  140. // console.log(accQty);
  141. // if (accQty > itemDetail.acceptedQty) {
  142. // setError("acceptedQty", {
  143. // message: `acceptedQty must not greater than ${itemDetail.acceptedQty}`,
  144. // type: "required",
  145. // });
  146. // }
  147. // if (accQty < 1) {
  148. // setError("acceptedQty", {
  149. // message: `minimal value is 1`,
  150. // type: "required",
  151. // });
  152. // }
  153. // if (isNaN(accQty)) {
  154. // setError("acceptedQty", {
  155. // message: `value must be a number`,
  156. // type: "required",
  157. // });
  158. // }
  159. // }, [accQty]);
  160. // useEffect(() => {
  161. // clearErrors();
  162. // validateForm();
  163. // }, [validateForm]);
  164. const qrContent = useMemo(
  165. () => ({
  166. stockInLineId: itemDetail.id,
  167. itemId: itemDetail.itemId,
  168. lotNo: itemDetail.lotNo,
  169. // warehouseId: 2 // for testing
  170. // expiryDate: itemDetail.expiryDate,
  171. // productionDate: itemDetail.productionDate,
  172. // supplier: itemDetail.supplier,
  173. // poCode: itemDetail.poCode,
  174. }),
  175. [itemDetail],
  176. );
  177. const [isOpenScanner, setOpenScanner] = useState(false);
  178. const closeHandler = useCallback<NonNullable<ModalProps["onClose"]>>(
  179. (...args) => {
  180. setOpenScanner(false);
  181. },
  182. [],
  183. );
  184. const onOpenScanner = useCallback(() => {
  185. setOpenScanner(true);
  186. }, []);
  187. const onCloseScanner = useCallback(() => {
  188. setOpenScanner(false);
  189. }, []);
  190. const scannerConfig = useMemo<ScannerConfig>(
  191. () => ({
  192. onUpdate: (err, result) => {
  193. console.log(result);
  194. console.log(Boolean(result));
  195. if (result) {
  196. const data: QrCodeInfo = JSON.parse(result.getText());
  197. console.log(data);
  198. if (data.warehouseId) {
  199. console.log(data.warehouseId);
  200. setWarehouseId(data.warehouseId);
  201. onCloseScanner();
  202. }
  203. } else return;
  204. },
  205. }),
  206. [onCloseScanner],
  207. );
  208. // QR Code Scanner
  209. const scanner = useQrCodeScannerContext();
  210. useEffect(() => {
  211. if (isOpenScanner) {
  212. scanner.startScan();
  213. } else if (!isOpenScanner) {
  214. scanner.stopScan();
  215. }
  216. }, [isOpenScanner]);
  217. useEffect(() => {
  218. if (scanner.values.length > 0) {
  219. console.log(scanner.values[0]);
  220. const data: QrCodeInfo = JSON.parse(scanner.values[0]);
  221. console.log(data);
  222. if (data.warehouseId) {
  223. console.log(data.warehouseId);
  224. setWarehouseId(data.warehouseId);
  225. onCloseScanner();
  226. }
  227. scanner.resetScan();
  228. }
  229. }, [scanner.values]);
  230. useEffect(() => {
  231. setValue("status", "completed");
  232. setValue("warehouseId", options[0].value);
  233. }, []);
  234. useEffect(() => {
  235. if (warehouseId > 0) {
  236. setValue("warehouseId", warehouseId);
  237. clearErrors("warehouseId");
  238. }
  239. }, [warehouseId]);
  240. const getWarningTextHardcode = useCallback((): string | undefined => {
  241. const defaultWarehouseId = options[0].value;
  242. const currWarehouseId = watch("warehouseId");
  243. if (defaultWarehouseId !== currWarehouseId) {
  244. return t("not default warehosue");
  245. }
  246. return undefined;
  247. }, [options]);
  248. return (
  249. <Grid container justifyContent="flex-start" alignItems="flex-start">
  250. <Grid item xs={12}>
  251. <Typography variant="h6" display="block" marginBlockEnd={1}>
  252. {t("Putaway Detail")}
  253. </Typography>
  254. </Grid>
  255. <Grid
  256. container
  257. justifyContent="flex-start"
  258. alignItems="flex-start"
  259. spacing={2}
  260. sx={{ mt: 0.5 }}
  261. >
  262. <Grid item xs={12}>
  263. <TextField
  264. label={t("LotNo")}
  265. fullWidth
  266. value={itemDetail.lotNo}
  267. disabled
  268. />
  269. </Grid>
  270. <Grid item xs={6}>
  271. <TextField
  272. label={t("Supplier")}
  273. fullWidth
  274. value={itemDetail.supplier}
  275. disabled
  276. />
  277. </Grid>
  278. <Grid item xs={6}>
  279. <TextField
  280. label={t("Po Code")}
  281. fullWidth
  282. value={itemDetail.poCode}
  283. disabled
  284. />
  285. </Grid>
  286. <Grid item xs={6}>
  287. <TextField
  288. label={t("itemName")}
  289. fullWidth
  290. value={itemDetail.itemName}
  291. disabled
  292. />
  293. </Grid>
  294. <Grid item xs={6}>
  295. <TextField
  296. label={t("itemNo")}
  297. fullWidth
  298. value={itemDetail.itemNo}
  299. disabled
  300. />
  301. </Grid>
  302. <Grid item xs={6}>
  303. <TextField
  304. label={t("qty")}
  305. fullWidth
  306. value={itemDetail.acceptedQty}
  307. disabled
  308. />
  309. </Grid>
  310. <Grid item xs={6}>
  311. <TextField
  312. label={t("productionDate")}
  313. fullWidth
  314. value={dayjs(itemDetail.productionDate)
  315. .add(-1, "month")
  316. .format(OUTPUT_DATE_FORMAT)}
  317. disabled
  318. />
  319. </Grid>
  320. <Grid item xs={6}>
  321. <TextField
  322. label={t("expiryDate")}
  323. fullWidth
  324. value={dayjs(itemDetail.expiryDate)
  325. .add(-1, "month")
  326. .format(OUTPUT_DATE_FORMAT)}
  327. disabled
  328. />
  329. </Grid>
  330. <Grid item xs={6}>
  331. <FormControl fullWidth>
  332. <Autocomplete
  333. noOptionsText={t("No Warehouse")}
  334. disableClearable
  335. disabled
  336. fullWidth
  337. defaultValue={options[0]} /// modify this later
  338. // onChange={onChange}
  339. getOptionLabel={(option) => option.label}
  340. options={options}
  341. renderInput={(params) => (
  342. <TextField {...params} label={t("Default Warehouse")} />
  343. )}
  344. />
  345. </FormControl>
  346. </Grid>
  347. <Grid item xs={5.5}>
  348. <TextField
  349. label={t("acceptedQty")}
  350. fullWidth
  351. {...register("acceptedQty", {
  352. required: "acceptedQty required!",
  353. min: 1,
  354. max: itemDetail.acceptedQty,
  355. valueAsNumber: true,
  356. })}
  357. // defaultValue={itemDetail.acceptedQty}
  358. disabled={disabled}
  359. error={Boolean(errors.acceptedQty)}
  360. helperText={errors.acceptedQty?.message}
  361. />
  362. </Grid>
  363. <Grid item xs={1}>
  364. <Button disabled={disabled} onClick={onOpenScanner}>
  365. {t("bind")}
  366. </Button>
  367. </Grid>
  368. <Grid item xs={5.5}>
  369. {/* <Controller
  370. control={control}
  371. name="warehouseId"
  372. render={({ field }) => {
  373. console.log(field);
  374. return (
  375. <Autocomplete
  376. noOptionsText={t("No Warehouse")}
  377. disableClearable
  378. fullWidth
  379. value={options.find((o) => o.value == field.value)}
  380. onChange={onChange}
  381. getOptionLabel={(option) => option.label}
  382. options={options}
  383. renderInput={(params) => (
  384. <TextField
  385. {...params}
  386. label={"Select warehouse"}
  387. error={Boolean(errors.warehouseId?.message)}
  388. helperText={warehouseHelperText}
  389. // helperText={errors.warehouseId?.message}
  390. />
  391. )}
  392. />
  393. );
  394. }}
  395. /> */}
  396. <FormControl fullWidth>
  397. <Autocomplete
  398. noOptionsText={t("No Warehouse")}
  399. disableClearable
  400. fullWidth
  401. // value={warehouseId > 0
  402. // ? options.find((o) => o.value === warehouseId)
  403. // : undefined}
  404. defaultValue={options[0]}
  405. // defaultValue={options.find((o) => o.value === 1)}
  406. value={currentValue}
  407. onChange={onChange}
  408. getOptionLabel={(option) => option.label}
  409. options={options}
  410. renderInput={(params) => (
  411. <TextField
  412. {...params}
  413. // label={"Select warehouse"}
  414. disabled={disabled}
  415. error={Boolean(errors.warehouseId?.message)}
  416. helperText={
  417. errors.warehouseId?.message ?? getWarningTextHardcode()
  418. }
  419. // helperText={warehouseHelperText}
  420. />
  421. )}
  422. />
  423. </FormControl>
  424. </Grid>
  425. <Grid
  426. item
  427. xs={12}
  428. style={{ display: "flex", justifyContent: "center" }}
  429. >
  430. <QrCode content={qrContent} sx={{ width: 200, height: 200 }} />
  431. </Grid>
  432. </Grid>
  433. {/* <Grid
  434. container
  435. justifyContent="flex-start"
  436. alignItems="flex-start"
  437. spacing={2}
  438. sx={{ mt: 0.5 }}
  439. >
  440. <Button onClick={onOpenScanner}>bind</Button>
  441. </Grid> */}
  442. <Modal open={isOpenScanner} onClose={closeHandler}>
  443. <Box sx={style}>
  444. <Typography variant="h4">
  445. {t("Please scan warehouse qr code.")}
  446. </Typography>
  447. {/* <ReactQrCodeScanner scannerConfig={scannerConfig} /> */}
  448. </Box>
  449. </Modal>
  450. </Grid>
  451. );
  452. };
  453. export default PutawayForm;