diff --git a/package.json b/package.json index a4e7e89..3ab7c61 100755 --- a/package.json +++ b/package.json @@ -11,10 +11,12 @@ }, "dependencies": { "chartjs-adapter-date-fns": "^3.0.0", + "recharts": "^2.15.1", "d3": "^7.9.0", "react": "^18.3.1", "react-dom": "^18.3.1", "chart.js": "^4.0.0", + "chartjs-chart-box-and-violin-plot": "^4.0.0", "react-chartjs-2": "^5.0.0", "axios": "^1.7.9" }, diff --git a/src/App.jsx b/src/App.jsx index 21396db..f127d25 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; -import Dashboard from "./Components/Dashboard"; -import LoginModal from "./Components/LoginModal"; // Импортируем компонент авторизации +import Dashboard from "./Components/Layout/Dashboard"; +import LoginModal from "./Components/UI/LoginModal"; // Импортируем компонент авторизации import "./Style/LoginModal.css"; // Импортируем стили function App() { diff --git a/src/Charts/Components/BarChartComponent.jsx b/src/Charts/Components/BarChartComponent.jsx new file mode 100644 index 0000000..b0d425c --- /dev/null +++ b/src/Charts/Components/BarChartComponent.jsx @@ -0,0 +1,45 @@ +import React from 'react'; +import { BarChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Bar, ResponsiveContainer } from 'recharts'; + +const BarChartComponent = ({ chartData, metricName, metricType, colors }) => { + // Преобразуем данные для отображения + const data = Object.keys(chartData).map(instance => { + const instanceData = chartData[instance].reduce((acc, point) => { + if (point.value !== null) { + acc[point.quantile] = point.value; + } + return acc; + }, {}); + return { instance, ...instanceData }; + }); + + // Получаем все уникальные квантили + const allQuantiles = [...new Set( + Object.values(chartData).flat().map(point => point.quantile) + )]; + + return ( +
+

{metricName} ({metricType})

+ + + + + + + + {allQuantiles.map((quantile, index) => ( + + ))} + + +
+ ); +}; + +export default BarChartComponent; \ No newline at end of file diff --git a/src/Charts/Components/CounterComponent.jsx b/src/Charts/Components/CounterComponent.jsx new file mode 100644 index 0000000..209076a --- /dev/null +++ b/src/Charts/Components/CounterComponent.jsx @@ -0,0 +1,12 @@ +import React from 'react'; + +const CounterComponent = ({ value, metricName }) => { + return ( +
+

{metricName}

+

{value}

+
+ ); +}; + +export default CounterComponent; \ No newline at end of file diff --git a/src/Charts/Components/LineChartComponent.jsx b/src/Charts/Components/LineChartComponent.jsx new file mode 100644 index 0000000..1ded03c --- /dev/null +++ b/src/Charts/Components/LineChartComponent.jsx @@ -0,0 +1,48 @@ +import React from 'react'; +import { LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Line, ResponsiveContainer } from 'recharts'; + +const LineChartComponent = ({ chartData, metricName, metricType, colors }) => { + // Создаем массив уникальных временных меток + const allTimes = Object.values(chartData) + .flat() + .map(point => point.time) + .filter((time, index, self) => self.indexOf(time) === index); // Убираем дубликаты + + // Формируем данные для графика + 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; + }); + + console.log('Processed Data:', data); // Логируем данные для графика + + return ( +
+

{metricName} ({metricType})

+ + + + + + + + {Object.keys(chartData).map((key, index) => ( + + ))} + + +
+ ); +}; + +export default LineChartComponent; \ No newline at end of file diff --git a/src/Charts/Components/ScatterChartComponent.jsx b/src/Charts/Components/ScatterChartComponent.jsx new file mode 100644 index 0000000..15d5e90 --- /dev/null +++ b/src/Charts/Components/ScatterChartComponent.jsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { ScatterChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Scatter, ResponsiveContainer } from 'recharts'; + +const ScatterChartComponent = ({ chartData, metricName, metricType, colors }) => { + return ( +
+

{metricName} ({metricType})

+ + + + + + + + {Object.keys(chartData).map((instance, index) => ( + + ))} + + +
+ ); +}; + +export default ScatterChartComponent; \ No newline at end of file diff --git a/src/Charts/GpuTemperatureChart.jsx b/src/Charts/GpuTemperatureChart.jsx deleted file mode 100644 index a8a71d0..0000000 --- a/src/Charts/GpuTemperatureChart.jsx +++ /dev/null @@ -1,77 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Line } from "react-chartjs-2"; -import axios from "axios"; -import { - Chart as ChartJS, - LineElement, - PointElement, - LinearScale, - CategoryScale, -} from "chart.js"; -import ExpandableInfo from "../Components/ExpandableInfo" - -ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale); - -const GpuTemperatureChart = () => { - const chartRef = useRef(null); - const [data, setData] = useState({ - labels: Array(10).fill("").map((_, i) => i), // 20 точек по X - datasets: [ - { - label: "Температура GPU (°C)", - data: [], // Начальные значения (например, 50°C) - borderColor: "blue", - borderWidth: 2, - fill: false, - cubicInterpolationMode: "monotone", // Сглаживание - tension: 0.4, // Делаем линию плавнее - }, - ], - }); - - useEffect(() => { - const fetchData = async () => { - try { - const response = await axios.get("/data.json"); // Укажите путь к JSON-файлу - setData({ - labels: response.data.labels, - datasets: [{ ...data.datasets[0], data: response.data.datasets[0].data }], - }); - } catch (error) { - console.error("Ошибка загрузки данных:", error); - } - }; - - fetchData(); - const interval = setInterval(fetchData, 5000); // Обновляем данные каждые 5 секунд - - return () => clearInterval(interval); - }, []); - - // Пример данных для меню "Подробнее" - const details = [ - { label: "Использование", value: " 20%" }, - { label: "Оперативная память ГП", value: " 1,2/7,9 ГБ" }, - { label: "Общая память ГП", value: " 1,2/7,9 ГБ" }, - ]; - - return ( -
-

График температуры ГП

- - -
- ); -}; - -export default GpuTemperatureChart; \ No newline at end of file diff --git a/src/Charts/PrometheusChart.jsx b/src/Charts/PrometheusChart.jsx new file mode 100644 index 0000000..a80eafb --- /dev/null +++ b/src/Charts/PrometheusChart.jsx @@ -0,0 +1,134 @@ +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; +import LineChartComponent from './Components/LineChartComponent'; +import BarChartComponent from './Components/BarChartComponent'; +import ScatterChartComponent from './Components/ScatterChartComponent'; + +const MAX_POINTS = 20; // Ограничение точек на графике +const COLORS = ['#3e95cd', '#8e5ea2', '#3cba9f', '#e8c3b9', '#c45850']; // Фиксированные цвета для линий + +const PrometheusChart = ({ metricName }) => { + const [chartData, setChartData] = useState({}); + const [metricType, setMetricType] = useState(''); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await axios.get(`http://192.168.2.33:3000/metrics?metric=prometheus_target_metadata_cache_bytes`); + const result = response.data; + + // Проверяем структуру данных + let metrics; + if (Array.isArray(result)) { + // Если данные пришли в виде массива + metrics = result; + } else if (result.data && Array.isArray(result.data)) { + // Если данные пришли в виде объекта с ключом data + metrics = result.data; + } else { + throw new Error('Invalid data format'); + } + + if (!Array.isArray(metrics) || metrics.length === 0) { + throw new Error('No metrics data available'); + } + + const type = metrics[0].type; + setMetricType(type); + + if (type === 'summary') { + // Обработка данных для summary + const newData = metrics.map(m => ({ + instance: m.instance, + quantile: m.quantile, + value: m.value + })); + + // Группируем данные по instance + const groupedData = newData.reduce((acc, point) => { + if (!acc[point.instance]) { + acc[point.instance] = []; + } + acc[point.instance].push(point); + return acc; + }, {}); + + setChartData(groupedData); + } else { + // Обработка данных для counter, gauge, unknown + const newDataPoints = metrics.map(m => ({ + time: new Date(m.timestamp).toLocaleTimeString(), + value: m.value, + instance: m.instance, + device: m.device || m.scrape_job, // Используем device или scrape_job + })); + + // Группируем данные по instance и device/scrape_job + setChartData(prevData => { + const updatedData = { ...prevData }; + + newDataPoints.forEach(point => { + const key = `${point.instance}-${point.device}`; // Уникальный ключ + if (!updatedData[key]) { + updatedData[key] = []; + } + updatedData[key].push({ + time: point.time, + value: point.value, + }); + }); + + return updatedData; + }); + } + } catch (error) { + console.error('Error fetching metrics:', error); + } + }; + + fetchData(); // Вызываем сразу при монтировании + const interval = setInterval(fetchData, 5000); // Обновляем каждые 5 секунд + return () => clearInterval(interval); // Очищаем интервал при размонтировании + }, [metricName]); + + if (!Object.keys(chartData).length) return

Loading...

; + + const renderChart = () => { + switch (metricType) { + case 'counter': + case 'gauge': + return ( + + ); + case 'summary': + return ( + + ); + case 'unknown': + return ( + + ); + default: + return

Unsupported metric type

; + } + }; + + return renderChart(); +}; + +export default PrometheusChart; \ No newline at end of file diff --git a/src/Charts/PrometheusChart2.jsx b/src/Charts/PrometheusChart2.jsx new file mode 100644 index 0000000..eb1046a --- /dev/null +++ b/src/Charts/PrometheusChart2.jsx @@ -0,0 +1,83 @@ +import React, { useEffect, useState } from 'react'; +import axios from 'axios'; +import { LineChart, XAxis, YAxis, CartesianGrid, Tooltip, Legend, Line, ResponsiveContainer } from 'recharts'; + +const MAX_POINTS = 20; // Ограничение точек на графике +const COLORS = ['#3e95cd', '#8e5ea2', '#3cba9f', '#e8c3b9', '#c45850']; // Фиксированные цвета для линий + +const PrometheusChart2 = ({ metricName }) => { + const [chartData, setChartData] = useState({}); + const [metricType, setMetricType] = useState(''); + + useEffect(() => { + const fetchData = async () => { + try { + const response = await axios.get(`http://192.168.2.33:3000/metrics?metric=node_network_iface_link`); + const metrics = response.data; + if (!Array.isArray(metrics) || metrics.length === 0) { + throw new Error('No metrics data available'); + } + + const type = metrics[0].type; + setMetricType(type); + + // Обработка данных для counter, gauge, unknown + const newDataPoints = metrics.map(m => ({ + time: new Date(m.timestamp).toLocaleTimeString(), + value: m.value, + instance: m.instance // Добавляем идентификатор инстанса + })); + + // Обновляем данные для каждого инстанса + setChartData(prevData => { + const updatedData = { ...prevData }; + + newDataPoints.forEach(point => { + if (!updatedData[point.instance]) { + updatedData[point.instance] = []; + } + // Добавляем новую точку и ограничиваем количество точек + updatedData[point.instance] = [...updatedData[point.instance], point].slice(-MAX_POINTS); + }); + + return updatedData; + }); + } catch (error) { + console.error('Error fetching metrics:', error); + } + }; + + fetchData(); // Вызываем сразу при монтировании + const interval = setInterval(fetchData, 5000); // Обновляем каждые 5 секунд + return () => clearInterval(interval); // Очищаем интервал при размонтировании + }, [metricName]); + + if (!Object.keys(chartData).length) return

