FPSMS-frontend
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 

461 строка
13 KiB

  1. "use client";
  2. import { PurchaseQcResult, PurchaseQCInput } from "@/app/api/po/actions";
  3. import {
  4. Box,
  5. Card,
  6. CardContent,
  7. Checkbox,
  8. FormControl,
  9. FormControlLabel,
  10. Grid,
  11. Radio,
  12. RadioGroup,
  13. Stack,
  14. Tab,
  15. Tabs,
  16. TabsProps,
  17. TextField,
  18. Tooltip,
  19. Typography,
  20. } from "@mui/material";
  21. import { useFormContext, Controller } from "react-hook-form";
  22. import { useTranslation } from "react-i18next";
  23. import StyledDataGrid from "../StyledDataGrid";
  24. import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
  25. import {
  26. GridColDef,
  27. GridRowIdGetter,
  28. GridRowModel,
  29. useGridApiContext,
  30. GridRenderCellParams,
  31. GridRenderEditCellParams,
  32. useGridApiRef,
  33. GridRowSelectionModel,
  34. } from "@mui/x-data-grid";
  35. import InputDataGrid from "../InputDataGrid";
  36. import { TableRow } from "../InputDataGrid/InputDataGrid";
  37. import TwoLineCell from "./TwoLineCell";
  38. import QcSelect from "./QcSelect";
  39. import { GridEditInputCell } from "@mui/x-data-grid";
  40. import { StockInLine } from "@/app/api/po";
  41. import { stockInLineStatusMap } from "@/app/utils/formatUtil";
  42. import { fetchQcItemCheck, fetchQcResult } from "@/app/api/qc/actions";
  43. import { QcItemWithChecks } from "@/app/api/qc";
  44. import axios from "@/app/(main)/axios/axiosInstance";
  45. import { NEXT_PUBLIC_API_URL } from "@/config/api";
  46. import axiosInstance from "@/app/(main)/axios/axiosInstance";
  47. import EscalationComponent from "./EscalationComponent";
  48. import QcDataGrid from "./QCDatagrid";
  49. import StockInFormVer2 from "./StockInFormVer2";
  50. import { dummyEscalationHistory, dummyQCData, QcData } from "./dummyQcTemplate";
  51. import { ModalFormInput } from "@/app/api/po/actions";
  52. import { escape } from "lodash";
  53. interface Props {
  54. itemDetail: StockInLine;
  55. qc: QcItemWithChecks[];
  56. disabled: boolean;
  57. qcItems: QcData[]
  58. setQcItems: Dispatch<SetStateAction<QcData[]>>
  59. }
  60. type EntryError =
  61. | {
  62. [field in keyof QcData]?: string;
  63. }
  64. | undefined;
  65. type QcRow = TableRow<Partial<QcData>, EntryError>;
  66. // fetchQcItemCheck
  67. const QcFormVer2: React.FC<Props> = ({ qc, itemDetail, disabled, qcItems, setQcItems }) => {
  68. const { t } = useTranslation("purchaseOrder");
  69. const apiRef = useGridApiRef();
  70. const {
  71. register,
  72. formState: { errors, defaultValues, touchedFields },
  73. watch,
  74. control,
  75. setValue,
  76. getValues,
  77. reset,
  78. resetField,
  79. setError,
  80. clearErrors,
  81. } = useFormContext<PurchaseQCInput>();
  82. const [tabIndex, setTabIndex] = useState(0);
  83. const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>();
  84. const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory);
  85. const [qcResult, setQcResult] = useState();
  86. const qcAccept = watch("qcAccept");
  87. // const [qcAccept, setQcAccept] = useState(true);
  88. // const [qcItems, setQcItems] = useState(dummyQCData)
  89. const column = useMemo<GridColDef[]>(
  90. () => [
  91. {
  92. field: "escalation",
  93. headerName: t("escalation"),
  94. flex: 1,
  95. },
  96. {
  97. field: "supervisor",
  98. headerName: t("supervisor"),
  99. flex: 1,
  100. },
  101. ], []
  102. )
  103. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  104. (_e, newValue) => {
  105. setTabIndex(newValue);
  106. },
  107. [],
  108. );
  109. //// validate form
  110. const accQty = watch("acceptQty");
  111. const validateForm = useCallback(() => {
  112. console.log(accQty);
  113. if (accQty > itemDetail.acceptedQty) {
  114. setError("acceptQty", {
  115. message: `${t("acceptQty must not greater than")} ${
  116. itemDetail.acceptedQty
  117. }`,
  118. type: "required",
  119. });
  120. }
  121. if (accQty < 1) {
  122. setError("acceptQty", {
  123. message: t("minimal value is 1"),
  124. type: "required",
  125. });
  126. }
  127. if (isNaN(accQty)) {
  128. setError("acceptQty", {
  129. message: t("value must be a number"),
  130. type: "required",
  131. });
  132. }
  133. }, [accQty]);
  134. useEffect(() => {
  135. clearErrors();
  136. validateForm();
  137. }, [clearErrors, validateForm]);
  138. const columns = useMemo<GridColDef[]>(
  139. () => [
  140. {
  141. field: "escalation",
  142. headerName: t("escalation"),
  143. flex: 1,
  144. },
  145. {
  146. field: "supervisor",
  147. headerName: t("supervisor"),
  148. flex: 1,
  149. },
  150. ],
  151. [],
  152. );
  153. /// validate datagrid
  154. const validation = useCallback(
  155. (newRow: GridRowModel<QcRow>): EntryError => {
  156. const error: EntryError = {};
  157. // const { qcItemId, failQty } = newRow;
  158. return Object.keys(error).length > 0 ? error : undefined;
  159. },
  160. [],
  161. );
  162. function BooleanEditCell(params: GridRenderEditCellParams) {
  163. const apiRef = useGridApiContext();
  164. const { id, field, value } = params;
  165. const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
  166. apiRef.current.setEditCellValue({ id, field, value: e.target.checked });
  167. apiRef.current.stopCellEditMode({ id, field }); // commit immediately
  168. };
  169. return <Checkbox checked={!!value} onChange={handleChange} sx={{ p: 0 }} />;
  170. }
  171. const qcColumns: GridColDef[] = [
  172. {
  173. field: "qcItem",
  174. headerName: t("qcItem"),
  175. flex: 2,
  176. renderCell: (params) => (
  177. <Box>
  178. <b>{params.value}</b><br/>
  179. {params.row.qcDescription}<br/>
  180. </Box>
  181. ),
  182. },
  183. {
  184. field: 'isPassed',
  185. headerName: t("qcResult"),
  186. flex: 1.5,
  187. renderCell: (params) => {
  188. const currentValue = params.value;
  189. return (
  190. <FormControl>
  191. <RadioGroup
  192. row
  193. aria-labelledby="demo-radio-buttons-group-label"
  194. value={currentValue === undefined ? "" : (currentValue ? "true" : "false")}
  195. onChange={(e) => {
  196. const value = e.target.value;
  197. setQcItems((prev) =>
  198. prev.map((r): QcData => (r.id === params.id ? { ...r, isPassed: value === "true" } : r))
  199. );
  200. }}
  201. name={`isPassed-${params.id}`}
  202. >
  203. <FormControlLabel
  204. value="true"
  205. control={<Radio />}
  206. label="合格"
  207. sx={{
  208. color: currentValue === true ? "green" : "inherit",
  209. "& .Mui-checked": {color: "green"}
  210. }}
  211. />
  212. <FormControlLabel
  213. value="false"
  214. control={<Radio />}
  215. label="不合格"
  216. sx={{
  217. color: currentValue === false ? "red" : "inherit",
  218. "& .Mui-checked": {color: "red"}
  219. }}
  220. />
  221. </RadioGroup>
  222. </FormControl>
  223. );
  224. },
  225. },
  226. {
  227. field: "failedQty",
  228. headerName: t("failedQty"),
  229. flex: 1,
  230. // editable: true,
  231. renderCell: (params) => (
  232. <TextField
  233. type="number"
  234. size="small"
  235. value={!params.row.isPassed? (params.value ?? '') : '0'}
  236. disabled={params.row.isPassed}
  237. onChange={(e) => {
  238. const v = e.target.value;
  239. const next = v === '' ? undefined : Number(v);
  240. if (Number.isNaN(next)) return;
  241. setQcItems((prev) =>
  242. prev.map((r) => (r.id === params.id ? { ...r, failedQty: next } : r))
  243. );
  244. }}
  245. onClick={(e) => e.stopPropagation()}
  246. onMouseDown={(e) => e.stopPropagation()}
  247. onKeyDown={(e) => e.stopPropagation()}
  248. inputProps={{ min: 0 }}
  249. sx={{ width: '100%' }}
  250. />
  251. ),
  252. },
  253. {
  254. field: "remarks",
  255. headerName: t("remarks"),
  256. flex: 2,
  257. renderCell: (params) => (
  258. <TextField
  259. size="small"
  260. value={params.value ?? ''}
  261. onChange={(e) => {
  262. const remarks = e.target.value;
  263. // const next = v === '' ? undefined : Number(v);
  264. // if (Number.isNaN(next)) return;
  265. setQcItems((prev) =>
  266. prev.map((r) => (r.id === params.id ? { ...r, remarks: remarks } : r))
  267. );
  268. }}
  269. onClick={(e) => e.stopPropagation()}
  270. onMouseDown={(e) => e.stopPropagation()}
  271. onKeyDown={(e) => e.stopPropagation()}
  272. inputProps={{ min: 0 }}
  273. sx={{ width: '100%' }}
  274. />
  275. ),
  276. },
  277. ]
  278. useEffect(() => {
  279. console.log(itemDetail);
  280. }, [itemDetail]);
  281. // Set initial value for acceptQty
  282. useEffect(() => {
  283. if (itemDetail?.acceptedQty !== undefined) {
  284. setValue("acceptQty", itemDetail.acceptedQty);
  285. }
  286. }, [itemDetail?.acceptedQty, setValue]);
  287. // const [openCollapse, setOpenCollapse] = useState(false)
  288. const [isCollapsed, setIsCollapsed] = useState<boolean>(false);
  289. const onFailedOpenCollapse = useCallback((qcItems: QcData[]) => {
  290. const isFailed = qcItems.some((qc) => !qc.isPassed)
  291. console.log(isFailed)
  292. if (isFailed) {
  293. setIsCollapsed(true)
  294. } else {
  295. setIsCollapsed(false)
  296. }
  297. }, [])
  298. // const handleRadioChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
  299. // const value = event.target.value === 'true';
  300. // setValue("qcAccept", value);
  301. // }, [setValue]);
  302. useEffect(() => {
  303. console.log(itemDetail);
  304. }, [itemDetail]);
  305. useEffect(() => {
  306. // onFailedOpenCollapse(qcItems) // This function is no longer needed
  307. }, [qcItems]); // Removed onFailedOpenCollapse from dependency array
  308. return (
  309. <>
  310. <Grid container justifyContent="flex-start" alignItems="flex-start">
  311. <Grid
  312. container
  313. justifyContent="flex-start"
  314. alignItems="flex-start"
  315. spacing={2}
  316. sx={{ mt: 0.5 }}
  317. >
  318. <Grid item xs={12}>
  319. <Tabs
  320. value={tabIndex}
  321. onChange={handleTabChange}
  322. variant="scrollable"
  323. >
  324. <Tab label={t("QC Info")} iconPosition="end" />
  325. <Tab label={t("Escalation History")} iconPosition="end" />
  326. </Tabs>
  327. </Grid>
  328. {tabIndex == 0 && (
  329. <>
  330. <Grid item xs={12}>
  331. {/* <QcDataGrid<ModalFormInput, QcData, EntryError>
  332. apiRef={apiRef}
  333. columns={qcColumns}
  334. _formKey="qcResult"
  335. validateRow={validation}
  336. /> */}
  337. <StyledDataGrid
  338. columns={qcColumns}
  339. rows={qcItems}
  340. autoHeight
  341. />
  342. </Grid>
  343. {/* <Grid item xs={12}>
  344. <EscalationComponent
  345. forSupervisor={false}
  346. isCollapsed={isCollapsed}
  347. setIsCollapsed={setIsCollapsed}
  348. />
  349. </Grid> */}
  350. </>
  351. )}
  352. {tabIndex == 1 && (
  353. <>
  354. {/* <Grid item xs={12}>
  355. <StockInFormVer2
  356. itemDetail={itemDetail}
  357. disabled={false}
  358. />
  359. </Grid> */}
  360. <Grid item xs={12}>
  361. <Typography variant="h6" display="block" marginBlockEnd={1}>
  362. {t("Escalation Info")}
  363. </Typography>
  364. </Grid>
  365. <Grid item xs={12}>
  366. <StyledDataGrid
  367. rows={escalationHistory}
  368. columns={columns}
  369. onRowSelectionModelChange={(newRowSelectionModel) => {
  370. setRowSelectionModel(newRowSelectionModel);
  371. }}
  372. />
  373. </Grid>
  374. </>
  375. )}
  376. <Grid item xs={12}>
  377. <FormControl>
  378. <Controller
  379. name="qcAccept"
  380. control={control}
  381. defaultValue={true}
  382. render={({ field }) => (
  383. <RadioGroup
  384. row
  385. aria-labelledby="demo-radio-buttons-group-label"
  386. {...field}
  387. value={field.value?.toString() || "true"}
  388. onChange={(e) => {
  389. const value = e.target.value === 'true';
  390. if (!value && Boolean(errors.acceptQty)) {
  391. setValue("acceptQty", itemDetail.acceptedQty);
  392. }
  393. field.onChange(value);
  394. }}
  395. >
  396. <FormControlLabel value="true" control={<Radio />} label="接受" />
  397. <Box sx={{mr:2}}>
  398. <TextField
  399. type="number"
  400. label={t("acceptQty")}
  401. sx={{ width: '150px' }}
  402. defaultValue={accQty}
  403. disabled={!qcAccept}
  404. {...register("acceptQty", {
  405. required: "acceptQty required!",
  406. })}
  407. error={Boolean(errors.acceptQty)}
  408. helperText={errors.acceptQty?.message}
  409. />
  410. </Box>
  411. <FormControlLabel value="false" control={<Radio />} label="不接受及上報" />
  412. </RadioGroup>
  413. )}
  414. />
  415. </FormControl>
  416. </Grid>
  417. {/* <Grid item xs={12}>
  418. <Typography variant="h6" display="block" marginBlockEnd={1}>
  419. {t("Escalation Result")}
  420. </Typography>
  421. </Grid>
  422. <Grid item xs={12}>
  423. <EscalationComponent
  424. forSupervisor={true}
  425. isCollapsed={isCollapsed}
  426. setIsCollapsed={setIsCollapsed}
  427. />
  428. </Grid> */}
  429. </Grid>
  430. </Grid>
  431. </>
  432. );
  433. };
  434. export default QcFormVer2;