Релиз 0.3-3
Some checks failed
ci/woodpecker/tag/woodpecker Pipeline failed

This commit is contained in:
parent 7bf34f498a
commit cf1d392f39
160 changed files with 216 additions and 15810 deletions

View file

@ -1,13 +0,0 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GoNets</title>
<link rel="icon" type="image/x-icon" href="/assets/icon-4fEsvohP.png" />
<script type="module" crossorigin src="/assets/index-QSWEVNb0.js"></script>
<link rel="stylesheet" crossorigin href="/assets/index-DWFkiel1.css">
</head>
<body>
<div id="root"></div>
</body>
</html>

View file

@ -1,13 +1,13 @@
import axios, { AxiosResponse } from "axios";
const axiosWithoutCredentials = axios.create({
baseURL: `https://localhost:8001`,
baseURL: `http://localhost:8001`,
})
export class NetworkService {
static createAxiosInstance(ipAddress: string) {
return axios.create({
baseURL: `https://${ipAddress}:8001`,
baseURL: `http://${ipAddress}:8001`,
});
}

View file

@ -100,7 +100,7 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
<CardContent isExpanded={isExpanded}>
<InfoRow>
<Label>Имя:</Label>
<Value>{device.hostname || 'Неизвестно'}</Value>
<Value>{device.hostname || device.ip}</Value>
</InfoRow>
<InfoRow>

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, {useState} from 'react';
import { DeviceUser } from '../../../store/useDeviceStore';
interface TableScannerProps {
@ -9,18 +9,19 @@ import {IconComponent} from "../../../const/buttons/icons";
import {buttonNames} from "../../../const/buttons/names";
import {ModalMap} from "../../modalwindows/windowsType";
import {useModalContext} from "../../../store";
import Pagination from "../Pagination";
const TableContainer = styled.div`
width: 100%;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
background: ${({ theme }) => theme.primary};
margin: 24px 0;
`;
const StyledTable = styled.table`
width: 100%;
border-collapse: separate;
border-spacing: 0;
width: 100%;
border-collapse: separate;
border-spacing: 0;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
background: ${({ theme }) => theme.primary};
`;
const TableHeaderCell = styled.th<{ sortable?: boolean }>`
@ -28,7 +29,7 @@ const TableHeaderCell = styled.th<{ sortable?: boolean }>`
text-align: left;
font-weight: 600;
font-size: 14px;
color: ${({ theme }) => theme.textSecondary};
color: ${({ theme }) => theme.textColor};
background: ${({ theme }) => theme.primary};
border-bottom: 1px solid ${({ theme }) => theme.borderColor};
transition: all 0.2s ease;
@ -47,6 +48,11 @@ const TableCell = styled.td`
color: ${({ theme }) => theme.text};
border-bottom: 1px solid ${({ theme }) => theme.borderColor};
transition: all 0.2s ease;
button{
background-color: transparent;
border: none;
cursor: pointer;
}
`;
const StatusBadge = styled.span<{ status:string }>`
@ -68,7 +74,23 @@ const TableScanner = ({devices}:TableScannerProps) => {
});
};
return (
// Пагинация
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage] = useState(10);
const indexOfLastDevice = currentPage * devicesPerPage;
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
const currentDevices = devices.slice(indexOfFirstDevice, indexOfLastDevice);
const totalPages = Math.ceil(devices.length / devicesPerPage);
const handlePrevPage = () => {
if(currentPage > 1) setCurrentPage(currentPage - 1);
}
const handleNextPage = () => {
if(currentPage < totalPages) setCurrentPage(currentPage + 1);
}
return (
<TableContainer>
<StyledTable>
<thead>
@ -84,24 +106,30 @@ const TableScanner = ({devices}:TableScannerProps) => {
</tr>
</thead>
<tbody>
{devices.map((item, i) => (
{currentDevices.map((item, i) => (
<TableRow key={i}>
<TableCell><StatusBadge status={item.status}></StatusBadge></TableCell>
<TableCell>{item.hostname || 'Неизвестен'}</TableCell>
<TableCell>{item.hostname || item.ip}</TableCell>
<TableCell>{item.vendor}</TableCell>
<TableCell>{item.ip}</TableCell>
<TableCell>{item.open_ports.join(', ') || '-'}</TableCell>
<TableCell>{item.last_seen}</TableCell>
<TableCell>{item.note || '-'}</TableCell>
<TableCell>
<span onClick={(e)=>openModalNote(e, item)}>
<button onClick={(e)=>openModalNote(e, item)}>
<IconComponent name={buttonNames[16]} width="26" height="26" />
</span>
</button>
</TableCell>
</TableRow>
))}
</tbody>
</StyledTable>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
/>
</TableContainer>
);
};

View file

@ -2,8 +2,10 @@
import { MainButton } from "../../../UI/buttons/mainButton"
import { workSpaceNames } from "../../../../const/workspace/names"
import { useWorkSpaceStore } from "../../../../store"
import styled from "styled-components"
import styled, {useTheme} from "styled-components"
import ThemeButton from "../../../UI/buttons/toggletheme"
import {buttonNames} from "../../../../const/buttons/names";
import {IconComponent} from "../../../../const/buttons/icons";
type Props = {
from: 'reg' | 'helloWindow' | 'workSpace'
@ -29,23 +31,41 @@ const Content = styled.div`
justify-content: space-between;
height: 100%;
box-sizing: border-box;
padding: 25px 50px;
background: ${(props => props.theme.secondary)};
padding: 40px 50px;
background: ${(props => props.theme.primary)};
border-radius: 18px;
position: relative;
`
const Listing = styled.div`
display: flex;
flex-direction: column;
gap: 15px;
background-color: ${(props => props.theme.secondary)};
border-radius: 10px;
padding: 24px 20px;
p{
font-weight: bold;
font-size: 24px;
}
`
const List = styled.div`
const List = styled.ul`
display: flex;
flex-direction: column;
gap: 15px;
margin-left: 10px;
margin: 10px 0;
padding: 0;
list-style-type: none;
font-size: 18px;
gap: 4px;
& li:before {
color: ${(props => props.theme.accent)};
content: "•";
font-size: 28px;
width: .8em;
display: inline-block;
position: relative;
top: 2px;
}
`
const Title = styled.div`
@ -55,43 +75,55 @@ const Title = styled.div`
const SubTitle = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
gap: 24px;
background-color:rgba(255, 248, 225, 0.6);
border-left: #ff9800 solid 4px;
border-radius: 0 8px 8px 0;
padding: 20px 12px;
a{
text-decoration: none;
color: ${(props => props.theme.errorColor)};
font-weight: 500;
font-size: 18px;
width: max-content;
}
p{
font-size: 18px;
color: ${(props=>props.theme.textColor)};
}
.icon{
width: 32px;
height: 32px;
fill: #ff9800;
}
`
const Links = styled.div`
display: flex;
flex-direction: column;
justify-content: start;
gap: 20px;
`
const LinkColumn = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
`
const LinkBlock = styled.div`
display: flex;
gap: 5px;
const LinkBlock = styled.a`
text-decoration: none;
color: ${(props => props.theme.accent)};;
`
const Copyright = styled.div`
width: 100%;
text-align: center;
margin-top: 50px;
text-align: start;
color: ${(props => props.theme.offlineColor)};
`
const ButtonContainer = styled.div`
position: absolute;
top: 12px;
right: 14px;
`
const AboutLine = styled.hr`
border: ${(props => props.theme.hoverBtn)} 1px solid;
width: 100%;
display: flex;
justifi-content: flex-start;
align-items: center;
`
const MainTitle = styled.div`
@ -99,24 +131,33 @@ const MainTitle = styled.div`
display: flex;
gap: 15px;
align-items: center;
justify-content: center;
padding-top: 50px;
justify-content: space-between;
padding-top: 10px;
box-sizing: border-box;
img{
width: 40px;
height: 40px;
width: 50px;
height: 50px;
}
.version{
text-wrap: nowrap;
background-color: ${(props => props.theme.hoverBtn)};
color: ${(props => props.theme.borderColor)};
border-radius: 20px;
font-size: 18px;
padding: 10px 18px;
line-height: 1;
}
.title{
display: flex;
justify-content: center;
align-items: center;
gap: 24px;
}
`
const Text = styled.div`
display: flex;
flex-direction: column;
gap: 30px;
`
export default function About({ from, onBack }: Props) {
const { setWorkModule } = useWorkSpaceStore()
const getBackHandler = () => {
switch (from) {
case 'reg':
@ -138,68 +179,63 @@ export default function About({ from, onBack }: Props) {
<Content>
<MainTitle>
<img src="./assets/images/gossys.png" alt="" />
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
<div className={'title'}>
<img src="./assets/images/gossys.png" alt="" />
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
</div>
<p className={'version'}>v 0.0.3</p>
</MainTitle>
<AboutLine/>
<Text>
<Title>
<p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
</Title>
<Title>
<p>Приложение ГоNets является минимально работоспособным кроссплатформенным продуктом (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
</Title>
<Listing>
<p>Возможности:</p>
<List>
<li>Визуализация активных сетевых ресурсов</li>
<li>Настройка параметров локальных и удалённых сетевых ресурсов</li>
<li>Управление взаимодействием ресурсов с Клиентом ГoNets</li>
</List>
</Listing>
<SubTitle>
<svg className="icon" viewBox="0 0 24 24">
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
</svg>
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
<a href={'https://gossys.dev/pages/projects/gonets'}>Перейти на ГоNets-PRO</a>
</SubTitle>
<Listing>
<p>Данное приложение умеет:</p>
<List>
<p>- Отображать в графическом виде активные сетевые ресурсы</p>
<p>- Настраивать сетевые параметры локального и удаленных сетевых ресурсов</p>
<p>- Отображать и изменять взаимодействие сетевых ресурсов с установленным Клиентом ГоNets</p>
</List>
</Listing>
<Links>
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
Домашняя страницa
</LinkBlock>
<SubTitle>
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
<p>Для коммерческого использования приобретите версию ГоNets-PRO (Подробная информация на странице продукта)</p>
</SubTitle>
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
Документация
</LinkBlock>
<Links>
<LinkBlock>
<p>Ссылка на домашнюю страницу:</p>
<a href="https://gossys.dev/pages/projects/gonets">https://gossys.dev/pages/projects/gonets</a>
</LinkBlock>
<LinkBlock>
<p>Инструкции и информация:</p>
<a href="https://wiki.gossys.dev/ru/gonets">https://wiki.gossys.dev/ru/gonets</a>
</LinkBlock>
<LinkColumn>
<p>Распространяется под лицензией GosysPUB текущей версии.</p>
<LinkBlock>
<p>Полный текст актуальной лицензии:</p>
<a href="https://wiki.gossys.dev/ru/lic/GosysPUB">https://wiki.gossys.dev/ru/lic/GosysPUB</a>
</LinkBlock>
</LinkColumn>
</Links>
<Copyright>
<p>Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.</p>
</Copyright>
</Text>
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
Лицензия
</LinkBlock>
</Links>
<Copyright>
Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.
</Copyright>
<ButtonContainer>
{showBackButton && backHandler && (
<MainButton
title="Назад"
<IconComponent
width="36"
height="36"
name={buttonNames[12]}
onClick={backHandler}
style={{ marginRight: '10px' }}
/>
)}
{from == 'reg' &&

View file

@ -1,4 +1,4 @@
import React from 'react';
import React, { useState } from 'react';
import styled from 'styled-components';
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
import {DeviceUser} from "../../../../../../store/useDeviceStore";
@ -28,10 +28,15 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
const activeDevices = devices.filter(device => device.status === 'online');
const inactiveDevices = devices.filter(device => device.status !== 'online');
// Пагинация
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage] = useState(10);
return (
<>
<ScanBtn onClick={handleScan} disabled={loading}>
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
{devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'}
</ScanBtn>
{devices.length > 0 ? (
@ -42,7 +47,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
}}>
{/* Колонка активных устройств */}
<div style={{ flex: 1 }}>
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные устройства ({activeDevices.length})</h3>
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные узлы ({activeDevices.length})</h3>
{activeDevices.map((device, index) => (
<DeviceCard
key={index}
@ -53,7 +58,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
</div>
{/* Колонка неактивных устройств */}
<div style={{ flex: 1 }}>
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные устройства ({inactiveDevices.length})</h3>
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные узлы ({inactiveDevices.length})</h3>
{inactiveDevices.map((device, index) => (
<DeviceCard
key={index}
@ -64,7 +69,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
</div>
</div>
) : scanInitiated ? (
<p style={{ marginTop: '20px' }}>Устройства не найдены</p>
<p style={{ marginTop: '20px' }}>Узлы не найдены</p>
) : null}
</>
);

View file

@ -30,13 +30,13 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
return (
<>
<ScanBtn onClick={handleScan} disabled={loading}>
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
{devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'}
</ScanBtn>
{devices.length > 0 ? (
<TableScanner devices={devices} />
) : scanInitiated ? (
<p style={{ marginTop: '20px' }}>Устройства не найдены</p>
<p style={{ marginTop: '20px' }}>Узлы не найдены</p>
) : null}
</>
);

View file

@ -77,7 +77,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
span {
position: absolute;
display: inline-block;
background: ${(props) => props.theme.accent};
background: ${(props) => props.theme.scannerColor};
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
@ -163,7 +163,7 @@ export default function UserScanner() {
return (
<Wrapper scanInitiated={scanInitiated} loading={loading}>
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование пользователей в сети</TitleWrapper>)}
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование узлов подсети</TitleWrapper>)}
<UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center"></span>
<span className="ring1"></span>
@ -177,7 +177,7 @@ export default function UserScanner() {
{loading ? (
<LoadingText>
Сканирование сети<ScanningText />
Сканирование узлов <ScanningText />
</LoadingText>
) : selectedView.value == 1 ? (
<TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />

View file

@ -136,7 +136,8 @@ const ScanBtn = styled.button<{scanInitiated: boolean}>`
`
export function WifiScanner() {
const { ipAddress } = useIpStore();
//const { ipAddress } = useIpStore();
const ipAddress = '192.168.1.147'
const [loading, setLoading] = useState(false);
const [scanInitiated, setScanInitiated] = useState(false);
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
@ -161,17 +162,19 @@ export function WifiScanner() {
return (
<Wrapper scanInitiated={scanInitiated} loading={loading}>
<TitleWrapper>Сканирование Wi-Fi сети</TitleWrapper>
<TitleWrapper>Сканирование точек доступа Wi-Fi сети</TitleWrapper>
<UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center circel"></span>
<span className="ring1 circel"></span>
<span className="ring2 circel"></span>
<span className="ring3 circel"></span>
{wifiNetworks.map((network) => (
{wifiNetworks.map((network, index) => (
<WifiDot
key={network.bssid}
network={network}
index={index}
total={wifiNetworks.length}
onSelect={setCurrentNetwork}
/>
))}
@ -186,11 +189,11 @@ export function WifiScanner() {
{loading ? (
<LoadingText>
Сканирование сети <ScanningText/>
Сканирование точек доступа <ScanningText/>
</LoadingText>
) : (
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }>
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'}
</ScanBtn>
)}
</Wrapper>

View file

@ -31,43 +31,45 @@ const Dot = styled.div<{ top: string; left: string }>`
}
`;
const WifiDot = ({ network, onSelect }: { network: WifiNetworkScanner, onSelect: (n: WifiNetworkScanner) => void }) => {
const getPositionInRing = (bssid: string, ringSize: number) => {
const hash = bssid.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
const randomAngle = (hash % 360) * (Math.PI / 180);
const innerRadius = ringSize * 0.4;
const outerRadius = ringSize * 0.5;
const randomValue = (hash % 1000) / 1000;
const randomRadius = Math.sqrt(innerRadius * innerRadius + randomValue * (outerRadius * outerRadius - innerRadius * innerRadius));
const getPositionInRing = (index: number, total: number, ringSize: number) => {
const angle = (index * (2 * Math.PI / total)) + (Math.PI / total * (index % 2));
const innerRadius = ringSize * 0.4;
const outerRadius = ringSize * 0.5;
const radius = innerRadius + (outerRadius - innerRadius) * (0.3 + 0.7 * (index % 10) / 10);
return {
x: Math.cos(randomAngle) * randomRadius,
y: Math.sin(randomAngle) * randomRadius
};
return {
x: Math.cos(angle) * radius,
y: Math.sin(angle) * radius
};
};
const getPosition = (signal: number, bssid: string) => {
const WifiDot = ({ network, index, total, onSelect }: {
network: WifiNetworkScanner,
index: number,
total: number,
onSelect: (n: WifiNetworkScanner) => void
}) => {
const getPosition = (signal: number, index: number, total: number) => {
let strength: 1 | 2 | 3;
if (signal >= -50) {
strength = 3;
} else if (signal >= -70) {
strength = 2;
} else if (signal >= -90) {
strength = 1;
} else {
strength = 1;
}
const ringSizes = { 1: 550, 2: 400, 3: 250 };
const ringSize = ringSizes[strength];
const offset = getPositionInRing(bssid, ringSize);
const offset = getPositionInRing(index, total, ringSize);
return {
top: `calc(50% - ${offset.y}px)`,
left: `calc(50% - ${offset.x}px)`
top: `calc(50% + ${offset.y}px)`,
left: `calc(50% + ${offset.x}px)`
};
};
const position = getPosition(network.signal, network.bssid);
const position = getPosition(network.signal, index, total);
return (
<Dot top={position.top} left={position.left} onClick={() => onSelect(network)}>

View file

@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
setHasInited(false)
initializationAttempt.current = 0;
const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`);
const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`);
let connectionTimeout;
setSocket(newSocket);

View file

@ -115,7 +115,7 @@ export const DevicesBar = () => {
const checkWebSocketAvailability = async (ip: string): Promise<boolean> => {
return new Promise((resolve) => {
const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`);
const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`);
const timeout = setTimeout(() => {
testSocket.close();
resolve(false);

View file

@ -9,6 +9,7 @@ import settings from '@iconify/icons-ic/baseline-settings';
import info from '@iconify/icons-ic/baseline-info';
import arrowDown from '@iconify/icons-ic/baseline-keyboard-arrow-down';
import arrowRight from '@iconify/icons-ic/baseline-keyboard-arrow-right';
import arrowLeft from '@iconify/icons-ic/baseline-keyboard-arrow-left';
import edit from '@iconify/icons-ic/baseline-edit';
import checkCircle from '@iconify/icons-ic/round-check-circle';
import cancel from '@iconify/icons-ic/baseline-cancel';
@ -37,6 +38,7 @@ export const ICONS = {
[buttonNames[14]]: userSearch,
[buttonNames[15]]: wifiFind,
[buttonNames[16]]: noteEdit,
[buttonNames[17]]: arrowLeft,
} as const;
type IconName = keyof typeof ICONS;

View file

@ -16,5 +16,6 @@ export const buttonNames: names ={
13: 'noAdmin',
14: 'userSearch',
15: 'wifiFind',
16: 'noteEdit'
16: 'noteEdit',
17: 'arrowLeft',
}

View file

@ -3,7 +3,7 @@ import { toolBarNames } from "./names"
import { useTheme } from "styled-components"
import { Icon } from '@iconify/react';
import desktop from '@iconify/icons-ic/round-desktop-windows';
import desktop from '@iconify/icons-ic/baseline-menu';
export const ICONS = {
[toolBarNames[1]]: desktop,

View file

@ -24,7 +24,7 @@ export const dark = {
background: '#3A3A3A',
textColor: '#FFFFFF',
borderColor: '#b6b6b6',
scannerColor: '#0E9C9D',
scannerColor: '#00E5FF',
offlineColor: '#656565',
hoverBtn: '#4a4a4a',
};

View file

@ -1,2 +0,0 @@
/node_modules
/dist

Binary file not shown.

Before

Width:  |  Height:  |  Size: 126 KiB

View file

@ -1,28 +0,0 @@
import js from '@eslint/js'
import globals from 'globals'
import reactHooks from 'eslint-plugin-react-hooks'
import reactRefresh from 'eslint-plugin-react-refresh'
import tseslint from 'typescript-eslint'
export default tseslint.config(
{ ignores: ['dist'] },
{
extends: [js.configs.recommended, ...tseslint.configs.recommended],
files: ['**/*.{ts,tsx}'],
languageOptions: {
ecmaVersion: 2020,
globals: globals.browser,
},
plugins: {
'react-hooks': reactHooks,
'react-refresh': reactRefresh,
},
rules: {
...reactHooks.configs.recommended.rules,
'react-refresh/only-export-components': [
'warn',
{ allowConstantExport: true },
],
},
},
)

View file

@ -1,12 +0,0 @@
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GoNets</title>
<link rel="icon" type="image/x-icon" href="assets/icon.png" />
</head>
<body>
<div id="root"></div>
<script type="module" src="./index.tsx"></script>
</body>
</html>

View file

@ -1,52 +0,0 @@
import React from 'react';
import { createRoot } from 'react-dom/client';
import { HashRouter } from 'react-router-dom';
import { useIpStore } from './src/store';
import { useEffect } from 'react';
import {useCheckAdmin} from './src/utils/checkAdmin.ts'
import {
ModalProvider,
useModalContext,
ThemeProvider
} from './src/store';
import WindowComponent from './src/components/modalwindows/modalWindow.tsx';
import Router from './src/router.tsx';
const MainComponent = () => {
const checkAdmin = useCheckAdmin()
const {ipAddress} = useIpStore()
useEffect(() => {
const checkAdminStatus = async () => {
await checkAdmin()
}
checkAdminStatus()
}, [ipAddress])
const { isOpen, modalType, modalData } = useModalContext();
return (
<>
<WindowComponent isOpen={isOpen} type={modalType} data={modalData} />
<Router />
</>
);
};
const container = document.getElementById('root');
if (!container) {
throw new Error('Root element with id "root" not found');
}
const root = createRoot(container);
root.render(
<ThemeProvider>
<HashRouter>
<ModalProvider>
<MainComponent />
</ModalProvider>
</HashRouter>
</ThemeProvider>
);

View file

@ -1,61 +0,0 @@
const { app, BrowserWindow, session, Menu, ipcMain } = require('electron');
const path = require('path');
ipcMain.on('quit-app', () => {
app.quit();
});
app.commandLine.appendSwitch('ignore-certificate-errors');
function createWindow() {
const win = new BrowserWindow({
icon: path.join(__dirname, 'assets/icon.png'),
webPreferences: {
preload: path.join(__dirname, 'preload.js'),
contextIsolation: true,
},
show: false
});
win.maximize();
win.show();
if (process.env.NODE_ENV === 'development') {
win.loadURL('https://localhost:8001');
win.webContents.openDevTools();
} else {
win.loadFile(path.join(__dirname, 'dist/index.html'));
}
}
app.whenReady().then(() => {
session.defaultSession.setCertificateVerifyProc((request, callback) => {
// Разрешаем для:
// 1. localhost
// 2. Всех IP в локальной сети (192.168.x.x, 10.x.x.x и т.д.)
// 3. Конкретного домена (если есть)
if (
request.hostname === 'localhost' ||
request.hostname === '127.0.0.1' ||
request.hostname === '0.0.0.0' ||
/^(192\.168\.|10\.|172\.(1[6-9]|2[0-9]|3[0-1])\.)/.test(request.hostname)
) {
return callback(0);
}
callback(-2);
});
Menu.setApplicationMenu(null);
createWindow();
});
app.on('window-all-closed', () => {
if (process.platform !== 'darwin') {
app.quit();
}
});
app.on('activate', () => {
if (BrowserWindow.getAllWindows().length === 0) {
createWindow();
}
});

File diff suppressed because it is too large Load diff

View file

@ -1,39 +0,0 @@
{
"name": "gonets",
"version": "0.0.2",
"main": "main.js",
"scripts": {
"dev": "vite",
"build": "tsc && vite build",
"preview": "vite preview",
"start": "electron .",
"package": "vite build && electron-packager . --out=dist --overwrite --icon=assets/icon"
},
"dependencies": {
"@iconify/icons-ic": "^1.2.13",
"@iconify/icons-ph": "^1.2.5",
"@iconify/react": "^5.2.0",
"@reduxjs/toolkit": "^2.6.0",
"@xyflow/react": "^12.3.5",
"axios": "^1.7.9",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-flow-renderer": "^10.3.17",
"react-redux": "^9.2.0",
"react-router-dom": "^7.0.2",
"styled-components": "^6.1.13",
"zustand": "^5.0.3"
},
"devDependencies": {
"@iconify-json/ic": "^1.2.2",
"@types/react": "^18.0.28",
"@types/react-dom": "^18.0.11",
"@vitejs/plugin-basic-ssl": "^2.0.0",
"@vitejs/plugin-react": "^4.4.1",
"electron": "^34.0.2",
"electron-packager": "^17.1.2",
"esbuild-wasm": "^0.17.12",
"typescript": "^4.9.5",
"vite": "^6.3.4"
}
}

View file

@ -1,19 +0,0 @@
const { contextBridge, ipcRenderer } = require('electron');
contextBridge.exposeInMainWorld('electron', {
send: (channel, data) => {
ipcRenderer.send(channel, data);
},
on: (channel, func) => {
ipcRenderer.on(channel, (event, ...args) => func(...args));
},
quitApp: () => {
ipcRenderer.send('quit-app');
},
onThemeChange: (callback) => {
ipcRenderer.on('theme-update', (event, theme) => {
callback(theme);
});
}
});

View file

@ -1,836 +0,0 @@
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 1815.11 418.96024210526315" style="max-height: 500px" width="1815.11" height="418.96024210526315">
<desc>
Created with Pixso.
</desc>
<defs>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19033_0" y2="-712.221313" x2="270.959900" y1="-1308.030151" x1="1840.109863">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19034_0" y2="-712.765381" x2="270.761597" y1="-1308.578369" x1="1839.901978">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19035_0" y2="-679.637817" x2="283.338562" y1="-1275.450806" x1="1852.479004">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19036_0" y2="-880.955322" x2="206.882507" y1="-1476.766113" x1="1776.022949">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19037_0" y2="-848.618530" x2="219.157166" y1="-1444.431519" x1="1788.306396">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19038_0" y2="-638.924438" x2="298.801636" y1="-1234.733765" x1="1867.942139">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19039_0" y2="-936.472778" x2="185.797729" y1="-1532.290161" x1="1754.938477">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19040_0" y2="-690.210938" x2="279.326172" y1="-1286.019775" x1="1848.466797">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19041_0" y2="-523.330200" x2="342.703064" y1="-1119.139282" x1="1911.843506">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19042_0" y2="-572.199951" x2="324.145142" y1="-1168.012207" x1="1893.285767">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19043_0" y2="-684.131104" x2="281.774048" y1="-1279.698242" x1="1850.905151">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19044_0" y2="-814.235718" x2="232.216248" y1="-1410.045044" x1="1801.366211">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19045_0" y2="-853.401001" x2="217.349915" y1="-1449.210327" x1="1786.489990">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19046_0" y2="-806.166626" x2="235.284180" y1="-1401.981812" x1="1804.432251">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19047_0" y2="-824.094238" x2="228.479492" y1="-1419.903076" x1="1797.619019">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19048_0" y2="-761.783691" x2="252.137390" y1="-1357.603516" x1="1821.277344">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19049_0" y2="-608.633911" x2="310.301025" y1="-1204.443481" x1="1879.449463">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19050_0" y2="-589.109619" x2="317.717834" y1="-1184.921387" x1="1886.862549">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19051_0" y2="-574.739014" x2="323.179565" y1="-1170.548340" x1="1892.312744">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19052_0" y2="-510.040039" x2="347.747314" y1="-1105.853027" x1="1916.899292">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19053_0" y2="-602.636475" x2="312.581299" y1="-1198.442871" x1="1881.727905">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19054_0" y2="-457.936646" x2="367.535767" y1="-1053.743042" x1="1936.684692">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19055_0" y2="-669.309570" x2="287.256714" y1="-1265.125000" x1="1856.405640">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19056_0" y2="-778.359009" x2="245.844360" y1="-1374.172485" x1="1814.993408">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19057_0" y2="-953.510498" x2="179.315979" y1="-1549.335693" x1="1748.464844">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19058_0" y2="-920.018555" x2="192.050781" y1="-1515.837646" x1="1761.191650">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19059_0" y2="-802.950073" x2="236.501221" y1="-1398.764526" x1="1805.654297">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19060_0" y2="-722.904053" x2="266.916870" y1="-1318.703857" x1="1836.050903">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19061_0" y2="-88.563477" x2="1586.212402" y1="507.200928" x1="17.034790">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19062_0" y2="81.508667" x2="1650.698730" y1="677.302612" x1="81.543884">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19063_0" y2="12.648682" x2="1624.639160" y1="608.453613" x1="55.482483">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19064_0" y2="-56.019775" x2="1598.548340" y1="539.775146" x1="29.407227">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19065_0" y2="-349.650391" x2="1487.043701" y1="246.165894" x1="-82.104065">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19066_0" y2="-542.731079" x2="335.340210" y1="-1138.536011" x1="1904.482666">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19067_0" y2="-867.725708" x2="211.896606" y1="-1463.536499" x1="1781.049194">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19068_0" y2="-832.167236" x2="225.404663" y1="-1427.976074" x1="1794.552490">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19069_0" y2="-785.108154" x2="243.289429" y1="-1380.918945" x1="1812.429199">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19070_0" y2="-746.875610" x2="257.806580" y1="-1342.691528" x1="1826.945801">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19071_0" y2="-431.014648" x2="377.765869" y1="-1026.823975" x1="1946.915649">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19072_0" y2="-578.737061" x2="321.657166" y1="-1174.551514" x1="1890.793945">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19073_0" y2="-640.375244" x2="298.242615" y1="-1236.190430" x1="1867.394775">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19074_0" y2="136.595459" x2="1671.701538" y1="732.386963" x1="102.556885">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19075_0" y2="-851.596802" x2="218.019714" y1="-1447.403687" x1="1787.174072">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19076_0" y2="91.515869" x2="1654.585449" y1="687.262451" x1="85.415100">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19077_0" y2="-636.860107" x2="299.587952" y1="-1232.671387" x1="1868.727295">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19078_0" y2="-635.054810" x2="300.269287" y1="-1230.862671" x1="1869.408447">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19079_0" y2="-643.379150" x2="297.107666" y1="-1239.189453" x1="1866.248291">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19080_0" y2="-196.003906" x2="1545.392700" y1="399.793701" x1="-23.754089">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19081_0" y2="-178.466919" x2="1552.046021" y1="417.357788" x1="-17.090454">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19082_0" y2="-70.970093" x2="1592.883179" y1="524.854614" x1="23.737427">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19083_0" y2="255.285400" x2="1716.785522" y1="851.064453" x1="147.633545">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19084_0" y2="-468.268066" x2="363.617554" y1="-1064.076416" x1="1932.758301">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19085_0" y2="-533.139160" x2="338.975037" y1="-1128.951660" x1="1908.124268">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19086_0" y2="-572.218994" x2="324.135681" y1="-1168.031982" x1="1893.276123">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19087_0" y2="-790.724487" x2="241.158875" y1="-1386.530640" x1="1810.299805">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19088_0" y2="-734.533569" x2="262.490845" y1="-1330.342651" x1="1831.630371">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19089_0" y2="-181.838135" x2="1550.774536" y1="413.973633" x1="-18.375244">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19091_0" y2="-770.605835" x2="248.787354" y1="-1366.422241" x1="1817.928101">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19092_0" y2="-656.410156" x2="292.158752" y1="-1252.219971" x1="1861.299316">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19093_0" y2="-807.879028" x2="234.638977" y1="-1403.695679" x1="1803.779297">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19094_0" y2="-541.958984" x2="335.700134" y1="-1137.770752" x1="1904.849854">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19095_0" y2="-911.495239" x2="195.279907" y1="-1507.300903" x1="1764.429443">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19096_0" y2="-959.253418" x2="177.138245" y1="-1555.057861" x1="1746.288818">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19097_0" y2="-985.545288" x2="167.164673" y1="-1581.354370" x1="1736.304932">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19098_0" y2="-878.533691" x2="207.800232" y1="-1474.343018" x1="1776.940430">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19099_0" y2="-587.691162" x2="318.258789" y1="-1183.504150" x1="1887.398560">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19100_0" y2="-562.047729" x2="327.997192" y1="-1157.858276" x1="1897.145996">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19101_0" y2="-968.866333" x2="173.487244" y1="-1564.679810" x1="1742.635010">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19102_0" y2="-838.678467" x2="222.942749" y1="-1434.488525" x1="1792.072754">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19103_0" y2="-763.245728" x2="251.588379" y1="-1359.052856" x1="1820.718628">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19104_0" y2="-745.715210" x2="258.241577" y1="-1341.525024" x1="1827.390259">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19105_0" y2="108.613647" x2="1661.081299" y1="704.385864" x1="91.922668">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19106_0" y2="-141.191772" x2="1566.211914" y1="454.617798" x1="-2.940430">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19107_0" y2="171.605225" x2="1685.069946" y1="767.390137" x1="115.908752">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19108_0" y2="-884.187988" x2="205.653015" y1="-1480.000244" x1="1774.792236">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19109_0" y2="-901.711304" x2="199.010132" y1="-1497.515259" x1="1768.139282">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19110_0" y2="37.703979" x2="1634.147949" y1="633.531860" x1="65.011292">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19111_0" y2="-541.860962" x2="335.662231" y1="-1137.676636" x1="1904.809448">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19112_0" y2="-547.931274" x2="333.363098" y1="-1143.741333" x1="1902.503662">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19113_0" y2="-463.031372" x2="365.604980" y1="-1058.843872" x1="1934.745361">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19114_0" y2="-436.564575" x2="375.646118" y1="-1032.376587" x1="1944.796387">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19115_0" y2="-830.786743" x2="225.932434" y1="-1426.595825" x1="1795.072998">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19116_0" y2="-994.658203" x2="163.690491" y1="-1590.466797" x1="1732.840576">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19117_0" y2="-394.041870" x2="391.800293" y1="-989.854614" x1="1960.950195">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19118_0" y2="-532.041260" x2="339.400513" y1="-1127.852295" x1="1908.539551">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19119_0" y2="-786.902832" x2="242.598938" y1="-1382.719727" x1="1811.747070">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19120_0" y2="-509.625732" x2="347.908325" y1="-1105.437744" x1="1917.047729">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19121_0" y2="187.665039" x2="1691.103516" y1="783.447876" x1="121.945007">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19122_0" y2="280.240845" x2="1726.264160" y1="876.082397" x1="157.135376">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19123_0" y2="-1026.346680" x2="151.663452" y1="-1622.157227" x1="1720.802856">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19124_0" y2="-365.034912" x2="402.824951" y1="-960.850830" x1="1971.964478">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19127_0" y2="-510.000244" x2="285.704285" y1="-1227.124512" x1="2022.169922">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19128_0" y2="-419.934448" x2="322.905457" y1="-1137.061157" x1="2059.371094">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19129_0" y2="-348.977417" x2="352.214783" y1="-1066.102173" x1="2088.689209">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19130_0" y2="-553.336060" x2="267.799377" y1="-1270.463257" x1="2004.265137">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19131_0" y2="-535.517944" x2="275.152161" y1="-1252.645386" x1="2011.627441">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19132_0" y2="-367.547485" x2="344.548401" y1="-1084.675659" x1="2081.014160">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19133_0" y2="-305.386230" x2="370.222900" y1="-1022.509644" x1="2106.698730">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19134_0" y2="-661.351562" x2="223.178467" y1="-1378.478271" x1="1959.653564">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19135_0" y2="-680.692017" x2="215.191284" y1="-1397.818970" x1="1951.666626">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19136_0" y2="-626.946045" x2="237.392456" y1="-1344.071777" x1="1973.867676">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19137_0" y2="-326.333984" x2="361.573853" y1="-1043.458496" x1="2098.039062">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19138_0" y2="-628.664062" x2="236.681213" y1="-1345.783936" x1="1973.149658">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19139_0" y2="-225.000977" x2="403.421448" y1="-942.129395" x1="2139.895020">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19140_0" y2="-114.167847" x2="449.206177" y1="-831.292358" x1="2185.671875">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19141_0" y2="-332.256226" x2="359.122803" y1="-1049.382690" x1="2095.584473">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19142_0" y2="-306.446167" x2="369.787659" y1="-1023.566772" x1="2106.252197">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19143_0" y2="-615.994141" x2="241.914551" y1="-1333.116699" x1="1978.383911">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19144_0" y2="-281.688843" x2="380.008789" y1="-998.813232" x1="2116.475586">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19145_0" y2="-452.273682" x2="309.542419" y1="-1169.396729" x1="2046.019531">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19146_0" y2="-475.159912" x2="300.086975" y1="-1192.284668" x1="2036.565186">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19147_0" y2="-512.887573" x2="284.501648" y1="-1230.013062" x1="2020.978271">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19148_0" y2="-16.661743" x2="1710.429443" y1="700.462769" x1="-26.046570">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19149_0" y2="-501.537354" x2="289.204712" y1="-1218.656250" x1="2025.672485">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19150_0" y2="-211.520752" x2="408.985901" y1="-928.650757" x1="2145.460449">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19151_0" y2="-246.238037" x2="394.648071" y1="-963.367432" x1="2131.123535">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19152_0" y2="-169.549683" x2="426.323181" y1="-886.674927" x1="2162.796143">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19153_0" y2="-425.535889" x2="320.587646" y1="-1142.662354" x1="2057.060303">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19154_0" y2="-35.174561" x2="1702.530029" y1="681.950928" x1="-33.938721">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19155_0" y2="-89.187866" x2="1680.382080" y1="627.931274" x1="-56.077576">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19156_0" y2="-181.335327" x2="1642.316895" y1="535.789185" x1="-94.147400">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19157_0" y2="-428.551392" x2="319.337830" y1="-1145.679810" x1="2055.812256">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19158_0" y2="-243.617432" x2="395.736816" y1="-960.747803" x1="2132.202637">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19159_0" y2="-303.808472" x2="370.875916" y1="-1020.938843" x1="2107.341553">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19160_0" y2="-361.952759" x2="346.848816" y1="-1079.073853" x1="2083.333496">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19161_0" y2="-584.513428" x2="254.928223" y1="-1301.631348" x1="1991.393677">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19162_0" y2="-732.547729" x2="193.765625" y1="-1449.675903" x1="1930.240723">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19163_0" y2="-387.317383" x2="336.372070" y1="-1104.442627" x1="2072.846680">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19164_0" y2="-547.665771" x2="270.136536" y1="-1264.797119" x1="2006.611572">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19165_0" y2="-494.261963" x2="292.205688" y1="-1211.380127" x1="2028.671021">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19166_0" y2="-45.325562" x2="1698.596191" y1="671.799438" x1="-37.866760">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19167_0" y2="-271.514038" x2="384.210510" y1="-988.644409" x1="2120.675049">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19168_0" y2="-181.371216" x2="1642.248047" y1="535.747437" x1="-94.225708">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19169_0" y2="-421.084595" x2="1543.309448" y1="296.035767" x1="-193.166260">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19170_0" y2="-634.904907" x2="234.112793" y1="-1352.022339" x1="1970.572754">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19171_0" y2="-611.658325" x2="243.718201" y1="-1328.783081" x1="1980.177734">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19172_0" y2="171.647095" x2="1788.105713" y1="888.778442" x1="51.642212">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19173_0" y2="44.584839" x2="1735.573730" y1="761.715332" x1="-0.891785">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19174_0" y2="-321.950439" x2="1584.263306" y1="395.176758" x1="-152.200989">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19175_0" y2="-517.690308" x2="282.524231" y1="-1234.818237" x1="2018.989746">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19176_0" y2="-340.158936" x2="355.857727" y1="-1057.280029" x1="2092.323486">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19177_0" y2="-565.488403" x2="262.774292" y1="-1282.613159" x1="1999.248901">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19178_0" y2="-740.342773" x2="190.548523" y1="-1457.469727" x1="1927.023438">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19179_0" y2="-579.238770" x2="257.105530" y1="-1296.362549" x1="1993.570801">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19180_0" y2="-323.012939" x2="362.945740" y1="-1040.135742" x1="2099.411621">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19181_0" y2="-257.915771" x2="389.822021" y1="-975.044189" x1="2126.296875">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19182_0" y2="-243.395386" x2="395.822388" y1="-960.519897" x1="2132.296387">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19183_0" y2="-248.131592" x2="393.863159" y1="-965.260742" x1="2130.337891">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19184_0" y2="-182.964966" x2="420.786377" y1="-900.087036" x1="2157.261475">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19185_0" y2="-479.541748" x2="298.290771" y1="-1196.665283" x1="2034.755493">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19186_0" y2="-462.184082" x2="305.454651" y1="-1179.311523" x1="2041.920166">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19187_0" y2="-528.727661" x2="277.963867" y1="-1245.851929" x1="2014.437622">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19188_0" y2="-709.725220" x2="203.200684" y1="-1426.850952" x1="1939.666016">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19189_0" y2="-616.124390" x2="241.859924" y1="-1333.247192" x1="1978.334229">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19190_0" y2="-668.157959" x2="220.368225" y1="-1385.281494" x1="1956.842773">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19191_0" y2="-704.029785" x2="205.554565" y1="-1421.155273" x1="1942.013794">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19192_0" y2="-631.046143" x2="235.696838" y1="-1348.168213" x1="1972.165283">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19193_0" y2="-478.551025" x2="298.688049" y1="-1195.680176" x1="2035.164307">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19194_0" y2="-707.575684" x2="204.107727" y1="-1424.693115" x1="1940.564697">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19195_0" y2="57.502930" x2="1740.995117" y1="774.617188" x1="4.535767">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19196_0" y2="-95.063232" x2="1677.948975" y1="622.064453" x1="-58.530945">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19197_0" y2="-653.411255" x2="226.453186" y1="-1370.539185" x1="1962.927979">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19198_0" y2="-514.821411" x2="283.707214" y1="-1231.950806" x1="2020.172852">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19200_0" y2="133.678345" x2="1772.458618" y1="850.802734" x1="35.997070">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
<linearGradient gradientUnits="userSpaceOnUse" id="paint_linear_47_19201_0" y2="-10.831787" x2="1712.766113" y1="706.300537" x1="-23.694580">
<stop stop-color="#FFFFFF"/>
<stop stop-color="#000000" offset="1.000000"/>
</linearGradient>
</defs>
<g opacity="0.660000">
<g opacity="0.130000">
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19033_0)" d="M1274.23 255.03L1274.23 49.67L1278.49 49.67L1278.49 256.79L1250.04 285.23L1162.91 285.23L1140.42 307.71L1051.26 307.71L1029.78 286.24L832.87 286.24L832.87 281.98L1031.55 281.98L1053.02 303.45L1138.66 303.45L1161.14 280.97L1248.28 280.97L1274.23 255.03Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19034_0)" d="M797.86 263.9L1233.06 263.9L1254.82 242.14L1254.82 83.59L1256.96 83.59L1256.96 243.02L1233.93 266.03L797.86 266.03L797.86 263.9Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19035_0)" d="M1219.14 247.52L1092.19 247.52L1059.96 215.29L928.97 215.29L887.09 173.42L890.1 170.41L930.73 211.03L1061.73 211.03L1093.97 243.26L1219.14 243.26L1219.14 247.52Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19036_0)" d="M1480.87 314.06L1312.74 314.06L1283.53 284.86L1285.04 283.35L1313.62 311.93L1479.99 311.93L1509.3 282.63L1641.96 282.63L1641.96 284.75L1510.18 284.75L1480.87 314.06Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19037_0)" d="M1302.47 266.47L1305.48 263.46L1328.24 286.22L1462.24 286.22L1491.61 256.86L1494.61 259.87L1464.01 290.48L1326.47 290.48L1302.47 266.47Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19038_0)" d="M1011.65 247.52L744.55 247.52L719.63 222.6L722.64 219.59L746.31 243.26L1011.65 243.26L1011.65 247.52Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19039_0)" d="M1403.99 349.22L1501.45 349.22L1534.48 316.18L1585.82 316.18L1585.82 320.44L1536.25 320.44L1503.21 353.48L1403.99 353.48L1403.99 349.22Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19040_0)" d="M1219.14 220.03L1219.14 222.16L1111.77 222.16L1111.46 221.85L1049.84 160.24L1051.34 158.73L1112.65 220.03L1219.14 220.03Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19041_0)" d="M584.92 312.82L502.32 312.82L502.01 312.51L397.79 208.3L316.09 208.3L316.09 206.17L398.67 206.17L503.2 310.69L584.92 310.69L584.92 312.82Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19042_0)" d="M691.58 290.48L518.23 290.48L470.75 243.01L473.77 240L519.99 286.22L691.58 286.22L691.58 290.48Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19043_0)" d="M1306.1 18.35L1301.84 18.35L1301.84 221.1L1306.1 221.1L1306.1 18.35Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19044_0)" d="M1340.51 239.37L1432.28 239.37L1504.07 167.6L1585.82 167.6L1585.82 171.85L1505.83 171.85L1434.05 243.63L1340.51 243.63L1340.51 239.37Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19045_0)" d="M1493.34 218.9L1606.36 218.9L1676.82 148.45L1678.32 149.96L1607.25 221.03L1494.22 221.03L1463.88 251.36L1462.37 249.86L1493.34 218.9Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19046_0)" d="M1298.42 269.44C1293.86 269.44 1290.15 265.73 1290.15 261.17C1290.15 256.6 1293.86 252.89 1298.42 252.89C1302.98 252.89 1306.68 256.6 1306.68 261.17C1306.68 265.73 1302.98 269.44 1298.42 269.44ZM1298.42 257.15C1296.19 257.15 1294.4 258.95 1294.4 261.16C1294.4 263.38 1296.19 265.18 1298.42 265.18C1300.63 265.18 1302.43 263.38 1302.43 261.16C1302.43 258.95 1300.63 257.15 1298.42 257.15Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19047_0)" d="M1592.39 178.32C1587.83 178.32 1584.12 174.61 1584.12 170.05C1584.12 165.49 1587.83 161.78 1592.39 161.78C1596.96 161.78 1600.67 165.49 1600.67 170.05C1600.67 174.61 1596.96 178.32 1592.39 178.32ZM1592.39 166.03C1590.19 166.03 1588.38 167.83 1588.38 170.05C1588.38 172.26 1590.19 174.06 1592.39 174.06C1594.61 174.06 1596.41 172.26 1596.41 170.05C1596.41 167.83 1594.61 166.03 1592.39 166.03Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19048_0)" d="M1337.41 203.87C1332.85 203.87 1329.13 200.16 1329.13 195.59C1329.13 191.03 1332.85 187.32 1337.41 187.32C1341.97 187.32 1345.68 191.03 1345.68 195.59C1345.68 200.16 1341.97 203.87 1337.41 203.87ZM1337.41 191.58C1335.19 191.58 1333.38 193.38 1333.38 195.59C1333.38 197.8 1335.19 199.6 1337.41 199.6C1339.62 199.6 1341.42 197.8 1341.42 195.59C1341.42 193.38 1339.62 191.58 1337.41 191.58Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19049_0)" d="M1276.36 51.79C1271.8 51.79 1268.09 48.08 1268.09 43.52C1268.09 38.96 1271.8 35.25 1276.36 35.25C1280.92 35.25 1284.63 38.96 1284.63 43.52C1284.63 48.08 1280.92 51.79 1276.36 51.79ZM1276.36 39.5C1274.14 39.5 1272.34 41.3 1272.34 43.52C1272.34 45.73 1274.14 47.53 1276.36 47.53C1278.56 47.53 1280.38 45.73 1280.38 43.52C1280.38 41.3 1278.56 39.5 1276.36 39.5Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19050_0)" d="M885.18 178C880.62 178 876.91 174.28 876.91 169.72C876.91 165.16 880.62 161.45 885.18 161.45C889.74 161.45 893.45 165.16 893.45 169.72C893.45 174.28 889.74 178 885.18 178ZM885.18 165.71C882.97 165.71 881.17 167.51 881.17 169.72C881.17 171.93 882.97 173.73 885.18 173.73C887.39 173.73 889.2 171.93 889.2 169.72C889.2 167.51 887.39 165.71 885.18 165.71Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19051_0)" d="M717.51 225.22C712.94 225.22 709.23 221.51 709.23 216.95C709.23 212.39 712.94 208.68 717.51 208.68C722.07 208.68 725.78 212.39 725.78 216.95C725.78 221.51 722.07 225.22 717.51 225.22ZM717.51 212.93C715.29 212.93 713.49 214.73 713.49 216.95C713.49 219.16 715.29 220.96 717.51 220.96C719.72 220.96 721.52 219.16 721.52 216.95C721.52 214.73 719.72 212.93 717.51 212.93Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19052_0)" d="M469.01 245.54C464.45 245.54 460.74 241.83 460.74 237.27C460.74 232.7 464.45 228.99 469.01 228.99C473.57 228.99 477.29 232.7 477.29 237.27C477.29 241.83 473.57 245.54 469.01 245.54ZM469.01 233.25C466.8 233.25 465 235.05 465 237.26C465 239.48 466.8 241.28 469.01 241.28C471.23 241.28 473.02 239.48 473.02 237.26C473.02 235.05 471.23 233.25 469.01 233.25Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19053_0)" d="M675.08 273.24C670.51 273.24 666.8 269.53 666.8 264.97C666.8 260.41 670.51 256.7 675.08 256.7C679.64 256.7 683.35 260.41 683.35 264.97C683.35 269.53 679.64 273.24 675.08 273.24ZM675.08 260.95C672.86 260.95 671.06 262.75 671.06 264.96C671.06 267.18 672.86 268.98 675.08 268.98C677.29 268.98 679.09 267.18 679.09 264.96C679.09 262.75 677.29 260.95 675.08 260.95Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19054_0)" d="M534.64 160.99C530.08 160.99 526.37 157.28 526.37 152.72C526.37 148.16 530.08 144.45 534.64 144.45C539.2 144.45 542.91 148.16 542.91 152.72C542.91 157.28 539.2 160.99 534.64 160.99ZM534.64 148.71C532.43 148.71 530.63 150.5 530.63 152.72C530.63 154.93 532.43 156.73 534.64 156.73C536.85 156.73 538.65 154.93 538.65 152.72C538.65 150.5 536.85 148.71 534.64 148.71Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19055_0)" d="M826.72 291.96C822.16 291.96 818.45 288.25 818.45 283.69C818.45 279.13 822.16 275.42 826.72 275.42C831.28 275.42 834.99 279.13 834.99 283.69C834.99 288.25 831.28 291.96 826.72 291.96ZM826.72 279.67C824.51 279.67 822.71 281.47 822.71 283.69C822.71 285.9 824.51 287.7 826.72 287.7C828.93 287.7 830.73 285.9 830.73 283.69C830.73 281.47 828.93 279.67 826.72 279.67Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19056_0)" d="M1303.97 235.52C1299.41 235.52 1295.68 231.81 1295.68 227.24C1295.68 222.68 1299.41 218.97 1303.97 218.97C1308.53 218.97 1312.24 222.68 1312.24 227.24C1312.24 231.81 1308.53 235.52 1303.97 235.52ZM1303.97 223.23C1301.76 223.23 1299.96 225.03 1299.96 227.24C1299.96 229.45 1301.76 231.25 1303.97 231.25C1306.18 231.25 1307.98 229.45 1307.98 227.24C1307.98 225.03 1306.18 223.23 1303.97 223.23Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19057_0)" d="M1591.97 326.59C1587.41 326.59 1583.7 322.88 1583.7 318.31C1583.7 313.75 1587.41 310.04 1591.97 310.04C1596.54 310.04 1600.25 313.75 1600.25 318.31C1600.25 322.88 1596.54 326.59 1591.97 326.59ZM1591.97 314.3C1589.75 314.3 1587.95 316.1 1587.95 318.31C1587.95 320.53 1589.75 322.32 1591.97 322.32C1594.18 322.32 1595.99 320.53 1595.99 318.31C1595.99 316.1 1594.18 314.3 1591.97 314.3Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19058_0)" d="M1403.99 358.93C1408.18 358.93 1411.57 355.54 1411.57 351.35C1411.57 347.16 1408.18 343.76 1403.99 343.76C1399.8 343.76 1396.4 347.16 1396.4 351.35C1396.4 355.54 1399.8 358.93 1403.99 358.93Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19059_0)" d="M1340.51 249.09C1344.7 249.09 1348.09 245.69 1348.09 241.5C1348.09 237.31 1344.7 233.92 1340.51 233.92C1336.32 233.92 1332.92 237.31 1332.92 241.5C1332.92 245.69 1336.32 249.09 1340.51 249.09Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19060_0)" d="M1538.89 77.45C1538.89 73.26 1535.49 69.86 1531.3 69.86C1527.11 69.86 1523.72 73.26 1523.72 77.45C1523.72 81.64 1527.11 85.04 1531.3 85.04C1535.49 85.04 1538.89 81.64 1538.89 77.45Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19061_0)" d="M1306.29 25.8C1310.36 24.83 1312.89 20.75 1311.93 16.67C1310.97 12.59 1306.88 10.07 1302.8 11.03C1298.72 11.99 1296.18 16.08 1297.16 20.16C1298.12 24.23 1302.21 26.76 1306.29 25.8Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19062_0)" d="M1226.48 247.08C1227.44 243 1224.92 238.92 1220.84 237.96C1216.77 236.99 1212.68 239.52 1211.72 243.6C1210.76 247.67 1213.28 251.76 1217.36 252.72C1221.43 253.68 1225.52 251.16 1226.48 247.08Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19063_0)" d="M1018.67 248.29C1020.26 244.42 1018.43 239.99 1014.56 238.38C1010.69 236.78 1006.25 238.62 1004.64 242.49C1003.04 246.36 1004.88 250.8 1008.75 252.4C1012.62 254 1017.06 252.17 1018.67 248.29Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19064_0)" d="M692.54 295.85C696.7 295.34 699.65 291.55 699.14 287.39C698.63 283.24 694.84 280.28 690.68 280.79C686.53 281.3 683.57 285.09 684.08 289.25C684.59 293.41 688.38 296.36 692.54 295.85Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19065_0)" d="M309.1 105.61C313.22 104.81 315.9 100.83 315.1 96.72C314.3 92.6 310.32 89.92 306.2 90.72C302.09 91.52 299.41 95.5 300.21 99.62C301.01 103.73 304.99 106.41 309.1 105.61Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19066_0)" d="M604.82 230.68C609.01 230.68 612.4 227.28 612.4 223.09C612.4 218.9 609.01 215.51 604.82 215.51C600.63 215.51 597.23 218.9 597.23 223.09C597.23 227.28 600.63 230.68 604.82 230.68Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19067_0)" d="M1492.87 265.35C1497.06 265.35 1500.45 261.96 1500.45 257.77C1500.45 253.58 1497.06 250.18 1492.87 250.18C1488.68 250.18 1485.28 253.58 1485.28 257.77C1485.28 261.96 1488.68 265.35 1492.87 265.35Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19068_0)" d="M1682.25 152.38C1678.26 152.38 1675.04 149.15 1675.04 145.17C1675.04 141.2 1678.26 137.96 1682.25 137.96C1686.22 137.96 1689.45 141.2 1689.45 145.17C1689.45 149.15 1686.22 152.38 1682.25 152.38ZM1682.25 140.09C1679.45 140.09 1677.17 142.37 1677.17 145.17C1677.17 147.98 1679.44 150.25 1682.25 150.25C1685.05 150.25 1687.33 147.98 1687.33 145.17C1687.33 142.37 1685.05 140.09 1682.25 140.09Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19069_0)" d="M1458.73 183.4C1454.75 183.4 1451.52 180.17 1451.52 176.19C1451.52 172.22 1454.75 168.98 1458.73 168.98C1462.7 168.98 1465.94 172.22 1465.94 176.19C1465.94 180.17 1462.7 183.4 1458.73 183.4ZM1458.73 171.11C1455.93 171.11 1453.65 173.39 1453.65 176.19C1453.65 179 1455.92 181.27 1458.73 181.27C1461.53 181.27 1463.81 179 1463.81 176.19C1463.81 173.39 1461.53 171.11 1458.73 171.11Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19070_0)" d="M1225.29 228.31C1221.31 228.31 1218.07 225.08 1218.07 221.1C1218.07 217.12 1221.31 213.89 1225.29 213.89C1229.27 213.89 1232.5 217.12 1232.5 221.1C1232.5 225.08 1229.27 228.31 1225.29 228.31ZM1225.29 216.02C1222.49 216.02 1220.21 218.3 1220.21 221.1C1220.21 223.9 1222.49 226.18 1225.29 226.18C1228.09 226.18 1230.37 223.9 1230.37 221.1C1230.37 218.3 1228.09 216.02 1225.29 216.02Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19071_0)" d="M309.95 214.44C305.97 214.44 302.74 211.21 302.74 207.23C302.74 203.26 305.97 200.03 309.95 200.03C313.92 200.03 317.15 203.26 317.15 207.23C317.15 211.21 313.92 214.44 309.95 214.44ZM309.95 202.15C307.15 202.15 304.86 204.43 304.86 207.23C304.86 210.04 307.14 212.32 309.95 212.32C312.75 212.32 315.03 210.04 315.03 207.23C315.03 204.43 312.75 202.15 309.95 202.15Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19072_0)" d="M654.63 252.6C650.65 252.6 647.42 249.37 647.42 245.39C647.42 241.42 650.65 238.19 654.63 238.19C658.61 238.19 661.84 241.42 661.84 245.39C661.84 249.37 658.61 252.6 654.63 252.6ZM654.63 240.31C651.83 240.31 649.55 242.59 649.55 245.39C649.55 248.2 651.83 250.48 654.63 250.48C657.44 250.48 659.71 248.2 659.71 245.39C659.71 242.59 657.44 240.31 654.63 240.31Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19073_0)" d="M903.88 228.48C899.91 228.48 896.67 225.25 896.67 221.27C896.67 217.3 899.91 214.07 903.88 214.07C907.86 214.07 911.09 217.3 911.09 221.27C911.09 225.25 907.86 228.48 903.88 228.48ZM903.88 216.19C901.08 216.19 898.8 218.47 898.8 221.27C898.8 224.08 901.08 226.36 903.88 226.36C906.69 226.36 908.96 224.08 908.96 221.27C908.96 218.47 906.69 216.19 903.88 216.19Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19074_0)" d="M1286.63 289.37C1289.77 288.07 1291.26 284.47 1289.96 281.34C1288.66 278.2 1285.06 276.71 1281.93 278.01C1278.8 279.31 1277.3 282.91 1278.61 286.04C1279.91 289.18 1283.5 290.67 1286.63 289.37Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19075_0)" d="M1463.12 256.76C1466.52 256.76 1469.27 254.01 1469.27 250.61C1469.27 247.22 1466.52 244.47 1463.12 244.47C1459.73 244.47 1456.98 247.22 1456.98 250.61C1456.98 254.01 1459.73 256.76 1463.12 256.76Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19076_0)" d="M1337.96 217.72C1340.35 215.32 1340.35 211.43 1337.96 209.03C1335.55 206.63 1331.66 206.63 1329.26 209.03C1326.86 211.43 1326.86 215.32 1329.26 217.72C1331.66 220.12 1335.55 220.12 1337.96 217.72Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19077_0)" d="M1255.89 89.74C1259.29 89.74 1262.04 86.99 1262.04 83.59C1262.04 80.2 1259.29 77.45 1255.89 77.45C1252.5 77.45 1249.75 80.2 1249.75 83.59C1249.75 86.99 1252.5 89.74 1255.89 89.74Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19078_0)" d="M1050.59 165.63C1053.98 165.63 1056.74 162.88 1056.74 159.49C1056.74 156.09 1053.98 153.34 1050.59 153.34C1047.19 153.34 1044.43 156.09 1044.43 159.49C1044.43 162.88 1047.19 165.63 1050.59 165.63Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19079_0)" d="M797.86 271.11C801.25 271.11 804 268.36 804 264.96C804 261.57 801.25 258.82 797.86 258.82C794.47 258.82 791.71 261.57 791.71 264.96C791.71 268.36 794.47 271.11 797.86 271.11Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19080_0)" d="M512.21 202.86C515.35 201.56 516.85 197.97 515.56 194.84C514.26 191.7 510.67 190.2 507.53 191.49C504.39 192.79 502.9 196.38 504.19 199.52C505.48 202.65 509.07 204.15 512.21 202.86Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19081_0)" d="M663.27 165C665.67 162.6 665.67 158.71 663.27 156.31C660.87 153.91 656.98 153.91 654.58 156.31C652.18 158.71 652.18 162.6 654.58 165C656.98 167.4 660.87 167.4 663.27 165Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19082_0)" d="M589.26 316.1C591.66 313.7 591.66 309.81 589.26 307.41C586.86 305.01 582.97 305.01 580.57 307.41C578.17 309.81 578.17 313.7 580.57 316.1C582.97 318.5 586.86 318.5 589.26 316.1Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19083_0)" d="M1644.31 289.37C1647.45 288.07 1648.94 284.48 1647.64 281.34C1646.34 278.2 1642.75 276.71 1639.61 278.01C1636.47 279.31 1634.99 282.91 1636.28 286.04C1637.58 289.18 1641.18 290.67 1644.31 289.37Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19084_0)" d="M668.93 267.05L528.85 267.05L394.47 132.69L339.19 132.69L306.16 99.67L309.17 96.65L340.96 128.43L396.24 128.43L530.62 262.79L668.93 262.79L668.93 267.05Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19085_0)" d="M548.91 198.22L512.97 198.22L512.97 196.09L549.79 196.09L598.03 244.33L648.49 244.33L648.49 246.46L597.15 246.46L548.91 198.22Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19086_0)" d="M897.74 222.34L772.55 222.34L772.24 222.03L711.92 161.72L658.92 161.72L658.92 159.59L712.8 159.59L773.43 220.21L897.74 220.21L897.74 222.34Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19087_0)" d="M1421.86 214.44L1333.61 214.44L1333.61 212.31L1420.98 212.31L1454.37 178.93L1455.87 180.43L1421.86 214.44Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19088_0)" d="M1426.31 101.5L1503.85 101.5L1529.79 75.55L1532.8 78.57L1505.61 105.76L1428.07 105.76L1342.02 191.8L1339.01 188.78L1426.31 101.5Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19089_0)" d="M540 155.26L536.98 158.27L603.32 224.6L606.33 221.58L540 155.26Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19091_0)" d="M1014.88 315.26L1014.88 319.52L871.78 319.52L844.74 346.55L763.71 346.55L733.44 376.82L733.44 516.89L729.18 516.89L729.18 375.06L761.95 342.29L842.97 342.29L870.01 315.26L1014.88 315.26Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19092_0)" d="M603.46 370.77L363.14 370.77L363.14 366.51L605.22 366.51L634.74 396.02L687.33 396.02L687.33 400.28L632.97 400.28L603.46 370.77Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19093_0)" d="M1071.89 448.15L1014.44 390.7L961.94 390.7L936.66 365.42L773.87 365.42L773.87 363.3L937.54 363.3L962.83 388.57L1015.32 388.57L1073.39 446.64L1071.89 448.15Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19094_0)" d="M276.57 350.54L273.56 347.53L309.61 311.48L418.5 311.48L418.5 315.74L311.38 315.74L276.57 350.54Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19095_0)" d="M1354.6 333.02L1265.44 333.02L1265.44 328.76L1356.36 328.76L1399.26 371.65L1485.98 371.65L1485.98 375.91L1397.5 375.91L1354.6 333.02Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19096_0)" d="M1364.19 490.8L1362.06 490.8L1362.06 422.99L1297.31 358.24L1298.81 356.74L1364.19 422.11L1364.19 490.8Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19097_0)" d="M1405.48 418.84L1345.59 358.97L1347.11 357.46L1407.6 417.96L1407.6 453.82L1451.58 497.79L1450.08 499.3L1405.48 454.7L1405.48 418.84Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19098_0)" d="M1293.16 445.77L1247.12 399.74L1132.29 399.74L1099.03 366.49L987.48 366.49L987.48 362.23L1100.79 362.23L1134.05 395.48L1248.89 395.48L1296.17 442.76L1293.16 445.77Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19099_0)" d="M356.99 376.91C352.43 376.91 348.72 373.2 348.72 368.64C348.72 364.08 352.43 360.37 356.99 360.37C361.55 360.37 365.26 364.08 365.26 368.64C365.26 373.2 361.55 376.91 356.99 376.91ZM356.99 364.63C354.78 364.63 352.98 366.42 352.98 368.64C352.98 370.85 354.78 372.65 356.99 372.65C359.2 372.65 361 370.85 361 368.64C361 366.42 359.2 364.63 356.99 364.63Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19100_0)" d="M424.65 321.89C420.08 321.89 416.37 318.18 416.37 313.61C416.37 309.05 420.08 305.34 424.65 305.34C429.21 305.34 432.92 309.05 432.92 313.61C432.92 318.18 429.21 321.89 424.65 321.89ZM424.65 309.6C422.43 309.6 420.63 311.4 420.63 313.61C420.63 315.82 422.43 317.62 424.65 317.62C426.86 317.62 428.66 315.82 428.66 313.61C428.66 311.4 426.86 309.6 424.65 309.6Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19101_0)" d="M1492.13 382.05C1487.56 382.05 1483.85 378.34 1483.85 373.78C1483.85 369.22 1487.56 365.51 1492.13 365.51C1496.69 365.51 1500.4 369.22 1500.4 373.78C1500.4 378.34 1496.69 382.05 1492.13 382.05ZM1492.13 369.76C1489.91 369.76 1488.11 371.57 1488.11 373.78C1488.11 375.99 1489.91 377.79 1492.13 377.79C1494.34 377.79 1496.15 375.99 1496.15 373.78C1496.15 371.57 1494.34 369.76 1492.13 369.76Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19102_0)" d="M1083.65 388.2C1079.09 388.2 1075.37 384.49 1075.37 379.92C1075.37 375.36 1079.09 371.65 1083.65 371.65C1088.21 371.65 1091.92 375.36 1091.92 379.92C1091.92 384.49 1088.21 388.2 1083.65 388.2ZM1083.65 375.91C1081.43 375.91 1079.64 377.71 1079.64 379.92C1079.64 382.13 1081.43 383.93 1083.65 383.93C1085.87 383.93 1087.66 382.13 1087.66 379.92C1087.66 377.71 1085.87 375.91 1083.65 375.91Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19103_0)" d="M1021.02 325.66C1016.46 325.66 1012.75 321.95 1012.75 317.39C1012.75 312.83 1016.46 309.12 1021.02 309.12C1025.57 309.12 1029.3 312.83 1029.3 317.39C1029.3 321.95 1025.57 325.66 1021.02 325.66ZM1021.02 313.37C1018.81 313.37 1017.01 315.17 1017.01 317.38C1017.01 319.6 1018.81 321.4 1021.02 321.4C1023.24 321.4 1025.03 319.6 1025.03 317.38C1025.03 315.17 1023.24 313.37 1021.02 313.37Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19104_0)" d="M794.34 391.67C789.78 391.67 786.07 387.96 786.07 383.4C786.07 378.84 789.78 375.13 794.34 375.13C798.9 375.13 802.61 378.84 802.61 383.4C802.61 387.96 798.9 391.67 794.34 391.67ZM794.34 379.38C792.13 379.38 790.33 381.18 790.33 383.4C790.33 385.61 792.13 387.41 794.34 387.41C796.55 387.41 798.35 385.61 798.35 383.4C798.35 381.18 796.55 379.38 794.34 379.38Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19105_0)" d="M990.37 371.37C994.25 369.77 996.09 365.33 994.48 361.46C992.88 357.59 988.44 355.75 984.57 357.35C980.7 358.95 978.87 363.39 980.47 367.26C982.07 371.13 986.5 372.97 990.37 371.37Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19106_0)" d="M276.77 356.44C280.85 355.48 283.37 351.4 282.41 347.32C281.45 343.24 277.36 340.72 273.28 341.68C269.21 342.64 266.68 346.73 267.64 350.8C268.6 354.88 272.69 357.41 276.77 356.44Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19107_0)" d="M1271.91 334.95C1274.11 331.38 1273.02 326.71 1269.44 324.51C1265.88 322.31 1261.21 323.41 1259.01 326.97C1256.81 330.54 1257.9 335.21 1261.47 337.41C1265.04 339.62 1269.71 338.51 1271.91 334.95Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19108_0)" d="M1293.44 359.54C1289.47 359.54 1286.24 356.31 1286.24 352.33C1286.24 348.36 1289.47 345.12 1293.44 345.12C1297.42 345.12 1300.66 348.36 1300.66 352.33C1300.66 356.31 1297.42 359.54 1293.44 359.54ZM1293.44 347.25C1290.65 347.25 1288.37 349.53 1288.37 352.33C1288.37 355.14 1290.65 357.41 1293.44 357.41C1296.26 357.41 1298.53 355.14 1298.53 352.33C1298.53 349.53 1296.26 347.25 1293.44 347.25Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19109_0)" d="M1342.61 360.91C1338.63 360.91 1335.4 357.68 1335.4 353.7C1335.4 349.73 1338.63 346.49 1342.61 346.49C1346.59 346.49 1349.81 349.73 1349.81 353.7C1349.81 357.68 1346.59 360.91 1342.61 360.91ZM1342.61 348.62C1339.81 348.62 1337.53 350.9 1337.53 353.7C1337.53 356.51 1339.8 358.78 1342.61 358.78C1345.41 358.78 1347.69 356.51 1347.69 353.7C1347.69 350.9 1345.41 348.62 1342.61 348.62Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19110_0)" d="M778.22 368.71C780.62 366.31 780.62 362.42 778.22 360.02C775.82 357.62 771.92 357.62 769.52 360.02C767.12 362.42 767.12 366.31 769.52 368.71C771.92 371.11 775.82 371.11 778.22 368.71Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19111_0)" d="M317.2 337.47C320.6 337.47 323.35 334.72 323.35 331.32C323.35 327.93 320.6 325.18 317.2 325.18C313.81 325.18 311.06 327.93 311.06 331.32C311.06 334.72 313.81 337.47 317.2 337.47Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19112_0)" d="M173.65 371.9L277.09 371.9L318.42 330.57L319.93 332.08L277.98 374.03L174.53 374.03L128.46 420.1L126.95 418.6L173.65 371.9Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19113_0)" d="M194.79 237.97L123.21 237.97L123.21 233.71L196.56 233.71L250.56 287.7L396.85 287.7L396.85 291.96L248.8 291.96L194.79 237.97Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19114_0)" d="M372.45 270.04L372.45 272.17L274.63 272.17L274.32 271.86L182.9 180.46L184.41 178.95L275.51 270.04L372.45 270.04Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19115_0)" d="M1134.42 347.26L1021.02 347.26L1021.02 345.12L1135.3 345.12L1152.38 362.19L1231.43 362.19L1231.43 364.32L1151.5 364.32L1134.42 347.26Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19116_0)" d="M1639.26 382.63L1609.04 352.41L1539.01 352.41L1539.01 350.28L1609.92 350.28L1640.76 381.12L1639.26 382.63Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19117_0)" d="M123.21 243.42C127.4 243.42 130.79 240.02 130.79 235.83C130.79 231.64 127.4 228.25 123.21 228.25C119.02 228.25 115.62 231.64 115.62 235.83C115.62 240.02 119.02 243.42 123.21 243.42Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19118_0)" d="M404.44 289.83C404.44 285.65 401.04 282.25 396.85 282.25C392.66 282.25 389.26 285.65 389.26 289.83C389.26 294.02 392.66 297.42 396.85 297.42C401.04 297.42 404.44 294.02 404.44 289.83Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19119_0)" d="M1015.32 353.82C1011.34 353.82 1008.11 350.59 1008.11 346.62C1008.11 342.64 1011.34 339.41 1015.32 339.41C1019.3 339.41 1022.53 342.64 1022.53 346.62C1022.53 350.59 1019.3 353.82 1015.32 353.82ZM1015.32 341.53C1012.52 341.53 1010.24 343.81 1010.24 346.62C1010.24 349.42 1012.51 351.7 1015.32 351.7C1018.13 351.7 1020.41 349.42 1020.41 346.62C1020.41 343.81 1018.13 341.53 1015.32 341.53Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19120_0)" d="M378.63 278.32C374.65 278.32 371.42 275.08 371.42 271.11C371.42 267.13 374.65 263.9 378.63 263.9C382.6 263.9 385.83 267.13 385.83 271.11C385.83 275.08 382.6 278.32 378.63 278.32ZM378.63 266.03C375.82 266.03 373.54 268.3 373.54 271.11C373.54 273.91 375.82 276.19 378.63 276.19C381.43 276.19 383.71 273.91 383.71 271.11C383.71 268.3 381.43 266.03 378.63 266.03Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19121_0)" d="M1232.99 367.6C1235.38 365.2 1235.38 361.31 1232.99 358.91C1230.57 356.51 1226.68 356.51 1224.29 358.91C1221.89 361.31 1221.89 365.2 1224.29 367.6C1226.68 370 1230.57 370 1232.99 367.6Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19122_0)" d="M1541.36 357.02C1544.49 355.72 1545.98 352.13 1544.69 348.99C1543.38 345.86 1539.78 344.37 1536.65 345.67C1533.52 346.97 1532.02 350.56 1533.33 353.7C1534.63 356.83 1538.22 358.32 1541.36 357.02Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19123_0)" d="M1640 389.54C1643.4 389.54 1646.15 386.79 1646.15 383.4C1646.15 380.01 1643.4 377.25 1640 377.25C1636.62 377.25 1633.86 380.01 1633.86 383.4C1633.86 386.79 1636.62 389.54 1640 389.54Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19124_0)" d="M183.65 185.85C187.05 185.85 189.8 183.1 189.8 179.71C189.8 176.31 187.05 173.56 183.65 173.56C180.26 173.56 177.51 176.31 177.51 179.71C177.51 183.1 180.26 185.85 183.65 185.85Z" id="Vector"/>
</g>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19127_0)" d="M870.37 424.97L778.63 424.97L741.54 390.87L741.54 243.22L746.36 243.22L746.36 389.03L780.63 420.54L872.36 420.54L902.98 448.68L1064.99 448.68L1064.99 453.11L900.98 453.11L870.37 424.97Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19128_0)" d="M327.12 395.32L599.2 395.32L632.61 364.61L694.15 364.61L694.15 369.04L634.61 369.04L601.2 399.76L327.12 399.76L327.12 395.32Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19129_0)" d="M253.98 333.18L167.77 333.18L167.77 330.96L254.98 330.96L292.79 365.72L575.95 365.72L575.95 367.93L291.79 367.93L253.98 333.18Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19130_0)" d="M1065.49 376.8L1006.06 376.8L977.43 403.11L792.13 403.11L792.13 400.89L976.43 400.89L1005.06 374.58L1064.5 374.58L1129.54 314.78L1131.24 316.35L1065.49 376.8Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19131_0)" d="M822.26 379.97L954.66 379.97L996.88 341.15L1000.29 344.29L956.66 384.4L822.26 384.4L822.26 379.97Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19132_0)" d="M707.99 210.18L671.76 176.88L675.17 173.74L712.81 208.35L712.81 342.72L707.99 342.72L707.99 210.18Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19133_0)" d="M681.13 227.38L620.29 171.46L620.29 125.05L622.7 125.05L622.7 170.54L683.54 226.47L683.54 278.54L681.13 278.54L681.13 227.38Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19134_0)" d="M1386.45 409.94L1384.74 408.37L1458.06 340.98L1458.06 270.39L1460.47 270.39L1460.47 341.89L1460.11 342.22L1386.45 409.94Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19135_0)" d="M1507.2 345.29L1507.2 307.96L1557.7 261.54L1559.4 263.11L1509.61 308.88L1509.61 346.21L1441.12 409.18L1439.41 407.61L1507.2 345.29Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19136_0)" d="M1329.92 369.6L1199.91 369.6L1162.26 404.22L1033.97 404.22L1033.97 399.79L1160.26 399.79L1197.91 365.17L1327.93 365.17L1380.05 317.26L1383.46 320.39L1329.92 369.6Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19137_0)" d="M400.79 253.52L253.61 253.52L253.61 249.09L402.78 249.09L494.22 333.14L663.12 333.14L663.12 337.57L492.22 337.57L400.79 253.52Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19138_0)" d="M1385.59 324.18C1380.44 324.18 1376.24 320.32 1376.24 315.57C1376.24 310.82 1380.44 306.96 1385.59 306.96C1390.76 306.96 1394.96 310.82 1394.96 315.57C1394.96 320.32 1390.76 324.18 1385.59 324.18ZM1385.59 311.39C1383.1 311.39 1381.05 313.26 1381.05 315.57C1381.05 317.87 1383.1 319.74 1385.59 319.74C1388.1 319.74 1390.15 317.87 1390.15 315.57C1390.15 313.26 1388.1 311.39 1385.59 311.39Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19139_0)" d="M246.65 259.91C241.49 259.91 237.29 256.05 237.29 251.3C237.29 246.55 241.49 242.69 246.65 242.69C251.82 242.69 256.02 246.55 256.02 251.3C256.02 256.05 251.82 259.91 246.65 259.91ZM246.65 247.12C244.15 247.12 242.11 248.99 242.11 251.3C242.11 253.6 244.15 255.48 246.65 255.48C249.16 255.48 251.2 253.6 251.2 251.3C251.2 248.99 249.16 247.12 246.65 247.12Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19140_0)" d="M102.27 183.51C97.11 183.51 92.91 179.64 92.91 174.9C92.91 170.15 97.11 166.28 102.27 166.28C107.44 166.28 111.64 170.15 111.64 174.9C111.64 179.64 107.44 183.51 102.27 183.51ZM102.27 170.71C99.77 170.71 97.73 172.59 97.73 174.89C97.73 177.2 99.77 179.07 102.27 179.07C104.78 179.07 106.82 177.2 106.82 174.89C106.82 172.59 104.78 170.71 102.27 170.71Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19141_0)" d="M514.75 289.04C509.59 289.04 505.38 285.18 505.38 280.43C505.38 275.68 509.59 271.82 514.75 271.82C519.92 271.82 524.12 275.68 524.12 280.43C524.12 285.18 519.92 289.04 514.75 289.04ZM514.75 276.25C512.24 276.25 510.21 278.12 510.21 280.42C510.21 282.73 512.24 284.6 514.75 284.6C517.26 284.6 519.3 282.73 519.3 280.42C519.3 278.12 517.26 276.25 514.75 276.25Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19142_0)" d="M156.98 384.4C151.82 384.4 147.62 380.54 147.62 375.79C147.62 371.04 151.82 367.18 156.98 367.18C162.15 367.18 166.35 371.04 166.35 375.79C166.35 380.54 162.15 384.4 156.98 384.4ZM156.98 371.61C154.48 371.61 152.44 373.48 152.44 375.79C152.44 378.09 154.48 379.96 156.98 379.96C159.49 379.96 161.53 378.09 161.53 375.79C161.53 373.48 159.49 371.61 156.98 371.61Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19143_0)" d="M1142.85 394.41C1137.68 394.41 1133.48 390.55 1133.48 385.8C1133.48 381.05 1137.68 377.19 1142.85 377.19C1148.01 377.19 1152.21 381.05 1152.21 385.8C1152.21 390.55 1148.01 394.41 1142.85 394.41ZM1142.85 381.62C1140.34 381.62 1138.3 383.49 1138.3 385.8C1138.3 388.1 1140.34 389.97 1142.85 389.97C1145.35 389.97 1147.4 388.1 1147.4 385.8C1147.4 383.49 1145.35 381.62 1142.85 381.62Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19144_0)" d="M667.19 177.97C662.02 177.97 657.82 174.11 657.82 169.36C657.82 164.61 662.02 160.75 667.19 160.75C672.35 160.75 676.55 164.61 676.55 169.36C676.55 174.11 672.35 177.97 667.19 177.97ZM667.19 165.18C664.68 165.18 662.64 167.05 662.64 169.35C662.64 171.66 664.68 173.53 667.19 173.53C669.69 173.53 671.73 171.66 671.73 169.35C671.73 167.05 669.69 165.18 667.19 165.18Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19145_0)" d="M710.4 358.06C705.24 358.06 701.03 354.2 701.03 349.45C701.03 344.7 705.24 340.84 710.4 340.84C715.57 340.84 719.77 344.7 719.77 349.45C719.77 354.2 715.57 358.06 710.4 358.06ZM710.4 345.27C707.9 345.27 705.86 347.14 705.86 349.45C705.86 351.75 707.9 353.62 710.4 353.62C712.91 353.62 714.95 351.75 714.95 349.45C714.95 347.14 712.91 345.27 710.4 345.27Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19146_0)" d="M1210.73 209.57C1205.56 209.57 1201.37 205.71 1201.37 200.96C1201.37 196.21 1205.56 192.35 1210.73 192.35C1215.89 192.35 1220.1 196.21 1220.1 200.96C1220.1 205.71 1215.89 209.57 1210.73 209.57ZM1210.73 196.78C1208.23 196.78 1206.18 198.66 1206.18 200.96C1206.18 203.26 1208.23 205.14 1210.73 205.14C1213.24 205.14 1215.28 203.26 1215.28 200.96C1215.28 198.66 1213.24 196.78 1210.73 196.78Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19147_0)" d="M815.3 390.79C810.14 390.79 805.94 386.93 805.94 382.18C805.94 377.43 810.14 373.57 815.3 373.57C820.47 373.57 824.67 377.43 824.67 382.18C824.67 386.93 820.47 390.79 815.3 390.79ZM815.3 378C812.8 378 810.76 379.87 810.76 382.18C810.76 384.48 812.8 386.36 815.3 386.36C817.81 386.36 819.85 384.48 819.85 382.18C819.85 379.87 817.81 378 815.3 378Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19148_0)" d="M1007.14 343.73C1007.72 339.4 1004.38 335.46 999.66 334.92C994.95 334.39 990.67 337.47 990.08 341.8C989.5 346.12 992.85 350.07 997.56 350.6C1002.27 351.13 1006.56 348.05 1007.14 343.73Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19149_0)" d="M1069.51 283.44C1069.51 287.79 1065.66 291.33 1060.92 291.33C1056.18 291.33 1052.34 287.79 1052.34 283.44C1052.34 279.07 1056.18 275.54 1060.92 275.54C1065.66 275.54 1069.51 279.07 1069.51 283.44Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19150_0)" d="M18.26 318.62C18.26 322.98 14.41 326.52 9.67 326.52C4.92 326.52 1.08 322.98 1.08 318.62C1.08 314.26 4.92 310.73 9.67 310.73C14.41 310.73 18.26 314.26 18.26 318.62Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19151_0)" d="M389.09 228.88C389.09 233.24 385.25 236.77 380.51 236.77C375.76 236.77 371.92 233.24 371.92 228.88C371.92 224.52 375.76 220.98 380.51 220.98C385.25 220.98 389.09 224.52 389.09 228.88Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19152_0)" d="M346.25 156.09C346.25 160.45 342.4 163.98 337.66 163.98C332.92 163.98 329.07 160.45 329.07 156.09C329.07 151.73 332.92 148.19 337.66 148.19C342.4 148.19 346.25 151.73 346.25 156.09Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19153_0)" d="M663.12 343.26C667.87 343.26 671.71 339.72 671.71 335.36C671.71 331 667.87 327.46 663.12 327.46C658.38 327.46 654.53 331 654.53 335.36C654.53 339.72 658.38 343.26 663.12 343.26Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19154_0)" d="M1393 187.55C1393.52 183.22 1390.12 179.32 1385.41 178.84C1380.68 178.36 1376.44 181.49 1375.92 185.83C1375.41 190.16 1378.81 194.06 1383.53 194.54C1388.24 195.01 1392.48 191.89 1393 187.55Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19155_0)" d="M696.48 373.49C700.51 371.2 701.76 366.33 699.27 362.62C696.78 358.91 691.48 357.76 687.45 360.05C683.41 362.35 682.16 367.21 684.66 370.92C687.15 374.63 692.44 375.78 696.48 373.49Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19156_0)" d="M748.48 249.89C752.52 247.6 753.77 242.73 751.27 239.02C748.78 235.31 743.49 234.16 739.45 236.45C735.42 238.75 734.17 243.61 736.66 247.32C739.15 251.03 744.45 252.18 748.48 249.89Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19157_0)" d="M582.91 374.33C578.41 374.33 574.75 370.96 574.75 366.83C574.75 362.69 578.41 359.32 582.91 359.32C587.41 359.32 591.07 362.69 591.07 366.83C591.07 370.96 587.41 374.33 582.91 374.33ZM582.91 361.54C579.74 361.54 577.16 363.91 577.16 366.83C577.16 369.74 579.74 372.11 582.91 372.11C586.09 372.11 588.66 369.74 588.66 366.83C588.66 363.91 586.09 361.54 582.91 361.54Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19158_0)" d="M55.48 346.85C50.98 346.85 47.32 343.49 47.32 339.35C47.32 335.21 50.98 331.85 55.48 331.85C59.98 331.85 63.64 335.21 63.64 339.35C63.64 343.49 59.98 346.85 55.48 346.85ZM55.48 334.06C52.31 334.06 49.73 336.43 49.73 339.35C49.73 342.27 52.3 344.64 55.48 344.64C58.65 344.64 61.23 342.27 61.23 339.35C61.23 336.43 58.65 334.06 55.48 334.06Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19159_0)" d="M287.58 334.7C283.08 334.7 279.42 331.33 279.42 327.19C279.42 323.05 283.08 319.69 287.58 319.69C292.09 319.69 295.75 323.05 295.75 327.19C295.75 331.33 292.09 334.7 287.58 334.7ZM287.58 321.9C284.41 321.9 281.83 324.27 281.83 327.19C281.83 330.11 284.41 332.48 287.58 332.48C290.76 332.48 293.34 330.11 293.34 327.19C293.34 324.27 290.76 321.9 287.58 321.9Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19160_0)" d="M514.75 321.9C510.25 321.9 506.59 318.54 506.59 314.4C506.59 310.26 510.25 306.89 514.75 306.89C519.25 306.89 522.91 310.26 522.91 314.4C522.91 318.54 519.25 321.9 514.75 321.9ZM514.75 309.11C511.58 309.11 509 311.48 509 314.4C509 317.32 511.58 319.69 514.75 319.69C517.93 319.69 520.5 317.32 520.5 314.4C520.5 311.48 517.93 309.11 514.75 309.11Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19161_0)" d="M1278.37 309.97C1273.87 309.97 1270.21 306.6 1270.21 302.46C1270.21 298.32 1273.87 294.96 1278.37 294.96C1282.87 294.96 1286.53 298.32 1286.53 302.46C1286.53 306.6 1282.87 309.97 1278.37 309.97ZM1278.37 297.17C1275.19 297.17 1272.62 299.54 1272.62 302.46C1272.62 305.38 1275.18 307.75 1278.37 307.75C1281.54 307.75 1284.12 305.38 1284.12 302.46C1284.12 299.54 1281.54 297.17 1278.37 297.17Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19162_0)" d="M1695.9 333.61C1691.4 333.61 1687.74 330.24 1687.74 326.1C1687.74 321.96 1691.4 318.6 1695.9 318.6C1700.4 318.6 1704.06 321.96 1704.06 326.1C1704.06 330.24 1700.4 333.61 1695.9 333.61ZM1695.9 320.81C1692.72 320.81 1690.14 323.19 1690.14 326.1C1690.14 329.02 1692.72 331.39 1695.9 331.39C1699.07 331.39 1701.65 329.02 1701.65 326.1C1701.65 323.19 1699.07 320.81 1695.9 320.81Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19163_0)" d="M682.33 292.44C677.83 292.44 674.17 289.07 674.17 284.93C674.17 280.79 677.83 277.43 682.33 277.43C686.83 277.43 690.49 280.79 690.49 284.93C690.49 289.07 686.83 292.44 682.33 292.44ZM682.33 279.64C679.16 279.64 676.58 282.01 676.58 284.93C676.58 287.85 679.16 290.22 682.33 290.22C685.51 290.22 688.08 287.85 688.08 284.93C688.08 282.01 685.51 279.64 682.33 279.64Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19164_0)" d="M1135.89 317.56C1131.39 317.56 1127.72 314.2 1127.72 310.06C1127.72 305.92 1131.39 302.55 1135.89 302.55C1140.39 302.55 1144.05 305.92 1144.05 310.06C1144.05 314.2 1140.39 317.56 1135.89 317.56ZM1135.89 304.77C1132.72 304.77 1130.14 307.14 1130.14 310.06C1130.14 312.98 1132.71 315.35 1135.89 315.35C1139.06 315.35 1141.64 312.98 1141.64 310.06C1141.64 307.14 1139.06 304.77 1135.89 304.77Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19165_0)" d="M1141.19 254.59C1136.69 254.59 1133.04 251.22 1133.04 247.08C1133.04 242.94 1136.69 239.58 1141.19 239.58C1145.69 239.58 1149.37 242.94 1149.37 247.08C1149.37 251.22 1145.69 254.59 1141.19 254.59ZM1141.19 241.79C1138.03 241.79 1135.44 244.16 1135.44 247.08C1135.44 250 1138.03 252.37 1141.19 252.37C1144.38 252.37 1146.94 250 1146.94 247.08C1146.94 244.16 1144.38 241.79 1141.19 241.79Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19166_0)" d="M842.46 367.9C843.07 364.41 840.5 361.13 836.71 360.56C832.91 360 829.34 362.36 828.72 365.85C828.11 369.34 830.68 372.62 834.48 373.19C838.27 373.75 841.84 371.39 842.46 367.9Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19167_0)" d="M174.73 332.07C174.73 335.6 171.62 338.47 167.77 338.47C163.93 338.47 160.82 335.6 160.82 332.07C160.82 328.54 163.93 325.67 167.77 325.67C171.62 325.67 174.73 328.54 174.73 332.07Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19168_0)" d="M446.45 350.4C447.07 346.91 444.49 343.63 440.7 343.06C436.9 342.5 433.33 344.87 432.72 348.35C432.1 351.84 434.68 355.13 438.47 355.69C442.27 356.25 445.84 353.88 446.45 350.4Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19169_0)" d="M276.14 138.43C278.86 135.93 278.86 131.88 276.14 129.38C273.42 126.89 269.02 126.89 266.3 129.38C263.58 131.88 263.58 135.93 266.3 138.43C269.02 140.93 273.42 140.93 276.14 138.43Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19170_0)" d="M1565.51 262.32C1565.51 265.85 1562.39 268.72 1558.55 268.72C1554.71 268.72 1551.59 265.85 1551.59 262.32C1551.59 258.79 1554.71 255.93 1558.55 255.93C1562.39 255.93 1565.51 258.79 1565.51 262.32Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19171_0)" d="M1466.21 270.39C1466.21 273.92 1463.1 276.79 1459.26 276.79C1455.42 276.79 1452.3 273.92 1452.3 270.39C1452.3 266.86 1455.42 263.99 1459.26 263.99C1463.1 263.99 1466.21 266.86 1466.21 270.39Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19172_0)" d="M1532.15 378.58C1535.42 376.72 1536.42 372.78 1534.41 369.78C1532.38 366.77 1528.1 365.84 1524.83 367.7C1521.57 369.56 1520.55 373.5 1522.57 376.5C1524.6 379.51 1528.88 380.44 1532.15 378.58Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19173_0)" d="M1149.56 363.87C1150.46 360.43 1148.15 356.98 1144.41 356.17C1140.67 355.36 1136.92 357.49 1136.03 360.93C1135.15 364.37 1137.47 367.81 1141.21 368.62C1144.94 369.43 1148.68 367.3 1149.56 363.87Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19174_0)" d="M627.92 127.53C629.4 124.26 627.72 120.52 624.17 119.16C620.63 117.8 616.55 119.34 615.07 122.6C613.6 125.87 615.27 129.61 618.82 130.97C622.37 132.33 626.44 130.79 627.92 127.53Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19175_0)" d="M1079.41 305.15L1012.21 305.15L943.91 367.93L835.56 367.93L835.56 365.72L942.91 365.72L1011.21 302.94L1078.41 302.94L1134.99 250.92L1136.68 252.49L1079.41 305.15Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19176_0)" d="M290.11 334.02L291.82 332.45L309.1 348.34L439.6 348.34L439.6 350.55L308.1 350.55L290.11 334.02Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19177_0)" d="M1291.61 324.47L1212.97 324.47L1148.49 383.75L1145.07 380.62L1210.97 320.04L1289.62 320.04L1382.16 234.96L1382.16 186.77L1386.98 186.77L1386.98 236.8L1291.61 324.47Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19178_0)" d="M1528.48 372.11L1643.46 372.11L1689.54 329.75L1691.24 331.32L1644.46 374.33L1528.48 374.33L1528.48 372.11Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19179_0)" d="M1207.6 301.35L1271.41 301.35L1271.41 303.57L1208.6 303.57L1143.69 363.23L1141.99 361.67L1207.6 301.35Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19180_0)" d="M278.2 435.6L276.5 437.17L229.7 394.15L112.59 394.15L112.24 393.82L59.72 345.54L61.42 343.98L113.59 391.93L230.7 391.93L278.2 435.6Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19181_0)" d="M63.21 320.84L9.67 320.84L9.67 316.4L65.2 316.4L127.39 373.57L150.02 373.57L150.02 378.01L125.39 378.01L63.21 320.84Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19182_0)" d="M319.66 180.01L270.37 134.69L272.07 133.12L320.66 177.79L368.46 177.79L511.48 309.27L509.78 310.84L367.46 180.01L319.66 180.01Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19183_0)" d="M337.66 158.3L337.66 153.87L380.34 153.87L512.34 275.22L508.93 278.35L378.35 158.3L337.66 158.3Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19184_0)" d="M104.44 180.18L107.84 177.04L161.81 226.66L380.51 226.66L380.51 231.1L159.82 231.1L104.44 180.18Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19185_0)" d="M1150.62 198.74L1206.21 198.74L1206.21 203.18L1152.62 203.18L1062.63 285.91L1059.22 282.77L1150.62 198.74Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19186_0)" d="M891.11 325.75L817 325.75L786.88 353.45L783.47 350.31L815 321.32L889.11 321.32L978.49 239.15L1033.97 239.15L1033.97 243.59L980.48 243.59L891.11 325.75Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19187_0)" d="M1209.92 248.39L1270.92 248.39L1307.92 214.37L1309.62 215.94L1271.91 250.61L1210.92 250.61L1165.77 292.11L1164.06 290.55L1209.92 248.39Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19188_0)" d="M1551.59 344.99L1621.93 344.99L1698.21 274.86L1754.42 274.86L1754.42 279.29L1700.21 279.29L1623.92 349.42L1551.59 349.42L1551.59 344.99Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19189_0)" d="M1558.02 222.63L1648.65 222.63L1691.02 183.69L1694.43 186.82L1650.66 227.06L1560.02 227.06L1507.22 275.6L1503.81 272.47L1558.02 222.63Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19190_0)" d="M1637.86 252.71L1710.89 252.71L1746.41 220.05L1748.11 221.62L1711.89 254.93L1638.87 254.93L1573.32 315.18L1571.62 313.61L1637.86 252.71Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19191_0)" d="M1542.4 355.67C1537.24 355.67 1533.04 351.81 1533.04 347.06C1533.04 342.31 1537.24 338.45 1542.4 338.45C1547.56 338.45 1551.77 342.31 1551.77 347.06C1551.77 351.81 1547.56 355.67 1542.4 355.67ZM1542.4 342.88C1539.89 342.88 1537.85 344.75 1537.85 347.06C1537.85 349.36 1539.89 351.23 1542.4 351.23C1544.9 351.23 1546.95 349.36 1546.95 347.06C1546.95 344.75 1544.9 342.88 1542.4 342.88Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19192_0)" d="M1500.78 286.69C1495.61 286.69 1491.41 282.83 1491.41 278.08C1491.41 273.33 1495.61 269.47 1500.78 269.47C1505.93 269.47 1510.14 273.33 1510.14 278.08C1510.14 282.83 1505.93 286.69 1500.78 286.69ZM1500.78 273.9C1498.27 273.9 1496.22 275.77 1496.22 278.08C1496.22 280.38 1498.27 282.25 1500.78 282.25C1503.27 282.25 1505.32 280.38 1505.32 278.08C1505.32 275.77 1503.27 273.9 1500.78 273.9Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19193_0)" d="M778.22 364.46C773.05 364.46 768.85 360.59 768.85 355.84C768.85 351.09 773.05 347.23 778.22 347.23C783.38 347.23 787.58 351.09 787.58 355.84C787.58 360.59 783.38 364.46 778.22 364.46ZM778.22 351.66C775.71 351.66 773.67 353.54 773.67 355.84C773.67 358.14 775.71 360.02 778.22 360.02C780.72 360.02 782.76 358.14 782.76 355.84C782.76 353.54 780.72 351.66 778.22 351.66Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19194_0)" d="M1754.42 284.97C1759.16 284.97 1763.01 281.43 1763.01 277.07C1763.01 272.71 1759.16 269.18 1754.42 269.18C1749.68 269.18 1745.82 272.71 1745.82 277.07C1745.82 281.43 1749.68 284.97 1754.42 284.97Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19195_0)" d="M1696.01 192.55C1700.39 190.88 1702.47 186.26 1700.66 182.23C1698.84 178.2 1693.81 176.29 1689.43 177.96C1685.05 179.63 1682.97 184.24 1684.78 188.27C1686.6 192.3 1691.63 194.22 1696.01 192.55Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19196_0)" d="M1038.49 247.34C1042.53 245.05 1043.78 240.18 1041.28 236.47C1038.79 232.76 1033.49 231.61 1029.46 233.91C1025.43 236.2 1024.18 241.07 1026.67 244.78C1029.17 248.49 1034.46 249.63 1038.49 247.34Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19197_0)" d="M1751.81 223.54C1747.31 223.54 1743.65 220.18 1743.65 216.04C1743.65 211.9 1747.31 208.54 1751.81 208.54C1756.31 208.54 1759.97 211.9 1759.97 216.04C1759.97 220.18 1756.31 223.54 1751.81 223.54ZM1751.81 210.75C1748.64 210.75 1746.05 213.12 1746.05 216.04C1746.05 218.96 1748.64 221.33 1751.81 221.33C1754.99 221.33 1757.56 218.96 1757.56 216.04C1757.56 213.12 1754.99 210.75 1751.81 210.75Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19198_0)" d="M1313.95 217.84C1309.44 217.84 1305.79 214.47 1305.79 210.33C1305.79 206.19 1309.44 202.83 1313.95 202.83C1318.45 202.83 1322.11 206.19 1322.11 210.33C1322.11 214.47 1318.45 217.84 1313.95 217.84ZM1313.95 205.04C1310.78 205.04 1308.19 207.41 1308.19 210.33C1308.19 213.25 1310.78 215.62 1313.95 215.62C1317.13 215.62 1319.7 213.25 1319.7 210.33C1319.7 207.41 1317.13 205.04 1313.95 205.04Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19200_0)" d="M1578.9 316.84C1580.36 313.58 1578.68 309.84 1575.13 308.49C1571.58 307.13 1567.51 308.68 1566.04 311.95C1564.57 315.21 1566.25 318.95 1569.8 320.31C1573.35 321.66 1577.42 320.11 1578.9 316.84Z" id="Vector"/>
<path fill-rule="nonzero" fill-opacity="1.000000" fill="url(#paint_linear_47_19201_0)" d="M1167.57 297.24C1171.13 295.89 1172.81 292.15 1171.34 288.88C1169.87 285.62 1165.8 284.07 1162.26 285.42C1158.71 286.77 1157.02 290.51 1158.49 293.78C1159.96 297.04 1164.03 298.59 1167.57 297.24Z" id="Vector"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 94 KiB