Loading...

; + + return ( +
+

{metricName} ({metricType})

+ + + + + + + + {Object.keys(chartData).map((instance, index) => ( + + ))} + + +
+ ); +}; + +export default PrometheusChart2; \ No newline at end of file diff --git a/src/Charts/RamUsageChart.jsx b/src/Charts/RamUsageChart.jsx deleted file mode 100644 index 26f5555..0000000 --- a/src/Charts/RamUsageChart.jsx +++ /dev/null @@ -1,76 +0,0 @@ -import React, { useEffect, useRef, useState } from "react"; -import { Line } from "react-chartjs-2"; -import { - Chart as ChartJS, - LineElement, - PointElement, - LinearScale, - CategoryScale, -} from "chart.js"; -import ExpandableInfo from "../Components/ExpandableInfo" - -ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale); - -const RamUsageChart = () => { - const chartRef = useRef(null); - const [data, setData] = useState({ - labels: Array(10).fill("").map((_, i) => i), // 20 точек по X - datasets: [ - { - label: "Загруженность RAM (%)", - data: Array(20).fill(50), // Начальные значения (например, 50%) - borderColor: "green", - borderWidth: 2, - fill: false, - cubicInterpolationMode: "monotone", // Сглаживание - tension: 0.4, // Делаем линию плавнее - }, - ], - }); - - useEffect(() => { - const interval = setInterval(() => { - setData((prevData) => { - const newTemp = Math.floor(Math.random() * 20) + 40; // Генерируем новую температуру (50-600°C) - const newLabels = [...prevData.labels.slice(1), prevData.labels[prevData.labels.length - 1] + 1]; // Сдвигаем ось X - const newDataset = [...prevData.datasets[0].data.slice(1), newTemp]; // Сдвигаем данные влево - - return { - labels: newLabels, - datasets: [{ ...prevData.datasets[0], data: newDataset }], - }; - }); - }, 1000); // Обновление каждую секунду - - return () => clearInterval(interval); - }, []); - - // Пример данных для меню "Подробнее" - const details = [ - { label: "Используется", value: " 6,2 ГБ" }, - { label: "Доступно", value: " 9,5 ГБ" }, - { label: "Выделено", value: " 6,8/18,2 ГБ" }, - { label: "Скорость", value: " 3200 МГц" }, - - ]; - - return ( -
-

График загруженности ОЗУ

- - -
- ); -}; - -export default RamUsageChart; \ No newline at end of file diff --git a/src/Charts/TestCharts.jsx b/src/Charts/TestCharts.jsx deleted file mode 100644 index b66f5eb..0000000 --- a/src/Charts/TestCharts.jsx +++ /dev/null @@ -1,165 +0,0 @@ -import React, { useEffect, useState, useRef } from 'react'; -import axios from 'axios'; -import { Line } from 'react-chartjs-2'; -import { - Chart as ChartJS, - CategoryScale, - LinearScale, - PointElement, - LineElement, - Title, - Tooltip, - Legend, - TimeScale, -} from 'chart.js'; -import 'chartjs-adapter-date-fns'; // Импортируем адаптер дат - -// Регистрируем компоненты Chart.js -ChartJS.register( - CategoryScale, - LinearScale, - PointElement, - LineElement, - Title, - Tooltip, - Legend, - TimeScale // Регистрируем временную шкалу -); - -const NetworkSpeedChart = () => { - const [chartData, setChartData] = useState({ - labels: [], - datasets: [], - }); - - const chartRef = useRef(null); // Референс на график - - // Функция для загрузки данных - const fetchData = async () => { - try { - const response = await axios.get('http://192.168.2.33:3000/metrics?metric=zvks_abonents_total'); - const newData = response.data; - - console.log('New data from backend:', newData); // Проверяем новые данные - - // Обновляем состояние, добавляя новые данные к существующим - setChartData((prevChartData) => { - // Группируем новые данные по устройству (device) - const newGroupedData = newData.reduce((acc, entry) => { - const device = entry.device; - if (!acc[device]) { - acc[device] = []; - } - acc[device].push(entry); - return acc; - }, {}); - - // Создаем новый набор данных - const newDatasets = Object.keys(newGroupedData).map((device, index) => { - // Находим существующий dataset для этого устройства - const existingDataset = prevChartData.datasets.find((dataset) => dataset.label === `Device: ${device}`); - - // Если dataset уже существует, добавляем новые данные к нему - if (existingDataset) { - return { - ...existingDataset, - data: [ - ...existingDataset.data, - ...newGroupedData[device].map((entry) => ({ - x: new Date(entry.timestamp), // Временная метка - y: entry.value, // Значение - })), - ], - }; - } - - // Если dataset не существует, создаем новый - return { - label: `Device: ${device}`, - data: newGroupedData[device].map((entry) => ({ - x: new Date(entry.timestamp), - y: entry.value, - })), - borderColor: `hsl(${(index * 360) / Object.keys(newGroupedData).length}, 70%, 50%)`, - backgroundColor: `hsla(${(index * 360) / Object.keys(newGroupedData).length}, 70%, 50%, 0.2)`, - tension: 0.2, - }; - }); - - // Обновляем labels (метки времени) - const newLabels = [ - ...prevChartData.labels, - ...newData.map((entry) => new Date(entry.timestamp)), - ]; - - return { - labels: newLabels, - datasets: newDatasets, - }; - }); - } catch (error) { - console.error('Ошибка при загрузке метрик:', error); - } - }; - - // Загружаем данные при монтировании компонента и обновляем каждые 5 секунд - useEffect(() => { - fetchData(); - const interval = setInterval(fetchData, 5000); - - // Очищаем интервал и уничтожаем график при размонтировании компонента - return () => { - clearInterval(interval); - if (chartRef.current) { - chartRef.current.destroy(); - } - }; - }, []); - - // Опции графика - const options = { - responsive: true, - plugins: { - legend: { - position: 'top', - }, - title: { - display: true, - text: 'node_network_receive_bytes_total', - }, - }, - scales: { - x: { - type: 'time', // Используем временную шкалу - time: { - unit: 'second', // Единица времени - displayFormats: { - second: 'HH:mm:ss', // Формат отображения времени - }, - }, - title: { - display: true, - text: 'Time', - }, - }, - y: { - title: { - display: true, - text: 'Данные', - }, - }, - }, - animation: { - duration: 1000, // Длительность анимации - easing: 'linear', // Тип анимации - }, - }; - - return ( -
- -
- ); -}; - -export default NetworkSpeedChart; \ No newline at end of file diff --git a/src/Charts/TestCharts3.jsx b/src/Charts/TestCharts3.jsx deleted file mode 100644 index cb8a36f..0000000 --- a/src/Charts/TestCharts3.jsx +++ /dev/null @@ -1,68 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { Line } from 'react-chartjs-2'; -import axios from 'axios'; -import { Chart as ChartJS, Title, Tooltip, Legend, LineElement, CategoryScale, LinearScale } from 'chart.js'; - -// Регистрация компонентов Chart.js -ChartJS.register(Title, Tooltip, Legend, LineElement, CategoryScale, LinearScale); - -const SimpleGraph = () => { - const [data, setData] = useState([]); - - useEffect(() => { - const fetchData = async () => { - try { - // Загружаем данные из файла с использованием axios - const response = await axios.get('/data.json'); // Путь должен быть относительно папки public - const rawData = response.data; - - // Проверяем, что данные действительно массив - if (Array.isArray(rawData)) { - const chartData = rawData.map(item => ({ - timestamp: item.timestamp, - value: item.value, - })); - - setData(chartData); - } else { - throw new Error('Ошибка: Данные не являются массивом.'); - } - } catch (error) { - console.error('Error fetching data:', error); - } - }; - - fetchData(); - }, []); - - if (data.length === 0) return
Loading...
; - - // Настройки графика - const chartOptions = { - responsive: true, - plugins: { - title: { - display: true, - text: 'Simple Data Graph', - }, - }, - }; - - const chartData = { - labels: data.map(item => item.timestamp), // Массив меток для оси X - datasets: [ - { - label: 'Value', - data: data.map(item => item.value), // Массив значений для оси Y - borderColor: 'rgb(75, 192, 192)', - backgroundColor: 'rgba(75, 192, 192, 0.2)', - fill: false, - tension: 0.1, - }, - ], - }; - - return ; -}; - -export default SimpleGraph; diff --git a/src/Components/Dashboard.jsx b/src/Components/Dashboard.jsx deleted file mode 100644 index a1c9a34..0000000 --- a/src/Components/Dashboard.jsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useState, useEffect } from "react"; -import SidebarMenu from "./SidebarMenu"; -import SystemStatusTable from "../Charts/SystemStatusTable"; -import SystemStatusTableSoftware from "../Charts/SystemStatusTableSoftware"; -import TreeChart from "./TreeChart"; -import "../Style/Dashboard.css"; -import ErrorIndicator from "./ErrorIndicator"; -import tabContentData from "./tabContent"; -import menuData from "./menuData.json"; // Загружаем новое меню - -const Dashboard = () => { - const [tabs, setTabs] = useState([]); - const [activeTab, setActiveTab] = useState("Главная"); - const [tabContent, setTabContent] = useState({}); - const [treeData, setTreeData] = useState(null); - - useEffect(() => { - setTabContent(tabContentData); - setTreeData(menuData); // Теперь menuData - объект, а не массив - }, []); - - const handleOpenTab = (id, title) => { - if (!tabs.includes(id)) { - setTabs([...tabs, id]); - } - setActiveTab(id); - }; - - const handleCloseTab = (id) => { - const newTabs = tabs.filter((tab) => tab !== id); - setTabs(newTabs); - if (activeTab === id) { - setActiveTab(newTabs.length > 0 ? newTabs[newTabs.length - 1] : "Главная"); - } - }; - - const renderTabContent = () => { - if (activeTab === "Главная") { - return ( -
-

