Обновил структуру, добавил графики, улучшил интерфейс
parent
01f0be9cdd
commit
46a8bbb35d
|
|
@ -11,7 +11,9 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"react": "^18.3.1",
|
"react": "^18.3.1",
|
||||||
"react-dom": "^18.3.1"
|
"react-dom": "^18.3.1",
|
||||||
|
"chart.js": "^4.0.0",
|
||||||
|
"react-chartjs-2": "^5.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@eslint/js": "^9.17.0",
|
"@eslint/js": "^9.17.0",
|
||||||
|
|
|
||||||
23
src/App.jsx
23
src/App.jsx
|
|
@ -1,13 +1,32 @@
|
||||||
import React from "react";
|
import React from "react";
|
||||||
import SidebarMenu from "./SidebarMenu/SidebarMenu"; // Импорт компонента бокового меню
|
import SidebarMenu from "./SidebarMenu/SidebarMenu"; // Импорт компонента бокового меню
|
||||||
|
import CpuTemperatureChart from './Charts/CpuTemperatureChart';
|
||||||
|
import ErrorIndicator from "./SidebarMenu/ErrorIndicator"; // Индикатор ошибок
|
||||||
|
import GpuTemperatureChart from './Charts/GpuTemperatureChart';
|
||||||
|
import RamUsageChart from './Charts/RamUsageChart'
|
||||||
|
import Dashboard from "./SidebarMenu/Dashboard";
|
||||||
function App() {
|
function App() {
|
||||||
return (
|
return (
|
||||||
<div style={{ display: "flex" }}>
|
<div style={{ display: "flex" }}>
|
||||||
<SidebarMenu />
|
<SidebarMenu />
|
||||||
<div style={{ padding: "20px", flex: 1 }}>Рабочая область</div>
|
<div style={{ marginLeft: "250px" }}>
|
||||||
|
<h1 className="text-xl font-bold">Мониторинг состояния системы</h1>
|
||||||
|
<div className="indicator-container">
|
||||||
|
{/* Индикатор ошибок */}
|
||||||
|
<h2 className="sidebar-indicator">Индикатор ошибок: </h2>
|
||||||
|
<ErrorIndicator criticalCount={3} warningCount={7} />
|
||||||
|
</div>
|
||||||
|
{/* График температуры CPU
|
||||||
|
<div className="flex justify-center items-center h-96 bg-gray-100">
|
||||||
|
<CpuTemperatureChart />
|
||||||
|
<GpuTemperatureChart />
|
||||||
|
<RamUsageChart />
|
||||||
|
</div> */}
|
||||||
|
<Dashboard/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default App;
|
export default App;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,75 @@
|
||||||
|
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 "../SidebarMenu/ExpandableInfo"
|
||||||
|
|
||||||
|
ChartJS.register(LineElement, PointElement, LinearScale, CategoryScale);
|
||||||
|
|
||||||
|
const CpuTemperatureChart = () => {
|
||||||
|
const chartRef = useRef(null);
|
||||||
|
const [data, setData] = useState({
|
||||||
|
labels: Array(10).fill("").map((_, i) => i), // 20 точек по X
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Температура CPU (°C)",
|
||||||
|
data: Array(20).fill(50), // Начальные значения (например, 50°C)
|
||||||
|
borderColor: "red",
|
||||||
|
borderWidth: 2,
|
||||||
|
fill: false,
|
||||||
|
cubicInterpolationMode: "monotone", // Сглаживание
|
||||||
|
tension: 0.4, // Делаем линию плавнее
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setData((prevData) => {
|
||||||
|
const newTemp = Math.floor(Math.random() * 20) + 50; // Генерируем новую температуру (50-70°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: " 45%" },
|
||||||
|
{ label: "Скорость", value: " 3.6 GHz" },
|
||||||
|
{ label: "Процессы", value: " 120" },
|
||||||
|
{ label: "Время работы", value: " 2 ч 30 мин" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-4 flex flex-col">
|
||||||
|
<h2 className="text-xl font-semibold mb-4">График температуры ЦП</h2>
|
||||||
|
<Line
|
||||||
|
ref={chartRef}
|
||||||
|
data={data}
|
||||||
|
options={{
|
||||||
|
animation: false, // Отключаем анимацию обновления (чтобы был плавный сдвиг)
|
||||||
|
scales: {
|
||||||
|
x: { display: true },
|
||||||
|
y: { min: 30, max: 80 }, // Ограничиваем Y (например, 30-80°C)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ExpandableInfo details={details} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CpuTemperatureChart;
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
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 "../SidebarMenu/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: Array(20).fill(50), // Начальные значения (например, 50°C)
|
||||||
|
borderColor: "blue",
|
||||||
|
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: " 20%" },
|
||||||
|
{ label: "Оперативная память ГП", value: " 1,2/7,9 ГБ" },
|
||||||
|
{ label: "Общая память ГП", value: " 1,2/7,9 ГБ" },
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-4 flex flex-col">
|
||||||
|
<h2 className="text-xl font-semibold mb-4">График температуры ГП</h2>
|
||||||
|
<Line
|
||||||
|
ref={chartRef}
|
||||||
|
data={data}
|
||||||
|
options={{
|
||||||
|
animation: false, // Отключаем анимацию обновления (чтобы был плавный сдвиг)
|
||||||
|
scales: {
|
||||||
|
x: { display: true },
|
||||||
|
y: { min: 30, max: 80 }, // Ограничиваем Y (например, 30-80°C)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ExpandableInfo details={details} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GpuTemperatureChart;
|
||||||
|
|
@ -0,0 +1,76 @@
|
||||||
|
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 "../SidebarMenu/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 (
|
||||||
|
<div className="w-full max-w-2xl mx-auto p-4 flex flex-col">
|
||||||
|
<h2 className="text-xl font-semibold mb-4">График загруженности ОЗУ</h2>
|
||||||
|
<Line
|
||||||
|
ref={chartRef}
|
||||||
|
data={data}
|
||||||
|
options={{
|
||||||
|
animation: false, // Отключаем анимацию обновления (чтобы был плавный сдвиг)
|
||||||
|
scales: {
|
||||||
|
x: { display: true },
|
||||||
|
y: { min: 0, max: 100 }, // Ограничиваем Y (например, 30-80°C)
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ExpandableInfo details={details} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default RamUsageChart;
|
||||||
|
|
@ -0,0 +1,46 @@
|
||||||
|
import React from "react";
|
||||||
|
import CpuTemperatureChart from "../Charts/CpuTemperatureChart";
|
||||||
|
import GpuTemperatureChart from "../Charts/GpuTemperatureChart";
|
||||||
|
import RamUsageChart from "../Charts/RamUsageChart";
|
||||||
|
|
||||||
|
import "../Style/Dashboard.css"; // Подключаем стили
|
||||||
|
|
||||||
|
const Dashboard = () => {
|
||||||
|
return (
|
||||||
|
<div className="dashboard-container">
|
||||||
|
{/* Левая колонка (Графики) */}
|
||||||
|
<div className="left-column">
|
||||||
|
<CpuTemperatureChart />
|
||||||
|
<GpuTemperatureChart /> {/* Можно заменить на другие графики */}
|
||||||
|
<RamUsageChart />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Правая колонка (Информационный блок) */}
|
||||||
|
<div className="right-column">
|
||||||
|
<h2>Информационный блок</h2>
|
||||||
|
<p>Здесь можно выводить любые данные о системе.</p>
|
||||||
|
|
||||||
|
<div className="info-item">
|
||||||
|
<span className="label">Температура CPU:</span>
|
||||||
|
<span className="value">65°C</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="info-item">
|
||||||
|
<span className="label">Загрузка процессора:</span>
|
||||||
|
<span className="value">45%</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="info-item">
|
||||||
|
<span className="label">Ошибки аппаратного обеспечения:</span>
|
||||||
|
<span className="value error">2</span>
|
||||||
|
</div>
|
||||||
|
<div className="info-item">
|
||||||
|
<span className="label">Ошибки программного обеспечения:</span>
|
||||||
|
<span className="value error">1</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dashboard;
|
||||||
|
|
@ -0,0 +1,24 @@
|
||||||
|
import React from "react";
|
||||||
|
import criticalIcon from "../assets/images/critical.png"; // Красный треугольник
|
||||||
|
import warningIcon from "../assets/images/warning.png"; // Желтый треугольник
|
||||||
|
import "../Style/ErrorIndicator.css"; // Подключаем стили
|
||||||
|
|
||||||
|
const ErrorIndicator = ({ criticalCount, warningCount }) => {
|
||||||
|
return (
|
||||||
|
<div className="error-indicator">
|
||||||
|
{/* Красный индикатор (критические ошибки) */}
|
||||||
|
<div className="error-item critical">
|
||||||
|
<img src={criticalIcon} alt="Критическая ошибка" />
|
||||||
|
<span>{criticalCount}</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Желтый индикатор (предупреждения) */}
|
||||||
|
<div className="error-item warning">
|
||||||
|
<img src={warningIcon} alt="Предупреждение" />
|
||||||
|
<span>{warningCount}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ErrorIndicator;
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
import React, { useState } from "react";
|
||||||
|
import "../Style/Expandable.css"
|
||||||
|
|
||||||
|
const ExpandableInfo = ({ details }) => {
|
||||||
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
|
const toggleExpand = () => {
|
||||||
|
setIsExpanded(!isExpanded);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="expandable-info">
|
||||||
|
<button onClick={toggleExpand} className="expand-button">
|
||||||
|
{isExpanded ? "Скрыть" : "Подробнее"}
|
||||||
|
</button>
|
||||||
|
{isExpanded && (
|
||||||
|
<div className="details-menu">
|
||||||
|
{details.map((detail, index) => (
|
||||||
|
<div key={index} className="detail-item">
|
||||||
|
<span className="label">{detail.label}:</span>
|
||||||
|
<span className="value">{detail.value}</span>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ExpandableInfo;
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import "./SidebarMenu.css"; // Импортируем стили для компонента
|
import "../Style/SidebarMenu.css"; // Импортируем стили для компонента
|
||||||
|
|
||||||
const menuItems = [
|
const menuItems = [
|
||||||
{
|
{
|
||||||
title: "Выбор сервиса",
|
title: "Выбор сервиса",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
.dashboard-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.left-column {
|
||||||
|
flex: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.right-column {
|
||||||
|
flex: 1;
|
||||||
|
background: #f3f3f3;
|
||||||
|
padding: 20px;
|
||||||
|
border-radius: 8px;
|
||||||
|
box-shadow: 0px 4px 8px rgba(0, 0, 0, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
h2 {
|
||||||
|
font-size: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.info-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 10px 0;
|
||||||
|
border-bottom: 1px solid #ddd;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #007bff;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value.error {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
.error-indicator {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-item img {
|
||||||
|
width: 30px;
|
||||||
|
height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-item span {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.critical span {
|
||||||
|
color: red;
|
||||||
|
}
|
||||||
|
|
||||||
|
.warning span {
|
||||||
|
color: orange;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,38 @@
|
||||||
|
.expandable-info {
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-button {
|
||||||
|
background-color: #444;
|
||||||
|
color: white;
|
||||||
|
border: none;
|
||||||
|
padding: 8px 16px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-button:hover {
|
||||||
|
background-color: #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.details-menu {
|
||||||
|
margin-top: 10px;
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid #ddd;
|
||||||
|
border-radius: 4px;
|
||||||
|
background-color: #f9f9f9;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.label {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.value {
|
||||||
|
color: #555;
|
||||||
|
}
|
||||||
|
|
@ -1,7 +1,10 @@
|
||||||
/* SidebarMenu.css */
|
/* SidebarMenu.css */
|
||||||
|
|
||||||
.sidebar {
|
.sidebar {
|
||||||
width: 250px;
|
position: fixed;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
|
width: 250px;
|
||||||
|
/* height: 100vh; */
|
||||||
background-color: #333;
|
background-color: #333;
|
||||||
color: white;
|
color: white;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
|
|
@ -14,6 +17,12 @@
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebar-indicator {
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: bold;
|
||||||
|
margin-bottom: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebar-section {
|
.sidebar-section {
|
||||||
margin-bottom: 15px;
|
margin-bottom: 15px;
|
||||||
}
|
}
|
||||||
|
|
@ -49,3 +58,9 @@
|
||||||
.sidebar-item:hover {
|
.sidebar-item:hover {
|
||||||
color: #ccc;
|
color: #ccc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.indicator-container {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 15px;
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.3 KiB |
Loading…
Reference in New Issue