diff --git a/src/app/api/jo/index.ts b/src/app/api/jo/index.ts index b6d889c..ed8b99f 100644 --- a/src/app/api/jo/index.ts +++ b/src/app/api/jo/index.ts @@ -21,6 +21,7 @@ export interface JobOrder { reqQty: number; item: Item; itemName: string; + bomId: number; // uom: Uom; pickLines?: JoDetailPickLine[]; status: JoStatus; diff --git a/src/components/JoSearch/JoSearch.tsx b/src/components/JoSearch/JoSearch.tsx index d2dd42e..1206935 100644 --- a/src/components/JoSearch/JoSearch.tsx +++ b/src/components/JoSearch/JoSearch.tsx @@ -161,11 +161,20 @@ const JoSearch: React.FC = ({ defaultInputs, bomCombo, printerCombo, jobT const fetchBomForJo = useCallback(async (jo: JobOrder): Promise => { try { - const detailedJo = detailedJos.get(jo.id) || await fetchJoDetailClient(jo.id); - const matchingBom = bomCombo.find(bom => { - return true; // 临时占位 - }); - return matchingBom || null; + // 若列表的 jo 已有 bomId(之後若 API 有回傳),可直接用 + const bomId = (jo as { bomId?: number }).bomId; + if (bomId != null) { + const matchingBom = bomCombo.find(bom => bom.id === bomId); + if (matchingBom) return matchingBom; + } + // 否則打明細 API,明細會帶 bomId + const detailedJo = detailedJos.get(jo.id) ?? await fetchJoDetailClient(jo.id); + const detailBomId = (detailedJo as { bomId?: number }).bomId; + if (detailBomId != null) { + const matchingBom = bomCombo.find(bom => bom.id === detailBomId); + if (matchingBom) return matchingBom; + } + return null; } catch (error) { console.error("Error fetching BOM for JO:", error); return null; diff --git a/src/components/NavigationContent/NavigationContent.tsx b/src/components/NavigationContent/NavigationContent.tsx index e8a727c..46f4c6b 100644 --- a/src/components/NavigationContent/NavigationContent.tsx +++ b/src/components/NavigationContent/NavigationContent.tsx @@ -1,5 +1,7 @@ import { useSession } from "next-auth/react"; import Box from "@mui/material/Box"; +import Stack from "@mui/material/Stack"; +import Typography from "@mui/material/Typography"; import React from "react"; import List from "@mui/material/List"; import ListItemButton from "@mui/material/ListItemButton"; @@ -440,7 +442,14 @@ const NavigationContent: React.FC = () => { minHeight: 56, }} > - + + + {process.env.NODE_ENV === "production" && ( + + 正式服務器 Production Server + + )} + diff --git a/src/components/StockIssue/SearchPage.tsx b/src/components/StockIssue/SearchPage.tsx index d7a9a18..b900f80 100644 --- a/src/components/StockIssue/SearchPage.tsx +++ b/src/components/StockIssue/SearchPage.tsx @@ -1,5 +1,6 @@ "use client"; - +import dayjs from "dayjs"; +import { OUTPUT_DATE_FORMAT } from "@/app/utils/formatUtil"; import SearchBox, { Criterion } from "../SearchBox"; import { useCallback, useMemo, useState } from "react"; import { useTranslation } from "react-i18next"; @@ -52,7 +53,7 @@ const SearchPage: React.FC = ({ dataList }) => { const [selectedIds, setSelectedIds] = useState<(string | number)[]>([]); const [submittingIds, setSubmittingIds] = useState>(new Set()); const [batchSubmitting, setBatchSubmitting] = useState(false); - + const [paging, setPaging] = useState({ pageNum: 1, pageSize: 10 }); const searchCriteria: Criterion[] = useMemo( () => [ { @@ -251,7 +252,33 @@ const SearchPage: React.FC = ({ dataList }) => { { name: "itemDescription", label: t("Item") }, { name: "lotNo", label: t("Lot No.") }, { name: "storeLocation", label: t("Location") }, - { name: "expiryDate", label: t("Expiry Date") }, + { + name: "expiryDate", + label: t("Expiry Date"), + renderCell: (item) => { + const raw = String(item.expiryDate ?? "").trim(); + if (!raw) return "—"; + let d; + if (raw.includes(",")) { + const parts = raw.split(",").map((s) => parseInt(s.trim(), 10)); + const [y, m, d_] = parts; + if (parts.length >= 3 && y != null && m != null && d_ != null && !Number.isNaN(y) && !Number.isNaN(m) && !Number.isNaN(d_)) { + d = dayjs(new Date(y, m - 1, d_)); + } else { + d = dayjs(""); + } + } else { + let normalized = raw; + if (raw.length === 7) { + normalized = raw.slice(0, 4) + "0" + raw.slice(4, 5) + raw.slice(5, 7); + } else if (raw.length === 6) { + normalized = raw.slice(0, 4) + "0" + raw.slice(4, 5) + "0" + raw.slice(5, 6); + } + d = dayjs(normalized, "YYYYMMDD", true); + } + return d.isValid() ? d.format(OUTPUT_DATE_FORMAT) : raw; + }, + }, { name: "remainingQty", label: t("Remaining Qty") }, { name: "id", @@ -280,6 +307,7 @@ const SearchPage: React.FC = ({ dataList }) => { (_: React.SyntheticEvent, value: string) => { setTab(value as "miss" | "bad" | "expiry"); setSelectedIds([]); + setPaging((prev) => ({ ...prev, pageNum: 1 })); // 新增:切 Tab 时回到第 1 页 }, [], ); @@ -291,8 +319,9 @@ const SearchPage: React.FC = ({ dataList }) => { items={items} columns={missColumns} - pagingController={{ pageNum: 1, pageSize: 10 }} + pagingController={paging} checkboxIds={selectedIds} + setPagingController={setPaging} setCheckboxIds={setSelectedIds} /> ); @@ -304,7 +333,8 @@ const SearchPage: React.FC = ({ dataList }) => { items={items} columns={badColumns} - pagingController={{ pageNum: 1, pageSize: 10 }} + pagingController={paging} + setPagingController={setPaging} checkboxIds={selectedIds} setCheckboxIds={setSelectedIds} /> @@ -316,7 +346,8 @@ const SearchPage: React.FC = ({ dataList }) => { items={items} columns={expiryColumns} - pagingController={{ pageNum: 1, pageSize: 10 }} + pagingController={paging} + setPagingController={setPaging} checkboxIds={selectedIds} setCheckboxIds={setSelectedIds} /> @@ -336,16 +367,18 @@ const SearchPage: React.FC = ({ dataList }) => { onSearch={handleSearch} /> - - - +{tab === "expiry" && ( + + + +)} {renderCurrentTab()} { ) ) : null )} + {/* {viewScope === "approver-by-section" && tabValue === 1 && ( - )} + )} */} {viewScope === "approver-all" && tabValue === 2 && ( { sx={{ mb: 2 }} > - + {/* */} @@ -137,6 +138,7 @@ const StockTakeTab: React.FC = () => { onReStockTakeClick={handleReStockTakeClick} /> )} + {/* {tabValue === 1 && ( { @@ -145,7 +147,8 @@ const StockTakeTab: React.FC = () => { }} /> )} - {tabValue === 2 && ( + */} + {tabValue === 1 && (