FPSMS-frontend
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

528 rivejä
14 KiB

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