Релиз 0.0.3-2
This commit is contained in:
parent
5287a64587
commit
0a404ac220
173 changed files with 369 additions and 16817 deletions
|
|
@ -1,13 +0,0 @@
|
|||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>GoNets</title>
|
||||
<link rel="icon" type="image/x-icon" href="/assets/icon-4fEsvohP.png" />
|
||||
<script type="module" crossorigin src="/assets/index-QSWEVNb0.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-DWFkiel1.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -157,4 +157,20 @@ 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,6 +5,7 @@ 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;
|
||||
|
|
@ -74,8 +75,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();
|
||||
|
|
@ -87,7 +88,7 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
|
||||
|
||||
return (
|
||||
<CardWrapper status={status} onClick={toggleExpanded}>
|
||||
<CardWrapper status={status} onClick={()=>setIsExpanded(!isExpanded)}>
|
||||
<CardHeader status={status}>
|
||||
<p>
|
||||
MAC: {device.mac}
|
||||
|
|
@ -100,7 +101,7 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
<CardContent isExpanded={isExpanded}>
|
||||
<InfoRow>
|
||||
<Label>Имя:</Label>
|
||||
<Value>{device.hostname || 'Неизвестно'}</Value>
|
||||
<Value>{device.hostname || (nameSetting === 'ip' ? device.ip : 'Неизвестно')}</Value>
|
||||
</InfoRow>
|
||||
|
||||
<InfoRow>
|
||||
|
|
|
|||
|
|
@ -17,10 +17,10 @@ const Button = styled.button.withConfig({
|
|||
color: white;
|
||||
font-size: ${(props) =>
|
||||
props.size === "small"
|
||||
? "14px"
|
||||
? "16px"
|
||||
: props.size === "big"
|
||||
? "16px"
|
||||
: "14px"}; // По умолчанию
|
||||
? "18px"
|
||||
: "16px"}; // По умолчанию
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
|||
|
|
@ -1,109 +0,0 @@
|
|||
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,12 +3,13 @@ import { Handle, Position } from '@xyflow/react';
|
|||
import { Ip } from './style';
|
||||
import { Row, IpTitle, IpContent } from './style';
|
||||
import Input from '../../../../UI/inputs/input';
|
||||
import { useModalContext, usePannableStore } from '../../../../../store';
|
||||
import {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
|
||||
|
|
@ -45,6 +46,7 @@ export const IpNode = ({ data }: CustomNodeProps) => {
|
|||
const { toggleEditingMode } = usePannableStore();
|
||||
const [isInput, setIsInput] = useState(false)
|
||||
const { openModal } = useModalContext();
|
||||
const {ipAddress} = useIpStore();
|
||||
|
||||
const [ipData, setIpData] = useState<changeIp>(
|
||||
{
|
||||
|
|
@ -146,7 +148,20 @@ 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 (
|
||||
<>
|
||||
|
|
@ -170,7 +185,10 @@ export const IpNode = ({ data }: CustomNodeProps) => {
|
|||
</Row>
|
||||
|
||||
) : (
|
||||
<IconComponent name={buttonNames[7]} onClick={handleClick} width='18' height='18' setColor='white'/>
|
||||
<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>
|
||||
|
|
|
|||
|
|
@ -120,7 +120,7 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
|||
);
|
||||
useDevicesStore.setState({ devices: updatedDevices });
|
||||
}
|
||||
|
||||
|
||||
onClose()
|
||||
}
|
||||
} catch (err) {
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ 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,
|
||||
|
|
@ -74,6 +75,11 @@ 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}>`
|
||||
export const ModalContainer = styled.div<{height?: string, width?: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}>`
|
|||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
height: ${(props) => props.height ? props.height : '230px'};
|
||||
width: 520px;
|
||||
width: ${(props) => props.width ? props.width : '520px'};
|
||||
`
|
||||
|
||||
export const ModalTitle = styled.div`
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@
|
|||
import { MainButton } from "../../../UI/buttons/mainButton"
|
||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||
import { useWorkSpaceStore } from "../../../../store"
|
||||
import styled from "styled-components"
|
||||
import styled, {useTheme} from "styled-components"
|
||||
import ThemeButton from "../../../UI/buttons/toggletheme"
|
||||
import {buttonNames} from "../../../../const/buttons/names";
|
||||
import {IconComponent} from "../../../../const/buttons/icons";
|
||||
|
||||
type Props = {
|
||||
from: 'reg' | 'helloWindow' | 'workSpace'
|
||||
|
|
@ -29,23 +31,41 @@ const Content = styled.div`
|
|||
justify-content: space-between;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 25px 50px;
|
||||
background: ${(props => props.theme.secondary)};
|
||||
padding: 40px 50px;
|
||||
background: ${(props => props.theme.primary)};
|
||||
border-radius: 18px;
|
||||
position: relative;
|
||||
`
|
||||
|
||||
const Listing = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
|
||||
background-color: ${(props => props.theme.secondary)};
|
||||
border-radius: 10px;
|
||||
padding: 24px 20px;
|
||||
p{
|
||||
font-weight: bold;
|
||||
font-size: 24px;
|
||||
}
|
||||
`
|
||||
|
||||
const List = styled.div`
|
||||
const List = styled.ul`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 15px;
|
||||
margin-left: 10px;
|
||||
margin: 10px 0;
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
font-size: 18px;
|
||||
gap: 4px;
|
||||
& li:before {
|
||||
color: ${(props => props.theme.accent)};
|
||||
content: "•";
|
||||
font-size: 28px;
|
||||
width: .8em;
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
top: 2px;
|
||||
}
|
||||
`
|
||||
|
||||
const Title = styled.div`
|
||||
|
|
@ -55,43 +75,55 @@ const Title = styled.div`
|
|||
const SubTitle = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
|
||||
gap: 24px;
|
||||
background-color:rgba(255, 248, 225, 0.6);
|
||||
border-left: #ff9800 solid 4px;
|
||||
border-radius: 0 8px 8px 0;
|
||||
padding: 20px 12px;
|
||||
|
||||
a{
|
||||
text-decoration: none;
|
||||
color: ${(props => props.theme.errorColor)};
|
||||
font-weight: 500;
|
||||
font-size: 18px;
|
||||
width: max-content;
|
||||
}
|
||||
p{
|
||||
font-size: 18px;
|
||||
color: ${(props=>props.theme.textColor)};
|
||||
}
|
||||
.icon{
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
fill: #ff9800;
|
||||
}
|
||||
|
||||
`
|
||||
|
||||
const Links = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: start;
|
||||
gap: 20px;
|
||||
`
|
||||
|
||||
const LinkColumn = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
`
|
||||
|
||||
|
||||
const LinkBlock = styled.div`
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
const LinkBlock = styled.a`
|
||||
text-decoration: none;
|
||||
color: ${(props => props.theme.accent)};;
|
||||
`
|
||||
|
||||
const Copyright = styled.div`
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
margin-top: 50px;
|
||||
text-align: start;
|
||||
color: ${(props => props.theme.offlineColor)};
|
||||
`
|
||||
|
||||
const ButtonContainer = styled.div`
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
right: 14px;
|
||||
`
|
||||
const AboutLine = styled.hr`
|
||||
border: ${(props => props.theme.hoverBtn)} 1px solid;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justifi-content: flex-start;
|
||||
align-items: center;
|
||||
`
|
||||
|
||||
const MainTitle = styled.div`
|
||||
|
|
@ -99,24 +131,33 @@ const MainTitle = styled.div`
|
|||
display: flex;
|
||||
gap: 15px;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding-top: 50px;
|
||||
justify-content: space-between;
|
||||
padding-top: 10px;
|
||||
box-sizing: border-box;
|
||||
|
||||
img{
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
}
|
||||
.version{
|
||||
text-wrap: nowrap;
|
||||
background-color: ${(props => props.theme.hoverBtn)};
|
||||
color: ${(props => props.theme.borderColor)};
|
||||
border-radius: 20px;
|
||||
font-size: 18px;
|
||||
padding: 10px 18px;
|
||||
line-height: 1;
|
||||
}
|
||||
.title{
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 24px;
|
||||
}
|
||||
`
|
||||
const Text = styled.div`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 30px;
|
||||
`
|
||||
|
||||
export default function About({ from, onBack }: Props) {
|
||||
const { setWorkModule } = useWorkSpaceStore()
|
||||
|
||||
const getBackHandler = () => {
|
||||
switch (from) {
|
||||
case 'reg':
|
||||
|
|
@ -138,68 +179,63 @@ export default function About({ from, onBack }: Props) {
|
|||
<Content>
|
||||
|
||||
<MainTitle>
|
||||
<img src="./assets/images/gossys.png" alt="" />
|
||||
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
|
||||
|
||||
<div className={'title'}>
|
||||
<img src="./assets/images/gossys.png" alt="" />
|
||||
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
|
||||
</div>
|
||||
<p className={'version'}>v 0.0.3</p>
|
||||
</MainTitle>
|
||||
|
||||
<AboutLine/>
|
||||
|
||||
<Text>
|
||||
<Title>
|
||||
<p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
||||
</Title>
|
||||
|
||||
<Title>
|
||||
<p>Приложение ГоNets является минимально работоспособным кроссплатформенным продуктом (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
||||
</Title>
|
||||
<Listing>
|
||||
<p>Возможности:</p>
|
||||
<List>
|
||||
<li>Визуализация активных сетевых ресурсов</li>
|
||||
<li>Настройка параметров локальных и удалённых сетевых ресурсов</li>
|
||||
<li>Управление взаимодействием ресурсов с Клиентом ГoNets</li>
|
||||
</List>
|
||||
</Listing>
|
||||
|
||||
<SubTitle>
|
||||
<svg className="icon" viewBox="0 0 24 24">
|
||||
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
|
||||
</svg>
|
||||
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
|
||||
<a href={'https://gossys.dev/pages/projects/gonets'}>Перейти на ГоNets-PRO</a>
|
||||
</SubTitle>
|
||||
|
||||
<Listing>
|
||||
<p>Данное приложение умеет:</p>
|
||||
<List>
|
||||
<p>- Отображать в графическом виде активные сетевые ресурсы</p>
|
||||
<p>- Настраивать сетевые параметры локального и удаленных сетевых ресурсов</p>
|
||||
<p>- Отображать и изменять взаимодействие сетевых ресурсов с установленным Клиентом ГоNets</p>
|
||||
</List>
|
||||
</Listing>
|
||||
<Links>
|
||||
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
|
||||
Домашняя страницa
|
||||
</LinkBlock>
|
||||
|
||||
<SubTitle>
|
||||
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
|
||||
<p>Для коммерческого использования приобретите версию ГоNets-PRO (Подробная информация на странице продукта)</p>
|
||||
</SubTitle>
|
||||
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
|
||||
Документация
|
||||
</LinkBlock>
|
||||
|
||||
<Links>
|
||||
<LinkBlock>
|
||||
<p>Ссылка на домашнюю страницу:</p>
|
||||
<a href="https://gossys.dev/pages/projects/gonets">https://gossys.dev/pages/projects/gonets</a>
|
||||
</LinkBlock>
|
||||
|
||||
<LinkBlock>
|
||||
<p>Инструкции и информация:</p>
|
||||
<a href="https://wiki.gossys.dev/ru/gonets">https://wiki.gossys.dev/ru/gonets</a>
|
||||
</LinkBlock>
|
||||
|
||||
<LinkColumn>
|
||||
<p>Распространяется под лицензией GosysPUB текущей версии.</p>
|
||||
<LinkBlock>
|
||||
<p>Полный текст актуальной лицензии:</p>
|
||||
<a href="https://wiki.gossys.dev/ru/lic/GosysPUB">https://wiki.gossys.dev/ru/lic/GosysPUB</a>
|
||||
</LinkBlock>
|
||||
</LinkColumn>
|
||||
|
||||
</Links>
|
||||
|
||||
<Copyright>
|
||||
<p>Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.</p>
|
||||
</Copyright>
|
||||
|
||||
</Text>
|
||||
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
|
||||
Лицензия
|
||||
</LinkBlock>
|
||||
</Links>
|
||||
|
||||
<Copyright>
|
||||
Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.
|
||||
</Copyright>
|
||||
|
||||
<ButtonContainer>
|
||||
{showBackButton && backHandler && (
|
||||
<MainButton
|
||||
title="Назад"
|
||||
<IconComponent
|
||||
width="36"
|
||||
height="36"
|
||||
name={buttonNames[12]}
|
||||
onClick={backHandler}
|
||||
style={{ marginRight: '10px' }}
|
||||
/>
|
||||
|
||||
)}
|
||||
|
||||
{from == 'reg' &&
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import React, {useEffect, useState} 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;
|
||||
|
|
@ -28,10 +30,36 @@ 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 ? (
|
||||
|
|
@ -42,8 +70,8 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
}}>
|
||||
{/* Колонка активных устройств */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные устройства ({activeDevices.length})</h3>
|
||||
{activeDevices.map((device, index) => (
|
||||
<h3 style={{ color: '#4CAF50', marginBottom:'12px' }}>Активные узлы ({activeDevices.length})</h3>
|
||||
{currentActiveDevices.map((device, index) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
status="online"
|
||||
|
|
@ -53,8 +81,8 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
|
|||
</div>
|
||||
{/* Колонка неактивных устройств */}
|
||||
<div style={{ flex: 1 }}>
|
||||
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные устройства ({inactiveDevices.length})</h3>
|
||||
{inactiveDevices.map((device, index) => (
|
||||
<h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные узлы ({inactiveDevices.length})</h3>
|
||||
{currentInactiveDevices.map((device, index) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
status="offline"
|
||||
|
|
@ -64,8 +92,17 @@ 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 "../../../../../UI/tables/TableScanner";
|
||||
import TableScanner from "./TableScanner";
|
||||
|
||||
const ScanBtn = styled.button`
|
||||
position: relative;
|
||||
|
|
@ -20,7 +20,7 @@ const ScanBtn = styled.button`
|
|||
|
||||
interface ListUsersProps {
|
||||
loading: boolean;
|
||||
handleScan: ()=>void;
|
||||
handleScan: (replay?: boolean) => 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,11 +1,12 @@
|
|||
import { useIpStore } from "../../../../../store";
|
||||
import styled, { keyframes } from "styled-components";
|
||||
import { useState } from "react";
|
||||
import {useEffect, 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,
|
||||
|
|
@ -77,7 +78,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
span {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
background: ${(props) => props.theme.accent};
|
||||
background: ${(props) => props.theme.scannerColor};
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
|
|
@ -136,9 +137,11 @@ export default function UserScanner() {
|
|||
const [scanInitiated, setScanInitiated] = useState(devices.length > 0);
|
||||
|
||||
// Сканирование
|
||||
const handleScan = async () => {
|
||||
const handleScan = async (replay:boolean) => {
|
||||
try {
|
||||
setLoading(true);
|
||||
if(!replay){
|
||||
setLoading(true);
|
||||
}
|
||||
setScanInitiated(true);
|
||||
const res = await NetworkService.scanUser(ipAddress);
|
||||
console.log(res);
|
||||
|
|
@ -151,6 +154,23 @@ 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: 'Таблица' },
|
||||
|
|
@ -163,7 +183,7 @@ export default function UserScanner() {
|
|||
|
||||
return (
|
||||
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
||||
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование пользователей в сети</TitleWrapper>)}
|
||||
{(!scanInitiated || loading) && (<TitleWrapper>Сканирование узлов подсети</TitleWrapper>)}
|
||||
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
||||
<span className="center"></span>
|
||||
<span className="ring1"></span>
|
||||
|
|
@ -177,12 +197,12 @@ export default function UserScanner() {
|
|||
|
||||
{loading ? (
|
||||
<LoadingText>
|
||||
Сканирование сети<ScanningText />
|
||||
Сканирование узлов <ScanningText />
|
||||
</LoadingText>
|
||||
) : selectedView.value == 1 ? (
|
||||
<TableUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
|
||||
<TableUsers loading={loading} handleScan={()=>handleScan(false)} devices={devices} scanInitiated={scanInitiated} />
|
||||
):(
|
||||
<ListUsers loading={loading} handleScan={handleScan} devices={devices} scanInitiated={scanInitiated} />
|
||||
<ListUsers loading={loading} handleScan={()=>handleScan(false)} devices={devices} scanInitiated={scanInitiated} />
|
||||
)}
|
||||
</Wrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -136,7 +136,8 @@ const ScanBtn = styled.button<{scanInitiated: boolean}>`
|
|||
`
|
||||
|
||||
export function WifiScanner() {
|
||||
const { ipAddress } = useIpStore();
|
||||
//const { ipAddress } = useIpStore();
|
||||
const ipAddress = '192.168.1.147'
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [scanInitiated, setScanInitiated] = useState(false);
|
||||
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
||||
|
|
@ -161,17 +162,19 @@ export function WifiScanner() {
|
|||
|
||||
return (
|
||||
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
||||
<TitleWrapper>Сканирование Wi-Fi сети</TitleWrapper>
|
||||
<TitleWrapper>Сканирование точек доступа Wi-Fi сети</TitleWrapper>
|
||||
|
||||
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
||||
<span className="center circel"></span>
|
||||
<span className="ring1 circel"></span>
|
||||
<span className="ring2 circel"></span>
|
||||
<span className="ring3 circel"></span>
|
||||
{wifiNetworks.map((network) => (
|
||||
{wifiNetworks.map((network, index) => (
|
||||
<WifiDot
|
||||
key={network.bssid}
|
||||
network={network}
|
||||
index={index}
|
||||
total={wifiNetworks.length}
|
||||
onSelect={setCurrentNetwork}
|
||||
/>
|
||||
))}
|
||||
|
|
@ -186,11 +189,11 @@ export function WifiScanner() {
|
|||
|
||||
{loading ? (
|
||||
<LoadingText>
|
||||
Сканирование сети <ScanningText/>
|
||||
Сканирование точек доступа <ScanningText/>
|
||||
</LoadingText>
|
||||
) : (
|
||||
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }>
|
||||
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'}
|
||||
</ScanBtn>
|
||||
)}
|
||||
</Wrapper>
|
||||
|
|
|
|||
|
|
@ -31,43 +31,45 @@ const Dot = styled.div<{ top: string; left: string }>`
|
|||
}
|
||||
`;
|
||||
|
||||
const WifiDot = ({ network, onSelect }: { network: WifiNetworkScanner, onSelect: (n: WifiNetworkScanner) => void }) => {
|
||||
const getPositionInRing = (bssid: string, ringSize: number) => {
|
||||
const hash = bssid.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
|
||||
const randomAngle = (hash % 360) * (Math.PI / 180);
|
||||
const innerRadius = ringSize * 0.4;
|
||||
const outerRadius = ringSize * 0.5;
|
||||
const randomValue = (hash % 1000) / 1000;
|
||||
const randomRadius = Math.sqrt(innerRadius * innerRadius + randomValue * (outerRadius * outerRadius - innerRadius * innerRadius));
|
||||
const getPositionInRing = (index: number, total: number, ringSize: number) => {
|
||||
const angle = (index * (2 * Math.PI / total)) + (Math.PI / total * (index % 2));
|
||||
const innerRadius = ringSize * 0.4;
|
||||
const outerRadius = ringSize * 0.5;
|
||||
const radius = innerRadius + (outerRadius - innerRadius) * (0.3 + 0.7 * (index % 10) / 10);
|
||||
|
||||
return {
|
||||
x: Math.cos(randomAngle) * randomRadius,
|
||||
y: Math.sin(randomAngle) * randomRadius
|
||||
};
|
||||
return {
|
||||
x: Math.cos(angle) * radius,
|
||||
y: Math.sin(angle) * radius
|
||||
};
|
||||
};
|
||||
|
||||
const getPosition = (signal: number, bssid: string) => {
|
||||
const WifiDot = ({ network, index, total, onSelect }: {
|
||||
network: WifiNetworkScanner,
|
||||
index: number,
|
||||
total: number,
|
||||
onSelect: (n: WifiNetworkScanner) => void
|
||||
}) => {
|
||||
const getPosition = (signal: number, index: number, total: number) => {
|
||||
let strength: 1 | 2 | 3;
|
||||
if (signal >= -50) {
|
||||
strength = 3;
|
||||
} else if (signal >= -70) {
|
||||
strength = 2;
|
||||
} else if (signal >= -90) {
|
||||
strength = 1;
|
||||
} else {
|
||||
strength = 1;
|
||||
}
|
||||
|
||||
const ringSizes = { 1: 550, 2: 400, 3: 250 };
|
||||
const ringSize = ringSizes[strength];
|
||||
const offset = getPositionInRing(bssid, ringSize);
|
||||
const offset = getPositionInRing(index, total, ringSize);
|
||||
|
||||
return {
|
||||
top: `calc(50% - ${offset.y}px)`,
|
||||
left: `calc(50% - ${offset.x}px)`
|
||||
top: `calc(50% + ${offset.y}px)`,
|
||||
left: `calc(50% + ${offset.x}px)`
|
||||
};
|
||||
};
|
||||
|
||||
const position = getPosition(network.signal, network.bssid);
|
||||
const position = getPosition(network.signal, index, total);
|
||||
|
||||
return (
|
||||
<Dot top={position.top} left={position.left} onClick={() => onSelect(network)}>
|
||||
|
|
|
|||
|
|
@ -81,6 +81,11 @@ export const ToolBar = () => {
|
|||
/>
|
||||
</IconContainer>
|
||||
</Button>
|
||||
<Button>
|
||||
<IconContainer>
|
||||
<IconComponent name={buttonNames[3]} width="30" height="30" onClick={() => openModal(ModalMap['10'])} />
|
||||
</IconContainer>
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
</Container>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import settings from '@iconify/icons-ic/baseline-settings';
|
|||
import info from '@iconify/icons-ic/baseline-info';
|
||||
import arrowDown from '@iconify/icons-ic/baseline-keyboard-arrow-down';
|
||||
import arrowRight from '@iconify/icons-ic/baseline-keyboard-arrow-right';
|
||||
import arrowLeft from '@iconify/icons-ic/baseline-keyboard-arrow-left';
|
||||
import edit from '@iconify/icons-ic/baseline-edit';
|
||||
import checkCircle from '@iconify/icons-ic/round-check-circle';
|
||||
import cancel from '@iconify/icons-ic/baseline-cancel';
|
||||
|
|
@ -19,6 +20,8 @@ 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,
|
||||
|
|
@ -37,6 +40,9 @@ 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,5 +16,8 @@ export const buttonNames: names ={
|
|||
13: 'noAdmin',
|
||||
14: 'userSearch',
|
||||
15: 'wifiFind',
|
||||
16: 'noteEdit'
|
||||
16: 'noteEdit',
|
||||
17: 'arrowLeft',
|
||||
18: 'swapHoriz',
|
||||
19: 'check'
|
||||
}
|
||||
|
|
@ -3,7 +3,7 @@ import { toolBarNames } from "./names"
|
|||
import { useTheme } from "styled-components"
|
||||
import { Icon } from '@iconify/react';
|
||||
|
||||
import desktop from '@iconify/icons-ic/round-desktop-windows';
|
||||
import desktop from '@iconify/icons-ic/baseline-menu';
|
||||
|
||||
export const ICONS = {
|
||||
[toolBarNames[1]]: desktop,
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const dark = {
|
|||
background: '#3A3A3A',
|
||||
textColor: '#FFFFFF',
|
||||
borderColor: '#b6b6b6',
|
||||
scannerColor: '#0E9C9D',
|
||||
scannerColor: '#00E5FF',
|
||||
offlineColor: '#656565',
|
||||
hoverBtn: '#4a4a4a',
|
||||
};
|
||||
Loading…
Add table
Add a link
Reference in a new issue