View file

@ -1,20 +0,0 @@
<svg width="529.000000" height="199.000000" viewBox="0 0 529 199" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<desc>
Created with Pixso.
</desc>
<defs>
<clipPath id="clip47_19202">
<rect id="GO_NETS_LOGO 1" rx="0.000000" width="528.000000" height="198.000000" transform="translate(0.500000 0.500000)" fill="white" fill-opacity="0"/>
</clipPath>
</defs>
<rect id="GO_NETS_LOGO 1" rx="0.000000" width="528.000000" height="198.000000" transform="translate(0.500000 0.500000)" fill="#FFFFFF" fill-opacity="0"/>
<g clip-path="url(#clip47_19202)">
<path id="Vector" d="M337.61 194.16L361.93 194.16L361.93 1.04L337.85 1.04L337.85 87.6L337.85 101.58L337.85 125.69L337.85 159.44L193.39 1.28L169.31 1.28L169.55 194.16L193.63 194.16L193.63 169.33L193.39 125.93L193.39 101.82L193.39 87.84L193.39 71.93L193.39 36.48C196.12 39.54 199.81 43.64 204.46 48.78C209.12 53.92 217.71 63.41 230.23 77.23C242.75 91.05 254.58 104.03 265.74 116.17C276.89 128.3 288.21 140.68 299.69 153.3C311.16 165.91 320.35 175.92 327.25 183.31C334.16 190.71 337.61 194.32 337.61 194.16Z" fill="#006666" fill-opacity="1.000000" fill-rule="nonzero"/>
<path id="Vector" d="M72.5 0.02L193.33 0.02L193.33 72.62L169.16 72.62L169.16 24.22L72.5 24.22C59.12 24.22 47.72 28.94 38.3 38.38C28.87 47.82 24.16 59.23 24.16 72.62L24.16 193.62L0 193.62L0 72.62C0 52.62 7.08 35.52 21.26 21.32C35.44 7.12 52.52 0.02 72.5 0.02Z" fill="#716B64" fill-opacity="1.000000" fill-rule="nonzero"/>
<path id="Vector" d="M113.73 36.13C70.16 36.13 34.82 71.52 34.82 115.15C34.82 158.78 70.16 194.17 113.73 194.17C157.3 194.17 192.64 158.78 192.64 115.15C192.64 71.52 157.3 36.13 113.73 36.13ZM113.73 60.55C143.85 60.55 168.25 84.99 168.25 115.15C168.25 145.3 143.85 169.74 113.73 169.74C83.62 169.74 59.21 145.3 59.21 115.15C59.21 84.99 83.62 60.55 113.73 60.55Z" fill="#5166A6" fill-opacity="1.000000" fill-rule="nonzero"/>
<path id="Vector" d="M452.16 0C450.58 0 449.2 0.8 448.01 2.37C446.83 3.94 446.24 5.45 446.24 6.93L446.24 55.42L369.16 55.95C367.28 54.15 364.75 53.05 361.94 53.05C356.16 53.05 351.46 57.7 351.46 63.48C351.46 69.27 356.16 73.98 361.94 73.98C365.27 73.98 368.13 72.32 370.05 69.89L446.24 69.29L446.24 187.02C446.24 189.42 446.83 191.19 448.01 192.3C450.06 193.41 451.45 193.96 452.16 193.96C453.89 193.96 455.31 193.41 456.42 192.3C457.52 191 458.08 189.24 458.08 187.02L458.08 69.29L475.96 69.29C477.85 69.29 479.31 68.52 480.34 67.04C481.36 65.56 481.88 64.02 481.88 62.36C481.88 60.69 481.24 59.15 479.98 57.67C478.72 56.2 477.38 55.42 475.96 55.42L458.08 55.42L458.08 6.93C458.08 4.71 457.45 3.03 456.18 1.83C454.92 0.63 453.58 0 452.16 0Z" fill="#716B64" fill-opacity="1.000000" fill-rule="nonzero"/>
<path id="Vector" d="M392.43 86.42C386.33 86.42 381.24 88.64 377.15 93.12C373.07 97.61 371 102.56 371 107.94L371 134.5L346.43 134.5C344.58 131.55 341.46 129.46 337.73 129.46C331.95 129.46 327.31 134.17 327.31 139.95C327.31 145.74 331.95 150.39 337.73 150.39C341.46 150.39 344.58 148.36 346.43 145.41L371 145.41L371 172.44C371 178.47 373.05 183.57 377.21 187.73C381.37 191.9 386.48 193.96 392.43 193.96L430.01 193.96L442.75 193.96C444.59 196.94 447.68 199 451.45 199C457.22 199 461.92 194.35 461.92 188.56C461.92 182.78 457.22 178.07 451.45 178.07C447.65 178.07 444.52 180.21 442.69 183.23L430.08 183.23C430.05 183.23 430.04 183.17 430.01 183.17L392.43 183.17C389.49 183.17 386.93 182.17 384.85 180.09C382.77 178 381.77 175.45 381.77 172.44L381.77 145.53L430.01 145.53C433.67 145.53 435.52 143.72 435.52 140.13L435.52 107.94C435.52 101.91 433.46 96.81 429.31 92.65C425.15 88.49 420.05 86.42 414.03 86.42L392.43 86.42ZM392.43 97.15L414.03 97.15C417.04 97.15 419.59 98.21 421.67 100.3C423.75 102.38 424.75 104.93 424.75 107.94L424.75 134.86L381.77 134.86L381.77 107.94C381.77 104.93 382.77 102.38 384.85 100.3C386.93 98.21 389.49 97.15 392.43 97.15Z" fill="#5166A6" fill-opacity="1.000000" fill-rule="nonzero"/>
<path id="Vector" d="M486.97 86.66C480.09 86.66 474.48 89.03 470.04 93.83C465.88 98.28 463.76 103.65 463.76 109.96C463.76 111.18 463.93 112.64 464.29 114.29L465.83 119.56C467.33 122.64 469.49 125.38 472.28 127.74L491.23 143.21L513.6 161.47C516.61 164.7 518.1 167.87 518.1 170.96C518.1 174.25 516.96 177.16 514.67 179.67C512.38 182.18 509.44 183.46 505.79 183.46L491.05 183.46C487.4 183.46 484.05 182.66 481.05 181.15C477.55 178.96 464.68 168.7 462.34 166.1C462.37 165.77 462.52 165.48 462.52 165.15C462.52 159.36 457.82 154.65 452.04 154.65C446.26 154.65 441.62 159.36 441.62 165.15C441.62 170.93 446.26 175.64 452.04 175.64C453.84 175.64 455.47 175.08 456.95 174.28C457.06 174.39 457.15 174.52 457.25 174.63C462.35 180.03 472.83 189.16 476.55 190.99C480.84 193.11 485.68 194.19 491.05 194.19L505.79 194.19C512.59 194.19 518.22 191.77 522.66 186.96C526.82 182.52 528.88 177.15 528.88 170.84C528.88 167.54 528.18 164.38 526.75 161.29C525.38 158 523.26 155.26 520.47 153.11L479.15 119.32C476.07 116.67 474.53 113.56 474.53 109.9C474.53 106.53 475.68 103.6 477.97 101.13C480.26 98.65 483.24 97.39 486.97 97.39L501.71 97.39C503.43 97.39 505.09 97.61 506.74 98.04L511.59 99.76C514.74 101.34 517.29 103.41 519.23 105.99C519.73 106.63 520.11 107.17 520.47 107.53C520.61 107.67 520.79 107.8 521 107.94L523.49 108.24C524.85 108.24 526.13 107.76 527.28 106.76C528.42 105.54 529 104.25 529 102.96L528.64 101.18L527.81 99.52C524.66 95.29 520.84 92.11 516.33 89.92C511.82 87.73 506.93 86.66 501.71 86.66L486.97 86.66Z" fill="#5166A6" fill-opacity="1.000000" fill-rule="nonzero"/>
<path id="Текст" d="" fill="#000000" fill-opacity="1.000000" fill-rule="evenodd"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 5.5 KiB

View file

@ -1,15 +0,0 @@
<svg width="42" height="13" viewBox="0 0 42 13" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1_2)">
<path d="M4.79966 0.00675106H12.7991V4.80641H11.1992V1.60664H4.79966C3.91972 1.60664 3.15978 1.91328 2.53316 2.53991C1.90653 3.16653 1.59989 3.91314 1.59989 4.80641V12.8058H5.81835e-07V4.80641C5.81835e-07 3.48651 0.466634 2.35325 1.41323 1.41998C2.3465 0.473385 3.47975 0.00675106 4.79966 0.00675106Z" fill="black"/>
<path d="M7.93277 5.73968H12.7858V10.5927C12.7858 11.166 12.5858 11.6593 12.1725 12.0592C11.7725 12.4592 11.2792 12.6725 10.6926 12.6725H5.83959V7.81953C5.83959 7.24624 6.03957 6.75294 6.45288 6.33964C6.86618 5.92633 7.34615 5.72635 7.93277 5.72635V5.73968ZM12.0925 10.5927V6.43296H7.93277C7.54613 6.43296 7.22616 6.56629 6.94618 6.83294C6.67953 7.09958 6.54621 7.43289 6.54621 7.81953V11.9792H10.7059C11.0926 11.9792 11.4125 11.8459 11.6925 11.5793C11.9592 11.3126 12.0925 10.9793 12.0925 10.5927Z" fill="black"/>
<path d="M22.1051 11.9126C22.1051 12.3792 21.9185 12.6192 21.5318 12.6192C21.2785 12.6192 21.0785 12.4859 20.9319 12.2192L15.8389 3.17986V11.9792C15.8389 12.3925 15.6256 12.6059 15.2123 12.6059C14.799 12.6059 14.5856 12.3925 14.5856 11.9792V0.7267C14.5856 0.260067 14.7856 0.0334158 15.1723 0.0334158C15.4256 0.0334158 15.6256 0.16674 15.7589 0.420055L20.8519 9.45942V0.660038C20.8519 0.246734 21.0652 0.0334158 21.4785 0.0334158C21.8918 0.0334158 22.1051 0.246734 22.1051 0.660038V11.8992V11.9126Z" fill="black"/>
<path d="M24.8116 9.61941V10.606C24.8116 10.8727 24.9049 11.1126 25.1049 11.2993C25.3049 11.4859 25.5315 11.5926 25.7982 11.5926H28.278C28.6113 11.5926 28.7713 11.7526 28.7713 12.0859C28.7713 12.4192 28.6113 12.5792 28.278 12.5792H25.7982C25.2516 12.5792 24.7849 12.3792 24.3983 11.9926C24.0116 11.6059 23.8117 11.1393 23.8117 10.5927V7.61955C23.8117 7.07292 24.0116 6.60629 24.3983 6.21965C24.7849 5.83301 25.2516 5.63302 25.7982 5.63302H26.7848C27.3314 5.63302 27.798 5.83301 28.1847 6.21965C28.5713 6.60629 28.7713 7.07292 28.7713 7.61955V9.11277C28.7713 9.44608 28.6113 9.60607 28.278 9.60607H24.8116V9.61941ZM24.8116 8.63281H27.7847V7.64621C27.7847 7.37956 27.6914 7.13958 27.4914 6.95293C27.2914 6.76627 27.0648 6.65961 26.7981 6.65961H25.8115C25.5449 6.65961 25.3049 6.75294 25.1182 6.95293C24.9316 7.15291 24.8249 7.37956 24.8249 7.64621V8.63281H24.8116Z" fill="black"/>
<path d="M32.7444 5.65968H34.2376C34.5709 5.65968 34.7309 5.81967 34.7309 6.15298C34.7309 6.48629 34.5709 6.64628 34.2376 6.64628H32.7444V12.0992C32.7444 12.4325 32.5844 12.5925 32.2511 12.5925C31.9177 12.5925 31.7578 12.4325 31.7578 12.0992V6.64628H31.2645C30.9311 6.64628 30.7712 6.48629 30.7712 6.15298C30.7712 5.81967 30.9311 5.65968 31.2645 5.65968H31.7578V3.17986C31.7578 2.84655 31.9177 2.68656 32.2511 2.68656C32.5844 2.68656 32.7444 2.84655 32.7444 3.17986V5.65968Z" fill="black"/>
<path d="M41.6504 6.63295C41.6504 6.76627 41.5971 6.88626 41.5037 6.99292C41.4104 7.09958 41.2904 7.15291 41.1571 7.15291C41.0771 7.15291 40.9971 7.12625 40.9171 7.09958C40.3438 6.8196 39.7839 6.67295 39.2239 6.67295C38.6639 6.67295 38.224 6.80627 37.8507 7.05959C37.7573 7.12625 37.7173 7.21958 37.7173 7.3529C37.7173 7.48622 37.7573 7.56622 37.8507 7.63288L41.0104 9.71273C41.4104 9.97938 41.6104 10.366 41.6104 10.8726C41.6104 11.3793 41.4104 11.7659 41.0104 12.0326C40.4371 12.4059 39.7972 12.5925 39.0772 12.5925C38.3573 12.5925 37.6907 12.4059 37.0241 12.0459C36.8507 11.9526 36.7574 11.8059 36.7574 11.6193C36.7574 11.4859 36.8107 11.3659 36.9041 11.2593C36.9974 11.1526 37.1174 11.0993 37.2507 11.0993C37.3307 11.0993 37.424 11.126 37.504 11.166C38.0106 11.4459 38.5306 11.5926 39.0639 11.5926C39.5972 11.5926 40.0505 11.4593 40.4638 11.1793C40.5705 11.1126 40.6371 10.9926 40.6371 10.846C40.6371 10.6993 40.5838 10.5927 40.4638 10.5127L37.2907 8.43282C36.9307 8.19284 36.7574 7.83287 36.7574 7.33957C36.7574 6.84627 36.9307 6.47296 37.2907 6.23298C37.8507 5.85967 38.4906 5.67302 39.2372 5.67302C39.9838 5.67302 40.6505 5.84634 41.3437 6.19298C41.5304 6.28631 41.6371 6.43296 41.6371 6.63295H41.6504Z" fill="black"/>
</g>
<defs>
<clipPath id="clip0_1_2">
<rect width="41.6504" height="12.8126" fill="white"/>
</clipPath>
</defs>
</svg>

