FPSMS-frontend
25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

529 lines
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("putawayLine"))
  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", "received");
  234. // setValue("status", "completed");
  235. setValue("warehouseId", options[0].value); //TODO: save all warehouse entry?
  236. }, []);
  237. useEffect(() => {
  238. if (warehouseId > 0) {
  239. setValue("warehouseId", warehouseId);
  240. clearErrors("warehouseId");
  241. }
  242. }, [warehouseId]);
  243. const getWarningTextHardcode = useCallback((): string | undefined => {
  244. console.log(options)
  245. if (options.length === 0) return undefined
  246. const defaultWarehouseId = options[0].value;
  247. const currWarehouseId = watch("warehouseId");
  248. if (defaultWarehouseId !== currWarehouseId) {
  249. return t("not default warehosue");
  250. }
  251. return undefined;
  252. }, [options]);
  253. const columns = useMemo<GridColDef[]>(
  254. () => [
  255. {
  256. field: "qty",
  257. headerName: t("qty"),
  258. flex: 1,
  259. // renderCell(params) {
  260. // return <>100</>
  261. // },
  262. },
  263. {
  264. field: "warehouse",
  265. headerName: t("warehouse"),
  266. flex: 1,
  267. // renderCell(params) {
  268. // return <>{filteredWarehouse[0].name}</>
  269. // },
  270. },
  271. {
  272. field: "printQty",
  273. headerName: t("printQty"),
  274. flex: 1,
  275. // renderCell(params) {
  276. // return <>100</>
  277. // },
  278. },
  279. ], [])
  280. const validation = useCallback(
  281. (newRow: GridRowModel<PutawayRow>): EntryError => {
  282. const error: EntryError = {};
  283. const { qty, warehouseId, printQty } = newRow;
  284. return Object.keys(error).length > 0 ? error : undefined;
  285. },
  286. [],
  287. );
  288. return (
  289. <Grid container justifyContent="flex-start" alignItems="flex-start">
  290. <Grid item xs={12}>
  291. <Typography variant="h6" display="block" marginBlockEnd={1}>
  292. {t("Putaway Detail")}
  293. </Typography>
  294. </Grid>
  295. <Grid
  296. container
  297. justifyContent="flex-start"
  298. alignItems="flex-start"
  299. spacing={2}
  300. sx={{ mt: 0.5 }}
  301. >
  302. <Grid item xs={12}>
  303. <TextField
  304. label={t("LotNo")}
  305. fullWidth
  306. value={itemDetail.lotNo}
  307. disabled
  308. />
  309. </Grid>
  310. <Grid item xs={6}>
  311. <TextField
  312. label={t("Supplier")}
  313. fullWidth
  314. value={itemDetail.supplier}
  315. disabled
  316. />
  317. </Grid>
  318. <Grid item xs={6}>
  319. <TextField
  320. label={t("Po Code")}
  321. fullWidth
  322. value={itemDetail.poCode}
  323. disabled
  324. />
  325. </Grid>
  326. <Grid item xs={6}>
  327. <TextField
  328. label={t("itemName")}
  329. fullWidth
  330. value={itemDetail.itemName}
  331. disabled
  332. />
  333. </Grid>
  334. <Grid item xs={6}>
  335. <TextField
  336. label={t("itemNo")}
  337. fullWidth
  338. value={itemDetail.itemNo}
  339. disabled
  340. />
  341. </Grid>
  342. <Grid item xs={6}>
  343. <TextField
  344. label={t("qty")}
  345. fullWidth
  346. value={itemDetail.acceptedQty}
  347. disabled
  348. />
  349. </Grid>
  350. <Grid item xs={6}>
  351. <TextField
  352. label={t("productionDate")}
  353. fullWidth
  354. value={
  355. // dayjs(itemDetail.productionDate)
  356. dayjs()
  357. // .add(-1, "month")
  358. .format(OUTPUT_DATE_FORMAT)}
  359. disabled
  360. />
  361. </Grid>
  362. <Grid item xs={6}>
  363. <TextField
  364. label={t("expiryDate")}
  365. fullWidth
  366. value={
  367. // dayjs(itemDetail.expiryDate)
  368. dayjs()
  369. .add(20, "day")
  370. .format(OUTPUT_DATE_FORMAT)}
  371. disabled
  372. />
  373. </Grid>
  374. <Grid item xs={6}>
  375. <FormControl fullWidth>
  376. <Autocomplete
  377. noOptionsText={t("No Warehouse")}
  378. disableClearable
  379. disabled
  380. fullWidth
  381. defaultValue={options[0]} /// modify this later
  382. // onChange={onChange}
  383. getOptionLabel={(option) => option.label}
  384. options={options}
  385. renderInput={(params) => (
  386. <TextField {...params} label={t("Default Warehouse")} />
  387. )}
  388. />
  389. </FormControl>
  390. </Grid>
  391. {/* <Grid item xs={5.5}>
  392. <TextField
  393. label={t("acceptedQty")}
  394. fullWidth
  395. {...register("acceptedQty", {
  396. required: "acceptedQty required!",
  397. min: 1,
  398. max: itemDetail.acceptedQty,
  399. valueAsNumber: true,
  400. })}
  401. // defaultValue={itemDetail.acceptedQty}
  402. disabled={disabled}
  403. error={Boolean(errors.acceptedQty)}
  404. helperText={errors.acceptedQty?.message}
  405. />
  406. </Grid>
  407. <Grid item xs={1}>
  408. <Button disabled={disabled} onClick={onOpenScanner}>
  409. {t("bind")}
  410. </Button>
  411. </Grid> */}
  412. {/* <Grid item xs={5.5}>
  413. <Controller
  414. control={control}
  415. name="warehouseId"
  416. render={({ field }) => {
  417. console.log(field);
  418. return (
  419. <Autocomplete
  420. noOptionsText={t("No Warehouse")}
  421. disableClearable
  422. fullWidth
  423. value={options.find((o) => o.value == field.value)}
  424. onChange={onChange}
  425. getOptionLabel={(option) => option.label}
  426. options={options}
  427. renderInput={(params) => (
  428. <TextField
  429. {...params}
  430. label={"Select warehouse"}
  431. error={Boolean(errors.warehouseId?.message)}
  432. helperText={warehouseHelperText}
  433. // helperText={errors.warehouseId?.message}
  434. />
  435. )}
  436. />
  437. );
  438. }}
  439. />
  440. <FormControl fullWidth>
  441. <Autocomplete
  442. noOptionsText={t("No Warehouse")}
  443. disableClearable
  444. fullWidth
  445. // value={warehouseId > 0
  446. // ? options.find((o) => o.value === warehouseId)
  447. // : undefined}
  448. defaultValue={options[0]}
  449. // defaultValue={options.find((o) => o.value === 1)}
  450. value={currentValue}
  451. onChange={onChange}
  452. getOptionLabel={(option) => option.label}
  453. options={options}
  454. renderInput={(params) => (
  455. <TextField
  456. {...params}
  457. // label={"Select warehouse"}
  458. disabled={disabled}
  459. error={Boolean(errors.warehouseId?.message)}
  460. helperText={
  461. errors.warehouseId?.message ?? getWarningTextHardcode()
  462. }
  463. // helperText={warehouseHelperText}
  464. />
  465. )}
  466. />
  467. </FormControl>
  468. </Grid> */}
  469. <Grid
  470. item
  471. xs={12}
  472. style={{ display: "flex", justifyContent: "center" }}
  473. >
  474. {/* <QrCode content={qrContent} sx={{ width: 200, height: 200 }} /> */}
  475. <InputDataGrid<PutawayInput, PutawayLine, EntryError>
  476. apiRef={apiRef}
  477. checkboxSelection={false}
  478. _formKey={"putawayLine"}
  479. columns={columns}
  480. validateRow={validation}
  481. needAdd={true}
  482. showRemoveBtn={false}
  483. />
  484. </Grid>
  485. </Grid>
  486. {/* <Grid
  487. container
  488. justifyContent="flex-start"
  489. alignItems="flex-start"
  490. spacing={2}
  491. sx={{ mt: 0.5 }}
  492. >
  493. <Button onClick={onOpenScanner}>bind</Button>
  494. </Grid> */}
  495. <Modal open={isOpenScanner} onClose={closeHandler}>
  496. <Box sx={style}>
  497. <Typography variant="h4">
  498. {t("Please scan warehouse qr code.")}
  499. </Typography>
  500. {/* <ReactQrCodeScanner scannerConfig={scannerConfig} /> */}
  501. </Box>
  502. </Modal>
  503. </Grid>
  504. );
  505. };
  506. export default PutawayForm;