From a70ad77ae4d9e029033c928fb0265872f784fa77 Mon Sep 17 00:00:00 2001 From: Truong Vo <41848815+vmtruong301296@users.noreply.github.com> Date: Tue, 2 Dec 2025 11:22:17 +0700 Subject: [PATCH] =?UTF-8?q?b=E1=BA=A3ng=20t=E1=BB=95ng=20h=E1=BB=A3p=20th?= =?UTF-8?q?=C3=B4ng=20tin=20history?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FRONTEND/src/components/DragTabs.tsx | 1 + FRONTEND/src/components/ModalHistory.tsx | 125 ++++++++++++++++++++--- 2 files changed, 113 insertions(+), 13 deletions(-) diff --git a/FRONTEND/src/components/DragTabs.tsx b/FRONTEND/src/components/DragTabs.tsx index 5b5635a..2222164 100644 --- a/FRONTEND/src/components/DragTabs.tsx +++ b/FRONTEND/src/components/DragTabs.tsx @@ -364,6 +364,7 @@ export default function DraggableTabs({ onClose={() => setIsHistoryModalOpen(false)} socket={socket} stationIds={tabs.map((el) => el.id)} + tabs={tabs} /> void; socket: Socket | null; stationIds?: number[]; + tabs?: TStation[]; } const TIME_PERIODS = [ @@ -48,6 +50,7 @@ function ModalHistory({ onClose, socket, stationIds = [], + tabs = [], }: ModalHistoryProps) { const [historyData, setHistoryData] = useState([]); const [activeStation, setActiveStation] = useState(""); @@ -65,10 +68,6 @@ function ModalHistory({ // Listen for history response const handleHistoryResponse = (data: StationHistory[]) => { setHistoryData(data); - // Set first station as active by default only if not already set - if (data.length > 0 && !activeStation) { - setActiveStation(data[0].stationId.toString()); - } }; socket.on("list_histories", handleHistoryResponse); @@ -76,7 +75,7 @@ function ModalHistory({ return () => { socket.off("list_histories", handleHistoryResponse); }; - }, [socket, opened, activeStation]); + }, [socket, opened]); // Request history when modal opens useEffect(() => { @@ -87,6 +86,41 @@ function ModalHistory({ // eslint-disable-next-line react-hooks/exhaustive-deps }, [socket, opened]); // Only re-fetch when modal opens/closes, not when stationIds changes + // Set activeStation from mergedHistoryData when data is available + useEffect(() => { + if (tabs.length > 0 || historyData.length > 0) { + // Create merged data to determine first station + const merged: StationHistory[] = [...historyData]; + tabs.forEach((tab) => { + const existingIndex = merged.findIndex((h) => h.stationId === tab.id); + if (existingIndex === -1) { + merged.push({ + stationId: tab.id, + stationName: tab.name, + history: [], + }); + } + }); + + // Sort by tabs order + if (tabs.length > 0) { + merged.sort((a, b) => { + const aIndex = tabs.findIndex((tab) => tab.id === a.stationId); + const bIndex = tabs.findIndex((tab) => tab.id === b.stationId); + if (aIndex !== -1 && bIndex !== -1) return aIndex - bIndex; + if (aIndex !== -1) return -1; + if (bIndex !== -1) return 1; + return 0; + }); + } + + if (merged.length > 0 && !activeStation) { + setActiveStation(merged[0].stationId.toString()); + } + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [historyData.length, tabs.length]); + // Reset state when modal closes useEffect(() => { if (!opened) { @@ -223,19 +257,84 @@ function ModalHistory({ return grouped; }; + // Merge tabs with historyData to ensure all stations from tabs are included + // If a station exists in tabs but not in historyData, add it with empty history + // Keep all stations from historyData, and add missing stations from tabs + // Sort by tabs order if tabs is provided + const mergedHistoryData: StationHistory[] = (() => { + const result: StationHistory[] = [...historyData]; + + if (tabs.length > 0) { + tabs.forEach((tab) => { + const existingIndex = result.findIndex( + (h) => h.stationId === tab.id + ); + if (existingIndex === -1) { + // Station from tabs not found in historyData, add it with empty history + result.push({ + stationId: tab.id, + stationName: tab.name, + history: [], + }); + } else { + // Update station name from tabs if different + result[existingIndex].stationName = tab.name; + } + }); + + // Sort by tabs order to maintain the same order as tabs + result.sort((a, b) => { + const aIndex = tabs.findIndex((tab) => tab.id === a.stationId); + const bIndex = tabs.findIndex((tab) => tab.id === b.stationId); + + // If both are in tabs, sort by tabs order + if (aIndex !== -1 && bIndex !== -1) { + return aIndex - bIndex; + } + // If only a is in tabs, a comes first + if (aIndex !== -1) return -1; + // If only b is in tabs, b comes first + if (bIndex !== -1) return 1; + // If neither is in tabs, keep original order + return 0; + }); + } + + return result; + })(); + // Apply time filter to ALL stations data (không filter bỏ stations không có data) - const allFilteredStations = historyData.map((station) => ({ + const allFilteredStations = mergedHistoryData.map((station) => ({ ...station, filteredHistory: filterHistoryByTime(station.history), })); // Group each station's history by line number - const allGroupedStations = allFilteredStations.map((station) => ({ - ...station, - groupedHistory: station.filteredHistory.length > 0 + // Đảm bảo tất cả lines từ tabs đều được hiển thị (kể cả không có data) + const allGroupedStations = allFilteredStations.map((station) => { + // Get lines from tabs for this station + const tabStation = tabs.find((tab) => tab.id === station.stationId); + const tabLines = tabStation?.lines || []; + + // Group filtered history by line number + const groupedHistory = station.filteredHistory.length > 0 ? groupHistoryByLine(station.filteredHistory) - : new Map(), - })); + : new Map(); + + // Ensure all lines from tabs are included in groupedHistory (even if empty) + tabLines.forEach((line) => { + const lineNumber = line.lineNumber || line.line_number || line.port; + if (lineNumber && !groupedHistory.has(lineNumber)) { + // Add empty line group if not exists + groupedHistory.set(lineNumber, []); + } + }); + + return { + ...station, + groupedHistory, + }; + }); // Function to scroll to a specific station (scroll to beginning of content, not header) const scrollToStation = (stationId: number) => { @@ -357,7 +456,7 @@ function ModalHistory({ - {historyData.map((station) => ( + {mergedHistoryData.map((station) => (