diff --git a/src/Charts2/Components/DateRangeSelector.jsx b/src/Charts2/Components/DateRangeSelector.jsx
new file mode 100644
index 0000000..4cec48e
--- /dev/null
+++ b/src/Charts2/Components/DateRangeSelector.jsx
@@ -0,0 +1,97 @@
+import React from 'react';
+import DatePicker from 'react-datepicker';
+import 'react-datepicker/dist/react-datepicker.css';
+
+const DateRangeSelector = ({
+ startDate,
+ endDate,
+ onStartDateChange,
+ onEndDateChange,
+ onApply
+}) => {
+ return (
+
+
+ Укажите диапазон дат:
+
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+
+ );
+};
+
+export default DateRangeSelector;
\ No newline at end of file
diff --git a/src/Charts2/Components/LineChartComponent.jsx b/src/Charts2/Components/LineChartComponent.jsx
new file mode 100644
index 0000000..4a5cea5
--- /dev/null
+++ b/src/Charts2/Components/LineChartComponent.jsx
@@ -0,0 +1,68 @@
+import React from 'react';
+import { LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer } from 'recharts';
+
+const LineChartComponent = ({
+ data,
+ title,
+ description,
+ metaInfo,
+ dataKey = 'value',
+ lineColor = '#8884d8',
+ height = 400,
+ showLegend = true,
+ showGrid = true,
+ customTooltip,
+ customXAxisFormatter,
+ customYAxis,
+ additionalLines = []
+}) => {
+ return (
+
+ {title &&
{title}
}
+ {description && (
+
{description}
+ )}
+ {metaInfo && (
+
+ {metaInfo}
+
+ )}
+
+
+
+ {showGrid && }
+ new Date(timestamp).toLocaleTimeString())}
+ />
+ {customYAxis || }
+ new Date(timestamp).toLocaleString()}
+ />
+ {showLegend && }
+
+ {additionalLines.map((lineProps, index) => (
+
+ ))}
+
+
+
+ );
+};
+
+export default LineChartComponent;
\ No newline at end of file
diff --git a/src/Charts2/Components/metricsService.jsx b/src/Charts2/Components/metricsService.jsx
new file mode 100644
index 0000000..644c14a
--- /dev/null
+++ b/src/Charts2/Components/metricsService.jsx
@@ -0,0 +1,142 @@
+import { io } from 'socket.io-client';
+
+class MetricsService {
+ constructor(baseUrl) {
+ console.log('MetricsService constructor');
+ this.baseUrl = baseUrl || window.location.origin;
+ this.socket = null;
+ this.subscriptions = new Map();
+ }
+
+ // HTTP методы - адаптированы под ваш бэкенд
+ async fetchMetricsRange(metric, start, end, step = 15) {
+ try {
+ // Формируем URL согласно вашему API
+ const url = new URL(`${this.baseUrl}/api/metrics`);
+ url.searchParams.append('metric', metric);
+ url.searchParams.append('start', start);
+ url.searchParams.append('end', end);
+ url.searchParams.append('step', step);
+
+ console.log('Fetching metrics range from:', url.toString());
+
+ const response = await fetch(url.toString());
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
+ }
+
+ const data = await response.json();
+
+ // Проверяем формат данных
+ if (!Array.isArray(data)) {
+ console.error('Unexpected data format:', data);
+ throw new Error('Invalid data format: expected array');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Error in fetchMetricsRange:', error);
+ throw error;
+ }
+ }
+
+ async fetchMetrics(metric) {
+ try {
+ // Формируем URL для текущих метрик
+ const url = new URL(`${this.baseUrl}/api/metrics`);
+ url.searchParams.append('metric', metric);
+
+ console.log('Fetching current metrics from:', url.toString());
+
+ const response = await fetch(url.toString());
+
+ if (!response.ok) {
+ const errorText = await response.text();
+ throw new Error(`HTTP ${response.status}: ${errorText}`);
+ }
+
+ const data = await response.json();
+
+ // Проверяем формат данных
+ if (!Array.isArray(data)) {
+ console.error('Unexpected data format:', data);
+ throw new Error('Invalid data format: expected array');
+ }
+
+ return data;
+ } catch (error) {
+ console.error('Error in fetchMetrics:', error);
+ throw error;
+ }
+ }
+
+ // WebSocket методы - остаются без изменений
+ connectWebSocket() {
+ if (this.socket && this.socket.connected) return;
+ console.trace('connectWebSocket called');
+ this.socket = io(`${this.baseUrl}/api/metrics-ws`, {
+ transports: ['websocket'],
+ withCredentials: true,
+ });
+
+ this.socket.on('connect', () => {
+ console.log('Socket.IO connected');
+ // Подписаться заново на все метрики
+ for (const [metric, callbacks] of this.subscriptions.entries()) {
+ this.socket.emit('subscribe-metric', { metric });
+ }
+ });
+
+ this.socket.on('disconnect', () => {
+ console.log('Socket.IO disconnected');
+ });
+
+ this.socket.on('metrics-data', ({ metric, data }) => {
+ const callbacks = this.subscriptions.get(metric) || [];
+ callbacks.forEach(cb => cb(data));
+ });
+
+ this.socket.on('metrics-error', payload => {
+ console.error('Metrics error:', payload);
+ });
+ }
+
+ subscribeToMetric(metric, callback, interval = 5000) {
+ this.connectWebSocket();
+
+ if (!this.subscriptions.has(metric)) {
+ this.subscriptions.set(metric, []);
+ this.socket.emit('subscribe-metric', { metric, interval });
+ }
+
+ this.subscriptions.get(metric).push(callback);
+
+ return () => this.unsubscribeFromMetric(metric, callback);
+ }
+
+
+ unsubscribeFromMetric(metric, callback) {
+ const callbacks = this.subscriptions.get(metric) || [];
+ const filtered = callbacks.filter(cb => cb !== callback);
+
+ if (filtered.length === 0) {
+ this.subscriptions.delete(metric);
+ if (this.socket && this.socket.connected) {
+ this.socket.emit('unsubscribe-metric', { metric });
+ }
+ } else {
+ this.subscriptions.set(metric, filtered);
+ }
+ }
+
+
+ disconnectWebSocket() {
+ if (this.socket) {
+ this.socket.close();
+ }
+ }
+}
+
+export const metricsService = new MetricsService(import.meta.env.VITE_BACK_URL);
\ No newline at end of file
diff --git a/src/Charts2/PrometheusChart.jsx b/src/Charts2/PrometheusChart.jsx
new file mode 100644
index 0000000..24c37a7
--- /dev/null
+++ b/src/Charts2/PrometheusChart.jsx
@@ -0,0 +1,169 @@
+import React, { useState, useEffect } from 'react';
+import LineChartComponent from './Components/LineChartComponent';
+import DateRangeSelector from './Components/DateRangeSelector';
+import { metricsService } from './Components/metricsService';
+import { Button, Radio, message } from 'antd';
+import moment from 'moment';
+
+const PrometheusChart = ({ metricName, chartHeight = 560 }) => {
+ const [chartData, setChartData] = useState([]);
+ const [isLoading, setIsLoading] = useState(true);
+ const [error, setError] = useState(null);
+ const [metricInfo, setMetricInfo] = useState({});
+ const [mode, setMode] = useState('realtime');
+ const [startDate, setStartDate] = useState(moment().subtract(1, 'hour').toDate());
+ const [endDate, setEndDate] = useState(moment().toDate());
+ const [isLiveUpdating, setIsLiveUpdating] = useState(false);
+
+ const fetchHistoricalData = async (start, end) => {
+ setIsLoading(true);
+ setError(null);
+
+ try {
+ const startUnix = Math.floor(new Date(start).getTime() / 1000);
+ const endUnix = Math.floor(new Date(end).getTime() / 1000);
+
+ const data = await metricsService.fetchMetricsRange(metricName, startUnix, endUnix, 15);
+
+ const dataArray = Array.isArray(data) ? data : [data];
+ const formattedData = dataArray.map(item => ({
+ timestamp: item.timestamp,
+ value: parseFloat(item.value),
+ name: item.__name__ || metricName,
+ status: item.status
+ }));
+
+ if (dataArray.length > 0) {
+ setMetricInfo({
+ type: dataArray[0].type,
+ description: dataArray[0].description,
+ instance: dataArray[0].instance,
+ job: dataArray[0].job
+ });
+ }
+
+ setChartData(formattedData);
+ } catch (err) {
+ console.error(`Error loading historical data for ${metricName}:`, err);
+ setError(err.message);
+ message.error(`Failed to load historical data: ${err.message}`);
+ } finally {
+ setIsLoading(false);
+ }
+ };
+
+ const startRealtimeUpdates = () => {
+ setIsLiveUpdating(true);
+ setIsLoading(true);
+
+ const end = new Date();
+ const start = new Date(end.getTime() - 3600 * 1000);
+ fetchHistoricalData(start, end).finally(() => {
+ setIsLoading(false);
+ });
+
+ return metricsService.subscribeToMetric(
+ metricName,
+ (newData) => {
+ const newDataArray = Array.isArray(newData) ? newData : [newData];
+ const formattedNewData = newDataArray.map(item => ({
+ timestamp: item.timestamp,
+ value: parseFloat(item.value),
+ name: item.__name__ || metricName,
+ status: item.status
+ }));
+
+ setChartData(prevData => [...prevData, ...formattedNewData].slice(-200));
+ },
+ 5000
+ );
+ };
+
+ const stopRealtimeUpdates = () => {
+ setIsLiveUpdating(false);
+ metricsService.unsubscribeFromMetric(metricName);
+ };
+
+ const handleCustomRangeApply = () => {
+ if (startDate && endDate) {
+ fetchHistoricalData(startDate, endDate);
+ }
+ };
+
+ useEffect(() => {
+ let unsubscribe;
+
+ if (mode === 'realtime') {
+ unsubscribe = startRealtimeUpdates();
+ } else {
+ stopRealtimeUpdates();
+ fetchHistoricalData(startDate, endDate);
+ }
+
+ return () => {
+ if (unsubscribe) unsubscribe();
+ stopRealtimeUpdates();
+ };
+ }, [mode, metricName]);
+
+ const metaInfo = [
+ metricInfo.instance && `Instance: ${metricInfo.instance}`,
+ metricInfo.job && `Job: ${metricInfo.job}`,
+ metricInfo.type && `Type: ${metricInfo.type}`
+ ].filter(Boolean).join(' | ');
+
+ return (
+
+
+ setMode(e.target.value)}
+ buttonStyle="solid"
+ style={{ marginBottom: 10 }}
+ >
+ Режим реального времени
+ Исторические данные
+
+
+ {mode === 'historical' && (
+
+ )}
+
+ {mode === 'realtime' && isLiveUpdating && (
+
+ )}
+
+
+ {isLoading ? (
+
Loading chart data...
+ ) : error ? (
+
Error loading metric: {error}
+ ) : chartData.length === 0 ? (
+
No data available for {metricName}
+ ) : (
+
+ )}
+
+ );
+};
+
+export default PrometheusChart;
\ No newline at end of file
diff --git a/src/Components/Layout/SidebarMenuComponents/MenuItem.jsx b/src/Components/Layout/SidebarMenuComponents/MenuItem.jsx
index 6d0eba6..636a973 100644
--- a/src/Components/Layout/SidebarMenuComponents/MenuItem.jsx
+++ b/src/Components/Layout/SidebarMenuComponents/MenuItem.jsx
@@ -15,7 +15,7 @@ import { getStatusColor } from "../../TreeChart/dataUtils";
const StyledListItem = styled(ListItem)(({ theme, level }) => ({
cursor: "pointer",
paddingLeft: theme.spacing(2 + level * 2),
- position: 'relative', // Добавляем для позиционирования индикатора
+ position: 'relative',
'&:hover': {
backgroundColor: theme.palette.action.hover,
},
diff --git a/src/Components/TreeChart/FlowChart.jsx b/src/Components/TreeChart/FlowChart.jsx
index bf8fe4a..0b0dd09 100644
--- a/src/Components/TreeChart/FlowChart.jsx
+++ b/src/Components/TreeChart/FlowChart.jsx
@@ -45,12 +45,10 @@ const FlowChart = ({ data }) => {
const findAndCollapseLastLevelParents = (items) => {
items.forEach(item => {
if (item.items && item.items.length > 0) {
- // Проверяем, есть ли у детей свои дети
const hasGrandchildren = item.items.some(child =>
child.items && child.items.length > 0
);
- // Если у детей нет своих детей - это родители последнего уровня
if (!hasGrandchildren) {
toggleNodeCollapse(item.id);
} else {
diff --git a/src/Components/TreeChart/FlowChartComponents/DataParser.jsx b/src/Components/TreeChart/FlowChartComponents/DataParser.jsx
index 916bc97..92717f6 100644
--- a/src/Components/TreeChart/FlowChartComponents/DataParser.jsx
+++ b/src/Components/TreeChart/FlowChartComponents/DataParser.jsx
@@ -39,7 +39,7 @@ export const useDataParser = (nodePositions, collapsedNodes) => {
const baseLevelRadius = 150;
const traverse = (item, parentId = null, level = 0, angleStart = 0, angleEnd = 2 * Math.PI, parentRadius = 0) => {
- if (!item || collapsedNodes[parentId]) return; // Пропускаем свёрнутые узлы
+ if (!item || collapsedNodes[parentId]) return;
const nodeId = item.id;
const items = item.items || [];
@@ -58,7 +58,7 @@ export const useDataParser = (nodePositions, collapsedNodes) => {
data: {
...item,
label: item.title,
- style: getNodeStyle(item, isLeaf), // Переносим стили в data
+ style: getNodeStyle(item, isLeaf),
hasChildren: items.length > 0,
collapsed: collapsedNodes[nodeId]
}
@@ -88,7 +88,7 @@ export const useDataParser = (nodePositions, collapsedNodes) => {
const centerNode = {
id: data.id,
- type: 'customNode', // Добавляем тип узла
+ type: 'customNode',
position: nodePositions[data.id] || { x: centerX, y: centerY },
style: getCenterNodeStyle(data),
data: { label: data.title, hasChildren: data.items.length > 0, collapsed: collapsedNodes[data.id] }
diff --git a/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx b/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx
index d64d391..52ccd94 100644
--- a/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx
+++ b/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx
@@ -10,13 +10,13 @@ const NodeWrapper = memo(({ id, data, selected }) => {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
- overflow: 'hidden', // Чтобы текст не выходил за границы
- textOverflow: 'ellipsis', // Добавляем многоточие если текст не помещается
- whiteSpace: 'nowrap', // Запрещаем перенос строк
- padding: '0 8px', // Горизонтальный padding для текста
- boxSizing: 'border-box' // Учитываем padding в общей ширине
+ overflow: 'hidden',
+ textOverflow: 'ellipsis',
+ whiteSpace: 'nowrap',
+ padding: '0 8px',
+ boxSizing: 'border-box'
}}
- title={data.label} // Простой tooltip при наведении
+ title={data.label}
>
{/* Хендл для входящих соединений */}
{
const [nodes, setNodes, onNodesChange] = useNodesState([]);
const [edges, setEdges, onEdgesChange] = useEdgesState([]);
const [nodePositions, setNodePositions] = useState({});
- const [collapsedNodes, setCollapsedNodes] = useState({}); // Добавили
+ const [collapsedNodes, setCollapsedNodes] = useState({});
const toggleNodeCollapse = useCallback((nodeId) => {
setCollapsedNodes((prev) => ({
diff --git a/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx b/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx
index 80e4fd0..efc6480 100644
--- a/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx
+++ b/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx
@@ -2,7 +2,6 @@ import { useCallback } from 'react';
export const useNodeHandlers = (debouncedSetNodePositions) => {
const onNodeDrag = useCallback((event, node) => {
- // Фиксируем позицию сразу при перемещении
node.position = {
x: Math.round(node.position.x),
y: Math.round(node.position.y)
diff --git a/src/Components/TreeChart/dataUtils.jsx b/src/Components/TreeChart/dataUtils.jsx
index 1a1c70d..b9cd7e0 100755
--- a/src/Components/TreeChart/dataUtils.jsx
+++ b/src/Components/TreeChart/dataUtils.jsx
@@ -1,48 +1,43 @@
const StatusManager = () => {
const getRandomStatus = () => {
const statuses = [
- ...Array(90).fill("green"), // 90% шанс
- ...Array(6).fill("yellow"), // 6% шанс
- ...Array(3).fill("orange"), // 3% шанс
- ...Array(1).fill("red"), // 1% шанс
+ ...Array(90).fill("green"),
+ ...Array(6).fill("yellow"),
+ ...Array(3).fill("orange"),
+ ...Array(1).fill("red"),
];
return statuses[Math.floor(Math.random() * statuses.length)];
};
const getStatusWeight = (status) => {
switch (status) {
- case "green": return 1; // 100% здоровья
+ case "green": return 1;
case "yellow": return 0.75;
case "orange": return 0.5;
- case "red": return 0.25; // 25% здоровья
- default: return 1; // По умолчанию "green"
+ case "red": return 0.25;
+ default: return 1;
}
};
const updateStatuses = (data) => {
if (!data.items || data.items.length === 0) {
- // Если это элемент нижнего уровня, генерируем случайный статус
data.status = getRandomStatus();
return getStatusWeight(data.status);
}
- // Рекурсивно обновляем статусы для всех дочерних элементов
let childStatusWeights = data.items.map((child) => updateStatuses(child));
- // Проверяем, есть ли дочерние элементы (избегаем деления на 0)
if (childStatusWeights.length === 0) {
data.status = "green";
return 1;
}
- // Вычисляем среднее арифметическое значение весов статусов
const averageStatusWeight =
childStatusWeights.reduce((sum, weight) => sum + weight, 0) / childStatusWeights.length;
- // Определяем статус текущего элемента
data.status = getStatusFromWeight(averageStatusWeight);
- return Math.max(0, averageStatusWeight); // Гарантия, что не будет отрицательных значений
+ return Math.max(0, averageStatusWeight);
};
const getStatusFromWeight = (weight) => {
@@ -69,16 +64,13 @@ const StatusManager = () => {
};
};
-// Создаем два независимых менеджера статусов
export const statusManager1 = StatusManager();
export const statusManager2 = StatusManager();
-// Функция для расчета процентов здоровья системы
export const calculateStatusPercentage = (averageStatusValue) => {
return Math.max(0, Math.min(100, averageStatusValue * 100));
};
-// Экспортируем getStatusColor отдельно
export const getStatusColor = (status) => {
switch (status) {
case "green":
diff --git a/src/Components/TreeChart/menuData.json b/src/Components/TreeChart/menuData.json
index 1d4c364..fcbdd2f 100755
--- a/src/Components/TreeChart/menuData.json
+++ b/src/Components/TreeChart/menuData.json
@@ -400,182 +400,136 @@
"items": [
{
"id": "182",
- "title": "Graviton S2082I (device$18)",
+ "title": "Graviton S2082I (device$19)",
"items": [
{
"id": "42",
- "title": "OS Linux (module$4) АО",
+ "title": "OS Linux (module$6) АО",
"items": [
{
- "id": "1902",
+ "id": "371",
"title": "Загрузка процессора за 1 минуту"
},
{
- "id": "1912",
+ "id": "372",
"title": "Загрузка процессора за 5 минут"
},
{
- "id": "1922",
+ "id": "373",
"title": "Загрузка процессора за 15 минут"
},
{
- "id": "1972",
+ "id": "378",
"title": "Общий объем SWAP-файла"
},
{
- "id": "1982",
+ "id": "379",
"title": "Используемый объем SWAP-файла"
},
{
- "id": "1992",
+ "id": "380",
"title": "Общий объем физической оперативной памяти"
},
{
- "id": "2002",
+ "id": "381",
"title": "Доступный объем физической оперативной памяти"
},
{
- "id": "2012",
+ "id": "382",
"title": "Свободный объем физической и виртуальной оперативной памяти"
},
{
- "id": "2022",
+ "id": "383",
"title": "Буферизованный объем оперативной памяти"
},
{
- "id": "2032",
+ "id": "384",
"title": "Кэшированый объем оперативной памяти"
},
{
- "id": "2742",
- "title": "Используемый объем SWAP-файла"
- },
- {
- "id": "2752",
+ "id": "375",
"title": "Время затраченное процессором на процессы с пониженным приоритетом"
},
{
- "id": "2762",
+ "id": "376",
"title": "Время затраченное процессором на процессы ядра ОС"
},
{
- "id": "2772",
+ "id": "377",
"title": "Время простоя процессора"
},
{
- "id": "2782",
+ "id": "385",
"title": "Общая емкость жестких дисков"
},
{
- "id": "2792",
+ "id": "386",
"title": "Доступная емкость жестких дисков"
}
]
},
- {
- "id": "52",
- "title": "Vinteo (module$5) ПО",
- "items": [
- {
- "id": "312",
- "title": "Общее количество участников"
- },
- {
- "id": "322",
- "title": "Ожидание соединения"
- },
- {
- "id": "332",
- "title": "Зарегистрированные абоненты"
- },
- {
- "id": "342",
- "title": "Количество пользоватей HLS"
- },
- {
- "id": "352",
- "title": "Общее количество P2P комнат"
- },
- {
- "id": "362",
- "title": "Общее количество конференций"
- },
- {
- "id": "372",
- "title": "Общее количество активных конференций"
- },
- {
- "id": "382",
- "title": "Статус записи"
- },
- {
- "id": "392",
- "title": "Общее количество сохранённых записей"
- }
- ]
- },
{
"id": "2802",
"title": "Сетевой адаптер №1 (port$261) Eth_1",
"items": [
{
- "id": "2072",
+ "id": "388",
"title": "Скорость порта Eth_1"
},
{
- "id": "2092",
+ "id": "390",
"title": "Административное состояние порта Eth_1"
},
{
- "id": "2102",
+ "id": "391",
"title": "Оперативное состояние порта Eth_1"
},
{
- "id": "2112",
+ "id": "392",
"title": "Общее количество отправленных октетов Eth_1"
},
{
- "id": "2122",
+ "id": "393",
"title": "Количество входящих Multicast пакетов Eth_1"
},
{
- "id": "2132",
+ "id": "394",
"title": "Количество иcходящих Multiicast пакетов Eth_1"
},
{
- "id": "2142",
+ "id": "395",
"title": "Количество входящих Broadcast пакетов Eth_1"
},
{
- "id": "2152",
+ "id": "396",
"title": "Количество иcходящих Broadcast пакетов Eth_1"
},
{
- "id": "2162",
+ "id": "397",
"title": "Количество входящих Unicast пакетов Eth_1"
},
{
- "id": "2172",
+ "id": "398",
"title": "Количество иcходящих Unicast пакетов Eth_1"
},
{
- "id": "2182",
+ "id": "399",
"title": "Количество входящих пакетов помеченные как отброшенные Eth_1"
},
{
- "id": "2192",
+ "id": "400",
"title": "Количество иcходящих пакетов помеченные как отброшенные Eth_1"
},
{
- "id": "2202",
+ "id": "401",
"title": "Количество входящих пакетов с ошибкой Eth_1"
},
{
- "id": "2212",
+ "id": "402",
"title": "Количество исходящих пакетов с ошибкой Eth_1"
},
{
- "id": "2222",
+ "id": "403",
"title": "Количество входящих пакетов с неизвестным или неподдерживаемым протоколом Eth_1"
}
]
@@ -585,63 +539,63 @@
"title": "Сетевой адаптер №2 (port$262) Eth_2",
"items": [
{
- "id": "2242",
+ "id": "405",
"title": "Скорость порта Eth_2"
},
{
- "id": "2262",
+ "id": "407",
"title": "Административное состояние порта Eth_2"
},
{
- "id": "2272",
+ "id": "408",
"title": "Оперативное состояние порта Eth_2"
},
{
- "id": "2282",
+ "id": "409",
"title": "Общее количество отправленных октетов Eth_2"
},
{
- "id": "2292",
+ "id": "410",
"title": "Количество входящих Multicast пакетов Eth_2"
},
{
- "id": "2302",
+ "id": "411",
"title": "Количество иcходящих Multiicast пакетов Eth_2"
},
{
- "id": "2312",
+ "id": "412",
"title": "Количество входящих Broadcast пакетов Eth_2"
},
{
- "id": "2322",
+ "id": "413",
"title": "Количество иcходящих Broadcast пакетов Eth_2"
},
{
- "id": "2332",
+ "id": "414",
"title": "Количество входящих Unicast пакетов Eth_2"
},
{
- "id": "2342",
+ "id": "415",
"title": "Количество иcходящих Unicast пакетов Eth_2"
},
{
- "id": "2352",
+ "id": "416",
"title": "Количество входящих пакетов помеченные как отброшенные Eth_2"
},
{
- "id": "2362",
+ "id": "417",
"title": "Количество иcходящих пакетов помеченные как отброшенные Eth_2"
},
{
- "id": "2372",
+ "id": "418",
"title": "Количество входящих пакетов с ошибкой Eth_2"
},
{
- "id": "2382",
+ "id": "419",
"title": "Количество исходящих пакетов с ошибкой Eth_2"
},
{
- "id": "2392",
+ "id": "420",
"title": "Количество входящих пакетов с неизвестным или неподдерживаемым протоколом Eth_2"
}
]
@@ -651,63 +605,63 @@
"title": "Сетевой адаптер №3 (port$263) Eth_3",
"items": [
{
- "id": "2412",
+ "id": "422",
"title": "Скорость порта Eth_3"
},
{
- "id": "2432",
+ "id": "424",
"title": "Административное состояние порта Eth_3"
},
{
- "id": "2442",
+ "id": "425",
"title": "Оперативное состояние порта Eth_3"
},
{
- "id": "2452",
+ "id": "426",
"title": "Общее количество отправленных октетов Eth_3"
},
{
- "id": "2462",
+ "id": "427",
"title": "Количество входящих Multicast пакетов Eth_3"
},
{
- "id": "2472",
+ "id": "428",
"title": "Количество иcходящих Multiicast пакетов Eth_3"
},
{
- "id": "2482",
+ "id": "429",
"title": "Количество входящих Broadcast пакетов Eth_3"
},
{
- "id": "2492",
+ "id": "430",
"title": "Количество иcходящих Broadcast пакетов Eth_3"
},
{
- "id": "2502",
+ "id": "431",
"title": "Количество входящих Unicast пакетов Eth_3"
},
{
- "id": "2512",
+ "id": "432",
"title": "Количество иcходящих Unicast пакетов Eth_3"
},
{
- "id": "2522",
+ "id": "433",
"title": "Количество входящих пакетов помеченные как отброшенные Eth_3"
},
{
- "id": "2532",
+ "id": "434",
"title": "Количество иcходящих пакетов помеченные как отброшенные Eth_3"
},
{
- "id": "2542",
+ "id": "435",
"title": "Количество входящих пакетов с ошибкой Eth_3"
},
{
- "id": "2552",
+ "id": "436",
"title": "Количество исходящих пакетов с ошибкой Eth_3"
},
{
- "id": "2562",
+ "id": "437",
"title": "Количество входящих пакетов с неизвестным или неподдерживаемым протоколом Eth_3"
}
]
@@ -717,63 +671,63 @@
"title": "Сетевой адаптер №4 (port$264) Eth_4",
"items": [
{
- "id": "2582",
+ "id": "439",
"title": "Скорость порта Eth_4"
},
{
- "id": "2602",
+ "id": "441",
"title": "Административное состояние порта Eth_4"
},
{
- "id": "2612",
+ "id": "442",
"title": "Оперативное состояние порта Eth_4"
},
{
- "id": "2622",
+ "id": "443",
"title": "Общее количество отправленных октетов Eth_4"
},
{
- "id": "2632",
+ "id": "444",
"title": "Количество входящих Multicast пакетов Eth_4"
},
{
- "id": "2642",
+ "id": "445",
"title": "Количество иcходящих Multiicast пакетов Eth_4"
},
{
- "id": "2652",
+ "id": "446",
"title": "Количество входящих Broadcast пакетов Eth_4"
},
{
- "id": "2662",
+ "id": "447",
"title": "Количество иcходящих Broadcast пакетов Eth_4"
},
{
- "id": "2672",
+ "id": "448",
"title": "Количество входящих Unicast пакетов Eth_4"
},
{
- "id": "2682",
+ "id": "449",
"title": "Количество иcходящих Unicast пакетов Eth_4"
},
{
- "id": "2692",
+ "id": "450",
"title": "Количество входящих пакетов помеченные как отброшенные Eth_4"
},
{
- "id": "2702",
+ "id": "451",
"title": "Количество иcходящих пакетов помеченные как отброшенные Eth_4"
},
{
- "id": "2712",
+ "id": "452",
"title": "Количество входящих пакетов с ошибкой Eth_4"
},
{
- "id": "2722",
+ "id": "453",
"title": "Количество исходящих пакетов с ошибкой Eth_4"
},
{
- "id": "2732",
+ "id": "454",
"title": "Количество входящих пакетов с неизвестным или неподдерживаемым протоколом Eth_4"
}
]
@@ -889,15 +843,15 @@
"title": "Общее количество конференций"
},
{
- "id": "373",
+ "id": "373000",
"title": "Общее количество активных конференций"
},
{
- "id": "383",
+ "id": "38300",
"title": "Статус записи"
},
{
- "id": "393",
+ "id": "39300",
"title": "Общее количество сохранённых записей"
}
]
@@ -1281,11 +1235,11 @@
"title": "Общее количество активных конференций"
},
{
- "id": "384",
+ "id": "38400",
"title": "Статус записи"
},
{
- "id": "394",
+ "id": "39400",
"title": "Общее количество сохранённых записей"
}
]
@@ -1671,7 +1625,7 @@
"title": "Общее количество конференций"
},
{
- "id": "379",
+ "id": "37900",
"title": "Общее количество активных конференций"
},
{
@@ -1679,7 +1633,7 @@
"title": "Статус записи"
},
{
- "id": "399",
+ "id": "39900",
"title": "Общее количество сохранённых записей"
}
]
@@ -2447,15 +2401,15 @@
"title": "Общее количество конференций"
},
{
- "id": "378",
+ "id": "37800",
"title": "Общее количество активных конференций"
},
{
- "id": "388",
+ "id": "38800",
"title": "Статус записи"
},
{
- "id": "398",
+ "id": "39800",
"title": "Общее количество сохранённых записей"
}
]
@@ -2841,15 +2795,15 @@
"title": "Общее количество конференций"
},
{
- "id": "375",
+ "id": "37500",
"title": "Общее количество активных конференций"
},
{
- "id": "385",
+ "id": "38500",
"title": "Статус записи"
},
{
- "id": "395",
+ "id": "39500",
"title": "Общее количество сохранённых записей"
}
]
@@ -3229,15 +3183,15 @@
"title": "Общее количество конференций"
},
{
- "id": "376",
+ "id": "37600",
"title": "Общее количество активных конференций"
},
{
- "id": "386",
+ "id": "38600",
"title": "Статус записи"
},
{
- "id": "396",
+ "id": "39600",
"title": "Общее количество сохранённых записей"
}
]
@@ -3617,7 +3571,7 @@
"title": "Общее количество конференций"
},
{
- "id": "377",
+ "id": "37700",
"title": "Общее количество активных конференций"
},
{
@@ -3625,7 +3579,7 @@
"title": "Статус записи"
},
{
- "id": "397",
+ "id": "39700",
"title": "Общее количество сохранённых записей"
}
]
diff --git a/src/Components/TreeChart/tabContent.jsx b/src/Components/TreeChart/tabContent.jsx
index 15596a8..8b6ce63 100755
--- a/src/Components/TreeChart/tabContent.jsx
+++ b/src/Components/TreeChart/tabContent.jsx
@@ -2,9 +2,10 @@ import React, { lazy, Suspense } from "react";
import Skeleton from '@mui/material/Skeleton';
import Box from '@mui/material/Box';
-const PrometheusChart = lazy(() => import('../../Charts/PrometheusChart'));
+const PrometheusChart = lazy(() => import('../../Charts2/PrometheusChart'));
import LazyChartBatchRenderer from "../hooks/LazyChartBatchRender";
+// Функция для генерации названия метрики на основе id
const getMetricName = (id) => {
return `zvks_apiforsnmp_measure_${id}`;
};
@@ -12,28 +13,29 @@ const getMetricName = (id) => {
const getAllChildIds = (node) => {
let ids = [];
if (node.id) {
- ids.push(node.id);
+ ids.push(node.id);
}
if (node.items && node.items.length > 0) {
node.items.forEach((child) => {
- ids = ids.concat(getAllChildIds(child));
+ ids = ids.concat(getAllChildIds(child));
});
}
return ids;
};
+// Компонент Skeleton для графика
const ChartSkeleton = () => (
- {/* Заголовок */}
- {/* График */}
+
+
);
+// Компонент Skeleton для родительского контейнера
const ContainerSkeleton = () => (
{/* Заголовок */}
- {/* Описание */}
- {/* Место для дочерних элементов */}
+
{[...Array(3)].map((_, i) => (
@@ -42,24 +44,22 @@ const ContainerSkeleton = () => (
);
-const tabContent = (data, existingContent = {}) => {
- const tabContent = { ...existingContent };
+const tabContent = (data) => {
+ const tabContent = {};
+ // Функция для рекурсивного обхода и сбора данных
const generateContent = (nodes) => {
nodes.forEach((node) => {
- if (tabContent[node.id]) return;
-
if (node.items && node.items.length > 0) {
- generateContent(node.items);
+ const childrenContent = generateContent(node.items);
const content = (
-
+
{node.title}
}>
-
tabContent[child.id]?.content) || }
- />
+ tabContent[child.id].content)} />
+ Контент для {node.title}.
);
@@ -77,17 +77,26 @@ const tabContent = (data, existingContent = {}) => {
);
-
tabContent[node.id] = {
title: node.title,
content: content,
};
}
});
+
+ return (
+
+ {nodes.map((node) => (
+
{tabContent[node.id].content}
+ ))}
+
+ );
};
if (data.items && data.items.length > 0) {
generateContent(data.items);
+ } else {
+ console.warn("Данные отсутствуют или массив items пуст");
}
return tabContent;
diff --git a/src/main.jsx b/src/main.jsx
index e373476..b9a1a6d 100755
--- a/src/main.jsx
+++ b/src/main.jsx
@@ -2,8 +2,6 @@ import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import './index.css'
import App from './App.jsx'
-//import './Style/light-theme.css'; // Подключаем светлую тему по умолчанию
-//import './Style/dark-theme.css'; // Подключаем темную тему
createRoot(document.getElementById('root')).render(