Before

Width:  |  Height:  |  Size: 4.1 KiB

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="white" width="800px" height="800px" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg">
<g>
<path d="M10,1a9,9,0,1,0,9,9A9,9,0,0,0,10,1Zm0,16.4A7.4,7.4,0,1,1,17.4,10,7.41,7.41,0,0,1,10,17.4ZM13.29,5.29,10,8.59,6.71,5.29,5.29,6.71,8.59,10l-3.3,3.29,1.42,1.42L10,11.41l3.29,3.3,1.42-1.42L11.41,10l3.3-3.29Z"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 459 B

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 20.75H6C5.27065 20.75 4.57118 20.4603 4.05546 19.9445C3.53973 19.4288 3.25 18.7293 3.25 18V6C3.25 5.27065 3.53973 4.57118 4.05546 4.05546C4.57118 3.53973 5.27065 3.25 6 3.25H14.86C15.0589 3.25 15.2497 3.32902 15.3903 3.46967C15.531 3.61032 15.61 3.80109 15.61 4C15.61 4.19891 15.531 4.38968 15.3903 4.53033C15.2497 4.67098 15.0589 4.75 14.86 4.75H6C5.66848 4.75 5.35054 4.8817 5.11612 5.11612C4.8817 5.35054 4.75 5.66848 4.75 6V18C4.75 18.3315 4.8817 18.6495 5.11612 18.8839C5.35054 19.1183 5.66848 19.25 6 19.25H18C18.3315 19.25 18.6495 19.1183 18.8839 18.8839C19.1183 18.6495 19.25 18.3315 19.25 18V10.29C19.25 10.0911 19.329 9.90032 19.4697 9.75967C19.6103 9.61902 19.8011 9.54 20 9.54C20.1989 9.54 20.3897 9.61902 20.5303 9.75967C20.671 9.90032 20.75 10.0911 20.75 10.29V18C20.75 18.7293 20.4603 19.4288 19.9445 19.9445C19.4288 20.4603 18.7293 20.75 18 20.75Z" fill="white"/>
<path d="M10.5 15.25C10.3071 15.2352 10.1276 15.1455 10 15L7.00001 12C6.93317 11.86 6.91136 11.7028 6.93759 11.5499C6.96382 11.3971 7.03679 11.2561 7.14646 11.1464C7.25613 11.0368 7.3971 10.9638 7.54996 10.9376C7.70282 10.9113 7.86006 10.9331 8.00001 11L10.47 13.47L19 4.99998C19.14 4.93314 19.2972 4.91133 19.4501 4.93756C19.6029 4.96379 19.7439 5.03676 19.8536 5.14643C19.9632 5.2561 20.0362 5.39707 20.0624 5.54993C20.0887 5.70279 20.0669 5.86003 20 5.99998L11 15C10.8724 15.1455 10.693 15.2352 10.5 15.25Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.2799 6.40005L11.7399 15.94C10.7899 16.89 7.96987 17.33 7.33987 16.7C6.70987 16.07 7.13987 13.25 8.08987 12.3L17.6399 2.75002C17.8754 2.49308 18.1605 2.28654 18.4781 2.14284C18.7956 1.99914 19.139 1.92124 19.4875 1.9139C19.8359 1.90657 20.1823 1.96991 20.5056 2.10012C20.8289 2.23033 21.1225 2.42473 21.3686 2.67153C21.6147 2.91833 21.8083 3.21243 21.9376 3.53609C22.0669 3.85976 22.1294 4.20626 22.1211 4.55471C22.1128 4.90316 22.0339 5.24635 21.8894 5.5635C21.7448 5.88065 21.5375 6.16524 21.2799 6.40005V6.40005Z" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
<path d="M11 4H6C4.93913 4 3.92178 4.42142 3.17163 5.17157C2.42149 5.92172 2 6.93913 2 8V18C2 19.0609 2.42149 20.0783 3.17163 20.8284C3.92178 21.5786 4.93913 22 6 22H17C19.21 22 20 20.2 20 18V13" stroke="white" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg fill="white" height="800px" width="800px" version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 490 490" xml:space="preserve">
<g>
<g>
<path d="M0,0v490h490V0H0z M430.1,332.9h-87.5v50.9h-33.1v50.9H180.4v-50.6h-33.1v-51.3H59.9v-278h46.7v66.5h38.5V54.8h40.8v66.5
h38.5V54.8h40.8v66.5h38.5V54.8h40.8v66.5H383V54.8h46.7v278.1L430.1,332.9L430.1,332.9z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 579 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 16 KiB

