Merge branch 'main' into that

This commit is contained in:
nguyentrungthat 2025-12-02 14:23:43 +07:00
commit 68b217f769
2 changed files with 113 additions and 13 deletions

View File

@ -364,6 +364,7 @@ export default function DraggableTabs({
onClose={() => setIsHistoryModalOpen(false)}
socket={socket}
stationIds={tabs.map((el) => el.id)}
tabs={tabs}
/>
<ModalConfig

View File

@ -9,6 +9,7 @@ import {
} from "@mantine/core";
import classes from "./Component.module.css";
import type { Socket } from "socket.io-client";
import type { TStation } from "../untils/types";
interface HistoryItem {
id: number;
@ -32,6 +33,7 @@ interface ModalHistoryProps {
onClose: () => 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<StationHistory[]>([]);
const [activeStation, setActiveStation] = useState<string>("");
@ -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<number, HistoryItem[]>(),
}));
: new Map<number, HistoryItem[]>();
// 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({
</Text>
<ScrollArea h="calc(100% - 30px)">
<Flex direction="column" gap="xs">
{historyData.map((station) => (
{mergedHistoryData.map((station) => (
<Button
key={station.stationId}
fullWidth
@ -717,7 +816,7 @@ function ModalHistory({
style={{ height: "calc(75vh - 80px)" }}
>
<Text c="dimmed" size="lg">
{historyData.length > 0
{mergedHistoryData.length > 0
? `No history data available for ${
TIME_PERIODS.find(
(p) => p.value === activeTimePeriod