fix
This commit is contained in:
parent
5fb9c14db5
commit
6f8aa1d69b
|
|
@ -48,8 +48,6 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
|
|||
const [activeTimePeriod, setActiveTimePeriod] = useState<string>("current");
|
||||
const scrollViewportRef = useRef<HTMLDivElement>(null);
|
||||
const isAutoSwitchingRef = useRef(false);
|
||||
const lastScrollTopRef = useRef(0);
|
||||
const scrollTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!socket || !opened) return;
|
||||
|
|
@ -88,20 +86,6 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
|
|||
}
|
||||
}, [opened]);
|
||||
|
||||
// Initialize scroll position when content loads
|
||||
useEffect(() => {
|
||||
if (scrollViewportRef.current && historyData.length > 0) {
|
||||
setTimeout(() => {
|
||||
if (scrollViewportRef.current) {
|
||||
const scrollHeight = scrollViewportRef.current.scrollHeight;
|
||||
const clientHeight = scrollViewportRef.current.clientHeight;
|
||||
// Start at middle position
|
||||
scrollViewportRef.current.scrollTop = (scrollHeight - clientHeight) / 2;
|
||||
lastScrollTopRef.current = scrollViewportRef.current.scrollTop;
|
||||
}
|
||||
}, 200);
|
||||
}
|
||||
}, [historyData]);
|
||||
|
||||
// Utility function to format timestamp (can be used later if needed)
|
||||
// const formatTimestamp = (timestamp: number) => {
|
||||
|
|
@ -109,27 +93,27 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
|
|||
// return date.toLocaleString();
|
||||
// };
|
||||
|
||||
// Get time range based on selected period (returns [startTime, endTime])
|
||||
// Get time range based on selected period (cumulative - tích lũy)
|
||||
const getTimeRange = (period: string): [number, number] => {
|
||||
const now = Date.now();
|
||||
const HOUR = 60 * 60 * 1000;
|
||||
|
||||
switch (period) {
|
||||
case "current":
|
||||
// Current: từ 0-4h gần nhất
|
||||
// Current: chỉ hiện tại (4h gần nhất)
|
||||
return [now - 4 * HOUR, now];
|
||||
case "last_4h":
|
||||
// Last 4h: từ 4h-8h trước
|
||||
return [now - 8 * HOUR, now - 4 * HOUR];
|
||||
// Last 4h: bao gồm current + last 4h (0-8h)
|
||||
return [now - 8 * HOUR, now];
|
||||
case "last_8h":
|
||||
// Last 8h: từ 8h-24h trước
|
||||
return [now - 24 * HOUR, now - 8 * HOUR];
|
||||
// Last 8h: bao gồm current + last 4h + last 8h (0-24h)
|
||||
return [now - 24 * HOUR, now];
|
||||
case "last_24h":
|
||||
// Last 24h: từ 24h-48h trước
|
||||
return [now - 48 * HOUR, now - 24 * HOUR];
|
||||
// Last 24h: bao gồm current + last 4h + last 8h + last 24h (0-48h)
|
||||
return [now - 48 * HOUR, now];
|
||||
case "last_48h":
|
||||
// Last 48h: từ 48h trở về trước (tất cả data cũ hơn 48h)
|
||||
return [0, now - 48 * HOUR];
|
||||
// Last 48h: tất cả data (từ đầu đến giờ)
|
||||
return [0, now];
|
||||
default:
|
||||
return [0, now];
|
||||
}
|
||||
|
|
@ -143,6 +127,25 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
|
|||
);
|
||||
};
|
||||
|
||||
// Group history items by line number
|
||||
const groupHistoryByLine = (history: HistoryItem[]): Map<number, HistoryItem[]> => {
|
||||
const grouped = new Map<number, HistoryItem[]>();
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
const currentStationData = historyData.find(
|
||||
(station) => station.stationId.toString() === activeStation
|
||||
);
|
||||
|
|
@ -151,6 +154,9 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
|
|||
const filteredHistory = currentStationData
|
||||
? filterHistoryByTime(currentStationData.history)
|
||||
: [];
|
||||
|
||||
// Group filtered history by line number
|
||||
const groupedHistory = groupHistoryByLine(filteredHistory);
|
||||
|
||||
if (!opened) return null;
|
||||
|
||||
|
|
@ -282,134 +288,96 @@ function ModalHistory({ opened, onClose, socket, stationIds = [] }: ModalHistory
|
|||
h="calc(75vh - 80px)"
|
||||
viewportRef={scrollViewportRef}
|
||||
onScrollPositionChange={(position) => {
|
||||
if (!scrollViewportRef.current) return;
|
||||
if (!scrollViewportRef.current || isAutoSwitchingRef.current) return;
|
||||
|
||||
const scrollTop = position.y;
|
||||
const target = scrollViewportRef.current;
|
||||
const scrollHeight = target.scrollHeight;
|
||||
const clientHeight = target.clientHeight;
|
||||
|
||||
// Clear existing timeout
|
||||
if (scrollTimeoutRef.current) {
|
||||
clearTimeout(scrollTimeoutRef.current);
|
||||
}
|
||||
// Check if scrolled to bottom
|
||||
const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 5;
|
||||
|
||||
// Debounce scroll handling
|
||||
scrollTimeoutRef.current = setTimeout(() => {
|
||||
if (isAutoSwitchingRef.current) {
|
||||
return;
|
||||
}
|
||||
if (isAtBottom && historyData.length > 0) {
|
||||
isAutoSwitchingRef.current = true;
|
||||
|
||||
// Determine scroll direction
|
||||
const isScrollingUp = scrollTop < lastScrollTopRef.current;
|
||||
const isScrollingDown = scrollTop > lastScrollTopRef.current;
|
||||
|
||||
// Update last scroll position AFTER checking direction
|
||||
lastScrollTopRef.current = scrollTop;
|
||||
const currentStationIndex = historyData.findIndex(
|
||||
(station) => station.stationId.toString() === activeStation
|
||||
);
|
||||
|
||||
// Check if at absolute top or bottom (no spacer needed)
|
||||
const isAtTop = scrollTop === 0;
|
||||
const isAtBottom = Math.abs(scrollTop + clientHeight - scrollHeight) < 5;
|
||||
|
||||
// Can only trigger one direction at a time
|
||||
const canScrollUp = isAtTop && isScrollingUp && historyData.length > 0;
|
||||
const canScrollDown = isAtBottom && isScrollingDown && historyData.length > 0;
|
||||
|
||||
// Handle scroll up - khi scroll lên đến đầu
|
||||
if (canScrollUp) {
|
||||
isAutoSwitchingRef.current = true;
|
||||
|
||||
const currentPeriodIndex = TIME_PERIODS.findIndex(
|
||||
(p) => p.value === activeTimePeriod
|
||||
);
|
||||
|
||||
if (currentPeriodIndex > 0) {
|
||||
setActiveTimePeriod(TIME_PERIODS[currentPeriodIndex - 1].value);
|
||||
} else {
|
||||
const currentStationIndex = historyData.findIndex(
|
||||
(station) => station.stationId.toString() === activeStation
|
||||
);
|
||||
|
||||
if (currentStationIndex > 0) {
|
||||
setActiveStation(historyData[currentStationIndex - 1].stationId.toString());
|
||||
setActiveTimePeriod(TIME_PERIODS[TIME_PERIODS.length - 1].value);
|
||||
} else {
|
||||
isAutoSwitchingRef.current = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (currentStationIndex !== -1 && currentStationIndex < historyData.length - 1) {
|
||||
// Chuyển sang station tiếp theo
|
||||
setActiveStation(historyData[currentStationIndex + 1].stationId.toString());
|
||||
|
||||
// Scroll to bottom of new content
|
||||
setTimeout(() => {
|
||||
if (scrollViewportRef.current) {
|
||||
const newScrollHeight = scrollViewportRef.current.scrollHeight;
|
||||
const newClientHeight = scrollViewportRef.current.clientHeight;
|
||||
scrollViewportRef.current.scrollTop = newScrollHeight - newClientHeight;
|
||||
lastScrollTopRef.current = scrollViewportRef.current.scrollTop;
|
||||
}
|
||||
setTimeout(() => {
|
||||
isAutoSwitchingRef.current = false;
|
||||
}, 100);
|
||||
}, 150);
|
||||
}
|
||||
// Handle scroll down - khi scroll xuống đến cuối
|
||||
else if (canScrollDown) {
|
||||
isAutoSwitchingRef.current = true;
|
||||
|
||||
const currentPeriodIndex = TIME_PERIODS.findIndex(
|
||||
(p) => p.value === activeTimePeriod
|
||||
);
|
||||
|
||||
if (currentPeriodIndex < TIME_PERIODS.length - 1) {
|
||||
setActiveTimePeriod(TIME_PERIODS[currentPeriodIndex + 1].value);
|
||||
} else {
|
||||
const currentStationIndex = historyData.findIndex(
|
||||
(station) => station.stationId.toString() === activeStation
|
||||
);
|
||||
|
||||
if (currentStationIndex !== -1 && currentStationIndex < historyData.length - 1) {
|
||||
setActiveStation(historyData[currentStationIndex + 1].stationId.toString());
|
||||
setActiveTimePeriod(TIME_PERIODS[0].value);
|
||||
} else {
|
||||
isAutoSwitchingRef.current = false;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Scroll to top of new content
|
||||
// Reset scroll to top
|
||||
setTimeout(() => {
|
||||
if (scrollViewportRef.current) {
|
||||
scrollViewportRef.current.scrollTop = 0;
|
||||
lastScrollTopRef.current = 0;
|
||||
}
|
||||
setTimeout(() => {
|
||||
isAutoSwitchingRef.current = false;
|
||||
}, 100);
|
||||
}, 150);
|
||||
} else {
|
||||
isAutoSwitchingRef.current = false;
|
||||
}
|
||||
}, 100); // Debounce 100ms
|
||||
}
|
||||
}}
|
||||
>
|
||||
<Box>
|
||||
{filteredHistory.length > 0 ? (
|
||||
<Box style={{ minHeight: "calc(75vh - 60px)" }}>
|
||||
{groupedHistory.size > 0 ? (
|
||||
<>
|
||||
{filteredHistory.map((item, index) => (
|
||||
{Array.from(groupedHistory.entries())
|
||||
.sort(([lineA], [lineB]) => lineA - lineB)
|
||||
.map(([lineNumber, items]) => (
|
||||
<Box
|
||||
key={`${item.stationId}-${item.number}-${item.timestamp}-${index}`}
|
||||
key={`line-group-${lineNumber}`}
|
||||
style={{
|
||||
padding: "10px 16px",
|
||||
borderBottom: "1px solid #dee2e6",
|
||||
backgroundColor: index % 2 === 0 ? "#f8f9fa" : "white",
|
||||
marginBottom: "8px",
|
||||
border: "1px solid #dee2e6",
|
||||
borderRadius: "4px",
|
||||
overflow: "hidden",
|
||||
}}
|
||||
>
|
||||
<Text size="sm" style={{ fontFamily: "monospace" }}>
|
||||
Line {item.number}: {item.pid} {item.vid} SN: {item.sn}
|
||||
<span style={{ marginLeft: "20px", color: "#868e96" }}>
|
||||
| {item.scenario}
|
||||
</span>
|
||||
</Text>
|
||||
{/* Header của nhóm - hiển thị line number */}
|
||||
<Box
|
||||
style={{
|
||||
padding: "8px 16px",
|
||||
backgroundColor: "#e9ecef",
|
||||
fontWeight: 600,
|
||||
}}
|
||||
>
|
||||
<Text size="sm" fw={600}>
|
||||
Line {lineNumber} ({items.length} {items.length > 1 ? 'records' : 'record'})
|
||||
</Text>
|
||||
</Box>
|
||||
|
||||
{/* Các items trong nhóm */}
|
||||
{items.map((item, itemIndex) => (
|
||||
<Box
|
||||
key={`${item.stationId}-${item.number}-${item.timestamp}-${itemIndex}`}
|
||||
style={{
|
||||
padding: "8px 16px 8px 32px", // Tăng padding-left lên 32px
|
||||
borderTop: itemIndex > 0 ? "1px solid #f1f3f5" : "none",
|
||||
backgroundColor: itemIndex % 2 === 0 ? "white" : "#f8f9fa",
|
||||
}}
|
||||
>
|
||||
<Text size="sm" style={{ fontFamily: "monospace" }}>
|
||||
{item.pid} {item.vid} SN: {item.sn}
|
||||
<span style={{ marginLeft: "20px", color: "#868e96" }}>
|
||||
| {item.scenario}
|
||||
</span>
|
||||
<span style={{ marginLeft: "10px", color: "#adb5bd", fontSize: "11px" }}>
|
||||
{new Date(item.timestamp).toLocaleString()}
|
||||
</span>
|
||||
</Text>
|
||||
</Box>
|
||||
))}
|
||||
</Box>
|
||||
))}
|
||||
|
||||
{/* Spacer để đảm bảo có thể scroll ngay cả khi content ít */}
|
||||
<Box style={{ height: "200px" }} />
|
||||
</>
|
||||
) : (
|
||||
<Flex
|
||||
|
|
|
|||
Loading…
Reference in New Issue