Общий мониторинг

- - - -
- ); - } else if (activeTab === "Визуализация") { - return handleOpenTab(id, title)} />; - } else { - const tabData = tabContent[activeTab]; - return tabData ? tabData.content :

Нет данных

; - } - }; - - return ( -
- - -
-
-
setActiveTab("Главная")} - > - Главная -
-
setActiveTab("Визуализация")} - > - Визуализация -
- {tabs.map((tab) => ( -
setActiveTab(tab)} - > - {tab} - -
- ))} -
- -
- {renderTabContent()} -
-
-
- ); -}; - -export default Dashboard; \ No newline at end of file diff --git a/src/Components/Layout/Dashboard.jsx b/src/Components/Layout/Dashboard.jsx new file mode 100644 index 0000000..2791d02 --- /dev/null +++ b/src/Components/Layout/Dashboard.jsx @@ -0,0 +1,75 @@ +import React, { useState, useEffect } from "react"; +import SidebarMenu from "./SidebarMenu"; +import TreeChart from "../TreeChart/TreeChart"; +import "../../Style/Dashboard.css"; +import ErrorIndicator from "../UI/ErrorIndicator"; +import tabContentData from "../TreeChart/tabContent"; +import Tabs from "../UI/Tabs"; +import menuData from "../TreeChart//menuData.json"; // Загружаем новое меню +import TableComponent from '../UI/TreeTable'; +import TreeTable from "../UI/TreeTable"; + + +const Dashboard = () => { + const [tabs, setTabs] = useState([]); + const [activeTab, setActiveTab] = useState("Главная"); + const [tabContent, setTabContent] = useState({}); + const [treeData, setTreeData] = useState(null); + + useEffect(() => { + setTabContent(tabContentData); + setTreeData(menuData); + }, []); + + const handleOpenTab = (id, title) => { + if (!tabs.some((tab) => tab.id === id)) { + setTabs([...tabs, { id, title }]); + } + setActiveTab(id); + }; + + const handleCloseTab = (id) => { + const newTabs = tabs.filter((tab) => tab.id !== id); + setTabs(newTabs); + if (activeTab === id) { + setActiveTab(newTabs.length > 0 ? newTabs[newTabs.length - 1].id : "Главная"); + } + }; + + const renderTabContent = () => { + if (activeTab === "Главная") { + return ( +
+

