prepared the graph for refactoring and added an indicator
parent
d5aa312104
commit
b9a2be4860
|
|
@ -1,34 +1,26 @@
|
||||||
import React from 'react';
|
const ChartSkeleton = () => (
|
||||||
import { Box, Skeleton } from '@mui/material';
|
<Box sx={{
|
||||||
|
|
||||||
const ChartSkeleton = ({ count = 1 }) => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{Array.from({ length: count }).map((_, index) => (
|
|
||||||
<Box
|
|
||||||
key={index}
|
|
||||||
sx={{
|
|
||||||
backgroundColor: '#fff',
|
backgroundColor: '#fff',
|
||||||
borderRadius: '8px',
|
borderRadius: '8px',
|
||||||
padding: '20px',
|
padding: '20px',
|
||||||
mb: 3,
|
marginBottom: '20px',
|
||||||
height: '400px'
|
position: 'relative'
|
||||||
}}
|
}}>
|
||||||
>
|
<Box sx={{ position: 'absolute', right: '20px', top: '20px' }}>
|
||||||
|
<Skeleton variant="circular" width={16} height={16} />
|
||||||
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between', mb: 2 }}>
|
||||||
<Skeleton variant="text" width="40%" height={30} />
|
<Skeleton variant="text" width="40%" height={30} />
|
||||||
<Skeleton variant="text" width="20%" height={30} />
|
<Skeleton variant="text" width="30%" height={30} />
|
||||||
</Box>
|
</Box>
|
||||||
<Skeleton variant="rectangular" width="100%" height="80%" />
|
|
||||||
|
<Skeleton variant="rectangular" width="100%" height={300} />
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 2, gap: 2 }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', mt: 2, gap: 2 }}>
|
||||||
{[1, 2, 3, 4].map((i) => (
|
{[1, 2, 3, 4].map((_, i) => (
|
||||||
<Skeleton key={i} variant="rounded" width={80} height={36} />
|
<Skeleton key={i} variant="rounded" width={80} height={36} />
|
||||||
))}
|
))}
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
))}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
|
||||||
|
|
||||||
export default ChartSkeleton;
|
|
||||||
|
|
@ -101,15 +101,14 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
|
||||||
const processMetricsData = useCallback((response) => {
|
const processMetricsData = useCallback((response, replace = false) => {
|
||||||
console.log('Processing metrics data:', response);
|
console.log('Processing metrics data:', response);
|
||||||
if (response.metric !== metricName) return;
|
if (response.metric !== metricName) return;
|
||||||
|
|
||||||
const dataArray = Array.isArray(response.data) ? response.data : [response.data];
|
const dataArray = Array.isArray(response.data) ? response.data : [response.data];
|
||||||
if (!dataArray.length) return;
|
if (!dataArray.length) return;
|
||||||
|
|
||||||
setChartData(prev => {
|
const newData = {};
|
||||||
const newData = { ...(prev || {}) };
|
|
||||||
const rangeSeconds = useCustomRange
|
const rangeSeconds = useCustomRange
|
||||||
? (endDate.getTime() - startDate.getTime()) / 1000
|
? (endDate.getTime() - startDate.getTime()) / 1000
|
||||||
: selectedRange.value;
|
: selectedRange.value;
|
||||||
|
|
@ -118,10 +117,8 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
const instance = item.instance || 'default';
|
const instance = item.instance || 'default';
|
||||||
if (!newData[instance]) newData[instance] = [];
|
if (!newData[instance]) newData[instance] = [];
|
||||||
|
|
||||||
// Унифицированная конвертация timestamp
|
|
||||||
let timestamp;
|
let timestamp;
|
||||||
if (typeof item.timestamp === 'number') {
|
if (typeof item.timestamp === 'number') {
|
||||||
// Определяем, в секундах или миллисекундах пришел timestamp
|
|
||||||
timestamp = item.timestamp > 1e12 ? item.timestamp : item.timestamp * 1000;
|
timestamp = item.timestamp > 1e12 ? item.timestamp : item.timestamp * 1000;
|
||||||
} else {
|
} else {
|
||||||
timestamp = Date.now();
|
timestamp = Date.now();
|
||||||
|
|
@ -133,21 +130,34 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
newData[instance].push({
|
newData[instance].push({
|
||||||
time: formattedTime.display,
|
time: formattedTime.display,
|
||||||
fullTime: formattedTime.fullDisplay,
|
fullTime: formattedTime.fullDisplay,
|
||||||
value: value,
|
value,
|
||||||
timestamp: timestamp
|
timestamp
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Сортируем и ограничиваем данные
|
|
||||||
Object.keys(newData).forEach(instance => {
|
Object.keys(newData).forEach(instance => {
|
||||||
newData[instance] = newData[instance]
|
newData[instance] = newData[instance]
|
||||||
.sort((a, b) => a.timestamp - b.timestamp)
|
.sort((a, b) => a.timestamp - b.timestamp)
|
||||||
.slice(-1000);
|
.slice(-1000);
|
||||||
});
|
});
|
||||||
return newData;
|
|
||||||
|
if (replace) {
|
||||||
|
setChartData(newData); // Заменяем полностью
|
||||||
|
} else {
|
||||||
|
setChartData(prev => {
|
||||||
|
const merged = { ...(prev || {}) };
|
||||||
|
Object.keys(newData).forEach(instance => {
|
||||||
|
if (!merged[instance]) merged[instance] = [];
|
||||||
|
merged[instance] = [...merged[instance], ...newData[instance]]
|
||||||
|
.sort((a, b) => a.timestamp - b.timestamp)
|
||||||
|
.slice(-1000);
|
||||||
});
|
});
|
||||||
|
return merged;
|
||||||
|
});
|
||||||
|
}
|
||||||
}, [metricName, selectedRange.value, formatTime, useCustomRange, startDate, endDate]);
|
}, [metricName, selectedRange.value, formatTime, useCustomRange, startDate, endDate]);
|
||||||
|
|
||||||
|
|
||||||
const fetchData = useCallback(() => {
|
const fetchData = useCallback(() => {
|
||||||
if (isSelectingRange) return;
|
if (isSelectingRange) return;
|
||||||
|
|
||||||
|
|
@ -197,7 +207,7 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
processMetricsData({
|
processMetricsData({
|
||||||
metric: metricName,
|
metric: metricName,
|
||||||
data: processedData
|
data: processedData
|
||||||
});
|
}, true);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Ошибка при получении кастомных данных:', error);
|
console.error('Ошибка при получении кастомных данных:', error);
|
||||||
|
|
@ -340,7 +350,9 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Обработчик данных с сервера
|
// Обработчик данных с сервера
|
||||||
const handleMetricsData = (data) => {
|
const handleMetricsData = (data) => {
|
||||||
|
if (!useCustomRange) {
|
||||||
processMetricsData({ metric: metricName, data });
|
processMetricsData({ metric: metricName, data });
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Подписываемся на обновления метрики
|
// Подписываемся на обновления метрики
|
||||||
|
|
@ -359,7 +371,8 @@ const PrometheusChart = ({ metricName }) => {
|
||||||
clearInterval(intervalRef.current);
|
clearInterval(intervalRef.current);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}, [metricName, processMetricsData]);
|
}, [metricName, useCustomRange, processMetricsData]);
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (useCustomRange && !isSelectingRange) {
|
if (useCustomRange && !isSelectingRange) {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
// src/services/WebSocketManager.js
|
|
||||||
import { io } from 'socket.io-client';
|
import { io } from 'socket.io-client';
|
||||||
|
|
||||||
class WebSocketManager {
|
class WebSocketManager {
|
||||||
|
|
@ -7,13 +6,16 @@ class WebSocketManager {
|
||||||
this.subscribers = new Map();
|
this.subscribers = new Map();
|
||||||
this.connectionStatus = 'disconnected';
|
this.connectionStatus = 'disconnected';
|
||||||
this.connectionCallbacks = new Set();
|
this.connectionCallbacks = new Set();
|
||||||
|
this.connecting = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
connect() {
|
connect() {
|
||||||
if (this.socket && (this.socket.connected || this.socket.reconnecting)) {
|
if (this.socket?.connected || this.connecting) {
|
||||||
return this.socket;
|
return this.socket;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.connecting = true;
|
||||||
|
|
||||||
this.socket = io(`${import.meta.env.VITE_BACK_WS_URL}/api/metrics-ws`, {
|
this.socket = io(`${import.meta.env.VITE_BACK_WS_URL}/api/metrics-ws`, {
|
||||||
transports: ['websocket'],
|
transports: ['websocket'],
|
||||||
reconnection: true,
|
reconnection: true,
|
||||||
|
|
@ -24,11 +26,13 @@ class WebSocketManager {
|
||||||
|
|
||||||
this.socket.on('connect', () => {
|
this.socket.on('connect', () => {
|
||||||
this.connectionStatus = 'connected';
|
this.connectionStatus = 'connected';
|
||||||
|
this.connecting = false;
|
||||||
this.notifyConnectionStatus();
|
this.notifyConnectionStatus();
|
||||||
});
|
});
|
||||||
|
|
||||||
this.socket.on('disconnect', (reason) => {
|
this.socket.on('disconnect', (reason) => {
|
||||||
this.connectionStatus = 'disconnected';
|
this.connectionStatus = 'disconnected';
|
||||||
|
this.connecting = false;
|
||||||
this.notifyConnectionStatus();
|
this.notifyConnectionStatus();
|
||||||
if (reason === 'io server disconnect') this.socket.connect();
|
if (reason === 'io server disconnect') this.socket.connect();
|
||||||
});
|
});
|
||||||
|
|
@ -50,7 +54,9 @@ class WebSocketManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
subscribe(metricName, callback) {
|
subscribe(metricName, callback) {
|
||||||
|
if (!this.socket?.connected) {
|
||||||
this.connect();
|
this.connect();
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.subscribers.has(metricName)) {
|
if (!this.subscribers.has(metricName)) {
|
||||||
this.subscribers.set(metricName, new Set());
|
this.subscribers.set(metricName, new Set());
|
||||||
|
|
|
||||||
|
|
@ -1,9 +1,30 @@
|
||||||
import SystemStatusChart from "../../Charts/SystemStatusChart";
|
import SystemStatusChart from "../../Charts/SystemStatusChart";
|
||||||
import TreeTable from "../UI/TreeTable";
|
import TreeTable from "../UI/TreeTable";
|
||||||
import FlowChart from "../TreeChart/FlowChart";
|
import FlowChart from "../TreeChart/FlowChart";
|
||||||
|
import { getStatusColor } from "../TreeChart/dataUtils";
|
||||||
|
|
||||||
|
|
||||||
const TabContent = ({ activeTab, statusHistories, treeData1, tabContent, handleOpenTab }) => {
|
const TabContent = ({ activeTab, statusHistories, treeData1, tabContent, handleOpenTab }) => {
|
||||||
|
// Функция для подсчета количества элементов каждого статуса
|
||||||
|
const countStatuses = (data) => {
|
||||||
|
const counts = { green: 0, yellow: 0, orange: 0, red: 0 };
|
||||||
|
|
||||||
|
const countRecursive = (node) => {
|
||||||
|
if (node.status) {
|
||||||
|
counts[node.status]++;
|
||||||
|
}
|
||||||
|
if (node.items && node.items.length > 0) {
|
||||||
|
node.items.forEach(child => countRecursive(child));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
countRecursive(data);
|
||||||
|
return counts;
|
||||||
|
};
|
||||||
|
|
||||||
if (activeTab === "Главная") {
|
if (activeTab === "Главная") {
|
||||||
|
const statusCounts = treeData1 ? countStatuses(treeData1) : { green: 0, yellow: 0, orange: 0, red: 0 };
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2 style={{ textAlign: 'center' }}>Общий мониторинг состояния системы</h2>
|
<h2 style={{ textAlign: 'center' }}>Общий мониторинг состояния системы</h2>
|
||||||
|
|
@ -17,6 +38,32 @@ const TabContent = ({ activeTab, statusHistories, treeData1, tabContent, handleO
|
||||||
<SystemStatusChart data={statusHistories.history2} />
|
<SystemStatusChart data={statusHistories.history2} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Контейнер для индикаторов статусов */}
|
||||||
|
<div style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'flex-end',
|
||||||
|
marginTop: '20px',
|
||||||
|
gap: '10px'
|
||||||
|
}}>
|
||||||
|
{Object.entries(statusCounts).map(([status, count]) => (
|
||||||
|
<div key={status} style={{
|
||||||
|
width: '30px',
|
||||||
|
height: '30px',
|
||||||
|
backgroundColor: getStatusColor(status),
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
color: 'white',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
borderRadius: '5px',
|
||||||
|
boxShadow: '0 2px 5px rgba(0,0,0,0.2)'
|
||||||
|
}}>
|
||||||
|
{count}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
<label>Статус компонентов системы</label>
|
<label>Статус компонентов системы</label>
|
||||||
<TreeTable data={treeData1} />
|
<TreeTable data={treeData1} />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue