diff --git a/src/App.jsx b/src/App.jsx
index c2372e5..dfe1af0 100755
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -1,11 +1,10 @@
import React, { useState, useMemo, useEffect } from "react";
-import { ThemeProvider, CssBaseline, Switch, Box, CircularProgress } from "@mui/material";
+import { ThemeProvider, CssBaseline, Switch, Box, CircularProgress, Typography } from "@mui/material";
import Dashboard from "./Components/Layout/Dashboard";
import LoginModal from "./Components/UI/LoginModal";
import { lightTheme, darkTheme } from "./Style/theme";
import Logo from './assets/images/logo.svg?react';
-import { checkAuth } from "./Components/UI/auth";
-import axios from 'axios';
+import { checkAuth } from "./Components/UI/auth";
function App() {
const [authState, setAuthState] = useState({
@@ -24,13 +23,11 @@ function App() {
const verifyAuth = async () => {
try {
const authStatus = await checkAuth();
-
setAuthState({
isAuthenticated: authStatus.isAuthenticated,
isLoading: false,
user: authStatus.user || null
});
-
setShowLoginModal(!authStatus.isAuthenticated);
} catch (error) {
console.error('Auth verification error:', error);
@@ -42,7 +39,6 @@ function App() {
setShowLoginModal(true);
}
};
-
verifyAuth();
}, []);
@@ -57,7 +53,7 @@ function App() {
const handleLogout = async () => {
try {
- await axios.get(`${import.meta.env.VITE_BACK_URL}/api/metrics`, {
+ await fetch('http://192.168.2.39:3000/api/auth/logout', {
method: 'POST',
credentials: 'include'
});
@@ -73,20 +69,25 @@ function App() {
}
};
+ // Полноэкранный лоадер во время проверки авторизации
if (authState.isLoading) {
return (
- Проверка авторизации...
+
+ Проверка авторизации...
+
);
@@ -95,26 +96,20 @@ function App() {
return (
- {!authState.isAuthenticated && showLoginModal ? (
+ {!authState.isAuthenticated ? (
<>
-
+
-
setShowLoginModal(false)}
/>
@@ -124,19 +119,15 @@ function App() {
display: "flex",
height: "100vh",
overflow: "hidden",
- bgcolor: "background.default",
- color: "text.primary"
+ bgcolor: "background.default"
}}>
-
+
+ setIsDarkMode(!isDarkMode)}
+ sx={{ position: "absolute", top: 10, right: 10 }}
/>
-
- setIsDarkMode((prev) => !prev)}
- />
-
)}
diff --git a/src/Charts/PrometheusChart.jsx b/src/Charts/PrometheusChart.jsx
index 420fd75..0b1ae11 100755
--- a/src/Charts/PrometheusChart.jsx
+++ b/src/Charts/PrometheusChart.jsx
@@ -6,6 +6,37 @@ import { ConnectionStatusIndicator } from './Components/ConnectionStatusIndicato
import { CurrentRangeDisplay } from './Components/CurrentRangeDisplay';
import { TIME_RANGES, COLORS, SECOND, MINUTE, HOUR, DAY } from './Components/constants';
import axios from 'axios';
+import Skeleton from '@mui/material/Skeleton';
+import Box from '@mui/material/Box';
+
+
+// Компонент Skeleton для графика
+const ChartSkeleton = () => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {[1, 2, 3, 4].map((_, i) => (
+
+ ))}
+
+
+);
const PrometheusChart = ({ metricName }) => {
const [chartData, setChartData] = useState(null);
@@ -395,11 +426,21 @@ const PrometheusChart = ({ metricName }) => {
}, [selectedGraphRange, chartData, interpolateData]);
if (chartData === null) {
- return
Loading data...
;
+ return ;
}
if (Object.keys(chartData).length === 0) {
- return No data available
;
+ return (
+
+ No data available
+
+ );
}
return (
diff --git a/src/Components/Layout/Dashboard.jsx b/src/Components/Layout/Dashboard.jsx
index 9b66e58..a747672 100755
--- a/src/Components/Layout/Dashboard.jsx
+++ b/src/Components/Layout/Dashboard.jsx
@@ -58,10 +58,11 @@ const Content = styled(Box)(({ theme }) => ({
const Dashboard = () => {
const { tabs, activeTab, handleOpenTab, handleCloseTab, setActiveTab } = useTabs("Главная");
- const { sidebarWidth, startResizing } = useSidebarResize(250);
+ const { sidebarWidth, startResizing } = useSidebarResize(290);
const [tabContent, setTabContent] = useState({});
const [treeData1, setTreeData1] = useState(menuData);
const [treeData2, setTreeData2] = useState(menuData);
+ const [collapsed, setCollapsed] = useState(false);
const [statusHistories, setStatusHistories] = useState({
history1: [],
history2: [],
@@ -103,12 +104,14 @@ const Dashboard = () => {
return (
{/* Сайдбар */}
-
+
diff --git a/src/Components/Layout/SidebarMenu.jsx b/src/Components/Layout/SidebarMenu.jsx
index 7efd192..98e7962 100644
--- a/src/Components/Layout/SidebarMenu.jsx
+++ b/src/Components/Layout/SidebarMenu.jsx
@@ -32,8 +32,7 @@ const SidebarResizer = styled('div')(({ theme }) => ({
zIndex: 2
}));
-const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing }) => {
- const [collapsed, setCollapsed] = useState(false);
+const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing, collapsed, setCollapsed }) => {
const [hovered, setHovered] = useState(false);
const [menuData, setMenuData] = useState(data);
@@ -105,11 +104,7 @@ const SidebarMenu = ({ data, onOpenTab, sidebarWidth, startResizing }) => {
}
}}
>
- {collapsed ? (
- hovered ? :
- ) : (
-
- )}
+ {collapsed ? : }
diff --git a/src/Components/UI/LoginModal.jsx b/src/Components/UI/LoginModal.jsx
index d38d7b7..22559d6 100755
--- a/src/Components/UI/LoginModal.jsx
+++ b/src/Components/UI/LoginModal.jsx
@@ -17,8 +17,7 @@ const LoginModal = ({ onLogin, onClose }) => {
e.preventDefault();
try {
- const response = await axios.post(
- `${import.meta.env.VITE_BACK_URL}/api/auth/login`, {
+ const response = await fetch('http://192.168.2.39:3000/api/auth/login', {
method: 'POST',
credentials: 'include',
headers: {
@@ -31,7 +30,7 @@ const LoginModal = ({ onLogin, onClose }) => {
if (data.success) {
localStorage.setItem('access_token', data.access_token);
- onLogin(data.user); // Передаем данные пользователя
+ onLogin(data.user);
onClose();
} else {
setError(data.message || "Неверный логин или пароль");
diff --git a/src/Components/UI/TreeTable.jsx b/src/Components/UI/TreeTable.jsx
index bbc86b1..3087ddb 100755
--- a/src/Components/UI/TreeTable.jsx
+++ b/src/Components/UI/TreeTable.jsx
@@ -324,7 +324,7 @@ const TreeTable = ({ data }) => {
diff --git a/src/Components/UI/auth.jsx b/src/Components/UI/auth.jsx
index 7532538..6d3699c 100644
--- a/src/Components/UI/auth.jsx
+++ b/src/Components/UI/auth.jsx
@@ -2,24 +2,21 @@ import axios from 'axios';
export const checkAuth = async () => {
try {
- const response = await axios.get(
- `${import.meta.env.VITE_BACK_URL}/api/auth/check`,
- {
- withCredentials: true, // аналог `credentials: 'include'` в fetch
+ const response = await fetch('http://192.168.2.39:3000/api/auth/check', {
+ method: 'GET',
+ credentials: 'include', // Важно для отправки cookies
headers: {
- Authorization: `Bearer ${localStorage.getItem('access_token') || ''}`,
+ 'Authorization': `Bearer ${localStorage.getItem('access_token') || ''}`,
},
- }
- );
+ });
- // У axios нет свойства .ok, проверяем статус 200-299
- if (response.status >= 200 && response.status < 300) {
- return response.data; // Данные уже в JSON, не нужно .json()
- } else {
+ if (!response.ok) {
throw new Error('Not authenticated');
- }
- } catch (err) {
- console.error('Auth check failed:', err);
- return { isAuthenticated: false };
+ }
+
+ return await response.json();
+} catch (err) {
+ console.error('Auth check failed:', err);
+ return { isAuthenticated: false };
}
};
\ No newline at end of file
diff --git a/src/Components/hooks/LazyChartBatchRender.jsx b/src/Components/hooks/LazyChartBatchRender.jsx
index a12f491..d9f1dca 100644
--- a/src/Components/hooks/LazyChartBatchRender.jsx
+++ b/src/Components/hooks/LazyChartBatchRender.jsx
@@ -1,28 +1,73 @@
-import { useEffect, useState } from "react";
+import React, { useEffect, useRef, useState } from 'react';
+import Box from '@mui/material/Box';
+import Skeleton from '@mui/material/Skeleton';
-const LazyChartBatchRenderer = ({ charts, batchSize = 3, delay = 150 }) => {
- const [visibleCharts, setVisibleCharts] = useState([]);
+const LazyChartBatchRenderer = ({ charts }) => {
+ const [visibleIndices, setVisibleIndices] = useState(new Set());
+ const placeholderRefs = useRef([]);
+
+ const ChartSkeleton = () => (
+
+
+
+
+
+
+
+
+
+
+ {[1, 2, 3, 4].map((_, i) => (
+
+ ))}
+
+
+ );
useEffect(() => {
- let index = 0;
- const timer = setInterval(() => {
- setVisibleCharts((prev) => [
- ...prev,
- ...charts.slice(index, index + batchSize),
- ]);
- index += batchSize;
- if (index >= charts.length) clearInterval(timer);
- }, delay);
+ const observer = new IntersectionObserver(
+ (entries) => {
+ setVisibleIndices((prev) => {
+ const updated = new Set(prev);
+ entries.forEach((entry) => {
+ const index = parseInt(entry.target.dataset.index, 10);
+ if (entry.isIntersecting) {
+ updated.add(index);
+ } else {
+ updated.delete(index);
+ }
+ });
+ return updated;
+ });
+ },
+ {
+ root: null,
+ rootMargin: '200px',
+ threshold: 0.1,
+ }
+ );
- return () => clearInterval(timer);
+ placeholderRefs.current.forEach((ref) => {
+ if (ref) observer.observe(ref);
+ });
+
+ return () => {
+ observer.disconnect();
+ };
}, [charts]);
return (
- <>
- {visibleCharts.map((chart, idx) => (
- {chart}
+
+ {charts.map((chart, index) => (
+
(placeholderRefs.current[index] = el)}
+ data-index={index}
+ >
+ {visibleIndices.has(index) ? chart : }
+
))}
- >
+
);
};
diff --git a/src/Style/theme.jsx b/src/Style/theme.jsx
index 017bc36..6d33a29 100644
--- a/src/Style/theme.jsx
+++ b/src/Style/theme.jsx
@@ -139,7 +139,7 @@ export const darkTheme = createTheme({
// Фоновые цвета
background: {
- default: "#1E1E1E", // Основной фон приложения
+ default: "#2d2d2d", // Основной фон приложения
paper: "#2d2d2d", // Фон "бумажных" поверхностей
},