Общий мониторинг

+ + +
+ ); + } else if (activeTab === "Визуализация") { + return handleOpenTab(id, title)} />; + } else { + const tabData = tabContent[activeTab]; + return tabData ? tabData.content :

Нет данных

; + } + }; + + return ( +
+ + +
+ setActiveTab(id)} + onCloseTab={handleCloseTab} + /> +
+ {renderTabContent()} +
+
+
+ ); +}; + +export default Dashboard; \ No newline at end of file diff --git a/src/Components/SidebarMenu.jsx b/src/Components/Layout/SidebarMenu.jsx similarity index 69% rename from src/Components/SidebarMenu.jsx rename to src/Components/Layout/SidebarMenu.jsx index 2c5adb9..f32cbaf 100644 --- a/src/Components/SidebarMenu.jsx +++ b/src/Components/Layout/SidebarMenu.jsx @@ -1,10 +1,24 @@ import React, { useState } from "react"; -import "../Style/SidebarMenu.css"; -import menuData from "./menuData.json"; +import "../../Style/SidebarMenu.css"; +import menuData from "../TreeChart/menuData.json"; + +const getStatusColor = (status) => { + switch (status) { + case "green": + return "#4CAF50"; // Зеленый + case "yellow": + return "#FFEB3B"; // Желтый + case "red": + return "#F44336"; // Красный + default: + return "#3d74c7"; // Белый (или любой другой стандартный цвет) + } +}; const MenuItem = ({ item, onSelectItem }) => { const [isOpen, setIsOpen] = useState(false); const hasChildren = Array.isArray(item.items) && item.items.length > 0; + const backgroundColor = getStatusColor(item.status); const handleClick = () => { if (hasChildren) { @@ -16,7 +30,7 @@ const MenuItem = ({ item, onSelectItem }) => { return (
-
+
{item.title} {hasChildren && {isOpen ? "▲" : "▼"}}
@@ -34,7 +48,7 @@ const MenuItem = ({ item, onSelectItem }) => { function SidebarMenu({ onOpenTab }) { const handleSelectItem = (item) => { onOpenTab(item.id, item.title); // Передаем id и title - }; + }; return (
diff --git a/src/Components/LoginModal.jsx b/src/Components/LoginModal.jsx deleted file mode 100644 index 895df58..0000000 --- a/src/Components/LoginModal.jsx +++ /dev/null @@ -1,49 +0,0 @@ -import React, { useState } from "react"; - -const Login = ({ onLogin, onClose }) => { - const [username, setUsername] = useState(""); - const [password, setPassword] = useState(""); - const [error, setError] = useState(""); - - const handleSubmit = (e) => { - e.preventDefault(); - if (username === "admin" && password === "admin") { - onLogin(); // Успешная авторизация - onClose(); // Закрыть модальное окно - } else { - setError("Неверный логин или пароль"); - } - }; - - return ( -
-
-

Авторизация

-
-
- - setUsername(e.target.value)} - required - /> -
-
- - setPassword(e.target.value)} - required - /> -
- {error &&

{error}

} - -
-
-
- ); -}; - -export default Login; \ No newline at end of file diff --git a/src/Components/TreeChart.jsx b/src/Components/TreeChart/TreeChart.jsx similarity index 77% rename from src/Components/TreeChart.jsx rename to src/Components/TreeChart/TreeChart.jsx index c008388..fc61b48 100644 --- a/src/Components/TreeChart.jsx +++ b/src/Components/TreeChart/TreeChart.jsx @@ -11,7 +11,7 @@ const TreeChart = ({ data, onNodeClick }) => { d3.select(chartRef.current).selectAll("*").remove(); const width = 928; - const height = 600; + const height = 1000; const root = d3.hierarchy(data, (d) => d.items); const links = root.links(); @@ -19,8 +19,8 @@ const TreeChart = ({ data, onNodeClick }) => { const simulation = d3 .forceSimulation(nodes) - .force("link", d3.forceLink(links).id((d) => d.data.title).distance(80).strength(1)) // Увеличил дистанцию - .force("charge", d3.forceManyBody().strength(-500)) // Увеличил отталкивание узлов + .force("link", d3.forceLink(links).id((d) => d.data.title).distance(80).strength(1)) + .force("charge", d3.forceManyBody().strength(-500)) .force("x", d3.forceX()) .force("y", d3.forceY()); @@ -46,9 +46,21 @@ const TreeChart = ({ data, onNodeClick }) => { .selectAll("circle") .data(nodes) .join("circle") - .attr("fill", (d) => (d.children ? "#555" : "#000")) + .attr("fill", (d) => { + // Окрашиваем узлы в зависимости от статуса + switch (d.data.status) { + case "green": + return "#4CAF50"; // Зеленый + case "yellow": + return "#FFEB3B"; // Желтый + case "red": + return "#F44336"; // Красный + default: + return "#555"; // Серый по умолчанию + } + }) .attr("stroke", "#fff") - .attr("r", 7) // Немного увеличил размер узлов для удобства клика + .attr("r", 7) .call(drag(simulation)); // Добавляем текстовые подписи @@ -57,13 +69,13 @@ const TreeChart = ({ data, onNodeClick }) => { .attr("fill", "#000") .attr("font-family", "Arial") .attr("font-size", 12) - .attr("pointer-events", "none") // Отключаем обработку событий текста + .attr("pointer-events", "none") .selectAll("text") .data(nodes) .join("text") .text((d) => d.data.title) - .attr("dx", 12) // Отодвигаем текст дальше от узла - .attr("dy", 4) // Немного поднимаем текст + .attr("dx", 12) + .attr("dy", 4); node.append("title").text((d) => d.data.title); @@ -85,7 +97,7 @@ const TreeChart = ({ data, onNodeClick }) => { .attr("cy", (d) => d.y); text - .attr("x", (d) => d.x + 12) // Смещаем текст правее узла + .attr("x", (d) => d.x + 12) .attr("y", (d) => d.y + 4); }); @@ -118,4 +130,4 @@ const TreeChart = ({ data, onNodeClick }) => { return ; }; -export default TreeChart; +export default TreeChart; \ No newline at end of file diff --git a/src/Components/TreeChart/menuData.json b/src/Components/TreeChart/menuData.json new file mode 100644 index 0000000..73144fd --- /dev/null +++ b/src/Components/TreeChart/menuData.json @@ -0,0 +1,225 @@ +{ + "title": "Сервис ВКС", + "status": "red", + "items": [ + { + "title": "Функциональные задачи", + "status": "yellow", + "items": [ + { + "id": "system_control", + "title": "Контроль системы", + "status": "red" + }, + { + "id": "system_management", + "title": "Система управления", + "status": "green" + }, + { + "id": "conference", + "title": "Проведение ВКС", + "status": "green" + }, + { + "id": "backup", + "title": "Резервное копирование", + "status": "green" + }, + { + "id": "relay_info", + "title": "Ретрансляция информации", + "status": "green" + } + ] + }, + { + "title": "Медиа сервер", + "items": [ + { + "title": "Аппаратное обеспечение", + "items": [ + { + "id": "media_system_software_1", + "title": "Центральный процессор" + }, + { + "id": "media_system_software_2", + "title": "Оперативная память" + }, + { + "id": "media_system_software_3", + "title": "Жесткий диск" + }, + { + "id": "media_system_software_4", + "title": "Сетевые адаптеры" + } + ] + }, + { + "title": "Программное обеспечение", + "items": [ + { + "id": "media_software_1", + "title": "ПО" + }, + { + "id": "media_software_2", + "title": "ПО" + }, + { + "id": "media_software_3", + "title": "ПО" + }, + { + "id": "media_software_4", + "title": "ПО" + } + ] + } + ] + }, + { + "title": "Сервер резервного копирования", + "items": [ + { + "title": "Аппаратное обеспечение", + "items": [ + { + "id": "copy_system_software_1", + "title": "Центральный процессор" + }, + { + "id": "copy_system_software_2", + "title": "Оперативная память" + }, + { + "id": "copy_system_software_3", + "title": "Жесткий диск" + }, + { + "id": "copy_system_software_4", + "title": "Сетевые адаптеры" + } + ] + }, + { + "title": "Программное обеспечение", + "items": [ + { + "id": "copy_software_1", + "title": "ПО" + }, + { + "id": "copy_software_2", + "title": "ПО" + }, + { + "id": "copy_software_3", + "title": "ПО" + }, + { + "id": "copy_software_4", + "title": "ПО" + } + ] + } + ] + }, + { + "title": "Сервер системы управления", + "items": [ + { + "title": "Аппаратное обеспечение", + "items": [ + { + "id": "control_system_software_1", + "title": "Центральный процессор" + }, + { + "id": "control_system_software_2", + "title": "Оперативная память" + }, + { + "id": "control_system_software_3", + "title": "Жесткий диск" + }, + { + "id": "control_system_software_4", + "title": "Сетевые адаптеры" + } + ] + }, + { + "title": "Программное обеспечение", + "items": [ + { + "id": "control_software_1", + "title": "ПО" + }, + { + "id": "control_software_2", + "title": "ПО" + }, + { + "id": "control_software_3", + "title": "ПО" + }, + { + "id": "control_software_4", + "title": "ПО" + } + ] + } + ] + }, + { + "title": "Сервер сбора и ретрансляции информации", + "items": [ + { + "title": "Аппаратное обеспечение", + "items": [ + { + "id": "system_software_1", + "title": "Центральный процессор" + }, + { + "id": "system_software_2", + "title": "Оперативная память" + }, + { + "id": "system_software_3", + "title": "Жесткий диск" + }, + { + "id": "system_software_4", + "title": "Сетевые адаптеры" + } + ] + }, + { + "title": "Программное обеспечение", + "items": [ + { + "id": "software_1", + "title": "ПО" + }, + { + "id": "software_2", + "title": "ПО" + }, + { + "id": "software_3", + "title": "ПО" + }, + { + "id": "software_4", + "title": "ПО" + } + ] + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Components/TreeChart/menuData3.json b/src/Components/TreeChart/menuData3.json new file mode 100644 index 0000000..1c0eeda --- /dev/null +++ b/src/Components/TreeChart/menuData3.json @@ -0,0 +1,117 @@ +{ + "title": "Сервис ВКС", + "items": [ + { + "title": "Функциональные задачи", + "items": [ + { + "title": "Тест", + "items": [ + { + "id": "test1", + "title": "тест2" + }, + { + "id": "test2", + "title": "Тест3" + } + ] + }, + { + "id": "system_control", + "title": "Контроль системы" + }, + { + "id": "system_management", + "title": "Система управления" + }, + { + "id": "conference", + "title": "Проведение ВКС" + }, + { + "id": "backup", + "title": "Резервное копирование" + }, + { + "id": "relay_info", + "title": "Ретрансляция информации" + } + ] + }, + { + "title": "Аппаратное обеспечение", + "items": [ + { + "id": "hardware_software_1", + "title": "Сервер системы управления" + }, + { + "id": "hardware_software_2", + "title": "Сервер системы управления" + }, + { + "id": "hardware_software_3", + "title": "Медиа-сервер" + }, + { + "id": "hardware_software_4", + "title": "Медиа-сервер" + }, + { + "id": "hardware_software_5", + "title": "Медиа-сервер" + }, + { + "id": "hardware_software_6", + "title": "Медиа-сервер" + }, + { + "id": "hardware_software_7", + "title": "Сервер резервного копирования" + }, + { + "id": "hardware_software_8", + "title": "Сервер сбора и ретрансляции информации" + } + ] + }, + { + "title": "Программное обеспечение", + "items": [ + { + "id": "software_1", + "title": "БП/ППО" + }, + { + "id": "software_2", + "title": "БП/ППО" + }, + { + "id": "software_3", + "title": "БП/ППО" + }, + { + "id": "software_4", + "title": "БП/ППО" + }, + { + "id": "software_5", + "title": "БП/ППО" + }, + { + "id": "software_6", + "title": "БП/ППО" + }, + { + "id": "software_7", + "title": "БП/ППО" + }, + { + "id": "software_8", + "title": "БП/ППО" + } + ] + } + ] +} \ No newline at end of file diff --git a/src/Components/TreeChart/tabContent.jsx b/src/Components/TreeChart/tabContent.jsx new file mode 100644 index 0000000..922e777 --- /dev/null +++ b/src/Components/TreeChart/tabContent.jsx @@ -0,0 +1,56 @@ +import React from "react"; +import PrometheusChart from '../../Charts/PrometheusChart'; + +const tabContent = { + // Сервис ВКС + service1: { title: "Сервис ВКС", content:

Сервис ВКС

}, + + // Функциональные задачи + system_control: { title: "Контроль системы", content:

Контроль системы

Описание контроля.

}, + system_management: { title: "Система управления", content:

Система управления

Описание системы управления.

}, + conference: { title: "Проведение ВКС", content:

Проведение ВКС

Информация о проведении ВКС.

}, + backup: { title: "Резервное копирование", content:

Резервное копирование

Процесс резервного копирования.

}, + relay_info: { title: "Ретрансляция информации", content:

Ретрансляция информации

Детали ретрансляции.

}, + + // Медиа сервер + media_system_software_1: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора медиа сервера.

}, + media_system_software_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти медиа сервера.

}, + media_system_software_3: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска медиа сервера.

}, + media_system_software_4: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров медиа сервера.

}, + media_software_1: { title: "ПО", content:

Программное обеспечение медиа сервера

}, + media_software_2: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_3: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + media_software_4: { title: "ПО", content:

Программное обеспечение медиа сервера

Описание ПО медиа сервера.

}, + + // Сервер резервного копирования + copy_system_software_1: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора сервера резервного копирования.

}, + copy_system_software_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти сервера резервного копирования.

}, + copy_system_software_3: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска сервера резервного копирования.

}, + copy_system_software_4: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров сервера резервного копирования.

}, + copy_software_1: { title: "ПО", content:

Программное обеспечение сервера резервного копирования

Описание ПО сервера резервного копирования.

}, + copy_software_2: { title: "ПО", content:

Программное обеспечение сервера резервного копирования

Описание ПО сервера резервного копирования.

}, + copy_software_3: { title: "ПО", content:

Программное обеспечение сервера резервного копирования

Описание ПО сервера резервного копирования.

}, + copy_software_4: { title: "ПО", content:

Программное обеспечение сервера резервного копирования

Описание ПО сервера резервного копирования.

}, + + // Сервер системы управления + control_system_software_1: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора сервера системы управления.

}, + control_system_software_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти сервера системы управления.

}, + control_system_software_3: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска сервера системы управления.

}, + control_system_software_4: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров сервера системы управления.

}, + control_software_1: { title: "ПО", content:

Программное обеспечение сервера системы управления

Описание ПО сервера системы управления.

}, + control_software_2: { title: "ПО", content:

Программное обеспечение сервера системы управления

Описание ПО сервера системы управления.

}, + control_software_3: { title: "ПО", content:

Программное обеспечение сервера системы управления

Описание ПО сервера системы управления.

}, + control_software_4: { title: "ПО", content:

Программное обеспечение сервера системы управления

Описание ПО сервера системы управления.

}, + + // Сервер сбора и ретрансляции информации + system_software_1: { title: "Центральный процессор", content:

