Compare commits
52 commits
release/0.
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 1aa31aa8d2 | |||
| 8b936603cc | |||
| b77725b9f9 | |||
| 660fb66fc0 | |||
| a69c5ac5fb | |||
| 5fc8e1ce7e | |||
| 11a1c6e392 | |||
| 84adc5d941 | |||
|
|
98581132dd | ||
| 40c98e68d7 | |||
| a13636ca49 | |||
| 9f0ab4ae5a | |||
| 752604b20b | |||
| ab38a0db50 | |||
| e1b027b301 | |||
| 6a4ad48577 | |||
| e264750187 | |||
|
|
2b8eff58ac | ||
|
|
81ff7959f7 | ||
|
|
bc4e9caacb | ||
|
|
af1420dbde | ||
|
|
5bc23e7eb8 | ||
|
|
8f48df2cf5 | ||
| 4e92aba2a2 | |||
|
|
4286f28d1e | ||
| 3315123029 | |||
| ab78185f08 | |||
| 0c8533257d | |||
| b1306ff66f | |||
| c326edfb9a | |||
| ec054bbaad | |||
| f0d5481dfe | |||
| 990a7594fc | |||
| 35225b90a1 | |||
| 4215a62b6c | |||
| 6efbb4285f | |||
| 200d47754c | |||
| 44dab7402d | |||
| ba8cea01e7 | |||
| 7491ce455d | |||
| 9ac52c8a8b | |||
|
|
48e8551b0e | ||
| 486d46c467 | |||
|
|
e896a13f3c | ||
|
|
b997f554bc | ||
|
|
ca33a55375 | ||
| 26a364aad6 | |||
|
|
c423cf7172 | ||
| 1672f54e02 | |||
| 61c526d3ee | |||
|
|
ee350c1852 | ||
| 4257864b3d |
3
.gitignore
vendored
|
|
@ -6,5 +6,6 @@ __pycache__
|
|||
.wheels
|
||||
.vendor
|
||||
.python3-deb
|
||||
SRV/storage/devices/devices.json
|
||||
SRV/storage/subnets/*
|
||||
SRV/storage/wifi/wifi.json
|
||||
build/
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@ steps:
|
|||
- python3 --version
|
||||
- node -v
|
||||
- npm -v
|
||||
raspberry
|
||||
|
||||
# ПОДГОТОВКА СПИСКА PYTHON ЗАВИСИМОСТЕЙ GONETS
|
||||
- rm -rf /usr/lib/python3.11/EXTERNALLY-MANAGED
|
||||
|
|
|
|||
13
FRONT/BUILD/index.html
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
<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>
|
||||
|
|
@ -157,20 +157,4 @@ export class NetworkService {
|
|||
return error.response;
|
||||
}
|
||||
}
|
||||
static async clearSubnet(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post(`/settings/clear_subnet`);
|
||||
} catch (error: any) {
|
||||
return error.response;
|
||||
}
|
||||
}
|
||||
static async clearNetwork(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post(`/settings/clear_wifi`);
|
||||
} catch (error: any) {
|
||||
return error.response;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -5,7 +5,6 @@ import {buttonNames} from "../../../const/buttons/names";
|
|||
import {ModalMap} from "../../modalwindows/windowsType";
|
||||
import {IconComponent} from "../../../const/buttons/icons";
|
||||
import {useModalContext} from "../../../store";
|
||||
import {useNameSetting} from "../../../hooks/useSettings";
|
||||
|
||||
interface DeviceCardProps {
|
||||
device: DeviceUser;
|
||||
|
|
@ -75,8 +74,8 @@ const Value = styled.span`
|
|||
|
||||
const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const toggleExpanded = () => setIsExpanded(!isExpanded);
|
||||
const { openModal } = useModalContext();
|
||||
const nameSetting = useNameSetting();
|
||||
|
||||
const openModalNote = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
|
|
@ -88,7 +87,7 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
|
||||
|
||||
return (
|
||||
<CardWrapper status={status} onClick={()=>setIsExpanded(!isExpanded)}>
|
||||
<CardWrapper status={status} onClick={toggleExpanded}>
|
||||
<CardHeader status={status}>
|
||||
<p>
|
||||
MAC: {device.mac}
|
||||
|
|
@ -101,7 +100,7 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
<CardContent isExpanded={isExpanded}>
|
||||
<InfoRow>
|
||||
<Label>Имя:</Label>
|
||||
<Value>{device.hostname || (nameSetting === 'ip' ? device.ip : 'Неизвестно')}</Value>
|
||||
<Value>{device.hostname || 'Неизвестно'}</Value>
|
||||
</InfoRow>
|
||||
|
||||
<InfoRow>
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ const Button = styled.button.withConfig({
|
|||
color: white;
|
||||
font-size: ${(props) =>
|
||||
props.size === "small"
|
||||
? "16px"
|
||||
? "14px"
|
||||
: props.size === "big"
|
||||
? "18px"
|
||||
: "16px"}; // По умолчанию
|
||||
? "16px"
|
||||
: "14px"}; // По умолчанию
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,109 @@
|
|||
import React 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";
|
||||
|
||||
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;
|
||||
`;
|
||||
|
||||
const TableHeaderCell = styled.th<{ sortable?: boolean }>`
|
||||
padding: 16px 24px;
|
||||
text-align: left;
|
||||
font-weight: 600;
|
||||
font-size: 14px;
|
||||
color: ${({ theme }) => theme.textSecondary};
|
||||
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;
|
||||
`;
|
||||
|
||||
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 || ''
|
||||
});
|
||||
};
|
||||
|
||||
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>
|
||||
{devices.map((item, i) => (
|
||||
<TableRow key={i}>
|
||||
<TableCell><StatusBadge status={item.status}></StatusBadge></TableCell>
|
||||
<TableCell>{item.hostname || 'Неизвестен'}</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)}>
|
||||
<IconComponent name={buttonNames[16]} width="26" height="26" />
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</tbody>
|
||||
</StyledTable>
|
||||
</TableContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default TableScanner;
|
||||
|
|
@ -3,13 +3,12 @@ import { Handle, Position } from '@xyflow/react';
|
|||
import { Ip } from './style';
|
||||
import { Row, IpTitle, IpContent } from './style';
|
||||
import Input from '../../../../UI/inputs/input';
|
||||
import {useIpStore, useModalContext, usePannableStore} from '../../../../../store';
|
||||
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";
|
||||
import {NetworkService} from "../../../../../api/request";
|
||||
|
||||
interface IIp {
|
||||
ipType: string
|
||||
|
|
@ -46,7 +45,6 @@ export const IpNode = ({ data }: CustomNodeProps) => {
|
|||
const { toggleEditingMode } = usePannableStore();
|
||||
const [isInput, setIsInput] = useState(false)
|
||||
const { openModal } = useModalContext();
|
||||
const {ipAddress} = useIpStore();
|
||||
|
||||
const [ipData, setIpData] = useState<changeIp>(
|
||||
{
|
||||
|
|
@ -148,20 +146,7 @@ export const IpNode = ({ data }: CustomNodeProps) => {
|
|||
|
||||
setIsInput(false)
|
||||
};
|
||||
const switchMethod = async ()=>{
|
||||
const methodDCHP ={
|
||||
"connection_id": ipData.connection,
|
||||
"ipv4_updates": {
|
||||
"ipv4.method": "auto",
|
||||
}
|
||||
}
|
||||
try {
|
||||
await NetworkService.saveIp(ipAddress, methodDCHP);
|
||||
window.location.reload();
|
||||
}catch (err){
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -185,10 +170,7 @@ export const IpNode = ({ data }: CustomNodeProps) => {
|
|||
</Row>
|
||||
|
||||
) : (
|
||||
<Row>
|
||||
{typeMap[data.ipData.ipType] ==='Static' && <IconComponent name={buttonNames[18]} onClick={switchMethod} width='18' height='18' setColor='white'/>}
|
||||
<IconComponent name={buttonNames[7]} onClick={handleClick} width='18' height='18' setColor='white'/>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
</IpTitle>
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {NoteDevice} from "./modalContainers/NoteDevice";
|
|||
import { NoteWifi } from "./modalContainers/NoteWifi";
|
||||
import {ConnectNetwork} from "./modalContainers/ConnectNetwork";
|
||||
import {DisconnectConfirm} from "./modalContainers/disconnectConfirm";
|
||||
import {Settings} from "../settings/Settings";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean,
|
||||
|
|
@ -75,11 +74,6 @@ export default function WindowComponent({ isOpen, type, data }: Props) {
|
|||
{type === ModalMap['5'] && (
|
||||
<RightsConfirm onClose={closeLoader} />
|
||||
)}
|
||||
{type === ModalMap['10'] && (
|
||||
<Settings
|
||||
onClose={closeLoader}
|
||||
/>
|
||||
)}
|
||||
</ModalWindow >
|
||||
}
|
||||
</>
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ export const ModalWindow = styled.div`
|
|||
height: 100%;
|
||||
`
|
||||
|
||||
export const ModalContainer = styled.div<{height?: string, width?:string}>`
|
||||
export const ModalContainer = styled.div<{height?: string}>`
|
||||
border-radius: 12px;
|
||||
background: ${(props)=>props.theme.secondary};
|
||||
color: ${(props=>props.theme.textColor)};
|
||||
|
|
@ -25,7 +25,7 @@ export const ModalContainer = styled.div<{height?: string, width?:string}>`
|
|||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
height: ${(props) => props.height ? props.height : '230px'};
|
||||
width: ${(props) => props.width ? props.width : '520px'};
|
||||
width: 520px;
|
||||
`
|
||||
|
||||
export const ModalTitle = styled.div`
|
||||
|
|
|
|||
|
|
@ -2,10 +2,8 @@
|
|||
import { MainButton } from "../../../UI/buttons/mainButton"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import styled, {useTheme} from "styled-components"
|
||||
import styled 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'
|
||||
|
|
@ -31,41 +29,23 @@ const Content = styled.div`
|
|||
justify-content: space-between;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 40px 50px;
|
||||
background: ${(props => props.theme.primary)};
|
||||
padding: 25px 50px;
|
||||
background: ${(props => props.theme.secondary)};
|
||||
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;
|
||||
}
|
||||
gap: 15px;
|
||||
|
||||
`
|
||||
|
||||
const List = styled.ul`
|
||||
const List = styled.div`
|
||||
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;
|
||||
}
|
||||
gap: 15px;
|
||||
margin-left: 10px;
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
|
|
@ -75,55 +55,43 @@ const Title = styled.div`
|
|||
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;
|
||||
gap: 10px;
|
||||
|
||||
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;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
`
|
||||
|
||||
const LinkBlock = styled.a`
|
||||
text-decoration: none;
|
||||
color: ${(props => props.theme.accent)};;
|
||||
const LinkColumn = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
`
|
||||
|
||||
|
||||
const LinkBlock = styled.div`
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
`
|
||||
|
||||
const Copyright = styled.div`
|
||||
width: 100%;
|
||||
text-align: start;
|
||||
color: ${(props => props.theme.offlineColor)};
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
`
|
||||
|
||||
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`
|
||||
|
|
@ -131,33 +99,24 @@ const MainTitle = styled.div`
|
|||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-top: 10px;
|
||||
justify-content: center;
|
||||
padding-top: 50px;
|
||||
box-sizing: border-box;
|
||||
|
||||
img{
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
.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{
|
||||
`
|
||||
const Text = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
`
|
||||
|
||||
export default function About({ from, onBack }: Props) {
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
const getBackHandler = () => {
|
||||
switch (from) {
|
||||
case 'reg':
|
||||
|
|
@ -179,63 +138,68 @@ export default function About({ from, onBack }: Props) {
|
|||
<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/>
|
||||
|
||||
<Text>
|
||||
|
||||
<Title>
|
||||
<p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
||||
<p>Приложение ГоNets является минимально работоспособным кроссплатформенным продуктом (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
||||
</Title>
|
||||
|
||||
|
||||
<Listing>
|
||||
<p>Возможности:</p>
|
||||
<p>Данное приложение умеет:</p>
|
||||
<List>
|
||||
<li>Визуализация активных сетевых ресурсов</li>
|
||||
<li>Настройка параметров локальных и удалённых сетевых ресурсов</li>
|
||||
<li>Управление взаимодействием ресурсов с Клиентом ГoNets</li>
|
||||
<p>- Отображать в графическом виде активные сетевые ресурсы</p>
|
||||
<p>- Настраивать сетевые параметры локального и удаленных сетевых ресурсов</p>
|
||||
<p>- Отображать и изменять взаимодействие сетевых ресурсов с установленным Клиентом ГоNets</p>
|
||||
</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>
|
||||
<p>Для коммерческого использования приобретите версию ГоNets-PRO (Подробная информация на странице продукта)</p>
|
||||
</SubTitle>
|
||||
|
||||
<Links>
|
||||
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
|
||||
Домашняя страницa
|
||||
<LinkBlock>
|
||||
<p>Ссылка на домашнюю страницу:</p>
|
||||
<a href="https://gossys.dev/pages/projects/gonets">https://gossys.dev/pages/projects/gonets</a>
|
||||
</LinkBlock>
|
||||
|
||||
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
|
||||
Документация
|
||||
<LinkBlock>
|
||||
<p>Инструкции и информация:</p>
|
||||
<a href="https://wiki.gossys.dev/ru/gonets">https://wiki.gossys.dev/ru/gonets</a>
|
||||
</LinkBlock>
|
||||
|
||||
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
|
||||
Лицензия
|
||||
<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>
|
||||
Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.
|
||||
<p>Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.</p>
|
||||
</Copyright>
|
||||
|
||||
</Text>
|
||||
|
||||
|
||||
<ButtonContainer>
|
||||
{showBackButton && backHandler && (
|
||||
<IconComponent
|
||||
width="36"
|
||||
height="36"
|
||||
name={buttonNames[12]}
|
||||
<MainButton
|
||||
title="Назад"
|
||||
onClick={backHandler}
|
||||
style={{ marginRight: '10px' }}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
{from == 'reg' &&
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
|
||||
import {DeviceUser} from "../../../../../../store/useDeviceStore";
|
||||
import {usePaginateSetting} from "../../../../../../hooks/useSettings";
|
||||
import Pagination from "../../../../../UI/Pagination";
|
||||
|
||||
const ScanBtn = styled.button`
|
||||
position: relative;
|
||||
|
|
@ -30,36 +28,10 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
const activeDevices = devices.filter(device => device.status === 'online');
|
||||
const inactiveDevices = devices.filter(device => device.status !== 'online');
|
||||
|
||||
// Пагинация
|
||||
const paginateSetting = usePaginateSetting();
|
||||
const [currentPage, setCurrentPage] = useState(1);
|
||||
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
|
||||
|
||||
useEffect(() => {
|
||||
setDevicesPerPage(paginateSetting);
|
||||
}, [paginateSetting]);
|
||||
|
||||
const indexOfLastDevice = currentPage * devicesPerPage;
|
||||
const indexOfFirstDevice = indexOfLastDevice - devicesPerPage;
|
||||
const currentActiveDevices = activeDevices.slice(indexOfFirstDevice, indexOfLastDevice);
|
||||
const currentInactiveDevices = inactiveDevices.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);
|
||||
}
|
||||
const onPageChange = (page: number) => {
|
||||
setCurrentPage(page);
|
||||
}
|
||||
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScanBtn onClick={handleScan} disabled={loading}>
|
||||
{devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'}
|
||||
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
</ScanBtn>
|
||||
|
||||
{devices.length > 0 ? (
|
||||
|
|
@ -70,8 +42,8 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
}}>
|
||||
{/* Колонка активных устройств */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные узлы ({activeDevices.length})</h3>
|
||||
{currentActiveDevices.map((device, index) => (
|
||||
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные устройства ({activeDevices.length})</h3>
|
||||
{activeDevices.map((device, index) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
status="online"
|
||||
|
|
@ -81,8 +53,8 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
</div>
|
||||
{/* Колонка неактивных устройств */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные узлы ({inactiveDevices.length})</h3>
|
||||
{currentInactiveDevices.map((device, index) => (
|
||||
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные устройства ({inactiveDevices.length})</h3>
|
||||
{inactiveDevices.map((device, index) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
status="offline"
|
||||
|
|
@ -92,17 +64,8 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
</div>
|
||||
</div>
|
||||
) : scanInitiated ? (
|
||||
<p style={{ marginTop: '20px' }}>Узлы не найдены</p>
|
||||
<p style={{ marginTop: '20px' }}>Устройства не найдены</p>
|
||||
) : null}
|
||||
{totalPages > 1 && (
|
||||
<Pagination
|
||||
onPageChange={onPageChange}
|
||||
currentPage={currentPage}
|
||||
totalPages={totalPages}
|
||||
onPrevPage={handlePrevPage}
|
||||
onNextPage={handleNextPage}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React from 'react';
|
|||
import styled from 'styled-components';
|
||||
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
|
||||
import {DeviceUser} from "../../../../../../store/useDeviceStore";
|
||||
import TableScanner from "./TableScanner";
|
||||
import TableScanner from "../../../../../UI/tables/TableScanner";
|
||||
|
||||
const ScanBtn = styled.button`
|
||||
position: relative;
|
||||
|
|
@ -20,7 +20,7 @@ const ScanBtn = styled.button`
|
|||
|
||||
interface ListUsersProps {
|
||||
loading: boolean;
|
||||
handleScan: (replay?: boolean) => void;
|
||||
handleScan: ()=>void;
|
||||
devices: DeviceUser[];
|
||||
scanInitiated: boolean;
|
||||
}
|
||||
|
|
@ -29,14 +29,14 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
|
||||
return (
|
||||
<>
|
||||
<ScanBtn onClick={()=>handleScan()} disabled={loading}>
|
||||
{devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'}
|
||||
<ScanBtn onClick={handleScan} disabled={loading}>
|
||||
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
</ScanBtn>
|
||||
|
||||
{devices.length > 0 ? (
|
||||
<TableScanner devices={devices} />
|
||||
) : scanInitiated ? (
|
||||
<p style={{ marginTop: '20px' }}>Узлы не найдены</p>
|
||||
<p style={{ marginTop: '20px' }}>Устройства не найдены</p>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
import { useIpStore } from "../../../../../store";
|
||||
import styled, { keyframes } from "styled-components";
|
||||
import {useEffect, useState} from "react";
|
||||
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";
|
||||
import {useIntervalBoolean, useIntervalTime} from "../../../../../hooks/useSettings";
|
||||
|
||||
type Option ={
|
||||
value: number,
|
||||
|
|
@ -78,7 +77,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
span {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
background: ${(props) => props.theme.scannerColor};
|
||||
background: ${(props) => props.theme.accent};
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
|
@ -137,11 +136,9 @@ export default function UserScanner() {
|
|||
const [scanInitiated, setScanInitiated] = useState(devices.length > 0);
|
||||
|
||||
// Сканирование
|
||||
const handleScan = async (replay:boolean) => {
|
||||
const handleScan = async () => {
|
||||
try {
|
||||
if(!replay){
|
||||
setLoading(true);
|
||||
}
|
||||
setScanInitiated(true);
|
||||
const res = await NetworkService.scanUser(ipAddress);
|
||||
console.log(res);
|
||||
|
|
@ -154,23 +151,6 @@ export default function UserScanner() {
|
|||
}
|
||||
};
|
||||
|
||||
// Интервально сканирование
|
||||
const intervalToggle = useIntervalBoolean();
|
||||
const intervalTime = useIntervalTime();
|
||||
useEffect(() => {
|
||||
let intervalId: NodeJS.Timeout | null = null;
|
||||
|
||||
if (intervalToggle) {
|
||||
intervalId = setInterval(()=>handleScan(true), intervalTime * 60 * 1000);
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (intervalId) {
|
||||
clearInterval(intervalId);
|
||||
}
|
||||
};
|
||||
}, [intervalToggle, intervalTime, handleScan]);
|
||||
|
||||
// Способ отображения
|
||||
const viewOptions: Option[] = [
|
||||
{ value: 1, label: 'Таблица' },
|
||||
|
|
@ -183,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>
|
||||
|
|
@ -197,12 +177,12 @@ export default function UserScanner() {
|
|||
|
||||
{loading ? (
|
||||
<LoadingText>
|
||||
Сканирование узлов <ScanningText />
|
||||
Сканирование сети<ScanningText />
|
||||
</LoadingText>
|
||||
) : selectedView.value == 1 ? (
|
||||
<TableUsers loading={loading} handleScan={()=>handleScan(false)} devices={devices} scanInitiated={scanInitiated} />
|
||||
<TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
|
||||
):(
|
||||
<ListUsers loading={loading} handleScan={()=>handleScan(false)} devices={devices} scanInitiated={scanInitiated} />
|
||||
<ListUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -136,8 +136,7 @@ const ScanBtn = styled.button<{scanInitiated: boolean}>`
|
|||
`
|
||||
|
||||
export function WifiScanner() {
|
||||
//const { ipAddress } = useIpStore();
|
||||
const ipAddress = '192.168.1.147'
|
||||
const { ipAddress } = useIpStore();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [scanInitiated, setScanInitiated] = useState(false);
|
||||
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
||||
|
|
@ -162,19 +161,17 @@ 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, index) => (
|
||||
{wifiNetworks.map((network) => (
|
||||
<WifiDot
|
||||
key={network.bssid}
|
||||
network={network}
|
||||
index={index}
|
||||
total={wifiNetworks.length}
|
||||
onSelect={setCurrentNetwork}
|
||||
/>
|
||||
))}
|
||||
|
|
@ -189,11 +186,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>
|
||||
|
|
|
|||
|
|
@ -31,45 +31,43 @@ const Dot = styled.div<{ top: string; left: string }>`
|
|||
}
|
||||
`;
|
||||
|
||||
const getPositionInRing = (index: number, total: number, ringSize: number) => {
|
||||
const angle = (index * (2 * Math.PI / total)) + (Math.PI / total * (index % 2));
|
||||
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 radius = innerRadius + (outerRadius - innerRadius) * (0.3 + 0.7 * (index % 10) / 10);
|
||||
const randomValue = (hash % 1000) / 1000;
|
||||
const randomRadius = Math.sqrt(innerRadius * innerRadius + randomValue * (outerRadius * outerRadius - innerRadius * innerRadius));
|
||||
|
||||
return {
|
||||
x: Math.cos(angle) * radius,
|
||||
y: Math.sin(angle) * radius
|
||||
x: Math.cos(randomAngle) * randomRadius,
|
||||
y: Math.sin(randomAngle) * randomRadius
|
||||
};
|
||||
};
|
||||
|
||||
const WifiDot = ({ network, index, total, onSelect }: {
|
||||
network: WifiNetworkScanner,
|
||||
index: number,
|
||||
total: number,
|
||||
onSelect: (n: WifiNetworkScanner) => void
|
||||
}) => {
|
||||
const getPosition = (signal: number, index: number, total: number) => {
|
||||
const getPosition = (signal: number, bssid: string) => {
|
||||
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(index, total, ringSize);
|
||||
const offset = getPositionInRing(bssid, 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, index, total);
|
||||
const position = getPosition(network.signal, network.bssid);
|
||||
|
||||
return (
|
||||
<Dot top={position.top} left={position.left} onClick={() => onSelect(network)}>
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -81,11 +81,6 @@ export const ToolBar = () => {
|
|||
/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[3]} width="30" height="30" onClick={() => openModal(ModalMap['10'])} />
|
||||
</IconContainer>
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@ 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';
|
||||
|
|
@ -20,8 +19,6 @@ import refresh from '@iconify/icons-ic/baseline-refresh';
|
|||
import close from '@iconify/icons-ic/round-close';
|
||||
import wifiFind from '@iconify/icons-ic/baseline-wifi-find';
|
||||
import noteEdit from '@iconify/icons-ph/note-pencil';
|
||||
import swapHoriz from '@iconify/icons-ic/baseline-swap-horiz';
|
||||
import check from '@iconify/icons-ic/baseline-check';
|
||||
|
||||
export const ICONS = {
|
||||
[buttonNames[1]]: exitToApp,
|
||||
|
|
@ -40,9 +37,6 @@ export const ICONS = {
|
|||
[buttonNames[14]]: userSearch,
|
||||
[buttonNames[15]]: wifiFind,
|
||||
[buttonNames[16]]: noteEdit,
|
||||
[buttonNames[17]]: arrowLeft,
|
||||
[buttonNames[18]]: swapHoriz,
|
||||
[buttonNames[19]]: check,
|
||||
} as const;
|
||||
|
||||
type IconName = keyof typeof ICONS;
|
||||
|
|
|
|||
|
|
@ -16,8 +16,5 @@ export const buttonNames: names ={
|
|||
13: 'noAdmin',
|
||||
14: 'userSearch',
|
||||
15: 'wifiFind',
|
||||
16: 'noteEdit',
|
||||
17: 'arrowLeft',
|
||||
18: 'swapHoriz',
|
||||
19: 'check'
|
||||
16: 'noteEdit'
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { toolBarNames } from "./names"
|
|||
import { useTheme } from "styled-components"
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
import desktop from '@iconify/icons-ic/baseline-menu';
|
||||
import desktop from '@iconify/icons-ic/round-desktop-windows';
|
||||
|
||||
export const ICONS = {
|
||||
[toolBarNames[1]]: desktop,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const dark = {
|
|||
background: '#3A3A3A',
|
||||
textColor: '#FFFFFF',
|
||||
borderColor: '#b6b6b6',
|
||||
scannerColor: '#00E5FF',
|
||||
scannerColor: '#0E9C9D',
|
||||
offlineColor: '#656565',
|
||||
hoverBtn: '#4a4a4a',
|
||||
};
|
||||
2
FRONT/DEV/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
/node_modules
|
||||
/dist
|
||||
BIN
FRONT/DEV/assets/icon.png
Normal file
|
After Width: | Height: | Size: 126 KiB |
28
FRONT/DEV/eslint.config.js
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
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 },
|
||||
],
|
||||
},
|
||||
},
|
||||
)
|
||||
12
FRONT/DEV/index.html
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<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>
|
||||
61
FRONT/DEV/index.tsx
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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';
|
||||
|
||||
import {
|
||||
ModalProvider,
|
||||
useModalContext,
|
||||
ThemeProvider
|
||||
} from './src/store';
|
||||
|
||||
import WindowComponent from './src/components/modalwindows/modalWindow';
|
||||
import Router from './src/router';
|
||||
import { I18nProvider } from "./src/utils/i18n/provider";
|
||||
import { useLanguage } from "./src/hooks/useSettings";
|
||||
|
||||
const AppWithProviders = () => {
|
||||
const currentLanguage = useLanguage();
|
||||
|
||||
return (
|
||||
<I18nProvider initialLanguage={currentLanguage}>
|
||||
<ThemeProvider>
|
||||
<HashRouter>
|
||||
<ModalProvider>
|
||||
<MainComponent />
|
||||
</ModalProvider>
|
||||
</HashRouter>
|
||||
</ThemeProvider>
|
||||
</I18nProvider>
|
||||
);
|
||||
};
|
||||
|
||||
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(<AppWithProviders />);
|
||||
61
FRONT/DEV/main.js
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
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();
|
||||
}
|
||||
});
|
||||
4966
FRONT/DEV/package-lock.json
generated
Normal file
39
FRONT/DEV/package.json
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"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": "^39.2.3",
|
||||
"electron-packager": "^17.1.2",
|
||||
"esbuild-wasm": "^0.17.12",
|
||||
"typescript": "^4.9.5",
|
||||
"vite": "^6.3.4"
|
||||
}
|
||||
}
|
||||
11
FRONT/DEV/preload.js
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
send: (channel, data) => ipcRenderer.send(channel, data),
|
||||
on: (channel, func) => ipcRenderer.on(channel, (event, ...args) => func(...args)),
|
||||
quitApp: () => ipcRenderer.send('quit-app'),
|
||||
|
||||
showSaveDialog: (options) => ipcRenderer.invoke('show-save-dialog', options),
|
||||
writeFile: (path, data) => ipcRenderer.invoke('write-file', { path, data }),
|
||||
onThemeChange: (callback) => ipcRenderer.on('theme-update', (event, theme) => callback(theme))
|
||||
});
|
||||
836
FRONT/DEV/public/assets/images/back1.svg
Normal file
|
|
@ -0,0 +1,836 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 94 KiB |
20
FRONT/DEV/public/assets/images/bigLogo.svg
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
15
FRONT/DEV/public/assets/images/bukvi.svg
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 4.1 KiB |
12
FRONT/DEV/public/assets/images/cancel.svg
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<?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>
|
||||
|
After Width: | Height: | Size: 459 B |
5
FRONT/DEV/public/assets/images/check.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?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>
|
||||
|
After Width: | Height: | Size: 1.6 KiB |
5
FRONT/DEV/public/assets/images/edit.svg
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
<?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>
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
11
FRONT/DEV/public/assets/images/ethernet.svg
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<?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>
|
||||
|
After Width: | Height: | Size: 579 B |
BIN
FRONT/DEV/public/assets/images/gossys.png
Normal file
|
After Width: | Height: | Size: 69 KiB |
23
FRONT/DEV/public/assets/images/logo.svg
Normal file
|
After Width: | Height: | Size: 16 KiB |
17
FRONT/DEV/public/assets/images/signal.svg
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 966 B |
1
FRONT/DEV/public/assets/images/vite.svg
Normal file
|
|
@ -0,0 +1 @@
|
|||
<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>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
27
FRONT/DEV/public/assets/images/vpn.svg
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
<?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>
|
||||
|
After Width: | Height: | Size: 1.5 KiB |
6
FRONT/DEV/public/assets/images/wifi.svg
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<?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>
|
||||
|
After Width: | Height: | Size: 906 B |
186
FRONT/DEV/public/locales/en/common.json
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
{
|
||||
"userScanner-title": "Subnet Node Scanning",
|
||||
"userScanner-visible": "Display Method",
|
||||
"userScanner-loading": "Scanning nodes",
|
||||
"userScanner-refresh": "Refresh List",
|
||||
"userScanner-scan": "Scan Nodes",
|
||||
"userScanner-notFound": "Nodes not found",
|
||||
"tableScanner-online": "Online",
|
||||
"tableScanner-name": "Name",
|
||||
"tableScanner-vendor": "Vendor",
|
||||
"tableScanner-ip": "IP Address",
|
||||
"tableScanner-ports": "Open Ports",
|
||||
"tableScanner-activity": "Last Activity",
|
||||
"tableScanner-note": "Note",
|
||||
"tableScanner-action": "Action",
|
||||
"tableScanner-unknown": "Unknown",
|
||||
"tableScanner-noData": "No data to display",
|
||||
"listUsers-activeNodes": "Active Nodes",
|
||||
"listUsers-inactiveNodes": "Inactive Nodes",
|
||||
"ipInput-placeholder": "Enter IP",
|
||||
"ipInput-error": "Enter a valid IP address",
|
||||
"userScanner-visible_table": "Table",
|
||||
"userScanner-visible_list": "List",
|
||||
|
||||
"wifiScanner-title": "Wi-Fi Access Point Scanning",
|
||||
"wifiScanner-scanning": "Scanning access points",
|
||||
"wifiScanner-scanError": "Network scanning error",
|
||||
"wifiScanner-refresh": "Refresh List",
|
||||
"wifiScanner-scan": "Scan Access Points",
|
||||
"wifiModal-bssid": "BSSID",
|
||||
"wifiModal-vendor": "Vendor",
|
||||
"wifiModal-signal": "Signal",
|
||||
"wifiModal-channel": "Channel",
|
||||
"wifiModal-encryption": "Encryption",
|
||||
"wifiModal-frequency": "Frequency",
|
||||
"wifiModal-hidden": "Hidden Network",
|
||||
"wifiModal-note": "Note",
|
||||
"wifiModal-connect": "Connect",
|
||||
"wifiModal-disconnect": "Disconnect",
|
||||
"wifiModal-unknown": "Unknown",
|
||||
"wifiModal-none": "None",
|
||||
"common-yes": "Yes",
|
||||
"common-no": "No",
|
||||
|
||||
"addNode": "Add Node",
|
||||
"enterNodeAddress": "Enter node address...",
|
||||
"connectionLost": "Connection Lost",
|
||||
"serverDisconnected": "Server connection interrupted",
|
||||
"checkAddressAndRetry": "Please check the address availability and try again",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"save": "Save",
|
||||
"retry": "Retry",
|
||||
"connect": "Connect",
|
||||
"submit": "Submit",
|
||||
"common-back": "Back",
|
||||
"error": "Error",
|
||||
"networkConnection": "Network Connection",
|
||||
"enterPassword": "Enter password...",
|
||||
"connectingAttempt": "Attempting to connect to the network",
|
||||
"connectionError": "Network connection error",
|
||||
"connectionErrorLog": "Connection error: ",
|
||||
"customizeTable-title": "Subnet Node Table Customization",
|
||||
|
||||
"wifiDisconnect-title": "Wi-Fi Network Disconnection",
|
||||
"wifiDisconnect-confirm": "Confirm Disconnection",
|
||||
"wifiDisconnect-message1": "You are about to disconnect from the network - ",
|
||||
"wifiDisconnect-message2": " All active connections will be interrupted.",
|
||||
"wifiDisconnect-disconnect": "Disconnect",
|
||||
"wifiDisconnect-progress": "Disconnecting from the network",
|
||||
|
||||
"ipChange-title": "IP Address Change",
|
||||
"ipChange-checkingIp": "Checking IP address validity",
|
||||
"ipChange-generalInfo": "General Information",
|
||||
"ipChange-interface": "Interface",
|
||||
"ipChange-ipInfo": "IP Information",
|
||||
"ipChange-currentIp": "Current IP",
|
||||
"ipChange-currentGateway": "Current Gateway",
|
||||
"ipChange-currentDns": "Current DNS",
|
||||
"ipChange-newIp": "New IP",
|
||||
"ipChange-newGateway": "New Gateway",
|
||||
"ipChange-newDns": "New DNS",
|
||||
"ipChange-checkError": "An error occurred while checking validity",
|
||||
"ipChange-adminRequired": "To change network settings, you must log in as an administrator",
|
||||
|
||||
"note-edit": "Edit Note",
|
||||
"note-add": "Add Note",
|
||||
"note-enter": "Enter note...",
|
||||
|
||||
"auth-successLogin": "Successful Login",
|
||||
"auth-successLogout": "Successful Logout",
|
||||
"auth-getAccess": "Obtaining Access Rights",
|
||||
"auth-enableAdmin": "Enable Administrative Access",
|
||||
"auth-enterAdminPass": "Enter administrator password...",
|
||||
"auth-wrongPass": "Incorrect operator password",
|
||||
"auth-switchToLimited": "Switch to Limited Access",
|
||||
"auth-limitedModeDesc": "In this mode, administrator privileges are restricted.",
|
||||
"auth-restrictAccess": "Restrict Access",
|
||||
"auth-closeHint": "This window can be closed, or it will disappear on its own",
|
||||
|
||||
"operatorConfig-title": "Node Operator Configuration",
|
||||
"operatorConfig-instruction": "Before starting, you need to create an application operator password",
|
||||
"operatorConfig-passwordPlaceholder": "Enter password...",
|
||||
"operatorConfig-repeatPasswordPlaceholder": "Repeat password...",
|
||||
"operatorConfig-passwordMismatch": "Passwords do not match!",
|
||||
"operatorConfig-submit": "Submit",
|
||||
|
||||
"about-logoAlt": "Gossys Logo",
|
||||
"about-title": "About the Application",
|
||||
"about-description": "GoNets is a minimally viable cross-platform product (MVP) for demonstrating our approach to managing PC settings in a corporate environment.",
|
||||
"about-featuresTitle": "Features",
|
||||
"about-feature1": "Visualization of active network resources",
|
||||
"about-feature2": "Configuration of local and remote network resource parameters",
|
||||
"about-feature3": "Management of resource interaction with the GoNets Client",
|
||||
"about-disclaimer": "The software is for demonstration and home use only.",
|
||||
"about-goToPro": "Go to GoNets-PRO",
|
||||
"about-homepage": "Homepage",
|
||||
"about-documentation": "Documentation",
|
||||
"about-license": "License",
|
||||
"about-copyright": "Developed and maintained by Hybrid Office Systems LLC, 2024-2025. All rights reserved.",
|
||||
|
||||
"main-logoAlt": "Application Logo",
|
||||
"main-networkSettings": "Network Settings",
|
||||
"main-about": "About the Program",
|
||||
|
||||
"nodes-availableNodes": "Available Nodes",
|
||||
"nodes-rootAddress": "Root Address",
|
||||
"nodes-name": "Name",
|
||||
"nodes-loading": "Loading",
|
||||
"nodes-additionalNodes": "Additional Nodes",
|
||||
"nodes-node": "Node",
|
||||
"nodes-address": "Address",
|
||||
"nodes-connectionError": "Failed to connect to the server at the address ",
|
||||
|
||||
"settings-nodesSection": "Subnet Nodes",
|
||||
"settings-intervalScanning": "Interval Scanning",
|
||||
"settings-scanInterval": "Node Scan Interval (min)",
|
||||
"settings-displayNames": "Display Names When Missing",
|
||||
"settings-ipAddress": "IP Address",
|
||||
"settings-unknown": "Unknown",
|
||||
"settings-rowsPerPage": "Rows Per Table",
|
||||
"settings-displaySettings": "Nodes Display",
|
||||
"settings-configure": "Configure",
|
||||
"settings-clearSessionData": "Clear Session Data",
|
||||
"settings-clear": "Clear",
|
||||
"settings-clearFailed": "Data was not deleted: ",
|
||||
"settings-exportData": "Export Nodes Data",
|
||||
"settings-export": "Export",
|
||||
"settings-saveNodesTitle": "Save Nodes Data",
|
||||
"settings-exportSuccess": "Data exported successfully!",
|
||||
"settings-exportError": "Export error: ",
|
||||
"settings-status": "Online",
|
||||
"settings-name": "Name",
|
||||
"settings-vendor": "Manufacturer",
|
||||
"settings-openPorts": "Open Ports",
|
||||
"settings-lastActivity": "Last Activity",
|
||||
"settings-note": "Note",
|
||||
"settings-action": "Action",
|
||||
|
||||
"settings-title": "Settings",
|
||||
"settings-resetSettings": "Reset Settings",
|
||||
"settings-resetConfirm": "Are you sure you want to reset all settings to default values?",
|
||||
"settings-cancel": "Cancel",
|
||||
"settings-save": "Save",
|
||||
"settings-fixErrors": "Fix errors before saving",
|
||||
"settings-fillRequired": "Fill in all required fields",
|
||||
"settings-dataNotDeleted": "Data was not deleted: ",
|
||||
"settings-saveNetworksTitle": "Save Networks Data",
|
||||
"settings-fieldRequired": "Field cannot be empty",
|
||||
"settings-enterNumber": "Please enter a number",
|
||||
"settings-valueTooSmall": "Value cannot be less than ",
|
||||
"settings-valueTooLarge": "Value cannot be greater than ",
|
||||
|
||||
"settings-sections-subnetNodes": "Subnet Nodes",
|
||||
"settings-sections-accessPoints": "Access Points",
|
||||
"settings-sections-appearance": "Appearance",
|
||||
|
||||
"settings-exportNetworkData": "Export Access Points Data",
|
||||
"settings-colorTheme": "Color Theme",
|
||||
"settings-light": "Light",
|
||||
"settings-dark": "Dark",
|
||||
"settings-system": "As in System",
|
||||
"settings-interfaceLanguage": "Interface Language",
|
||||
"settings-russian": "Russian",
|
||||
"settings-english": "English"
|
||||
}
|
||||
192
FRONT/DEV/public/locales/ru/common.json
Normal file
|
|
@ -0,0 +1,192 @@
|
|||
{
|
||||
"userScanner-title": "Сканирование узлов подсети",
|
||||
"userScanner-visible": "Способ отображения",
|
||||
"userScanner-loading": "Cканирование узлов",
|
||||
"userScanner-refresh": "Обновить список",
|
||||
"userScanner-scan": "Сканировать узлы",
|
||||
"userScanner-notFound": "Узлы не найдены",
|
||||
"tableScanner-online": "Онлайн",
|
||||
"tableScanner-name": "Имя",
|
||||
"tableScanner-vendor": "Производитель",
|
||||
"tableScanner-ip": "IP адрес",
|
||||
"tableScanner-ports": "Открытые порты",
|
||||
"tableScanner-activity": "Последняя активность",
|
||||
"tableScanner-note": "Заметка",
|
||||
"tableScanner-action": "Действие",
|
||||
"tableScanner-unknown": "Неизвестно",
|
||||
"tableScanner-noData": "Нет данных для отображения",
|
||||
"listUsers-activeNodes": "Активные узлы",
|
||||
"listUsers-inactiveNodes": "Неактивные узлы",
|
||||
"ipInput-placeholder": "Введите IP",
|
||||
"ipInput-error": "Введите корректный IP-адрес",
|
||||
"userScanner-visible_table": "Таблица",
|
||||
"userScanner-visible_list": "Список",
|
||||
|
||||
"wifiScanner-title": "Сканирование точек доступа Wi-Fi",
|
||||
"wifiScanner-scanning": "Сканирование точек доступа",
|
||||
"wifiScanner-scanError": "Ошибка сканирования сети",
|
||||
"wifiScanner-refresh": "Обновить список",
|
||||
"wifiScanner-scan": "Сканировать точки доступа",
|
||||
"wifiModal-bssid": "BSSID",
|
||||
"wifiModal-vendor": "Производитель",
|
||||
"wifiModal-signal": "Сигнал",
|
||||
"wifiModal-channel": "Канал",
|
||||
"wifiModal-encryption": "Шифрование",
|
||||
"wifiModal-frequency": "Частота",
|
||||
"wifiModal-hidden": "Скрытая сеть",
|
||||
"wifiModal-note": "Заметка",
|
||||
"wifiModal-connect": "Подключиться",
|
||||
"wifiModal-disconnect": "Отключиться",
|
||||
"wifiModal-unknown": "Неизвестно",
|
||||
"wifiModal-none": "Отсутствует",
|
||||
"common-yes": "Да",
|
||||
"common-no": "Нет",
|
||||
|
||||
"addNode": "Добавление узла",
|
||||
"enterNodeAddress": "Введите адрес узла...",
|
||||
"connectionLost": "Соединение прервано",
|
||||
"serverDisconnected": "Произошёл разрыв с сервером",
|
||||
"checkAddressAndRetry": "Пожалуйста, проверьте доступ к адресу и попробуйте снова",
|
||||
"cancel": "Отменить",
|
||||
"close": "Закрыть",
|
||||
"save": "Сохранить",
|
||||
"retry": "Повторить",
|
||||
"connect": "Подключиться",
|
||||
"common-back": "Назад",
|
||||
"submit": "Подтвердить",
|
||||
"error": "Ошибка",
|
||||
"networkConnection": "Подключение к сети",
|
||||
"enterPassword": "Введите пароль...",
|
||||
"connectingAttempt": "Попытка подключения к сети",
|
||||
"connectionError": "Ошибка подключения к сети",
|
||||
"connectionErrorLog": "Ошибка подключения: ",
|
||||
"customizeTable-title": "Кастомизация таблицы узлов подсети",
|
||||
|
||||
"wifiDisconnect-title": "Отключение от Wi-Fi сети",
|
||||
"wifiDisconnect-confirm": "Подтверждение отключения",
|
||||
"wifiDisconnect-message1": "Вы собираетесь отключиться от сети - ",
|
||||
"wifiDisconnect-message2": " Все активные подключения будут прерваны.",
|
||||
"wifiDisconnect-disconnect": "Отключиться",
|
||||
"wifiDisconnect-progress": "Отключение от сети",
|
||||
|
||||
"ipChange-title": "Изменение IP-Адреса",
|
||||
"ipChange-checkingIp": "Идет проверка корректности IP адреса",
|
||||
"ipChange-generalInfo": "Общие сведения",
|
||||
"ipChange-interface": "Интерфейс",
|
||||
"ipChange-ipInfo": "Сведения об IP",
|
||||
"ipChange-currentIp": "Текущий ip",
|
||||
"ipChange-currentGateway": "Текущий шлюз",
|
||||
"ipChange-currentDns": "Текущий DNS",
|
||||
"ipChange-newIp": "Новый ip",
|
||||
"ipChange-newGateway": "Новый шлюз",
|
||||
"ipChange-newDns": "Новый DNS",
|
||||
"ipChange-checkError": "Произошла ошибка проверки корректности",
|
||||
"ipChange-adminRequired": "Для изменения сетевых настроек необходимо авторизироваться под администратором",
|
||||
|
||||
"note-edit": "Редактирование заметки",
|
||||
"note-add": "Добавление заметки",
|
||||
"note-enter": "Введите заметку...",
|
||||
|
||||
"auth-successLogin": "Успешный вход",
|
||||
"auth-successLogout": "Успешный выход",
|
||||
"auth-getAccess": "Получение прав доступа",
|
||||
"auth-enableAdmin": "Включить административный доступ",
|
||||
"auth-enterAdminPass": "Введите пароль администратора...",
|
||||
"auth-wrongPass": "Неверный пароль оператора",
|
||||
"auth-switchToLimited": "Переключиться на ограниченный доступ",
|
||||
"auth-limitedModeDesc": "В данном режиме доступа ограничиваются полномочия администратора.",
|
||||
"auth-restrictAccess": "Ограничить доступ",
|
||||
"auth-closeHint": "Это окно можно закрыть, либо оно пропадёт само",
|
||||
|
||||
"operatorConfig-title": "Настройка оператора узла",
|
||||
"operatorConfig-instruction": "Перед началом работы необходимо создать пароль оператора приложения",
|
||||
"operatorConfig-passwordPlaceholder": "Введите пароль...",
|
||||
"operatorConfig-repeatPasswordPlaceholder": "Повторите пароль...",
|
||||
"operatorConfig-passwordMismatch": "Пароли не совпадают!",
|
||||
"operatorConfig-submit": "Отправить",
|
||||
|
||||
"about-logoAlt": "Логотип Gossys",
|
||||
"about-title": "О приложении",
|
||||
"about-description": "ГоNets - минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.",
|
||||
"about-featuresTitle": "Возможности",
|
||||
"about-feature1": "Визуализация активных сетевых ресурсов",
|
||||
"about-feature2": "Настройка параметров локальных и удалённых сетевых ресурсов",
|
||||
"about-feature3": "Управление взаимодействием ресурсов с Клиентом ГoNets",
|
||||
"about-disclaimer": "Программное обеспечение исключительно для ознакомления и домашнего использования.",
|
||||
"about-goToPro": "Перейти на ГоNets-PRO",
|
||||
"about-homepage": "Домашняя страница",
|
||||
"about-documentation": "Документация",
|
||||
"about-license": "Лицензия",
|
||||
"about-copyright": "Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.",
|
||||
|
||||
"main-logoAlt": "Логотип приложения",
|
||||
"main-networkSettings": "Настройки сети",
|
||||
"main-about": "О программе",
|
||||
|
||||
"nodes-availableNodes": "Доступные узлы",
|
||||
"nodes-rootAddress": "Корневой адрес",
|
||||
"nodes-name": "Имя",
|
||||
"nodes-loading": "Загрузка",
|
||||
"nodes-additionalNodes": "Дополнительные узлы",
|
||||
"nodes-node": "Узел",
|
||||
"nodes-address": "Адрес",
|
||||
"nodes-connectionError": "Не удалось подключиться к серверу по адресу ",
|
||||
|
||||
"settings-nodesSection": "Узлы подсети",
|
||||
"settings-intervalScanning": "Интервальное сканирование",
|
||||
"settings-scanInterval": "Интервал сканирования узлов (мин)",
|
||||
"settings-displayNames": "Отображаемые имена при отсутствии",
|
||||
"settings-ipAddress": "IP адрес",
|
||||
"settings-unknown": "Неизвестно",
|
||||
"settings-rowsPerPage": "Количество строк в таблице",
|
||||
"settings-displaySettings": "Отображение узлов",
|
||||
"settings-configure": "Настроить",
|
||||
"settings-clearSessionData": "Очистить данные сессии",
|
||||
"settings-clear": "Очистить",
|
||||
"settings-clearFailed": "Данные не были удалены: ",
|
||||
"settings-exportData": "Экспорт данных узлов",
|
||||
"settings-export": "Экспортировать",
|
||||
"settings-saveNodesTitle": "Сохранить данные узлов",
|
||||
"settings-exportSuccess": "Данные успешно экспортированы!",
|
||||
"settings-exportError": "Ошибка экспорта: ",
|
||||
"settings-status": "Онлайн",
|
||||
"settings-name": "Имя",
|
||||
"settings-vendor": "Производитель",
|
||||
"settings-openPorts": "Открытые порты",
|
||||
"settings-lastActivity": "Последняя активность",
|
||||
"settings-note": "Заметка",
|
||||
"settings-action": "Действие",
|
||||
|
||||
"settings-title": "Настройки",
|
||||
"settings-resetSettings": "Сбросить настройки",
|
||||
"settings-resetConfirm": "Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?",
|
||||
"settings-cancel": "Отменить",
|
||||
"settings-save": "Сохранить",
|
||||
"settings-fixErrors": "Исправьте ошибки перед сохранением",
|
||||
"settings-fillRequired": "Заполните все обязательные поля",
|
||||
"settings-dataNotDeleted": "Данные не были удалены: ",
|
||||
"settings-saveNetworksTitle": "Сохранить данные сетей",
|
||||
"settings-fieldRequired": "Поле не может быть пустым",
|
||||
"settings-enterNumber": "Пожалуйста, введите число",
|
||||
"settings-valueTooSmall": "Значение не может быть меньше ",
|
||||
"settings-valueTooLarge": "Значение не может быть больше ",
|
||||
|
||||
"settings-sections-subnetNodes": "Узлы подсети",
|
||||
"settings-sections-accessPoints": "Точки доступа",
|
||||
"settings-sections-appearance": "Внешний вид",
|
||||
|
||||
"settings-exportNetworkData": "Экспорт данных точек доступа",
|
||||
"settings-colorTheme": "Цветовая тема",
|
||||
"settings-light": "Светлая",
|
||||
"settings-dark": "Тёмная",
|
||||
"settings-system": "Как в системе",
|
||||
"settings-interfaceLanguage": "Язык интерфейса",
|
||||
"settings-russian": "Русский",
|
||||
"settings-english": "Английский"
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
196
FRONT/DEV/src/api/request.ts
Normal file
|
|
@ -0,0 +1,196 @@
|
|||
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, ipScanning: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post('/subnet/devices',{
|
||||
subnet: ipScanning,
|
||||
});
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
// Settings
|
||||
static async clearSubnet(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post(`/settings/clear_subnet`);
|
||||
} catch (error: any) {
|
||||
return error.response;
|
||||
}
|
||||
}
|
||||
static async clearNetwork(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post(`/settings/clear_wifi`);
|
||||
} catch (error: any) {
|
||||
return error.response;
|
||||
}
|
||||
}
|
||||
static async exportDevices(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post(`/settings/export_devices`);
|
||||
} catch (error: any) {
|
||||
return error.response;
|
||||
}
|
||||
}
|
||||
static async exportNetwork(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
const axiosInstance = this.createAxiosInstance(ipAddress);
|
||||
return await axiosInstance.post(`/settings/export_wifi`);
|
||||
} catch (error: any) {
|
||||
return error.response;
|
||||
}
|
||||
}
|
||||
}
|
||||
108
FRONT/DEV/src/assets/css/flow.css
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
.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;
|
||||
}
|
||||
36
FRONT/DEV/src/assets/css/index.css
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
138
FRONT/DEV/src/components/UI/Dropdown/DeviceCard.tsx
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
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";
|
||||
import {useNameSetting} from "../../../hooks/useSettings";
|
||||
|
||||
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 { openModal } = useModalContext();
|
||||
const nameSetting = useNameSetting();
|
||||
|
||||
const openModalNote = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
openModal(ModalMap['6'], {
|
||||
address: device.mac,
|
||||
currentNote: device.note || ''
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<CardWrapper status={status} onClick={()=>setIsExpanded(!isExpanded)}>
|
||||
<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 || (nameSetting === 'ip' ? 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;
|
||||
130
FRONT/DEV/src/components/UI/Dropdown/DropdownType.tsx
Normal file
|
|
@ -0,0 +1,130 @@
|
|||
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;
|
||||
77
FRONT/DEV/src/components/UI/Pagination.tsx
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
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<{ active?: boolean }>`
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
background: ${(props) => props.active ? props.theme.borderColor : props.theme.primary};
|
||||
color: ${(props) => props.active ? props.theme.primary : 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.active ? props.theme.textColor : props.theme.background};;
|
||||
}
|
||||
`
|
||||
|
||||
interface PaginationProps {
|
||||
currentPage: number;
|
||||
totalPages: number;
|
||||
onPrevPage: () => void;
|
||||
onNextPage: () => void;
|
||||
onPageChange: (page: number) => void;
|
||||
}
|
||||
|
||||
const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage, onPageChange}: PaginationProps) => {
|
||||
|
||||
const pageNumbers = Array.from({ length: totalPages }, (_, i) => i + 1);
|
||||
|
||||
return (
|
||||
<PaginateWrapper>
|
||||
<Page
|
||||
onClick={onPrevPage}
|
||||
disabled={currentPage === 1}
|
||||
>
|
||||
<IconComponent
|
||||
width="20"
|
||||
height="20"
|
||||
name={buttonNames[17]}
|
||||
/>
|
||||
</Page>
|
||||
{pageNumbers.map((page, i) => (
|
||||
<Page
|
||||
key={i}
|
||||
onClick={() => onPageChange(page)}
|
||||
active={currentPage === page}
|
||||
>
|
||||
{page}
|
||||
</Page>
|
||||
))}
|
||||
<Page
|
||||
onClick={onNextPage}
|
||||
disabled={currentPage === totalPages}
|
||||
>
|
||||
<IconComponent
|
||||
width="20"
|
||||
height="20"
|
||||
name={buttonNames[6]}
|
||||
/>
|
||||
</Page>
|
||||
</PaginateWrapper>
|
||||
);
|
||||
};
|
||||
|
||||
export default Pagination;
|
||||
29
FRONT/DEV/src/components/UI/animDot.tsx
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
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>
|
||||
);
|
||||
75
FRONT/DEV/src/components/UI/buttons/mainButton.tsx
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
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"
|
||||
? "16px"
|
||||
: props.size === "big"
|
||||
? "18px"
|
||||
: "16px"}; // По умолчанию
|
||||
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>
|
||||
);
|
||||
};
|
||||
45
FRONT/DEV/src/components/UI/buttons/quitButton.tsx
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
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>
|
||||
);
|
||||
};
|
||||
26
FRONT/DEV/src/components/UI/buttons/toggletheme.tsx
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
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>
|
||||
);
|
||||
}
|
||||
65
FRONT/DEV/src/components/UI/inputs/input.tsx
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
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;
|
||||
101
FRONT/DEV/src/components/UI/loader.tsx
Normal file
|
|
@ -0,0 +1,101 @@
|
|||
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>
|
||||
)
|
||||
}
|
||||
103
FRONT/DEV/src/components/UI/routerLoader.tsx
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
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>
|
||||
|
||||
)
|
||||
}
|
||||
63
FRONT/DEV/src/components/UI/signalSvg.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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;
|
||||
`
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,20 @@
|
|||
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;
|
||||
`
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
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 >
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
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;
|
||||
`;
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
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'
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,43 @@
|
|||
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;
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
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;
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
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;
|
||||
`
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
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'
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
|
||||
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)} />
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
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};
|
||||
`
|
||||
|
|
@ -0,0 +1,263 @@
|
|||
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 {useIpStore, 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";
|
||||
import {NetworkService} from "../../../../../api/request";
|
||||
|
||||
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 {ipAddress} = useIpStore();
|
||||
|
||||
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)
|
||||
};
|
||||
const switchMethod = async ()=>{
|
||||
const methodDCHP ={
|
||||
"connection_id": ipData.connection,
|
||||
"ipv4_updates": {
|
||||
"ipv4.method": "auto",
|
||||
}
|
||||
}
|
||||
try {
|
||||
await NetworkService.saveIp(ipAddress, methodDCHP);
|
||||
window.location.reload();
|
||||
}catch (err){
|
||||
console.error(err)
|
||||
}
|
||||
}
|
||||
|
||||
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>
|
||||
|
||||
) : (
|
||||
<Row>
|
||||
{typeMap[data.ipData.ipType] ==='Static' && <IconComponent name={buttonNames[18]} onClick={switchMethod} width='18' height='18' setColor='white'/>}
|
||||
<IconComponent name={buttonNames[7]} onClick={handleClick} width='18' height='18' setColor='white'/>
|
||||
</Row>
|
||||
)}
|
||||
|
||||
</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>
|
||||
</>
|
||||
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
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;
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
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;
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
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
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
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;
|
||||
}
|
||||
`
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
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>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
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;
|
||||
}
|
||||
`
|
||||
11
FRONT/DEV/src/components/customeNodes/types/colors.ts
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
type ColorType = Record<string, string>;
|
||||
|
||||
export const NodeColors: ColorType = {
|
||||
'0': '#c0c0c0',
|
||||
'1': '#ef6e7d',
|
||||
'2': '#f19e4c',
|
||||
'3': '#e9c44c',
|
||||
'4': '#51ce82',
|
||||
'5': '#4cd2d0',
|
||||
'6': '#a085f3',
|
||||
};
|
||||
16
FRONT/DEV/src/components/customeNodes/types/types.ts
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
|
||||
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,
|
||||
};
|
||||
10
FRONT/DEV/src/components/modalwindows/modal.tsx
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
import { ModalWindow, ModalContainer } from "./style"
|
||||
import { modalNames } from "../../const/modals/names"
|
||||
|
||||
export const WindowComponent = ()=>{
|
||||
return(
|
||||
<ModalWindow>
|
||||
|
||||
</ModalWindow>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,87 @@
|
|||
// 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";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
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 {t} = useTranslation('common');
|
||||
|
||||
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(t('connectionErrorLog'), error);
|
||||
setError(t('connectionError'));
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContainer style={{height: '230px'}}>
|
||||
<ModalTitle>
|
||||
<h3>{t('networkConnection')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '8px'}}>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText={t('enterPassword')}
|
||||
value={password}
|
||||
id='deviceNote'
|
||||
onChange={(value) => setPassword(value)}
|
||||
/>
|
||||
{error && (
|
||||
<p style={{color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right'}}>
|
||||
{error}
|
||||
</p>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
color={theme.successColor}
|
||||
hoverColor={theme.successColor}
|
||||
title={t('connect')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
|
||||
{isLoading && <RouterLoader loaderText={t('connectingAttempt')}/>}
|
||||
</ModalContainer>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,83 @@
|
|||
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";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
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 {t} = useTranslation('common');
|
||||
|
||||
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 ? t('note-edit') : t('note-add')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholderText={t('note-enter')}
|
||||
value={note}
|
||||
id='deviceNote'
|
||||
onChange={(value) => setNote(value)}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('save')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContainer>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
// 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";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
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 {t} = useTranslation('common');
|
||||
|
||||
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 ? t('note-edit') : t('note-add')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholderText={t('note-enter')}
|
||||
value={note}
|
||||
id='deviceNote'
|
||||
onChange={(value) => setNote(value)}
|
||||
/>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('save')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContainer>
|
||||
);
|
||||
};
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
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";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
onSave?: (data: string) => void;
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const AddDevice = ({ onSave, onClose }: Props) => {
|
||||
const [ipAddress, setIpAddress] = useState('')
|
||||
const [error, setError] = useState(false)
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const handleSubmit = () => {
|
||||
if (!validateIPAddress(ipAddress)) {
|
||||
setError(true)
|
||||
return;
|
||||
}
|
||||
// e.preventDefault(); // Предотвращаем перезагрузку страницы
|
||||
if (onSave && ipAddress) {
|
||||
setError(false)
|
||||
onSave(ipAddress);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContainer style={{ height: '230px' }}>
|
||||
<ModalTitle>
|
||||
<h3>{t('addNode')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
|
||||
<Input
|
||||
type="text"
|
||||
placeholderText={t('enterNodeAddress')}
|
||||
id="newIp"
|
||||
value={ipAddress}
|
||||
onChange={(value) => setIpAddress(value)}
|
||||
/>
|
||||
{error && (
|
||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
||||
{t('invalidAddress')}
|
||||
</p>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
/>
|
||||
<MainButton
|
||||
type="submit"
|
||||
size="small"
|
||||
title={t('save')}
|
||||
onClick={handleSubmit}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
|
||||
import { useModalContext } from "../../../store"
|
||||
import { useIpStore } from "../../../store"
|
||||
import { MainButton } from "../../UI/buttons/mainButton"
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
type Props={
|
||||
message: string
|
||||
}
|
||||
|
||||
export const ConnectionError = ({message}: Props)=>{
|
||||
const {closeModal} = useModalContext()
|
||||
const { setIpAddress } = useIpStore();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
const close = ()=>{
|
||||
setIpAddress('localhost')
|
||||
closeModal()
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalContainer>
|
||||
<ModalTitle>
|
||||
<h3>{t('connectionLost')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent>
|
||||
<ModalText>
|
||||
<h3>{t('serverDisconnected')}</h3>
|
||||
<p>{t('checkAddressAndRetry')}</p>
|
||||
</ModalText>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title={t('close')}
|
||||
onClick={close}
|
||||
size="small"
|
||||
/>
|
||||
<MainButton
|
||||
title={t('retry')}
|
||||
onClick={closeModal}
|
||||
size="small"
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,143 @@
|
|||
import React, {useEffect, useState} from 'react';
|
||||
import {ButtonContainer, ModalContainer, ModalContent, ModalTitle} from "../style";
|
||||
import {MainButton} from "../../UI/buttons/mainButton";
|
||||
import {ModalMap} from "../windowsType";
|
||||
import {useModalContext} from "../../../store";
|
||||
import styled from "styled-components";
|
||||
import { useSettingsStore } from '../../../store/settings/settingsStore';
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
const StyledTableList = styled.ul`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 0 40px;
|
||||
list-style-type: none;
|
||||
`
|
||||
const StyledTableItem = styled.li`
|
||||
width: 160px;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
background-color: ${props => props.theme.accent};
|
||||
color: ${props => props.theme.primary};
|
||||
border: 1px solid ${props => props.theme.primary};
|
||||
|
||||
&:first-child {
|
||||
border-radius: 16px 0 0 16px;
|
||||
}
|
||||
&:last-child {
|
||||
border-radius: 0 16px 16px 0;
|
||||
}
|
||||
&.off{
|
||||
background-color: ${props => props.theme.offlineColor};
|
||||
}
|
||||
`
|
||||
const CustomizeNodes = () => {
|
||||
const { openModal } = useModalContext();
|
||||
const pendingChanges = useSettingsStore(state => state.pendingChanges);
|
||||
const updateSetting = useSettingsStore(state => state.updateSetting);
|
||||
const getSetting = useSettingsStore(state => state.getSetting);
|
||||
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
||||
const {t} = useTranslation('common');
|
||||
const [dragIndex, setDragIndex] = useState<number | null>(null);
|
||||
|
||||
// Получаем текущие значения из хранилища
|
||||
const initialColumns = getSetting('table-columns-order').currentValue as string[];
|
||||
const disabledColumns = getSetting('table-columns-disabled').currentValue as number[];
|
||||
const items = pendingChanges['table-columns-order'] ?? initialColumns;
|
||||
const disabledItems = pendingChanges['table-columns-disabled'] ?? disabledColumns;
|
||||
|
||||
// Drag & Drop
|
||||
const handleDragStart = (index: number) => {
|
||||
setDragIndex(index);
|
||||
};
|
||||
const handleDragOver = (index: number) => {
|
||||
if (dragIndex === null || index === dragIndex) return;
|
||||
|
||||
const newItems = [...items];
|
||||
const [movedItem] = newItems.splice(dragIndex, 1);
|
||||
newItems.splice(index, 0, movedItem);
|
||||
|
||||
updateSetting('table-columns-order', newItems);
|
||||
setDragIndex(index);
|
||||
};
|
||||
const handleDrop = () => {
|
||||
setDragIndex(null);
|
||||
};
|
||||
|
||||
// Переключение состояния
|
||||
const handleSwitch = (index: number) => {
|
||||
const newDisabledItems = disabledItems.includes(index)
|
||||
? disabledItems.filter((i: number) => i !== index)
|
||||
: [...disabledItems, index];
|
||||
|
||||
updateSetting('table-columns-disabled', newDisabledItems);
|
||||
};
|
||||
|
||||
// Закрытие с сохранением
|
||||
const handleSave = () => {
|
||||
openModal(ModalMap['10']);
|
||||
};
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
initializeSettings();
|
||||
}, [initializeSettings]);
|
||||
|
||||
const getItemText = (item: string) => {
|
||||
if (item === 'Онлайн') return t('settings-status');
|
||||
if (item === 'Имя') return t('settings-name');
|
||||
if (item === 'Производитель') return t('settings-vendor');
|
||||
if (item === 'IP адрес') return t('settings-ipAddress');
|
||||
if (item === 'Открытые порты') return t('settings-openPorts');
|
||||
if (item === 'Последняя активность') return t('settings-lastActivity');
|
||||
if (item === 'Заметка') return t('settings-note');
|
||||
if (item === 'Действие') return t('settings-action');
|
||||
return item;
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalContainer style={{ height: '35vh', width: '80vw'}}>
|
||||
<ModalTitle>
|
||||
<h3>{t('customizeTable-title')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{ display: 'flex', height: "100%", gap: '24px', padding: 0 }}>
|
||||
<StyledTableList>
|
||||
{items.map((item:string, index:number) => (
|
||||
<StyledTableItem
|
||||
key={index}
|
||||
className={disabledItems.includes(index) ? 'off' : ''}
|
||||
draggable
|
||||
onDragStart={() => handleDragStart(index)}
|
||||
onDragOver={(e) => {
|
||||
e.preventDefault();
|
||||
handleDragOver(index);
|
||||
}}
|
||||
onDragEnd={handleDrop}
|
||||
onClick={() => handleSwitch(index)}
|
||||
>
|
||||
{getItemText(item)}
|
||||
</StyledTableItem>
|
||||
))}
|
||||
</StyledTableList>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
type="button"
|
||||
size="small"
|
||||
title={t('common-back')}
|
||||
onClick={handleSave}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomizeNodes;
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
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";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
type Props={
|
||||
address: string,
|
||||
onClose: () => void
|
||||
}
|
||||
|
||||
export const DisconnectConfirm = ({address, onClose}: Props)=>{
|
||||
const { ipAddress } = useIpStore();
|
||||
const theme = useTheme()
|
||||
const [isLoading, setIsLoading] = useState(false);
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
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>{t('wifiDisconnect-title')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent>
|
||||
<ModalText>
|
||||
<h3>{t('wifiDisconnect-confirm')}</h3>
|
||||
<p>{t('wifiDisconnect-message1')} + {address}. {t('wifiDisconnect-message2')}</p>
|
||||
<p>Вы собираетесь отключиться от сети "{address}". Все активные подключения будут прерваны.</p>
|
||||
</ModalText>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title={t('cancel')}
|
||||
onClick={onClose}
|
||||
size="small"
|
||||
/>
|
||||
<MainButton
|
||||
title={t('wifiDisconnect-disconnect')}
|
||||
color={theme.errorColor}
|
||||
hoverColor={theme.errorColor}
|
||||
onClick={handleDisconnect}
|
||||
size="small"
|
||||
/>
|
||||
</ButtonContainer>
|
||||
{isLoading && <RouterLoader loaderText={t('wifiDisconnect-progress')}/>}
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
export {IpConfirm} from './ipConfirm'
|
||||
export {ConnectionError} from './connectionError'
|
||||
export {AddDevice} from './addDevice'
|
||||
export {RightsConfirm} from './rightsConfirm'
|
||||
|
|
@ -0,0 +1,223 @@
|
|||
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";
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
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();
|
||||
const {t} = useTranslation('common');
|
||||
|
||||
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={t('ipChange-checkingIp')} />
|
||||
) : (
|
||||
<ModalContainer {...(checkStatus ? { height: "unset" } : {})}>
|
||||
<ModalTitle>
|
||||
<h3>{t('ipChange-title')}</h3>
|
||||
</ModalTitle>
|
||||
{checkStatus ? (
|
||||
<ModalContent>
|
||||
<Information>
|
||||
<h3>{t('ipChange-generalInfo')}</h3>
|
||||
<Info>
|
||||
<p>{t('ipChange-interface')}: {ipData.device}</p>
|
||||
<p>--{'>'}</p>
|
||||
<p>{ipData.connection}</p>
|
||||
</Info>
|
||||
<h3>{t('ipChange-ipInfo')}</h3>
|
||||
<DataContainer>
|
||||
<CurrentData>
|
||||
<p>{t('ipChange-currentIp')}: {ipData.currentIp}</p>
|
||||
<p>{t('ipChange-currentGateway')}: {ipData.currentGate}</p>
|
||||
<p>{t('ipChange-currentDns')}: {ipData.currentDns}</p>
|
||||
</CurrentData>
|
||||
|
||||
<NewData>
|
||||
<p>{t('ipChange-newIp')}: {ipData.newIp}</p>
|
||||
<p>{t('ipChange-newGateway')}: {ipData.newGate}</p>
|
||||
<p>{t('ipChange-newDns')}: {ipData.newDns}</p>
|
||||
</NewData>
|
||||
</DataContainer>
|
||||
</Information>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title={t('cancel')}
|
||||
size='small'
|
||||
onClick={() => cancelIp(currentIp)}
|
||||
/>
|
||||
<MainButton
|
||||
title={t('save')}
|
||||
size='small'
|
||||
onClick={() => saveIp(newIP)}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ModalContent>
|
||||
) : (
|
||||
<>
|
||||
<ModalContent>
|
||||
<ModalText>
|
||||
{isAdmin ? (
|
||||
<>
|
||||
<h3>{t('ipChange-checkError')}</h3>
|
||||
<ErrorMessage>{message}</ErrorMessage>
|
||||
</>
|
||||
):(
|
||||
<h3>{t('ipChange-adminRequired')}</h3>
|
||||
)}
|
||||
</ModalText>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title={t('close')}
|
||||
size='small'
|
||||
onClick={smoothClose}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</>
|
||||
)}
|
||||
</ModalContainer>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,132 @@
|
|||
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"
|
||||
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||
|
||||
interface Props {
|
||||
onClose: () => void;
|
||||
}
|
||||
|
||||
export const RightsConfirm = ({ onClose }: Props) => {
|
||||
|
||||
const { ipAddress } = useIpStore();
|
||||
const { isAdmin } = useAdminStore()
|
||||
const checkAdmin = useCheckAdmin();
|
||||
const [error, setError] = useState(false)
|
||||
const {t} = useTranslation('common');
|
||||
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: t('auth-successLogin'),
|
||||
status: true
|
||||
}));
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
onClose();
|
||||
}else{
|
||||
setError(true)
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(t('auth-error'), err);
|
||||
}
|
||||
}
|
||||
|
||||
const logout = async () => {
|
||||
await NetworkService.logout(ipAddress);
|
||||
await checkAdmin();
|
||||
setSuccess(prevData => ({
|
||||
...prevData,
|
||||
message: t('auth-successLogout'),
|
||||
status: true
|
||||
}));
|
||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||
onClose();
|
||||
}
|
||||
|
||||
return (
|
||||
<ModalContainer style={!isAdmin ? {height: '250px'} : {height: 'unset'}}>
|
||||
{!success.status ? (
|
||||
!isAdmin ? (
|
||||
<>
|
||||
<ModalTitle>
|
||||
<h3>{t('auth-getAccess')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '10px'}}>
|
||||
<h3 style={{marginLeft: '5px', fontSize: '14px'}}>{t('auth-enableAdmin')}</h3>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText={t('auth-enterAdminPass')}
|
||||
id="password"
|
||||
value={user.password}
|
||||
onChange={(value) => handleChange('password', value)}
|
||||
/>
|
||||
|
||||
{error && (
|
||||
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
|
||||
{t('auth-wrongPass')}
|
||||
</p>
|
||||
)}
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton size="small" title={t('cancel')} onClick={onClose} />
|
||||
<MainButton size="small" title={t('submit')} onClick={() => login(user)} />
|
||||
</ButtonContainer>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
<ModalTitle>
|
||||
<h3>{t('auth-switchToLimited')}</h3>
|
||||
</ModalTitle>
|
||||
|
||||
<ModalContent>
|
||||
<p>{t('auth-limitedModeDesc')}</p>
|
||||
</ModalContent>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton size="small" title={t('cancel')} onClick={onClose} />
|
||||
<MainButton size="small" title={t('auth-restrictAccess')} 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'}}>{t('auth-closeHint')}</p>
|
||||
<MainButton size="small" title={t('close')} onClick={onClose} style={{width: '30%'}}/>
|
||||
</ModalContainer>
|
||||
)}
|
||||
</ModalContainer>
|
||||
)
|
||||
}
|
||||
91
FRONT/DEV/src/components/modalwindows/modalWindow.tsx
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
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";
|
||||
import {Settings} from "../settings/Settings";
|
||||
import CustomizeNodes from "./modalContainers/customizeNodes";
|
||||
|
||||
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} />
|
||||
)}
|
||||
{type === ModalMap['10'] && (
|
||||
<Settings
|
||||
onClose={closeLoader}
|
||||
/>
|
||||
)}
|
||||
{type === ModalMap['11'] && (
|
||||
<CustomizeNodes />
|
||||
)}
|
||||
</ModalWindow >
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
71
FRONT/DEV/src/components/modalwindows/style.ts
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
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, width?: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: ${(props) => props.width ? props.width : '520px'};
|
||||
`
|
||||
|
||||
export const ModalTitle = styled.div`
|
||||
padding: 20px;
|
||||
border-bottom: 1px solid #9D9D9D;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
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;
|
||||
`
|
||||
15
FRONT/DEV/src/components/modalwindows/windowsType.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
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',
|
||||
"10": 'settings',
|
||||
'11': 'customNodes',
|
||||
}
|
||||
200
FRONT/DEV/src/components/register.tsx
Normal file
|
|
@ -0,0 +1,200 @@
|
|||
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"
|
||||
import {useTranslation} from "../utils/i18n/hooks";
|
||||
|
||||
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 {t} = useTranslation('common')
|
||||
|
||||
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" />
|
||||
<h3>{t('operatorConfig-title')}</h3>
|
||||
{ipAddress != 'localhost' &&
|
||||
<p>{ipAddress}</p>
|
||||
}
|
||||
</Title>
|
||||
|
||||
<Content>
|
||||
<div>
|
||||
<p>{t('operatorConfig-instruction')}</p>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText={t('operatorConfig-passwordPlaceholder')}
|
||||
id="password"
|
||||
value={user.password}
|
||||
onChange={(value) => handleChange('password', value)}
|
||||
/>
|
||||
|
||||
<Input
|
||||
type="password"
|
||||
placeholderText={t('operatorConfig-repeatPasswordPlaceholder')}
|
||||
id="repeatPassword"
|
||||
value={repeatPassword}
|
||||
onChange={(value) => handleChange('repeatPassword', value)}
|
||||
style={{ marginTop: '10px' }}
|
||||
/>
|
||||
{!passwordsMatch && (
|
||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
||||
{t('operatorConfig-passwordMismatch')}
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</Content>
|
||||
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
style={{ width: '100%' }}
|
||||
title={t('operatorConfig-submit')}
|
||||
type="submit"
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
|
||||
<Menu>
|
||||
{ipAddress != 'localhost' &&
|
||||
<MainButton
|
||||
title={t('common-back')}
|
||||
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)} />
|
||||
)}
|
||||
</>
|
||||
);
|
||||
}
|
||||