From 2d714b598562245f5818ae847a5b7eb0489bdee9 Mon Sep 17 00:00:00 2001 From: DmitriyA Date: Tue, 25 Mar 2025 08:54:21 -0400 Subject: [PATCH] added the logo, refactored the graph --- package.json | 3 +- public/system_monitor_icon.svg | 26 +- src/App.jsx | 37 +- src/Components/Layout/Dashboard.jsx | 2 +- src/Components/TreeChart/FlowChart.jsx | 196 +++-------- .../FlowChartComponents/DataParser.jsx | 104 ++++++ .../FlowChartComponents/NodeWrapper.jsx | 26 ++ .../FlowChartComponents/nodeUtils.jsx | 3 + .../FlowChartComponents/useFlowChart.jsx | 46 +++ .../FlowChartComponents/useNodeHandlers.jsx | 24 ++ src/Components/TreeChart/TreeChart.jsx | 139 -------- .../TreeChart/TreeChartComponents/Label.jsx | 21 -- .../TreeChart/TreeChartComponents/Link.jsx | 16 - .../TreeChart/TreeChartComponents/Node.jsx | 19 -- .../TreeChartComponents/NodePosition.jsx | 30 -- src/Components/UI/LoginModal.jsx | 1 + src/Components/hooks/TabContent.jsx | 2 +- src/assets/images/logo.svg | 319 ++++++++++++++++++ vite.config.js | 8 +- 19 files changed, 625 insertions(+), 397 deletions(-) mode change 100755 => 100644 public/system_monitor_icon.svg create mode 100644 src/Components/TreeChart/FlowChartComponents/DataParser.jsx create mode 100644 src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx create mode 100644 src/Components/TreeChart/FlowChartComponents/nodeUtils.jsx create mode 100644 src/Components/TreeChart/FlowChartComponents/useFlowChart.jsx create mode 100644 src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx delete mode 100755 src/Components/TreeChart/TreeChart.jsx delete mode 100644 src/Components/TreeChart/TreeChartComponents/Label.jsx delete mode 100644 src/Components/TreeChart/TreeChartComponents/Link.jsx delete mode 100644 src/Components/TreeChart/TreeChartComponents/Node.jsx delete mode 100644 src/Components/TreeChart/TreeChartComponents/NodePosition.jsx create mode 100644 src/assets/images/logo.svg diff --git a/package.json b/package.json index b6412fd..8c3fa88 100755 --- a/package.json +++ b/package.json @@ -24,7 +24,8 @@ "@emotion/styled": "^11.14.0", "@mui/material": "^6.4.7", "@mui/icons-material": "^6.4.8", - "reactflow": "^11.11.4" + "reactflow": "^11.11.4", + "vite-plugin-svgr": "^4.3.0" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/public/system_monitor_icon.svg b/public/system_monitor_icon.svg old mode 100755 new mode 100644 index be3a475..ba02f29 --- a/public/system_monitor_icon.svg +++ b/public/system_monitor_icon.svg @@ -1,15 +1,11 @@ - - - - - - - - - - - \ No newline at end of file + + + + + + + + + + + diff --git a/src/App.jsx b/src/App.jsx index 2b0954c..b0345dd 100755 --- a/src/App.jsx +++ b/src/App.jsx @@ -3,7 +3,7 @@ import { ThemeProvider, CssBaseline, Switch, Box } from "@mui/material"; import Dashboard from "./Components/Layout/Dashboard"; import LoginModal from "./Components/UI/LoginModal"; import { lightTheme, darkTheme } from "./Style/theme"; -import "./Style/LoginModal.css"; +import Logo from './assets/images/logo.svg?react'; // Импорт как компонента function App() { const [isAuthenticated, setIsAuthenticated] = useState(false); @@ -23,9 +23,38 @@ function App() { {!isAuthenticated && showLoginModal ? ( - setShowLoginModal(false)} /> + <> + {/* Логотип */} + + + + + setShowLoginModal(false)} + /> + ) : ( - + setIsDarkMode((prev) => !prev)} /> @@ -36,4 +65,4 @@ function App() { ); } -export default App; +export default App; \ No newline at end of file diff --git a/src/Components/Layout/Dashboard.jsx b/src/Components/Layout/Dashboard.jsx index 1db005c..77b2715 100755 --- a/src/Components/Layout/Dashboard.jsx +++ b/src/Components/Layout/Dashboard.jsx @@ -59,7 +59,7 @@ const Dashboard = () => {
{/* Сайдбар */}
- +
diff --git a/src/Components/TreeChart/FlowChart.jsx b/src/Components/TreeChart/FlowChart.jsx index 63e3c85..082cdd6 100644 --- a/src/Components/TreeChart/FlowChart.jsx +++ b/src/Components/TreeChart/FlowChart.jsx @@ -1,170 +1,70 @@ -import React, { useCallback, useEffect, useState, useMemo } from 'react'; -import ReactFlow, { Controls, Background, useNodesState, useEdgesState } from 'reactflow'; +import React, { useEffect, useMemo } from 'react'; +import ReactFlow, { Controls, Background } from 'reactflow'; import 'reactflow/dist/style.css'; -import { statusManager1, getStatusColor } from './dataUtils'; +import { debounce } from 'lodash'; +import { useFlowChart } from './FlowChartComponents/useFlowChart'; +import { useNodeHandlers } from './FlowChartComponents/useNodeHandlers'; +import { useDataParser } from './FlowChartComponents/DataParser'; +import NodeWrapper from './FlowChartComponents/NodeWrapper'; // Исправленный импорт + +// Определяем кастомные типы узлов +const nodeTypes = { + customNode: NodeWrapper // Тип должен соответствовать тому, что вы указываете в parseData +}; const FlowChart = ({ data }) => { - const [nodes, setNodes, onNodesChange] = useNodesState([]); - const [edges, setEdges, onEdgesChange] = useEdgesState([]); - const [collapsedNodes, setCollapsedNodes] = useState(new Set()); // Состояние для свернутых узлов - const [nodePositions, setNodePositions] = useState({}); // Состояние для сохранения позиций узлов + const { + nodes, + edges, + nodePositions, + setNodes, + setEdges, + onNodesChange, + onEdgesChange, + setNodePositions, + collapsedNodes, + toggleNodeCollapse + } = useFlowChart(data); - // Обновление статусов - useEffect(() => { - const updateStatuses = (data) => { - statusManager1.updateStatuses(data); - }; + const { parseData } = useDataParser(nodePositions, collapsedNodes); - updateStatuses(data); // Обновляем статусы только при изменении данных - }, [data]); // Зависимость от data - - // Функция для построения радиального графа - const parseData = useCallback( - (data) => { - const nodes = []; - const edges = []; - - const centerX = 500; // Центр графа по X - const centerY = 400; // Центр графа по Y - const levelRadius = 150; // Расстояние между уровнями - - // Основная рекурсивная функция - const traverse = (item, parentId = null, level = 0, angleStart = 0, angleEnd = 2 * Math.PI) => { - const nodeId = item.id; - - // Угол узла на круге - const angle = (angleStart + angleEnd) / 2; // Угол для текущего узла - const nodeX = centerX + Math.cos(angle) * level * levelRadius; - const nodeY = centerY + Math.sin(angle) * level * levelRadius; - - // Используем сохраненную позицию, если она есть - const savedPosition = nodePositions[nodeId]; - const position = savedPosition || { x: nodeX, y: nodeY }; - - const node = { - id: nodeId, - position, - style: { - width: 50, - height: 50, - borderRadius: '50%', // Делаем круги - backgroundColor: getStatusColor(item.status), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - color: 'white', - border: '2px solid #fff', - }, - data: { label: item.title }, - }; - - nodes.push(node); - - // Создаем ребро к родителю - if (parentId) { - edges.push({ id: `${parentId}-${nodeId}`, source: parentId, target: nodeId }); - } - - // Дочерние узлы (если узел не свернут) - if (item.items && item.items.length > 0 && !collapsedNodes.has(nodeId)) { - const angleStep = (angleEnd - angleStart) / item.items.length; // Шаг угла для дочерних узлов - item.items.forEach((child, index) => { - // Равномерно распределяем дочерние узлы вокруг родителя - traverse( - child, - nodeId, - level + 1, - angleStart + index * angleStep, // Начальный угол для дочернего узла - angleStart + (index + 1) * angleStep // Конечный угол для дочернего узла - ); - }); - } - }; - - // Начинаем с центрального узла - const centerNode = { - id: data.id, - position: { x: centerX, y: centerY }, - style: { - width: 50, - height: 50, - borderRadius: '50%', - backgroundColor: getStatusColor(data.status), - display: 'flex', - alignItems: 'center', - justifyContent: 'center', - color: 'white', - border: '2px solid #fff', - }, - data: { label: data.title }, - }; - - nodes.push(centerNode); - - // Обрабатываем дочерние узлы центрального узла - if (data.items && data.items.length > 0 && !collapsedNodes.has(data.id)) { - const angleStep = (2 * Math.PI) / data.items.length; // Равномерно распределяем вокруг центра - data.items.forEach((child, index) => { - traverse( - child, - data.id, - 1, // Уровень 1 (первый круг вокруг центра) - index * angleStep, // Начальный угол - (index + 1) * angleStep // Конечный угол - ); - }); - } - - return { nodes, edges }; - }, - [collapsedNodes, nodePositions] // Зависимость от collapsedNodes и nodePositions + const debouncedSetNodePositions = useMemo( + () => debounce(setNodePositions, 100), + [setNodePositions] ); - // Обработчик клика по узлу - const onNodeClick = useCallback( - (event, node) => { - const nodeId = node.id; - const newCollapsedNodes = new Set(collapsedNodes); + const { onNodeDrag, onNodeDragStop } = useNodeHandlers(debouncedSetNodePositions); - if (newCollapsedNodes.has(nodeId)) { - newCollapsedNodes.delete(nodeId); // Разворачиваем узел - } else { - newCollapsedNodes.add(nodeId); // Сворачиваем узел - } - - setCollapsedNodes(newCollapsedNodes); // Обновляем состояние - }, - [collapsedNodes] - ); - - // Обработчик завершения перетаскивания узла - const onNodeDragStop = useCallback( - (event, node) => { - // Сохраняем новую позицию узла - setNodePositions((prevPositions) => ({ - ...prevPositions, - [node.id]: node.position, - })); - }, - [] - ); - - // Обновляем узлы и ребра при изменении данных useEffect(() => { const { nodes: initialNodes, edges: initialEdges } = parseData(data); setNodes(initialNodes); setEdges(initialEdges); }, [data, parseData, setNodes, setEdges]); + const onNodeClick = (event, node) => { + if (node.data.hasChildren) { + toggleNodeCollapse(node.id); + } + }; + + useEffect(() => { + return () => { + debouncedSetNodePositions.cancel(); + }; + }, [debouncedSetNodePositions]); + return ( -
+
@@ -174,4 +74,4 @@ const FlowChart = ({ data }) => { ); }; -export default React.memo(FlowChart); // Оптимизация рендера \ No newline at end of file +export default React.memo(FlowChart); \ No newline at end of file diff --git a/src/Components/TreeChart/FlowChartComponents/DataParser.jsx b/src/Components/TreeChart/FlowChartComponents/DataParser.jsx new file mode 100644 index 0000000..73855cd --- /dev/null +++ b/src/Components/TreeChart/FlowChartComponents/DataParser.jsx @@ -0,0 +1,104 @@ +import { useCallback } from 'react'; +import { isLeafNode } from './nodeUtils'; +import { getStatusColor } from '../dataUtils'; + +export const useDataParser = (nodePositions, collapsedNodes) => { + const getNodeStyle = useCallback((item, isLeaf) => ({ + width: isLeaf ? 40 : 50, + height: isLeaf ? 40 : 50, + borderRadius: '50%', + backgroundColor: getStatusColor(item.status), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: 'black', + border: '2px solid #fff', + fontSize: isLeaf ? '0.8rem' : '1rem' + }), []); + + const getCenterNodeStyle = useCallback((item) => ({ + width: 60, + height: 60, + borderRadius: '50%', + backgroundColor: getStatusColor(item.status), + display: 'flex', + alignItems: 'center', + justifyContent: 'center', + color: 'black', + border: '2px solid #fff', + fontSize: '1.2rem' + }), []); + + const parseData = useCallback((data) => { + if (!data) return { nodes: [], edges: [] }; + + const nodes = []; + const edges = []; + const centerX = 500; + const centerY = 400; + const baseLevelRadius = 150; + + const traverse = (item, parentId = null, level = 0, angleStart = 0, angleEnd = 2 * Math.PI, parentRadius = 0) => { + if (!item || collapsedNodes[parentId]) return; // Пропускаем свёрнутые узлы + + const nodeId = item.id; + const items = item.items || []; + const isLeaf = isLeafNode(item); + + const savedPosition = nodePositions[nodeId]; + let position = savedPosition || { + x: Math.round(centerX + Math.cos((angleStart + angleEnd) / 2) * (parentRadius + baseLevelRadius)), + y: Math.round(centerY + Math.sin((angleStart + angleEnd) / 2) * (parentRadius + baseLevelRadius)) + }; + + const node = { + id: nodeId, + position, + style: getNodeStyle(item, isLeaf), + data: { label: item.title, hasChildren: items.length > 0, collapsed: collapsedNodes[nodeId] } + }; + + nodes.push(node); + + if (parentId) { + edges.push({ + id: `${parentId}-${nodeId}`, + source: parentId, + target: nodeId, + style: { stroke: isLeaf ? '#aaa' : '#666', strokeWidth: isLeaf ? 1 : 2 } + }); + } + + if (!collapsedNodes[nodeId] && items.length > 0) { + const spreadAngle = angleEnd - angleStart; + items.forEach((child, index) => { + if (!child) return; + const itemAngleStart = angleStart + (index / items.length) * spreadAngle; + const itemAngleEnd = angleStart + ((index + 1) / items.length) * spreadAngle; + traverse(child, nodeId, level + 1, itemAngleStart, itemAngleEnd, parentRadius + baseLevelRadius); + }); + } + }; + + const centerNode = { + id: data.id, + position: nodePositions[data.id] || { x: centerX, y: centerY }, + style: getCenterNodeStyle(data), + data: { label: data.title, hasChildren: data.items.length > 0, collapsed: collapsedNodes[data.id] } + }; + + nodes.push(centerNode); + + if (!collapsedNodes[data.id] && data.items.length > 0) { + const angleStep = (2 * Math.PI) / data.items.length; + data.items.forEach((child, index) => { + if (!child) return; + traverse(child, data.id, 1, index * angleStep, (index + 1) * angleStep, 0); + }); + } + + return { nodes, edges }; + }, [nodePositions, collapsedNodes, getNodeStyle, getCenterNodeStyle]); + + return { parseData }; +}; \ No newline at end of file diff --git a/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx b/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx new file mode 100644 index 0000000..af1283d --- /dev/null +++ b/src/Components/TreeChart/FlowChartComponents/NodeWrapper.jsx @@ -0,0 +1,26 @@ +import React, { memo } from 'react'; + +const NodeWrapper = memo(({ id, data, selected, style }) => { + return ( +
+ {data.label} + {data.hasChildren && ( + + {data.collapsed ? '+' : '-'} + + )} +
+ ); +}); + +export default NodeWrapper; diff --git a/src/Components/TreeChart/FlowChartComponents/nodeUtils.jsx b/src/Components/TreeChart/FlowChartComponents/nodeUtils.jsx new file mode 100644 index 0000000..5aab37a --- /dev/null +++ b/src/Components/TreeChart/FlowChartComponents/nodeUtils.jsx @@ -0,0 +1,3 @@ +export const isLeafNode = (item) => { + return !item.items || item.items.length === 0; +}; \ No newline at end of file diff --git a/src/Components/TreeChart/FlowChartComponents/useFlowChart.jsx b/src/Components/TreeChart/FlowChartComponents/useFlowChart.jsx new file mode 100644 index 0000000..b435fb5 --- /dev/null +++ b/src/Components/TreeChart/FlowChartComponents/useFlowChart.jsx @@ -0,0 +1,46 @@ +import { useState, useCallback, useEffect } from 'react'; +import { useNodesState, useEdgesState } from 'reactflow'; +import { statusManager1 } from '../dataUtils'; + +export const useFlowChart = (initialData) => { + const [nodes, setNodes, onNodesChange] = useNodesState([]); + const [edges, setEdges, onEdgesChange] = useEdgesState([]); + const [nodePositions, setNodePositions] = useState({}); + const [collapsedNodes, setCollapsedNodes] = useState({}); // Добавили + + const toggleNodeCollapse = useCallback((nodeId) => { + setCollapsedNodes((prev) => ({ + ...prev, + [nodeId]: !prev[nodeId] + })); + }, []); + + const initializeNodePositions = useCallback((nodes) => { + const positions = {}; + nodes.forEach(node => { + positions[node.id] = node.position; + }); + setNodePositions(positions); + }, []); + + useEffect(() => { + const updateStatuses = (data) => { + statusManager1.updateStatuses(data); + }; + updateStatuses(initialData); + }, [initialData]); + + return { + nodes, + edges, + nodePositions, + setNodes, + setEdges, + onNodesChange, + onEdgesChange, + setNodePositions, + collapsedNodes, + toggleNodeCollapse, + initializeNodePositions + }; +}; diff --git a/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx b/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx new file mode 100644 index 0000000..80e4fd0 --- /dev/null +++ b/src/Components/TreeChart/FlowChartComponents/useNodeHandlers.jsx @@ -0,0 +1,24 @@ +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) + }; + }, []); + + const onNodeDragStop = useCallback((event, node) => { + node.position = { + x: Math.round(node.position.x), + y: Math.round(node.position.y) + }; + debouncedSetNodePositions(prev => ({ + ...prev, + [node.id]: node.position + })); + }, [debouncedSetNodePositions]); + + return { onNodeDrag, onNodeDragStop }; +}; \ No newline at end of file diff --git a/src/Components/TreeChart/TreeChart.jsx b/src/Components/TreeChart/TreeChart.jsx deleted file mode 100755 index dad7ed9..0000000 --- a/src/Components/TreeChart/TreeChart.jsx +++ /dev/null @@ -1,139 +0,0 @@ -import React, { useRef, useEffect, useMemo, useState } from "react"; -import * as d3 from "d3"; -import { calculateNodePositions } from "./TreeChartComponents/NodePosition"; -import { getStatusColor } from "./dataUtils"; - -const TreeChart = ({ data }) => { - const chartRef = useRef(); - const nodePositions = useRef(new Map()); - const [treeData, setTreeData] = useState(data); - - // Пересчитываем позиции узлов при изменении данных - const { root, nodes, links } = useMemo(() => { - return calculateNodePositions(treeData, nodePositions.current); - }, [treeData]); - - useEffect(() => { - if (!chartRef.current) return; - - // Удаляем старый граф перед отрисовкой нового - d3.select(chartRef.current).selectAll("*").remove(); - - // Определяем границы графа - const xMin = d3.min(nodes, (d) => d.x) || -500; - const xMax = d3.max(nodes, (d) => d.x) || 500; - const yMin = d3.min(nodes, (d) => d.y) || -500; - const yMax = d3.max(nodes, (d) => d.y) || 500; - - const width = xMax - xMin + 200; - const height = yMax - yMin + 200; - - const svg = d3.select(chartRef.current) - .attr("width", "100%") - .attr("height", "100%") - .attr("viewBox", `${xMin - 100} ${yMin - 100} ${width} ${height}`) - .attr("style", "max-width: 100%; height: auto;") - .call(d3.zoom() - .scaleExtent([0.5, 5]) - .on("zoom", (event) => { - svg.select("g").attr("transform", event.transform); - }) - ); - - const g = svg.append("g"); - - g.append("g").attr("class", "links"); - g.append("g").attr("class", "nodes"); - g.append("g").attr("class", "labels"); - - // Рисуем связи - g.select(".links") - .selectAll("line") - .data(links) - .join("line") - .attr("stroke", "#999") - .attr("stroke-opacity", 0.6) - .attr("x1", (d) => d.source.x) - .attr("y1", (d) => d.source.y) - .attr("x2", (d) => d.target.x) - .attr("y2", (d) => d.target.y); - - // Рисуем узлы - g.select(".nodes") - .selectAll("circle") - .data(nodes) - .join("circle") - .attr("fill", (d) => getStatusColor(d.data.status)) - .attr("stroke", "#fff") - .attr("r", 7) - .attr("cx", (d) => d.x) - .attr("cy", (d) => d.y) - .on("click", (event, d) => toggleNode(d)) - .call(drag()); - - // Рисуем текстовые метки - g.select(".labels") - .selectAll("text") - .data(nodes) - .join("text") - .text((d) => (nodes.length > 50 ? "" : d.data.title)) - .attr("dx", 12) - .attr("dy", 4) - .style("user-select", "none") - .style("pointer-events", "none") - .style("fill", "var(--TreeChart-text-color)") - .attr("x", (d) => d.x + 12) - .attr("y", (d) => d.y + 4); - - }, [root, links, nodes]); - - const drag = () => { - function dragstarted(event, d) { - d3.select(this).raise().attr("stroke", "#000"); - } - - function dragged(event, d) { - d.x = event.x; - d.y = event.y; - d3.select(this).attr("cx", d.x).attr("cy", d.y); - - d3.select(this.parentNode) - .select("text") - .attr("x", d.x + 12) - .attr("y", d.y + 4); - - d3.select(chartRef.current) - .selectAll(".links line") - .filter((link) => link.source === d || link.target === d) - .attr("x1", (link) => link.source.x) - .attr("y1", (link) => link.source.y) - .attr("x2", (link) => link.target.x) - .attr("y2", (link) => link.target.y); - } - - function dragended(event, d) { - d3.select(this).attr("stroke", "#fff"); - nodePositions.current.set(d.data.id, { x: d.x, y: d.y }); - } - - return d3.drag().on("start", dragstarted).on("drag", dragged).on("end", dragended); - }; - - const toggleNode = (d) => { - d.data.collapsed = !d.data.collapsed; - - if (d.data.collapsed) { - d._children = d.data.children; - d.data.children = []; - } else { - d.data.children = d._children; - d._children = null; - } - - setTreeData({ ...treeData }); - }; - - return ; -}; - -export default TreeChart; diff --git a/src/Components/TreeChart/TreeChartComponents/Label.jsx b/src/Components/TreeChart/TreeChartComponents/Label.jsx deleted file mode 100644 index 50fda20..0000000 --- a/src/Components/TreeChart/TreeChartComponents/Label.jsx +++ /dev/null @@ -1,21 +0,0 @@ -import React from "react"; - -const Label = ({ node }) => { - return ( - - {node.data.title} - - ); -}; - -export default Label; \ No newline at end of file diff --git a/src/Components/TreeChart/TreeChartComponents/Link.jsx b/src/Components/TreeChart/TreeChartComponents/Link.jsx deleted file mode 100644 index 653539a..0000000 --- a/src/Components/TreeChart/TreeChartComponents/Link.jsx +++ /dev/null @@ -1,16 +0,0 @@ -import React from "react"; - -const Link = ({ link }) => { - return ( - - ); -}; - -export default Link; \ No newline at end of file diff --git a/src/Components/TreeChart/TreeChartComponents/Node.jsx b/src/Components/TreeChart/TreeChartComponents/Node.jsx deleted file mode 100644 index 3291608..0000000 --- a/src/Components/TreeChart/TreeChartComponents/Node.jsx +++ /dev/null @@ -1,19 +0,0 @@ -import React from "react"; -import * as d3 from "d3"; -import { getStatusColor } from "../dataUtils"; - -const Node = ({ node, onNodeClick, drag }) => { - return ( - drag(event, node)} - onClick={() => onNodeClick(node.data.id, node.data.title)} - /> - ); -}; - -export default Node; \ No newline at end of file diff --git a/src/Components/TreeChart/TreeChartComponents/NodePosition.jsx b/src/Components/TreeChart/TreeChartComponents/NodePosition.jsx deleted file mode 100644 index 2a7fd4d..0000000 --- a/src/Components/TreeChart/TreeChartComponents/NodePosition.jsx +++ /dev/null @@ -1,30 +0,0 @@ -import * as d3 from "d3"; - -export const calculateNodePositions = (data, nodePositions) => { - if (!data || !data.items) return { root: null, nodes: [], links: [] }; - - const root = d3.hierarchy(data, (d) => d.items); - const treeLayout = d3.tree().size([4000 * Math.PI, 300]); // Угловое распределение (радиан, радиус) - - treeLayout(root); // Заполняем координаты - - const nodes = root.descendants(); - const links = nodes - .filter((d) => d.parent) - .map((d) => ({ - source: d.parent, - target: d, - })); - - // Преобразуем полярные координаты в декартовые - nodes.forEach((node) => { - const angle = node.x; // x теперь угол (радианы) - const radius = node.y; // y теперь радиус - nodePositions.set(node.data.id, { - x: radius * Math.cos(angle), - y: radius * Math.sin(angle), - }); - }); - - return { root, nodes, links }; -}; diff --git a/src/Components/UI/LoginModal.jsx b/src/Components/UI/LoginModal.jsx index 98405a8..61c154d 100755 --- a/src/Components/UI/LoginModal.jsx +++ b/src/Components/UI/LoginModal.jsx @@ -1,6 +1,7 @@ import React, { useState } from "react"; import Modal from "./Modal"; import "../../Style/LoginModal.css"; +import Logo from '../../assets/images/logo.svg?react'; import TextField from '@mui/material/TextField'; const LoginModal = ({ onLogin, onClose }) => { diff --git a/src/Components/hooks/TabContent.jsx b/src/Components/hooks/TabContent.jsx index 5747736..10e58a0 100644 --- a/src/Components/hooks/TabContent.jsx +++ b/src/Components/hooks/TabContent.jsx @@ -1,6 +1,6 @@ import SystemStatusChart from "../../Charts/SystemStatusChart"; import TreeTable from "../UI/TreeTable"; -import TreeChart from "../TreeChart/TreeChart"; + import FlowChart from "../TreeChart/FlowChart"; const TabContent = ({ activeTab, statusHistories, treeData1, tabContent, handleOpenTab }) => { diff --git a/src/assets/images/logo.svg b/src/assets/images/logo.svg new file mode 100644 index 0000000..c767d1c --- /dev/null +++ b/src/assets/images/logo.svg @@ -0,0 +1,319 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/vite.config.js b/vite.config.js index b034150..7011d63 100755 --- a/vite.config.js +++ b/vite.config.js @@ -1,11 +1,15 @@ import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' +import svgr from 'vite-plugin-svgr' // https://vite.dev/config/ export default defineConfig({ - plugins: [react()], + plugins: [ + react(), + svgr() + ], server: { host: true, allowedHosts: ['dev.msf.enode'] } -}) +}) \ No newline at end of file