Добавление заметок
This commit is contained in:
parent
ea56d17ea7
commit
e61ffd793c
20 changed files with 274 additions and 118 deletions
|
|
@ -95,7 +95,35 @@ export class NetworkService {
|
|||
static async scanWifi(ipAddress: string): Promise<AxiosResponse> {
|
||||
try {
|
||||
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) {
|
||||
return error.response;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,6 +1,10 @@
|
|||
import React from 'react';
|
||||
import React, {useState} from 'react';
|
||||
import styled, { css } from 'styled-components';
|
||||
import { DeviceUser } from "../../../store/useDeviceStore";
|
||||
import {buttonNames} from "../../../const/buttons/names";
|
||||
import {ModalMap} from "../../modalwindows/windowsType";
|
||||
import {IconComponent} from "../../../const/buttons/icons";
|
||||
import {useModalContext} from "../../../store";
|
||||
|
||||
interface DeviceCardProps {
|
||||
device: DeviceUser;
|
||||
|
|
@ -24,7 +28,7 @@ const CardWrapper = styled.div<{ status: 'online' | 'offline' }>`
|
|||
|
||||
const CardHeader = styled.div<{ status: 'online' | 'offline' }>`
|
||||
background: ${({ status, theme }) =>
|
||||
status === 'online' ? theme.successColor : theme.errorColor};
|
||||
status === 'online' ? theme.successColor : theme.offlineColor};
|
||||
color: white;
|
||||
padding: 16px;
|
||||
font-size: 18px;
|
||||
|
|
@ -67,9 +71,19 @@ const Value = styled.span`
|
|||
color: #333;
|
||||
`;
|
||||
|
||||
|
||||
const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
||||
const [isExpanded, setIsExpanded] = React.useState(false);
|
||||
const [isExpanded, setIsExpanded] = useState(false);
|
||||
const toggleExpanded = () => setIsExpanded(!isExpanded);
|
||||
const { openModal } = useModalContext();
|
||||
|
||||
const openModalNote = (e: React.MouseEvent) => {
|
||||
e.stopPropagation();
|
||||
openModal(ModalMap['6'], {
|
||||
mac: device.mac,
|
||||
currentNote: device.note || ''
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
|
|
@ -78,6 +92,9 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
<p>
|
||||
MAC: {device.mac}
|
||||
</p>
|
||||
<span onClick={openModalNote}>
|
||||
<IconComponent name={buttonNames[7]} width="24" height="24" setColor={'white'} />
|
||||
</span>
|
||||
</CardHeader>
|
||||
|
||||
<CardContent isExpanded={isExpanded}>
|
||||
|
|
@ -107,6 +124,11 @@ const DeviceCard: React.FC<DeviceCardProps> = ({ device, status }) => {
|
|||
<Label>Последняя активность:</Label>
|
||||
<Value>{device.last_seen}</Value>
|
||||
</InfoRow>
|
||||
|
||||
<InfoRow>
|
||||
<Label>Заметка:</Label>
|
||||
<Value>{device.note || '-'}</Value>
|
||||
</InfoRow>
|
||||
</CardContent>
|
||||
</CardWrapper>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ const DropdownRow = styled.button`
|
|||
font-size: 16px;
|
||||
border: none;
|
||||
width: 100%;
|
||||
color: ${({theme}) => theme.textColor};
|
||||
transition: all .3s ease;
|
||||
cursor: pointer;
|
||||
|
||||
|
|
|
|||
|
|
@ -9,15 +9,15 @@ const Button = styled.button.withConfig({
|
|||
border: none;
|
||||
padding: ${(props) =>
|
||||
props.size === "small"
|
||||
? "4px 8px"
|
||||
? "10px 28px"
|
||||
: props.size === "big"
|
||||
? "10px 20px"
|
||||
: "6px 10px"}; // По умолчанию
|
||||
: "12px 20px"}; // По умолчанию
|
||||
border-radius: 8px;
|
||||
color: white;
|
||||
font-size: ${(props) =>
|
||||
props.size === "small"
|
||||
? "12px"
|
||||
? "14px"
|
||||
: props.size === "big"
|
||||
? "16px"
|
||||
: "14px"}; // По умолчанию
|
||||
|
|
@ -30,7 +30,7 @@ const Button = styled.button.withConfig({
|
|||
? 'row-reverse'
|
||||
: 'row'};
|
||||
gap: 10px;
|
||||
|
||||
transition: all .3s ease;
|
||||
&:hover{
|
||||
background-color: ${(props) => props.$hoverсolor};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ type Props = {
|
|||
placeholderText: string;
|
||||
id: string;
|
||||
value?: string;
|
||||
onChange?: (identifier: string, value: string) => void;
|
||||
onChange?: (value: string) => void;
|
||||
onFileChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
|
||||
type?: string;
|
||||
multiple?: boolean;
|
||||
|
|
@ -24,7 +24,7 @@ const InputContainer = styled.div`
|
|||
`;
|
||||
|
||||
const StyledInput = styled.input`
|
||||
padding: 8px 10px;
|
||||
padding: 12px 10px;
|
||||
color: black;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #A1A1A1;
|
||||
|
|
@ -41,7 +41,7 @@ const Input = React.forwardRef<HTMLInputElement, Props>(({ placeholderText, id,
|
|||
const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = e.target;
|
||||
if (onChange) {
|
||||
onChange(id, value);
|
||||
onChange(value);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,10 @@ 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%;
|
||||
|
|
@ -48,12 +52,21 @@ const TableCell = styled.td`
|
|||
const StatusBadge = styled.span<{ status:string }>`
|
||||
width: 10px;
|
||||
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;
|
||||
border-radius:4px;
|
||||
`
|
||||
|
||||
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 (
|
||||
<TableContainer>
|
||||
|
|
@ -66,6 +79,8 @@ const TableScanner = ({devices}:TableScannerProps) => {
|
|||
<TableHeaderCell>IP адрес</TableHeaderCell>
|
||||
<TableHeaderCell>Открытые порты</TableHeaderCell>
|
||||
<TableHeaderCell>Последняя активность</TableHeaderCell>
|
||||
<TableHeaderCell>Заметка</TableHeaderCell>
|
||||
<TableHeaderCell></TableHeaderCell>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -77,6 +92,12 @@ const TableScanner = ({devices}:TableScannerProps) => {
|
|||
<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[7]} width="24" height="24" />
|
||||
</span>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
))}
|
||||
</tbody>
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
@ -3,6 +3,7 @@ import { IpConfirm, ConnectionError, AddDevice, RightsConfirm } from "./modalCon
|
|||
import { ModalMap } from "./windowsType";
|
||||
import Loader from "../UI/loader";
|
||||
import { ModalWindow } from "./style";
|
||||
import {NoteDevice} from "./modalContainers/NoteDevice";
|
||||
|
||||
type Props = {
|
||||
isOpen: boolean,
|
||||
|
|
@ -35,6 +36,13 @@ export default function WindowComponent({ isOpen, type, data }: Props) {
|
|||
message={data.message}
|
||||
/>
|
||||
}
|
||||
{type === ModalMap['6'] && (
|
||||
<NoteDevice
|
||||
mac={data.mac}
|
||||
currentNote={data.currentNote}
|
||||
onClose={closeLoader}
|
||||
/>
|
||||
)}
|
||||
|
||||
{type === ModalMap['4'] && (
|
||||
<AddDevice onSave={data?.onSave} onClose={closeLoader} />
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ export const ModalContainer = styled.div<{height?: string}>`
|
|||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
gap: 15px;
|
||||
height: ${(props) => props.height ? props.height : '200px'};
|
||||
height: ${(props) => props.height ? props.height : '230px'};
|
||||
width: 520px;
|
||||
`
|
||||
|
||||
|
|
|
|||
|
|
@ -5,5 +5,6 @@ export const ModalMap: ModalType = {
|
|||
'2': 'loader',
|
||||
'3': 'connectionError',
|
||||
'4': 'addDevice',
|
||||
'5': 'sudo'
|
||||
'5': 'sudo',
|
||||
'6': 'noteDevice',
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,8 +25,6 @@ interface ListUsersProps {
|
|||
}
|
||||
|
||||
const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps) => {
|
||||
|
||||
// Фильтр по статусу
|
||||
const activeDevices = 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 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) => (
|
||||
<DeviceCard
|
||||
key={index}
|
||||
|
|
|
|||
|
|
@ -14,21 +14,21 @@ type Option ={
|
|||
|
||||
// Анимации
|
||||
const ring1 = keyframes`
|
||||
0% { width: 250px; height: 250px; }
|
||||
50% { width: 280px; height: 280px; }
|
||||
100% { width: 250px; height: 250px; }
|
||||
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.2; }
|
||||
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
|
||||
`;
|
||||
|
||||
const ring2 = keyframes`
|
||||
0% { width: 400px; height: 400px; }
|
||||
50% { width: 430px; height: 430px; }
|
||||
100% { width: 400px; height: 400px; }
|
||||
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.15; }
|
||||
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
|
||||
`;
|
||||
|
||||
const ring3 = keyframes`
|
||||
0% { width: 550px; height: 550px; }
|
||||
50% { width: 580px; height: 580px; }
|
||||
100% { width: 550px; height: 550px; }
|
||||
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.1; }
|
||||
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
|
||||
`;
|
||||
const dotAnimation = keyframes`
|
||||
0% { content: '.'; }
|
||||
|
|
@ -36,6 +36,11 @@ const dotAnimation = keyframes`
|
|||
66% { content: '...'; }
|
||||
100% { content: '.'; }
|
||||
`;
|
||||
const pulse = keyframes`
|
||||
0% { transform: translate(-50%, -50%) scale(1); }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); }
|
||||
100% { transform: translate(-50%, -50%) scale(1); }
|
||||
`;
|
||||
|
||||
// Стили
|
||||
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
|
||||
|
|
@ -60,8 +65,8 @@ const TitleWrapper = styled.h2`
|
|||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 32px;
|
||||
width: max-content;
|
||||
`
|
||||
|
||||
const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
|
@ -80,7 +85,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
&.center {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
||||
animation: ${({ loading }) => (loading ? pulse : "none")} 2s ease infinite;
|
||||
}
|
||||
&.ring1 {
|
||||
opacity: 0.12;
|
||||
|
|
@ -94,6 +99,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
width: 400px;
|
||||
height: 400px;
|
||||
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
|
||||
animation-delay: ${({ loading }) => (loading ? "0.1s" : "0")};
|
||||
}
|
||||
|
||||
&.ring3 {
|
||||
|
|
@ -101,9 +107,9 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
width: 550px;
|
||||
height: 550px;
|
||||
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
|
||||
animation-delay: ${({ loading }) => (loading ? "0.2s" : "0")};
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
const LoadingText = styled.div`
|
||||
|
|
|
|||
|
|
@ -15,21 +15,19 @@ type Option ={
|
|||
|
||||
// Анимации
|
||||
const ring1 = keyframes`
|
||||
0% { width: 250px; height: 250px; }
|
||||
50% { width: 280px; height: 280px; }
|
||||
100% { width: 250px; height: 250px; }
|
||||
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.2; }
|
||||
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.12; }
|
||||
`;
|
||||
|
||||
const ring2 = keyframes`
|
||||
0% { width: 400px; height: 400px; }
|
||||
50% { width: 430px; height: 430px; }
|
||||
100% { width: 400px; height: 400px; }
|
||||
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.15; }
|
||||
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.08; }
|
||||
`;
|
||||
|
||||
const ring3 = keyframes`
|
||||
0% { width: 550px; height: 550px; }
|
||||
50% { width: 580px; height: 580px; }
|
||||
100% { width: 550px; height: 550px; }
|
||||
0% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); opacity: 0.1; }
|
||||
100% { transform: translate(-50%, -50%) scale(1); opacity: 0.04; }
|
||||
`;
|
||||
const dotAnimation = keyframes`
|
||||
0% { content: '.'; }
|
||||
|
|
@ -37,13 +35,18 @@ const dotAnimation = keyframes`
|
|||
66% { content: '...'; }
|
||||
100% { content: '.'; }
|
||||
`;
|
||||
const pulse = keyframes`
|
||||
0% { transform: translate(-50%, -50%) scale(1); }
|
||||
50% { transform: translate(-50%, -50%) scale(1.1); }
|
||||
100% { transform: translate(-50%, -50%) scale(1); }
|
||||
`;
|
||||
|
||||
// Стили
|
||||
const Wrapper = styled.div<{ scanInitiated: boolean, loading: boolean}>`
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content:${({scanInitiated, loading})=>(scanInitiated && !loading ? 'start' : 'center')};
|
||||
justify-content:center ;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: relative;
|
||||
|
|
@ -69,7 +72,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
border-radius: 50%;
|
||||
position: relative;
|
||||
transition: .4s ease;
|
||||
transform: ${({scanInitiated,loading})=>(scanInitiated && !loading ? 'scale(0)' : 'scale(1)')};
|
||||
transform: scale(1);
|
||||
span {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
|
|
@ -81,7 +84,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
&.center {
|
||||
width: 60px;
|
||||
height: 60px;
|
||||
|
||||
animation: ${({ loading }) => (loading ? pulse : "none")} 2s ease infinite;
|
||||
}
|
||||
&.ring1 {
|
||||
opacity: 0.12;
|
||||
|
|
@ -95,6 +98,7 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
width: 400px;
|
||||
height: 400px;
|
||||
animation: ${({ loading }) => (loading ? ring2 : "none")} 4s linear infinite;
|
||||
animation-delay: ${({ loading }) => (loading ? "0.1s" : "0")};
|
||||
}
|
||||
|
||||
&.ring3 {
|
||||
|
|
@ -102,11 +106,12 @@ const UserLocate = styled.div<{ loading: boolean, scanInitiated: boolean }>`
|
|||
width: 550px;
|
||||
height: 550px;
|
||||
animation: ${({ loading }) => (loading ? ring3 : "none")} 4s linear infinite;
|
||||
animation-delay: ${({ loading }) => (loading ? "0.2s" : "0")};
|
||||
}
|
||||
}
|
||||
|
||||
`;
|
||||
|
||||
|
||||
const LoadingText = styled.div`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
|
|
@ -122,7 +127,7 @@ const ScanningText = styled.span`
|
|||
animation: ${dotAnimation} 1.5s infinite;
|
||||
}
|
||||
`;
|
||||
const ScanBtn = styled.button<{loading: boolean}>`
|
||||
const ScanBtn = styled.button<{scanInitiated: boolean}>`
|
||||
position: relative;
|
||||
padding: 14px 22px;
|
||||
background-color: #4CAF50;
|
||||
|
|
@ -130,57 +135,25 @@ const ScanBtn = styled.button<{loading: boolean}>`
|
|||
border: none;
|
||||
border-radius: 12px;
|
||||
font-size: 18px;
|
||||
transition: .4s ease;
|
||||
transform: ${({loading}) => (!loading ? "translateY(240px)" : "translateY(0)")};
|
||||
transition: transform .4s ease;
|
||||
transform: ${({scanInitiated}) => (scanInitiated ? "translateY(240px)" : "translateY(0)")};
|
||||
&: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() {
|
||||
const {ipAddress} = useIpStore();
|
||||
const ipAddress = '192.168.1.147';
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [scanInitiated, setScanInitiated] = useState(false);
|
||||
const {wifiNetworks, setWifiNetworks} = useNetworkStore();
|
||||
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
|
||||
|
||||
const handleScan = async () => {
|
||||
try {
|
||||
setWifiNetworks([]);
|
||||
setLoading(true);
|
||||
setScanInitiated(true);
|
||||
try {
|
||||
const res = await NetworkService.scanWifi(ipAddress);
|
||||
const networks = res.data.access_points || [];
|
||||
setWifiNetworks(networks);
|
||||
|
|
@ -194,16 +167,14 @@ export function WifiScanner() {
|
|||
|
||||
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) => (
|
||||
{wifiNetworks.map((network) => (
|
||||
<WifiDot
|
||||
key={network.bssid}
|
||||
network={network}
|
||||
|
|
@ -224,7 +195,7 @@ export function WifiScanner() {
|
|||
Сканирование сети <ScanningText/>
|
||||
</LoadingText>
|
||||
) : (
|
||||
<ScanBtn onClick={handleScan} disabled={loading} loading={loading}>
|
||||
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={scanInitiated}>
|
||||
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать сеть'}
|
||||
</ScanBtn>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ const CloseBtn = styled.div`
|
|||
top: -20px;
|
||||
right: -20px;
|
||||
cursor: pointer;
|
||||
background-color: white;
|
||||
background-color: ${(props) => props.theme.primary};
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
@ -37,7 +37,7 @@ const CloseBtn = styled.div`
|
|||
visibility: visible;
|
||||
|
||||
&:hover {
|
||||
background-color: #d6d6d6;
|
||||
background-color: ${(props) => props.theme.hoverBtn};
|
||||
}
|
||||
`
|
||||
const ModalTitle = styled.div`
|
||||
|
|
@ -100,9 +100,9 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
|||
<ModalTitle>
|
||||
<h4>{network.ssid}</h4>
|
||||
<SignalBarsWrapper>
|
||||
<SignalBar active={network.signal >= 1} />
|
||||
<SignalBar active={network.signal >= 2} />
|
||||
<SignalBar active={network.signal >= 3} />
|
||||
<SignalBar active={network.signal >= -120} />
|
||||
<SignalBar active={network.signal >= -70} />
|
||||
<SignalBar active={network.signal >= -50} />
|
||||
</SignalBarsWrapper>
|
||||
</ModalTitle>
|
||||
<ModalContent>
|
||||
|
|
|
|||
|
|
@ -46,15 +46,24 @@ const WifiDot = ({ network, onSelect }: { network: WifiNetworkScanner, onSelect:
|
|||
};
|
||||
|
||||
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 ringSize = ringSizes[strength as 1 | 2 | 3];
|
||||
const ringSize = ringSizes[strength];
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ export const ToolBar = () => {
|
|||
</Button>
|
||||
<Button>
|
||||
<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>
|
||||
</Button>
|
||||
</ButtonContainer>
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ import admin from '@iconify/icons-ic/baseline-admin-panel-settings';
|
|||
import noAdmin from '@iconify/icons-ic/baseline-remove-moderator'
|
||||
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';
|
||||
|
||||
export const ICONS = {
|
||||
[buttonNames[1]]: exitToApp,
|
||||
|
|
@ -33,6 +34,7 @@ export const ICONS = {
|
|||
[buttonNames[12]]: close,
|
||||
[buttonNames[13]]: noAdmin,
|
||||
[buttonNames[14]]: userSearch,
|
||||
[buttonNames[15]]: wifiFind,
|
||||
} as const;
|
||||
|
||||
type IconName = keyof typeof ICONS;
|
||||
|
|
|
|||
|
|
@ -14,5 +14,6 @@ export const buttonNames: names ={
|
|||
11: 'reset',
|
||||
12: 'close',
|
||||
13: 'noAdmin',
|
||||
14: 'userSearch'
|
||||
14: 'userSearch',
|
||||
15: 'wifiFind',
|
||||
}
|
||||
|
|
@ -11,7 +11,10 @@ export const light = {
|
|||
secondary: '#EDEDED',
|
||||
background: '#E8E8E8',
|
||||
textColor: '#000000',
|
||||
borderColor: '#404040',
|
||||
scannerColor: '#007ACC',
|
||||
offlineColor: '#ababab',
|
||||
hoverBtn: '#d6d6d6',
|
||||
};
|
||||
|
||||
export const dark = {
|
||||
|
|
@ -20,5 +23,8 @@ export const dark = {
|
|||
secondary: '#252525',
|
||||
background: '#3A3A3A',
|
||||
textColor: '#FFFFFF',
|
||||
borderColor: '#b6b6b6',
|
||||
scannerColor: '#0E9C9D',
|
||||
offlineColor: '#656565',
|
||||
hoverBtn: '#4a4a4a',
|
||||
};
|
||||
|
|
@ -8,6 +8,7 @@ export interface DeviceUser {
|
|||
open_ports: string[];
|
||||
status: string;
|
||||
last_seen: string;
|
||||
note: string;
|
||||
}
|
||||
|
||||
export interface WifiNetworkScanner {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue