"use client"; import React, { useState, useEffect, useCallback, useRef } from "react"; import { Box, Card, CardContent, CircularProgress, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Typography, FormControl, Select, MenuItem, Stack } from "@mui/material"; import { useTranslation } from "react-i18next"; import dayjs from "dayjs"; import { fetchOperatorKpi, OperatorKpiResponse, OperatorKpiProcessInfo } from "@/app/api/jo/actions"; import { arrayToDayjs } from "@/app/utils/formatUtil"; const REFRESH_INTERVAL = 10 * 60 * 1000; // 10 分鐘 const OperatorKpiDashboard: React.FC = () => { const { t } = useTranslation(["common", "jo"]); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [selectedDate, setSelectedDate] = useState(dayjs().format("YYYY-MM-DD")); const refreshCountRef = useRef(0); const [now, setNow] = useState(dayjs()); const [lastDataRefreshTime, setLastDataRefreshTime] = useState(null); const formatTime = (timeData: any): string => { if (!timeData) return "-"; if (Array.isArray(timeData)) { try { const parsed = arrayToDayjs(timeData, true); if (parsed.isValid()) { return parsed.format("HH:mm"); } } catch (e) { console.error("Error parsing time array:", e); } } if (typeof timeData === "string") { const parsed = dayjs(timeData); if (parsed.isValid()) { return parsed.format("HH:mm"); } } return "-"; }; const formatMinutesToHHmm = (minutes: number): string => { if (!minutes || minutes <= 0) return "00:00"; const hours = Math.floor(minutes / 60); const mins = minutes % 60; return `${hours.toString().padStart(2, "0")}:${mins.toString().padStart(2, "0")}`; }; const loadData = useCallback(async () => { setLoading(true); try { const result = await fetchOperatorKpi(selectedDate); setData(result); setLastDataRefreshTime(dayjs()); refreshCountRef.current += 1; } catch (error) { console.error("Error fetching operator KPI:", error); setData([]); } finally { setLoading(false); } }, [selectedDate]); useEffect(() => { loadData(); const interval = setInterval(() => { loadData(); }, REFRESH_INTERVAL); return () => clearInterval(interval); }, [loadData]); useEffect(() => { const timer = setInterval(() => setNow(dayjs()), 60 * 1000); return () => clearInterval(timer); }, []); const renderCurrentProcesses = (processes: OperatorKpiProcessInfo[]) => { if (!processes || processes.length === 0) { return ( - ); } // 只顯示目前一個處理中的工序(樣式比照 Excel:欄位名稱縱向排列) const p = processes[0]; const jobOrder = p.jobOrderCode ? `[${p.jobOrderCode}]` : "-"; const itemInfo = p.itemCode && p.itemName ? `${p.itemCode} - ${p.itemName}` : p.itemCode || p.itemName || "-"; // 格式化所需時間(分鐘轉換為 HH:mm) const formatRequiredTime = (minutes: number | null | undefined): string => { if (!minutes || minutes <= 0) return "-"; const hours = Math.floor(minutes / 60); const mins = minutes % 60; return `${hours.toString().padStart(2, "0")}:${mins.toString().padStart(2, "0")}`; }; // 計算預計完成時間 const calculateEstimatedCompletionTime = (): string => { if (!p.startTime || !p.processingTime || p.processingTime <= 0) return "-"; try { const start = arrayToDayjs(p.startTime, true); if (!start.isValid()) return "-"; const estimated = start.add(p.processingTime, "minute"); return estimated.format("HH:mm"); } catch (e) { console.error("Error calculating estimated completion time:", e); return "-"; } }; return ( <> {t("Job Order and Product")}: {jobOrder} {itemInfo} {t("Process")}: {p.processName || "-"} {t("Start Time")}: {formatTime(p.startTime)} {t("Required Time")}: {formatRequiredTime(p.processingTime)} {t("Estimated Completion Time")}: {calculateEstimatedCompletionTime()} ); }; return ( {/* Title */} {t("Operator KPI Dashboard")} {/* Filters */} {t("Now")}: {now.format('HH:mm')} {t("Auto-refresh every 10 minutes")} | {t("Last updated")}: {lastDataRefreshTime ? lastDataRefreshTime.format('HH:mm:ss') : '--:--:--'} {loading ? ( ) : ( {t("No.")} {t("Operator")} {t("Job Details")} {data.length === 0 ? ( {t("No data available")} ) : ( data.map((row, index) => { const jobOrderCount = row.totalJobOrderCount || 0; return ( {index + 1} {t("Operator Name & No.")}:{" "} {row.operatorName || "-"}{" "} {row.staffNo ? `(${row.staffNo})` : ""} {t("Count of Job Orders")}:{" "} {jobOrderCount} {t("Total Processing Time")}:{" "} {formatMinutesToHHmm(row.totalProcessingMinutes || 0)} {renderCurrentProcesses(row.currentProcesses)} ); }) )}
)}
); }; export default OperatorKpiDashboard;