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.
 
 

458 rivejä
14 KiB

  1. "use client";
  2. import {
  3. StockInInput,
  4. } from "@/app/api/stockIn/actions";
  5. import {
  6. Box,
  7. Button,
  8. Card,
  9. CardContent,
  10. Grid,
  11. InputAdornment,
  12. Stack,
  13. TextField,
  14. Tooltip,
  15. Typography,
  16. } from "@mui/material";
  17. import { Controller, useFormContext } from "react-hook-form";
  18. import { useTranslation } from "react-i18next";
  19. import StyledDataGrid from "../StyledDataGrid";
  20. import { useCallback, useEffect, useState, useMemo } from "react";
  21. import {
  22. useGridApiRef,
  23. } from "@mui/x-data-grid";
  24. import { StockInLine } from "@/app/api/stockIn";
  25. import { DatePicker, LocalizationProvider } from "@mui/x-date-pickers";
  26. import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs";
  27. import { dayjsToDateString, INPUT_DATE_FORMAT, OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil";
  28. import dayjs from "dayjs";
  29. import CalculateExpiryDateModal from "./CalculateExpiryDateModal";
  30. // change PurchaseQcResult to stock in entry props
  31. interface Props {
  32. itemDetail: StockInLine;
  33. // qc: QcItemWithChecks[];
  34. disabled: boolean;
  35. putawayMode?: boolean;
  36. /** When true, use compact field sizing to match Putaway Detail (上架詳情) section. */
  37. compactFields?: boolean;
  38. }
  39. type EntryError =
  40. | {
  41. [field in keyof StockInInput]?: string;
  42. }
  43. | undefined;
  44. // type PoQcRow = TableRow<Partial<PurchaseQcResult>, EntryError>;
  45. const textfieldSx = {
  46. width: "100%",
  47. "& .MuiInputBase-root": {
  48. // height: "120", // Scales with root font size
  49. height: "5rem", // Scales with root font size
  50. },
  51. "& .MuiInputBase-input": {
  52. height: "100%",
  53. boxSizing: "border-box",
  54. padding: "0.75rem",
  55. fontSize: 30,
  56. },
  57. "& .MuiInputLabel-root": {
  58. fontSize: 30,
  59. transform: "translate(14px, 1.2rem) scale(1)",
  60. "&.MuiInputLabel-shrink": {
  61. fontSize: 24,
  62. transform: "translate(14px, -0.5rem) scale(1)",
  63. },
  64. // [theme.breakpoints.down("sm")]: {
  65. // fontSize: "1rem",
  66. // transform: "translate(14px, 1.5rem) scale(1)",
  67. // "&.MuiInputLabel-shrink": {
  68. // fontSize: "0.875rem",
  69. // },
  70. // },
  71. },
  72. };
  73. const StockInForm: React.FC<Props> = ({
  74. // qc,
  75. itemDetail,
  76. disabled,
  77. putawayMode = false,
  78. compactFields = false,
  79. }) => {
  80. const {
  81. t,
  82. i18n: { language },
  83. } = useTranslation("purchaseOrder");
  84. const apiRef = useGridApiRef();
  85. const {
  86. register,
  87. formState: { errors, defaultValues, touchedFields },
  88. watch,
  89. control,
  90. setValue,
  91. getValues,
  92. reset,
  93. resetField,
  94. setError,
  95. clearErrors,
  96. } = useFormContext<StockInInput>();
  97. // console.log(itemDetail);
  98. useEffect(() => {
  99. // console.log("triggered");
  100. // // receiptDate default tdy
  101. // setValue("receiptDate", dayjs().add(0, "month").format(INPUT_DATE_FORMAT));
  102. // setValue("status", "received");
  103. }, [setValue]);
  104. useEffect(() => {
  105. console.log(errors);
  106. }, [errors]);
  107. const productionDate = watch("productionDate");
  108. const expiryDate = watch("expiryDate");
  109. const uom = watch("uom");
  110. const [openModal, setOpenModal] = useState<boolean>(false);
  111. const [openExpDatePicker, setOpenExpDatePicker] = useState<boolean>(false);
  112. //// TODO : Add Checking ////
  113. // Check if dates are input
  114. // if (data.expiryDate === undefined || data.expiryDate == null) {
  115. // validationErrors.push("請輸入到期日!");
  116. // }
  117. useEffect(() => {
  118. // console.log(uom);
  119. // console.log(productionDate);
  120. // console.log(expiryDate);
  121. if (expiryDate) clearErrors();
  122. }, [productionDate, expiryDate, clearErrors]);
  123. useEffect(() => {
  124. console.log("%c StockInForm itemDetail update: ", "color: brown", itemDetail);
  125. }, [itemDetail]);
  126. const handleOpenModal = useCallback(() => {
  127. setOpenModal(true);
  128. }, []);
  129. const handleOnModalClose = useCallback(() => {
  130. setOpenExpDatePicker(false);
  131. setOpenModal(false);
  132. }, []);
  133. const handleReturnExpiryDate = useCallback((result: dayjs.Dayjs) => {
  134. if (result) {
  135. setValue("expiryDate", dayjsToDateString(result));
  136. }
  137. }, []);
  138. return (
  139. <>
  140. <Grid container justifyContent="flex-start" alignItems="flex-start">
  141. {/* <Grid item xs={12}>
  142. <Typography variant="h6" display="block" marginBlockEnd={1}>
  143. {t("Stock In Detail")}
  144. </Typography>
  145. </Grid> */}
  146. <Grid
  147. container
  148. justifyContent="flex-start"
  149. alignItems="flex-start"
  150. spacing={compactFields ? 1 : 2}
  151. sx={{ mt: 0.5 }}
  152. >
  153. <Grid item xs={6}>
  154. <TextField
  155. label={t("dnNo")}
  156. fullWidth
  157. {...register("dnNo", {
  158. // required: "productLotNo required!",
  159. })}
  160. sx={compactFields ? undefined : textfieldSx}
  161. disabled={disabled}
  162. // error={Boolean(errors.productLotNo)}
  163. // helperText={errors.productLotNo?.message}
  164. />
  165. </Grid>
  166. <Grid item xs={6}>
  167. <TextField
  168. label={t("itemName")}
  169. fullWidth
  170. {...register("itemName", {
  171. // required: "productLotNo required!",
  172. })}
  173. sx={compactFields ? undefined : textfieldSx}
  174. disabled={true}
  175. // error={Boolean(errors.productLotNo)}
  176. // helperText={errors.productLotNo?.message}
  177. />
  178. </Grid>
  179. <Grid item xs={6}>
  180. <TextField
  181. label={t("PO No.")}
  182. fullWidth
  183. {...register("poCode", {
  184. // required: "productLotNo required!",
  185. })}
  186. sx={compactFields ? undefined : textfieldSx}
  187. disabled={true}
  188. // error={Boolean(errors.productLotNo)}
  189. // helperText={errors.productLotNo?.message}
  190. />
  191. </Grid>
  192. {putawayMode || (
  193. <>
  194. <Grid item xs={6}>
  195. <Controller
  196. control={control}
  197. name="receiptDate"
  198. rules={{ required: true }}
  199. render={({ field }) => {
  200. return (
  201. <LocalizationProvider
  202. dateAdapter={AdapterDayjs}
  203. adapterLocale={`${language}-hk`}
  204. >
  205. <DatePicker
  206. {...field}
  207. sx={compactFields ? { width: '100%' } : textfieldSx}
  208. label={t("receiptDate")}
  209. value={dayjs(watch("receiptDate"))}
  210. format={OUTPUT_DATE_FORMAT}
  211. disabled={true}
  212. onChange={(date) => {
  213. if (!date) return;
  214. // setValue("receiptDate", date.format(INPUT_DATE_FORMAT));
  215. field.onChange(date);
  216. }}
  217. inputRef={field.ref}
  218. slotProps={{
  219. textField: {
  220. fullWidth: true,
  221. // required: true,
  222. error: Boolean(errors.receiptDate?.message),
  223. helperText: errors.receiptDate?.message,
  224. },
  225. }}
  226. />
  227. </LocalizationProvider>
  228. );
  229. }}
  230. />
  231. </Grid>
  232. <Grid item xs={6}>
  233. <TextField
  234. label={t("Supplier")}
  235. fullWidth
  236. {...register("supplier", {
  237. // required: "productLotNo required!",
  238. })}
  239. sx={compactFields ? undefined : textfieldSx}
  240. disabled={true}
  241. />
  242. </Grid>
  243. </>
  244. )}
  245. <Grid item xs={6}>
  246. {putawayMode ? (
  247. <TextField
  248. label={t("stockLotNo")}
  249. fullWidth
  250. {...register("lotNo", {
  251. // required: "productLotNo required!",
  252. })}
  253. sx={compactFields ? undefined : textfieldSx}
  254. disabled={disabled}
  255. error={Boolean(errors.productLotNo)}
  256. helperText={errors.productLotNo?.message}
  257. />) : (
  258. <TextField
  259. label={t("productLotNo")}
  260. fullWidth
  261. {...register("productLotNo", {
  262. // required: "productLotNo required!",
  263. })}
  264. sx={compactFields ? undefined : textfieldSx}
  265. disabled={disabled}
  266. error={Boolean(errors.productLotNo)}
  267. helperText={errors.productLotNo?.message}
  268. />)}
  269. </Grid>
  270. <Grid item xs={6}>
  271. <Controller
  272. control={control}
  273. name="expiryDate"
  274. // rules={{ required: !Boolean(productionDate) }}
  275. render={({ field }) => {
  276. return (
  277. <LocalizationProvider
  278. dateAdapter={AdapterDayjs}
  279. adapterLocale={`${language}-hk`}
  280. >
  281. <DatePicker
  282. {...field}
  283. sx={compactFields ? { width: '100%' } : textfieldSx}
  284. label={t("expiryDate")}
  285. value={expiryDate ? dayjs(expiryDate) : undefined}
  286. format={OUTPUT_DATE_FORMAT}
  287. disabled={disabled}
  288. onChange={(date) => {
  289. if (!date) return;
  290. console.log(date.format(INPUT_DATE_FORMAT));
  291. setValue("expiryDate", date.format(INPUT_DATE_FORMAT));
  292. // field.onChange(date);
  293. }}
  294. inputRef={field.ref}
  295. open={openExpDatePicker && !openModal}
  296. onOpen={() => setOpenExpDatePicker(true)}
  297. onClose={() => setOpenExpDatePicker(false)}
  298. slotProps={{
  299. textField: {
  300. fullWidth: true,
  301. InputProps: {
  302. ...(!disabled && {endAdornment: (
  303. <InputAdornment position="end">
  304. <Button
  305. type="button"
  306. variant="contained"
  307. color="primary"
  308. size="small"
  309. sx={{
  310. fontSize: compactFields ? '0.875rem' : '24px',
  311. whiteSpace: 'nowrap',
  312. flexShrink: 0,
  313. }}
  314. onClick={handleOpenModal}
  315. >
  316. {t("Calculate Expiry Date")}
  317. </Button>
  318. </InputAdornment>
  319. ),})
  320. },
  321. // required: true,
  322. error: Boolean(errors.expiryDate?.message),
  323. helperText: errors.expiryDate?.message,
  324. onClick: () => setOpenExpDatePicker(true),
  325. },
  326. }}
  327. />
  328. </LocalizationProvider>
  329. );
  330. }}
  331. />
  332. </Grid>
  333. {putawayMode || (<>
  334. <Grid item xs={3}>
  335. <TextField
  336. label={t("receivedQty")}
  337. fullWidth
  338. {...register("receivedQty", {
  339. required: "receivedQty required!",
  340. })}
  341. sx={compactFields ? undefined : textfieldSx}
  342. disabled={true}
  343. />
  344. </Grid>
  345. <Grid item xs={3}>
  346. <TextField
  347. label={t("qty")}
  348. fullWidth
  349. {...register("qty", {
  350. required: "qty required!",
  351. })}
  352. sx={compactFields ? undefined : textfieldSx}
  353. disabled={true}
  354. />
  355. </Grid></>
  356. )}
  357. {putawayMode ? (
  358. <>
  359. <Grid item xs={6}>
  360. <TextField
  361. label={"本批次庫存收貨數量"}
  362. fullWidth
  363. sx={compactFields ? undefined : textfieldSx}
  364. disabled={true}
  365. value={`${itemDetail.acceptedQty ?? 0} (${itemDetail.stockUomDesc ?? ""})`}
  366. />
  367. </Grid>
  368. <Grid item xs={6}>
  369. <TextField
  370. label={"本批次採購收貨數量"}
  371. fullWidth
  372. sx={compactFields ? undefined : textfieldSx}
  373. disabled={true}
  374. value={`${itemDetail.purchaseAcceptedQty ?? 0} (${itemDetail.purchaseUomDesc ?? ""})`}
  375. />
  376. </Grid>
  377. <Grid item xs={6}>
  378. <TextField
  379. label={"已上架數量"}
  380. fullWidth
  381. sx={compactFields ? undefined : textfieldSx}
  382. disabled={true}
  383. value={`${itemDetail.putAwayLines?.reduce((sum, p) => sum + (p.stockQty ?? 0), 0) ?? 0} (${itemDetail.stockUomDesc ?? ""})`}
  384. />
  385. </Grid>
  386. </>
  387. ) : (
  388. <Grid item xs={6}>
  389. <TextField
  390. label={t("acceptedQty")}
  391. fullWidth
  392. sx={compactFields ? undefined : textfieldSx}
  393. disabled={true}
  394. {...register("acceptedQty", {
  395. required: "acceptedQty required!",
  396. })}
  397. />
  398. </Grid>
  399. )}
  400. {/* <Grid item xs={4}>
  401. <TextField
  402. label={t("acceptedWeight")}
  403. fullWidth
  404. // {...register("acceptedWeight", {
  405. // required: "acceptedWeight required!",
  406. // })}
  407. disabled={disabled}
  408. error={Boolean(errors.acceptedWeight)}
  409. helperText={errors.acceptedWeight?.message}
  410. />
  411. </Grid> */}
  412. </Grid>
  413. <Grid
  414. container
  415. justifyContent="flex-start"
  416. alignItems="flex-start"
  417. spacing={2}
  418. sx={{ mt: 0.5 }}
  419. >
  420. {/* <Grid item xs={12}>
  421. <InputDataGrid<PurchaseQCInput, PurchaseQcResult, EntryError>
  422. apiRef={apiRef}
  423. checkboxSelection={false}
  424. _formKey={"qcCheck"}
  425. columns={columns}
  426. validateRow={validationTest}
  427. />
  428. </Grid> */}
  429. </Grid>
  430. </Grid>
  431. <CalculateExpiryDateModal
  432. open={openModal}
  433. onClose={handleOnModalClose}
  434. onSubmit={handleReturnExpiryDate}
  435. textfieldSx={compactFields ? undefined : textfieldSx}
  436. />
  437. </>
  438. );
  439. };
  440. export default StockInForm;