diff --git a/src/components/CollapsibleCard/CollapsibleCard.tsx b/src/components/CollapsibleCard/CollapsibleCard.tsx new file mode 100644 index 0000000..c98dd8c --- /dev/null +++ b/src/components/CollapsibleCard/CollapsibleCard.tsx @@ -0,0 +1,66 @@ +import React, { useState } from "react"; +import { + Card, + CardHeader, + CardContent, + IconButton, + Collapse, + Checkbox, + Box, +} from "@mui/material"; +import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; +import ExpandLessIcon from "@mui/icons-material/ExpandLess"; + +interface CollapsibleCardProps { + title: string; + children: React.ReactNode; + defaultOpen?: boolean; + showFilter?: boolean; + filterText?: string; +} + +const CollapsibleCard: React.FC = ({ + title, + children, + defaultOpen = true, + showFilter = false, + filterText = "Optional Filter", +}) => { + + const [filter, setFilter] = useState(false); + + const onFilterChange = (bool : boolean) => { + setFilter(bool); + setOpen(true); + } + const [open, setOpen] = useState(defaultOpen); + + return ( + + + {showFilter && ( + <> + {onFilterChange(e.target.checked);}} + // disabled={!isEmpty(pickOrder.consoCode)} + /> {filterText} + )} + setOpen((v) => !v)}> + {open ? : } + + + } + /> + + {children} + + + ); +}; + +export default CollapsibleCard; diff --git a/src/components/CollapsibleCard/index.ts b/src/components/CollapsibleCard/index.ts new file mode 100644 index 0000000..6f7795f --- /dev/null +++ b/src/components/CollapsibleCard/index.ts @@ -0,0 +1 @@ +export { default } from "./CollapsibleCard"; diff --git a/src/components/DashboardPage/CollapsibleCard.tsx b/src/components/DashboardPage/CollapsibleCard.tsx deleted file mode 100644 index b3cda21..0000000 --- a/src/components/DashboardPage/CollapsibleCard.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import React, { useState } from "react"; -import { - Card, - CardHeader, - CardContent, - IconButton, - Collapse, -} from "@mui/material"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import ExpandLessIcon from "@mui/icons-material/ExpandLess"; - -interface CollapsibleCardProps { - title: string; - children: React.ReactNode; - defaultOpen?: boolean; -} - -const CollapsibleCard: React.FC = ({ - title, - children, - defaultOpen = true, -}) => { - const [open, setOpen] = useState(defaultOpen); - - return ( - - setOpen((v) => !v)}> - {open ? : } - - } - /> - - {children} - - - ); -}; - -export default CollapsibleCard; diff --git a/src/components/DashboardPage/DashboardPage.tsx b/src/components/DashboardPage/DashboardPage.tsx index b8c9b0e..ed96321 100644 --- a/src/components/DashboardPage/DashboardPage.tsx +++ b/src/components/DashboardPage/DashboardPage.tsx @@ -13,7 +13,7 @@ import PendingStorageChart from "./chart/PendingStorageChart"; import ApplicationCompletionChart from "./chart/ApplicationCompletionChart"; import OrderCompletionChart from "./chart/OrderCompletionChart"; import DashboardBox from "./Dashboardbox"; -import CollapsibleCard from "./CollapsibleCard"; +import CollapsibleCard from "../CollapsibleCard"; // import SupervisorQcApproval, { IQCItems } from "./QC/SupervisorQcApproval"; import { EscalationResult } from "@/app/api/escalation"; import EscalationLogTable from "./escalation/EscalationLogTable"; @@ -28,6 +28,12 @@ const DashboardPage: React.FC = ({ }) => { const { t } = useTranslation("dashboard"); const router = useRouter(); + + const [escLog, setEscLog] = useState([]) + + useEffect(() => { + setEscLog(escalationLogs); + }, [escalationLogs]) return ( @@ -35,7 +41,7 @@ const DashboardPage: React.FC = ({ - + diff --git a/src/components/PoDetail/QcComponent.tsx b/src/components/PoDetail/QcComponent.tsx index a06d53c..90f33de 100644 --- a/src/components/PoDetail/QcComponent.tsx +++ b/src/components/PoDetail/QcComponent.tsx @@ -20,7 +20,7 @@ import { Tooltip, Typography, } from "@mui/material"; -import { useFormContext, Controller, FieldPath, useFieldArray } from "react-hook-form"; +import { useFormContext, Controller, FieldPath } from "react-hook-form"; import { useTranslation } from "react-i18next"; import StyledDataGrid from "../StyledDataGrid"; import { Dispatch, SetStateAction, useCallback, useEffect, useMemo, useState } from "react"; @@ -51,11 +51,12 @@ import QcDataGrid from "./QCDatagrid"; import StockInFormVer2 from "./StockInFormVer2"; import { dummyEscalationHistory, dummyQCData } from "./dummyQcTemplate"; import { ModalFormInput } from "@/app/api/po/actions"; -import { escape } from "lodash"; +import { escape, min } from "lodash"; import { PanoramaSharp } from "@mui/icons-material"; import EscalationLogTable from "../DashboardPage/escalation/EscalationLogTable"; import { EscalationResult } from "@/app/api/escalation"; import { EscalationCombo } from "@/app/api/user"; +import CollapsibleCard from "../CollapsibleCard/CollapsibleCard"; interface Props { itemDetail: StockInLine & { qcResult?: PurchaseQcResult[] } & { escResult?: EscalationResult[] }; @@ -135,16 +136,20 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled = false, escala const accQty = watch("acceptQty"); const validateForm = useCallback(() => { if (qcDecision == 1) { - if (accQty > itemDetail.acceptedQty){ + if (isNaN(accQty) || accQty === undefined || accQty === null || typeof(accQty) != "number") { + setError("acceptQty", { message: t("value must be a number") }); + } else + if (!Number.isInteger(accQty)) { + setError("acceptQty", { message: t("value must be integer") }); + } + if (accQty > itemDetail.acceptedQty) { setError("acceptQty", { message: `${t("acceptQty must not greater than")} ${ itemDetail.acceptedQty}` }); - } - if (accQty < 1){ + } else + if (accQty < 1) { setError("acceptQty", { message: t("minimal value is 1") }); - } - if (isNaN(accQty)){ - setError("acceptQty", { message: t("value must be a number") }); - } + } else + console.log("%c Validated accQty:", "color:yellow", accQty); } },[setError, qcDecision, accQty, itemDetail]) @@ -343,7 +348,6 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled = false, escala onClick={(e) => e.stopPropagation()} onMouseDown={(e) => e.stopPropagation()} onKeyDown={(e) => e.stopPropagation()} - inputProps={{ min: 0 }} sx={{ width: '100%' }} /> ); @@ -490,7 +494,7 @@ const QcComponent: React.FC = ({ qc, itemDetail, disabled = false, escala */} - {/* */} + = ({ qc, itemDetail, disabled = false, escala autoHeight sortModel={[]} /> - {/* */} + {/* = ({ qc, itemDetail, disabled = false, escala // value={field.value?.toString() || "true"} onChange={(e) => { const value = e.target.value.toString();// === 'true'; - if (value != "1" && Boolean(errors.acceptQty)) { - // if (!value && Boolean(errors.acceptQty)) { - setValue("acceptQty", itemDetail.acceptedQty ?? 0); + + const input = document.getElementById('accQty') as HTMLInputElement; + console.log("%c Error", "color:pink", errors.acceptQty); + if (input) { // Selected Reject in new flow with Error + if (value == "1") { // Selected Accept + input.value = Number(accQty).toString(); + } else { + if (Boolean(errors.acceptQty)) { + setValue("acceptQty", 0); + } + input.value = '0'; + } } + // setValue("acceptQty", itemDetail.acceptedQty ?? 0); + // clearErrors("acceptQty"); + // } field.onChange(value); }} > } label="接受來貨" /> - {(itemDetail.status == "escalated" || (disabled && accQty != itemDetail.acceptedQty && qcDecision == 1)) && ( //TODO Improve + {(itemDetail.status == "escalated"|| (disabled && accQty != itemDetail.acceptedQty && qcDecision == 1)) && ( //TODO Improve { + const value = e.target.value; + const input = document.getElementById('accQty') as HTMLInputElement; + input.value = Number(value).toString() + setValue(`acceptQty`, Number(value)); + }} + onInput={(e: React.ChangeEvent) => { + const input = e.target.value; + + const numReg = /^[0-9]+$/ + let r = ''; + if (!numReg.test(input)) { + const result = input.replace(/\D/g, ""); + r = (result === '' ? result : Number(result)).toString(); + } else { + r = Number(input).toString() + } + e.target.value = r; + }} + inputProps={{ min: 1, max:itemDetail.acceptedQty }} + // onChange={(e) => { + // const inputValue = e.target.value; + // if (inputValue === '' || /^[0-9]*$/.test(inputValue)) { + // setValue("acceptQty", Number(inputValue === '' ? null : parseInt(inputValue, 10))); + // } + // }} + // {...register("acceptQty", { + // required: "acceptQty required!", + // })} error={Boolean(errors.acceptQty)} helperText={errors.acceptQty?.message} - /> + /> )} diff --git a/src/components/PoDetail/QcStockInModalVer2.tsx b/src/components/PoDetail/QcStockInModalVer2.tsx index 352d6f8..4502c00 100644 --- a/src/components/PoDetail/QcStockInModalVer2.tsx +++ b/src/components/PoDetail/QcStockInModalVer2.tsx @@ -218,7 +218,7 @@ const [qcItems, setQcItems] = useState(dummyQCData) // Get QC data from the shared form context const qcAccept = data.qcDecision == 1; // const qcAccept = data.qcAccept; - const acceptQty = Number(data.acceptQty); + let acceptQty = Number(data.acceptQty); const qcResults = data.qcResult || dummyQCData; // qcItems; // const qcResults = data.qcResult as PurchaseQcResult[]; // qcItems; // const qcResults = viewOnly? data.qcResult as PurchaseQcResult[] : qcItems; @@ -241,12 +241,15 @@ const [qcItems, setQcItems] = useState(dummyQCData) // if (qcAccept === undefined) { validationErrors.push(t("QC decision is required")); } - + // Check if accept quantity is valid - if (acceptQty === undefined || acceptQty <= 0) { - validationErrors.push("Accept quantity must be greater than 0"); + if (data.qcDecision == 2) { + acceptQty = 0; + } else { + if (acceptQty === undefined || acceptQty <= 0) { + validationErrors.push("Accept quantity must be greater than 0"); + } } - // Check if dates are input if (data.productionDate === undefined || data.productionDate == null) { alert("請輸入生產日期!"); diff --git a/src/i18n/zh/purchaseOrder.json b/src/i18n/zh/purchaseOrder.json index cb557f8..105a2ce 100644 --- a/src/i18n/zh/purchaseOrder.json +++ b/src/i18n/zh/purchaseOrder.json @@ -78,7 +78,7 @@ "status": "狀態", "acceptedQty must not greater than": "接受數量不得大於", "minimal value is 1": "最小值為1", - "value must be a number": "值必須是數字", + "value must be a number": "請輸入數字", "qc Check": "質量控制檢查", "Please select QC": "請選擇質量控制", "failQty": "失敗數量", @@ -142,5 +142,7 @@ "Select All": "選擇全部採購單", "View Selected": "查看已選擇採購單", "No Option": "沒有選項", - "receivedTotal": "已來貨總數" + "receivedTotal": "已來貨總數", + "QC Record": "品檢記錄", + "value must be integer": "請輸入整數" }