"use client"; import React, { useState, useEffect, useCallback } from "react"; import { Box, Card, CardContent, CircularProgress, Table, TableBody, TableCell, TableContainer, TableHead, TableRow, Paper, Typography, Tabs, Tab, Chip, Stack } from "@mui/material"; import { useTranslation } from "react-i18next"; import dayjs from "dayjs"; import { fetchEquipmentStatus, EquipmentStatusByTypeResponse, EquipmentStatusPerDetail, } from "@/app/api/jo/actions"; import { arrayToDayjs } from "@/app/utils/formatUtil"; const REFRESH_INTERVAL = 60 * 1000; // 1 分鐘 const STATUS_COLORS: Record = { Processing: "success", Idle: "default", Repair: "warning", }; const formatDateTime = (value: any): string => { if (!value) return "-"; if (Array.isArray(value)) { try { const parsed = arrayToDayjs(value, true); if (parsed.isValid()) { return parsed.format("YYYY-MM-DD HH:mm"); } } catch (e) { console.error("Error parsing datetime array:", e); } } if (typeof value === "string") { const parsed = dayjs(value); if (parsed.isValid()) { return parsed.format("YYYY-MM-DD HH:mm"); } } return "-"; }; const formatTime = (value: any): string => { if (!value) return "-"; if (Array.isArray(value)) { try { const parsed = arrayToDayjs(value, true); if (parsed.isValid()) { return parsed.format("HH:mm"); } } catch (e) { console.error("Error parsing time array:", e); } } if (typeof value === "string") { const parsed = dayjs(value); if (parsed.isValid()) { return parsed.format("HH:mm"); } } return "-"; }; // 计算预计完成时间 const calculateEstimatedCompletionTime = ( startTime: any, processingTime: number | null | undefined ): string => { if (!startTime || !processingTime || processingTime <= 0) return "-"; try { const start = arrayToDayjs(startTime, true); if (!start.isValid()) return "-"; const estimated = start.add(processingTime, "minute"); return estimated.format("YYYY-MM-DD HH:mm"); } catch (e) { console.error("Error calculating estimated completion time:", e); return "-"; } }; // 计算剩余时间(分钟) const calculateRemainingTime = ( startTime: any, processingTime: number | null | undefined ): string => { if (!startTime || !processingTime || processingTime <= 0) return "-"; try { const start = arrayToDayjs(startTime, true); if (!start.isValid()) return "-"; const now = dayjs(); const estimated = start.add(processingTime, "minute"); const remainingMinutes = estimated.diff(now, "minute"); if (remainingMinutes < 0) { return `-${Math.abs(remainingMinutes)}`; } return remainingMinutes.toString(); } catch (e) { console.error("Error calculating remaining time:", e); return "-"; } }; const EquipmentStatusDashboard: React.FC = () => { const { t } = useTranslation(["common", "jo"]); const [data, setData] = useState([]); const [loading, setLoading] = useState(true); const [tabIndex, setTabIndex] = useState(0); const [now, setNow] = useState(dayjs()); const [lastDataRefreshTime, setLastDataRefreshTime] = useState(null); const loadData = useCallback(async () => { setLoading(true); try { const result = await fetchEquipmentStatus(); setData(result || []); setLastDataRefreshTime(dayjs()); } catch (error) { console.error("Error fetching equipment status:", error); setData([]); } finally { setLoading(false); } }, []); useEffect(() => { loadData(); const interval = setInterval(() => { loadData(); }, REFRESH_INTERVAL); return () => clearInterval(interval); }, [loadData]); // 添加定时更新剩余时间 useEffect(() => { const timer = setInterval(() => { // 触发重新渲染以更新剩余时间 setData((prev) => [...prev]); }, 60000); // 每分钟更新一次 return () => clearInterval(timer); }, []); useEffect(() => { const timer = setInterval(() => setNow(dayjs()), 60 * 1000); return () => clearInterval(timer); }, []); const handleTabChange = (_: React.SyntheticEvent, newValue: number) => { setTabIndex(newValue); }; const displayTypes = tabIndex === 0 ? data : data.filter((_, index) => index === tabIndex - 1); return ( {t("Production Equipment Status Dashboard")} {data.map((type, index) => ( ))} {t("Now")}: {now.format('HH:mm')} {t("Auto-refresh every 1 minute")} | {t("Last updated")}: {lastDataRefreshTime ? lastDataRefreshTime.format('HH:mm:ss') : '--:--:--'} {loading ? ( ) : displayTypes.length === 0 ? ( {t("No data available")} ) : ( {displayTypes.map((type) => { const details = type.details || []; if (details.length === 0) return null; return ( {type.equipmentTypeName || "-"} {t("Equipment Name and Code")} {details.map((d) => ( {d.equipmentDetailName || "-"} {d.equipmentDetailCode || "-"} ))} {/* 工序 Row */} {t("Process")} {details.map((d) => ( {d.status === "Processing" ? d.currentProcess?.processName || "-" : "-"} ))} {/* 狀態 Row - 修改:Processing 时只显示 job order code */} {t("Status")} {details.map((d) => { const chipColor = STATUS_COLORS[d.status] || "default"; const cp = d.currentProcess; // Processing 时只显示 job order code,不显示 Chip if (d.status === "Processing" && cp?.jobOrderCode) { return ( {cp.jobOrderCode} ); } // 其他状态显示 Chip return ( ); })} {/* 開始時間 Row */} {t("Start Time")} {details.map((d) => ( {d.status === "Processing" ? formatDateTime(d.currentProcess?.startTime) : "-"} ))} {/* 預計完成時間 Row */} {t("預計完成時間")} {details.map((d) => ( {d.status === "Processing" ? calculateEstimatedCompletionTime( d.currentProcess?.startTime, d.currentProcess?.processingTime ) : "-"} ))} {/* 剩餘時間 Row */} {t("Remaining Time (min)")} {details.map((d) => ( {d.status === "Processing" ? calculateRemainingTime( d.currentProcess?.startTime, d.currentProcess?.processingTime ) : "-"} ))}
); })}
)}
); }; export default EquipmentStatusDashboard;