diff --git a/src/app/api/bom/index.ts b/src/app/api/bom/index.ts index 1d57616..0b1ceef 100644 --- a/src/app/api/bom/index.ts +++ b/src/app/api/bom/index.ts @@ -83,7 +83,7 @@ export interface BomDetailResponse { id: number; itemCode?: string; itemName?: string; - isDark?: boolean; + isDark?: number; isFloat?: number; isDense?: number; isDrink?: boolean; diff --git a/src/components/ImportBom/ImportBomDetailTab.tsx b/src/components/ImportBom/ImportBomDetailTab.tsx index 9c5ef84..ccfdd7f 100644 --- a/src/components/ImportBom/ImportBomDetailTab.tsx +++ b/src/components/ImportBom/ImportBomDetailTab.tsx @@ -21,6 +21,8 @@ import type { BomCombo, BomDetailResponse } from "@/app/api/bom"; import { fetchBomComboClient, fetchBomDetailClient } from "@/app/api/bom/client"; import type { SelectChangeEvent } from "@mui/material/Select"; import { useTranslation } from "react-i18next"; +import SearchBox, { Criterion } from "../SearchBox"; +import { useMemo, useCallback } from "react"; const ImportBomDetailTab: React.FC = () => { const { t } = useTranslation( "common" ); const [bomList, setBomList] = useState([]); @@ -28,7 +30,8 @@ const ImportBomDetailTab: React.FC = () => { const [detail, setDetail] = useState(null); const [loadingList, setLoadingList] = useState(false); const [loadingDetail, setLoadingDetail] = useState(false); - + const [filteredBoms, setFilteredBoms] = useState([]) + const [currentBom, setCurrentBom] = useState(null); useEffect(() => { const loadList = async () => { setLoadingList(true); @@ -41,7 +44,91 @@ const ImportBomDetailTab: React.FC = () => { }; loadList(); }, []); + type BomSearchKey = "code" | "name"; + const searchCriteria: Criterion[] = useMemo( + () => [ + { label: t("Code"), paramName: "code", type: "text" }, + { label: t("Name"), paramName: "name", type: "text" }, + ], + [t], + ); + useEffect(() => { + setFilteredBoms(bomList); // 初始顯示全部 + }, [bomList]); + const handleSearchBom = useCallback( + (inputs: Record) => { + const code = (inputs.code ?? "").trim().toLowerCase(); + const name = (inputs.name ?? "").trim().toLowerCase(); + + const result = bomList.filter((b) => { + const label = b.label.toLowerCase(); + const okCode = !code || label.includes(code); + const okName = !name || label.includes(name); + return okCode && okName; + }); + + setFilteredBoms(result); + + // 如果只找到一個,直接載入明細 + if (result.length === 1) { + const only = result[0]; + setSelectedBomId(only.id); + setDetail(null); + setLoadingDetail(true); + fetchBomDetailClient(only.id) + .then((d) => setDetail(d)) + .finally(() => setLoadingDetail(false)); + } + }, + [bomList], + ); + const renderAllergic = (v?: number) => { + if (v === 0) return "有"; + if (v === 5) return "沒有"; + return "-"; + }; + + const renderIsFloat = (v?: number) => { + if (v === 5) return "沉"; + if (v === 3) return "浮"; + if (v === 0) return "不適用"; + return "-"; + }; + + const renderIsDense = (v?: number) => { + if (v === 5) return "淡"; + if (v === 3) return "濃"; + if (v === 0) return "不適用"; + return "-"; + }; + + const renderTimeSequence = (v?: number) => { + if (v === 5) return "上午"; + if (v === 1) return "下午"; + if (v === 0) return "不適用"; + return "-"; + }; + const renderType = (v?: string) => { + if (v === "FG") return "成品"; + if (v === "WIP") return "半成品"; + return "-"; + }; + + const renderComplexity = (v?: number) => { + if (v === 10) return "簡單"; + if (v === 5) return "中度"; + if (v === 3) return "複雜"; + if (v === 0) return "不適用"; + return "-"; + }; + /* + const handleResetBom = useCallback(() => { + setFilteredBoms(bomList); + setSelectedBomId(""); + setDetail(null); + }, [bomList]); + */ const handleChangeBom = async (event: SelectChangeEvent) => { const id = Number(event.target.value); setSelectedBomId(id); @@ -58,30 +145,22 @@ const ImportBomDetailTab: React.FC = () => { return ( - - - {t("Please Select BOM")} - - - - + + criteria={searchCriteria} + onSearch={handleSearchBom} + //onReset={handleResetBom} + /> + +{currentBom && ( + + + CODE / NAME + + + {currentBom.label} ({t("Output Quantity")} {currentBom.outputQty} {currentBom.outputQtyUom}) + + +)} {loadingDetail && ( {t("Loading BOM Detail...")} @@ -90,15 +169,44 @@ const ImportBomDetailTab: React.FC = () => { {detail && ( - - {detail.itemCode} {detail.itemName}({t("Output Quantity")} {detail.outputQty}{" "} - {detail.outputQtyUom}) - + + {detail.itemCode} {detail.itemName} + + {/* Basic Info 列表 */} + + + {t("Basic Info")} + + + + {/* 第一行:輸出數量 + 類型 */} + + {t("Output Quantity")}:{" "} + {detail.outputQty} {detail.outputQtyUom} + {" "} + {t("Type")}: {detail.description ?? "-"} + + + {/* 第二行:各種指標,排成一行 key:value, key:value */} + + {t("Allergic Substances")}: {renderAllergic(detail.allergicSubstances)} + {" "}{t("Depth")}: {detail.isDark ?? "-"} + {" "}{t("Float")}: {renderIsFloat(detail.isFloat)} + {" "}{t("Density")}: {renderIsDense(detail.isDense)} + + + + {t("Time Sequence")}: {renderTimeSequence(detail.timeSequence)} + {" "}{t("Complexity")}: {renderComplexity(detail.complexity)} + {" "}{t("Base Score")}: {detail.baseScore ?? "-"} + + + {/* 材料列表 */} - 材料 (Bom Material) + {t("Bom Material")}