View file

@ -1,17 +0,0 @@
<svg version="1.1" id="signalSvg" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 296 235" style="enable-background:new 0 0 296 235;" xml:space="preserve">
<style type="text/css">
.st1{fill-rule:evenodd;clip-rule:evenodd;fill:var(--st1-color, white);}
.st2{fill-rule:evenodd;clip-rule:evenodd;fill:var(--st2-color, white);}
.st3{fill-rule:evenodd;clip-rule:evenodd;fill:var(--st3-color, white);}
.st4{fill-rule:evenodd;clip-rule:evenodd;fill:var(--st4-color, white);}
.st5{fill-rule:evenodd;clip-rule:evenodd;fill:var(--st5-color, white);}
</style>
<g>
<polygon class="st1" points="47,138 47,188 0,188 0,164 "/>
<polygon class="st2" points="109,104 109,188 62,188 62,130 "/>
<polygon class="st3" points="171,69 171,188 124,188 124,95 "/>
<polygon class="st4" points="234,35 234,188 187,188 187,61 "/>
<polygon class="st5" points="296,0 296,188 249,188 249,26 "/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 966 B

View file

@ -1 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" aria-hidden="true" role="img" class="iconify iconify--logos" width="31.88" height="32" preserveAspectRatio="xMidYMid meet" viewBox="0 0 256 257"><defs><linearGradient id="IconifyId1813088fe1fbc01fb466" x1="-.828%" x2="57.636%" y1="7.652%" y2="78.411%"><stop offset="0%" stop-color="#41D1FF"></stop><stop offset="100%" stop-color="#BD34FE"></stop></linearGradient><linearGradient id="IconifyId1813088fe1fbc01fb467" x1="43.376%" x2="50.316%" y1="2.242%" y2="89.03%"><stop offset="0%" stop-color="#FFEA83"></stop><stop offset="8.333%" stop-color="#FFDD35"></stop><stop offset="100%" stop-color="#FFA800"></stop></linearGradient></defs><path fill="url(#IconifyId1813088fe1fbc01fb466)" d="M255.153 37.938L134.897 252.976c-2.483 4.44-8.862 4.466-11.382.048L.875 37.958c-2.746-4.814 1.371-10.646 6.827-9.67l120.385 21.517a6.537 6.537 0 0 0 2.322-.004l117.867-21.483c5.438-.991 9.574 4.796 6.877 9.62Z"></path><path fill="url(#IconifyId1813088fe1fbc01fb467)" d="M185.432.063L96.44 17.501a3.268 3.268 0 0 0-2.634 3.014l-5.474 92.456a3.268 3.268 0 0 0 3.997 3.378l24.777-5.718c2.318-.535 4.413 1.507 3.936 3.838l-7.361 36.047c-.495 2.426 1.782 4.5 4.151 3.78l15.304-4.649c2.372-.72 4.652 1.36 4.15 3.788l-11.698 56.621c-.732 3.542 3.979 5.473 5.943 2.437l1.313-2.028l72.516-144.72c1.215-2.423-.88-5.186-3.54-4.672l-25.505 4.922c-2.396.462-4.435-1.77-3.759-4.114l16.646-57.705c.677-2.35-1.37-4.583-3.769-4.113Z"></path></svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,27 +0,0 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 20010904//EN"
"http://www.w3.org/TR/2001/REC-SVG-20010904/DTD/svg10.dtd">
<svg version="1.0" xmlns="http://www.w3.org/2000/svg"
width="512.000000pt" height="512.000000pt" viewBox="0 0 512.000000 512.000000"
preserveAspectRatio="xMidYMid meet">
<g transform="translate(0.000000,512.000000) scale(0.100000,-0.100000)"
fill="white" stroke="none">
<path d="M2350 5050 c-581 -194 -1241 -344 -1878 -426 l-133 -17 4 -976 c4
-1045 4 -1044 58 -1310 174 -864 747 -1624 1524 -2023 72 -36 244 -118 383
-182 l253 -116 242 111 c344 158 493 235 647 337 649 426 1104 1093 1264 1849
59 280 59 275 63 1336 3 581 0 977 -5 977 -24 0 -392 51 -502 70 -552 94
-1141 242 -1607 404 -56 20 -103 36 -105 35 -2 0 -95 -32 -208 -69z m379 -976
c31 -8 94 -33 141 -56 69 -33 101 -57 171 -128 137 -137 199 -285 199 -476 0
-287 -178 -543 -444 -640 l-61 -22 -3 -266 -2 -266 187 0 c169 0 191 -2 230
-21 92 -44 122 -171 60 -252 -46 -61 -72 -67 -286 -67 l-191 0 0 -170 0 -170
173 0 c191 0 232 -7 281 -50 45 -40 60 -79 54 -141 -8 -85 -69 -143 -161 -154
-29 -4 -167 -5 -307 -3 -285 3 -294 5 -347 75 l-28 36 -5 725 -5 724 -64 24
c-268 101 -441 352 -441 638 0 115 20 198 71 305 54 110 179 239 284 293 166
84 326 104 494 62z"/>
<path d="M2430 3728 c-66 -25 -145 -101 -180 -173 -20 -42 -25 -65 -25 -140 0
-79 4 -97 28 -147 36 -74 119 -150 189 -174 30 -10 82 -18 118 -18 36 0 88 8
118 18 70 24 153 100 189 174 24 50 28 68 28 147 0 109 -23 163 -100 241 -91
92 -238 121 -365 72z" fill="white"/>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 1.5 KiB

View file

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="utf-8"?><!-- Uploaded to: SVG Repo, www.svgrepo.com, Generator: SVG Repo Mixer Tools -->
<svg width="800px" height="800px" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M0 7L1.17157 5.82843C2.98259 4.01741 5.43884 3 8 3C10.5612 3 13.0174 4.01742 14.8284 5.82843L16 7L14.5858 8.41421L13.4142 7.24264C11.9783 5.8067 10.0307 5 8 5C5.96928 5 4.02173 5.8067 2.58579 7.24264L1.41421 8.41421L0 7Z" fill="white"/>
<path d="M4.24264 11.2426L2.82843 9.82843L4 8.65685C5.06086 7.59599 6.49971 7 8 7C9.50029 7 10.9391 7.59599 12 8.65686L13.1716 9.82843L11.7574 11.2426L10.5858 10.0711C9.89999 9.38527 8.96986 9 8 9C7.03014 9 6.1 9.38527 5.41421 10.0711L4.24264 11.2426Z" fill="white"/>
<path d="M8 15L5.65685 12.6569L6.82842 11.4853C7.13914 11.1746 7.56057 11 8 11C8.43942 11 8.86085 11.1746 9.17157 11.4853L10.3431 12.6569L8 15Z" fill="white"/>
</svg>

Before

Width:  |  Height:  |  Size: 906 B

View file

@ -1,160 +0,0 @@
import axios, { AxiosResponse } from "axios";
const axiosWithoutCredentials = axios.create({
baseURL: `http://localhost:8001`,
})
export class NetworkService {
static createAxiosInstance(ipAddress: string) {
return axios.create({
baseURL: `http://${ipAddress}:8001`,
});
}
static async checkIp(ipAddress: string, data: any): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post('/ip/check_ip', data);
} catch (error: any) {
return error.response;
}
}
static async saveIp(ipAddress: string, data: any): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post('/ip/apply_ip', data);
} catch (error: any) {
return error.response;
}
}
static async getHostname(): Promise<AxiosResponse>{
try{
const response = await axiosWithoutCredentials.get('/network/get_hostname')
return response
} catch (error: any){
return error.response
}
}
static async createUser(ipAddress: string, data: any): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post('/auth/register', data);
} catch (error: any) {
return error.response;
}
}
static async login(ipAddress: string, data: any): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post('/auth/login', data);
} catch (error: any) {
return error.response;
}
}
static async logout(ipAddress: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get('/auth/logout');
} catch (error: any) {
return error.response;
}
}
static async checkServerConf(ipAddress: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get('/auth/check_json');
} catch (error: any) {
return error.response;
}
}
static async whoami(ipAddress: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get('/auth/me');
} catch (error: any) {
return error.response;
}
}
static async scanUser(ipAddress: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get('/subnet/devices');
} catch (error: any) {
return error.response;
}
}
static async scanWifi(ipAddress: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get('/subnet/wifi');
} catch (error: any) {
return error.response;
}
}
static async addNote(ipAddress: string, address:string, note: string,type:string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post(`/subnet/${type}/${address}/note`, {
note: note
});
} catch (error: any) {
return error.response;
}
}
static async editNote(ipAddress: string, address:string, note: string,type:string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.put(`/subnet/${type}/${address}/note`, {
note: note
});
} catch (error: any) {
return error.response;
}
}
static async deleteNote(ipAddress: string, address:string, type:string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.delete(`/subnet/${type}/${address}/note`);
} catch (error: any) {
return error.response;
}
}
static async statusNetwork(ipAddress: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get(`/subnet/wifi/status`);
} catch (error: any) {
return error.response;
}
}
static async connectNetwork(ipAddress: string, password:string, address: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post(`/subnet/wifi/connect`, {
ssid: address,
password: password,
});
} catch (error: any) {
return error.response;
}
}
static async disconnectNetwork(ipAddress: string, ssid: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post(`/subnet/wifi/disconnect`, {
ssid: ssid,
});
} catch (error: any) {
return error.response;
}
}
}

View file

@ -1,108 +0,0 @@
.react-flow {
/* Custom Variables */
--xy-theme-selected: #F57DBD;
--xy-theme-hover: #C5C5C5;
--xy-theme-edge-hover: black;
--xy-theme-color-focus: #E8E8E8;
/* Built-in Variables see https://reactflow.dev/learn/customization/theming */
--xy-node-border-default: 1px solid #EDEDED;
/* --xy-node-boxshadow-default:
0px 3.54px 4.55px 0px #00000005,
0px 3.54px 4.55px 0px #0000000D,
0px 0.51px 1.01px 0px #0000001A; */
--xy-node-border-radius-default: 8px;
--xy-handle-background-color-default: #ffffff;
--xy-handle-border-color-default: #AAAAAA;
--xy-edge-label-color-default: #505050;
}
.react-flow.dark {
--xy-node-boxshadow-default:
0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.05), /* light shadow */
0px 3.54px 4.55px 0px rgba(255, 255, 255, 0.13), /* medium shadow */
0px 0.51px 1.01px 0px rgba(255, 255, 255, 0.2); /* smallest shadow */
--xy-theme-color-focus: #535353;
}
/* Customizing Default Theming */
.react-flow__node {
/* box-shadow: var(--xy-node-boxshadow-default);
border-radius: var(--xy-node-border-radius-default);
background-color: var(--xy-node-background-color-default); */
display: flex;
justify-content: center;
align-items: center;
text-align: center;
font-size: 12px;
flex-direction: column;
/* border: var(--xy-node-border-default);
color: var(--xy-node-color, var(--xy-node-color-default)); */
pointer-events: all;
user-select: none;
}
.react-flow__node.selectable:focus {
box-shadow: 0px 0px 0px 4px var(--xy-theme-color-focus);
border-color: #d9d9d9;
}
.react-flow__node.selectable:focus:active {
box-shadow: var(--xy-node-boxshadow-default);
}
.react-flow__node.selectable:hover,
.react-flow__node.draggable:hover {
border-color: var(--xy-theme-hover);
}
.react-flow__node.selectable.selected {
border-color: var(--xy-theme-selected);
box-shadow: var(--xy-node-boxshadow-default);
}
.react-flow__node-group {
background-color: rgba(207, 182, 255, 0.4);
border-color: #9E86ED;
}
.react-flow__edge.selectable:hover .react-flow__edge-path,
.react-flow__edge.selectable.selected .react-flow__edge-path {
stroke: var(--xy-theme-edge-hover);
}
.react-flow__handle {
width: 15px;
height: 15px;
}
.react-flow__handle.connectionindicator:hover {
pointer-events: all;
border-color: var(--xy-theme-edge-hover);
background-color: white;
}
.react-flow__handle.connectionindicator:focus ,
.react-flow__handle.connectingfrom,
.react-flow__handle.connectingto {
border-color: var(--xy-theme-edge-hover);
}
.react-flow__node-resizer {
border-radius: 0;
border: none;
}
.react-flow__resize-control.handle {
background-color: #ffffff;
border-color: #9E86ED;
border-radius: 0;
width: 5px;
height: 5px;
}

View file

@ -1,36 +0,0 @@
html,
body {
margin: 0;
font-family: sans-serif;
box-sizing: border-box;
min-height: 100%;
height: 100%;
}
#root{
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
#app {
width: 100%;
height: 100%;
margin: 0;
padding: 0;
}
button{
background-color: #006666;
border: none;
padding: 6px 10px;
border-radius: 8px;
color: white;
font-size: 14px;
cursor: pointer;
&:hover{
background-color: #255252;
}
}

View file

@ -1,137 +0,0 @@
import React, {useState} from 'react';
import styled, { css } from 'styled-components';
import { DeviceUser } from "../../../store/useDeviceStore";
import {buttonNames} from "../../../const/buttons/names";
import {ModalMap} from "../../modalwindows/windowsType";
import {IconComponent} from "../../../const/buttons/icons";
import {useModalContext} from "../../../store";
interface DeviceCardProps {
device: DeviceUser;
status: 'online' | 'offline';
}
const CardWrapper = styled.div<{ status: 'online' | 'offline' }>`
background: white;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
overflow: hidden;
transition: all 0.3s ease;
cursor: pointer;
margin-bottom: 16px;
&:hover {
transform: translateY(-2px);
box-shadow: 0 6px 16px rgba(0, 0, 0, 0.15);
}
`;
const CardHeader = styled.div<{ status: 'online' | 'offline' }>`
background: ${({ status, theme }) =>
status === 'online' ? theme.successColor : theme.offlineColor};
color: white;
padding: 16px;
font-size: 18px;
font-weight: 500;
display: flex;
justify-content: space-between;
align-items: center;
`;
const CardContent = styled.div<{ isExpanded: boolean }>`
max-height: 0;
opacity: 0;
padding: 0 16px;
transition: all 0.3s ease;
color: #333;
font-size: 14px;
display: grid;
gap: 12px;
${({ isExpanded }) => isExpanded && css`
max-height: 500px;
opacity: 1;
padding: 16px;
`}
`;
const InfoRow = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
`;
const Label = styled.span`
font-weight: 500;
color: #666;
`;
const Value = styled.span`
color: #333;
`;
const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
const [isExpanded, setIsExpanded] = useState(false);
const toggleExpanded = () => setIsExpanded(!isExpanded);
const { openModal } = useModalContext();
const openModalNote = (e: React.MouseEvent) => {
e.stopPropagation();
openModal(ModalMap['6'], {
address: device.mac,
currentNote: device.note || ''
});
};
return (
<CardWrapper status={status} onClick={toggleExpanded}>
<CardHeader status={status}>
<p>
MAC: {device.mac}
</p>
<span onClick={openModalNote}>
<IconComponent name={buttonNames[16]} width="28" height="28" setColor={'white'} />
</span>
</CardHeader>
<CardContent isExpanded={isExpanded}>
<InfoRow>
<Label>Имя:</Label>
<Value>{device.hostname || device.ip}</Value>
</InfoRow>
<InfoRow>
<Label>Производитель:</Label>
<Value>{device.vendor || 'Неизвестно'}</Value>
</InfoRow>
<InfoRow>
<Label>IP адрес:</Label>
<Value>{device.ip}</Value>
</InfoRow>
{device.open_ports?.length > 0 && (
<InfoRow>
<Label>Открытые порты:</Label>
<Value>{device.open_ports.join(', ')}</Value>
</InfoRow>
)}
<InfoRow>
<Label>Последняя активность:</Label>
<Value>{device.last_seen}</Value>
</InfoRow>
<InfoRow>
<Label>Заметка:</Label>
<Value>{device.note || '-'}</Value>
</InfoRow>
</CardContent>
</CardWrapper>
);
};
export default DeviceCard;

View file

@ -1,130 +0,0 @@
import React, {useEffect, useRef, useState} from 'react';
import styled from "styled-components";
import {buttonNames} from "../../../const/buttons/names";
import {IconComponent} from "../../../const/buttons/icons";
type Option = {
value: number,
label: string,
}
interface DropdownTypeProps {
options: Option[];
label?: string;
onChange?: (selectedItem: Option) => void;
value: Option | null;
}
const DropdownWrapper = styled.div`
position: absolute;
top: 18px;
right:20px;
display: inline-block;
background-color: ${({theme}) => theme.background};
padding: 12px 16px;
border-radius: 12px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
z-index: 10;
`
const Label = styled.p`
color: ${({theme}) => theme.textColor};
font-size: 16px;
`
const Dropdown = styled.div`
background-color: ${({theme}) => theme.primary};
border-radius: 15px;
overflow: hidden;
transition: all .3s ease;
cursor: pointer;
z-index: 10;
margin-top: 6px;
`
const DropdownHeader = styled.div`
display: flex;
justify-content: space-between;
gap: 14px;
padding: 12px 24px;
font-size: 16px;
align-items: center;
height: 100%;
min-width: 200px;
`
const DropdownContent = styled.div<{$isOpen: boolean}>`
max-height: ${({$isOpen}) => $isOpen ? '500px' : '0'};
opacity: ${({$isOpen}) => $isOpen ? 1 : 0};
transition: all .3s ease;
display: flex;
flex-direction: column;
overflow: hidden;
`
const DropdownRow = styled.button`
padding: 12px 24px;
background-color: transparent;
text-align: start;
font-size: 16px;
border: none;
width: 100%;
color: ${({theme}) => theme.textColor};
transition: all .3s ease;
cursor: pointer;
&:hover {
background-color: ${({theme}) => theme.background};
}
`
const DropdownArrow = styled.div<{ $isOpen: boolean }>`
transition: transform 0.3s ease;
transform: rotate(${({ $isOpen }) => ($isOpen ? "90deg" : "0deg")});
`;
const DropdownType = ({options, value, onChange, label}: DropdownTypeProps) => {
const [isOpen, setIsOpen] = useState(false);
const dropdownRef = useRef<HTMLDivElement>(null);
function toggleOpen() {
setIsOpen((prev) => !prev);
}
function handleSelect(option: Option) {
onChange?.(option);
setIsOpen(false);
}
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
setIsOpen(false);
}
}
document.addEventListener("mousedown", handleClickOutside);
return () => document.removeEventListener("mousedown", handleClickOutside);
}, []);
return (
<DropdownWrapper ref={dropdownRef}>
<Label>{label}</Label>
<Dropdown>
<DropdownHeader onClick={toggleOpen}>
<p>{value?.label || 'Select an option'}</p>
<DropdownArrow $isOpen={isOpen}>
<IconComponent name={buttonNames[6]} />
</DropdownArrow>
</DropdownHeader>
<DropdownContent $isOpen={isOpen}>
{options.map((option, i) => (
<DropdownRow key={i} onClick={() => handleSelect(option)}>
{option.label}
</DropdownRow>
))}
</DropdownContent>
</Dropdown>
</DropdownWrapper>
);
};
export default DropdownType;

View file

@ -1,65 +0,0 @@
import React from 'react';
import styled from "styled-components";
import {buttonNames} from "../../const/buttons/names";
import {IconComponent} from "../../const/buttons/icons";
const PaginateWrapper = styled.div`
width: 100%;
margin-top: 24px;
display: flex;
justify-content: center;
align-items: center;
gap: 10px;
`
const Page = styled.button`
width: 36px;
height: 36px;
background: ${(props => props.theme.primary)};
color: ${(props => props.theme.textColor)};
border: 1px solid ${(props => props.theme.borderColor)};
font-size: 16px;
cursor: pointer;
border-radius: 4px;
transition: .4s ease;
&:hover {
background: ${(props => props.theme.background)};
}
`
interface PaginationProps {
currentPage: number;
totalPages: number;
onPrevPage: () => void;
onNextPage: () => void;
}
const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage}: PaginationProps) => {
return (
<PaginateWrapper>
<Page
onClick={onPrevPage}
disabled={currentPage === 1}
>
<IconComponent
width="20"
height="20"
name={buttonNames[17]}
/>
</Page>
<Page
onClick={onNextPage}
disabled={currentPage === totalPages}
>
<IconComponent
width="20"
height="20"
name={buttonNames[6]}
/>
</Page>
</PaginateWrapper>
);
};
export default Pagination;

View file

@ -1,29 +0,0 @@
import styled, { keyframes } from "styled-components";
const blink = keyframes`
0%, 100% { opacity: 0.3; }
50% { opacity: 1; }
`;
interface DotProps {
$delay: number;
}
const Dot = styled.span<DotProps>`
animation: ${blink} 1s infinite;
animation-delay: ${({ $delay }) => $delay}s;
margin: 0;
`;
const Wrapper = styled.div`
display: flex;
gap: 4px;
`;
export const AnimDot = () => (
<Wrapper>
<Dot $delay={0}>.</Dot>
<Dot $delay={0.2}>.</Dot>
<Dot $delay={0.4}>.</Dot>
</Wrapper>
);

View file

@ -1,75 +0,0 @@
import styled from "styled-components";
import { useTheme } from "styled-components";
import { IconComponent } from "../../../const/buttons/icons";
const Button = styled.button.withConfig({
shouldForwardProp: (prop) => !['color', '$hoverсolor', 'size', '$iconpos'].includes(prop),
})<{ color: string; size?: string, $hoverсolor: string, $iconpos: string }>`
background-color: ${(props) => props.color};
border: none;
padding: ${(props) =>
props.size === "small"
? "10px 28px"
: props.size === "big"
? "10px 20px"
: "12px 20px"}; // По умолчанию
border-radius: 8px;
color: white;
font-size: ${(props) =>
props.size === "small"
? "14px"
: props.size === "big"
? "16px"
: "14px"}; // По умолчанию
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
flex-direction: ${(props) =>
props.$iconpos === 'right'
? 'row-reverse'
: 'row'};
gap: 10px;
transition: all .3s ease;
&:hover{
background-color: ${(props) => props.$hoverсolor};
}
`;
type Props = {
color?: string;
hoverColor?: string
size?: "small" | "big";
title: string
onClick?: () => void;
type?: "button" | "submit" | "reset" | undefined
icon?: string
iconPosition?: 'left' | 'right'
iconSize?: {
width: string,
height: string
}
style?: any
};
export const MainButton = ({ color, size, title, hoverColor, type, icon, iconPosition, iconSize, onClick, style }: Props) => {
const theme = useTheme();
const defaultColor: string = theme.accent;
const defaultHoverColor: string = theme.accentHover
return (
<Button
className="unselectable"
color={color || defaultColor} size={size}
$hoverсolor={hoverColor || defaultHoverColor}
onClick={onClick}
type={type}
$iconpos={iconPosition || 'left'}
style={style}
>
{icon && <IconComponent name={icon} width={iconSize?.width} height={iconSize?.height} setColor="white"/> }
{title}
</Button>
);
};

View file

@ -1,45 +0,0 @@
import { MainButton } from "./mainButton";
import styled from "styled-components";
import { buttonNames } from "../../../const/buttons/names";
import { IconComponent as Icon } from '../../../const/buttons/icons';
import { useWorkSpaceStore } from "../../../store";
type Props = {
size: "small" | "big";
};
const Container = styled.div<{ size: "small" | "big" }>`
${(props) => props.size === "small" && "width: 100%;"}
`;
const Button = styled.div`
width: 100%;
cursor: pointer;
color: ${(props) => props.theme.textColor};
`;
const IconContainer = styled.div`
padding: 10px;
`;
export const QuitButton = ({ size }: Props) => {
const handleExit = () => {
window.electron.quitApp();
};
const { setWorkModule } = useWorkSpaceStore()
return (
<Container size={size} onClick={()=>setWorkModule('helloWindow')}>
{size === "big" ? (
<MainButton title="Выйти" size="big" />
) : (
<Button>
<IconContainer>
<Icon name={buttonNames[12]} width="30" height="30"/>
</IconContainer>
</Button>
)}
</Container>
);
};

View file

@ -1,26 +0,0 @@
import styled, { useTheme } from "styled-components";
import { IconComponent as Icon } from '../../../const/theme/icons';
import { ThemName } from "../../../const/theme/names";
const Button = styled.div`
width: 100%;
cursor: pointer;
`
const IconContainer = styled.div`
padding: 10px;
color: ${props => props.theme.text};
`
export default function ThemeButton() {
const theme = useTheme();
const name = localStorage.getItem('theme') as ThemName
return (
<Button onClick={theme.toggleTheme}>
<IconContainer>
<Icon name={name} width="30" height="30"/>
</IconContainer>
</Button>
);
}

View file

@ -1,65 +0,0 @@
import React from 'react';
import styled from 'styled-components';
type Props = {
placeholderText: string;
id: string;
value?: string;
onChange?: (value: string) => void;
onFileChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
type?: string;
multiple?: boolean;
style?: React.CSSProperties;
search?: boolean;
onFocus?: any
onBlur?: any
$size?: string
}
const InputContainer = styled.div`
width: 100%;
display: flex;
flex: 3;
`;
const StyledInput = styled.input`
padding: 8px 10px;
color: black;
border-radius: 8px;
border: 1px solid #A1A1A1;
background-color: transparent;
&:focus{
border: 1px solid #adadad;
}
width: 100%;
font-size: 18px;
color: ${props=>props.theme.textColor}
`;
const Input = React.forwardRef<HTMLInputElement, Props>(({ placeholderText, id, value, onChange, onFileChange, type = "text", multiple, style, onFocus, onBlur }, ref) => {
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target;
if (onChange) {
onChange(value);
}
};
return (
<InputContainer>
<StyledInput
type={type}
placeholder={placeholderText}
value={value}
multiple={multiple}
onChange={type === "file" && onFileChange ? onFileChange : handleInputChange}
ref={ref}
style={style}
onFocus={onFocus}
onBlur={onBlur}
/>
</InputContainer>
);
});
export default Input;

View file

@ -1,101 +0,0 @@
type Props = {
loaderText: string; //Тип передаваемых данных loaderText - string
}
import styled, { keyframes } from 'styled-components'
import { AnimDot } from './animDot';
//Константа которая устанавливает кейфвреймы для вращения
const spinAnimation = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
//Стиль экрана загрузки
export const LoaderContainer = styled.div`
top: 0px;
z-index:998;
background-color: rgba(0,0,0, 0.7);
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow-y: hidden;
`
//Стиль блока с элементами загрузки
export const Loaderblock = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
p{
font-size: 24px;
color: white;
}
`
//Стиль для блока с кольцом загрузки
export const LoadingSpinner = styled.div`
position: relative;
width: 65px;
height: 65px;
`
//Стиль для кольца загрузки
export const LoadingRing = styled.div`
position: absolute;
top: 0;
left: 0;
width: 65px;
height: 65px;
border-radius: 50%;
border: 12px solid white;
`
//Стиль для части кольца загрузки, которая будет вращаться
export const LoadingPart = styled.div`
position: absolute;
top: 0;
left: 0;
width: 65px;
height: 65px;
border-radius: 50%;
border: 12px solid transparent;
border-top: 12px solid ${props => props.theme.accent};
// Добавление аниманции и использование кейфремов
animation: ${spinAnimation} 1s linear infinite;
`
const Text = styled.div`
display: flex;
gap: 3px;
font-size: 24px;
color: white;
margin-top: 30px;
margin-left: 25px;
`
export default function Loader({ loaderText }: Props) {
return (
<Loaderblock>
<LoadingSpinner>
<LoadingRing />
<LoadingPart />
</LoadingSpinner>
{/* Текст экрана загрузки устанавливается с помощью передаваемых данных loaderText */}
<Text>{loaderText}<AnimDot/></Text>
</Loaderblock>
)
}

View file

@ -1,103 +0,0 @@
type Props = {
loaderText: string; //Тип передаваемых данных loaderText - string
}
import styled, { keyframes } from 'styled-components'
import { AnimDot } from './animDot';
import { ModalWindow } from '../modalwindows/style';
//Константа которая устанавливает кейфвреймы для вращения
const spinAnimation = keyframes`
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
`
//Стиль экрана загрузки
export const LoaderContainer = styled.div`
top: 0px;
z-index:998;
background-color: rgba(0,0,0, 0.7);
position: fixed;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
overflow-y: hidden;
`
//Стиль блока с элементами загрузки
export const Loaderblock = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 15px;
p{
font-size: 24px;
color: white;
}
`
//Стиль для блока с кольцом загрузки
export const LoadingSpinner = styled.div`
position: relative;
width: 65px;
height: 65px;
`
//Стиль для кольца загрузки
export const LoadingRing = styled.div`
position: absolute;
top: 0;
left: 0;
width: 65px;
height: 65px;
border-radius: 50%;
border: 12px solid white;
`
//Стиль для части кольца загрузки, которая будет вращаться
export const LoadingPart = styled.div`
position: absolute;
top: 0;
left: 0;
width: 65px;
height: 65px;
border-radius: 50%;
border: 12px solid transparent;
border-top: 12px solid ${props => props.theme.accent};
// Добавление аниманции и использование кейфремов
animation: ${spinAnimation} 1s linear infinite;
`
const Text = styled.div`
display: flex;
gap: 3px;
font-size: 24px;
color: white;
margin-top: 30px;
margin-left: 25px;
`
export default function RouterLoader({ loaderText }: Props) {
return (
<ModalWindow>
<Loaderblock>
<LoadingSpinner>
<LoadingRing />
<LoadingPart />
</LoadingSpinner>
{/* Текст экрана загрузки устанавливается с помощью передаваемых данных loaderText */}
<Text>{loaderText}<AnimDot/></Text>
</Loaderblock>
</ModalWindow>
)
}

View file

@ -1,63 +0,0 @@
import React, { useEffect, useRef } from 'react';
interface SignalSVGProps {
signal: number; // Принимаем signal как пропс
color: string;
size: string;
}
const SignalSVG: React.FC<SignalSVGProps> = ({ signal, color, size }) => {
const svgRef = useRef<SVGSVGElement | null>(null);
useEffect(() => {
// Устанавливаем цвета в зависимости от signal
const colors = {
'--st1-color': signal >= 1 ? color : 'black',
'--st2-color': signal >= 2 ? color : 'black',
'--st3-color': signal >= 3 ? color : 'black',
'--st4-color': signal >= 4 ? color : 'black',
'--st5-color': signal === 5 ? color : 'black',
};
// Применяем стили к SVG
if (svgRef.current) {
Object.entries(colors).forEach(([key, value]) => {
svgRef.current?.style.setProperty(key, value);
});
}
}, [signal]);
return (
<svg
ref={svgRef}
version="1.1"
xmlns="http://www.w3.org/2000/svg"
xmlnsXlink="http://www.w3.org/1999/xlink"
x="0px"
y="0px"
viewBox="0 0 296 235"
xmlSpace="preserve"
width={size} // Фиксированная ширина
height={size}
>
<style type="text/css">
{`
.st1 { fill-rule: evenodd; clip-rule: evenodd; fill: var(--st1-color, white); }
.st2 { fill-rule: evenodd; clip-rule: evenodd; fill: var(--st2-color, white); }
.st3 { fill-rule: evenodd; clip-rule: evenodd; fill: var(--st3-color, white); }
.st4 { fill-rule: evenodd; clip-rule: evenodd; fill: var(--st4-color, white); }
.st5 { fill-rule: evenodd; clip-rule: evenodd; fill: var(--st5-color, white); }
`}
</style>
<g>
<polygon className="st1" points="47,138 47,188 0,188 0,164" />
<polygon className="st2" points="109,104 109,188 62,188 62,130" />
<polygon className="st3" points="171,69 171,188 124,188 124,95" />
<polygon className="st4" points="234,35 234,188 187,188 187,61" />
<polygon className="st5" points="296,0 296,188 249,188 249,26" />
</g>
</svg>
);
};
export default SignalSVG;

View file

@ -1,137 +0,0 @@
import React, {useState} from 'react';
import { DeviceUser } from '../../../store/useDeviceStore';
interface TableScannerProps {
devices: DeviceUser[];
}
import styled from 'styled-components';
import {IconComponent} from "../../../const/buttons/icons";
import {buttonNames} from "../../../const/buttons/names";
import {ModalMap} from "../../modalwindows/windowsType";
import {useModalContext} from "../../../store";
import Pagination from "../Pagination";
const TableContainer = styled.div`
width: 100%;
margin: 24px 0;
`;
const StyledTable = styled.table`
width: 100%;
border-collapse: separate;
border-spacing: 0;
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08);
background: ${({ theme }) => theme.primary};
`;
const TableHeaderCell = styled.th<{ sortable?: boolean }>`
padding: 16px 24px;
text-align: left;
font-weight: 600;
font-size: 14px;
color: ${({ theme }) => theme.textColor};
background: ${({ theme }) => theme.primary};
border-bottom: 1px solid ${({ theme }) => theme.borderColor};
transition: all 0.2s ease;
`;
const TableRow = styled.tr<{ clickable?: boolean }>`
transition: all 0.2s ease;
&:hover {
background-color: ${({ theme }) => theme.background};
}
`;
const TableCell = styled.td`
padding: 16px 24px;
font-size: 14px;
color: ${({ theme }) => theme.text};
border-bottom: 1px solid ${({ theme }) => theme.borderColor};
transition: all 0.2s ease;
button{
background-color: transparent;
border: none;
cursor: pointer;
}
`;
const StatusBadge = styled.span<{ status:string }>`
width: 10px;
height: 30px;
background-color: ${({theme, status}) => (status==='online' ? theme.successColor : theme.offlineColor)};
display: inline-block;
border-radius:4px;
`
const TableScanner = ({devices}:TableScannerProps) => {
const { openModal } = useModalContext();
const openModalNote = (e: React.MouseEvent, device:DeviceUser ) => {
e.stopPropagation();
openModal(ModalMap['6'], {
address: device.mac,
currentNote: device.note || ''
});
};
// Пагинация
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage] = useState(10);
const indexOfLastDevice = currentPage * devicesPerPage;
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
const currentDevices = devices.slice(indexOfFirstDevice, indexOfLastDevice);
const totalPages = Math.ceil(devices.length / devicesPerPage);
const handlePrevPage = () => {
if(currentPage > 1) setCurrentPage(currentPage - 1);
}
const handleNextPage = () => {
if(currentPage < totalPages) setCurrentPage(currentPage + 1);
}
return (
<TableContainer>
<StyledTable>
<thead>
<tr>
<TableHeaderCell></TableHeaderCell>
<TableHeaderCell>Имя</TableHeaderCell>
<TableHeaderCell>Производитель</TableHeaderCell>
<TableHeaderCell>IP адрес</TableHeaderCell>
<TableHeaderCell>Открытые порты</TableHeaderCell>
<TableHeaderCell>Последняя активность</TableHeaderCell>
<TableHeaderCell>Заметка</TableHeaderCell>
<TableHeaderCell></TableHeaderCell>
</tr>
</thead>
<tbody>
{currentDevices.map((item, i) => (
<TableRow key={i}>
<TableCell><StatusBadge status={item.status}></StatusBadge></TableCell>
<TableCell>{item.hostname || item.ip}</TableCell>
<TableCell>{item.vendor}</TableCell>
<TableCell>{item.ip}</TableCell>
<TableCell>{item.open_ports.join(', ') || '-'}</TableCell>
<TableCell>{item.last_seen}</TableCell>
<TableCell>{item.note || '-'}</TableCell>
<TableCell>
<button onClick={(e)=>openModalNote(e, item)}>
<IconComponent name={buttonNames[16]} width="26" height="26" />
</button>
</TableCell>
</TableRow>
))}
</tbody>
</StyledTable>
<Pagination
currentPage={currentPage}
totalPages={totalPages}
onPrevPage={handlePrevPage}
onNextPage={handleNextPage}
/>
</TableContainer>
);
};
export default TableScanner;

View file

@ -1,38 +0,0 @@
import React from 'react';
import { Handle, Position } from '@xyflow/react';
import { Circle } from './style';
interface CustomNodeProps {
data: {
label: string;
style:{
width: any;
height: any;
borderType: any;
borderColor: any;
}
};
}
export const CircleNode = ({ data }: CustomNodeProps) => {
return (
<Circle width={data.style.width} height={data.style.height} bordertype={data.style.borderType} bordercolor={data.style.borderColor}>{/* */}
{/* <div style={{transform: 'rotate(-45deg)', width: '100%', height: '100%'}}> */}
{data.label}
{/* Узлы для подключения */}
<Handle type="source" position={Position.Top} id="source-top" style={{ top: 0, }} />
<Handle type="source" position={Position.Right} id="source-right" style={{ right: 0 }} />
<Handle type="source" position={Position.Bottom} id="source-bottom" style={{ bottom: 0 }} />
<Handle type="source" position={Position.Left} id="source-left" style={{ left: 0 }} />
{/* Для входящих подключений */}
<Handle type="target" position={Position.Top} id="target-top" style={{ top: 0 }} />
<Handle type="target" position={Position.Right} id="target-right" style={{ right: 0 }} />
<Handle type="target" position={Position.Bottom} id="target-bottom" style={{ bottom: 0 }} />
<Handle type="target" position={Position.Left} id="target-left" style={{ left: 0 }} />
{/* </div> */}
</Circle>
);
};

View file

@ -1,20 +0,0 @@
import styled from "styled-components";
type Props={
width: string,
height: string,
bordertype: string,
bordercolor: string
}
export const Circle = styled.div<Props>`
background-color: white;
border-radius: 100%;
display: flex;
align-items: center;
justify-content: center;
border: 2px ${(props) => props.bordertype} ${(props) => props.bordercolor};
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
`

View file

@ -1,37 +0,0 @@
import React from 'react';
import { Handle, Position } from '@xyflow/react';
import { Default } from './style';
interface CustomNodeProps {
data: {
label: string;
style:{
width: any;
height: any;
borderType: any;
borderColor: any;
}
};
}
export const DefaultNode = ({ data }: CustomNodeProps) => {
return (
<Default width={data.style.width} height={data.style.height} bordertype={data.style.borderType} bordercolor={data.style.borderColor}>
{data.label}
{/* Узлы для подключения */}
{/* <Handle type="source" position={Position.Top} id="source-top" style={{ top: 0, }} />
<Handle type="source" position={Position.Right} id="source-right" style={{ right: 0 }} />
<Handle type="source" position={Position.Bottom} id="source-bottom" style={{ bottom: 0 }} />
<Handle type="source" position={Position.Left} id="source-left" style={{ left: 0 }} /> */}
{/* Для входящих подключений */}
{/* <Handle type="target" position={Position.Top} id="target-top" style={{ top: 0 }} /> */}
<Handle type="target" position={Position.Right} id="target-right" style={{ right: 0 }} />
{/* <Handle type="target" position={Position.Bottom} id="target-bottom" style={{ bottom: 0 }} />
<Handle type="target" position={Position.Left} id="target-left" style={{ left: 0 }} /> */}
{/* </div> */}
</Default>
);
};

View file

@ -1,20 +0,0 @@
import styled from "styled-components";
type Props={
width: string,
height: string,
bordertype: string,
bordercolor: string
}
export const Default = styled.div<Props>`
background-color: white;
border: 2px ${(props) => props.bordertype} ${(props) => props.bordercolor};
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
visibility: visible;
`

View file

@ -1,45 +0,0 @@
import React from 'react';
import { Handle, Position } from '@xyflow/react';
import { Diamond, InnerDiamond, Border } from './style';
interface CustomNodeProps {
data: {
label: string;
style:{
width: any;
height: any;
borderType: any;
borderColor: any;
}
};
}
export const DiamondNode = ({ data }: CustomNodeProps) => {
const baseSize = Math.max(data.style.width, data.style.height);
return (
<Diamond width={data.style.width} height={data.style.height}>{/* */}
<Border width={data.style.width} height={data.style.height} bordercolor={data.style.borderColor}>
<InnerDiamond width={data.style.width} height={data.style.height}>
{data.label}
</InnerDiamond>
</Border>
<div style={{ width: '100%', height: '100%' }}>
{/* Узлы для подключения */}
<Handle type="source" position={Position.Top} id="source-top" />
<Handle type="source" position={Position.Right} id="source-right" />
<Handle type="source" position={Position.Bottom} id="source-bottom" />
<Handle type="source" position={Position.Left} id="source-left" />
{/* Для входящих подключений */}
<Handle type="target" position={Position.Top} id="target-top" />
<Handle type="target" position={Position.Right} id="target-right" />
<Handle type="target" position={Position.Bottom} id="target-bottom" />
<Handle type="target" position={Position.Left} id="target-left" />
</div>
</Diamond >
);
};

View file

@ -1,32 +0,0 @@
import styled from "styled-components";
interface StyledDefaultProps {
width: any;
height: any;
}
export const Diamond = styled.div<StyledDefaultProps>`
position: relative;
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
`;
export const InnerDiamond = styled.div<StyledDefaultProps>`
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
width: ${(props) => props.width - 6}px;
height: ${(props) => props.height - 4}px;
background-color: white;
display: flex;
align-items: center;
justify-content: center;
`
export const Border = styled.div<{ width: number; height: number, bordercolor: any }>`
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
background-color: ${(props) => props.bordercolor};
display: flex;
align-items: center;
justify-content: center;
`;

View file

@ -1,5 +0,0 @@
export {DefaultNode} from './default/defaultNode'
export {PillNode} from './pill/pillNode'
export {ParalelorgamNode} from './paralelogram/paralelogramNode'
export {CircleNode} from './circle/circleNode'
export {DiamondNode} from './diamond/diamondNode'

View file

@ -1,35 +0,0 @@
import React from 'react';
import { Handle, Position } from '@xyflow/react';
import { Paralelorgam } from './style';
interface CustomNodeProps {
data: {
label: string;
style:{
width: any;
height: any;
borderType: any;
borderColor: any;
}
};
}
export const ParalelorgamNode = ({ data }: CustomNodeProps) => {
return (
<Paralelorgam width={data.style.width} height={data.style.height} bordertype={data.style.borderType} bordercolor={data.style.borderColor}>
{data.label}
{/* Узлы для подключения */}
<Handle type="source" position={Position.Top} id="source-top"/>
<Handle type="source" position={Position.Right} id="source-right"/>
<Handle type="source" position={Position.Bottom} id="source-bottom"/>
<Handle type="source" position={Position.Left} id="source-left"/>
{/* Для входящих подключений */}
<Handle type="target" position={Position.Top} id="target-top"/>
<Handle type="target" position={Position.Right} id="target-right"/>
<Handle type="target" position={Position.Bottom} id="target-bottom"/>
<Handle type="target" position={Position.Left} id="target-left"/>
</Paralelorgam>
);
};

View file

@ -1,43 +0,0 @@
import styled from "styled-components";
type Props={
width: string,
height: string,
bordertype: string,
bordercolor: string
}
export const Paralelorgam = styled.div<Props>`
transform: skewX(-20deg);
background-color: white;
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
border: 2px ${(props) => props.bordertype} ${(props) => props.bordercolor};
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
.react-flow__handle-top{
transform: skewX(20deg);
top: -5px;
}
.react-flow__handle-bottom{
transform: skewX(20deg);
bottom: -5px;
}
.react-flow__handle-right{
transform: skewX(20deg);
right: -5px;
top: 25px;
}
.react-flow__handle-left{
transform: skewX(20deg);
left: -5px;
top: 25px;
}
`

View file

@ -1,40 +0,0 @@
import React from 'react';
import { Handle, Position } from '@xyflow/react';
import { Pill } from './style';
interface CustomNodeProps {
data: {
label: string;
style:{
width: any;
height: any;
borderType: any;
borderColor: any;
}
};
}
export const PillNode = ({ data }: CustomNodeProps) => {
return (
<Pill width={data.style.width} height={data.style.height} bordertype={data.style.borderType} bordercolor={data.style.borderColor}>{/* */}
{/* <div style={{transform: 'rotate(-45deg)', width: '100%', height: '100%'}}> */}
{data.label}
{/* Узлы для подключения */}
<Handle type="source" position={Position.Top} id="source-top" style={{ top: 0, }} />
<Handle type="source" position={Position.Right} id="source-right" style={{ right: 0 }} />
<Handle type="source" position={Position.Bottom} id="source-bottom" style={{ bottom: 0 }} />
<Handle type="source" position={Position.Left} id="source-left" style={{ left: 0 }} />
{/* Для входящих подключений */}
<Handle type="target" position={Position.Top} id="target-top" style={{ top: 0 }} />
<Handle type="target" position={Position.Right} id="target-right" style={{ right: 0 }} />
<Handle type="target" position={Position.Bottom} id="target-bottom" style={{ bottom: 0 }} />
<Handle type="target" position={Position.Left} id="target-left" style={{ left: 0 }} />
{/* </div> */}
</Pill>
);
};
export default PillNode;

View file

@ -1,19 +0,0 @@
import styled from "styled-components";
type Props={
width: string,
height: string,
bordertype: string,
bordercolor: string
}
export const Pill = styled.div<Props>`
background-color: white;
border-radius: 50px;
display: flex;
align-items: center;
justify-content: center;
border: 2px ${(props) => props.bordertype} ${(props) => props.bordercolor};
width: ${(props) => props.width}px;
height: ${(props) => props.height}px;
`

View file

@ -1,5 +0,0 @@
export {NodeGroupNode} from './networkGroup/nodeGroup'
export {NetWorkNode} from './networkNode/networkNode'
export {IpGroupeNode} from './ipGroup/ipGroup'
export {IpNode} from './ipNode/ipNode'
export {PortNode} from './portNode/portNode'

View file

@ -1,18 +0,0 @@
import { IpGroup } from "./style";
interface CustomNodeProps {
data: {
style: {
width: any,
height: any,
}
};
}
export const IpGroupeNode = ({ data }: CustomNodeProps) => {
return (
<IpGroup $width={data.style.width} $height={Number(data.style.height)} />
);
};

View file

@ -1,12 +0,0 @@
import styled from "styled-components"
type Props={
$width: string,
$height?: any
}
export const IpGroup = styled.div<Props>`
width: ${(props) => props.$width}px;
height: ${(props) => typeof props.$height === 'number' ? `${props.$height}px` : props.$height};
`

View file

@ -1,245 +0,0 @@
import { useState } from 'react';
import { Handle, Position } from '@xyflow/react';
import { Ip } from './style';
import { Row, IpTitle, IpContent } from './style';
import Input from '../../../../UI/inputs/input';
import { useModalContext, usePannableStore } from '../../../../../store';
import { ModalMap } from '../../../../modalwindows/windowsType';
import { validateIPAddress, validateSameSubnet } from '../../../../../utils/nodeController/validator';
import {changeIp} from '../../../../../const/types/types'
import { IconComponent } from "../../../../../const/buttons/icons";
import { buttonNames } from "../../../../../const/buttons/names";
interface IIp {
ipType: string
ipAdress: string,
ipGate: string,
ipDns: string[]
ipVersion: string
}
type StateType = Record<string, string>;
const typeMap: StateType = {
'auto': "DHCP",
'manual': "Static"
}
interface CustomNodeProps {
data: {
ipData: IIp,
interfaceData: {
device: string,
connection: string
},
style: {
width: any,
height: any,
borderType: string,
borderColor: string,
}
route: boolean
}
}
export const IpNode = ({ data }: CustomNodeProps) => {
const { toggleEditingMode } = usePannableStore();
const [isInput, setIsInput] = useState(false)
const { openModal } = useModalContext();
const [ipData, setIpData] = useState<changeIp>(
{
currentIp: data.ipData.ipAdress,
newIp: data.ipData.ipAdress,
currentGate: data.ipData.ipGate,
newGate: data.ipData.ipGate,
currentDns: data.ipData.ipDns,
newDns: data.ipData.ipDns,
device: data.interfaceData.device,
connection: data.interfaceData.connection,
version: data.ipData.ipVersion,
prefix: 0
}
)
const handleFocus = () => {
toggleEditingMode();
};
const handleBlur = () => {
toggleEditingMode();
};
const handleClick = () => {
if (isInput) {
setIsInput(false)
setIpData(prevData => ({
...prevData,
newIp: data.ipData.ipAdress,
newGate: data.ipData.ipGate,
newDns: data.ipData.ipDns
}));
}
else {
setIsInput(true)
}
}
const handleChange = (id: string, value: string) => {
if (id === 'newDns') {
const dnsArray = value.split(',').map(dns => dns.trim());
setIpData(prevData => ({
...prevData,
[id]: dnsArray
}));
} else {
setIpData(prevData => ({
...prevData,
[id]: value
}));
}
};
function getSubnetMask(ipAddress: string, ipVersion: string): number {
if (ipVersion === "6") {
return 64;
}
const firstOctet = parseInt(ipAddress.split(".")[0], 10);
if (firstOctet >= 0 && firstOctet <= 127) {
return 8;
} else if (firstOctet >= 128 && firstOctet <= 191) {
return 16;
} else if (firstOctet >= 192 && firstOctet <= 223) {
return 24;
} else {
return 24;
}
}
const handleConfirm = () => {
if (
!validateIPAddress(ipData.newIp) ||
!validateIPAddress(ipData.newGate) ||
!ipData.newDns.every(dns => validateIPAddress(dns))
) {
alert('Один или несколько IP-адресов некорректны.');
return;
}
if (!validateSameSubnet(ipData.newIp, ipData.newGate, ipData.newDns)) {
alert('Адрес, шлюз и хотя бы один DNS должны находиться в одной подсети.');
return;
}
const calculatedPrefix = getSubnetMask(ipData.newIp, data.ipData.ipVersion);
setIpData(prevData => ({
...prevData,
prefix: calculatedPrefix,
}));
openModal(ModalMap['1'], {
...ipData,
prefix: calculatedPrefix,
});
setIsInput(false)
};
return (
<>
<Ip $width={data.style.width} $height={Number(data.style.height)} $bordertype={data.style.borderType} $bordercolor={data.style.borderColor}>
<IpTitle>
{/* <Row>
<p>Версия IP:</p>
<p>v{data.ipData.ipVersion}</p>
</Row> */}
<Row>
<p>Метод:</p>
<p>{typeMap[data.ipData.ipType]}</p>
</Row>
{isInput ? (
<Row>
<IconComponent name={buttonNames[8]} onClick={handleConfirm} width='18' height='18' setColor='white'/>
<IconComponent name={buttonNames[9]} onClick={handleClick} width='18' height='18' setColor='white'/>
</Row>
) : (
<IconComponent name={buttonNames[7]} onClick={handleClick} width='18' height='18' setColor='white'/>
)}
</IpTitle>
<IpContent>
<Row className='row'>
<p className='row-label'>IPv{data.ipData.ipVersion}-Addr:</p>
{isInput ? (
<Input
type="text"
placeholderText={data.ipData.ipAdress}
id="newIp"
value={ipData.newIp}
onChange={(value) => handleChange('newIp', value)}
onBlur={handleBlur}
onFocus={handleFocus}
style={{fontSize: '12px', padding: '4px 10px'}}
/>
) : (
<p className='row-text'>{data.ipData.ipAdress}</p>
)}
</Row>
<Row className='row'>
<p className='row-label'>IPv{data.ipData.ipVersion}-GW:</p>
{isInput ? (
<Input
type="text"
placeholderText={data.ipData.ipGate}
id="newGate"
value={ipData.newGate}
onChange={(value) => handleChange('newGate', value)}
onBlur={handleBlur}
onFocus={handleFocus}
style={{fontSize: '12px', padding: '4px 10px'}}
/>
) : (
<p className='row-text'>{data.ipData.ipGate}</p>
)}
</Row>
<Row className='row'>
<p className='row-label'>IPv{data.ipData.ipVersion}-DNS:</p>
{isInput ? (
<Input
type="text"
placeholderText={data.ipData.ipDns.join(', ')}
id="newDns"
value={ipData.newDns.join(', ')}
onChange={( value) => handleChange('newDns', value)}
onBlur={handleBlur}
onFocus={handleFocus}
style={{fontSize: '12px', padding: '4px 10px'}}
/>
) : (
<p className='row-text'>{data.ipData.ipDns.join(', ')}</p>
)}
</Row>
{data.route &&
<>
<Handle type="source" position={Position.Right} id={`source-right`} style={{ right: 0, top: 80 }} />
<Handle type="target" position={Position.Right} id={`target-right`} style={{ right: 0, top: 80 }} />
</>
}
</IpContent>
</Ip>
</>
);
};

View file

@ -1,74 +0,0 @@
import styled from "styled-components";
type Props={
$width: string,
$height?: any
$bordertype: string,
$bordercolor: string
}
export const Ip = styled.div<Props>`
background-color: ${(props)=>props.theme.secondary};
display: flex;
flex-direction: column;
width: ${(props) => props.$width}px;
height: ${(props) => typeof props.$height === 'number' ? `${props.$height}px` : props.$height};
// box-shadow: 0px 0px 15px 2px rgba(0, 0, 0, 0.1);
border-radius: 8px;
p{
margin: 0;
}
img{
width: 15px;
height: 15px;
}
`
export const IpTitle = styled.div`
background: ${(props)=>props.theme.accent};
border-top-left-radius: 6px;
border-top-right-radius: 6px;
color: white;
display: flex;
justify-content: space-between;
padding: 10px;
`
export const IpContent = styled.div`
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
padding: 10px;
.row{
height: 24px;
.row-label{
flex: 1;
}
.row-text{
flex: 3;
}
p{
text-wrap: nowrap;
text-align: left;
}
}
`
export const Row = styled.div`
display: flex;
align-items: center;
gap: 5px;
p{
margin: 0;
}
`

View file

@ -1,64 +0,0 @@
import { useState } from "react";
import { NodeGroup, Row, CollapseButton } from "./style";
import { useGroupStore } from "../../../../../store";
import { MainButton } from "../../../../UI/buttons/mainButton";
import { IconComponent } from "../../../../../const/buttons/icons";
import { buttonNames } from "../../../../../const/buttons/names";
interface CustomNodeProps {
data: {
id: string;
label: string;
height: any
style: {
width: any;
height: any;
borderType: any;
borderColor: any;
};
};
}
export const NodeGroupNode = ({ data }: CustomNodeProps) => {
const [click, setClick] = useState(false);
const { groups, setGroups } = useGroupStore();
const handleClick = () => {
const newClickState = !click;
setClick(newClickState);
const updatedNodes = groups.map((groups) =>
groups.id === data.id ? { ...groups, click: newClickState } : groups
);
if (!groups.some((groups) => groups.id === data.id)) {
updatedNodes.push({ id: data.id, click: newClickState });
}
setGroups(updatedNodes);
};
return (
<NodeGroup
$width={data.style.width}
$height={Number(data.style.height)}
$bordertype={data.style.borderType}
$bordercolor={data.style.borderColor}
>
<Row $bordertype={data.style.borderType} $bordercolor={data.style.borderColor} $width={data.style.width}>
<p>{data.label}</p>
{/* <MainButton title={click ? 'Показать' : 'Скрыть'} size="small" onClick={handleClick}/> */}
<CollapseButton onClick={handleClick}>
{click ?(
'Развернуть'
):(
'Свернуть'
)}
{/* <IconComponent name={click ? buttonNames[5] : buttonNames[6]}/> */}
</CollapseButton>
</Row>
</NodeGroup>
);
};

View file

@ -1,47 +0,0 @@
import styled from "styled-components"
type Props={
$width?: string,
$height?: any
$bordertype?: string,
$bordercolor?: string
$backgroundcolor?: string
}
export const NodeGroup = styled.div<Props>`
border: 2px ${(props) => props.$bordertype} ${(props) => props.$bordercolor};
width: ${(props) => props.$width}px;
height: ${(props) => typeof props.$height === 'number' ? `${props.$height}px` : props.$height};
border-radius: 12px;
display: flex;
align-items: center;
justify-content: center;
visibility: visible;
`
export const Row = styled.div<Props>`
position: absolute;
top: -25px;
left: 10px;
display: flex;
justify-content: space-between;
align-items: center;
width: ${props=>props.$width}px;
gap: 5px;
padding-right: 10px;
p{
margin: 0;
}
`
export const CollapseButton = styled.p`
padding: unset;
background: none;
color: ${(props)=>props.theme.textColor};
margin-right: 15px !important;
&:hover{
background: none;
}
`

View file

@ -1,110 +0,0 @@
import React, { useEffect, useState } from 'react';
import { Handle, Position } from '@xyflow/react';
import { Network, TitleContainer, Device, Status, Content, Row, MAC, LabelColumn } from './style';
import SignalSVG from '../../../../UI/signalSvg';
interface CustomNodeProps {
data: {
networkData: {
device: string
type: string
mac: string
mtu: number
state: string
connection: string
id: string,
strength: string
}
style: {
width: any,
height: any,
borderType: string,
borderColor: string,
backgroundColor: string
}
height: string
visible: true
};
}
export const NetWorkNode = ({ data }: CustomNodeProps) => {
const [signal, setSignal] = useState(1);
useEffect(() => {
const perc = Number(data.networkData.strength);
switch (true) {
case perc < 20:
setSignal(1);
break;
case perc < 40:
setSignal(2);
break;
case perc < 60:
setSignal(3);
break;
case perc < 80:
setSignal(4);
break;
default:
setSignal(5);
break;
}
}, [data.networkData.strength])
return (
<Network $width={data.style.width} $height={Number(data.style.height)} $bordertype={data.style.borderType} $bordercolor={data.style.borderColor}>
<TitleContainer $backgroundcolor={data.style.backgroundColor} {...(!data.visible ? { $height: "100%" } : {})}>
{data.networkData.type == "WIFI" &&
<img src="./assets/images/wifi.svg" alt="" />
}
{data.networkData.type == "ETHERNET" &&
<img src="./assets/images/ethernet.svg" alt="" />
}
{data.networkData.type == "TUN" &&
<img src="./assets/images/vpn.svg" alt="" />
}
<Device>{data.networkData.device}</Device>
{data.networkData.type == 'WIFI' && data.networkData.state === "Подключено" ? (
<Status>
<SignalSVG signal={signal} color={'white'} size={'20px'} />
</Status>
) : (
<div></div>
)
}
</TitleContainer>
{data.visible &&
<Content>
<LabelColumn>
<p className='networkNode-label'>Конфигурация</p>
<MAC>
<Row className='networkNode-row'>
<p>MAC:</p>
<p>{data.networkData.mac}</p>
</Row>
<Row className='networkNode-row'>
<p>MTU:</p>
<p>{data.networkData.mtu}</p>
</Row>
</MAC>
</LabelColumn>
</Content>
}
{data.networkData.connection !== 'unknown' &&
<Handle type="source" position={Position.Left} id={`g0_${data.networkData.id}-source-left`} style={{ left: 0 }} />
}
</Network>
);
};
export default NetWorkNode

View file

@ -1,93 +0,0 @@
import styled from "styled-components";
type Props = {
$width?: string,
$height?: any
$bordertype?: string,
$bordercolor?: string
$backgroundcolor?: string
}
export const Network = styled.div<Props>`
background-color: ${(props) => props.theme.primary};
border: 2px ${(props) => props.$bordertype} ${(props) => props.$bordercolor};
min-width: ${(props) => props.$width}px;
height: ${(props) => typeof props.$height === 'number' ? `${props.$height}px` : props.$height};
border-radius: 8px;
display: flex;
flex-direction: column;
gap: 10px;
visibility: visible;
overflow: hidden;
p{
margin: 0;
}
`
export const TitleContainer = styled.div<Props>`
display: flex;
justify-content: space-between;
align-items: center;
background: ${(props) => props.$backgroundcolor};
color: white;
padding: 15px;
height: ${(props) => props.$height ? props.$height : 'unset'};
img{
width: 20px
}
`
export const Device = styled.p`
font-size: 18px;
`
export const Status = styled.p`
font-size: 14px;
img{
width: 20px;
height: 20px;
}
`
export const Content = styled.div`
display: flex;
flex-direction: column;
gap: 15px;
padding: 15px;
`
export const Row = styled.div`
display: flex;
gap: 5px;
`
export const MAC = styled.div`
display: flex;
justify-content: space-between;
background: ${(props) => props.theme.secondary};
border-radius: 8px;
// box-shadow: 0px 0px 15px 2px rgba(0, 0, 0, 0.1);
width: 100%;
.networkNode-row{
padding: 10px;
}
`
export const LabelColumn = styled.div`
display: flex;
flex-direction: column;
align-items: center;
gap: 5px;
width: 100%;
.networkNode-label{
font-size: 10px;
width: 100%;
text-align: left;
margin-left: 15px;
}
`

View file

@ -1,48 +0,0 @@
import { Handle, Position } from '@xyflow/react';
import { Port, Column } from './style';
interface CustomNodeProps {
data: {
label: string;
port: string
id: string;
style: {
width: any;
height: any;
borderType: any;
borderColor: any;
}
};
}
export const PortNode = ({ data }: CustomNodeProps) => {
return (
<Port $width={data.style.width} $height={Number(data.style.height)} $bordertype={data.style.borderType} $bordercolor={data.style.borderColor}>
<Column>
<p>{data.label}</p>
{data.port &&
<p>{data.port}</p>
}
</Column>
{data.port ? (
<>
<Handle type="target" position={Position.Left} id={`${data.id}_target-left`} style={{ left: 0 }} />
<Handle type="source" position={Position.Left} id={`${data.id}_target-left`} style={{ left: 0 }} />
</>
) : (
<>
<Handle type="target" position={Position.Right} id={`${data.id}_target-right`} style={{ right: 0 }} />
<Handle type="source" position={Position.Right} id={`${data.id}source-right`} style={{ right: 0 }} />
</>
)}
</Port>
);
};

View file

@ -1,30 +0,0 @@
import styled from "styled-components"
type Props={
$width: string,
$height?: any
$bordertype: string,
$bordercolor: string
}
export const Port = styled.div<Props>`
background-color: ${(props)=>props.theme.primary};
border: 2px ${(props) => props.$bordertype} ${(props) => props.$bordercolor};
width: ${(props) => props.$width}px;
height: ${(props) => typeof props.$height === 'number' ? `${props.$height}px` : props.$height};
border-radius: 8px;
display: flex;
align-items: center;
justify-content: center;
visibility: visible;
`
export const Column = styled.div`
display: flex;
flex-direction: column;
gap: 15px;
p{
margin: 0;
}
`

View file

@ -1,11 +0,0 @@
type ColorType = Record<string, string>;
export const NodeColors: ColorType = {
'0': '#c0c0c0',
'1': '#ef6e7d',
'2': '#f19e4c',
'3': '#e9c44c',
'4': '#51ce82',
'5': '#4cd2d0',
'6': '#a085f3',
};

View file

@ -1,16 +0,0 @@
import { DefaultNode, PillNode, ParalelorgamNode, DiamondNode, CircleNode } from '../nodes/baseForm'
import { NodeGroupNode, NetWorkNode, IpGroupeNode, IpNode, PortNode } from '../nodes/networkInterface';
export const nodeTypes = {
custom: DefaultNode,
pill: PillNode,
parallelogram: ParalelorgamNode,
diamond: DiamondNode,
circle: CircleNode,
nodegroup: NodeGroupNode,
portNode: PortNode,
network: NetWorkNode,
ipgroup: IpGroupeNode,
ip: IpNode,
};

View file

@ -1,10 +0,0 @@
import { ModalWindow, ModalContainer } from "./style"
import { modalNames } from "../../const/modals/names"
export const WindowComponent = ()=>{
return(
<ModalWindow>
</ModalWindow>
)
}

View file

@ -1,84 +0,0 @@
// components/modalContainers/NoteDevice.tsx
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
import { MainButton } from "../../UI/buttons/mainButton";
import Input from "../../UI/inputs/input";
import { useState } from "react";
import {NetworkService} from "../../../api/request";
import useNetworkStore from "../../../store/useDeviceStore";
import RouterLoader from "../../UI/routerLoader";
import {useTheme} from "styled-components";
import {useIpStore} from "../../../store";
interface Props {
address: string;
onClose: () => void;
}
export const ConnectNetwork = ({address, onClose}: Props) => {
const [password, setPassword] = useState('');
const theme = useTheme();
const { ipAddress } = useIpStore();
const [error, setError] = useState('');
const [isLoading, setIsLoading] = useState(false);
const handleSave = async () => {
try {
setIsLoading(true);
setError('')
const res = await NetworkService.connectNetwork(ipAddress, password, address);
if(res.status === 200) {
onClose()
}else{
setError(res.data.detail)
}
} catch (error) {
console.error('Error connection:', error);
setError('Ошибка подключения к сети')
}finally {
setIsLoading(false);
}
};
return (
<ModalContainer style={{ height: '230px' }}>
<ModalTitle>
<h3>Подключение к сети</h3>
</ModalTitle>
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<Input
type="password"
placeholderText='Введите пароль...'
value={password}
id='deviceNote'
onChange={(value) => setPassword(value)}
/>
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
{error}
</p>
</ModalContent>
<ButtonContainer>
<MainButton
type="button"
size="small"
title="Отмена"
onClick={onClose}
/>
<MainButton
type="button"
size="small"
color={theme.successColor}
hoverColor={theme.successColor}
title='Подключиться'
onClick={handleSave}
/>
</ButtonContainer>
{isLoading && <RouterLoader loaderText='Попытка подключения к сети'/>}
</ModalContainer>
);
};

View file

@ -1,82 +0,0 @@
// components/modalContainers/NoteDevice.tsx
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
import { MainButton } from "../../UI/buttons/mainButton";
import Input from "../../UI/inputs/input";
import { useState } from "react";
import {useIpStore} from "../../../store";
import {NetworkService} from "../../../api/request";
import useNetworkStore from "../../../store/useDeviceStore";
interface Props {
address: string;
onClose: () => void;
currentNote?: string;
type: string
}
export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
const [note, setNote] = useState(currentNote || '');
const { ipAddress } = useIpStore();
const { devices, setDevices } = useNetworkStore();
const handleSave = async () => {
try {
if (!currentNote && note.trim() !== '') {
await NetworkService.addNote(ipAddress, address, note, type);
}else if (currentNote && note.trim() !== '' && note !== currentNote) {
await NetworkService.editNote(ipAddress, address, note, type);
}else if (currentNote && note.trim() === '') {
await NetworkService.deleteNote(ipAddress, address, type);
}else {
onClose();
return;
}
const updatedDevices = devices.map(device =>
device.mac === address
? { ...device, note: note.trim() }
: device
);
setDevices(updatedDevices);
onClose();
} catch (error) {
console.error('Error saving note:', error);
}
};
return (
<ModalContainer style={{ height: '250px' }}>
<ModalTitle>
<h3>{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}</h3>
</ModalTitle>
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<Input
type="text"
placeholderText='Введите заметку...'
value={note}
id='deviceNote'
onChange={(value) => setNote(value)}
/>
</ModalContent>
<ButtonContainer>
<MainButton
type="button"
size="small"
title="Отмена"
onClick={onClose}
/>
<MainButton
type="button"
size="small"
title='Сохранить'
onClick={handleSave}
/>
</ButtonContainer>
</ModalContainer>
);
};

View file

@ -1,82 +0,0 @@
// components/modalContainers/NoteDevice.tsx
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
import { MainButton } from "../../UI/buttons/mainButton";
import Input from "../../UI/inputs/input";
import { useState } from "react";
import {NetworkService} from "../../../api/request";
import useNetworkStore from "../../../store/useDeviceStore";
import {useIpStore} from "../../../store";
interface Props {
address: string;
onClose: () => void;
currentNote?: string;
type: string
}
export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
const [note, setNote] = useState(currentNote || '');
const { ipAddress } = useIpStore();
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
const handleSave = async () => {
try {
if (!currentNote && note.trim() !== '') {
await NetworkService.addNote(ipAddress, address, note, type);
}else if (currentNote && note.trim() !== '' && note !== currentNote) {
await NetworkService.editNote(ipAddress, address, note, type);
}else if (currentNote && note.trim() === '') {
await NetworkService.deleteNote(ipAddress, address, type);
}else {
onClose();
return;
}
const updatedNetworks = wifiNetworks.map(network =>
network.ssid === address
? { ...network, note: note.trim() }
: { ...network }
);
setWifiNetworks(updatedNetworks);
onClose();
} catch (error) {
console.error('Error saving note:', error);
}
};
return (
<ModalContainer style={{ height: '220px' }}>
<ModalTitle>
<h3>{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}</h3>
</ModalTitle>
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
<Input
type="text"
placeholderText='Введите заметку...'
value={note}
id='deviceNote'
onChange={(value) => setNote(value)}
/>
</ModalContent>
<ButtonContainer>
<MainButton
type="button"
size="small"
title="Отмена"
onClick={onClose}
/>
<MainButton
type="button"
size="small"
title='Сохранить'
onClick={handleSave}
/>
</ButtonContainer>
</ModalContainer>
);
};

View file

@ -1,58 +0,0 @@
import Input from "../../UI/inputs/input"
import { MainButton } from "../../UI/buttons/mainButton";
import { useState } from "react";
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
import { validateIPAddress } from "../../../utils/nodeController/validator";
interface Props {
onSave?: (data: string) => void;
onClose: () => void;
}
export const AddDevice = ({ onSave, onClose }: Props) => {
const [ipAddress, setIpAddress] = useState('')
const [error, setError] = useState(false)
const handleSubmit = () => {
if (!validateIPAddress(ipAddress)) {
setError(true)
return;
}
// e.preventDefault(); // Предотвращаем перезагрузку страницы
if (onSave && ipAddress) {
setError(false)
onSave(ipAddress);
onClose();
}
};
return (
<ModalContainer style={{ height: '230px' }}>
{/* <form onSubmit={handleSubmit}> */}
<ModalTitle>
<h3>Добавление узла</h3>
</ModalTitle>
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
<Input
type="text"
placeholderText='Введите адрес узла...'
id="newIp"
value={ipAddress}
onChange={(value) => setIpAddress(value)}
/>
{error && (
<p style={{ color: 'red', marginTop: '5px' }}>
Некорректный адрес узла
</p>
)}
</ModalContent>
<ButtonContainer>
<MainButton type="button" size="small" title="Отмена" onClick={onClose} />
<MainButton type="submit" size="small" title="Сохранить" onClick={handleSubmit} />
</ButtonContainer>
{/* </form> */}
</ModalContainer>
)
}

View file

@ -1,38 +0,0 @@
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
import { useModalContext } from "../../../store"
import { useIpStore } from "../../../store"
import { MainButton } from "../../UI/buttons/mainButton"
type Props={
message: string
}
export const ConnectionError = ({message}: Props)=>{
const {closeModal} = useModalContext()
const { setIpAddress } = useIpStore();
const close = ()=>{
setIpAddress('localhost')
closeModal()
}
return(
<ModalContainer>
<ModalTitle>
<h3>Соединение прервано</h3>
</ModalTitle>
<ModalContent>
<ModalText>
<h3>Произошёл разрыв с сервером</h3>
<p>Пожалуйста, проверьте доступ к адресу и попробуйте снова.</p>
</ModalText>
</ModalContent>
<ButtonContainer>
<MainButton title="Закрыть" onClick={close} size="small"/>
<MainButton title="Повторить" onClick={closeModal} size="small"/>
</ButtonContainer>
</ModalContainer>
)
}

View file

@ -1,54 +0,0 @@
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
import { useModalContext } from "../../../store"
import { useIpStore } from "../../../store"
import { MainButton } from "../../UI/buttons/mainButton"
import {useState} from "react";
import {NetworkService} from "../../../api/request";
import RouterLoader from "../../UI/routerLoader";
import {useTheme} from "styled-components";
type Props={
address: string,
onClose: () => void
}
export const DisconnectConfirm = ({address, onClose}: Props)=>{
const { ipAddress } = useIpStore();
const theme = useTheme()
const [isLoading, setIsLoading] = useState(false);
const handleDisconnect = async () => {
try {
setIsLoading(true);
const res = await NetworkService.disconnectNetwork(ipAddress, address);
if(res.status === 200) {
onClose()
}
} catch (error) {
console.error('Error connection:', error);
}finally {
setIsLoading(false);
}
};
return(
<ModalContainer>
<ModalTitle>
<h3>Отключение от Wi-Fi сети</h3>
</ModalTitle>
<ModalContent>
<ModalText>
<h3>Подтверждение отключения</h3>
<p>Вы собираетесь отключиться от сети "{address}". Все активные подключения будут прерваны.</p>
</ModalText>
</ModalContent>
<ButtonContainer>
<MainButton title="Отменить" onClick={onClose} size="small"/>
<MainButton title="Отключиться" color={theme.errorColor} hoverColor={theme.errorColor} onClick={handleDisconnect} size="small"/>
</ButtonContainer>
{isLoading && <RouterLoader loaderText='Отключение от сети'/>}
</ModalContainer>
)
}

View file

@ -1,4 +0,0 @@
export {IpConfirm} from './ipConfirm'
export {ConnectionError} from './connectionError'
export {AddDevice} from './addDevice'
export {RightsConfirm} from './rightsConfirm'

View file

@ -1,211 +0,0 @@
import styled from "styled-components";
import Loader from "../../UI/loader";
import { useEffect, useState } from "react";
import { changeIp } from '../../../const/types/types'
import { NetworkService } from '../../../api/request'
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer, ErrorMessage } from "../style"
import { useIpStore } from "../../../store";
import { MainButton } from "../../UI/buttons/mainButton";
import { useAdminStore } from "../../../store/adminStore";
import { useDevicesStore } from "../../../store";
type Props = {
ipData: changeIp
onClose: () => void;
smoothClose: () => void;
}
const Information = styled.div`
display: flex;
flex-direction: column;
gap: 15px;
`
const Info = styled.div`
display: flex;
gap: 10px;
`
const DataContainer = styled.div`
display: flex;
gap: 35px;
`
const CurrentData = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
`
const NewData = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
`
export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
const [loader, setLoader] = useState(true)
const [newIP, setNewIp] = useState(null)
const [currentIp, setCurrentIP] = useState({})
const [checkStatus, setcheckStatus] = useState(false)
const [message, setMessgae] = useState('')
const { ipAddress, setIpAddress } = useIpStore();
const {isAdmin} = useAdminStore()
const { devices, addDevice, deleteDevice } = useDevicesStore();
useEffect(() => {
if (ipData && isAdmin) {
const data = {
"connection_id": ipData.connection,
"ipv4_updates": {
"ipv4.method": "manual",
"ipv4.address-data": [
{
"address": ipData.newIp,
"prefix": ipData.prefix
}
],
"ipv4.gateway": ipData.newGate,
"ipv4.dns-data": ipData.newDns
}
};
const oldData = {
"connection_id": ipData.connection,
"ipv4_updates": {
"ipv4.method": "manual",
"ipv4.address-data": [
{
"address": ipData.currentIp,
"prefix": ipData.prefix
}
],
"ipv4.gateway": ipData.currentGate,
"ipv4.dns-data": ipData.currentDns
}
}
setCurrentIP(oldData)
checkIp(data);
}
}, [ipData, isAdmin]);
const checkIp = async (data: any) => {
try {
const result = await NetworkService.checkIp(ipAddress, data);
setcheckStatus(result.data.available)
setMessgae(result.data.message)
if (result.data.available) {
setNewIp(data)
}
} catch (err) {
console.error('Error:', err);
} finally {
setLoader(false);
}
};
const saveIp = async (data: any) => {
try {
const result = await NetworkService.saveIp(ipAddress, data);
if (result.status === 200) {
if(ipAddress!=='localhost'){
setIpAddress(ipData.newIp)
const updatedDevices = devices.map(device =>
device.address === ipData.currentIp
? { ...device, address: ipData.newIp }
: device
);
useDevicesStore.setState({ devices: updatedDevices });
}
onClose()
}
} catch (err) {
console.error('Error:', err);
} finally {
setLoader(false);
}
};
const cancelIp = async (data: any) => {
try {
const result = await NetworkService.saveIp(ipAddress, data);
if (result.status === 200) {
onClose()
}
} catch (err) {
console.error('Error:', err);
} finally {
setLoader(false);
}
};
return (
<>
{loader && isAdmin ? (
<Loader loaderText="Идет проверка корректности IP адреса" />
) : (
<ModalContainer {...(checkStatus ? { height: "unset" } : {})}>
<ModalTitle>
<h3>Изменение IP-Адреса</h3>
</ModalTitle>
{checkStatus ? (
<ModalContent>
<Information>
<h3>Общие сведения</h3>
<Info>
<p>Интерфейс: {ipData.device}</p>
<p>--{'>'}</p>
<p>{ipData.connection}</p>
</Info>
<h3>Сведения об IP</h3>
<DataContainer>
<CurrentData>
<p>Текущий ip: {ipData.currentIp}</p>
<p>Текущий шлюз: {ipData.currentGate}</p>
<p>Текущий DNS: {ipData.currentDns}</p>
</CurrentData>
<NewData>
<p>Новый ip: {ipData.newIp}</p>
<p>Новый шлюз: {ipData.newGate}</p>
<p>Новый DNS: {ipData.newDns}</p>
</NewData>
</DataContainer>
</Information>
<ButtonContainer>
<MainButton title="Отмена" size='small' onClick={() => cancelIp(currentIp)}/>
<MainButton title="Сохранить" size='small' onClick={() => saveIp(newIP)}/>
</ButtonContainer>
</ModalContent>
) : (
<>
<ModalContent>
<ModalText>
{isAdmin ? (
<>
<h3>Произошла ошибка проверки корректности</h3>
<ErrorMessage>{message}</ErrorMessage>
</>
):(
<h3>Для изменения сетевых настроек необходимо авторизироваться под администратором</h3>
)}
</ModalText>
</ModalContent>
<ButtonContainer>
<MainButton title="Закрыть" size='small' onClick={smoothClose}/>
</ButtonContainer>
</>
)}
</ModalContainer>
)}
</>
)
}

View file

@ -1,135 +0,0 @@
import { useEffect, useState } from "react"
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
import Input from "../../UI/inputs/input"
import { MainButton } from "../../UI/buttons/mainButton"
import { NetworkService } from "../../../api/request"
import { useIpStore } from "../../../store"
import { useAdminStore } from "../../../store/adminStore"
import { useCheckAdmin } from "../../../utils/checkAdmin"
import { buttonNames } from "../../../const/buttons/names"
import { IconComponent } from "../../../const/buttons/icons"
interface Props {
onClose: () => void;
}
export const RightsConfirm = ({ onClose }: Props) => {
const { ipAddress } = useIpStore();
const { isAdmin, setAdmin } = useAdminStore()
const checkAdmin = useCheckAdmin();
const [error, setError] = useState(false)
const [success, setSuccess] = useState({
message: '',
status: false
})
const [user, setUser] = useState({
username: 'operator',
password: ""
})
const handleChange = (id: string, value: string) => {
setUser(prevData => ({
...prevData,
[id]: value
}));
}
const login = async (user: object) => {
try {
const response = await NetworkService.login(ipAddress, user);
if(response.status === 200){
await checkAdmin();
setError(false)
setSuccess(prevData => ({
...prevData,
message: 'Успешный вход',
status: true
}));
await new Promise(resolve => setTimeout(resolve, 3000));
onClose();
}else{
setError(true)
}
} catch (err) {
console.error('Error:', err);
}
}
const logout = async () => {
await NetworkService.logout(ipAddress);
await checkAdmin();
setSuccess(prevData => ({
...prevData,
message: 'Успешный выход',
status: true
}));
await new Promise(resolve => setTimeout(resolve, 3000));
onClose();
}
return (
<ModalContainer style={!isAdmin ? {height: '250px'} : {height: 'unset'}}>
{!success.status ? (
!isAdmin ? (
<>
<ModalTitle>
<h3>Получение прав доступа</h3>
</ModalTitle>
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '10px'}}>
<h3 style={{marginLeft: '5px', fontSize: '14px'}}>Включить административный доступ</h3>
<Input
type="password"
placeholderText='Введите пароль администратора...'
id="password"
value={user.password}
onChange={(value) => handleChange('password', value)}
/>
{error && (
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
Неверный пароль оператора
</p>
)}
</ModalContent>
<ButtonContainer>
<MainButton size="small" title="Отмена" onClick={onClose} />
<MainButton size="small" title="Отправить" onClick={() => login(user)} />
</ButtonContainer>
</>
) : (
<>
<ModalTitle>
<h3>Переключиться на ограниченный доступ</h3>
</ModalTitle>
<ModalContent>
<p>В данном режиме доступа ограничиваются полномочия администратора.</p>
</ModalContent>
<ButtonContainer>
<MainButton size="small" title="Отмена" onClick={onClose} />
<MainButton size="small" title="Ограничить доступ" onClick={logout} />
</ButtonContainer>
</>
)
):(
<ModalContainer style={{display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', marginTop: '15px'}}>
<IconComponent name={buttonNames[8]} width="50" height="50"/>
<h3>{success.message}</h3>
<p style={{fontSize: '12px'}}>Это окно можно закрыть, либо оно пропадёт само</p>
<MainButton size="small" title="Закрыть" onClick={onClose} style={{width: '30%'}}/>
</ModalContainer>
)}
</ModalContainer>
)
}

View file

@ -1,81 +0,0 @@
import { useModalContext } from "../../store";
import { IpConfirm, ConnectionError, AddDevice, RightsConfirm } from "./modalContainers";
import { ModalMap } from "./windowsType";
import Loader from "../UI/loader";
import { ModalWindow } from "./style";
import {NoteDevice} from "./modalContainers/NoteDevice";
import { NoteWifi } from "./modalContainers/NoteWifi";
import {ConnectNetwork} from "./modalContainers/ConnectNetwork";
import {DisconnectConfirm} from "./modalContainers/disconnectConfirm";
type Props = {
isOpen: boolean,
type: string
data: any;
}
export default function WindowComponent({ isOpen, type, data }: Props) {
const { closeModal, closeLoader } = useModalContext()
return (
<>
{isOpen &&
<ModalWindow>
{type == ModalMap['1'] && data &&
<IpConfirm
ipData={data}
onClose={closeModal}
smoothClose={closeLoader}
/>
}
{type == ModalMap['2'] &&
<Loader loaderText={data} />
}
{type === ModalMap['3'] &&
<ConnectionError
message={data.message}
/>
}
{type === ModalMap['6'] && (
<NoteDevice
type={'devices'}
address={data.address}
currentNote={data.currentNote}
onClose={closeLoader}
/>
)}
{type === ModalMap['7'] && (
<NoteWifi
type={'wifi'}
address={data.address}
currentNote={data.currentNote}
onClose={closeLoader}
/>
)}
{type === ModalMap['8'] && (
<ConnectNetwork
address={data.address}
onClose={closeLoader}
/>
)}
{type === ModalMap['9'] && (
<DisconnectConfirm
address={data.address}
onClose={closeLoader}
/>
)}
{type === ModalMap['4'] && (
<AddDevice onSave={data?.onSave} onClose={closeLoader} />
)}
{type === ModalMap['5'] && (
<RightsConfirm onClose={closeLoader} />
)}
</ModalWindow >
}
</>
)
}

View file

@ -1,70 +0,0 @@
import styled from "styled-components";
export const ModalWindow = styled.div`
width: 100%;
position: fixed;
top: 0;
left: 0;
z-index: 9999;
background-color: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
height: 100%;
`
export const ModalContainer = styled.div<{height?: string}>`
border-radius: 12px;
background: ${(props)=>props.theme.secondary};
color: ${(props=>props.theme.textColor)};
position: relative;
max-height: 100%;
overflow: auto;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 15px;
height: ${(props) => props.height ? props.height : '230px'};
width: 520px;
`
export const ModalTitle = styled.div`
padding: 20px;
border-bottom: 1px solid #9D9D9D;
display: flex;
justify-content: space-between;
h3{
font-size: 18px;
}
`
export const ModalContent = styled.div`
padding: 0 20px;
h3{
font-size: 14px;
}
p{
font-size: 12px;
}
`
export const ModalText = styled.div`
display: flex;
flex-direction: column;
align-items: flex-start;
gap: 10px;
`
export const ErrorMessage = styled.p`
color: #d95454;
`
export const ButtonContainer = styled.div`
justify-content: flex-end;
display: flex;
gap: 10px;
padding: 10px 20px 20px;
`

View file

@ -1,13 +0,0 @@
export type ModalType = Record<string, string>;
export const ModalMap: ModalType = {
'1': 'ipConfirm',
'2': 'loader',
'3': 'connectionError',
'4': 'addDevice',
'5': 'sudo',
'6': 'noteDevice',
'7': 'noteWifi',
'8': 'connectNetwork',
'9': 'disconnectConfirm',
}

View file

@ -1,194 +0,0 @@
import { ModalContainer, ModalTitle, ModalContent, ModalText } from "./modalwindows/style"
import Input from "./UI/inputs/input"
import { MainButton } from "./UI/buttons/mainButton"
import styled from "styled-components"
import { useEffect, useState } from "react"
import { useIpStore } from "../store"
import { NetworkService } from "../api/request"
import { IconComponent } from "../const/buttons/icons"
import { buttonNames } from "../const/buttons/names"
import ThemeButton from "./UI/buttons/toggletheme"
import About from "./workSpace/modules/appModules/about"
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
background: ${(props) => props.theme.background};
`
const Container = styled.form`
display: flex;
flex-direction: column;
gap: 25px;
align-items: center;
background: ${(props) => props.theme.primary};
padding: 25px;
border-radius: 18px;
width: 350px;
`
const Title = styled.div`
display: flex;
flex-direction: column;
gap: 15px;
align-items: center;
text-align: center;
font-size: 20px;
`
const Content = styled.div`
display: flex;
flex-direction: column;
gap: 25px;
text-align: center;
`
const ButtonContainer = styled.div`
width: 100%;
display: flex;
justify-content: center;
`
const Menu = styled.div`
display: flex;
align-items: center;
`
const Button = styled.div`
width: 100%;
cursor: pointer;
`
const IconContainer = styled.div`
padding: 10px;
color: ${props => props.theme.text};
`
export const CreateUser = () => {
const { ipAddress, setIpAddress } = useIpStore();
const [repeatPassword, setRepeatPassword] = useState('');
const [passwordsMatch, setPasswordsMatch] = useState(true);
const [about, setAbout] = useState(false)
const [user, setUser] = useState({
username: 'operator',
password: '',
is_admin: true
})
const handleChange = (id: string, value: string) => {
if (id === 'repeatPassword') {
setRepeatPassword(value);
setPasswordsMatch(user.password === value);
} else {
setUser(prevData => ({
...prevData,
[id]: value
}));
if (id === 'password') {
setPasswordsMatch(value === repeatPassword);
}
}
}
const handleSave = async (user: object) => {
try {
const result = await NetworkService.createUser(ipAddress, user);
if (result.status === 200) { window.location.reload() }
} catch (err) {
console.error('Error:', err);
}
}
const handleSubmit = () => {
if (passwordsMatch) {
handleSave(user);
}
};
const back = ()=>{
setIpAddress('localhost')
}
const toAbout = ()=>{
setAbout(true)
}
return (
<>
{!about ? (
<Wrapper>
<Container onSubmit={handleSubmit}>
<Title>
<IconComponent name={buttonNames[3]} width="52" height="52"></IconComponent>
<h3>Настройка оператора узла</h3>
{ipAddress != 'localhost' &&
<p>{ipAddress}</p>
}
</Title>
<Content>
<div>
<p>Перед началом работы необходимо создать пароль оператора приложения</p>
</div>
<div>
<Input
type="password"
placeholderText='Введите пароль...'
id="password"
value={user.password}
onChange={(value) => handleChange('password', value)}
/>
<Input
type="password"
placeholderText='Повторите пароль...'
id="repeatPassword"
value={repeatPassword}
onChange={(value) => handleChange('repeatPassword', value)}
style={{ marginTop: '10px' }}
/>
{!passwordsMatch && (
<p style={{ color: 'red', marginTop: '5px' }}>
Пароли не совпадают!
</p>
)}
</div>
</Content>
<ButtonContainer>
<MainButton style={{ width: '100%' }} title="Отправить" type="submit" />
</ButtonContainer>
</Container>
<Menu>
{ipAddress != 'localhost' &&
<MainButton title="Назад" onClick={back} style={{marginRight: '10px'}}/>
}
<ThemeButton />
<Button>
<IconContainer>
<IconComponent name={buttonNames[4]} width="30" height="30" onClick={toAbout}/>
</IconContainer>
</Button>
</Menu>
</Wrapper>
): (
<About from="reg" onBack={() => setAbout(false)} />
)}
</>
)
}