Центральный процессор

Описание центрального процессора сервера сбора и ретрансляции информации.

}, + system_software_2: { title: "Оперативная память", content:

Оперативная память

Описание оперативной памяти сервера сбора и ретрансляции информации.

}, + system_software_3: { title: "Жесткий диск", content:

Жесткий диск

Описание жесткого диска сервера сбора и ретрансляции информации.

}, + system_software_4: { title: "Сетевые адаптеры", content:

Сетевые адаптеры

Описание сетевых адаптеров сервера сбора и ретрансляции информации.

}, + software_1: { title: "ПО", content:

Программное обеспечение сервера сбора и ретрансляции информации

Описание ПО сервера сбора и ретрансляции информации.

}, + software_2: { title: "ПО", content:

Программное обеспечение сервера сбора и ретрансляции информации

Описание ПО сервера сбора и ретрансляции информации.

}, + software_3: { title: "ПО", content:

Программное обеспечение сервера сбора и ретрансляции информации

Описание ПО сервера сбора и ретрансляции информации.

}, + software_4: { title: "ПО", content:

Программное обеспечение сервера сбора и ретрансляции информации

Описание ПО сервера сбора и ретрансляции информации.

}, +}; + +export default tabContent; \ No newline at end of file diff --git a/src/Components/TreeChart/tabContent3.jsx b/src/Components/TreeChart/tabContent3.jsx new file mode 100644 index 0000000..4124a32 --- /dev/null +++ b/src/Components/TreeChart/tabContent3.jsx @@ -0,0 +1,31 @@ +import React from "react"; +import NetworkSpeedChart2 from '../../Charts/TestCharts2'; +import PrometheusChart from '../../Charts/PrometheusChart'; +import PrometheusChart2 from '../../Charts/PrometheusChart2'; + +const tabContent = { + service1: { title: "Сервис ВКС", content:

Сервис ВКС

}, + system_control: { title: "Контроль системы", content:

Контроль системы

Описание контроля.

}, + system_management: { title: "Система управления", content:

Система управления

Описание системы управления.

}, + conference: { title: "Проведение ВКС", content:

Проведение ВКС

Информация о проведении ВКС.

}, + backup: { title: "Резервное копирование", content:

Резервное копирование

Процесс резервного копирования.

}, + relay_info: { title: "Ретрансляция информации", content:

Ретрансляция информации

Детали ретрансляции.

}, + hardware_software_1: { title: "Сервер системы управления", content:

Сервер системы управления

}, + hardware_software_2: { title: "Сервер системы управления", content:

Сервер системы управления

}, + hardware_software_3: { title: "Медиа-сервер", content:

Медиа-сервер

}, + hardware_software_4: { title: "Медиа-сервер", content:

Медиа-сервер

}, + hardware_software_5: { title: "Медиа-сервер", content:

Медиа-сервер

}, + hardware_software_6: { title: "Медиа-сервер", content:

Медиа-сервер

}, + hardware_software_7: { title: "Сервер резервного копирования", content:

Сервер резервного копирования

}, + hardware_software_8: { title: "Сервер сбора и ретрансляции информации", content:

Сервер сбора и ретрансляции информации

}, + software_1: { title: "БП/ППО", content:

БП/ППО

}, + software_2: { title: "БП/ППО", content:

БП/ППО

}, + software_3: { title: "БП/ППО", content:

БП/ППО

}, + software_4: { title: "БП/ППО", content:

БП/ППО

}, + software_5: { title: "БП/ППО", content:

БП/ППО

}, + software_6: { title: "БП/ППО", content:

БП/ППО

}, + software_7: { title: "БП/ППО", content:

БП/ППО

}, + software_8: { title: "БП/ППО", content:

БП/ППО

}, +}; + +export default tabContent; \ No newline at end of file diff --git a/src/Components/ErrorIndicator.jsx b/src/Components/UI/ErrorIndicator.jsx similarity index 70% rename from src/Components/ErrorIndicator.jsx rename to src/Components/UI/ErrorIndicator.jsx index 7d71840..7215211 100644 --- a/src/Components/ErrorIndicator.jsx +++ b/src/Components/UI/ErrorIndicator.jsx @@ -1,7 +1,7 @@ import React from "react"; -import criticalIcon from "../assets/images/critical.png"; // Красный треугольник -import warningIcon from "../assets/images/warning.png"; // Желтый треугольник -import "../Style/ErrorIndicator.css"; // Подключаем стили +import criticalIcon from "../../assets/images/critical.png"; // Красный треугольник +import warningIcon from "../../assets/images/warning.png"; // Желтый треугольник +import "../../Style/ErrorIndicator.css"; // Подключаем стили const ErrorIndicator = ({ criticalCount, warningCount }) => { return ( diff --git a/src/Components/ExpandableInfo.jsx b/src/Components/UI/ExpandableInfo.jsx similarity index 100% rename from src/Components/ExpandableInfo.jsx rename to src/Components/UI/ExpandableInfo.jsx diff --git a/src/Components/UI/LoginModal.jsx b/src/Components/UI/LoginModal.jsx new file mode 100644 index 0000000..c4d5f09 --- /dev/null +++ b/src/Components/UI/LoginModal.jsx @@ -0,0 +1,49 @@ +import React, { useState } from "react"; +import Modal from "./Modal"; +import "../../Style/LoginModal.css"; + +const LoginModal = ({ onLogin, onClose }) => { + const [username, setUsername] = useState(""); + const [password, setPassword] = useState(""); + const [error, setError] = useState(""); + + const handleSubmit = (e) => { + e.preventDefault(); + if (username === "admin" && password === "admin") { + onLogin(); // Успешная авторизация + onClose(); // Закрыть модальное окно + } else { + setError("Неверный логин или пароль"); + } + }; + + return ( + +

