From b58947b1e5c5a0f5e2a544591535d71c6f8e30ec Mon Sep 17 00:00:00 2001 From: "B.E.N.S.O.N" Date: Mon, 26 Jan 2026 12:39:10 +0800 Subject: [PATCH] Dashboard: Goods Receipt Status --- src/app/api/dashboard/actions.ts | 18 ++ src/app/api/dashboard/client.ts | 17 ++ .../goodsReceiptStatus/GoodsReceiptStatus.tsx | 206 ++++++++++++------ src/i18n/en/dashboard.json | 19 ++ src/i18n/zh/dashboard.json | 19 ++ 5 files changed, 207 insertions(+), 72 deletions(-) create mode 100644 src/app/api/dashboard/client.ts diff --git a/src/app/api/dashboard/actions.ts b/src/app/api/dashboard/actions.ts index bd0d240..bd71988 100644 --- a/src/app/api/dashboard/actions.ts +++ b/src/app/api/dashboard/actions.ts @@ -190,3 +190,21 @@ export const testing = cache(async (queryParams?: Record) => { ); } }); + +export interface GoodsReceiptStatusRow { + supplierId: number | null; + supplierName: string; + expectedNoOfDelivery: number; + noOfOrdersReceivedAtDock: number; + noOfItemsInspected: number; + noOfItemsWithIqcIssue: number; + noOfItemsCompletedPutAwayAtStore: number; +} + +export const fetchGoodsReceiptStatus = cache(async (date?: string) => { + const url = date + ? `${BASE_API_URL}/dashboard/goods-receipt-status?date=${date}` + : `${BASE_API_URL}/dashboard/goods-receipt-status`; + + return await serverFetchJson(url, { method: "GET" }); +}); diff --git a/src/app/api/dashboard/client.ts b/src/app/api/dashboard/client.ts new file mode 100644 index 0000000..ff0facb --- /dev/null +++ b/src/app/api/dashboard/client.ts @@ -0,0 +1,17 @@ +"use client"; + +import { + fetchGoodsReceiptStatus, + type GoodsReceiptStatusRow, +} from "./actions"; + +export const fetchGoodsReceiptStatusClient = async ( + date?: string, +): Promise => { + return await fetchGoodsReceiptStatus(date); +}; + +export type { GoodsReceiptStatusRow }; + +export default fetchGoodsReceiptStatusClient; + diff --git a/src/components/DashboardPage/goodsReceiptStatus/GoodsReceiptStatus.tsx b/src/components/DashboardPage/goodsReceiptStatus/GoodsReceiptStatus.tsx index 4e725d2..c788078 100644 --- a/src/components/DashboardPage/goodsReceiptStatus/GoodsReceiptStatus.tsx +++ b/src/components/DashboardPage/goodsReceiptStatus/GoodsReceiptStatus.tsx @@ -1,13 +1,9 @@ "use client"; -import React, { useState, useEffect, useCallback, useMemo, useRef } from 'react'; +import React, { useState, useEffect, useCallback, useMemo } from 'react'; import { Box, Typography, - FormControl, - InputLabel, - Select, - MenuItem, Card, CardContent, Stack, @@ -19,88 +15,112 @@ import { TableRow, Paper, CircularProgress, - Chip + Button } from '@mui/material'; import { useTranslation } from 'react-i18next'; import dayjs from 'dayjs'; +import { LocalizationProvider } from '@mui/x-date-pickers/LocalizationProvider'; +import { AdapterDayjs } from '@mui/x-date-pickers/AdapterDayjs'; +import { DatePicker } from '@mui/x-date-pickers/DatePicker'; +import { fetchGoodsReceiptStatusClient, type GoodsReceiptStatusRow } from '@/app/api/dashboard/client'; -interface GoodsReceiptStatusItem { - id: string; -} +const REFRESH_MS = 15 * 60 * 1000; const GoodsReceiptStatus: React.FC = () => { const { t } = useTranslation("dashboard"); - const [selectedFilter, setSelectedFilter] = useState(""); - const [data, setData] = useState([]); - const [loading, setLoading] = useState(false); - const [currentTime, setCurrentTime] = useState(null); - const [isClient, setIsClient] = useState(false); - - useEffect(() => { - setIsClient(true); - setCurrentTime(dayjs()); - }, []); + 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 { - setData([]); + 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(); - }, 5 * 60 * 1000); + }, REFRESH_MS); return () => clearInterval(refreshInterval); - }, [loadData]); + }, [loadData, screenCleared]); - useEffect(() => { - if (!isClient) return; - - const timeInterval = setInterval(() => { - setCurrentTime(dayjs()); - }, 60 * 1000); - - return () => clearInterval(timeInterval); - }, [isClient]); - const filteredData = useMemo(() => { - if (!selectedFilter) return data; - return data.filter(item => true); - }, [data, selectedFilter]); + const selectedDateLabel = useMemo(() => { + return selectedDate.format('YYYY-MM-DD'); + }, [selectedDate]); + + if (screenCleared) { + return ( + + + + + {t("Screen cleared")} + + + + + + ); + } return ( - {/* Filter */} - - - - {t("Filter")} - - - - - - {t("Auto-refresh every 5 minutes")} | {t("Last updated")}: {isClient && currentTime ? currentTime.format('HH:mm:ss') : '--:--:--'} + {/* Header */} + + + + {t("Date")}: + + + { + if (!value) return; + setSelectedDate(value); + }} + slotProps={{ + textField: { + size: "small", + sx: { minWidth: 160 } + } + }} + /> + + + {t("Allow to select Date to view history.")} + + + + + + + {t("Auto-refresh every 15 minutes")} | {t("Last updated")}: {lastUpdated ? lastUpdated.format('HH:mm:ss') : '--:--:--'} + + {/* Table */} @@ -114,38 +134,80 @@ const GoodsReceiptStatus: React.FC = () => { - {t("Column 1")} - {t("Column 2")} - {t("Column 3")} - {/* TODO: Add table columns when implementing */} + {t("Supplier")} + {t("Expected No. of Delivery")} + {t("No. of Orders Received at Dock")} + {t("No. of Items Inspected")} + {t("No. of Items with IQC Issue")} + {t("No. of Items Completed Put Away at Store")} + + + + + {t("Show Supplier Name")} + + + + + {t("Based on Expected Delivery Date")} + + + + + {t("Upon entry of DN and Lot No. for all items of the order")} + + + + + {t("Upon any IQC decision received")} + + + + + {t("Count any item with IQC defect in any IQC criteria")} + + + + + {t("Upon completion of put away for an material in order. Count no. of items being put away")} + + - {filteredData.length === 0 ? ( + {data.length === 0 ? ( - + - {t("No data available")} + {t("No data available")} ({selectedDateLabel}) ) : ( - filteredData.map((row, index) => ( + data.map((row, index) => ( - {/* TODO: Add table cell content when implementing */} - - + {row.supplierName || '-'} - - - + + {row.expectedNoOfDelivery ?? 0} - - - + + {row.noOfOrdersReceivedAtDock ?? 0} + + + {row.noOfItemsInspected ?? 0} + + + {row.noOfItemsWithIqcIssue ?? 0} + + + {row.noOfItemsCompletedPutAwayAtStore ?? 0} )) diff --git a/src/i18n/en/dashboard.json b/src/i18n/en/dashboard.json index 5a1c9aa..9434bbf 100644 --- a/src/i18n/en/dashboard.json +++ b/src/i18n/en/dashboard.json @@ -79,6 +79,25 @@ "Tomorrow": "Tomorrow", "Day After Tomorrow": "Day After Tomorrow", "Goods Receipt Status": "Goods Receipt Status", + "Date": "Date", + "Time": "Time", + "Allow to select Date to view history.": "Allow to select Date to view history.", + "Auto-refresh every 15 minutes": "Auto-refresh every 15 minutes", + "Exit Screen": "Exit Screen", + "Restore Screen": "Restore Screen", + "Screen cleared": "Screen cleared", + "Supplier": "Supplier", + "Expected No. of Delivery": "Expected No. of Delivery", + "No. of Orders Received at Dock": "No. of Orders Received at Dock", + "No. of Items Inspected": "No. of Items Inspected", + "No. of Items with IQC Issue": "No. of Items with IQC Issue", + "No. of Items Completed Put Away at Store": "No. of Items Completed Put Away at Store", + "Show Supplier Name": "Show Supplier Name", + "Based on Expected Delivery Date": "Based on Expected Delivery Date", + "Upon entry of DN and Lot No. for all items of the order": "Upon entry of DN and Lot No. for all items of the order", + "Upon any IQC decision received": "Upon any IQC decision received", + "Count any item with IQC defect in any IQC criteria": "Count any item with IQC defect in any IQC criteria", + "Upon completion of put away for an material in order. Count no. of items being put away": "Upon completion of put away for an material in order. Count no. of items being put away", "Filter": "Filter", "All": "All", "Column 1": "Column 1", diff --git a/src/i18n/zh/dashboard.json b/src/i18n/zh/dashboard.json index c2cf9d7..2e8253d 100644 --- a/src/i18n/zh/dashboard.json +++ b/src/i18n/zh/dashboard.json @@ -79,6 +79,25 @@ "Tomorrow": "翌日", "Day After Tomorrow": "後日", "Goods Receipt Status": "貨物接收狀態", + "Date": "日期", + "Time": "時間", + "Allow to select Date to view history.": "可選擇日期查看歷史記錄。", + "Auto-refresh every 15 minutes": "每15分鐘自動刷新", + "Exit Screen": "退出畫面", + "Restore Screen": "恢復畫面", + "Screen cleared": "畫面已清除", + "Supplier": "供應商", + "Expected No. of Delivery": "預計送貨數", + "No. of Orders Received at Dock": "碼頭已收訂單數", + "No. of Items Inspected": "已檢驗貨品數", + "No. of Items with IQC Issue": "IQC異常貨品數", + "No. of Items Completed Put Away at Store": "已完成上架貨品數", + "Show Supplier Name": "顯示供應商名稱", + "Based on Expected Delivery Date": "按預計送貨日期統計", + "Upon entry of DN and Lot No. for all items of the order": "當訂單所有貨品已輸入DN及批號時", + "Upon any IQC decision received": "當收到任何IQC判定", + "Count any item with IQC defect in any IQC criteria": "統計任何IQC準則不合格的貨品", + "Upon completion of put away for an material in order. Count no. of items being put away": "當訂單物料完成上架。統計正在上架的貨品數", "Filter": "篩選", "All": "全部", "Column 1": "欄位1",