View file

@ -1,249 +0,0 @@
import { MainButton } from "../../../UI/buttons/mainButton"
import { workSpaceNames } from "../../../../const/workspace/names"
import { useWorkSpaceStore } from "../../../../store"
import styled, {useTheme} from "styled-components"
import ThemeButton from "../../../UI/buttons/toggletheme"
import {buttonNames} from "../../../../const/buttons/names";
import {IconComponent} from "../../../../const/buttons/icons";
type Props = {
from: 'reg' | 'helloWindow' | 'workSpace'
onBack?: () => void
}
const Wrapper = styled.div`
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
width: 100%;
background: ${(props => props.theme.background)};
padding: 50px;
box-sizing: border-box;
`
const Content = styled.div`
max-width: 1200px;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
box-sizing: border-box;
padding: 40px 50px;
background: ${(props => props.theme.primary)};
border-radius: 18px;
position: relative;
`
const Listing = styled.div`
display: flex;
flex-direction: column;
background-color: ${(props => props.theme.secondary)};
border-radius: 10px;
padding: 24px 20px;
p{
font-weight: bold;
font-size: 24px;
}
`
const List = styled.ul`
display: flex;
flex-direction: column;
margin: 10px 0;
padding: 0;
list-style-type: none;
font-size: 18px;
gap: 4px;
& li:before {
color: ${(props => props.theme.accent)};
content: "•";
font-size: 28px;
width: .8em;
display: inline-block;
position: relative;
top: 2px;
}
`
const Title = styled.div`
font-size: 20px;
`
const SubTitle = styled.div`
display: flex;
flex-direction: column;
gap: 24px;
background-color:rgba(255, 248, 225, 0.6);
border-left: #ff9800 solid 4px;
border-radius: 0 8px 8px 0;
padding: 20px 12px;
a{
text-decoration: none;
color: ${(props => props.theme.errorColor)};
font-weight: 500;
font-size: 18px;
width: max-content;
}
p{
font-size: 18px;
color: ${(props=>props.theme.textColor)};
}
.icon{
width: 32px;
height: 32px;
fill: #ff9800;
}
`
const Links = styled.div`
display: flex;
justify-content: start;
gap: 20px;
`
const LinkBlock = styled.a`
text-decoration: none;
color: ${(props => props.theme.accent)};;
`
const Copyright = styled.div`
width: 100%;
text-align: start;
color: ${(props => props.theme.offlineColor)};
`
const ButtonContainer = styled.div`
position: absolute;
top: 12px;
right: 14px;
`
const AboutLine = styled.hr`
border: ${(props => props.theme.hoverBtn)} 1px solid;
width: 100%;
`
const MainTitle = styled.div`
width: 100%;
display: flex;
gap: 15px;
align-items: center;
justify-content: space-between;
padding-top: 10px;
box-sizing: border-box;
img{
width: 50px;
height: 50px;
}
.version{
text-wrap: nowrap;
background-color: ${(props => props.theme.hoverBtn)};
color: ${(props => props.theme.borderColor)};
border-radius: 20px;
font-size: 18px;
padding: 10px 18px;
line-height: 1;
}
.title{
display: flex;
justify-content: center;
align-items: center;
gap: 24px;
}
`
export default function About({ from, onBack }: Props) {
const { setWorkModule } = useWorkSpaceStore()
const getBackHandler = () => {
switch (from) {
case 'reg':
return onBack
case 'helloWindow':
return () => setWorkModule(workSpaceNames[1])
case 'workSpace':
return () => setWorkModule(workSpaceNames[3])
default:
return undefined
}
}
const backHandler = getBackHandler()
const showBackButton = from !== 'reg' || (from === 'reg' && onBack)
return (
<Wrapper>
<Content>
<MainTitle>
<div className={'title'}>
<img src="./assets/images/gossys.png" alt="" />
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
</div>
<p className={'version'}>v 0.0.3</p>
</MainTitle>
<AboutLine/>
<Title>
<p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
</Title>
<Listing>
<p>Возможности:</p>
<List>
<li>Визуализация активных сетевых ресурсов</li>
<li>Настройка параметров локальных и удалённых сетевых ресурсов</li>
<li>Управление взаимодействием ресурсов с Клиентом ГoNets</li>
</List>
</Listing>
<SubTitle>
<svg className="icon" viewBox="0 0 24 24">
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
</svg>
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
<a href={'https://gossys.dev/pages/projects/gonets'}>Перейти на ГоNets-PRO</a>
</SubTitle>
<Links>
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
Домашняя страницa
</LinkBlock>
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
Документация
</LinkBlock>
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
Лицензия
</LinkBlock>
</Links>
<Copyright>
Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.
</Copyright>
<ButtonContainer>
{showBackButton && backHandler && (
<IconComponent
width="36"
height="36"
name={buttonNames[12]}
onClick={backHandler}
/>
)}
{from == 'reg' &&
<ThemeButton />
}
</ButtonContainer>
</Content>
</Wrapper>
)
}

View file

@ -1,46 +0,0 @@
import styled from "styled-components"
import { IconComponent } from "../../../../const/buttons/icons"
import { buttonNames } from "../../../../const/buttons/names"
import { useWorkSpaceStore } from "../../../../store"
import { useModalContext } from "../../../../store"
import { useAdminStore } from "../../../../store/adminStore"
import { ModalMap } from "../../../modalwindows/windowsType"
import { QuitButton } from "../../../UI/buttons/quitButton"
import { usePosStore } from "../../../../store/posStore"
const Container = styled.div`
display: flex;
align-items: center;
justify-content: flex-end;
background-color: ${(props) => props.theme.primary};
padding: 0 5px;
`
const ButtonContainer = styled.div`
display: flex;
align-items: center;
`
const IconContainer = styled.div`
padding: 10px;
svg{
cursor: pointer;
}
`
export const Header = () => {
const {setNullPos} = usePosStore()
return (
<Container>
<ButtonContainer>
<IconContainer>
<IconComponent name={buttonNames[11]} width="30" height="30" onClick={()=>setNullPos(true)}/>
</IconContainer>
<QuitButton size="small"/>
</ButtonContainer>
</Container>
)
}

View file

@ -1,82 +0,0 @@
import styled from "styled-components"
import { MainButton } from "../../../UI/buttons/mainButton"
import { useWorkSpaceStore } from "../../../../store"
import { workSpaceNames } from "../../../../const/workspace/names"
import { buttonNames } from "../../../../const/buttons/names"
const Wrapper = styled.div`
width: 100%;
height: 100%;
display: flex;
justify-content: center;
align-items: center;
background-color: ${(props => props.theme.background)};
background-image: url('./assets/images/back1.svg');
background-size: 100%;
background-repeat: no-repeat;
background-position: bottom;
`
const Container = styled.div`
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 52px;
margin-bottom: 100px;
`
const ButtonWrapper = styled.div`
display: flex;
gap: 75px;
flex-direction: column;
`
const ButtonContainer = styled.div`
display: flex;
gap: 15px;
flex-direction: column;
`
const Title = styled.div`
display: flex;
align-items: flex-end;
gap: 10px;
h1{
font-size: 52px;
}
`
export const HelloWindow = () => {
const { setWorkModule } = useWorkSpaceStore()
return (
<Wrapper>
<Container className="unselectable">
<Title>
<img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/>
</Title>
<ButtonWrapper>
<ButtonContainer>
<MainButton
title="Настройки сети"
icon={buttonNames[3]}
iconSize={{ width: '32', height: '32' }}
onClick={() => setWorkModule(workSpaceNames[3])}
/>
<MainButton
title="О программе"
icon={buttonNames[4]}
iconSize={{ width: '32', height: '32' }}
onClick={()=>setWorkModule(workSpaceNames[4])}
/>
</ButtonContainer>
</ButtonWrapper>
</Container>
</Wrapper>
)
}

View file

@ -1,78 +0,0 @@
import React, { useState } from 'react';
import styled from 'styled-components';
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
import {DeviceUser} from "../../../../../../store/useDeviceStore";
const ScanBtn = styled.button`
position: relative;
padding: 14px 22px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
transition: .4s ease;
&:hover{
transform: scale(1.03);
}
`
interface ListUsersProps {
loading: boolean;
handleScan: ()=>void;
devices: DeviceUser[];
scanInitiated: boolean;
}
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
const activeDevices = devices.filter(device => device.status === 'online');
const inactiveDevices = devices.filter(device => device.status !== 'online');
// Пагинация
const [currentPage, setCurrentPage] = useState(1);
const [devicesPerPage] = useState(10);
return (
<>
<ScanBtn onClick={handleScan} disabled={loading}>
{devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'}
</ScanBtn>
{devices.length > 0 ? (
<div style={{
display: 'flex',
width: '70%',
gap: '20px',
}}>
{/* Колонка активных устройств */}
<div style={{ flex: 1 }}>
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные узлы ({activeDevices.length})</h3>
{activeDevices.map((device, index) => (
<DeviceCard
key={index}
status="online"
device={device}
/>
))}
</div>
{/* Колонка неактивных устройств */}
<div style={{ flex: 1 }}>
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные узлы ({inactiveDevices.length})</h3>
{inactiveDevices.map((device, index) => (
<DeviceCard
key={index}
status="offline"
device={device}
/>
))}
</div>
</div>
) : scanInitiated ? (
<p style={{ marginTop: '20px' }}>Узлы не найдены</p>
) : null}
</>
);
};
export default ListUsers;

View file

@ -1,45 +0,0 @@
import React from 'react';
import styled from 'styled-components';
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
import {DeviceUser} from "../../../../../../store/useDeviceStore";
import TableScanner from "../../../../../UI/tables/TableScanner";
const ScanBtn = styled.button`
position: relative;
padding: 14px 22px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
transition: .4s ease;
&:hover{
transform: scale(1.03);
}
`
interface ListUsersProps {
loading: boolean;
handleScan: ()=>void;
devices: DeviceUser[];
scanInitiated: boolean;
}
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
return (
<>
<ScanBtn onClick={handleScan} disabled={loading}>
{devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'}
</ScanBtn>
{devices.length > 0 ? (
<TableScanner devices={devices} />
) : scanInitiated ? (
<p style={{ marginTop: '20px' }}>Узлы не найдены</p>
) : null}
</>
);
};
export default ListUsers;

View file

@ -1,191 +0,0 @@
import { useIpStore } from "../../../../../store";
import styled, { keyframes } from "styled-components";
import { useState } from "react";
import { NetworkService } from "../../../../../api/request";
import ListUsers from "./modules/ListUsers";
import TableUsers from "./modules/TableUsers";
import DropdownType from "../../../../UI/Dropdown/DropdownType";
import useNetworkStore from "../../../../../store/useDeviceStore";
type Option ={
value: number,
label: string,
}
// Анимации
const ring1 = keyframes`
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.2; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
`;
const ring2 = keyframes`
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.15; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
`;
const ring3 = keyframes`
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.1; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
`;
const dotAnimation = keyframes`
0% { content: '.'; }
33% { content: '..'; }
66% { content: '...'; }
100% { content: '.'; }
`;
const pulse = keyframes`
0% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.1); }
100% { transform: translate(-50%, -50%) scale(1); }
`;
// Стили
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
display: flex;
flex-direction: column;
align-items: center;
justify-content:${({scanInitiated, loading})=>(scanInitiated && !loading ? 'start' : 'center')};
height: 100%;
width: 100%;
background: ${(props) => props.theme.background};
padding: 50px;
box-sizing: border-box;
gap: 24px;
transition: justify-content .4s ease;
overflow-y: auto;
overflow-x: hidden;
position: relative;
`;
const TitleWrapper = styled.h2`
position: absolute;
top: 20px;
left: 50%;
transform: translateX(-50%);
font-size: 32px;
width: max-content;
`
const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 60px;
height: 60px;
border-radius: 50%;
position: relative;
transition: .4s ease;
transform: ${({scanInitiated,loading})=>(scanInitiated && !loading ? 'scale(0)' : 'scale(1)')};
span {
position: absolute;
display: inline-block;
background: ${(props) => props.theme.scannerColor};
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
&.center {
width: 60px;
height: 60px;
animation: ${({ loading }) => (loading ? pulse : "none")} 2s ease infinite;
}
&.ring1 {
opacity: 0.12;
width: 250px;
height: 250px;
animation: ${({ loading }) => (loading ? ring1 : "none")} 4s linear infinite;
}
&.ring2 {
opacity: 0.08;
width: 400px;
height: 400px;
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.1s" : "0")};
}
&.ring3 {
opacity: 0.04;
width: 550px;
height: 550px;
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.2s" : "0")};
}
}
`;
const LoadingText = styled.div`
position: relative;
padding: 14px 22px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
`;
const ScanningText = styled.span`
&::after {
content: '.';
animation: ${dotAnimation} 1.5s infinite;
}
`;
export default function UserScanner() {
const { ipAddress } = useIpStore();
const [loading, setLoading] = useState(false);
const { devices, setDevices } = useNetworkStore();
const [scanInitiated, setScanInitiated] = useState(devices.length > 0);
// Сканирование
const handleScan = async () => {
try {
setLoading(true);
setScanInitiated(true);
const res = await NetworkService.scanUser(ipAddress);
console.log(res);
setDevices(res.data.devices || []);
} catch (err) {
console.error("Ошибка сканирования сети:", err);
setDevices([])
} finally {
setLoading(false);
}
};
// Способ отображения
const viewOptions: Option[] = [
{ value: 1, label: 'Таблица' },
{ value: 2, label: 'Список' },
];
const [selectedView, setSelectedView] = useState<Option>(viewOptions[0]);
const handleViewChange = (option: Option) => {
setSelectedView(option);
};
return (
<Wrapper scanInitiated={scanInitiated} loading={loading}>
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование узлов подсети</TitleWrapper>)}
<UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center"></span>
<span className="ring1"></span>
<span className="ring2"></span>
<span className="ring3"></span>
</UserLocate>
{scanInitiated && !loading && (
<DropdownType options={viewOptions} value={selectedView} onChange={handleViewChange} label="Способ отображения" />
)}
{loading ? (
<LoadingText>
Сканирование узлов <ScanningText />
</LoadingText>
) : selectedView.value == 1 ? (
<TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
):(
<ListUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
)}
</Wrapper>
);
}

View file

@ -1,202 +0,0 @@
import styled, { keyframes } from "styled-components";
import React, { useState } from "react";
import { NetworkService } from "../../../../../api/request";
import WifiDot, {WifiNetworkScanner} from "./modules/WifiDot";
import ModalWifiScaner from "./modules/ModalWifiScaner";
import useNetworkStore from "../../../../../store/useDeviceStore";
import {useIpStore} from "../../../../../store";
// Анимации
const ring1 = keyframes`
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.2; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
`;
const ring2 = keyframes`
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.15; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
`;
const ring3 = keyframes`
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.1; }
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
`;
const dotAnimation = keyframes`
0% { content: '.'; }
33% { content: '..'; }
66% { content: '...'; }
100% { content: '.'; }
`;
const pulse = keyframes`
0% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.1); }
100% { transform: translate(-50%, -50%) scale(1); }
`;
// Стили
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
display: flex;
flex-direction: column;
align-items: center;
justify-content:center ;
height: 100%;
width: 100%;
position: relative;
background: ${(props) => props.theme.background};
padding: 50px;
box-sizing: border-box;
gap: 24px;
transition: justify-content .4s ease;
overflow-y: auto;
overflow-x: hidden;
`;
const TitleWrapper = styled.h2`
position: absolute;
top: 24px;
left: 50%;
transform: translateX(-50%);
font-size: 32px;
`
const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 60px;
height: 60px;
border-radius: 50%;
position: relative;
transition: .4s ease;
transform: scale(1);
.circel {
position: absolute;
display: inline-block;
background: ${(props) => props.theme.scannerColor};
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
border-radius: 50%;
&.center {
width: 60px;
height: 60px;
animation: ${({ loading }) => (loading ? pulse : "none")} 2s ease infinite;
}
&.ring1 {
opacity: 0.12;
width: 250px;
height: 250px;
animation: ${({ loading }) => (loading ? ring1 : "none")} 4s linear infinite;
}
&.ring2 {
opacity: 0.08;
width: 400px;
height: 400px;
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.1s" : "0")};
}
&.ring3 {
opacity: 0.04;
width: 550px;
height: 550px;
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.2s" : "0")};
}
}
`;
const LoadingText = styled.div`
position: relative;
padding: 14px 22px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
`;
const ScanningText = styled.span`
&::after {
content: '.';
animation: ${dotAnimation} 1.5s infinite;
}
`;
const ScanBtn = styled.button<{scanInitiated: boolean}>`
position: relative;
padding: 14px 22px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 12px;
font-size: 18px;
transition: transform .4s ease;
transform: ${({scanInitiated}) => (scanInitiated ? "translateY(240px)" : "translateY(0)")};
&:hover{
transform: ${({scanInitiated}) => (scanInitiated ? "translateY(240px)" : "translateY(0)")} scale(1.03);
}
`
export function WifiScanner() {
//const { ipAddress } = useIpStore();
const ipAddress = '192.168.1.147'
const [loading, setLoading] = useState(false);
const [scanInitiated, setScanInitiated] = useState(false);
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
const handleScan = async () => {
setWifiNetworks([]);
setCurrentNetwork(null)
setLoading(true);
setScanInitiated(true);
try {
const res = await NetworkService.scanWifi(ipAddress);
const networks = res.data.access_points || [];
setWifiNetworks(networks);
} catch (err) {
console.error("Ошибка сканирования сети:", err);
setWifiNetworks([]);
} finally {
setLoading(false);
}
};
return (
<Wrapper scanInitiated={scanInitiated} loading={loading}>
<TitleWrapper>Сканирование точек доступа Wi-Fi сети</TitleWrapper>
<UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center circel"></span>
<span className="ring1 circel"></span>
<span className="ring2 circel"></span>
<span className="ring3 circel"></span>
{wifiNetworks.map((network, index) => (
<WifiDot
key={network.bssid}
network={network}
index={index}
total={wifiNetworks.length}
onSelect={setCurrentNetwork}
/>
))}
{currentNetwork && (
<ModalWifiScaner
onClose={() => setCurrentNetwork(null)}
network={currentNetwork}
/>
)}
</UserLocate>
{loading ? (
<LoadingText>
Сканирование точек доступа <ScanningText/>
</LoadingText>
) : (
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }>
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'}
</ScanBtn>
)}
</Wrapper>
);
}

View file

@ -1,225 +0,0 @@
import React, {useEffect, useState} from 'react';
import styled from 'styled-components';
import {WifiNetworkScanner} from "./WifiDot";
import {IconComponent} from "../../../../../../const/buttons/icons";
import {buttonNames} from "../../../../../../const/buttons/names";
import {ModalMap} from "../../../../../modalwindows/windowsType";
import {useIpStore, useModalContext} from "../../../../../../store";
import {Status} from "../../../../../customeNodes/nodes/networkInterface/networkNode/style";
import SignalSVG from "../../../../../UI/signalSvg";
import { useTheme } from 'styled-components';
import useNetworkStore, {DeviceUser} from "../../../../../../store/useDeviceStore";
import {NetworkService} from "../../../../../../api/request";
const Modal = styled.div`
display: flex;
justify-content: center;
flex-direction: column;
background-color: ${(props) => props.theme.secondary};
border-radius: 24px;
padding: 32px 28px;
bottom: -40%;
left: 220px;
position: absolute;
z-index: 20;
min-width: 300px;
&:hover .close-btn{
opacity: 1;
visibility: visible;
}
`
const CloseBtn = styled.div`
position: absolute;
top: -20px;
right: -20px;
cursor: pointer;
background-color: ${(props) => props.theme.primary};
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
width: 40px;
height: 40px;
transition: all .4s ease;
opacity: 0;
visibility: visible;
&:hover {
background-color: ${(props) => props.theme.hoverBtn};
}
`
const ModalTitle = styled.div`
display: flex;
justify-content: space-between;
align-items: center;
font-size: 20px;
line-height: 1;
margin-bottom: 32px;
h4{
font-weight: normal;
}
.functionalIcons{
display: flex;
justify-content: center;
align-items: center;
gap: 18px;
}
`
const ModalContent = styled.div`
display: flex;
flex-direction: column;
gap: 10px;
justify-content: space-between;
align-items: center;
width: 100%;
`
const ModalRow = styled.div`
display: flex;
justify-content: space-between;
align-items: start;
gap: 12px;
width: 100%;
font-size: 14px;
h6{
margin: 0;
padding: 0;
font-size: 1em;
}
p{
max-width:180px;
text-align: right;
}
`
const ModalConnect = styled.button`
color: ${(props) => props.theme.primary};
padding: 10px 12px;
font-size: 18px;
background-color: ${(props) => props.theme.accent};
border-radius: 12px;
margin-top: 4px;
border: none;
outline: none;
width: 100%;
cursor: pointer;
transition: .4s ease;
&:hover{
background-color: ${(props) => props.theme.accentHover};
}
`
const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onClose: ()=>void }) => {
const { openModal } = useModalContext();
const { ipAddress } = useIpStore();
const theme = useTheme()
const [signal, setSignal] = useState(1);
const { wifiNetworks } = useNetworkStore();
const [isConnected, setIsConnected] = useState(false);
useEffect(() => {
const getStatus = async () => {
try {
const response = await NetworkService.statusNetwork(ipAddress);
const { status, ssid } = response.data;
setIsConnected(
!!network?.ssid &&
ssid === network.ssid &&
status === 'connected'
);
} catch (error) {
setIsConnected(false);
}
};
getStatus();
}, [network?.ssid, ipAddress]);
const currentNetwork = wifiNetworks.find(n => n.bssid === network.bssid) || network;
const openModalNote = (e: React.MouseEvent) => {
e.stopPropagation();
openModal(ModalMap['7'], {
address: currentNetwork.ssid,
currentNote: currentNetwork.note || ''
});
};
const openModalNetwork= (e: React.MouseEvent) => {
e.stopPropagation();
if(isConnected){
openModal(ModalMap['9'], {
address: currentNetwork.ssid,
});
}else{
openModal(ModalMap['8'], {
address: currentNetwork.ssid,
});
}
};
useEffect(() => {
const perc = Number(network.signal);
switch (true) {
case perc >= -50: setSignal(5); break;
case perc >= -60: setSignal(4); break;
case perc >= -70: setSignal(3); break;
case perc >= -80: setSignal(2); break;
default: setSignal(1); break;
}
}, [network.signal])
return (
<Modal>
<CloseBtn className={'close-btn'} onClick={onClose}>
<IconComponent name={buttonNames[12]} width="30" height="30" />
</CloseBtn>
<ModalTitle>
<h4>{network.ssid}</h4>
<div className={'functionalIcons'}>
<span onClick={openModalNote} style={{cursor: "pointer"}}>
<IconComponent name={buttonNames[16]} width="30" height="30" />
</span>
<Status>
<SignalSVG signal={signal} size={'36px'} color={theme.accent} />
</Status>
</div>
</ModalTitle>
<ModalContent>
<ModalRow>
<h6>BSSID:</h6>
<p>{network.bssid}</p>
</ModalRow>
<ModalRow>
<h6>Производитель:</h6>
<p>{network.vendor}</p>
</ModalRow>
<ModalRow>
<h6>Сигнал:</h6>
<p>{network.signal} dBm</p>
</ModalRow>
<ModalRow>
<h6>Канал:</h6>
<p>{network.channel}</p>
</ModalRow>
<ModalRow>
<h6>Шифрование:</h6>
<p>{network.encryption}</p>
</ModalRow>
<ModalRow>
<h6>Частота:</h6>
<p>{network.frequency}</p>
</ModalRow>
<ModalRow>
<h6>Скрытая сеть:</h6>
<p>{network.hidden ? 'Да' : 'Нет'}</p>
</ModalRow>
<ModalRow>
<h6>Заметка:</h6>
<p>{currentNetwork.note ? currentNetwork.note : '-'}</p>
</ModalRow>
<ModalConnect onClick={openModalNetwork}>{isConnected ? 'Отключиться' : 'Подключиться'}</ModalConnect>
</ModalContent>
</Modal>
);
};
export default ModalWifiScaner;

View file

@ -1,81 +0,0 @@
import React from 'react';
import styled from "styled-components";
export interface WifiNetworkScanner {
ssid: string;
bssid: string;
channel: number;
signal: number;
encryption: string;
frequency: string;
vendor: string;
hidden: boolean;
note: string;
}
//Точка вайфай
const Dot = styled.div<{ top: string; left: string }>`
position: absolute;
top: ${props => props.top};
left: ${props => props.left};
display: flex;
justify-content: center;
align-items: center;
font-size: 18px;
transform: translate(-50%, -50%);
user-select: none;
transition: .4s;
text-wrap: nowrap;
&:hover {
transform: translate(-50%, -50%) scale(1.1);
}
`;
const getPositionInRing = (index: number, total: number, ringSize: number) => {
const angle = (index * (2 * Math.PI / total)) + (Math.PI / total * (index % 2));
const innerRadius = ringSize * 0.4;
const outerRadius = ringSize * 0.5;
const radius = innerRadius + (outerRadius - innerRadius) * (0.3 + 0.7 * (index % 10) / 10);
return {
x: Math.cos(angle) * radius,
y: Math.sin(angle) * radius
};
};
const WifiDot = ({ network, index, total, onSelect }: {
network: WifiNetworkScanner,
index: number,
total: number,
onSelect: (n: WifiNetworkScanner) => void
}) => {
const getPosition = (signal: number, index: number, total: number) => {
let strength: 1 | 2 | 3;
if (signal >= -50) {
strength = 3;
} else if (signal >= -70) {
strength = 2;
} else {
strength = 1;
}
const ringSizes = { 1: 550, 2: 400, 3: 250 };
const ringSize = ringSizes[strength];
const offset = getPositionInRing(index, total, ringSize);
return {
top: `calc(50% + ${offset.y}px)`,
left: `calc(50% + ${offset.x}px)`
};
};
const position = getPosition(network.signal, index, total);
return (
<Dot top={position.top} left={position.left} onClick={() => onSelect(network)}>
{network.ssid}
</Dot>
);
};
export default WifiDot;

Some files were not shown because too many files have changed in this diff Show more