Авторизация

+
+
+ + setUsername(e.target.value)} + required + /> +
+
+ + setPassword(e.target.value)} + required + /> +
+ {error &&

{error}

} + +
+
+ ); +}; + +export default LoginModal; \ No newline at end of file diff --git a/src/Components/UI/Modal.jsx b/src/Components/UI/Modal.jsx new file mode 100644 index 0000000..3718035 --- /dev/null +++ b/src/Components/UI/Modal.jsx @@ -0,0 +1,14 @@ +import React from "react"; + +const Modal = ({ children, onClose }) => { + return ( +
+
+ {children} + +
+
+ ); +}; + +export default Modal; \ No newline at end of file diff --git a/src/Components/UI/Tabs.jsx b/src/Components/UI/Tabs.jsx new file mode 100644 index 0000000..c8c18bb --- /dev/null +++ b/src/Components/UI/Tabs.jsx @@ -0,0 +1,55 @@ +import React from "react"; +import "../../Style/common.css"; // Общие стили для табов + +const Tabs = ({ tabs, activeTab, onTabClick, onCloseTab }) => { + const handleMouseDown = (e, id) => { + // Проверяем, была ли нажата средняя кнопка мыши (button === 1) + if (e.button === 1) { + e.preventDefault(); // Предотвращаем стандартное поведение (например, прокрутку) + onCloseTab(id); // Закрываем вкладку + } + }; + + return ( +
+ {/* Всегда отображаемые вкладки */} +
onTabClick("Главная")} + onMouseDown={(e) => handleMouseDown(e, "Главная")} // Добавляем обработчик для СКМ + > + Главная +
+
onTabClick("Визуализация")} + onMouseDown={(e) => handleMouseDown(e, "Визуализация")} // Добавляем обработчик для СКМ + > + Визуализация +
+ + {/* Динамически добавляемые вкладки */} + {tabs.map((tab) => ( +
onTabClick(tab.id)} + onMouseDown={(e) => handleMouseDown(e, tab.id)} // Добавляем обработчик для СКМ + > + {tab.title} + +
+ ))} +
+ ); +}; + +export default Tabs; \ No newline at end of file diff --git a/src/Components/UI/TreeTable.jsx b/src/Components/UI/TreeTable.jsx new file mode 100644 index 0000000..60a52d3 --- /dev/null +++ b/src/Components/UI/TreeTable.jsx @@ -0,0 +1,36 @@ +import React from "react"; +import "../../Style/TreeTable.css"; // Подключаем стили + +const TreeTable = ({ data }) => { + return ( +
+ {/* Первый уровень заголовков */} +
+ {data.map((item, index) => ( +
+
{item.title}
+
+ ))} +
+ + {/* Вложенные элементы */} +
+ {data.map((item, index) => ( +
+ {item.items && ( +
+ {item.items.map((child, childIndex) => ( +
+ {child.title} +
+ ))} +
+ )} +
+ ))} +
+
+ ); +}; + +export default TreeTable; diff --git a/src/Components/menuData.json b/src/Components/menuData.json deleted file mode 100644 index daf3ef1..0000000 --- a/src/Components/menuData.json +++ /dev/null @@ -1,55 +0,0 @@ -{ - "title": "Сервис ВКС", - "items": [ - { - "title": "Функциональные задачи", - "items": [ - { - "id": "system_control", - "title": "Контроль системы" - }, - { - "id": "system_management", - "title": "Система управления" - }, - { - "id": "conference", - "title": "Проведение ВКС" - }, - { - "id": "backup", - "title": "Резервное копирование" - }, - { - "id": "relay_info", - "title": "Ретрансляция информации" - } - ] - }, - { - "title": "Аппаратное ПО", - "items": [ - { - "id": "hardware_software_1", - "title": "ПО1" - }, - { - "id": "hardware_software_2", - "title": "ПО2" - }, - { - "id": "hardware_software_3", - "title": "ПО3" - }, - { - "id": "hardware_software_4", - "title": "ПО4" - }, - { - "id": "hardware_software_5", - "title": "ПО5" - } - ] - } - ] -} diff --git a/src/Components/tabContent.jsx b/src/Components/tabContent.jsx deleted file mode 100644 index 9f7aa40..0000000 --- a/src/Components/tabContent.jsx +++ /dev/null @@ -1,18 +0,0 @@ -import React from "react"; -import NetworkSpeedChart2 from '../Charts/TestCharts2'; - -const tabContent = { - service1: { title: "Сервис ВКС", content:

Сервис ВКС

}, - system_control: { title: "Контроль системы", content:

Контроль системы

Описание контроля.

}, - system_management: { title: "Система управления", content:

Система управления

Описание системы управления.

}, - conference: { title: "Проведение ВКС", content:

Проведение ВКС

Информация о проведении ВКС.

}, - backup: { title: "Резервное копирование", content:

Резервное копирование

Процесс резервного копирования.

}, - relay_info: { title: "Ретрансляция информации", content:

Ретрансляция информации

Детали ретрансляции.

}, - hardware_software_1: { title: "График скорости сети", content:

График скорости сети

}, - hardware_software_2: { title: "ПО2", content:

ПО2

}, - hardware_software_3: { title: "ПО3", content:

ПО3

}, - hardware_software_4: { title: "ПО4", content:

ПО4

}, - hardware_software_5: { title: "ПО5", content:

ПО5

}, -}; - -export default tabContent; \ No newline at end of file diff --git a/src/Style/Dashboard.css b/src/Style/Dashboard.css index 8a83bd3..e39b9ad 100644 --- a/src/Style/Dashboard.css +++ b/src/Style/Dashboard.css @@ -18,53 +18,12 @@ /* Ограничиваем высоту */ } - -/* Вкладки */ -.tabs { - display: flex; - gap: 5px; - padding: 5px; - background-color: #222; - border-bottom: 2px solid #444; - overflow-x: auto; - white-space: nowrap; -} - -.tab { - display: flex; - align-items: center; - background-color: #333; - color: white; - padding: 5px 10px; - border-radius: 5px 5px 0 0; - cursor: pointer; - max-width: 250px; - /* Ограничиваем максимальную ширину */ - min-width: 100px; - /* Минимальная ширина */ - flex-shrink: 0; - /* Не позволяет вкладкам сжиматься */ - position: relative; -} - -.tab.active { - background-color: #555; -} - -.close-tab { - background: none; - border: none; - cursor: pointer; - font-size: 16px; - padding: 0; -} - /* Контент */ .content { background-color: #f9f9f9; padding: 20px; border-radius: 10px; - box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px 5px rgba(29, 1, 1, 0.521); } .default-content { diff --git a/src/Style/ErrorIndicator.css b/src/Style/ErrorIndicator.css index efce90a..115f992 100644 --- a/src/Style/ErrorIndicator.css +++ b/src/Style/ErrorIndicator.css @@ -2,6 +2,7 @@ display: flex; align-items: center; gap: 15px; + padding-bottom: 20px; } .error-item { @@ -33,4 +34,5 @@ align-items: center; gap: 15px; justify-content: center; + } \ No newline at end of file diff --git a/src/Style/LoginModal.css b/src/Style/LoginModal.css index 0d6d28c..ae99fe0 100644 --- a/src/Style/LoginModal.css +++ b/src/Style/LoginModal.css @@ -11,7 +11,7 @@ } .modal { - background: white; + background: rgb(255, 255, 255); padding: 20px; border-radius: 8px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); @@ -26,6 +26,7 @@ .modal label { display: block; margin-bottom: 5px; + color: black; } .modal input { @@ -38,7 +39,8 @@ .modal button { padding: 10px 20px; - background: #007bff; + margin-bottom: 5px; + background: #08294b; color: white; border: none; border-radius: 4px; diff --git a/src/Style/SidebarMenu.css b/src/Style/SidebarMenu.css index 2956424..749045c 100644 --- a/src/Style/SidebarMenu.css +++ b/src/Style/SidebarMenu.css @@ -1,10 +1,10 @@ /* Боковое меню */ .sidebar { - width: 250px; - background-color: #333; + width: 270px; + background-color: #3d74c7; padding: 20px; box-sizing: border-box; - border-right: 1px solid #444; + border-right: 1px solid white; height: 100vh; /* Занимает всю высоту экрана */ overflow-y: auto; @@ -36,14 +36,15 @@ h2 { justify-content: space-between; align-items: center; padding: 10px; - background-color: #444; + background-color: #3d74c7; border-radius: 5px; + border: 1px solid white; cursor: pointer; transition: background-color 0.3s ease; } .menu-item-header:hover { - background-color: #222; + background-color: #195fc9; } .submenu { @@ -57,8 +58,8 @@ h2 { .tab { padding: 10px; - background-color: #444; - border: 1px solid #333; + background-color: #3d74c7; + border: 1px solid white; border-radius: 5px; margin-bottom: 5px; cursor: pointer; @@ -66,5 +67,5 @@ h2 { } .tab:hover { - background-color: #222; + background-color: #3d74c7; } \ No newline at end of file diff --git a/src/Style/TreeTable.css b/src/Style/TreeTable.css new file mode 100644 index 0000000..4549aee --- /dev/null +++ b/src/Style/TreeTable.css @@ -0,0 +1,67 @@ +.tree-table { + max-width: 90vw; /* Ограничение ширины таблицы, чтобы не растягивалась */ + min-width: 400px; /* Минимальная ширина для нормального отображения */ + margin: 0 auto; /* Центрирование */ + overflow-x: auto; + padding: 10px; + box-sizing: border-box; +} + +.tree-table-header { + display: flex; + justify-content: space-around; + width: 100%; + padding-bottom: 10px; + border-bottom: 2px solid #ccc; +} + +.tree-table-column { + flex: 1; + text-align: center; + min-width: 150px; + max-width: 100%; +} + +.tree-table-title { + font-weight: bold; + font-size: 18px; + color: #333; +} + +.tree-table-body { + display: flex; + justify-content: space-around; + width: 100%; + margin-top: 15px; +} + +.tree-table-items { + display: flex; + flex-direction: column; + align-items: center; + gap: 8px; + width: 100%; +} + +/* Ограничение по ширине, чтобы элементы не растягивали таблицу */ +.tree-table-item { + width: 170px; + /* Фиксированная ширина для всех элементов */ + height: 40px; + /* Фиксированная высота */ + display: flex; + align-items: center; + justify-content: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + padding: 10px; + background-color: white; + border-radius: 5px; + box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1); +} + +.tree-table-item:hover { + transform: scale(1.05); + background: #f1f1f1; +} \ No newline at end of file diff --git a/src/Style/common.css b/src/Style/common.css new file mode 100644 index 0000000..cbc0f84 --- /dev/null +++ b/src/Style/common.css @@ -0,0 +1,43 @@ +/* src/Style/common.css */ +/* Вкладки */ +.tabs { + display: flex; + gap: 5px; + padding: 5px; + background-color: #3d74c7; + border-bottom: 2px solid #195fc9; + overflow-x: auto; + border-radius: 5px; + white-space: nowrap; +} + +.tab { + display: flex; + align-items: center; + background-color: #3d74c7; + color: white; + padding: 5px 10px; + border-radius: 5px 5px 0 0; + cursor: pointer; + max-width: 250px; + min-width: 100px; + flex-shrink: 0; + position: relative; +} + +.tab.active { + background-color: #195fc9; +} + +.close-tab { + background: none; + border: none; + cursor: pointer; + font-size: 16px; + padding: 0; +} + +.error { + color: red; + margin-bottom: 10px; +} \ No newline at end of file