Добавление заметок

This commit is contained in:
test 2025-06-12 15:24:50 +05:00
parent ea56d17ea7
commit e61ffd793c
20 changed files with 274 additions and 118 deletions

View file

@ -95,7 +95,35 @@ export class NetworkService {
static async scanWifi(ipAddress: string): Promise<AxiosResponse> { static async scanWifi(ipAddress: string): Promise<AxiosResponse> {
try { try {
const axiosInstance = this.createAxiosInstance(ipAddress); const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.get('/subnet/user'); return await axiosInstance.get('/subnet/wifi');
} catch (error: any) {
return error.response;
}
}
static async addNote(ipAddress: string, mac:string, note: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.post(`/subnet/devices/${mac}/note`, {
note: note
});
} catch (error: any) {
return error.response;
}
}
static async editNote(ipAddress: string, mac:string, note: string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.put(`/subnet/devices/${mac}/note`, {
note: note
});
} catch (error: any) {
return error.response;
}
}
static async deleteNote(ipAddress: string, mac:string): Promise<AxiosResponse> {
try {
const axiosInstance = this.createAxiosInstance(ipAddress);
return await axiosInstance.delete(`/subnet/devices/${mac}/note`);
} catch (error: any) { } catch (error: any) {
return error.response; return error.response;
} }

View file

@ -1,6 +1,10 @@
import React from 'react'; import React, {useState} from 'react';
import styled, { css } from 'styled-components'; import styled, { css } from 'styled-components';
import { DeviceUser } from "../../../store/useDeviceStore"; import { DeviceUser } from "../../../store/useDeviceStore";
import {buttonNames} from "../../../const/buttons/names";
import {ModalMap} from "../../modalwindows/windowsType";
import {IconComponent} from "../../../const/buttons/icons";
import {useModalContext} from "../../../store";
interface DeviceCardProps { interface DeviceCardProps {
device: DeviceUser; device: DeviceUser;
@ -24,7 +28,7 @@ const CardWrapper = styled.div<{ status: 'online' | 'offline' }>`
const CardHeader = styled.div<{ status: 'online' | 'offline' }>` const CardHeader = styled.div<{ status: 'online' | 'offline' }>`
background: ${({ status, theme }) => background: ${({ status, theme }) =>
status === 'online' ? theme.successColor : theme.errorColor}; status === 'online' ? theme.successColor : theme.offlineColor};
color: white; color: white;
padding: 16px; padding: 16px;
font-size: 18px; font-size: 18px;
@ -67,9 +71,19 @@ const Value = styled.span`
color: #333; color: #333;
`; `;
const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => { const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
const [isExpanded, setIsExpanded] = React.useState(false); const [isExpanded, setIsExpanded] = useState(false);
const toggleExpanded = () => setIsExpanded(!isExpanded); const toggleExpanded = () => setIsExpanded(!isExpanded);
const { openModal } = useModalContext();
const openModalNote = (e: React.MouseEvent) => {
e.stopPropagation();
openModal(ModalMap['6'], {
mac: device.mac,
currentNote: device.note || ''
});
};
return ( return (
@ -78,6 +92,9 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
<p> <p>
MAC: {device.mac} MAC: {device.mac}
</p> </p>
<span onClick={openModalNote}>
<IconComponent name={buttonNames[7]} width="24" height="24" setColor={'white'} />
</span>
</CardHeader> </CardHeader>
<CardContent isExpanded={isExpanded}> <CardContent isExpanded={isExpanded}>
@ -107,6 +124,11 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
<Label>Последняя активность:</Label> <Label>Последняя активность:</Label>
<Value>{device.last_seen}</Value> <Value>{device.last_seen}</Value>
</InfoRow> </InfoRow>
<InfoRow>
<Label>Заметка:</Label>
<Value>{device.note || '-'}</Value>
</InfoRow>
</CardContent> </CardContent>
</CardWrapper> </CardWrapper>
); );

View file

@ -68,6 +68,7 @@ const DropdownRow = styled.button`
font-size: 16px; font-size: 16px;
border: none; border: none;
width: 100%; width: 100%;
color: ${({theme}) => theme.textColor};
transition: all .3s ease; transition: all .3s ease;
cursor: pointer; cursor: pointer;

View file

@ -9,15 +9,15 @@ const Button = styled.button.withConfig({
border: none; border: none;
padding: ${(props) => padding: ${(props) =>
props.size === "small" props.size === "small"
? "4px 8px" ? "10px 28px"
: props.size === "big" : props.size === "big"
? "10px 20px" ? "10px 20px"
: "6px 10px"}; // По умолчанию : "12px 20px"}; // По умолчанию
border-radius: 8px; border-radius: 8px;
color: white; color: white;
font-size: ${(props) => font-size: ${(props) =>
props.size === "small" props.size === "small"
? "12px" ? "14px"
: props.size === "big" : props.size === "big"
? "16px" ? "16px"
: "14px"}; // По умолчанию : "14px"}; // По умолчанию
@ -30,7 +30,7 @@ const Button = styled.button.withConfig({
? 'row-reverse' ? 'row-reverse'
: 'row'}; : 'row'};
gap: 10px; gap: 10px;
transition: all .3s ease;
&:hover{ &:hover{
background-color: ${(props) => props.$hoverсolor}; background-color: ${(props) => props.$hoverсolor};
} }

View file

@ -5,7 +5,7 @@ type Props = {
placeholderText: string; placeholderText: string;
id: string; id: string;
value?: string; value?: string;
onChange?: (identifier: string, value: string) => void; onChange?: (value: string) => void;
onFileChange?: (event: React.ChangeEvent<HTMLInputElement>) => void; onFileChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
type?: string; type?: string;
multiple?: boolean; multiple?: boolean;
@ -24,7 +24,7 @@ const InputContainer = styled.div`
`; `;
const StyledInput = styled.input` const StyledInput = styled.input`
padding: 8px 10px; padding: 12px 10px;
color: black; color: black;
border-radius: 8px; border-radius: 8px;
border: 1px solid #A1A1A1; border: 1px solid #A1A1A1;
@ -41,7 +41,7 @@ const Input = React.forwardRef<HTMLInputElement, Props>(({ placeholderText, id,
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => { const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { value } = e.target; const { value } = e.target;
if (onChange) { if (onChange) {
onChange(id, value); onChange(value);
} }
}; };

View file

@ -5,6 +5,10 @@ interface TableScannerProps {
devices: DeviceUser[]; devices: DeviceUser[];
} }
import styled from 'styled-components'; 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` const TableContainer = styled.div`
width: 100%; width: 100%;
@ -48,12 +52,21 @@ const TableCell = styled.td`
const StatusBadge = styled.span<{ status:string }>` const StatusBadge = styled.span<{ status:string }>`
width: 10px; width: 10px;
height: 30px; height: 30px;
background-color: ${({theme, status}) => (status==='online' ? theme.successColor : theme.errorColor)}; background-color: ${({theme, status}) => (status==='online' ? theme.successColor : theme.offlineColor)};
display: inline-block; display: inline-block;
border-radius:4px; border-radius:4px;
` `
const TableScanner = ({devices}:TableScannerProps) => { const TableScanner = ({devices}:TableScannerProps) => {
const { openModal } = useModalContext();
const openModalNote = (e: React.MouseEvent, device:DeviceUser ) => {
e.stopPropagation();
openModal(ModalMap['6'], {
mac: device.mac,
currentNote: device.note || ''
});
};
return ( return (
<TableContainer> <TableContainer>
@ -66,6 +79,8 @@ const TableScanner = ({devices}:TableScannerProps) => {
<TableHeaderCell>IP адрес</TableHeaderCell> <TableHeaderCell>IP адрес</TableHeaderCell>
<TableHeaderCell>Открытые порты</TableHeaderCell> <TableHeaderCell>Открытые порты</TableHeaderCell>
<TableHeaderCell>Последняя активность</TableHeaderCell> <TableHeaderCell>Последняя активность</TableHeaderCell>
<TableHeaderCell>Заметка</TableHeaderCell>
<TableHeaderCell></TableHeaderCell>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -77,6 +92,12 @@ const TableScanner = ({devices}:TableScannerProps) => {
<TableCell>{item.ip}</TableCell> <TableCell>{item.ip}</TableCell>
<TableCell>{item.open_ports.join(', ') || '-'}</TableCell> <TableCell>{item.open_ports.join(', ') || '-'}</TableCell>
<TableCell>{item.last_seen}</TableCell> <TableCell>{item.last_seen}</TableCell>
<TableCell>{item.note || '-'}</TableCell>
<TableCell>
<span onClick={(e)=>openModalNote(e, item)}>
<IconComponent name={buttonNames[7]} width="24" height="24" />
</span>
</TableCell>
</TableRow> </TableRow>
))} ))}
</tbody> </tbody>

View file

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

View file

@ -3,6 +3,7 @@ import { IpConfirm, ConnectionError, AddDevice, RightsConfirm } from "./modalCon
import { ModalMap } from "./windowsType"; import { ModalMap } from "./windowsType";
import Loader from "../UI/loader"; import Loader from "../UI/loader";
import { ModalWindow } from "./style"; import { ModalWindow } from "./style";
import {NoteDevice} from "./modalContainers/NoteDevice";
type Props = { type Props = {
isOpen: boolean, isOpen: boolean,
@ -35,6 +36,13 @@ export default function WindowComponent({ isOpen, type, data }: Props) {
message={data.message} message={data.message}
/> />
} }
{type === ModalMap['6'] && (
<NoteDevice
mac={data.mac}
currentNote={data.currentNote}
onClose={closeLoader}
/>
)}
{type === ModalMap['4'] && ( {type === ModalMap['4'] && (
<AddDevice onSave={data?.onSave} onClose={closeLoader} /> <AddDevice onSave={data?.onSave} onClose={closeLoader} />

View file

@ -24,7 +24,7 @@ export const ModalContainer = styled.div<{height?: string}>`
flex-direction: column; flex-direction: column;
justify-content: space-between; justify-content: space-between;
gap: 15px; gap: 15px;
height: ${(props) => props.height ? props.height : '200px'}; height: ${(props) => props.height ? props.height : '230px'};
width: 520px; width: 520px;
` `

View file

@ -5,5 +5,6 @@ export const ModalMap: ModalType = {
'2': 'loader', '2': 'loader',
'3': 'connectionError', '3': 'connectionError',
'4': 'addDevice', '4': 'addDevice',
'5': 'sudo' '5': 'sudo',
'6': 'noteDevice',
} }

View file

@ -25,8 +25,6 @@ interface ListUsersProps {
} }
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => { const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
// Фильтр по статусу
const activeDevices = devices.filter(device => device.status === 'online'); const activeDevices = devices.filter(device => device.status === 'online');
const inactiveDevices = devices.filter(device => device.status !== 'online'); const inactiveDevices = devices.filter(device => device.status !== 'online');
@ -55,7 +53,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps
</div> </div>
{/* Колонка неактивных устройств */} {/* Колонка неактивных устройств */}
<div style={{ flex: 1 }}> <div style={{ flex: 1 }}>
<h3 style={{ color: '#F44336', marginBottom:'12px' }}>Неактивные устройства ({inactiveDevices.length})</h3> <h3 style={{ color: '#ababab', marginBottom:'12px' }}>Неактивные устройства ({inactiveDevices.length})</h3>
{inactiveDevices.map((device, index) => ( {inactiveDevices.map((device, index) => (
<DeviceCard <DeviceCard
key={index} key={index}

View file

@ -14,21 +14,21 @@ type Option ={
// Анимации // Анимации
const ring1 = keyframes` const ring1 = keyframes`
0% { width: 250px; height: 250px; } 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
50% { width: 280px; height: 280px; } 50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.2; }
100% { width: 250px; height: 250px; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
`; `;
const ring2 = keyframes` const ring2 = keyframes`
0% { width: 400px; height: 400px; } 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
50% { width: 430px; height: 430px; } 50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.15; }
100% { width: 400px; height: 400px; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
`; `;
const ring3 = keyframes` const ring3 = keyframes`
0% { width: 550px; height: 550px; } 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
50% { width: 580px; height: 580px; } 50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.1; }
100% { width: 550px; height: 550px; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
`; `;
const dotAnimation = keyframes` const dotAnimation = keyframes`
0% { content: '.'; } 0% { content: '.'; }
@ -36,6 +36,11 @@ const dotAnimation = keyframes`
66% { content: '...'; } 66% { content: '...'; }
100% { content: '.'; } 100% { content: '.'; }
`; `;
const pulse = keyframes`
0% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.1); }
100% { transform: translate(-50%, -50%) scale(1); }
`;
// Стили // Стили
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>` const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
@ -60,8 +65,8 @@ const TitleWrapper = styled.h2`
left: 50%; left: 50%;
transform: translateX(-50%); transform: translateX(-50%);
font-size: 32px; font-size: 32px;
width: max-content;
` `
const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>` const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 60px; width: 60px;
height: 60px; height: 60px;
@ -77,10 +82,10 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
border-radius: 50%; border-radius: 50%;
&.center{ &.center {
width: 60px; width: 60px;
height: 60px; height: 60px;
animation: ${({ loading }) => (loading ? pulse : "none")} 2s ease infinite;
} }
&.ring1 { &.ring1 {
opacity: 0.12; opacity: 0.12;
@ -94,6 +99,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 400px; width: 400px;
height: 400px; height: 400px;
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite; animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.1s" : "0")};
} }
&.ring3 { &.ring3 {
@ -101,9 +107,9 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 550px; width: 550px;
height: 550px; height: 550px;
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite; animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.2s" : "0")};
} }
} }
`; `;
const LoadingText = styled.div` const LoadingText = styled.div`

View file

@ -15,21 +15,19 @@ type Option ={
// Анимации // Анимации
const ring1 = keyframes` const ring1 = keyframes`
0% { width: 250px; height: 250px; } 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
50% { width: 280px; height: 280px; } 50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.2; }
100% { width: 250px; height: 250px; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
`; `;
const ring2 = keyframes` const ring2 = keyframes`
0% { width: 400px; height: 400px; } 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
50% { width: 430px; height: 430px; } 50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.15; }
100% { width: 400px; height: 400px; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
`; `;
const ring3 = keyframes` const ring3 = keyframes`
0% { width: 550px; height: 550px; } 0% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
50% { width: 580px; height: 580px; } 50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.1; }
100% { width: 550px; height: 550px; } 100% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
`; `;
const dotAnimation = keyframes` const dotAnimation = keyframes`
0% { content: '.'; } 0% { content: '.'; }
@ -37,13 +35,18 @@ const dotAnimation = keyframes`
66% { content: '...'; } 66% { content: '...'; }
100% { content: '.'; } 100% { content: '.'; }
`; `;
const pulse = keyframes`
0% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.1); }
100% { transform: translate(-50%, -50%) scale(1); }
`;
// Стили // Стили
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>` const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
display: flex; display: flex;
flex-direction: column; flex-direction: column;
align-items: center; align-items: center;
justify-content:${({scanInitiated, loading})=>(scanInitiated && !loading ? 'start' : 'center')}; justify-content:center ;
height: 100%; height: 100%;
width: 100%; width: 100%;
position: relative; position: relative;
@ -69,7 +72,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
border-radius: 50%; border-radius: 50%;
position: relative; position: relative;
transition: .4s ease; transition: .4s ease;
transform: ${({scanInitiated,loading})=>(scanInitiated && !loading ? 'scale(0)' : 'scale(1)')}; transform: scale(1);
span { span {
position: absolute; position: absolute;
display: inline-block; display: inline-block;
@ -78,10 +81,10 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
border-radius: 50%; border-radius: 50%;
&.center{ &.center {
width: 60px; width: 60px;
height: 60px; height: 60px;
animation: ${({ loading }) => (loading ? pulse : "none")} 2s ease infinite;
} }
&.ring1 { &.ring1 {
opacity: 0.12; opacity: 0.12;
@ -95,6 +98,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 400px; width: 400px;
height: 400px; height: 400px;
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite; animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.1s" : "0")};
} }
&.ring3 { &.ring3 {
@ -102,11 +106,12 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
width: 550px; width: 550px;
height: 550px; height: 550px;
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite; animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
animation-delay: ${({ loading }) => (loading ? "0.2s" : "0")};
} }
} }
`; `;
const LoadingText = styled.div` const LoadingText = styled.div`
position: relative; position: relative;
padding: 14px 22px; padding: 14px 22px;
@ -122,7 +127,7 @@ const ScanningText = styled.span`
animation: ${dotAnimation} 1.5s infinite; animation: ${dotAnimation} 1.5s infinite;
} }
`; `;
const ScanBtn = styled.button<{loading: boolean}>` const ScanBtn = styled.button<{scanInitiated: boolean}>`
position: relative; position: relative;
padding: 14px 22px; padding: 14px 22px;
background-color: #4CAF50; background-color: #4CAF50;
@ -130,57 +135,25 @@ const ScanBtn = styled.button<{loading: boolean}>`
border: none; border: none;
border-radius: 12px; border-radius: 12px;
font-size: 18px; font-size: 18px;
transition: .4s ease; transition: transform .4s ease;
transform: ${({loading}) => (!loading ? "translateY(240px)" : "translateY(0)")}; transform: ${({scanInitiated}) => (scanInitiated ? "translateY(240px)" : "translateY(0)")};
&:hover{ &:hover{
transform: ${({loading}) => (!loading ? "translateY(240px)" : "translateY(0)")} scale(1.03); transform: ${({scanInitiated}) => (scanInitiated ? "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() { export function WifiScanner() {
const {ipAddress} = useIpStore(); const ipAddress = '192.168.1.147';
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [scanInitiated, setScanInitiated] = useState(false); const [scanInitiated, setScanInitiated] = useState(false);
const {wifiNetworks, setWifiNetworks} = useNetworkStore(); const {wifiNetworks, setWifiNetworks} = useNetworkStore();
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null); const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
const handleScan = async () => { const handleScan = async () => {
try { setWifiNetworks([]);
setLoading(true); setLoading(true);
setScanInitiated(true); setScanInitiated(true);
try {
const res = await NetworkService.scanWifi(ipAddress); const res = await NetworkService.scanWifi(ipAddress);
const networks = res.data.access_points || []; const networks = res.data.access_points || [];
setWifiNetworks(networks); setWifiNetworks(networks);
@ -194,16 +167,14 @@ export function WifiScanner() {
return ( return (
<Wrapper scanInitiated={scanInitiated} loading={loading}> <Wrapper scanInitiated={scanInitiated} loading={loading}>
{(!scanInitiated || loading) && (
<TitleWrapper>Сканирование Wi-Fi сети</TitleWrapper> <TitleWrapper>Сканирование Wi-Fi сети</TitleWrapper>
)}
<UserLocate loading={loading} scanInitiated={scanInitiated}> <UserLocate loading={loading} scanInitiated={scanInitiated}>
<span className="center"></span> <span className="center"></span>
<span className="ring1"></span> <span className="ring1"></span>
<span className="ring2"></span> <span className="ring2"></span>
<span className="ring3"></span> <span className="ring3"></span>
{data.map((network) => ( {wifiNetworks.map((network) => (
<WifiDot <WifiDot
key={network.bssid} key={network.bssid}
network={network} network={network}
@ -224,7 +195,7 @@ export function WifiScanner() {
Сканирование сети <ScanningText/> Сканирование сети <ScanningText/>
</LoadingText> </LoadingText>
) : ( ) : (
<ScanBtn onClick={handleScan} disabled={loading} loading={loading}> <ScanBtn onClick={handleScan} disabled={loading} scanInitiated={scanInitiated}>
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'} {wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
</ScanBtn> </ScanBtn>
)} )}

View file

@ -25,7 +25,7 @@ const CloseBtn = styled.div`
top: -20px; top: -20px;
right: -20px; right: -20px;
cursor: pointer; cursor: pointer;
background-color: white; background-color: ${(props) => props.theme.primary};
border-radius: 50%; border-radius: 50%;
display: flex; display: flex;
justify-content: center; justify-content: center;
@ -37,7 +37,7 @@ const CloseBtn = styled.div`
visibility: visible; visibility: visible;
&:hover { &:hover {
background-color: #d6d6d6; background-color: ${(props) => props.theme.hoverBtn};
} }
` `
const ModalTitle = styled.div` const ModalTitle = styled.div`
@ -100,9 +100,9 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
<ModalTitle> <ModalTitle>
<h4>{network.ssid}</h4> <h4>{network.ssid}</h4>
<SignalBarsWrapper> <SignalBarsWrapper>
<SignalBar active={network.signal >= 1} /> <SignalBar active={network.signal >= -120} />
<SignalBar active={network.signal >= 2} /> <SignalBar active={network.signal >= -70} />
<SignalBar active={network.signal >= 3} /> <SignalBar active={network.signal >= -50} />
</SignalBarsWrapper> </SignalBarsWrapper>
</ModalTitle> </ModalTitle>
<ModalContent> <ModalContent>

View file

@ -46,15 +46,24 @@ const WifiDot = ({ network, onSelect }: { network: WifiNetworkScanner, onSelect:
}; };
const getPosition = (signal: number, bssid: string) => { const getPosition = (signal: number, bssid: string) => {
const strength = Math.min(3, Math.max(1, signal)); 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 ringSizes = { 1: 550, 2: 400, 3: 250 };
const ringSize = ringSizes[strength as 1 | 2 | 3]; const ringSize = ringSizes[strength];
const offset = getPositionInRing(bssid, ringSize); const offset = getPositionInRing(bssid, ringSize);
return { return {
top: `calc(50% - ${offset.y}px)`, top: `calc(50% - ${offset.y}px)`,
left: `calc(50% - ${offset.x}px)` left: `calc(50% - ${offset.x}px)`
} };
}; };
const position = getPosition(network.signal, network.bssid); const position = getPosition(network.signal, network.bssid);

View file

@ -57,7 +57,7 @@ export const ToolBar = () => {
</Button> </Button>
<Button> <Button>
<IconContainer> <IconContainer>
<IconComponent name={buttonNames[14]} width="30" height="30" onClick={() => setWorkModule(workSpaceNames[6])} /> <IconComponent name={buttonNames[15]} width="30" height="30" onClick={() => setWorkModule(workSpaceNames[6])} />
</IconContainer> </IconContainer>
</Button> </Button>
</ButtonContainer> </ButtonContainer>

View file

@ -17,6 +17,7 @@ import admin from '@iconify/icons-ic/baseline-admin-panel-settings';
import noAdmin from '@iconify/icons-ic/baseline-remove-moderator' import noAdmin from '@iconify/icons-ic/baseline-remove-moderator'
import refresh from '@iconify/icons-ic/baseline-refresh'; import refresh from '@iconify/icons-ic/baseline-refresh';
import close from '@iconify/icons-ic/round-close'; import close from '@iconify/icons-ic/round-close';
import wifiFind from '@iconify/icons-ic/baseline-wifi-find';
export const ICONS = { export const ICONS = {
[buttonNames[1]]: exitToApp, [buttonNames[1]]: exitToApp,
@ -33,6 +34,7 @@ export const ICONS = {
[buttonNames[12]]: close, [buttonNames[12]]: close,
[buttonNames[13]]: noAdmin, [buttonNames[13]]: noAdmin,
[buttonNames[14]]: userSearch, [buttonNames[14]]: userSearch,
[buttonNames[15]]: wifiFind,
} as const; } as const;
type IconName = keyof typeof ICONS; type IconName = keyof typeof ICONS;

View file

@ -14,5 +14,6 @@ export const buttonNames: names ={
11: 'reset', 11: 'reset',
12: 'close', 12: 'close',
13: 'noAdmin', 13: 'noAdmin',
14: 'userSearch' 14: 'userSearch',
15: 'wifiFind',
} }

View file

@ -11,7 +11,10 @@ export const light = {
secondary: '#EDEDED', secondary: '#EDEDED',
background: '#E8E8E8', background: '#E8E8E8',
textColor: '#000000', textColor: '#000000',
borderColor: '#404040',
scannerColor: '#007ACC', scannerColor: '#007ACC',
offlineColor: '#ababab',
hoverBtn: '#d6d6d6',
}; };
export const dark = { export const dark = {
@ -20,5 +23,8 @@ export const dark = {
secondary: '#252525', secondary: '#252525',
background: '#3A3A3A', background: '#3A3A3A',
textColor: '#FFFFFF', textColor: '#FFFFFF',
borderColor: '#b6b6b6',
scannerColor: '#0E9C9D', scannerColor: '#0E9C9D',
offlineColor: '#656565',
hoverBtn: '#4a4a4a',
}; };

View file

@ -8,6 +8,7 @@ export interface DeviceUser {
open_ports: string[]; open_ports: string[];
status: string; status: string;
last_seen: string; last_seen: string;
note: string;
} }
export interface WifiNetworkScanner { export interface WifiNetworkScanner {