From 2ba35743fd3da33c66e0c25c9957d992f6618111 Mon Sep 17 00:00:00 2001 From: nguyentrungthat <80239428+nguentrungthat@users.noreply.github.com> Date: Fri, 28 Nov 2025 15:40:33 +0700 Subject: [PATCH] Refactor ModalHistory time filtering and UI Adjusted time period logic in ModalHistory to use correct time ranges for filtering history data. Improved code formatting, grouping, and UI rendering for station history. Added validation in backend addHistory to ensure pid and sn are present before saving history items. --- BACKEND/app/services/line_connection.ts | 1 + FRONTEND/src/components/ModalHistory.tsx | 173 ++++++++++++++--------- 2 files changed, 109 insertions(+), 65 deletions(-) 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; -