diff --git a/BACKEND/app/services/line_connection.ts b/BACKEND/app/services/line_connection.ts index 1f1f781..6253983 100644 --- a/BACKEND/app/services/line_connection.ts +++ b/BACKEND/app/services/line_connection.ts @@ -696,6 +696,7 @@ export default class LineConnection { } async addHistory(stationId: number, lineId: number, item: HistoryItem) { + if (!item.pid || !item.sn) return const key = `station:${stationId}:line:${lineId}:history` const now = Date.now() diff --git a/FRONTEND/src/components/ModalHistory.tsx b/FRONTEND/src/components/ModalHistory.tsx index aef06c9..a4d3481 100644 --- a/FRONTEND/src/components/ModalHistory.tsx +++ b/FRONTEND/src/components/ModalHistory.tsx @@ -42,7 +42,12 @@ const TIME_PERIODS = [ { label: "last 48h", value: "last_48h" }, ]; -function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistoryProps) { +function ModalHistory({ + opened, + onClose, + socket, + stationIds = [], +}: ModalHistoryProps) { const [historyData, setHistoryData] = useState([]); const [activeStation, setActiveStation] = useState(""); const [activeTimePeriod, setActiveTimePeriod] = useState("current"); @@ -71,7 +76,7 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory // Request history when modal opens useEffect(() => { if (!socket || !opened) return; - + // Request history with station IDs socket.emit("get_list_history", { stationIds }); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -86,7 +91,6 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory } }, [opened]); - // Utility function to format timestamp (can be used later if needed) // const formatTimestamp = (timestamp: number) => { // const date = new Date(timestamp); @@ -97,23 +101,23 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory const getTimeRange = (period: string): [number, number] => { const now = Date.now(); const HOUR = 60 * 60 * 1000; - + switch (period) { case "current": // Current: chỉ hiện tại (4h gần nhất) - return [now - 4 * HOUR, now]; + return [0, now]; case "last_4h": // Last 4h: bao gồm current + last 4h (0-8h) - return [now - 8 * HOUR, now]; + return [now - 4 * HOUR, now]; case "last_8h": // Last 8h: bao gồm current + last 4h + last 8h (0-24h) - return [now - 24 * HOUR, now]; + return [now - 8 * HOUR, now]; case "last_24h": // Last 24h: bao gồm current + last 4h + last 8h + last 24h (0-48h) - return [now - 48 * HOUR, now]; + return [now - 24 * HOUR, now]; case "last_48h": // Last 48h: tất cả data (từ đầu đến giờ) - return [0, now]; + return [now - 48 * HOUR, now]; default: return [0, now]; } @@ -122,27 +126,29 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory // Filter history based on time period const filterHistoryByTime = (history: HistoryItem[]): HistoryItem[] => { const [startTime, endTime] = getTimeRange(activeTimePeriod); - return history.filter((item) => - item.timestamp >= startTime && item.timestamp < endTime + return history.filter( + (item) => item.timestamp >= startTime && item.timestamp < endTime ); }; // Group history items by line number - const groupHistoryByLine = (history: HistoryItem[]): Map => { + const groupHistoryByLine = ( + history: HistoryItem[] + ): Map => { const grouped = new Map(); - + history.forEach((item) => { if (!grouped.has(item.number)) { grouped.set(item.number, []); } grouped.get(item.number)!.push(item); }); - + // Sort items within each group by timestamp (newest first) grouped.forEach((items) => { items.sort((a, b) => b.timestamp - a.timestamp); }); - + return grouped; }; @@ -154,7 +160,7 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory const filteredHistory = currentStationData ? filterHistoryByTime(currentStationData.history) : []; - + // Group filtered history by line number const groupedHistory = groupHistoryByLine(filteredHistory); @@ -214,8 +220,8 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
- HISTORY + List stations @@ -284,11 +290,15 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory backgroundColor: "white", }} > - { - if (!scrollViewportRef.current || isAutoSwitchingRef.current) return; + if ( + !scrollViewportRef.current || + isAutoSwitchingRef.current + ) + return; const scrollTop = position.y; const target = scrollViewportRef.current; @@ -296,19 +306,28 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory const clientHeight = target.clientHeight; // Check if scrolled to bottom - const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 5; + const isAtBottom = + Math.abs(scrollTop + clientHeight - scrollHeight) < 5; if (isAtBottom && historyData.length > 0) { isAutoSwitchingRef.current = true; const currentStationIndex = historyData.findIndex( - (station) => station.stationId.toString() === activeStation + (station) => + station.stationId.toString() === activeStation ); - if (currentStationIndex !== -1 && currentStationIndex < historyData.length - 1) { + if ( + currentStationIndex !== -1 && + currentStationIndex < historyData.length - 1 + ) { // Chuyển sang station tiếp theo - setActiveStation(historyData[currentStationIndex + 1].stationId.toString()); - + setActiveStation( + historyData[ + currentStationIndex + 1 + ].stationId.toString() + ); + // Reset scroll to top setTimeout(() => { if (scrollViewportRef.current) { @@ -330,52 +349,73 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory {Array.from(groupedHistory.entries()) .sort(([lineA], [lineB]) => lineA - lineB) .map(([lineNumber, items]) => ( - - {/* Header của nhóm - hiển thị line number */} - - Line {lineNumber} ({items.length} {items.length > 1 ? 'records' : 'record'}) - - - - {/* Các items trong nhóm */} - {items.map((item, itemIndex) => ( + {/* Header của nhóm - hiển thị line number */} 0 ? "1px solid #f1f3f5" : "none", - backgroundColor: itemIndex % 2 === 0 ? "white" : "#f8f9fa", + padding: "8px 16px", + backgroundColor: "#e9ecef", + fontWeight: 600, }} > - - {item.pid} {item.vid} SN: {item.sn} - - | {item.scenario} - - - {new Date(item.timestamp).toLocaleString()} - + + Line {lineNumber} ({items.length}{" "} + {items.length > 1 ? "records" : "record"}) - ))} - - ))} - + + {/* Các items trong nhóm */} + {items.map((item, itemIndex) => ( + 0 + ? "1px solid #f1f3f5" + : "none", + backgroundColor: + itemIndex % 2 === 0 ? "white" : "#f8f9fa", + }} + > + + {item.pid} {item.vid} SN: {item.sn} + + | {item.scenario} + + + {new Date( + item.timestamp + ).toLocaleString()} + + + + ))} + + ))} + {/* Spacer để đảm bảo có thể scroll ngay cả khi content ít */} @@ -383,11 +423,15 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory {currentStationData - ? `No history data available for ${TIME_PERIODS.find((p) => p.value === activeTimePeriod)?.label}` + ? `No history data available for ${ + TIME_PERIODS.find( + (p) => p.value === activeTimePeriod + )?.label + }` : "No history data available"} @@ -404,4 +448,3 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory } export default ModalHistory; -