FPSMS-frontend
選択できるのは25トピックまでです。 トピックは、先頭が英数字で、英数字とダッシュ('-')を使用した35文字以内のものにしてください。

QcComponent.tsx 24 KiB

4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
1ヶ月前
1ヶ月前
4ヶ月前
1ヶ月前
4ヶ月前
4ヶ月前
4ヶ月前
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644
  1. "use client";
  2. import {
  3. Box, Card, CardContent, Checkbox, Collapse, FormControl,
  4. FormControlLabel, Grid, Radio, RadioGroup, Stack, Tab,
  5. Tabs, TabsProps, TextField, Tooltip, Typography,
  6. } from "@mui/material";
  7. import { useFormContext, Controller, FieldPath } from "react-hook-form";
  8. import { useTranslation } from "react-i18next";
  9. import StyledDataGrid from "../StyledDataGrid";
  10. import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react";
  11. import {
  12. GridColDef,
  13. GridRowIdGetter,
  14. GridRowModel,
  15. useGridApiContext,
  16. GridRenderCellParams,
  17. GridRenderEditCellParams,
  18. useGridApiRef,
  19. GridRowSelectionModel,
  20. } from "@mui/x-data-grid";
  21. import InputDataGrid from "../InputDataGrid";
  22. import { TableRow } from "../InputDataGrid/InputDataGrid";
  23. import TwoLineCell from "../PoDetail/TwoLineCell";
  24. import QcSelect from "../PoDetail/QcSelect";
  25. import { GridEditInputCell } from "@mui/x-data-grid";
  26. import { ModalFormInput, StockInLine } from "@/app/api/stockIn";
  27. import { fetchQcCategory, fetchQcResult } from "@/app/api/qc/actions";
  28. import { QcCategory, QcData, QcInput, QcFormInput, QcResult } from "@/app/api/qc";
  29. import axios from "@/app/(main)/axios/axiosInstance";
  30. import { NEXT_PUBLIC_API_URL } from "@/config/api";
  31. import axiosInstance from "@/app/(main)/axios/axiosInstance";
  32. import EscalationComponent from "../PoDetail/EscalationComponent";
  33. import QcDataGrid from "../PoDetail/QCDatagrid";
  34. import { dummyEscalationHistory,
  35. dummyQcData_A1, dummyQcData_E1, dummyQcData_E2,
  36. dummyQcHeader_A1, dummyQcHeader_E1, dummyQcHeader_E2 } from "./dummyQcTemplate";
  37. import { escape, isNull, min, template } from "lodash";
  38. import { PanoramaSharp } from "@mui/icons-material";
  39. import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable";
  40. import { EscalationResult } from "@/app/api/escalation";
  41. import { EscalationCombo } from "@/app/api/user";
  42. import { fetchEscalationLogsByStockInLines } from "@/app/api/escalation/actions";
  43. import CollapsibleCard from "../CollapsibleCard/CollapsibleCard";
  44. import LoadingComponent from "../General/LoadingComponent";
  45. import QcForm from "./QcForm";
  46. interface Props {
  47. itemDetail: QcInput;
  48. // itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] };
  49. // qc: QcItemWithChecks[];
  50. disabled: boolean;
  51. // qcItems: QcData[]
  52. // setQcItems: Dispatch<SetStateAction<QcData[]>>
  53. }
  54. type EntryError =
  55. | {
  56. [field in keyof QcData]?: string;
  57. }
  58. | undefined;
  59. type QcRow = TableRow<Partial<QcResult>, EntryError>;
  60. // fetchQcItemCheck
  61. const QcComponent: React.FC<Props> = ({ itemDetail, disabled = false }) => {
  62. const { t } = useTranslation("purchaseOrder");
  63. const apiRef = useGridApiRef();
  64. const {
  65. register,
  66. formState: { errors, defaultValues, touchedFields },
  67. watch,
  68. control,
  69. setValue,
  70. getValues,
  71. reset,
  72. resetField,
  73. setError,
  74. clearErrors,
  75. } = useFormContext<QcFormInput>();
  76. const [tabIndex, setTabIndex] = useState(0);
  77. const [rowSelectionModel, setRowSelectionModel] = useState<GridRowSelectionModel>();
  78. const [escalationHistory, setEscalationHistory] = useState(dummyEscalationHistory);
  79. const qcAccept = watch("qcAccept");
  80. const qcDecision = watch("qcDecision"); //WIP
  81. // const qcResult = useMemo(() => [...watch("qcResult")], [watch("qcResult")]);
  82. const [qcCategory, setQcCategory] = useState<QcCategory>();
  83. const qcRecord = useMemo(() => { // Need testing
  84. const value = watch('qcResult'); //console.log("%c QC update!", "color:green", value);
  85. return Array.isArray(value) ? [...value] : [];
  86. }, [watch('qcResult')]);
  87. const [qcHistory, setQcHistory] = useState<QcResult[]>([]);
  88. const [qcResult, setQcResult] = useState<QcResult[]>([]);
  89. const [escResult, setEscResult] = useState<EscalationResult[]>([]);
  90. // const [qcAccept, setQcAccept] = useState(true);
  91. // const [qcItems, setQcItems] = useState(dummyQCData)
  92. const qcDisabled = (row : QcResult) => {
  93. return disabled || isExist(row.escalationLogId);
  94. };
  95. const isExist = (data : string | number | undefined) => {
  96. return (data !== null && data !== undefined);
  97. }
  98. const handleTabChange = useCallback<NonNullable<TabsProps["onChange"]>>(
  99. (_e, newValue) => {
  100. setTabIndex(newValue);
  101. },
  102. [],
  103. );
  104. const qcType = useMemo(() => {
  105. if (itemDetail) {
  106. const d = itemDetail;
  107. if (isExist(d.jobOrderId)) {
  108. return "EPQC";
  109. }
  110. }
  111. return "IQC"; // Default
  112. }, [itemDetail]);
  113. const detailMode = useMemo(() => {
  114. const isDetailMode = itemDetail.status == "escalated" || isExist(itemDetail.jobOrderId);
  115. return isDetailMode;
  116. }, [itemDetail]);
  117. // W I P //
  118. const validateFieldFail = (field : FieldPath<QcFormInput>, condition: boolean, message: string) : boolean => {
  119. // console.log("Checking if " + message)
  120. if (condition) { setError(field, { message: message}); return false; }
  121. else { clearErrors(field); return true; }
  122. }
  123. //// validate form
  124. const accQty = watch("acceptQty");
  125. const validateForm = useCallback(() => {
  126. if (qcDecision == 1) {
  127. if (isNaN(accQty) || accQty === undefined || accQty === null || typeof(accQty) != "number") {
  128. setError("acceptQty", { message: t("value must be a number") });
  129. } else
  130. if (!Number.isInteger(accQty)) {
  131. setError("acceptQty", { message: t("value must be integer") });
  132. }
  133. if (accQty > itemDetail.acceptedQty) {
  134. setError("acceptQty", { message: `${t("acceptQty must not greater than")} ${
  135. itemDetail.acceptedQty}` });
  136. } else
  137. if (accQty < 1) {
  138. setError("acceptQty", { message: t("minimal value is 1") });
  139. } else
  140. console.log("%c Validated accQty:", "color:yellow", accQty);
  141. }
  142. },[setError, qcDecision, accQty, itemDetail])
  143. useEffect(() => { // W I P // -----
  144. if (qcDecision == 1) {
  145. if (validateFieldFail("acceptQty", accQty > itemDetail.acceptedQty, `${t("acceptQty must not greater than")} ${
  146. itemDetail.acceptedQty}`)) return;
  147. if (validateFieldFail("acceptQty", accQty < 1, t("minimal value is 1"))) return;
  148. if (validateFieldFail("acceptQty", isNaN(accQty), t("value must be a number"))) return;
  149. }
  150. const qcResultItems = qcResult; //console.log("Validating:", qcResultItems);
  151. // Check if failed items have failed quantity
  152. const failedItemsWithoutQty = qcResultItems.filter(item =>
  153. item.qcPassed === false && (!item.failQty || item.failQty <= 0)
  154. );
  155. if (validateFieldFail("qcResult", failedItemsWithoutQty.length > 0, `${t("Failed items must have failed quantity")}`)) return;
  156. // Check if all QC items have results
  157. const itemsWithoutResult = qcResultItems.filter(item => item.qcPassed === undefined);
  158. if (validateFieldFail("qcDecision", (itemsWithoutResult.length > 0 && itemDetail.status != "escalated"),
  159. `${t("QC items without result")}`)) return;
  160. if (validateFieldFail("qcDecision", (!qcResultItems.every((qc) => qc.qcPassed) && qcDecision == 1 && itemDetail.status != "escalated"),
  161. "有不合格檢查項目,無法收貨!")) return; // TODO: Fix it please
  162. // submitDialogWithWarning(() => postStockInLineWithQc(qcData), t, {title:"有不合格檢查項目,確認接受收貨?",
  163. // confirmButtonText: t("confirm putaway"), html: ""});
  164. // return;
  165. // console.log("Validated without errors");
  166. }, [accQty, qcDecision, watch("qcResult")]);
  167. useEffect(() => {
  168. clearErrors();
  169. validateForm();
  170. }, [clearErrors, validateForm]);
  171. /// validate datagrid
  172. const validation = useCallback(
  173. (newRow: GridRowModel<QcRow>): EntryError => {
  174. const error: EntryError = {};
  175. // const { qcItemId, failQty } = newRow;
  176. return Object.keys(error).length > 0 ? error : undefined;
  177. },
  178. [],
  179. );
  180. // Set initial value for acceptQty
  181. useEffect(() => {
  182. if (itemDetail?.demandQty > 0) { //!== undefined) {
  183. setValue("acceptQty", itemDetail.demandQty); // TODO: THIS NEED TO UPDATE TO NOT USE DEMAND QTY
  184. } else {
  185. setValue("acceptQty", itemDetail?.acceptedQty);
  186. }
  187. }, [itemDetail?.demandQty, itemDetail?.acceptedQty, setValue]);
  188. // Fetch Qc Data
  189. useEffect(() => {
  190. // console.log("%c QC ItemDetail updated:", "color: gold", itemDetail);
  191. if (itemDetail) {
  192. const d = itemDetail;
  193. fetchNewQcData(d);
  194. if (d.status == "pending") {
  195. //
  196. } else {
  197. fetchQcResultData(d);
  198. }
  199. }
  200. }, [itemDetail]);
  201. const fetchNewQcData = useCallback(
  202. async (input: QcInput) => {
  203. try {
  204. const res = await fetchQcCategory(input.itemId, qcType);
  205. if (res.qcItems.length > 0) {
  206. console.log("%c Fetched Qc Template: ", "color:orange", res);
  207. setQcCategory(res);
  208. // setQcResult(res.qcItems);
  209. // setValue("qcResult", res.qcItems);
  210. } else throw("Result is undefined");
  211. } catch (e) {
  212. console.log("%c Error when fetching Qc Template: ", "color:red", e);
  213. alert(t("Missing QC Template, please contact administrator"));
  214. // closeHandler({}, "backdropClick");
  215. }
  216. },[fetchQcCategory, setValue]
  217. );
  218. const fetchQcResultData = useCallback(
  219. async (input: QcInput) => {
  220. try {
  221. const res = await fetchQcResult(input.id); // StockInLineId for now
  222. if (res.length > 0) {
  223. console.log("%c Fetched Qc Result: ", "color:orange", res);
  224. setValue("qcResult", res);
  225. fetchEscalationLogData(input.id);
  226. // } else {setStockInLineInfo((prev) => ({...prev, qcResult: []} as StockInLine));}
  227. } else throw("Result is undefined");
  228. } catch (e) {
  229. console.log("%c Error when fetching Qc Result: ", "color:red", e);
  230. // alert("Something went wrong, please retry");
  231. // closeHandler({}, "backdropClick");
  232. }
  233. },[fetchQcResult, setValue]
  234. );
  235. const fetchEscalationLogData = useCallback(
  236. async (stockInLineId: number) => {
  237. try {
  238. const res = await fetchEscalationLogsByStockInLines([stockInLineId]);
  239. if (res.length > 0) {
  240. console.log("%c Fetched Escalation Log: ", "color:orange", res[0]);
  241. setEscResult(res);
  242. // formProps.setValue("escalationLog", res[0]);
  243. }// else throw("Result is undefined");
  244. } catch (e) {
  245. console.log("%c Error when fetching EscalationLog: ", "color:red", e);
  246. // alert("Something went wrong, please retry");
  247. // closeHandler({}, "backdropClick");
  248. }
  249. },[fetchEscalationLogsByStockInLines]
  250. );
  251. // Set QC Data
  252. useEffect(() => {
  253. if (itemDetail) {
  254. const d = itemDetail;
  255. if (qcRecord.length < 1) { // No QC Data
  256. if (d.status == "pending") { // New QC
  257. if (qcCategory) {
  258. if (qcCategory.qcItems.length > 0) {
  259. const filledQcItems = fillQcResult(qcCategory.qcItems);
  260. setValue("qcResult", filledQcItems);
  261. console.log("%c New QC Record applied:", "color:green", filledQcItems);
  262. }
  263. }
  264. } else {
  265. console.log("%c No QC Record loaded:", "color:green");
  266. //
  267. }
  268. } else { // QC Result fetched
  269. if (qcRecord.some(qc => qc.order !== undefined)) { // If QC Result is filled with order
  270. if (d.status == "escalated") { // Copy the previous QC data for editing
  271. // If no editable Qc Data
  272. if (!qcRecord.some((qc) => !isExist(qc.escalationLogId))) {
  273. const mutableQcData = qcRecord.map(qc => ({ ...qc, escalationLogId: undefined }));
  274. const copiedQcData = [...mutableQcData, ...qcRecord];
  275. setValue("qcResult", copiedQcData);
  276. console.log("%c QC Record copied:", "color:green", copiedQcData);
  277. return;
  278. }
  279. }
  280. // Set QC Result
  281. // const filteredQcResult = qcRecord;
  282. const filteredQcResult = qcRecord.filter((qc) => !isExist(qc.escalationLogId));
  283. console.log("%c QC Result loaded:", "color:green", filteredQcResult);
  284. setQcResult(filteredQcResult);
  285. // Set QC History
  286. if (filteredQcResult.length < qcRecord.length) { // If there are Qc History
  287. if (qcHistory.length < 1) {
  288. const filteredQcHistory = qcRecord.filter((qc) => isExist(qc.escalationLogId));
  289. console.log("%c QC History loaded:", "color:green", filteredQcHistory);
  290. setQcHistory(filteredQcHistory);
  291. }
  292. }
  293. } else {
  294. if (qcCategory) {
  295. const filledQcData = fillQcResult(qcRecord, qcCategory?.qcItems);
  296. console.log("%c QC Result filled:", "color:green", filledQcData);
  297. setValue("qcResult", filledQcData);
  298. }
  299. }
  300. }
  301. }
  302. }, [qcRecord, qcCategory, setValue, itemDetail])
  303. const fillQcResult = (qcResults: QcResult[], qcItems: QcData[] = []) => {
  304. let result = [] as QcResult[];
  305. qcResults.forEach((r, index) => {
  306. const target = qcItems.find((t) => t.qcItemId === r.qcItemId);
  307. const n = { ...target, ...r }; //, id: index };
  308. result.push(n);
  309. });
  310. result.sort((a,b) => a.order! - b.order!);
  311. return result;
  312. };
  313. // const [openCollapse, setOpenCollapse] = useState(false)
  314. const [isCollapsed, setIsCollapsed] = useState<boolean>(true);
  315. const onFailedOpenCollapse = useCallback((qcItems: QcResult[]) => {
  316. const isFailed = qcItems.some((qc) => !qc.qcPassed)
  317. // console.log(isFailed)
  318. if (isFailed) {
  319. setIsCollapsed(true)
  320. } else {
  321. setIsCollapsed(false)
  322. }
  323. }, [])
  324. // const handleRadioChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
  325. // const value = event.target.value === 'true';
  326. // setValue("qcAccept", value);
  327. // }, [setValue]);
  328. const setDefaultQcDecision = (status : string | undefined) => {
  329. const param = status?.toLowerCase();
  330. if (param !== undefined && param !== null) {
  331. if (param == "received" || param == "completed" || param == "partially_completed") {
  332. return 1;
  333. } else if (param == "rejected") {
  334. return 2;
  335. } else if (param == "escalated") {
  336. return 1; // For new flow
  337. // return 3;
  338. } else { return undefined; }
  339. } else {
  340. return undefined;
  341. }
  342. }
  343. // }, [watch("qcResult")]);
  344. // useEffect(() => {
  345. // // onFailedOpenCollapse(qcItems)
  346. // }, [qcItems]);
  347. const getRowId = (row :any) => {
  348. return qcRecord.findIndex(qc => qc == row);
  349. // return row.id || `${row.name}-${Math.random().toString(36).substr(2, 9)}`;
  350. };
  351. const getRowHeight = (row :any) => { // Not used?
  352. console.log("row", row);
  353. if (!row.model.name) {
  354. return (row.model.name.length ?? 10) * 1.2 + 30;
  355. } else { return 60}
  356. };
  357. const formattedDesc = (content: string = "") => {
  358. return (
  359. <>
  360. {content.split("\\n").map((line, index) => (
  361. <span key={index}> {line} <br/></span>
  362. ))}
  363. </>
  364. );
  365. }
  366. const QcHeader = useMemo(() => () => {
  367. if (qcCategory === undefined || qcCategory === null) {
  368. return (
  369. <Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: '#333' }}>
  370. N/A
  371. </Typography>
  372. );
  373. } else
  374. return (
  375. <>
  376. <Box sx={{ mb: 2, p: 2, backgroundColor: '#f5f5f5', borderRadius: 1 }}>
  377. <Typography variant="h5" component="h2" sx={{ fontWeight: 'bold', color: '#333' }}>
  378. {qcCategory?.name} ({qcCategory?.code})
  379. </Typography>
  380. <Typography variant="subtitle1" sx={{ color: '#666' }}>
  381. <b>品檢類型</b>:{qcType}
  382. </Typography>
  383. <Typography variant="subtitle2" sx={{ color: '#666' }}>
  384. {formattedDesc(qcCategory?.description)}
  385. </Typography>
  386. </Box>
  387. </>
  388. );
  389. }, [qcType, qcCategory]);
  390. return (
  391. <>
  392. <Grid container justifyContent="flex-start" alignItems="flex-start">
  393. {(qcRecord.length > 0) ? (
  394. // {(qcRecord.length > 0 && qcCategory) ? (
  395. <Grid
  396. container
  397. justifyContent="flex-start"
  398. alignItems="flex-start"
  399. spacing={2}
  400. sx={{ mt: 0.5 }}
  401. >
  402. <Grid item xs={12}>
  403. <Tabs
  404. value={tabIndex}
  405. onChange={handleTabChange}
  406. variant="scrollable"
  407. >
  408. <Tab label={t("QC Info")} iconPosition="end" />
  409. {(escResult && escResult?.length > 0) &&
  410. (<Tab label={t("Escalation History")} iconPosition="end" />)}
  411. </Tabs>
  412. </Grid>
  413. {tabIndex == 0 && (
  414. <>
  415. <Grid item xs={12}>
  416. <QcHeader/>
  417. {/* <QcDataGrid<ModalFormInput, QcData, EntryError>
  418. apiRef={apiRef}
  419. columns={qcColumns}
  420. _formKey="qcResult"
  421. validateRow={validation}
  422. /> */}
  423. <QcForm
  424. rows={qcResult}
  425. disabled={disabled}
  426. />
  427. </Grid>
  428. </>
  429. )}
  430. {tabIndex == 1 && (
  431. <>
  432. {/* <Grid item xs={12}>
  433. <StockInFormVer2
  434. itemDetail={itemDetail}
  435. disabled={false}
  436. />
  437. </Grid> */}
  438. {/* <Grid item xs={12}>
  439. <Typography variant="h6" display="block" marginBlockEnd={1}>
  440. {t("Escalation Info")}
  441. </Typography>
  442. </Grid> */}
  443. <Grid item xs={12}>
  444. <EscalationLogTable type="qc" items={escResult || []}/>
  445. <CollapsibleCard title={t("QC Record")}>
  446. <QcHeader/>
  447. <QcForm
  448. disabled={disabled}
  449. rows={qcHistory}
  450. />
  451. </CollapsibleCard>
  452. </Grid>
  453. </>
  454. )}
  455. <Grid item xs={12}>
  456. <Card sx={{p:2}}>
  457. <Typography variant="h6" display="block" marginBlockEnd={1}>
  458. {t("Qc Decision")}
  459. </Typography>
  460. <FormControl>
  461. <Controller
  462. name="qcDecision"
  463. // name="qcAccept"
  464. control={control}
  465. defaultValue={setDefaultQcDecision(itemDetail?.status)}
  466. // defaultValue={true}
  467. render={({ field }) => (
  468. <>
  469. {/* <Typography sx={{color:"red"}}>
  470. {errors.qcDecision?.message}
  471. </Typography> */}
  472. <RadioGroup
  473. row
  474. aria-labelledby="demo-radio-buttons-group-label"
  475. {...field}
  476. value={field.value}
  477. // value={field.value?.toString() || "true"}
  478. onChange={(e) => {
  479. const value = e.target.value.toString();// === 'true';
  480. const input = document.getElementById('accQty') as HTMLInputElement; //TODO improve
  481. // console.log("%c AccQty Error", "color:red", errors.acceptQty);
  482. if (input) { // Selected Reject in new flow with Error
  483. if (value == "1") { // Selected Accept
  484. input.value = Number(accQty).toString();
  485. } else {
  486. if (Boolean(errors.acceptQty)) {
  487. setValue("acceptQty", 0);
  488. }
  489. input.value = '0';
  490. }
  491. }
  492. // setValue("acceptQty", itemDetail.acceptedQty ?? 0);
  493. // clearErrors("acceptQty");
  494. // }
  495. field.onChange(value);
  496. }}
  497. >
  498. <FormControlLabel disabled={disabled}
  499. value="1" control={<Radio />} label="接受來貨" />
  500. {(detailMode || (disabled && accQty != itemDetail.acceptedQty && qcDecision == 1)) && ( //TODO Improve
  501. <Box sx={{mr:2}}>
  502. <TextField
  503. // type="number"
  504. id="accQty"
  505. label={t("acceptQty")}
  506. sx={{ width: '150px' }}
  507. // value={Number(accQty)}
  508. defaultValue={Number(accQty)}
  509. // defaultValue={(qcDecision == 1)? Number(accQty) : 0}
  510. // value={(qcDecision == 1)? Number(accQty) : undefined }
  511. // value={qcAccept? accQty : 0 }
  512. disabled={qcDecision != 1 || disabled}
  513. // disabled={!qcAccept || disabled}
  514. onBlur={(e) => {
  515. const value = e.target.value;
  516. const input = document.getElementById('accQty') as HTMLInputElement;
  517. input.value = Number(value).toString()
  518. setValue(`acceptQty`, Number(value));
  519. }}
  520. onInput={(e: React.ChangeEvent<HTMLInputElement>) => {
  521. const input = e.target.value;
  522. const numReg = /^[0-9]+$/
  523. let r = '';
  524. if (!numReg.test(input)) {
  525. const result = input.replace(/\D/g, "");
  526. r = (result === '' ? result : Number(result)).toString();
  527. } else {
  528. r = Number(input).toString()
  529. }
  530. e.target.value = r;
  531. }}
  532. inputProps={{ min: 1, max:itemDetail.acceptedQty }}
  533. // onChange={(e) => {
  534. // const inputValue = e.target.value;
  535. // if (inputValue === '' || /^[0-9]*$/.test(inputValue)) {
  536. // setValue("acceptQty", Number(inputValue === '' ? null : parseInt(inputValue, 10)));
  537. // }
  538. // }}
  539. // {...register("acceptQty", {
  540. // required: "acceptQty required!",
  541. // })}
  542. error={Boolean(errors.acceptQty)}
  543. helperText={errors.acceptQty?.message}
  544. />
  545. <TextField
  546. type="number"
  547. label={t("rejectQty")}
  548. sx={{ width: '150px' }}
  549. value={
  550. (!Boolean(errors.acceptQty) && qcDecision !== undefined) ?
  551. (qcDecision == 1 ? itemDetail.acceptedQty - accQty : itemDetail.acceptedQty)
  552. : ""
  553. }
  554. error={Boolean(errors.acceptQty)}
  555. disabled={true}
  556. />
  557. </Box>)}
  558. <FormControlLabel disabled={disabled}
  559. value="2" control={<Radio />}
  560. sx={{"& .Mui-checked": {color: "red"}}}
  561. label= {detailMode ? "全部拒絕並退貨" : "不接受並需要退貨"} />
  562. {(itemDetail.status == "pending" || disabled) && (<>
  563. <FormControlLabel disabled={disabled}
  564. value="3" control={<Radio />}
  565. sx={{"& .Mui-checked": {color: "blue"}}}
  566. label="暫時存放到置物區,並等待品檢結果" />
  567. </>)}
  568. </RadioGroup>
  569. </>
  570. )}
  571. />
  572. </FormControl>
  573. </Card>
  574. </Grid>
  575. {qcDecision == 3 && (
  576. // {!qcAccept && (
  577. <Grid item xs={12}>
  578. <EscalationComponent
  579. forSupervisor={false}
  580. isCollapsed={isCollapsed}
  581. setIsCollapsed={setIsCollapsed}
  582. />
  583. </Grid>)}
  584. </Grid>
  585. ) : <LoadingComponent/>}
  586. </Grid>
  587. </>
  588. );
  589. };
  590. export default QcComponent;