diff --git a/src/app/api/dashboard/actions.ts b/src/app/api/dashboard/actions.ts index bd71988..931d4b4 100644 --- a/src/app/api/dashboard/actions.ts +++ b/src/app/api/dashboard/actions.ts @@ -193,12 +193,18 @@ export const testing = cache(async (queryParams?: Record) => { export interface GoodsReceiptStatusRow { supplierId: number | null; + supplierCode: string | null; supplierName: string; + purchaseOrderCode: string | null; + statistics: string; expectedNoOfDelivery: number; noOfOrdersReceivedAtDock: number; noOfItemsInspected: number; noOfItemsWithIqcIssue: number; noOfItemsCompletedPutAwayAtStore: number; + // When true, this PO should be hidden from the dashboard table, + // but still counted in the overall statistics (訂單已處理). + hideFromDashboard?: boolean; } export const fetchGoodsReceiptStatus = cache(async (date?: string) => { diff --git a/src/components/DashboardPage/DashboardPage.tsx b/src/components/DashboardPage/DashboardPage.tsx index 6265dd9..c9ad686 100644 --- a/src/components/DashboardPage/DashboardPage.tsx +++ b/src/components/DashboardPage/DashboardPage.tsx @@ -15,7 +15,7 @@ import OrderCompletionChart from "./chart/OrderCompletionChart"; import { EscalationResult } from "@/app/api/escalation"; import EscalationLogTable from "./escalation/EscalationLogTable"; import { TruckScheduleDashboard } from "./truckSchedule"; -import { GoodsReceiptStatus } from "./goodsReceiptStatus"; +import GoodsReceiptStatusNew from "./goodsReceiptStatus/GoodsReceiptStatusNew"; import { CardFilterContext } from "../CollapsibleCard/CollapsibleCard"; interface TabPanelProps { @@ -85,7 +85,7 @@ const DashboardPage: React.FC = ({ aria-label="dashboard tabs" > - + 0 ? getPendingLog().length : t("No")})`} @@ -99,7 +99,7 @@ const DashboardPage: React.FC = ({ - + { + const { t } = useTranslation("dashboard"); + const [selectedDate, setSelectedDate] = useState(dayjs()); + const [data, setData] = useState([]); + const [loading, setLoading] = useState(true); + const [lastUpdated, setLastUpdated] = useState(null); + const [screenCleared, setScreenCleared] = useState(false); + + const loadData = useCallback(async () => { + if (screenCleared) return; + try { + setLoading(true); + const dateParam = selectedDate.format('YYYY-MM-DD'); + const result = await fetchGoodsReceiptStatusClient(dateParam); + setData(result ?? []); + setLastUpdated(dayjs()); + } catch (error) { + console.error('Error fetching goods receipt status:', error); + setData([]); + } finally { + setLoading(false); + } + }, [selectedDate, screenCleared]); + + useEffect(() => { + if (screenCleared) return; + loadData(); + + const refreshInterval = setInterval(() => { + loadData(); + }, REFRESH_MS); + + return () => clearInterval(refreshInterval); + }, [loadData, screenCleared]); + + + const selectedDateLabel = useMemo(() => { + return selectedDate.format('YYYY-MM-DD'); + }, [selectedDate]); + + const totalStatistics = useMemo(() => { + // Overall statistics should count ALL POs, including those hidden from the table + const totalReceived = data.reduce((sum, row) => sum + (row.noOfOrdersReceivedAtDock || 0), 0); + const totalExpected = data.reduce((sum, row) => sum + (row.expectedNoOfDelivery || 0), 0); + return { received: totalReceived, expected: totalExpected }; + }, [data]); + + type StatusKey = 'pending' | 'receiving' | 'accepted'; + + const getStatusKey = useCallback((row: GoodsReceiptStatusRow): StatusKey => { + // Only when the whole PO is processed (all items finished IQC and PO completed) + // should we treat it as "accepted" (已收貨). + if (row.noOfOrdersReceivedAtDock === 1) { + return 'accepted'; + } + + // If some items have been inspected or put away but the order is not fully processed, + // treat as "receiving" / "processing". + if ((row.noOfItemsInspected ?? 0) > 0 || (row.noOfItemsCompletedPutAwayAtStore ?? 0) > 0) { + return 'receiving'; + } + + // Otherwise, nothing has started yet -> "pending". + return 'pending'; + }, []); + + const renderStatusChip = useCallback((row: GoodsReceiptStatusRow) => { + const statusKey = getStatusKey(row); + const label = t(statusKey); + + // Color mapping: pending -> red, receiving -> yellow, accepted -> default/green-ish + const color = + statusKey === 'pending' + ? 'error' + : statusKey === 'receiving' + ? 'warning' + : 'success'; + + return ( + + ); + }, [getStatusKey, t]); + + if (screenCleared) { + return ( + + + + + {t("Screen cleared")} + + + + + + ); + } + + return ( + + + {/* Header */} + + + + {t("Date")}: + + + { + if (!value) return; + setSelectedDate(value); + }} + slotProps={{ + textField: { + size: "small", + sx: { minWidth: 160 } + } + }} + /> + + + 訂單已處理: {totalStatistics.received}/{totalStatistics.expected} + + + + + + + {t("Auto-refresh every 15 minutes")} | {t("Last updated")}: {lastUpdated ? lastUpdated.format('HH:mm:ss') : '--:--:--'} + + + + + + {/* Table */} + + {loading ? ( + + + + ) : ( + + + + + {t("Supplier")} + {t("Purchase Order Code")} + {t("Status")} + {t("No. of Items with IQC Issue")} + + + + {data.length === 0 ? ( + + + + {t("No data available")} ({selectedDateLabel}) + + + + ) : ( + data + .filter((row) => !row.hideFromDashboard) // hide completed/rejected POs from table only + .map((row, index) => ( + + + + + {row.supplierCode || '-'} + + + - + + + {row.supplierName || '-'} + + + + + {row.purchaseOrderCode || '-'} + + + {renderStatusChip(row)} + + + {row.noOfItemsWithIqcIssue ?? 0} + + + )) + )} + +
+
+ )} +
+
+
+ ); +}; + +export default GoodsReceiptStatusNew; +4 \ No newline at end of file diff --git a/src/i18n/en/dashboard.json b/src/i18n/en/dashboard.json index 9434bbf..914459f 100644 --- a/src/i18n/en/dashboard.json +++ b/src/i18n/en/dashboard.json @@ -103,5 +103,13 @@ "Column 1": "Column 1", "Column 2": "Column 2", "Column 3": "Column 3", - "No data available": "No data available" + "No data available": "No data available", + "Supplier Code": "Supplier Code", + "Supplier Name": "Supplier Name", + "Purchase Order Code": "Purchase Order Code", + "Statistics": "Statistics", + "Show Supplier Code": "Show Supplier Code", + "Show Purchase Order Codes": "Show Purchase Order Codes", + "x/y orders received": "x/y orders received", + "Goods Receipt Status New": "Goods Receipt Status" } diff --git a/src/i18n/zh/dashboard.json b/src/i18n/zh/dashboard.json index 78646d2..dacab8b 100644 --- a/src/i18n/zh/dashboard.json +++ b/src/i18n/zh/dashboard.json @@ -103,5 +103,14 @@ "Column 1": "欄位1", "Column 2": "欄位2", "Column 3": "欄位3", - "No data available": "暫無資料" + "No data available": "暫無資料", + "Supplier Code": "供應商編號", + "Supplier Name": "供應商名稱", + "Purchase Order Code": "採購訂單編號", + "Statistics": "統計", + "Show Supplier Code": "顯示供應商編號", + "Show Purchase Order Codes": "顯示採購訂單編號", + "x/y orders received": "x/y張單已處理", + "Goods Receipt Status New": "採購單接收狀態", + "Status": "狀態" }