Merge pull request 'debugging' (#29) from debugging into redesign
test-org/trust-module-frontend/pipeline/pr-rc This commit looks good
Details
test-org/trust-module-frontend/pipeline/pr-rc This commit looks good
Details
Reviewed-on: http://git.enode/deployer3000/trust-module-frontend/pulls/29pull/28/head
commit
46484efdea
|
|
@ -11,28 +11,59 @@ const LineChartComponent = ({
|
|||
const [selectionArea, setSelectionArea] = useState(null);
|
||||
const [isSelecting, setIsSelecting] = useState(false);
|
||||
const chartRef = useRef(null);
|
||||
const containerRef = useRef(null);
|
||||
|
||||
const allTimes = Object.values(chartData)
|
||||
const allTimestamps = Object.values(chartData)
|
||||
.flat()
|
||||
.map(point => point.time)
|
||||
.filter((time, index, self) => self.indexOf(time) === index);
|
||||
.map(point => point.timestamp)
|
||||
.filter((timestamp, index, self) => self.indexOf(timestamp) === index)
|
||||
.sort((a, b) => a - b);
|
||||
|
||||
const data = allTimestamps.map(timestamp => {
|
||||
const point = { timestamp };
|
||||
|
||||
const firstPoint = Object.values(chartData)
|
||||
.flat()
|
||||
.find(p => p.timestamp === timestamp);
|
||||
|
||||
if (firstPoint) {
|
||||
point.time = firstPoint.time;
|
||||
point.fullTime = firstPoint.fullTime;
|
||||
}
|
||||
|
||||
const data = allTimes.map(time => {
|
||||
const point = { time };
|
||||
Object.keys(chartData).forEach(key => {
|
||||
const instanceData = chartData[key].find(p => p.time === time);
|
||||
const instanceData = chartData[key].find(p => p.timestamp === timestamp);
|
||||
point[key] = instanceData ? instanceData.value : null;
|
||||
});
|
||||
|
||||
return point;
|
||||
}).sort((a, b) => {
|
||||
const timeA = chartData[Object.keys(chartData)[0]].find(d => d.time === a.time)?.timestamp;
|
||||
const timeB = chartData[Object.keys(chartData)[0]].find(d => d.time === b.time)?.timestamp;
|
||||
return timeA - timeB;
|
||||
});
|
||||
|
||||
const displayData = filteredData || data;
|
||||
|
||||
const instanceKeys = displayData.length
|
||||
? Object.keys(displayData[0]).filter(k => !['timestamp', 'time', 'fullTime'].includes(k))
|
||||
: [];
|
||||
|
||||
// Функция для определения оптимального формата времени в зависимости от диапазона
|
||||
const getTimeFormat = () => {
|
||||
if (!data.length) return 'HH:mm:ss';
|
||||
|
||||
const first = data[0].timestamp;
|
||||
const last = data[data.length - 1].timestamp;
|
||||
const range = last - first;
|
||||
|
||||
// Если диапазон больше 24 часов - показываем дату
|
||||
if (range > 86400000) {
|
||||
return 'dd.MM HH:mm';
|
||||
}
|
||||
// Если больше 1 часа - показываем часы и минуты
|
||||
if (range > 3600000) {
|
||||
return 'HH:mm';
|
||||
}
|
||||
// Для коротких диапазонов - показываем время с секундами
|
||||
return 'HH:mm:ss';
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const handleSelectStart = (e) => {
|
||||
if (isSelecting) {
|
||||
|
|
@ -40,44 +71,66 @@ const LineChartComponent = ({
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
document.addEventListener('selectstart', handleSelectStart);
|
||||
return () => document.removeEventListener('selectstart', handleSelectStart);
|
||||
}, [isSelecting]);
|
||||
|
||||
const handleMouseDown = (e) => {
|
||||
if (!e || !e.activeLabel) return;
|
||||
if (!e) return;
|
||||
|
||||
// Получаем индекс точки по координатам
|
||||
const activeIndex = e.activeTooltipIndex;
|
||||
if (activeIndex === undefined || activeIndex < 0 || activeIndex >= data.length) return;
|
||||
|
||||
setIsSelecting(true);
|
||||
setSelectionArea({ start: e.activeLabel, end: null });
|
||||
setSelectionArea({
|
||||
start: data[activeIndex].timestamp,
|
||||
end: null,
|
||||
startIndex: activeIndex,
|
||||
endIndex: null
|
||||
});
|
||||
};
|
||||
|
||||
const handleMouseMove = (e) => {
|
||||
if (!selectionArea?.start || !e?.activeLabel) return;
|
||||
setSelectionArea(prev => ({ ...prev, end: e.activeLabel }));
|
||||
if (!isSelecting || !selectionArea?.start || !e) return;
|
||||
|
||||
const activeIndex = e.activeTooltipIndex;
|
||||
if (activeIndex === undefined || activeIndex < 0 || activeIndex >= data.length) return;
|
||||
|
||||
setSelectionArea(prev => ({
|
||||
...prev,
|
||||
end: data[activeIndex].timestamp,
|
||||
endIndex: activeIndex
|
||||
}));
|
||||
};
|
||||
|
||||
const handleMouseUp = () => {
|
||||
if (!isSelecting || !selectionArea?.start || !selectionArea?.end) {
|
||||
setIsSelecting(false);
|
||||
|
||||
if (!selectionArea?.start || !selectionArea?.end) {
|
||||
setSelectionArea(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const startIndex = data.findIndex(point => point.time === selectionArea.start);
|
||||
const endIndex = data.findIndex(point => point.time === selectionArea.end);
|
||||
const startIndex = Math.min(selectionArea.startIndex, selectionArea.endIndex);
|
||||
const endIndex = Math.max(selectionArea.startIndex, selectionArea.endIndex);
|
||||
|
||||
// Нормализуем индексы к диапазону [0, 1] для родительского компонента
|
||||
const normalizedStart = startIndex / (data.length - 1);
|
||||
const normalizedEnd = endIndex / (data.length - 1);
|
||||
|
||||
if (startIndex >= 0 && endIndex >= 0) {
|
||||
onRangeSelect({
|
||||
startIndex: Math.min(startIndex, endIndex),
|
||||
endIndex: Math.max(startIndex, endIndex)
|
||||
startIndex: normalizedStart,
|
||||
endIndex: normalizedEnd
|
||||
});
|
||||
}
|
||||
|
||||
setIsSelecting(false);
|
||||
setSelectionArea(null);
|
||||
};
|
||||
|
||||
const CustomTooltip = ({ active, payload, label }) => {
|
||||
if (active && payload && payload.length) {
|
||||
const currentPoint = data.find(point => point.timestamp === label);
|
||||
return (
|
||||
<div style={{
|
||||
backgroundColor: '#fff',
|
||||
|
|
@ -86,7 +139,9 @@ const LineChartComponent = ({
|
|||
borderRadius: '4px',
|
||||
boxShadow: '0 2px 5px rgba(0,0,0,0.1)'
|
||||
}}>
|
||||
<p style={{ fontWeight: 'bold', marginBottom: '5px' }}>{`${label}`}</p>
|
||||
<p style={{ fontWeight: 'bold', marginBottom: '5px' }}>
|
||||
{currentPoint?.fullTime || new Date(label).toLocaleString('ru-RU')}
|
||||
</p>
|
||||
{payload.map((item, index) => (
|
||||
<p key={index} style={{ color: item.color }}>
|
||||
{`Значение: ${item.value}`}
|
||||
|
|
@ -103,20 +158,7 @@ const LineChartComponent = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{ position: 'relative', height: '400px' }}
|
||||
ref={containerRef}
|
||||
className={isSelecting ? 'no-selection' : ''}
|
||||
>
|
||||
<style>
|
||||
{`
|
||||
.no-selection {
|
||||
user-select: none;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
|
||||
<div style={{ position: 'relative', height: '400px' }}>
|
||||
<ResponsiveContainer width="100%" height="100%">
|
||||
<LineChart
|
||||
data={displayData}
|
||||
|
|
@ -128,16 +170,44 @@ const LineChartComponent = ({
|
|||
>
|
||||
<CartesianGrid strokeDasharray="3 3" stroke="#f0f0f0" />
|
||||
<XAxis
|
||||
dataKey="time"
|
||||
tick={{ fontSize: 12 }}
|
||||
dataKey="timestamp"
|
||||
height={75}
|
||||
tick={{ fontSize: 12, angle: -45, textAnchor: 'end' }}
|
||||
interval={Math.max(1, Math.floor(data.length / 10))}
|
||||
tickFormatter={(timestamp) => {
|
||||
const date = new Date(timestamp);
|
||||
const format = getTimeFormat();
|
||||
|
||||
if (format === 'dd.MM HH:mm') {
|
||||
return date.toLocaleString('ru-RU', {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
} else if (format === 'HH:mm') {
|
||||
return date.toLocaleString('ru-RU', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
});
|
||||
} else {
|
||||
return date.toLocaleString('ru-RU', {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
});
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
<YAxis tick={{ fontSize: 12 }} />
|
||||
<Tooltip content={<CustomTooltip />} />
|
||||
{Object.keys(chartData).map((instance, index) => (
|
||||
{instanceKeys.map((instance, index) => (
|
||||
<Line
|
||||
key={instance}
|
||||
type="monotone"
|
||||
dataKey={instance}
|
||||
name={instance}
|
||||
stroke={colors[index % colors.length]}
|
||||
strokeWidth={2}
|
||||
dot={false}
|
||||
|
|
|
|||
|
|
@ -17,39 +17,57 @@ const PrometheusChart = ({ metricName }) => {
|
|||
const [selectedGraphRange, setSelectedGraphRange] = useState(null);
|
||||
const [filteredData, setFilteredData] = useState(null);
|
||||
const [isSelectingRange, setIsSelectingRange] = useState(false);
|
||||
const [lastCustomRange, setLastCustomRange] = useState(null);
|
||||
const intervalRef = useRef(null);
|
||||
const socketRef = useRef(null);
|
||||
const debounceRef = useRef(null);
|
||||
|
||||
const formatTime = useCallback((timestamp, rangeSeconds) => {
|
||||
const date = new Date(timestamp);
|
||||
if (rangeSeconds > 86400) {
|
||||
return {
|
||||
display: date.toLocaleString([], {
|
||||
day: '2-digit',
|
||||
month: '2-digit',
|
||||
year: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit'
|
||||
}),
|
||||
timestamp: timestamp
|
||||
};
|
||||
}
|
||||
return {
|
||||
display: date.toLocaleTimeString([], {
|
||||
const ts = typeof timestamp === 'number' ? timestamp : Date.now();
|
||||
const date = new Date(ts);
|
||||
|
||||
// Определяем формат в зависимости от диапазона
|
||||
const showFullDate = rangeSeconds > 86400; // больше суток
|
||||
|
||||
const timeOptions = {
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit'
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
};
|
||||
|
||||
const dateOptions = showFullDate ? {
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
...timeOptions
|
||||
} : timeOptions;
|
||||
|
||||
return {
|
||||
display: date.toLocaleString('ru-RU', dateOptions),
|
||||
fullDisplay: date.toLocaleString('ru-RU', {
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false
|
||||
}),
|
||||
timestamp: timestamp
|
||||
timestamp: ts
|
||||
};
|
||||
}, []);
|
||||
|
||||
const calculateStep = useCallback((start, end) => {
|
||||
const range = end - start;
|
||||
if (range <= 3600) return 5;
|
||||
if (range <= 21600) return 30;
|
||||
if (range <= 86400) return 120;
|
||||
return 300;
|
||||
if (range <= 60) return 1; // 1 мин
|
||||
if (range <= 300) return 5; // 5 мин
|
||||
if (range <= 1800) return 15; // 30 мин
|
||||
if (range <= 3600) return 30; // 1 час
|
||||
if (range <= 10800) return 60; // 3 часа
|
||||
if (range <= 21600) return 120; // 6 часов
|
||||
if (range <= 43200) return 300; // 12 часов
|
||||
if (range <= 86400) return 600; // 24 часа
|
||||
return 1800; // > 24 часов
|
||||
}, []);
|
||||
|
||||
const fetchData = useCallback(() => {
|
||||
|
|
@ -72,26 +90,8 @@ const PrometheusChart = ({ metricName }) => {
|
|||
}
|
||||
}, [metricName, selectedRange.value, isSelectingRange]);
|
||||
|
||||
const groupBySecond = (points) => {
|
||||
const grouped = [];
|
||||
const timeMap = {};
|
||||
|
||||
points.forEach(point => {
|
||||
const timeKey = Math.floor(point.timestamp / 1000);
|
||||
if (!timeMap[timeKey]) {
|
||||
timeMap[timeKey] = { ...point, count: 1 };
|
||||
grouped.push(timeMap[timeKey]);
|
||||
} else {
|
||||
timeMap[timeKey].value = (timeMap[timeKey].value * timeMap[timeKey].count + point.value) /
|
||||
(timeMap[timeKey].count + 1);
|
||||
timeMap[timeKey].count += 1;
|
||||
}
|
||||
});
|
||||
|
||||
return grouped;
|
||||
};
|
||||
|
||||
const processMetricsData = useCallback((response) => {
|
||||
console.log('Processing metrics data:', response);
|
||||
if (response.metric !== metricName) return;
|
||||
|
||||
const dataArray = Array.isArray(response.data) ? response.data : [response.data];
|
||||
|
|
@ -99,32 +99,43 @@ const PrometheusChart = ({ metricName }) => {
|
|||
|
||||
setChartData(prev => {
|
||||
const newData = { ...(prev || {}) };
|
||||
const rangeSeconds = useCustomRange
|
||||
? (endDate.getTime() - startDate.getTime()) / 1000
|
||||
: selectedRange.value;
|
||||
|
||||
dataArray.forEach(item => {
|
||||
const instance = item.instance || 'default';
|
||||
if (!newData[instance]) newData[instance] = [];
|
||||
|
||||
const timestamp = item.timestamp > 1e12 ? item.timestamp : item.timestamp * 1000;
|
||||
const value = parseFloat(item.value);
|
||||
// Унифицированная конвертация timestamp
|
||||
let timestamp;
|
||||
if (typeof item.timestamp === 'number') {
|
||||
// Определяем, в секундах или миллисекундах пришел timestamp
|
||||
timestamp = item.timestamp > 1e12 ? item.timestamp : item.timestamp * 1000;
|
||||
} else {
|
||||
timestamp = Date.now();
|
||||
}
|
||||
|
||||
const value = parseFloat(item.value);
|
||||
const formattedTime = formatTime(timestamp, rangeSeconds);
|
||||
|
||||
if (!newData[instance].some(p => p.timestamp === timestamp)) {
|
||||
newData[instance].push({
|
||||
time: formatTime(timestamp, selectedRange.value).display,
|
||||
time: formattedTime.display,
|
||||
fullTime: formattedTime.fullDisplay,
|
||||
value: value,
|
||||
timestamp: timestamp
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Сортируем и ограничиваем данные
|
||||
Object.keys(newData).forEach(instance => {
|
||||
newData[instance] = groupBySecond(newData[instance])
|
||||
newData[instance] = newData[instance]
|
||||
.sort((a, b) => a.timestamp - b.timestamp)
|
||||
.slice(-1000);
|
||||
});
|
||||
|
||||
return Object.keys(newData).length ? newData : prev;
|
||||
return newData;
|
||||
});
|
||||
}, [metricName, selectedRange.value, formatTime]);
|
||||
}, [metricName, selectedRange.value, formatTime, useCustomRange, startDate, endDate]);
|
||||
|
||||
const setupWebSocket = useCallback(() => {
|
||||
if (socketRef.current) {
|
||||
|
|
@ -178,7 +189,7 @@ const PrometheusChart = ({ metricName }) => {
|
|||
const fetchCustomRangeData = useCallback(async () => {
|
||||
const start = Math.floor(startDate.getTime() / 1000);
|
||||
const end = Math.floor(endDate.getTime() / 1000);
|
||||
const step = calculateStep(start, end);
|
||||
const rangeSeconds = end - start;
|
||||
|
||||
try {
|
||||
const response = await axios.get(`${import.meta.env.VITE_BACK_URL}/metrics`, {
|
||||
|
|
@ -186,18 +197,21 @@ const PrometheusChart = ({ metricName }) => {
|
|||
metric: metricName,
|
||||
start,
|
||||
end,
|
||||
step
|
||||
step: calculateStep(start, end)
|
||||
}
|
||||
});
|
||||
|
||||
if (response.data) { // Изменили условие, так как бэкенд возвращает массив напрямую
|
||||
if (response.data?.length) {
|
||||
// Преобразуем данные перед передачей в processMetricsData
|
||||
const processedData = response.data.map(item => ({
|
||||
...item,
|
||||
timestamp: item.timestamp, // оставляем в секундах - processMetricsData конвертирует
|
||||
value: item.value.toString()
|
||||
}));
|
||||
|
||||
processMetricsData({
|
||||
metric: metricName,
|
||||
data: response.data.map(item => ({
|
||||
...item,
|
||||
timestamp: item.timestamp / 1000, // или item.timestamp если уже в секундах
|
||||
value: item.value.toString() // преобразуем в строку, как ожидает processMetricsData
|
||||
}))
|
||||
data: processedData
|
||||
});
|
||||
}
|
||||
} catch (error) {
|
||||
|
|
@ -207,6 +221,12 @@ const PrometheusChart = ({ metricName }) => {
|
|||
|
||||
|
||||
const handleRangeChange = useCallback((event) => {
|
||||
// Очищаем текущий интервал
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
|
||||
const selectedValue = event.target.value;
|
||||
const range = TIME_RANGES.find(r => r.value === parseInt(selectedValue, 10));
|
||||
|
||||
|
|
@ -220,50 +240,131 @@ const PrometheusChart = ({ metricName }) => {
|
|||
setEndDate(now);
|
||||
setStartDate(new Date(now.getTime() - range.value * 1000));
|
||||
|
||||
// Переподключение сокета при возврате к стандартным диапазонам
|
||||
// Переподключение сокета
|
||||
if (!socketRef.current?.connected) {
|
||||
socketRef.current?.connect();
|
||||
}
|
||||
}, []);
|
||||
|
||||
const handleCustomRangeChange = useCallback(() => {
|
||||
// Отключаем WebSocket соединение
|
||||
if (socketRef.current?.connected) {
|
||||
socketRef.current.disconnect();
|
||||
setConnectionStatus('disconnected');
|
||||
}
|
||||
|
||||
// Очищаем интервал обновления
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
|
||||
setUseCustomRange(true);
|
||||
setChartData(null);
|
||||
setSelectedGraphRange(null);
|
||||
setFilteredData(null);
|
||||
}, []);
|
||||
fetchCustomRangeData();
|
||||
}, [fetchCustomRangeData]);
|
||||
|
||||
const handleResetZoom = useCallback(() => {
|
||||
setSelectedGraphRange(null);
|
||||
setFilteredData(null);
|
||||
setIsSelectingRange(false);
|
||||
|
||||
if (useCustomRange) {
|
||||
fetchCustomRangeData();
|
||||
} else {
|
||||
if (!socketRef.current?.connected) {
|
||||
socketRef.current?.connect();
|
||||
}
|
||||
fetchData();
|
||||
}, [fetchData]);
|
||||
}
|
||||
|
||||
if (lastCustomRange) {
|
||||
handleRangeSelect(lastCustomRange);
|
||||
return;
|
||||
}
|
||||
}, [fetchData, fetchCustomRangeData, useCustomRange]);
|
||||
|
||||
const interpolateData = useCallback((data, targetPointCount) => {
|
||||
if (!data || data.length < 2) return data;
|
||||
if (data.length >= targetPointCount) return data;
|
||||
|
||||
const interpolated = [];
|
||||
const step = (data.length - 1) / (targetPointCount - 1);
|
||||
|
||||
for (let i = 0; i < targetPointCount; i++) {
|
||||
const index = i * step;
|
||||
const lowerIndex = Math.floor(index);
|
||||
const upperIndex = Math.ceil(index);
|
||||
|
||||
if (lowerIndex === upperIndex) {
|
||||
interpolated.push(data[lowerIndex]);
|
||||
continue;
|
||||
}
|
||||
|
||||
const fraction = index - lowerIndex;
|
||||
const interpolatedPoint = {};
|
||||
|
||||
Object.keys(data[lowerIndex]).forEach(key => {
|
||||
if (key === 'timestamp') {
|
||||
interpolatedPoint[key] = data[lowerIndex][key] +
|
||||
fraction * (data[upperIndex][key] - data[lowerIndex][key]);
|
||||
|
||||
// Добавляем отображаемое время
|
||||
const { display, fullDisplay } = formatTime(interpolatedPoint[key],
|
||||
(endDate - startDate) / 1000);
|
||||
interpolatedPoint.time = display;
|
||||
interpolatedPoint.fullTime = fullDisplay;
|
||||
} else if (typeof data[lowerIndex][key] === 'number') {
|
||||
interpolatedPoint[key] = data[lowerIndex][key] +
|
||||
fraction * (data[upperIndex][key] - data[lowerIndex][key]);
|
||||
} else {
|
||||
interpolatedPoint[key] = data[lowerIndex][key];
|
||||
}
|
||||
});
|
||||
|
||||
interpolated.push(interpolatedPoint);
|
||||
}
|
||||
|
||||
return interpolated;
|
||||
}, []);
|
||||
|
||||
const handleRangeSelect = useCallback((range) => {
|
||||
if (range) {
|
||||
// Начало выделения - останавливаем обновления
|
||||
setLastCustomRange(range);
|
||||
if (!range || !chartData) return;
|
||||
|
||||
setIsSelectingRange(true);
|
||||
setSelectedGraphRange(range);
|
||||
|
||||
// Отключаем сокет
|
||||
// Отключаем автоматические обновления
|
||||
if (socketRef.current?.connected) {
|
||||
socketRef.current.disconnect();
|
||||
}
|
||||
// Очищаем интервал
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
} else {
|
||||
// Окончание выделения - возобновляем соединение
|
||||
setIsSelectingRange(false);
|
||||
|
||||
if (!useCustomRange && socketRef.current && !socketRef.current.connected) {
|
||||
socketRef.current.connect();
|
||||
}
|
||||
}
|
||||
}, [useCustomRange]);
|
||||
// Получаем все точки и сортируем по времени
|
||||
const allPoints = Object.values(chartData).flat();
|
||||
const sortedPoints = allPoints.sort((a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
// Вычисляем абсолютные индексы
|
||||
const startIndex = Math.floor(range.startIndex * (sortedPoints.length - 1));
|
||||
const endIndex = Math.floor(range.endIndex * (sortedPoints.length - 1));
|
||||
|
||||
// Фильтруем точки по выбранному диапазону
|
||||
const filtered = sortedPoints.slice(startIndex, endIndex + 1);
|
||||
|
||||
// Применяем интерполяцию только если точек меньше 100
|
||||
const interpolated = filtered.length < 100 ?
|
||||
interpolateData(filtered, Math.min(100, filtered.length * 3)) :
|
||||
filtered;
|
||||
|
||||
setFilteredData(interpolated);
|
||||
setIsSelectingRange(false);
|
||||
}, [chartData, interpolateData, formatTime]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = setupWebSocket();
|
||||
|
|
@ -273,16 +374,29 @@ const PrometheusChart = ({ metricName }) => {
|
|||
};
|
||||
}, [setupWebSocket]);
|
||||
|
||||
// Обновим useEffect для кастомного диапазона
|
||||
useEffect(() => {
|
||||
if (useCustomRange) {
|
||||
if (socketRef.current?.connected) {
|
||||
socketRef.current.disconnect();
|
||||
}
|
||||
fetchCustomRangeData();
|
||||
return;
|
||||
if (useCustomRange && !isSelectingRange) {
|
||||
// Очищаем предыдущий таймер
|
||||
if (debounceRef.current) {
|
||||
clearTimeout(debounceRef.current);
|
||||
}
|
||||
|
||||
if (!socketRef.current?.connected || isSelectingRange) return;
|
||||
// Устанавливаем новый таймер с задержкой 500 мс
|
||||
debounceRef.current = setTimeout(() => {
|
||||
fetchCustomRangeData();
|
||||
}, 500);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (debounceRef.current) {
|
||||
clearTimeout(debounceRef.current);
|
||||
}
|
||||
};
|
||||
}, [useCustomRange, isSelectingRange, startDate, endDate, fetchCustomRangeData]);
|
||||
|
||||
useEffect(() => {
|
||||
if (useCustomRange || isSelectingRange) return;
|
||||
|
||||
const fetchDataWrapper = () => {
|
||||
try {
|
||||
|
|
@ -292,45 +406,41 @@ const PrometheusChart = ({ metricName }) => {
|
|||
}
|
||||
};
|
||||
|
||||
// Очищаем предыдущий интервал
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
}
|
||||
|
||||
// Запускаем сразу и затем по интервалу
|
||||
fetchDataWrapper();
|
||||
intervalRef.current = setInterval(fetchDataWrapper, selectedRange.interval);
|
||||
|
||||
return () => {
|
||||
if (intervalRef.current) {
|
||||
clearInterval(intervalRef.current);
|
||||
intervalRef.current = null;
|
||||
}
|
||||
};
|
||||
}, [fetchData, fetchCustomRangeData, selectedRange.interval, useCustomRange, isSelectingRange]);
|
||||
}, [fetchData, selectedRange.interval, useCustomRange, isSelectingRange]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartData || !selectedGraphRange) {
|
||||
if (!selectedGraphRange || !chartData) {
|
||||
setFilteredData(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const { startIndex, endIndex } = selectedGraphRange;
|
||||
const allTimes = Object.values(chartData)
|
||||
.flat()
|
||||
.map(point => point.time)
|
||||
.filter((time, index, self) => self.indexOf(time) === index);
|
||||
const allPoints = Object.values(chartData).flat();
|
||||
const sortedPoints = allPoints.sort((a, b) => a.timestamp - b.timestamp);
|
||||
|
||||
const data = allTimes.map(time => {
|
||||
const point = { time };
|
||||
Object.keys(chartData).forEach(key => {
|
||||
const instanceData = chartData[key].find(p => p.time === time);
|
||||
point[key] = instanceData ? instanceData.value : null;
|
||||
});
|
||||
return point;
|
||||
}).sort((a, b) => {
|
||||
const timeA = chartData[Object.keys(chartData)[0]].find(d => d.time === a.time)?.timestamp;
|
||||
const timeB = chartData[Object.keys(chartData)[0]].find(d => d.time === b.time)?.timestamp;
|
||||
return timeA - timeB;
|
||||
});
|
||||
const startIndex = Math.floor(selectedGraphRange.startIndex * (sortedPoints.length - 1));
|
||||
const endIndex = Math.floor(selectedGraphRange.endIndex * (sortedPoints.length - 1));
|
||||
|
||||
const filtered = data.slice(startIndex, endIndex + 1);
|
||||
setFilteredData(filtered);
|
||||
}, [selectedGraphRange, chartData]);
|
||||
const filtered = sortedPoints.slice(startIndex, endIndex + 1);
|
||||
const interpolated = filtered.length > 100 ?
|
||||
interpolateData(filtered, 100) :
|
||||
filtered;
|
||||
|
||||
setFilteredData(interpolated);
|
||||
}, [selectedGraphRange, chartData, interpolateData]);
|
||||
|
||||
if (chartData === null) {
|
||||
return <div style={{ padding: '20px', textAlign: 'center' }}>Loading data...</div>;
|
||||
|
|
@ -373,7 +483,7 @@ const PrometheusChart = ({ metricName }) => {
|
|||
chartData={chartData}
|
||||
metricName={metricName}
|
||||
colors={COLORS}
|
||||
onRangeSelect={handleRangeSelect} // Используем модифицированный обработчик
|
||||
onRangeSelect={handleRangeSelect}
|
||||
filteredData={filteredData}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,6 @@ const getMetricName = (id) => {
|
|||
return `zvks_apiforsnmp_measure_${id}`;
|
||||
};
|
||||
|
||||
//!!!!!!!!!!Пофиксить вкладуи с eth4, во всех eth 1-4 открывается именно 4 !!!!!!!!!!!!!
|
||||
|
||||
// Функция для рекурсивного сбора всех id потомков
|
||||
const getAllChildIds = (node) => {
|
||||
let ids = [];
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import SystemStatusChart from "../../Charts/SystemStatusChart";
|
||||
import TreeTable from "../UI/TreeTable";
|
||||
|
||||
import FlowChart from "../TreeChart/FlowChart";
|
||||
|
||||
const TabContent = ({ activeTab, statusHistories, treeData1, tabContent, handleOpenTab }) => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue