фронт
This commit is contained in:
parent
eb1f18f53e
commit
d67ea17fd7
160 changed files with 2611 additions and 5680 deletions
|
|
@ -1,69 +1,213 @@
|
|||
import styled from "styled-components"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
|
||||
const Container = styled.div`
|
||||
height: 100%;
|
||||
import { MainButton } from "../../../UI/buttons/mainButton"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import styled from "styled-components"
|
||||
import ThemeButton from "../../../UI/buttons/toggletheme"
|
||||
|
||||
type Props = {
|
||||
from: 'reg' | 'helloWindow' | 'workSpace'
|
||||
onBack?: () => void
|
||||
}
|
||||
|
||||
const Wrapper = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 25px;
|
||||
text-align: center;
|
||||
|
||||
h1, h2, h3, p{
|
||||
margin: 0;
|
||||
}
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: ${(props => props.theme.background)};
|
||||
padding: 50px;
|
||||
box-sizing: border-box;
|
||||
`
|
||||
|
||||
const Content = styled.div`
|
||||
max-width: 450px;
|
||||
max-width: 1200px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 25px 50px;
|
||||
background: ${(props => props.theme.secondary)};
|
||||
border-radius: 18px;
|
||||
`
|
||||
|
||||
const Listing = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
`
|
||||
|
||||
const List = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
margin-left: 10px;
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
font-size: 20px;
|
||||
`
|
||||
|
||||
const SubTitle = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
p{
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const Links = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
`
|
||||
|
||||
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: center;
|
||||
margin-top: 50px;
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justifi-content: flex-start;
|
||||
align-items: center;
|
||||
gap: 100px;
|
||||
button{
|
||||
width: 250px;
|
||||
`
|
||||
|
||||
const MainTitle = styled.div`
|
||||
width: 100%;
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 50px;
|
||||
box-sizing: border-box;
|
||||
|
||||
img{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
`
|
||||
|
||||
const Row = styled.div`
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
align-items: flex-end;
|
||||
justify-content: center;
|
||||
margin-left: 50px;
|
||||
`
|
||||
|
||||
const Column = styled.div`
|
||||
const Text = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 30px;
|
||||
`
|
||||
|
||||
export default function About() {
|
||||
const navigate = useNavigate()
|
||||
const redirect = (path: string) => {
|
||||
navigate(path)
|
||||
export default function About({ from, onBack }: Props) {
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
const getBackHandler = () => {
|
||||
switch (from) {
|
||||
case 'reg':
|
||||
return onBack
|
||||
case 'helloWindow':
|
||||
return () => setWorkModule(workSpaceNames[1])
|
||||
case 'workSpace':
|
||||
return () => setWorkModule(workSpaceNames[3])
|
||||
default:
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
const backHandler = getBackHandler()
|
||||
const showBackButton = from !== 'reg' || (from === 'reg' && onBack)
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Wrapper>
|
||||
<Content>
|
||||
<Row>
|
||||
<h1>GoNetMan</h1>
|
||||
<p>v 0.0.1</p>
|
||||
</Row>
|
||||
|
||||
<Column>
|
||||
<h3>О программе</h3>
|
||||
GoNetMan - Это приложение представляющее собой панель управления сетевыми настройками в удобном и понятном графическом виде.
|
||||
</Column>
|
||||
<MainTitle>
|
||||
<img src="./assets/images/gossys.png" alt="" />
|
||||
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
|
||||
|
||||
</MainTitle>
|
||||
|
||||
<p>Copyright © ООО "Гибридные офисные системы"</p>
|
||||
|
||||
<button onClick={()=>redirect('/')}>В меню</button>
|
||||
<Text>
|
||||
|
||||
<Title>
|
||||
<p>Приложение ГоNets является минимально работоспособным кроссплатформенным продуктом (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
||||
</Title>
|
||||
|
||||
|
||||
<Listing>
|
||||
<p>Данное приложение умеет:</p>
|
||||
<List>
|
||||
<p>- Отображать в графическом виде активные сетевые ресурсы</p>
|
||||
<p>- Настраивать сетевые параметры локального и удаленных сетевых ресурсов</p>
|
||||
<p>- Отображать и изменять взаимодействие сетевых ресурсов с установленным Клиентом ГоNets</p>
|
||||
</List>
|
||||
</Listing>
|
||||
|
||||
<SubTitle>
|
||||
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
|
||||
<p>Для коммерческого использования приобретите версию ГоNets-PRO (Подробная информация на странице продукта)</p>
|
||||
</SubTitle>
|
||||
|
||||
<Links>
|
||||
<LinkBlock>
|
||||
<p>Ссылка на домашнюю страницу:</p>
|
||||
<a href="https://gossys.dev/pages/projects/gonets">https://gossys.dev/pages/projects/gonets</a>
|
||||
</LinkBlock>
|
||||
|
||||
<LinkBlock>
|
||||
<p>Инструкции и информация:</p>
|
||||
<a href="https://wiki.gossys.dev/ru/gonets">https://wiki.gossys.dev/ru/gonets</a>
|
||||
</LinkBlock>
|
||||
|
||||
<LinkColumn>
|
||||
<p>Распространяется под лицензией GosysPUB текущей версии.</p>
|
||||
<LinkBlock>
|
||||
<p>Полный текст актуальной лицензии:</p>
|
||||
<a href="https://wiki.gossys.dev/ru/lic/GosysPUB">https://wiki.gossys.dev/ru/lic/GosysPUB</a>
|
||||
</LinkBlock>
|
||||
</LinkColumn>
|
||||
|
||||
</Links>
|
||||
|
||||
<Copyright>
|
||||
<p>Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.</p>
|
||||
</Copyright>
|
||||
|
||||
</Text>
|
||||
|
||||
|
||||
<ButtonContainer>
|
||||
{showBackButton && backHandler && (
|
||||
<MainButton
|
||||
title="Назад"
|
||||
onClick={backHandler}
|
||||
style={{ marginRight: '10px' }}
|
||||
/>
|
||||
)}
|
||||
|
||||
{from == 'reg' &&
|
||||
<ThemeButton />
|
||||
}
|
||||
</ButtonContainer>
|
||||
</Content>
|
||||
</Container>
|
||||
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
|
@ -1,81 +1,82 @@
|
|||
import styled from "styled-components"
|
||||
import { MainButton } from "../../../UI/buttons/mainButton"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
import styled from "styled-components"
|
||||
import { MainButton } from "../../../UI/buttons/mainButton"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { buttonNames } from "../../../../const/buttons/names"
|
||||
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${(props => props.theme.background)};
|
||||
background-image: url('./assets/images/back1.svg');
|
||||
background-size: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom;
|
||||
`
|
||||
const Wrapper = styled.div`
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: ${(props => props.theme.background)};
|
||||
background-image: url('./assets/images/back1.svg');
|
||||
background-size: 100%;
|
||||
background-repeat: no-repeat;
|
||||
background-position: bottom;
|
||||
`
|
||||
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 52px;
|
||||
margin-bottom: 100px;
|
||||
`
|
||||
const Container = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 52px;
|
||||
margin-bottom: 100px;
|
||||
`
|
||||
|
||||
const ButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
gap: 75px;
|
||||
flex-direction: column;
|
||||
`
|
||||
const ButtonWrapper = styled.div`
|
||||
display: flex;
|
||||
gap: 75px;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-direction: column;
|
||||
`
|
||||
const ButtonContainer = styled.div`
|
||||
display: flex;
|
||||
gap: 15px;
|
||||
flex-direction: column;
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
const Title = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
|
||||
h1{
|
||||
font-size: 52px;
|
||||
}
|
||||
`
|
||||
|
||||
h1{
|
||||
font-size: 52px;
|
||||
}
|
||||
`
|
||||
export const HelloWindow = () => {
|
||||
|
||||
export const HelloWindow = () => {
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
return (
|
||||
<Wrapper>
|
||||
<Container className="unselectable">
|
||||
<Title>
|
||||
<img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/>
|
||||
</Title>
|
||||
return (
|
||||
<Wrapper>
|
||||
<Container className="unselectable">
|
||||
<Title>
|
||||
<img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/>
|
||||
</Title>
|
||||
|
||||
|
||||
<ButtonWrapper>
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title="Настройки сети"
|
||||
icon={buttonNames[3]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
onClick={() => setWorkModule(workSpaceNames[3])}
|
||||
/>
|
||||
<MainButton
|
||||
title="О программе"
|
||||
icon={buttonNames[4]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ButtonWrapper>
|
||||
</Container>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
<ButtonWrapper>
|
||||
<ButtonContainer>
|
||||
<MainButton
|
||||
title="Настройки сети"
|
||||
icon={buttonNames[3]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
onClick={() => setWorkModule(workSpaceNames[3])}
|
||||
/>
|
||||
<MainButton
|
||||
title="О программе"
|
||||
icon={buttonNames[4]}
|
||||
iconSize={{ width: '32', height: '32' }}
|
||||
onClick={()=>setWorkModule(workSpaceNames[4])}
|
||||
/>
|
||||
</ButtonContainer>
|
||||
</ButtonWrapper>
|
||||
</Container>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
|
||||
import {DeviceUser} from "../../../../../../store/useDeviceStore";
|
||||
|
||||
const ScanBtn = styled.button`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
transition: .4s ease;
|
||||
&:hover{
|
||||
transform: scale(1.03);
|
||||
}
|
||||
`
|
||||
|
||||
interface ListUsersProps {
|
||||
loading: boolean;
|
||||
handleScan: ()=>void;
|
||||
devices: DeviceUser[];
|
||||
scanInitiated: boolean;
|
||||
}
|
||||
|
||||
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
|
||||
|
||||
// Фильтр по статусу
|
||||
const activeDevices = devices.filter(device => device.status === 'online');
|
||||
const inactiveDevices = devices.filter(device => device.status !== 'online');
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScanBtn onClick={handleScan} disabled={loading}>
|
||||
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
</ScanBtn>
|
||||
|
||||
{devices.length > 0 ? (
|
||||
<div style={{
|
||||
display: 'flex',
|
||||
width: '70%',
|
||||
gap: '20px',
|
||||
}}>
|
||||
{/* Колонка активных устройств */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные устройства ({activeDevices.length})</h3>
|
||||
{activeDevices.map((device, index) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
status="online"
|
||||
device={device}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
{/* Колонка неактивных устройств */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ color: '#F44336', marginBottom:'12px' }}>Неактивные устройства ({inactiveDevices.length})</h3>
|
||||
{inactiveDevices.map((device, index) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
status="offline"
|
||||
device={device}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : scanInitiated ? (
|
||||
<p style={{ marginTop: '20px' }}>Устройства не найдены</p>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListUsers;
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import DeviceCard from "../../../../../UI/Dropdown/DeviceCard";
|
||||
import {DeviceUser} from "../../../../../../store/useDeviceStore";
|
||||
import TableScanner from "../../../../../UI/tables/TableScanner";
|
||||
|
||||
const ScanBtn = styled.button`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
transition: .4s ease;
|
||||
&:hover{
|
||||
transform: scale(1.03);
|
||||
}
|
||||
`
|
||||
|
||||
interface ListUsersProps {
|
||||
loading: boolean;
|
||||
handleScan: ()=>void;
|
||||
devices: DeviceUser[];
|
||||
scanInitiated: boolean;
|
||||
}
|
||||
|
||||
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<ScanBtn onClick={handleScan} disabled={loading}>
|
||||
{devices.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
</ScanBtn>
|
||||
|
||||
{devices.length > 0 ? (
|
||||
<TableScanner devices={devices} />
|
||||
) : scanInitiated ? (
|
||||
<p style={{ marginTop: '20px' }}>Устройства не найдены</p>
|
||||
) : null}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ListUsers;
|
||||
|
|
@ -0,0 +1,185 @@
|
|||
import { useIpStore } from "../../../../../store";
|
||||
import styled, { keyframes } from "styled-components";
|
||||
import { useState } from "react";
|
||||
import { NetworkService } from "../../../../../api/request";
|
||||
import ListUsers from "./modules/ListUsers";
|
||||
import TableUsers from "./modules/TableUsers";
|
||||
import DropdownType from "../../../../UI/Dropdown/DropdownType";
|
||||
import useNetworkStore from "../../../../../store/useDeviceStore";
|
||||
|
||||
type Option ={
|
||||
value: number,
|
||||
label: string,
|
||||
}
|
||||
|
||||
// Анимации
|
||||
const ring1 = keyframes`
|
||||
0% { width: 250px; height: 250px; }
|
||||
50% { width: 280px; height: 280px; }
|
||||
100% { width: 250px; height: 250px; }
|
||||
`;
|
||||
|
||||
const ring2 = keyframes`
|
||||
0% { width: 400px; height: 400px; }
|
||||
50% { width: 430px; height: 430px; }
|
||||
100% { width: 400px; height: 400px; }
|
||||
`;
|
||||
|
||||
const ring3 = keyframes`
|
||||
0% { width: 550px; height: 550px; }
|
||||
50% { width: 580px; height: 580px; }
|
||||
100% { width: 550px; height: 550px; }
|
||||
`;
|
||||
const dotAnimation = keyframes`
|
||||
0% { content: '.'; }
|
||||
33% { content: '..'; }
|
||||
66% { content: '...'; }
|
||||
100% { content: '.'; }
|
||||
`;
|
||||
|
||||
// Стили
|
||||
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content:${({scanInitiated, loading})=>(scanInitiated && !loading ? 'start' : 'center')};
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
background: ${(props) => props.theme.background};
|
||||
padding: 50px;
|
||||
box-sizing: border-box;
|
||||
gap: 24px;
|
||||
transition: justify-content .4s ease;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
position: relative;
|
||||
`;
|
||||
const TitleWrapper = styled.h2`
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 32px;
|
||||
`
|
||||
|
||||
const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
transition: .4s ease;
|
||||
transform: ${({scanInitiated,loading})=>(scanInitiated && !loading ? 'scale(0)' : 'scale(1)')};
|
||||
span {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
background: ${(props) => props.theme.accent};
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
&.center{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
||||
}
|
||||
&.ring1 {
|
||||
opacity: 0.12;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
animation: ${({ loading }) => (loading ? ring1 : "none")} 4s linear infinite;
|
||||
}
|
||||
|
||||
&.ring2 {
|
||||
opacity: 0.08;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
|
||||
}
|
||||
|
||||
&.ring3 {
|
||||
opacity: 0.04;
|
||||
width: 550px;
|
||||
height: 550px;
|
||||
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const LoadingText = styled.div`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
const ScanningText = styled.span`
|
||||
&::after {
|
||||
content: '.';
|
||||
animation: ${dotAnimation} 1.5s infinite;
|
||||
}
|
||||
`;
|
||||
|
||||
|
||||
export default function UserScanner() {
|
||||
const { ipAddress } = useIpStore();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { devices, setDevices } = useNetworkStore();
|
||||
const [scanInitiated, setScanInitiated] = useState(devices.length > 0);
|
||||
|
||||
// Сканирование
|
||||
const handleScan = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setScanInitiated(true);
|
||||
const res = await NetworkService.scanUser(ipAddress);
|
||||
setDevices(res.data.devices || []);
|
||||
console.log(res.data.devices);
|
||||
} catch (err) {
|
||||
console.error("Ошибка сканирования сети:", err);
|
||||
setDevices([])
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
// Способ отображения
|
||||
const viewOptions: Option[] = [
|
||||
{ value: 1, label: 'Таблица' },
|
||||
{ value: 2, label: 'Список' },
|
||||
];
|
||||
const [selectedView, setSelectedView] = useState<Option>(viewOptions[0]);
|
||||
const handleViewChange = (option: Option) => {
|
||||
setSelectedView(option);
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
||||
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование пользователей в сети</TitleWrapper>)}
|
||||
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
||||
<span className="center"></span>
|
||||
<span className="ring1"></span>
|
||||
<span className="ring2"></span>
|
||||
<span className="ring3"></span>
|
||||
</UserLocate>
|
||||
|
||||
{scanInitiated && !loading && (
|
||||
<DropdownType options={viewOptions} value={selectedView} onChange={handleViewChange} label="Способ отображения" />
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<LoadingText>
|
||||
Сканирование сети<ScanningText />
|
||||
</LoadingText>
|
||||
) : selectedView.value == 1 ? (
|
||||
<TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
|
||||
):(
|
||||
<ListUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,234 @@
|
|||
import { useIpStore } from "../../../../../store";
|
||||
import styled, { keyframes } from "styled-components";
|
||||
import React, { useState } from "react";
|
||||
import { NetworkService } from "../../../../../api/request";
|
||||
import useDeviceStore from "../../../../../store/useDeviceStore";
|
||||
import WifiDot, {WifiNetworkScanner} from "./modules/WifiDot";
|
||||
import ModalWifiScaner from "./modules/ModalWifiScaner";
|
||||
import useNetworkStore from "../../../../../store/useDeviceStore";
|
||||
|
||||
|
||||
type Option ={
|
||||
value: number,
|
||||
label: string,
|
||||
}
|
||||
|
||||
// Анимации
|
||||
const ring1 = keyframes`
|
||||
0% { width: 250px; height: 250px; }
|
||||
50% { width: 280px; height: 280px; }
|
||||
100% { width: 250px; height: 250px; }
|
||||
`;
|
||||
|
||||
const ring2 = keyframes`
|
||||
0% { width: 400px; height: 400px; }
|
||||
50% { width: 430px; height: 430px; }
|
||||
100% { width: 400px; height: 400px; }
|
||||
`;
|
||||
|
||||
const ring3 = keyframes`
|
||||
0% { width: 550px; height: 550px; }
|
||||
50% { width: 580px; height: 580px; }
|
||||
100% { width: 550px; height: 550px; }
|
||||
`;
|
||||
const dotAnimation = keyframes`
|
||||
0% { content: '.'; }
|
||||
33% { content: '..'; }
|
||||
66% { content: '...'; }
|
||||
100% { content: '.'; }
|
||||
`;
|
||||
|
||||
// Стили
|
||||
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content:${({scanInitiated, loading})=>(scanInitiated && !loading ? 'start' : 'center')};
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
background: ${(props) => props.theme.background};
|
||||
padding: 50px;
|
||||
box-sizing: border-box;
|
||||
gap: 24px;
|
||||
transition: justify-content .4s ease;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
`;
|
||||
const TitleWrapper = styled.h2`
|
||||
position: absolute;
|
||||
top: 24px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 32px;
|
||||
`
|
||||
|
||||
const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
border-radius: 50%;
|
||||
position: relative;
|
||||
transition: .4s ease;
|
||||
transform: ${({scanInitiated,loading})=>(scanInitiated && !loading ? 'scale(0)' : 'scale(1)')};
|
||||
span {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
background: ${(props) => props.theme.scannerColor};
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border-radius: 50%;
|
||||
&.center{
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
||||
}
|
||||
&.ring1 {
|
||||
opacity: 0.12;
|
||||
width: 250px;
|
||||
height: 250px;
|
||||
animation: ${({ loading }) => (loading ? ring1 : "none")} 4s linear infinite;
|
||||
}
|
||||
|
||||
&.ring2 {
|
||||
opacity: 0.08;
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
|
||||
}
|
||||
|
||||
&.ring3 {
|
||||
opacity: 0.04;
|
||||
width: 550px;
|
||||
height: 550px;
|
||||
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const LoadingText = styled.div`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
`;
|
||||
const ScanningText = styled.span`
|
||||
&::after {
|
||||
content: '.';
|
||||
animation: ${dotAnimation} 1.5s infinite;
|
||||
}
|
||||
`;
|
||||
const ScanBtn = styled.button<{loading: boolean}>`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
background-color: #4CAF50;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
transition: .4s ease;
|
||||
transform: ${({loading}) => (!loading ? "translateY(240px)" : "translateY(0)")};
|
||||
&:hover{
|
||||
transform: ${({loading}) => (!loading ? "translateY(240px)" : "translateY(0)")} scale(1.03);
|
||||
}
|
||||
`
|
||||
|
||||
const data = [
|
||||
{
|
||||
ssid: "Huawei",
|
||||
bssid: "wef",
|
||||
channel: 0,
|
||||
signal:2,
|
||||
encryption: "wef",
|
||||
frequency: "ewf",
|
||||
vendor: "striwefwng",
|
||||
hidden: false
|
||||
},
|
||||
{
|
||||
ssid: "Gu-2",
|
||||
bssid: "grgerger",
|
||||
channel: 0,
|
||||
signal: 2,
|
||||
encryption: "weergref",
|
||||
frequency: "eergewf",
|
||||
vendor: "grgregerge",
|
||||
hidden: false
|
||||
},
|
||||
{
|
||||
ssid: "Huawei_wifi2",
|
||||
bssid: "wqdqw",
|
||||
channel: 2,
|
||||
signal: 3,
|
||||
encryption: "wefwe",
|
||||
frequency: "wef",
|
||||
vendor: "fwefw",
|
||||
hidden: false
|
||||
},
|
||||
];
|
||||
|
||||
export function WifiScanner() {
|
||||
const {ipAddress} = useIpStore();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [scanInitiated, setScanInitiated] = useState(false);
|
||||
const {wifiNetworks, setWifiNetworks} = useNetworkStore();
|
||||
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
|
||||
|
||||
const handleScan = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
setScanInitiated(true);
|
||||
const res = await NetworkService.scanWifi(ipAddress);
|
||||
const networks = res.data.access_points || [];
|
||||
setWifiNetworks(networks);
|
||||
} catch (err) {
|
||||
console.error("Ошибка сканирования сети:", err);
|
||||
setWifiNetworks([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
||||
{(!scanInitiated || loading) && (
|
||||
<TitleWrapper>Сканирование Wi-Fi сети</TitleWrapper>
|
||||
)}
|
||||
|
||||
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
||||
<span className="center"></span>
|
||||
<span className="ring1"></span>
|
||||
<span className="ring2"></span>
|
||||
<span className="ring3"></span>
|
||||
{data.map((network) => (
|
||||
<WifiDot
|
||||
key={network.bssid}
|
||||
network={network}
|
||||
onSelect={setCurrentNetwork}
|
||||
/>
|
||||
))}
|
||||
</UserLocate>
|
||||
|
||||
{currentNetwork && (
|
||||
<ModalWifiScaner
|
||||
onClose={() => setCurrentNetwork(null)}
|
||||
network={currentNetwork}
|
||||
/>
|
||||
)}
|
||||
|
||||
{loading ? (
|
||||
<LoadingText>
|
||||
Сканирование сети <ScanningText/>
|
||||
</LoadingText>
|
||||
) : (
|
||||
<ScanBtn onClick={handleScan} disabled={loading} loading={loading}>
|
||||
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
</ScanBtn>
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
@ -0,0 +1,142 @@
|
|||
import React from 'react';
|
||||
import styled from 'styled-components';
|
||||
import {WifiNetworkScanner} from "./WifiDot";
|
||||
import {IconComponent} from "../../../../../../const/buttons/icons";
|
||||
import {buttonNames} from "../../../../../../const/buttons/names";
|
||||
const Modal = styled.div`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
background-color: ${(props) => props.theme.secondary};
|
||||
border-radius: 24px;
|
||||
padding: 20px 32px;
|
||||
top: 14%;
|
||||
right: 18%;
|
||||
position: absolute;
|
||||
z-index: 20;
|
||||
min-width: 210px;
|
||||
&:hover .close-btn{
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
`
|
||||
const CloseBtn = styled.div`
|
||||
position: absolute;
|
||||
top: -20px;
|
||||
right: -20px;
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
transition: all .4s ease;
|
||||
opacity: 0;
|
||||
visibility: visible;
|
||||
|
||||
&:hover {
|
||||
background-color: #d6d6d6;
|
||||
}
|
||||
`
|
||||
const ModalTitle = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
font-size: 20px;
|
||||
line-height: 1;
|
||||
margin-bottom: 32px;
|
||||
h4{
|
||||
font-weight: normal;
|
||||
}
|
||||
`
|
||||
const ModalContent = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
`
|
||||
const ModalRow = styled.div`
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 12px;
|
||||
width: 100%;
|
||||
font-size: 14px;
|
||||
h6{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 1em;
|
||||
}
|
||||
`
|
||||
|
||||
const SignalBarsWrapper = styled.div`
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 2px;
|
||||
height: 26px;
|
||||
margin-left: 8px;
|
||||
`;
|
||||
|
||||
const SignalBar = styled.div<{ active: boolean }>`
|
||||
width: 9px;
|
||||
background: ${({ active, theme }) => active ? theme.accent : '#ccc'};
|
||||
border-radius: 2px;
|
||||
|
||||
&:nth-child(1) { height: 8px; }
|
||||
&:nth-child(2) { height: 14px; }
|
||||
&:nth-child(3) { height: 22px; }
|
||||
`;
|
||||
|
||||
const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onClose: ()=>void }) => {
|
||||
return (
|
||||
<Modal>
|
||||
<CloseBtn className={'close-btn'} onClick={onClose}>
|
||||
<IconComponent name={buttonNames[12]} width="30" height="30" />
|
||||
</CloseBtn>
|
||||
<ModalTitle>
|
||||
<h4>{network.ssid}</h4>
|
||||
<SignalBarsWrapper>
|
||||
<SignalBar active={network.signal >= 1} />
|
||||
<SignalBar active={network.signal >= 2} />
|
||||
<SignalBar active={network.signal >= 3} />
|
||||
</SignalBarsWrapper>
|
||||
</ModalTitle>
|
||||
<ModalContent>
|
||||
<ModalRow>
|
||||
<h6>BSSID:</h6>
|
||||
<p>{network.bssid}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Производитель:</h6>
|
||||
<p>{network.vendor}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Сигнал:</h6>
|
||||
<p>{network.signal} dBm</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Канал:</h6>
|
||||
<p>{network.channel}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Шифрование:</h6>
|
||||
<p>{network.encryption}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Частота:</h6>
|
||||
<p>{network.frequency}</p>
|
||||
</ModalRow>
|
||||
<ModalRow>
|
||||
<h6>Скрытая сеть:</h6>
|
||||
<p>{network.hidden ? 'Да' : 'Нет'}</p>
|
||||
</ModalRow>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalWifiScaner;
|
||||
|
|
@ -0,0 +1,69 @@
|
|||
import React from 'react';
|
||||
import styled from "styled-components";
|
||||
|
||||
export interface WifiNetworkScanner {
|
||||
ssid: string;
|
||||
bssid: string;
|
||||
channel: number;
|
||||
signal: number;
|
||||
encryption: string;
|
||||
frequency: string;
|
||||
vendor: string;
|
||||
hidden: boolean;
|
||||
}
|
||||
|
||||
//Точка вайфайл
|
||||
const Dot = styled.div<{ top: string; left: string }>`
|
||||
position: absolute;
|
||||
top: ${props => props.top};
|
||||
left: ${props => props.left};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 18px;
|
||||
transform: translate(-50%, -50%);
|
||||
user-select: none;
|
||||
transition: .4s;
|
||||
text-wrap: nowrap;
|
||||
&:hover {
|
||||
transform: translate(-50%, -50%) scale(1.1);
|
||||
}
|
||||
`;
|
||||
|
||||
const WifiDot = ({ network, onSelect }: { network: WifiNetworkScanner, onSelect: (n: WifiNetworkScanner) => void }) => {
|
||||
const getPositionInRing = (bssid: string, ringSize: number) => {
|
||||
const hash = bssid.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
||||
const randomAngle = (hash % 360) * (Math.PI / 180);
|
||||
const innerRadius = ringSize * 0.4;
|
||||
const outerRadius = ringSize * 0.5;
|
||||
const randomValue = (hash % 1000) / 1000;
|
||||
const randomRadius = Math.sqrt(innerRadius * innerRadius + randomValue * (outerRadius * outerRadius - innerRadius * innerRadius));
|
||||
|
||||
return {
|
||||
x: Math.cos(randomAngle) * randomRadius,
|
||||
y: Math.sin(randomAngle) * randomRadius
|
||||
};
|
||||
};
|
||||
|
||||
const getPosition = (signal: number, bssid: string) => {
|
||||
const strength = Math.min(3, Math.max(1, signal));
|
||||
const ringSizes = { 1: 550, 2: 400, 3: 250 };
|
||||
const ringSize = ringSizes[strength as 1 | 2 | 3];
|
||||
const offset = getPositionInRing(bssid, ringSize);
|
||||
|
||||
return {
|
||||
top: `calc(50% - ${offset.y}px)`,
|
||||
left: `calc(50% - ${offset.x}px)`
|
||||
}
|
||||
};
|
||||
|
||||
const position = getPosition(network.signal, network.bssid);
|
||||
|
||||
return (
|
||||
<Dot top={position.top} left={position.left} onClick={() => onSelect(network)}>
|
||||
{network.ssid}
|
||||
</Dot>
|
||||
);
|
||||
};
|
||||
|
||||
export default WifiDot;
|
||||
|
|
@ -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);
|
||||
|
|
@ -93,7 +93,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
|
|||
|
||||
newSocket.onmessage = (event) => {
|
||||
const newData = JSON.parse(event.data);
|
||||
|
||||
|
||||
setData(() => [
|
||||
newData.activation,
|
||||
newData.deactivation,
|
||||
|
|
@ -126,7 +126,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
|
|||
const activationData = data[0].interfaces;
|
||||
|
||||
const wifi = activationData.find((el: any) => el.type === 'WIFI');
|
||||
|
||||
|
||||
setInterfaces(newInterfaces);
|
||||
setEdgeData(newEdgeData);
|
||||
if (wifi) { setWifi(wifi); }
|
||||
|
|
@ -192,13 +192,13 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
|
|||
}, [nodes, loader])
|
||||
|
||||
const onEdgesChange = useCallback(
|
||||
(changes: any) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
||||
[setEdges],
|
||||
(changes: any) => setEdges((eds) => applyEdgeChanges(changes, eds)),
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
const onConnect = useCallback(
|
||||
(connection: any) => setEdges((eds) => addEdge(connection, eds)),
|
||||
[setEdges],
|
||||
(connection: any) => setEdges((eds) => addEdge(connection, eds)),
|
||||
[setEdges],
|
||||
);
|
||||
|
||||
const handleNodesChange = (changes: any) => {
|
||||
|
|
@ -254,29 +254,29 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => {
|
|||
}, [isNullPos]);
|
||||
|
||||
return (
|
||||
<>
|
||||
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
style={{ background: '#F7F9FB' }}
|
||||
minZoom={0}
|
||||
maxZoom={4}
|
||||
fitView
|
||||
fitViewOptions={{ padding: 0.1 }}
|
||||
attributionPosition="bottom-left"
|
||||
snapToGrid
|
||||
snapGrid={[20, 20]}
|
||||
panOnDrag={!isPanDisabled}
|
||||
nodeTypes={nodeTypes}
|
||||
proOptions={proOptions}
|
||||
>
|
||||
<Background gap={20} size={1} bgColor={theme.background} />
|
||||
<Controls fitViewOptions={{ padding: 0.1 }} />
|
||||
</ReactFlow>
|
||||
</>
|
||||
<>
|
||||
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
onNodesChange={handleNodesChange}
|
||||
onEdgesChange={onEdgesChange}
|
||||
onConnect={onConnect}
|
||||
style={{ background: '#F7F9FB' }}
|
||||
minZoom={0}
|
||||
maxZoom={4}
|
||||
fitView
|
||||
fitViewOptions={{ padding: 0.1 }}
|
||||
attributionPosition="bottom-left"
|
||||
snapToGrid
|
||||
snapGrid={[20, 20]}
|
||||
panOnDrag={!isPanDisabled}
|
||||
nodeTypes={nodeTypes}
|
||||
proOptions={proOptions}
|
||||
>
|
||||
<Background gap={20} size={1} bgColor={theme.background} />
|
||||
<Controls fitViewOptions={{ padding: 0.1 }} />
|
||||
</ReactFlow>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -41,8 +41,8 @@ export const ToolBar = () => {
|
|||
const toolBarMenu = toolBar?.toolSpace
|
||||
|
||||
const { openModal } = useModalContext()
|
||||
const {isAdmin} = useAdminStore()
|
||||
const {setWorkModule} = useWorkSpaceStore()
|
||||
const { isAdmin } = useAdminStore()
|
||||
const { setWorkModule, workModule } = useWorkSpaceStore()
|
||||
|
||||
return (
|
||||
<Container>
|
||||
|
|
@ -50,13 +50,22 @@ export const ToolBar = () => {
|
|||
{toolBarMenu && toolBarMenu.map((menu) => (
|
||||
<ToolBarButton menuId={menu.id} key={menu.id} />
|
||||
))}
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[14]} width="30" height="30" onClick={() => setWorkModule(workSpaceNames[5])} />
|
||||
</IconContainer>
|
||||
</Button>
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[14]} width="30" height="30" onClick={() => setWorkModule(workSpaceNames[6])} />
|
||||
</IconContainer>
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
|
||||
<ButtonContainer>
|
||||
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={isAdmin ? buttonNames[10] : buttonNames[13]} width="30" height="30" onClick={() => openModal(ModalMap['5'])}/>
|
||||
<IconComponent name={isAdmin ? buttonNames[10] : buttonNames[13]} width="30" height="30" onClick={() => openModal(ModalMap['5'])} />
|
||||
</IconContainer>
|
||||
</Button>
|
||||
|
||||
|
|
@ -64,7 +73,12 @@ export const ToolBar = () => {
|
|||
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[4]} width="30" height="30" onClick={()=>setWorkModule(workSpaceNames[2])}/>
|
||||
<IconComponent name={buttonNames[4]} width="30" height="30"
|
||||
onClick={() => workModule === workSpaceNames[3]
|
||||
? setWorkModule(workSpaceNames[2])
|
||||
: setWorkModule(workSpaceNames[4])
|
||||
}
|
||||
/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const Container = styled.div`
|
|||
align-items: center;
|
||||
gap: 15px;
|
||||
padding: 20px;
|
||||
|
||||
width: 210px;
|
||||
p{
|
||||
margin: 0;
|
||||
}
|
||||
|
|
@ -115,12 +115,12 @@ 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);
|
||||
}, 500
|
||||
);
|
||||
testSocket.close();
|
||||
resolve(false);
|
||||
}, 500
|
||||
);
|
||||
|
||||
testSocket.onopen = () => {
|
||||
clearTimeout(timeout);
|
||||
|
|
@ -156,8 +156,8 @@ export const DevicesBar = () => {
|
|||
};
|
||||
|
||||
const handleAddDeviceComplete = (data: string) => {
|
||||
addDevice(data)
|
||||
setIpAddress(data)
|
||||
addDevice(data);
|
||||
switchWs(data);
|
||||
};
|
||||
|
||||
const handleDelete = (number: number, adress: string) => {
|
||||
|
|
@ -170,7 +170,6 @@ export const DevicesBar = () => {
|
|||
};
|
||||
|
||||
const switchWs = async (ip: string) => {
|
||||
// Проверяем доступность WebSocket
|
||||
const isAvailable = await checkWebSocketAvailability(ip);
|
||||
if (!isAvailable) {
|
||||
openModal(ModalMap['3'], {
|
||||
|
|
@ -179,7 +178,6 @@ export const DevicesBar = () => {
|
|||
return;
|
||||
}
|
||||
|
||||
// Если WebSocket доступен, обновляем IP и переключаем модуль
|
||||
setIpAddress(ip);
|
||||
setWorkModule(workSpaceNames[3]);
|
||||
};
|
||||
|
|
@ -198,12 +196,12 @@ export const DevicesBar = () => {
|
|||
|
||||
<DeviceBlock>
|
||||
<DeviceControl className="unselectable" onClick={() => switchWs(currentDevice.address)}>
|
||||
<p>Текущее устройство</p>
|
||||
<p>Корневой адрес</p>
|
||||
</DeviceControl>
|
||||
{currentDevice.hostname == '' ? (
|
||||
<Text className="paddingP">Адрес: Загрузка<AnimDot /></Text>
|
||||
<Text className="paddingP">Название: Загрузка<AnimDot /></Text>
|
||||
) : (
|
||||
<p className="paddingP">Адрес: {currentDevice.hostname}</p>
|
||||
<p className="paddingP">Название: {currentDevice.hostname}</p>
|
||||
)}
|
||||
</DeviceBlock>
|
||||
|
||||
|
|
|
|||
|
|
@ -3,11 +3,15 @@ import { useToolBar } from "../../../../hooks/useToolBar"
|
|||
import { Space } from "../../../../const/menus/sideBar/components"
|
||||
import styled from "styled-components"
|
||||
|
||||
const Container = styled.div`
|
||||
const Container = styled.div<{isOpen: boolean}>`
|
||||
background-color: ${(props) => props.theme.secondary};
|
||||
width: 250px;
|
||||
font-size: 14px;
|
||||
flex: 3;
|
||||
|
||||
overflow: hidden;
|
||||
max-width: ${({isOpen})=>(isOpen ? '250px' : '0')};
|
||||
transition: max-width .4s ease;
|
||||
`
|
||||
|
||||
export const ToolSpace = () => {
|
||||
|
|
@ -15,12 +19,12 @@ export const ToolSpace = () => {
|
|||
const name = toolBar?.current || ''
|
||||
const { isActive, isOpen } = useToolBar(name)
|
||||
|
||||
if (!isActive || !isOpen) {
|
||||
if (!isActive) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<Container>
|
||||
<Container isOpen={isOpen}>
|
||||
<Space name={name} />
|
||||
</Container>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,9 @@ import { useWorkSpaceStore } from "../../store";
|
|||
import { workSpaceNames } from "../../const/workspace/names";
|
||||
import { Header } from "./modules/appModules/header";
|
||||
import { ReactFlowProvider } from "@xyflow/react";
|
||||
import About from "./modules/appModules/about";
|
||||
import UserScanner from "./modules/appModules/usersScanner/userScanner";
|
||||
import {WifiScanner} from "./modules/appModules/wifiScanner/WifiScanner";
|
||||
|
||||
const Section = styled.div`
|
||||
display: flex;
|
||||
|
|
@ -54,16 +57,32 @@ export default function WorkSpace() {
|
|||
setIpAddress(currentWs);
|
||||
}, [currentWs]);
|
||||
|
||||
console.log('aboba');
|
||||
|
||||
localStorage.getItem('workSpace-storage')
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<FlowContainer>
|
||||
<SideBar />
|
||||
|
||||
{workModule === workSpaceNames[1] && <HelloWindow /> }
|
||||
{workModule === workSpaceNames[1] && <HelloWindow />}
|
||||
|
||||
{workModule === workSpaceNames[2] && (
|
||||
<About
|
||||
from={"workSpace"}
|
||||
/>
|
||||
)}
|
||||
|
||||
{workModule === workSpaceNames[4] && (
|
||||
<About
|
||||
from={"helloWindow"}
|
||||
/>
|
||||
)}
|
||||
{workModule === workSpaceNames[5] && (
|
||||
<UserScanner />
|
||||
)}
|
||||
{workModule === workSpaceNames[6] && (
|
||||
<WifiScanner />
|
||||
)}
|
||||
{workModule === workSpaceNames[3] &&
|
||||
<WorkSection>
|
||||
<Header />
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue