diff --git a/FRONT/DEV/src/api/request.ts b/FRONT/DEV/src/api/request.ts index 27dafd1..99f1f22 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`, }); } diff --git a/FRONT/DEV/src/components/UI/Pagination.tsx b/FRONT/DEV/src/components/UI/Pagination.tsx new file mode 100644 index 0000000..94e63d8 --- /dev/null +++ b/FRONT/DEV/src/components/UI/Pagination.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import styled from "styled-components"; +import {buttonNames} from "../../const/buttons/names"; +import {IconComponent} from "../../const/buttons/icons"; + +const PaginateWrapper = styled.div` + width: 100%; + margin-top: 24px; + display: flex; + justify-content: center; + align-items: center; + gap: 10px; +` +const Page = styled.button` + width: 36px; + height: 36px; + background: ${(props => props.theme.primary)}; + color: ${(props => props.theme.textColor)}; + border: 1px solid ${(props => props.theme.borderColor)}; + font-size: 16px; + cursor: pointer; + border-radius: 4px; + + transition: .4s ease; + &:hover { + background: ${(props => props.theme.background)}; + } +` + +interface PaginationProps { + currentPage: number; + totalPages: number; + onPrevPage: () => void; + onNextPage: () => void; +} + +const Pagination = ({currentPage, totalPages, onPrevPage, onNextPage}: PaginationProps) => { + return ( + + + + + + + + + + ); +}; + +export default Pagination; \ No newline at end of file diff --git a/FRONT/DEV/src/components/UI/tables/TableScanner.tsx b/FRONT/DEV/src/components/UI/tables/TableScanner.tsx index 320f625..8657a17 100644 --- a/FRONT/DEV/src/components/UI/tables/TableScanner.tsx +++ b/FRONT/DEV/src/components/UI/tables/TableScanner.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, {useState} from 'react'; import { DeviceUser } from '../../../store/useDeviceStore'; interface TableScannerProps { @@ -9,18 +9,19 @@ import {IconComponent} from "../../../const/buttons/icons"; import {buttonNames} from "../../../const/buttons/names"; import {ModalMap} from "../../modalwindows/windowsType"; import {useModalContext} from "../../../store"; +import Pagination from "../Pagination"; 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; + width: 100%; + border-collapse: separate; + border-spacing: 0; + box-shadow: 0 4px 24px rgba(0, 0, 0, 0.08); + background: ${({ theme }) => theme.primary}; `; const TableHeaderCell = styled.th<{ sortable?: boolean }>` @@ -28,7 +29,7 @@ const TableHeaderCell = styled.th<{ sortable?: boolean }>` text-align: left; font-weight: 600; font-size: 14px; - color: ${({ theme }) => theme.textSecondary}; + color: ${({ theme }) => theme.textColor}; background: ${({ theme }) => theme.primary}; border-bottom: 1px solid ${({ theme }) => theme.borderColor}; transition: all 0.2s ease; @@ -47,6 +48,11 @@ const TableCell = styled.td` color: ${({ theme }) => theme.text}; border-bottom: 1px solid ${({ theme }) => theme.borderColor}; transition: all 0.2s ease; + button{ + background-color: transparent; + border: none; + cursor: pointer; + } `; const StatusBadge = styled.span<{ status:string }>` @@ -68,7 +74,23 @@ const TableScanner = ({devices}:TableScannerProps) => { }); }; - return ( + // Пагинация + const [currentPage, setCurrentPage] = useState(1); + const [devicesPerPage] = useState(10); + + const indexOfLastDevice = currentPage * devicesPerPage; + const indexOfFirstDevice = indexOfLastDevice - devicesPerPage; + const currentDevices = devices.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); + } + + return ( @@ -84,7 +106,7 @@ const TableScanner = ({devices}:TableScannerProps) => { - {devices.map((item, i) => ( + {currentDevices.map((item, i) => ( {item.hostname || item.ip} @@ -94,14 +116,20 @@ const TableScanner = ({devices}:TableScannerProps) => { {item.last_seen} {item.note || '-'} - openModalNote(e, item)}> + ))} + ); }; diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx index 57207b9..59a4c39 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx @@ -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) { - -

О приложении

- +
+ +

О приложении

+
+

v 0.0.3

+ - + + <p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p> + - - <p>Приложение ГоNets является минимально работоспособным кроссплатформенным продуктом (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p> - + +

Возможности:

+ +
  • Визуализация активных сетевых ресурсов
  • +
  • Настройка параметров локальных и удалённых сетевых ресурсов
  • +
  • Управление взаимодействием ресурсов с Клиентом ГoNets
  • +
    +
    + + + + +

    Программное обеспечение исключительно для ознакомления и домашнего использования.

    + Перейти на ГоNets-PRO +
    - -

    Данное приложение умеет:

    - -

    - Отображать в графическом виде активные сетевые ресурсы

    -

    - Настраивать сетевые параметры локального и удаленных сетевых ресурсов

    -

    - Отображать и изменять взаимодействие сетевых ресурсов с установленным Клиентом ГоNets

    -
    -
    + + + Домашняя страницa + - -

    Программное обеспечение исключительно для ознакомления и домашнего использования.

    -

    Для коммерческого использования приобретите версию ГоNets-PRO (Подробная информация на странице продукта)

    -
    + + Документация + - - -

    Ссылка на домашнюю страницу:

    - https://gossys.dev/pages/projects/gonets -
    - - -

    Инструкции и информация:

    - https://wiki.gossys.dev/ru/gonets -
    - - -

    Распространяется под лицензией GosysPUB текущей версии.

    - -

    Полный текст актуальной лицензии:

    - https://wiki.gossys.dev/ru/lic/GosysPUB -
    -
    - -
    - - -

    Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.

    -
    - -
    + + Лицензия + + + + Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены. + {showBackButton && backHandler && ( - + )} {from == 'reg' && diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx index 5c0099e..52f146e 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/ListUsers.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState } from 'react'; import styled from 'styled-components'; import DeviceCard from "../../../../../UI/Dropdown/DeviceCard"; import {DeviceUser} from "../../../../../../store/useDeviceStore"; @@ -28,10 +28,15 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps const activeDevices = devices.filter(device => device.status === 'online'); const inactiveDevices = devices.filter(device => device.status !== 'online'); + // Пагинация + const [currentPage, setCurrentPage] = useState(1); + const [devicesPerPage] = useState(10); + + return ( <> - {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы подсети'} + {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'} {devices.length > 0 ? ( @@ -42,7 +47,7 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps }}> {/* Колонка активных устройств */}
    -

    Активные устройства ({activeDevices.length})

    +

    Активные узлы ({activeDevices.length})

    {activeDevices.map((device, index) => ( {/* Колонка неактивных устройств */}
    -

    Неактивные устройства ({inactiveDevices.length})

    +

    Неактивные узлы ({inactiveDevices.length})

    {inactiveDevices.map((device, index) => (
    ) : scanInitiated ? ( -

    Устройства не найдены

    +

    Узлы не найдены

    ) : null} ); diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx index cd2e9c4..77e064c 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableUsers.tsx @@ -30,13 +30,13 @@ const ListUsers = ({loading, handleScan, devices, scanInitiated}: ListUsersProps return ( <> - {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы подсети'} + {devices.length > 0 ? 'Обновить список' : 'Сканировать узлы'} {devices.length > 0 ? ( ) : scanInitiated ? ( -

    Устройства не найдены

    +

    Узлы не найдены

    ) : null} ); 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 dd3f035..19ca497 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/userScanner.tsx @@ -77,7 +77,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%); @@ -163,7 +163,7 @@ export default function UserScanner() { return ( - {(!scanInitiated || loading) && (Сканирование узлов в подсети)} + {(!scanInitiated || loading) && (Сканирование узлов подсети)} @@ -177,7 +177,7 @@ export default function UserScanner() { {loading ? ( - Сканирование узлов в подсети + Сканирование узлов ) : selectedView.value == 1 ? ( 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 9cabdd7..25000eb 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx @@ -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 ( - Сканирование точек доступа Wi-Fi + Сканирование точек доступа Wi-Fi сети - {wifiNetworks.map((network) => ( + {wifiNetworks.map((network, index) => ( ))} diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/WifiDot.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/WifiDot.tsx index eccd601..54fee2b 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/WifiDot.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/WifiDot.tsx @@ -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 ( onSelect(network)}> 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/toolSpace/devicesBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index 8b919f4..dd5ca2b 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/DEV/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/const/buttons/icons.tsx b/FRONT/DEV/src/const/buttons/icons.tsx index 301b1d6..9cab629 100644 --- a/FRONT/DEV/src/const/buttons/icons.tsx +++ b/FRONT/DEV/src/const/buttons/icons.tsx @@ -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'; @@ -37,6 +38,7 @@ export const ICONS = { [buttonNames[14]]: userSearch, [buttonNames[15]]: wifiFind, [buttonNames[16]]: noteEdit, + [buttonNames[17]]: arrowLeft, } as const; type IconName = keyof typeof ICONS; diff --git a/FRONT/DEV/src/const/buttons/names.ts b/FRONT/DEV/src/const/buttons/names.ts index 54c1d40..052321e 100644 --- a/FRONT/DEV/src/const/buttons/names.ts +++ b/FRONT/DEV/src/const/buttons/names.ts @@ -16,5 +16,6 @@ export const buttonNames: names ={ 13: 'noAdmin', 14: 'userSearch', 15: 'wifiFind', - 16: 'noteEdit' + 16: 'noteEdit', + 17: 'arrowLeft', } \ No newline at end of file diff --git a/FRONT/DEV/src/const/menus/sideBar/icons.tsx b/FRONT/DEV/src/const/menus/sideBar/icons.tsx index 1d80d6b..b5560ce 100644 --- a/FRONT/DEV/src/const/menus/sideBar/icons.tsx +++ b/FRONT/DEV/src/const/menus/sideBar/icons.tsx @@ -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, diff --git a/FRONT/DEV/src/const/theme/theme.ts b/FRONT/DEV/src/const/theme/theme.ts index aab3658..dbf1cb3 100644 --- a/FRONT/DEV/src/const/theme/theme.ts +++ b/FRONT/DEV/src/const/theme/theme.ts @@ -24,7 +24,7 @@ export const dark = { background: '#3A3A3A', textColor: '#FFFFFF', borderColor: '#b6b6b6', - scannerColor: '#0E9C9D', + scannerColor: '#00E5FF', offlineColor: '#656565', hoverBtn: '#4a4a4a', }; \ No newline at end of file