diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index 8b919f4..dd5ca2b 100644 --- a/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/BUILD/resources/app/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -115,7 +115,7 @@ export const DevicesBar = () => { const checkWebSocketAvailability = async (ip: string): Promise => { return new Promise((resolve) => { - const testSocket = new WebSocket(`wss://${ip}:8001/api/v1`); + const testSocket = new WebSocket(`ws://${ip}:8001/api/v1`); const timeout = setTimeout(() => { testSocket.close(); resolve(false); diff --git a/FRONT/DEV/src/api/request.ts b/FRONT/DEV/src/api/request.ts index 11f08de..ce00ee9 100644 --- a/FRONT/DEV/src/api/request.ts +++ b/FRONT/DEV/src/api/request.ts @@ -1,13 +1,13 @@ import axios, { AxiosResponse } from "axios"; const axiosWithoutCredentials = axios.create({ - baseURL: `https://localhost:8001`, + baseURL: `http://localhost:8001`, }) export class NetworkService { static createAxiosInstance(ipAddress: string) { return axios.create({ - baseURL: `https://${ipAddress}:8001`, + baseURL: `http://${ipAddress}:8001`, }); } @@ -86,7 +86,9 @@ export class NetworkService { static async scanUser(ipAddress: string): Promise { try { const axiosInstance = this.createAxiosInstance(ipAddress); - return await axiosInstance.get('/subnet/devices'); + return await axiosInstance.post('/subnet/devices',{ + subnet: '' + }); } catch (error: any) { return error.response; } @@ -157,6 +159,8 @@ export class NetworkService { return error.response; } } + + // Settings static async clearSubnet(ipAddress: string): Promise { try { const axiosInstance = this.createAxiosInstance(ipAddress); @@ -173,4 +177,12 @@ export class NetworkService { return error.response; } } + static async exportDevices(ipAddress: string): Promise { + try { + const axiosInstance = this.createAxiosInstance(ipAddress); + return await axiosInstance.post(`/settings/export`); + } catch (error: any) { + return error.response; + } + } } \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx new file mode 100644 index 0000000..dfa8037 --- /dev/null +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx @@ -0,0 +1,131 @@ +import React, {useEffect, useState} from 'react'; +import {ButtonContainer, ModalContainer, ModalContent, ModalTitle} from "../style"; +import {MainButton} from "../../UI/buttons/mainButton"; +import {ModalMap} from "../windowsType"; +import {useModalContext} from "../../../store"; +import styled from "styled-components"; +import { useSettingsStore } from '../../../store/settings/settingsStore'; + +const StyledTableList = styled.ul` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + box-sizing: border-box; + padding: 0 40px; + list-style-type: none; +` +const StyledTableItem = styled.li` + width: 160px; + height: 60px; + display: flex; + justify-content: center; + align-items: center; + text-align: center; + background-color: ${props => props.theme.accent}; + color: ${props => props.theme.primary}; + border: 1px solid ${props => props.theme.primary}; + + &:first-child { + border-radius: 16px 0 0 16px; + } + &:last-child { + border-radius: 0 16px 16px 0; + } + &.off{ + background-color: ${props => props.theme.offlineColor}; + } +` +const CustomizeNodes = () => { + const { openModal } = useModalContext(); + const pendingChanges = useSettingsStore(state => state.pendingChanges); + const updateSetting = useSettingsStore(state => state.updateSetting); + const getSetting = useSettingsStore(state => state.getSetting); + const initializeSettings = useSettingsStore(state => state.initializeSettings); + + const [dragIndex, setDragIndex] = useState(null); + + // Получаем текущие значения из хранилища + const initialColumns = getSetting('table-columns-order').currentValue as string[]; + const disabledColumns = getSetting('table-columns-disabled').currentValue as number[]; + const items = pendingChanges['table-columns-order'] ?? initialColumns; + const disabledItems = pendingChanges['table-columns-disabled'] ?? disabledColumns; + + // Drag & Drop + const handleDragStart = (index: number) => { + setDragIndex(index); + }; + const handleDragOver = (index: number) => { + if (dragIndex === null || index === dragIndex) return; + + const newItems = [...items]; + const [movedItem] = newItems.splice(dragIndex, 1); + newItems.splice(index, 0, movedItem); + + updateSetting('table-columns-order', newItems); + setDragIndex(index); + }; + const handleDrop = () => { + setDragIndex(null); + }; + + // Переключение состояния + const handleSwitch = (index: number) => { + const newDisabledItems = disabledItems.includes(index) + ? disabledItems.filter((i: number) => i !== index) + : [...disabledItems, index]; + + updateSetting('table-columns-disabled', newDisabledItems); + }; + + // Закрытие с сохранением + const handleSave = () => { + openModal(ModalMap['10']); + }; + + + useEffect(() => { + initializeSettings(); + }, [initializeSettings]); + + return ( + + +

Кастомизация таблицы узлов подсети

+ +
+ + + + {items.map((item:string, index:number) => ( + handleDragStart(index)} + onDragOver={(e) => { + e.preventDefault(); + handleDragOver(index); + }} + onDragEnd={handleDrop} + onClick={() => handleSwitch(index)} + > + {item} + + ))} + + + + + + +
+ ); +}; + +export default CustomizeNodes; \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalWindow.tsx b/FRONT/DEV/src/components/modalwindows/modalWindow.tsx index b6dbac9..f745b4f 100644 --- a/FRONT/DEV/src/components/modalwindows/modalWindow.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalWindow.tsx @@ -8,6 +8,7 @@ import { NoteWifi } from "./modalContainers/NoteWifi"; import {ConnectNetwork} from "./modalContainers/ConnectNetwork"; import {DisconnectConfirm} from "./modalContainers/disconnectConfirm"; import {Settings} from "../settings/Settings"; +import CustomizeNodes from "./modalContainers/customizeNodes"; type Props = { isOpen: boolean, @@ -80,6 +81,9 @@ export default function WindowComponent({ isOpen, type, data }: Props) { onClose={closeLoader} /> )} + {type === ModalMap['11'] && ( + + )} } diff --git a/FRONT/DEV/src/components/modalwindows/style.ts b/FRONT/DEV/src/components/modalwindows/style.ts index 2e588c2..f0a2639 100644 --- a/FRONT/DEV/src/components/modalwindows/style.ts +++ b/FRONT/DEV/src/components/modalwindows/style.ts @@ -32,6 +32,7 @@ export const ModalTitle = styled.div` padding: 20px; border-bottom: 1px solid #9D9D9D; display: flex; + align-items: center; justify-content: space-between; h3{ diff --git a/FRONT/DEV/src/components/modalwindows/windowsType.ts b/FRONT/DEV/src/components/modalwindows/windowsType.ts index ae6d760..9193a6f 100644 --- a/FRONT/DEV/src/components/modalwindows/windowsType.ts +++ b/FRONT/DEV/src/components/modalwindows/windowsType.ts @@ -10,4 +10,6 @@ export const ModalMap: ModalType = { '7': 'noteWifi', '8': 'connectNetwork', '9': 'disconnectConfirm', + "10": 'settings', + '11': 'customNodes', } diff --git a/FRONT/DEV/src/components/settings/SettingItem.tsx b/FRONT/DEV/src/components/settings/SettingItem.tsx index f1f850c..80014d1 100644 --- a/FRONT/DEV/src/components/settings/SettingItem.tsx +++ b/FRONT/DEV/src/components/settings/SettingItem.tsx @@ -6,6 +6,8 @@ import {buttonNames} from "../../const/buttons/names"; import SettingSelect from "./SettingSelect"; import {MainButton} from "../UI/buttons/mainButton"; import React from 'react'; +import {useModalContext} from "../../store"; +import {ModalMap} from "../modalwindows/windowsType"; const SettingBlock = styled.div` display: flex; @@ -19,6 +21,7 @@ const SettingBlock = styled.div` font-size: 16px; outline: none; background-color: ${props => props.theme.primary}; + color: ${props => props.theme.textColor}; border: 1px solid ${props => props.theme.borderColor}; padding: 6px 8px; border-radius: 4px; @@ -73,7 +76,7 @@ const InputWrapper = styled.div` gap: 6px; ` const ErrorText = styled.div` - color: red; + color: ${props => props.theme.errorColor}; font-size: 12px; `; @@ -87,7 +90,17 @@ interface SettingItemProps { const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => { const theme = useTheme(); const [error, setError] = useState(null); + const { openModal } = useModalContext(); + const handleButtonAction = () => { + if (setting.id === 'customize-nodes') { + openModal(ModalMap['11']); + } else if (setting.action) { + setting.action(); + } + }; + + // Изменение числа const handleNumberChange = (e: React.ChangeEvent) => { const inputValue = e.target.value; let newError: string | null = null; @@ -172,9 +185,10 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: type="button" size="small" title={setting.defaultValue || ''} - onClick={setting.action} + onClick={handleButtonAction} /> )} + {setting.type === 'hidden' && null} ); }); diff --git a/FRONT/DEV/src/components/settings/Settings.tsx b/FRONT/DEV/src/components/settings/Settings.tsx index eb1aad9..9723bc4 100644 --- a/FRONT/DEV/src/components/settings/Settings.tsx +++ b/FRONT/DEV/src/components/settings/Settings.tsx @@ -6,8 +6,6 @@ import {Setting, useSettingsStore} from "../../store/settings/settingsStore"; import {SettingItem} from "./SettingItem"; import {IconComponent} from "../../const/buttons/icons"; import {buttonNames} from "../../const/buttons/names"; -import {ModalMap} from "../modalwindows/windowsType"; -import {workSpaceNames} from "../../const/workspace/names"; interface Props { onClose: () => void; @@ -129,7 +127,7 @@ export const Settings = ({ onClose }: Props) => { }, [discardChanges, onClose]); return ( - +

Настройки

@@ -168,6 +166,13 @@ export const Settings = ({ onClose }: Props) => { + ` const TableScanner = ({devices}:TableScannerProps) => { const { openModal } = useModalContext(); + const nameSetting = useNameSetting(); const openModalNote = (e: React.MouseEvent, device:DeviceUser ) => { e.stopPropagation(); @@ -75,7 +80,27 @@ const TableScanner = ({devices}:TableScannerProps) => { currentNote: device.note || '' }); }; - const nameSetting = useNameSetting(); + + // Применение настроек отображения + const ordersColumns = useOrdersColumns(); + const disabledColumns = useDisabledColumns(); + const columnVisibility = useMemo(() => { + return { + status: !disabledColumns.includes(ordersColumns.indexOf('Онлайн')), + name: !disabledColumns.includes(ordersColumns.indexOf('Имя')), + vendor: !disabledColumns.includes(ordersColumns.indexOf('Производитель')), + ip: !disabledColumns.includes(ordersColumns.indexOf('IP адрес')), + ports: !disabledColumns.includes(ordersColumns.indexOf('Открытые порты')), + activity: !disabledColumns.includes(ordersColumns.indexOf('Последняя активность')), + note: !disabledColumns.includes(ordersColumns.indexOf('Заметка')), + action: !disabledColumns.includes(ordersColumns.indexOf('Действие')) + }; + }, [ordersColumns, disabledColumns]); + const sortedColumns = useMemo(() => { + return ordersColumns.filter(column => + !disabledColumns.includes(ordersColumns.indexOf(column)) + ); + }, [ordersColumns, disabledColumns]); // Пагинация const paginateSetting = usePaginateSetting(); @@ -105,32 +130,42 @@ const TableScanner = ({devices}:TableScannerProps) => { - - - Имя - Производитель - IP адрес - Открытые порты - Последняя активность - Заметка - - + + {columnVisibility.status && } + {columnVisibility.name && Имя} + {columnVisibility.vendor && Производитель} + {columnVisibility.ip && IP адрес} + {columnVisibility.ports && Открытые порты} + {columnVisibility.activity && Последняя активность} + {columnVisibility.note && Заметка} + {columnVisibility.action && } + {currentDevices.map((item, i) => ( - - {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')} - {item.vendor} - {item.ip} - {item.open_ports.join(', ') || '-'} - {item.last_seen} - {item.note || '-'} - - - + {columnVisibility.status && ( + + )} + {columnVisibility.name && ( + + {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')} + + )} + {columnVisibility.vendor && {item.vendor}} + {columnVisibility.ip && {item.ip}} + {columnVisibility.ports && ( + {item.open_ports.join(', ') || '-'} + )} + {columnVisibility.activity && {item.last_seen}} + {columnVisibility.note && {item.note || '-'}} + {columnVisibility.action && ( + + + + )} ))} diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx index 8a098b8..dbb8821 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx @@ -144,7 +144,7 @@ export default function UserScanner() { } setScanInitiated(true); const res = await NetworkService.scanUser(ipAddress); - console.log(res); + console.log(res) setDevices(res.data.devices || []); } catch (err) { console.error("Ошибка сканирования сети:", err); diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx index 25000eb..ea4d20b 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx @@ -137,7 +137,7 @@ const ScanBtn = styled.button<{scanInitiated: boolean}>` export function WifiScanner() { //const { ipAddress } = useIpStore(); - const ipAddress = '192.168.1.147' + const ipAddress = '192.168.1.148' const [loading, setLoading] = useState(false); const [scanInitiated, setScanInitiated] = useState(false); const { wifiNetworks, setWifiNetworks } = useNetworkStore(); diff --git a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx index 1996ddd..d0325ef 100644 --- a/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/workModules/networkInterfaces.tsx @@ -76,7 +76,7 @@ export const NetworkInterfaces = ({ startConnection, ws }: Props) => { setHasInited(false) initializationAttempt.current = 0; - const newSocket = new WebSocket(`wss://${ws}:8001/api/v1`); + const newSocket = new WebSocket(`ws://${ws}:8001/api/v1`); let connectionTimeout; setSocket(newSocket); diff --git a/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx index 1f49b9c..f6d5825 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx +++ b/FRONT/DEV/src/components/workSpace/sideBar/toolBar/toolBar.tsx @@ -69,7 +69,7 @@ export const ToolBar = () => { - + {/**/}