From 4286f28d1e92ec1fec7509527c4c63339b04cd5f Mon Sep 17 00:00:00 2001 From: test Date: Mon, 11 Aug 2025 13:56:43 +0500 Subject: [PATCH] =?UTF-8?q?=D0=9F=D0=BE=D0=BB=D0=BD=D0=B0=D1=8F=20=D0=B8?= =?UTF-8?q?=D0=BD=D1=82=D0=B5=D1=80=D0=BD=D0=B0=D1=86=D0=B8=D0=BE=D0=BD?= =?UTF-8?q?=D0=B0=D0=BB=D0=B8=D0=B7=D0=B0=D1=86=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- FRONT/DEV/public/locales/en/common.json | 190 +++++++++++- FRONT/DEV/public/locales/ru/common.json | 214 +++++++++++-- .../modalContainers/ConnectNetwork.tsx | 43 +-- .../modalContainers/NoteDevice.tsx | 11 +- .../modalwindows/modalContainers/NoteWifi.tsx | 10 +- .../modalContainers/addDevice.tsx | 24 +- .../modalContainers/connectionError.tsx | 24 +- .../modalContainers/customizeNodes.tsx | 22 +- .../modalContainers/disconnectConfirm.tsx | 25 +- .../modalContainers/ipConfirm.tsx | 50 +-- .../modalContainers/rightsConfirm.tsx | 65 ++-- FRONT/DEV/src/components/register.tsx | 132 ++++---- .../src/components/settings/SettingItem.tsx | 40 ++- .../DEV/src/components/settings/Settings.tsx | 66 ++-- .../workSpace/modules/appModules/about.tsx | 37 +-- .../modules/appModules/helloWindow.tsx | 16 +- .../usersScanner/modules/TableScanner.tsx | 39 +-- .../appModules/wifiScanner/WifiScanner.tsx | 44 +-- .../wifiScanner/modules/ModalWifiScaner.tsx | 75 ++--- .../sideBar/toolSpace/devicesBar.tsx | 41 +-- .../settings/config/appearanceSettings.ts | 55 ++-- .../store/settings/config/networkSettings.ts | 22 +- .../store/settings/config/nodesSettings.ts | 149 +++++---- FRONT/DEV/src/store/settings/settingsStore.ts | 24 +- SRV/main.py | 4 +- SRV/storage/devices/devices.json | 284 ++++++++++++++++++ 26 files changed, 1241 insertions(+), 465 deletions(-) create mode 100644 SRV/storage/devices/devices.json diff --git a/FRONT/DEV/public/locales/en/common.json b/FRONT/DEV/public/locales/en/common.json index 7d98f05..966fec1 100644 --- a/FRONT/DEV/public/locales/en/common.json +++ b/FRONT/DEV/public/locales/en/common.json @@ -1,22 +1,186 @@ { - "userScanner-title": "Subnet nodes scanning", - "userScanner-visible": "Display mode", + "userScanner-title": "Subnet Node Scanning", + "userScanner-visible": "Display Method", "userScanner-loading": "Scanning nodes", - "userScanner-refresh": "Refresh list", - "userScanner-scan": "Scan nodes", - "userScanner-notFound": "No nodes found", + "userScanner-refresh": "Refresh List", + "userScanner-scan": "Scan Nodes", + "userScanner-notFound": "Nodes not found", "tableScanner-online": "Online", "tableScanner-name": "Name", - "tableScanner-vendor": "Manufacturer", - "tableScanner-ip": "IP address", - "tableScanner-ports": "Open ports", - "tableScanner-activity": "Last activity", + "tableScanner-vendor": "Vendor", + "tableScanner-ip": "IP Address", + "tableScanner-ports": "Open Ports", + "tableScanner-activity": "Last Activity", "tableScanner-note": "Note", "tableScanner-action": "Action", "tableScanner-unknown": "Unknown", "tableScanner-noData": "No data to display", - "listUsers-activeNodes": "Active nodes", - "listUsers-inactiveNodes": "Inactive nodes", + "listUsers-activeNodes": "Active Nodes", + "listUsers-inactiveNodes": "Inactive Nodes", "ipInput-placeholder": "Enter IP", - "ipInput-error": "Please enter a valid IP address" -} \ No newline at end of file + "ipInput-error": "Enter a valid IP address", + "userScanner-visible_table": "Table", + "userScanner-visible_list": "List", + + "wifiScanner-title": "Wi-Fi Access Point Scanning", + "wifiScanner-scanning": "Scanning access points", + "wifiScanner-scanError": "Network scanning error", + "wifiScanner-refresh": "Refresh List", + "wifiScanner-scan": "Scan Access Points", + "wifiModal-bssid": "BSSID", + "wifiModal-vendor": "Vendor", + "wifiModal-signal": "Signal", + "wifiModal-channel": "Channel", + "wifiModal-encryption": "Encryption", + "wifiModal-frequency": "Frequency", + "wifiModal-hidden": "Hidden Network", + "wifiModal-note": "Note", + "wifiModal-connect": "Connect", + "wifiModal-disconnect": "Disconnect", + "wifiModal-unknown": "Unknown", + "wifiModal-none": "None", + "common-yes": "Yes", + "common-no": "No", + + "addNode": "Add Node", + "enterNodeAddress": "Enter node address...", + "connectionLost": "Connection Lost", + "serverDisconnected": "Server connection interrupted", + "checkAddressAndRetry": "Please check the address availability and try again", + "cancel": "Cancel", + "close": "Close", + "save": "Save", + "retry": "Retry", + "connect": "Connect", + "submit": "Submit", + "common-back": "Back", + "error": "Error", + "networkConnection": "Network Connection", + "enterPassword": "Enter password...", + "connectingAttempt": "Attempting to connect to the network", + "connectionError": "Network connection error", + "connectionErrorLog": "Connection error: ", + "customizeTable-title": "Subnet Node Table Customization", + + "wifiDisconnect-title": "Wi-Fi Network Disconnection", + "wifiDisconnect-confirm": "Confirm Disconnection", + "wifiDisconnect-message1": "You are about to disconnect from the network - ", + "wifiDisconnect-message2": " All active connections will be interrupted.", + "wifiDisconnect-disconnect": "Disconnect", + "wifiDisconnect-progress": "Disconnecting from the network", + + "ipChange-title": "IP Address Change", + "ipChange-checkingIp": "Checking IP address validity", + "ipChange-generalInfo": "General Information", + "ipChange-interface": "Interface", + "ipChange-ipInfo": "IP Information", + "ipChange-currentIp": "Current IP", + "ipChange-currentGateway": "Current Gateway", + "ipChange-currentDns": "Current DNS", + "ipChange-newIp": "New IP", + "ipChange-newGateway": "New Gateway", + "ipChange-newDns": "New DNS", + "ipChange-checkError": "An error occurred while checking validity", + "ipChange-adminRequired": "To change network settings, you must log in as an administrator", + + "note-edit": "Edit Note", + "note-add": "Add Note", + "note-enter": "Enter note...", + + "auth-successLogin": "Successful Login", + "auth-successLogout": "Successful Logout", + "auth-getAccess": "Obtaining Access Rights", + "auth-enableAdmin": "Enable Administrative Access", + "auth-enterAdminPass": "Enter administrator password...", + "auth-wrongPass": "Incorrect operator password", + "auth-switchToLimited": "Switch to Limited Access", + "auth-limitedModeDesc": "In this mode, administrator privileges are restricted.", + "auth-restrictAccess": "Restrict Access", + "auth-closeHint": "This window can be closed, or it will disappear on its own", + + "operatorConfig-title": "Node Operator Configuration", + "operatorConfig-instruction": "Before starting, you need to create an application operator password", + "operatorConfig-passwordPlaceholder": "Enter password...", + "operatorConfig-repeatPasswordPlaceholder": "Repeat password...", + "operatorConfig-passwordMismatch": "Passwords do not match!", + "operatorConfig-submit": "Submit", + + "about-logoAlt": "Gossys Logo", + "about-title": "About the Application", + "about-description": "GoNets is a minimally viable cross-platform product (MVP) for demonstrating our approach to managing PC settings in a corporate environment.", + "about-featuresTitle": "Features", + "about-feature1": "Visualization of active network resources", + "about-feature2": "Configuration of local and remote network resource parameters", + "about-feature3": "Management of resource interaction with the GoNets Client", + "about-disclaimer": "The software is for demonstration and home use only.", + "about-goToPro": "Go to GoNets-PRO", + "about-homepage": "Homepage", + "about-documentation": "Documentation", + "about-license": "License", + "about-copyright": "Developed and maintained by Hybrid Office Systems LLC, 2024-2025. All rights reserved.", + + "main-logoAlt": "Application Logo", + "main-networkSettings": "Network Settings", + "main-about": "About the Program", + + "nodes-availableNodes": "Available Nodes", + "nodes-rootAddress": "Root Address", + "nodes-name": "Name", + "nodes-loading": "Loading", + "nodes-additionalNodes": "Additional Nodes", + "nodes-node": "Node", + "nodes-address": "Address", + "nodes-connectionError": "Failed to connect to the server at the address ", + + "settings-nodesSection": "Subnet Nodes", + "settings-intervalScanning": "Interval Scanning", + "settings-scanInterval": "Node Scan Interval (min)", + "settings-displayNames": "Display Names When Missing", + "settings-ipAddress": "IP Address", + "settings-unknown": "Unknown", + "settings-rowsPerPage": "Rows Per Table", + "settings-displaySettings": "Nodes Display", + "settings-configure": "Configure", + "settings-clearSessionData": "Clear Session Data", + "settings-clear": "Clear", + "settings-clearFailed": "Data was not deleted: ", + "settings-exportData": "Export Nodes Data", + "settings-export": "Export", + "settings-saveNodesTitle": "Save Nodes Data", + "settings-exportSuccess": "Data exported successfully!", + "settings-exportError": "Export error: ", + "settings-status": "Online", + "settings-name": "Name", + "settings-vendor": "Manufacturer", + "settings-openPorts": "Open Ports", + "settings-lastActivity": "Last Activity", + "settings-note": "Note", + "settings-action": "Action", + + "settings-title": "Settings", + "settings-resetSettings": "Reset Settings", + "settings-resetConfirm": "Are you sure you want to reset all settings to default values?", + "settings-cancel": "Cancel", + "settings-save": "Save", + "settings-fixErrors": "Fix errors before saving", + "settings-fillRequired": "Fill in all required fields", + "settings-dataNotDeleted": "Data was not deleted: ", + "settings-saveNetworksTitle": "Save Networks Data", + "settings-fieldRequired": "Field cannot be empty", + "settings-enterNumber": "Please enter a number", + "settings-valueTooSmall": "Value cannot be less than ", + "settings-valueTooLarge": "Value cannot be greater than ", + + "settings-sections-subnetNodes": "Subnet Nodes", + "settings-sections-accessPoints": "Access Points", + "settings-sections-appearance": "Appearance", + + "settings-exportNetworkData": "Export Access Points Data", + "settings-colorTheme": "Color Theme", + "settings-light": "Light", + "settings-dark": "Dark", + "settings-system": "As in System", + "settings-interfaceLanguage": "Interface Language", + "settings-russian": "Russian", + "settings-english": "English" +} diff --git a/FRONT/DEV/public/locales/ru/common.json b/FRONT/DEV/public/locales/ru/common.json index f4a33eb..144e1d4 100644 --- a/FRONT/DEV/public/locales/ru/common.json +++ b/FRONT/DEV/public/locales/ru/common.json @@ -1,22 +1,192 @@ -{ - "userScanner-title": "Сканирование узлов подсети", - "userScanner-visible": "Способ отображения", - "userScanner-loading": "Cканирование узлов", - "userScanner-refresh": "Обновить список", - "userScanner-scan": "Сканировать узлы", - "userScanner-notFound": "Узлы не найдены", - "tableScanner-online": "Онлайн", - "tableScanner-name": "Имя", - "tableScanner-vendor": "Производитель", - "tableScanner-ip": "IP адрес", - "tableScanner-ports": "Открытые порты", - "tableScanner-activity": "Последняя активность", - "tableScanner-note": "Заметка", - "tableScanner-action": "Действие", - "tableScanner-unknown": "Неизвестно", - "tableScanner-noData": "Нет данных для отображения", - "listUsers-activeNodes": "Активные узлы", - "listUsers-inactiveNodes": "Неактивные узлы", - "ipInput-placeholder": "Введите IP", - "ipInput-error": "Введите корректный IP-адрес" -} + { + "userScanner-title": "Сканирование узлов подсети", + "userScanner-visible": "Способ отображения", + "userScanner-loading": "Cканирование узлов", + "userScanner-refresh": "Обновить список", + "userScanner-scan": "Сканировать узлы", + "userScanner-notFound": "Узлы не найдены", + "tableScanner-online": "Онлайн", + "tableScanner-name": "Имя", + "tableScanner-vendor": "Производитель", + "tableScanner-ip": "IP адрес", + "tableScanner-ports": "Открытые порты", + "tableScanner-activity": "Последняя активность", + "tableScanner-note": "Заметка", + "tableScanner-action": "Действие", + "tableScanner-unknown": "Неизвестно", + "tableScanner-noData": "Нет данных для отображения", + "listUsers-activeNodes": "Активные узлы", + "listUsers-inactiveNodes": "Неактивные узлы", + "ipInput-placeholder": "Введите IP", + "ipInput-error": "Введите корректный IP-адрес", + "userScanner-visible_table": "Таблица", + "userScanner-visible_list": "Список", + + "wifiScanner-title": "Сканирование точек доступа Wi-Fi", + "wifiScanner-scanning": "Сканирование точек доступа", + "wifiScanner-scanError": "Ошибка сканирования сети", + "wifiScanner-refresh": "Обновить список", + "wifiScanner-scan": "Сканировать точки доступа", + "wifiModal-bssid": "BSSID", + "wifiModal-vendor": "Производитель", + "wifiModal-signal": "Сигнал", + "wifiModal-channel": "Канал", + "wifiModal-encryption": "Шифрование", + "wifiModal-frequency": "Частота", + "wifiModal-hidden": "Скрытая сеть", + "wifiModal-note": "Заметка", + "wifiModal-connect": "Подключиться", + "wifiModal-disconnect": "Отключиться", + "wifiModal-unknown": "Неизвестно", + "wifiModal-none": "Отсутствует", + "common-yes": "Да", + "common-no": "Нет", + + "addNode": "Добавление узла", + "enterNodeAddress": "Введите адрес узла...", + "connectionLost": "Соединение прервано", + "serverDisconnected": "Произошёл разрыв с сервером", + "checkAddressAndRetry": "Пожалуйста, проверьте доступ к адресу и попробуйте снова", + "cancel": "Отменить", + "close": "Закрыть", + "save": "Сохранить", + "retry": "Повторить", + "connect": "Подключиться", + "common-back": "Назад", + "submit": "Подтвердить", + "error": "Ошибка", + "networkConnection": "Подключение к сети", + "enterPassword": "Введите пароль...", + "connectingAttempt": "Попытка подключения к сети", + "connectionError": "Ошибка подключения к сети", + "connectionErrorLog": "Ошибка подключения: ", + "customizeTable-title": "Кастомизация таблицы узлов подсети", + + "wifiDisconnect-title": "Отключение от Wi-Fi сети", + "wifiDisconnect-confirm": "Подтверждение отключения", + "wifiDisconnect-message1": "Вы собираетесь отключиться от сети - ", + "wifiDisconnect-message2": " Все активные подключения будут прерваны.", + "wifiDisconnect-disconnect": "Отключиться", + "wifiDisconnect-progress": "Отключение от сети", + + "ipChange-title": "Изменение IP-Адреса", + "ipChange-checkingIp": "Идет проверка корректности IP адреса", + "ipChange-generalInfo": "Общие сведения", + "ipChange-interface": "Интерфейс", + "ipChange-ipInfo": "Сведения об IP", + "ipChange-currentIp": "Текущий ip", + "ipChange-currentGateway": "Текущий шлюз", + "ipChange-currentDns": "Текущий DNS", + "ipChange-newIp": "Новый ip", + "ipChange-newGateway": "Новый шлюз", + "ipChange-newDns": "Новый DNS", + "ipChange-checkError": "Произошла ошибка проверки корректности", + "ipChange-adminRequired": "Для изменения сетевых настроек необходимо авторизироваться под администратором", + + "note-edit": "Редактирование заметки", + "note-add": "Добавление заметки", + "note-enter": "Введите заметку...", + + "auth-successLogin": "Успешный вход", + "auth-successLogout": "Успешный выход", + "auth-getAccess": "Получение прав доступа", + "auth-enableAdmin": "Включить административный доступ", + "auth-enterAdminPass": "Введите пароль администратора...", + "auth-wrongPass": "Неверный пароль оператора", + "auth-switchToLimited": "Переключиться на ограниченный доступ", + "auth-limitedModeDesc": "В данном режиме доступа ограничиваются полномочия администратора.", + "auth-restrictAccess": "Ограничить доступ", + "auth-closeHint": "Это окно можно закрыть, либо оно пропадёт само", + + "operatorConfig-title": "Настройка оператора узла", + "operatorConfig-instruction": "Перед началом работы необходимо создать пароль оператора приложения", + "operatorConfig-passwordPlaceholder": "Введите пароль...", + "operatorConfig-repeatPasswordPlaceholder": "Повторите пароль...", + "operatorConfig-passwordMismatch": "Пароли не совпадают!", + "operatorConfig-submit": "Отправить", + + "about-logoAlt": "Логотип Gossys", + "about-title": "О приложении", + "about-description": "ГоNets - минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.", + "about-featuresTitle": "Возможности", + "about-feature1": "Визуализация активных сетевых ресурсов", + "about-feature2": "Настройка параметров локальных и удалённых сетевых ресурсов", + "about-feature3": "Управление взаимодействием ресурсов с Клиентом ГoNets", + "about-disclaimer": "Программное обеспечение исключительно для ознакомления и домашнего использования.", + "about-goToPro": "Перейти на ГоNets-PRO", + "about-homepage": "Домашняя страница", + "about-documentation": "Документация", + "about-license": "Лицензия", + "about-copyright": "Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.", + + "main-logoAlt": "Логотип приложения", + "main-networkSettings": "Настройки сети", + "main-about": "О программе", + + "nodes-availableNodes": "Доступные узлы", + "nodes-rootAddress": "Корневой адрес", + "nodes-name": "Имя", + "nodes-loading": "Загрузка", + "nodes-additionalNodes": "Дополнительные узлы", + "nodes-node": "Узел", + "nodes-address": "Адрес", + "nodes-connectionError": "Не удалось подключиться к серверу по адресу ", + + "settings-nodesSection": "Узлы подсети", + "settings-intervalScanning": "Интервальное сканирование", + "settings-scanInterval": "Интервал сканирования узлов (мин)", + "settings-displayNames": "Отображаемые имена при отсутствии", + "settings-ipAddress": "IP адрес", + "settings-unknown": "Неизвестно", + "settings-rowsPerPage": "Количество строк в таблице", + "settings-displaySettings": "Отображение узлов", + "settings-configure": "Настроить", + "settings-clearSessionData": "Очистить данные сессии", + "settings-clear": "Очистить", + "settings-clearFailed": "Данные не были удалены: ", + "settings-exportData": "Экспорт данных узлов", + "settings-export": "Экспортировать", + "settings-saveNodesTitle": "Сохранить данные узлов", + "settings-exportSuccess": "Данные успешно экспортированы!", + "settings-exportError": "Ошибка экспорта: ", + "settings-status": "Онлайн", + "settings-name": "Имя", + "settings-vendor": "Производитель", + "settings-openPorts": "Открытые порты", + "settings-lastActivity": "Последняя активность", + "settings-note": "Заметка", + "settings-action": "Действие", + + "settings-title": "Настройки", + "settings-resetSettings": "Сбросить настройки", + "settings-resetConfirm": "Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?", + "settings-cancel": "Отменить", + "settings-save": "Сохранить", + "settings-fixErrors": "Исправьте ошибки перед сохранением", + "settings-fillRequired": "Заполните все обязательные поля", + "settings-dataNotDeleted": "Данные не были удалены: ", + "settings-saveNetworksTitle": "Сохранить данные сетей", + "settings-fieldRequired": "Поле не может быть пустым", + "settings-enterNumber": "Пожалуйста, введите число", + "settings-valueTooSmall": "Значение не может быть меньше ", + "settings-valueTooLarge": "Значение не может быть больше ", + + "settings-sections-subnetNodes": "Узлы подсети", + "settings-sections-accessPoints": "Точки доступа", + "settings-sections-appearance": "Внешний вид", + + "settings-exportNetworkData": "Экспорт данных точек доступа", + "settings-colorTheme": "Цветовая тема", + "settings-light": "Светлая", + "settings-dark": "Тёмная", + "settings-system": "Как в системе", + "settings-interfaceLanguage": "Язык интерфейса", + "settings-russian": "Русский", + "settings-english": "Английский" + + + + } + + + diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/ConnectNetwork.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/ConnectNetwork.tsx index ff3532f..484e995 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/ConnectNetwork.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/ConnectNetwork.tsx @@ -8,6 +8,7 @@ import useNetworkStore from "../../../store/useDeviceStore"; import RouterLoader from "../../UI/routerLoader"; import {useTheme} from "styled-components"; import {useIpStore} from "../../../store"; +import {useTranslation} from "../../../utils/i18n/hooks"; interface Props { address: string; @@ -19,53 +20,55 @@ interface Props { export const ConnectNetwork = ({address, onClose}: Props) => { const [password, setPassword] = useState(''); const theme = useTheme(); - const { ipAddress } = useIpStore(); + const {ipAddress} = useIpStore(); const [error, setError] = useState(''); const [isLoading, setIsLoading] = useState(false); + const {t} = useTranslation('common'); const handleSave = async () => { try { setIsLoading(true); - setError('') + setError(''); const res = await NetworkService.connectNetwork(ipAddress, password, address); - if(res.status === 200) { - onClose() - }else{ - setError(res.data.detail) + if (res.status === 200) { + onClose(); + } else { + setError(res.data.detail); } } catch (error) { - console.error('Error connection:', error); - setError('Ошибка подключения к сети') - }finally { + console.error(t('connectionErrorLog'), error); + setError(t('connectionError')); + } finally { setIsLoading(false); } }; - return ( - + -

Подключение к сети

+

{t('networkConnection')}

- + setPassword(value)} /> -

- {error} -

+ {error && ( +

+ {error} +

+ )}
{ size="small" color={theme.successColor} hoverColor={theme.successColor} - title='Подключиться' + title={t('connect')} onClick={handleSave} /> - {isLoading && } + {isLoading && }
); }; \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/NoteDevice.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/NoteDevice.tsx index 6d0b666..c3c1356 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/NoteDevice.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/NoteDevice.tsx @@ -1,4 +1,3 @@ -// components/modalContainers/NoteDevice.tsx import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style"; import { MainButton } from "../../UI/buttons/mainButton"; import Input from "../../UI/inputs/input"; @@ -6,6 +5,7 @@ import { useState } from "react"; import {useIpStore} from "../../../store"; import {NetworkService} from "../../../api/request"; import useNetworkStore from "../../../store/useDeviceStore"; +import {useTranslation} from "../../../utils/i18n/hooks"; interface Props { address: string; @@ -20,6 +20,7 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => { const [note, setNote] = useState(currentNote || ''); const { ipAddress } = useIpStore(); const { devices, setDevices } = useNetworkStore(); + const {t} = useTranslation('common'); const handleSave = async () => { try { @@ -50,13 +51,13 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => { return ( -

{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}

+

{currentNote ? t('note-edit') : t('note-add')}

setNote(value)} @@ -67,13 +68,13 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => { diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/NoteWifi.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/NoteWifi.tsx index c03a48a..4abaef6 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/NoteWifi.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/NoteWifi.tsx @@ -6,6 +6,7 @@ import { useState } from "react"; import {NetworkService} from "../../../api/request"; import useNetworkStore from "../../../store/useDeviceStore"; import {useIpStore} from "../../../store"; +import {useTranslation} from "../../../utils/i18n/hooks"; interface Props { address: string; @@ -20,6 +21,7 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => { const [note, setNote] = useState(currentNote || ''); const { ipAddress } = useIpStore(); const { wifiNetworks, setWifiNetworks } = useNetworkStore(); + const {t} = useTranslation('common'); const handleSave = async () => { try { @@ -50,13 +52,13 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => { return ( -

{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}

+

{currentNote ? t('note-edit') : t('note-add')}

setNote(value)} @@ -67,13 +69,13 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => { diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/addDevice.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/addDevice.tsx index 5611792..910d1a2 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/addDevice.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/addDevice.tsx @@ -3,6 +3,7 @@ import { MainButton } from "../../UI/buttons/mainButton"; import { useState } from "react"; import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style" import { validateIPAddress } from "../../../utils/nodeController/validator"; +import {useTranslation} from "../../../utils/i18n/hooks"; interface Props { onSave?: (data: string) => void; @@ -12,6 +13,7 @@ interface Props { export const AddDevice = ({ onSave, onClose }: Props) => { const [ipAddress, setIpAddress] = useState('') const [error, setError] = useState(false) + const {t} = useTranslation('common'); const handleSubmit = () => { if (!validateIPAddress(ipAddress)) { @@ -28,31 +30,39 @@ export const AddDevice = ({ onSave, onClose }: Props) => { return ( - {/*
*/} -

Добавление узла

+

{t('addNode')}

setIpAddress(value)} /> {error && (

- Некорректный адрес узла + {t('invalidAddress')}

)}
- - + + - {/*
*/}
) } \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/connectionError.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/connectionError.tsx index 224ba6e..fa69a59 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/connectionError.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/connectionError.tsx @@ -2,6 +2,7 @@ import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } import { useModalContext } from "../../../store" import { useIpStore } from "../../../store" import { MainButton } from "../../UI/buttons/mainButton" +import {useTranslation} from "../../../utils/i18n/hooks"; type Props={ message: string @@ -10,28 +11,37 @@ type Props={ export const ConnectionError = ({message}: Props)=>{ const {closeModal} = useModalContext() const { setIpAddress } = useIpStore(); + const {t} = useTranslation('common'); const close = ()=>{ setIpAddress('localhost') closeModal() } - return( + return ( -

Соединение прервано

+

{t('connectionLost')}

- + -

Произошёл разрыв с сервером

-

Пожалуйста, проверьте доступ к адресу и попробуйте снова.

+

{t('serverDisconnected')}

+

{t('checkAddressAndRetry')}

- - + +
) diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx index dfa8037..b8ab434 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/customizeNodes.tsx @@ -5,6 +5,7 @@ import {ModalMap} from "../windowsType"; import {useModalContext} from "../../../store"; import styled from "styled-components"; import { useSettingsStore } from '../../../store/settings/settingsStore'; +import {useTranslation} from "../../../utils/i18n/hooks"; const StyledTableList = styled.ul` display: flex; @@ -42,7 +43,7 @@ const CustomizeNodes = () => { const updateSetting = useSettingsStore(state => state.updateSetting); const getSetting = useSettingsStore(state => state.getSetting); const initializeSettings = useSettingsStore(state => state.initializeSettings); - + const {t} = useTranslation('common'); const [dragIndex, setDragIndex] = useState(null); // Получаем текущие значения из хранилища @@ -88,11 +89,22 @@ const CustomizeNodes = () => { initializeSettings(); }, [initializeSettings]); + const getItemText = (item: string) => { + if (item === 'Онлайн') return t('settings-status'); + if (item === 'Имя') return t('settings-name'); + if (item === 'Производитель') return t('settings-vendor'); + if (item === 'IP адрес') return t('settings-ipAddress'); + if (item === 'Открытые порты') return t('settings-openPorts'); + if (item === 'Последняя активность') return t('settings-lastActivity'); + if (item === 'Заметка') return t('settings-note'); + if (item === 'Действие') return t('settings-action'); + return item; + }; + return ( -

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

- +

{t('customizeTable-title')}

@@ -110,7 +122,7 @@ const CustomizeNodes = () => { onDragEnd={handleDrop} onClick={() => handleSwitch(index)} > - {item} + {getItemText(item)} ))} @@ -120,7 +132,7 @@ const CustomizeNodes = () => { diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/disconnectConfirm.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/disconnectConfirm.tsx index 148d657..e9d67bf 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/disconnectConfirm.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/disconnectConfirm.tsx @@ -6,6 +6,7 @@ import {useState} from "react"; import {NetworkService} from "../../../api/request"; import RouterLoader from "../../UI/routerLoader"; import {useTheme} from "styled-components"; +import {useTranslation} from "../../../utils/i18n/hooks"; type Props={ address: string, @@ -16,6 +17,7 @@ export const DisconnectConfirm = ({address, onClose}: Props)=>{ const { ipAddress } = useIpStore(); const theme = useTheme() const [isLoading, setIsLoading] = useState(false); + const {t} = useTranslation('common'); const handleDisconnect = async () => { try { @@ -34,21 +36,32 @@ export const DisconnectConfirm = ({address, onClose}: Props)=>{ return( -

Отключение от Wi-Fi сети

+

{t('wifiDisconnect-title')}

- + -

Подтверждение отключения

+

{t('wifiDisconnect-confirm')}

+

{t('wifiDisconnect-message1')} + {address}. {t('wifiDisconnect-message2')}

Вы собираетесь отключиться от сети "{address}". Все активные подключения будут прерваны.

- - + + - {isLoading && } + {isLoading && }
) } \ No newline at end of file diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/ipConfirm.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/ipConfirm.tsx index 2b2a334..e6bcbf5 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/ipConfirm.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/ipConfirm.tsx @@ -8,6 +8,7 @@ import { useIpStore } from "../../../store"; import { MainButton } from "../../UI/buttons/mainButton"; import { useAdminStore } from "../../../store/adminStore"; import { useDevicesStore } from "../../../store"; +import {useTranslation} from "../../../utils/i18n/hooks"; type Props = { ipData: changeIp @@ -52,6 +53,7 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => { const { ipAddress, setIpAddress } = useIpStore(); const {isAdmin} = useAdminStore() const { devices, addDevice, deleteDevice } = useDevicesStore(); + const {t} = useTranslation('common'); useEffect(() => { if (ipData && isAdmin) { @@ -146,40 +148,48 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => { return ( <> {loader && isAdmin ? ( - + ) : ( -

Изменение IP-Адреса

+

{t('ipChange-title')}

{checkStatus ? ( -

Общие сведения

+

{t('ipChange-generalInfo')}

-

Интерфейс: {ipData.device}

+

{t('ipChange-interface')}: {ipData.device}

--{'>'}

{ipData.connection}

-

Сведения об IP

+

{t('ipChange-ipInfo')}

-

Текущий ip: {ipData.currentIp}

-

Текущий шлюз: {ipData.currentGate}

-

Текущий DNS: {ipData.currentDns}

+

{t('ipChange-currentIp')}: {ipData.currentIp}

+

{t('ipChange-currentGateway')}: {ipData.currentGate}

+

{t('ipChange-currentDns')}: {ipData.currentDns}

-

Новый ip: {ipData.newIp}

-

Новый шлюз: {ipData.newGate}

-

Новый DNS: {ipData.newDns}

+

{t('ipChange-newIp')}: {ipData.newIp}

+

{t('ipChange-newGateway')}: {ipData.newGate}

+

{t('ipChange-newDns')}: {ipData.newDns}

- cancelIp(currentIp)}/> - saveIp(newIP)}/> + cancelIp(currentIp)} + /> + saveIp(newIP)} + />
) : ( @@ -188,19 +198,21 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => { {isAdmin ? ( <> -

Произошла ошибка проверки корректности

- {message} +

{t('ipChange-checkError')}

+ {message} - ):( -

Для изменения сетевых настроек необходимо авторизироваться под администратором

+

{t('ipChange-adminRequired')}

)} -
- + )} diff --git a/FRONT/DEV/src/components/modalwindows/modalContainers/rightsConfirm.tsx b/FRONT/DEV/src/components/modalwindows/modalContainers/rightsConfirm.tsx index 24351dd..4cceef9 100644 --- a/FRONT/DEV/src/components/modalwindows/modalContainers/rightsConfirm.tsx +++ b/FRONT/DEV/src/components/modalwindows/modalContainers/rightsConfirm.tsx @@ -9,6 +9,7 @@ import { useCheckAdmin } from "../../../utils/checkAdmin" import { buttonNames } from "../../../const/buttons/names" import { IconComponent } from "../../../const/buttons/icons" +import {useTranslation} from "../../../utils/i18n/hooks"; interface Props { onClose: () => void; @@ -17,9 +18,10 @@ interface Props { export const RightsConfirm = ({ onClose }: Props) => { const { ipAddress } = useIpStore(); - const { isAdmin, setAdmin } = useAdminStore() + const { isAdmin } = useAdminStore() const checkAdmin = useCheckAdmin(); const [error, setError] = useState(false) + const {t} = useTranslation('common'); const [success, setSuccess] = useState({ message: '', status: false @@ -45,7 +47,7 @@ export const RightsConfirm = ({ onClose }: Props) => { setError(false) setSuccess(prevData => ({ ...prevData, - message: 'Успешный вход', + message: t('auth-successLogin'), status: true })); await new Promise(resolve => setTimeout(resolve, 3000)); @@ -54,20 +56,20 @@ export const RightsConfirm = ({ onClose }: Props) => { setError(true) } } catch (err) { - console.error('Error:', err); + console.error(t('auth-error'), err); } } const logout = async () => { - await NetworkService.logout(ipAddress); - await checkAdmin(); - setSuccess(prevData => ({ - ...prevData, - message: 'Успешный выход', - status: true - })); - await new Promise(resolve => setTimeout(resolve, 3000)); - onClose(); + await NetworkService.logout(ipAddress); + await checkAdmin(); + setSuccess(prevData => ({ + ...prevData, + message: t('auth-successLogout'), + status: true + })); + await new Promise(resolve => setTimeout(resolve, 3000)); + onClose(); } return ( @@ -76,60 +78,55 @@ export const RightsConfirm = ({ onClose }: Props) => { !isAdmin ? ( <> -

Получение прав доступа

+

{t('auth-getAccess')}

- + - -

Включить административный доступ

+

{t('auth-enableAdmin')}

handleChange('password', value)} /> - + {error && (

- Неверный пароль оператора + {t('auth-wrongPass')}

)} -
- + - - login(user)} /> + + login(user)} /> ) : ( <> -

Переключиться на ограниченный доступ

+

{t('auth-switchToLimited')}

- + -

В данном режиме доступа ограничиваются полномочия администратора.

+

{t('auth-limitedModeDesc')}

- + - - + + - ) ):(

{success.message}

-

Это окно можно закрыть, либо оно пропадёт само

- +

{t('auth-closeHint')}

+
)} - -
) } \ No newline at end of file diff --git a/FRONT/DEV/src/components/register.tsx b/FRONT/DEV/src/components/register.tsx index a043285..11b0b59 100644 --- a/FRONT/DEV/src/components/register.tsx +++ b/FRONT/DEV/src/components/register.tsx @@ -9,6 +9,7 @@ import { IconComponent } from "../const/buttons/icons" import { buttonNames } from "../const/buttons/names" import ThemeButton from "./UI/buttons/toggletheme" import About from "./workSpace/modules/appModules/about" +import {useTranslation} from "../utils/i18n/hooks"; const Wrapper = styled.div` display: flex; @@ -73,6 +74,7 @@ export const CreateUser = () => { const [repeatPassword, setRepeatPassword] = useState(''); const [passwordsMatch, setPasswordsMatch] = useState(true); const [about, setAbout] = useState(false) + const {t} = useTranslation('common') const [user, setUser] = useState({ username: 'operator', @@ -118,77 +120,81 @@ export const CreateUser = () => { setAbout(true) } return ( - <> {!about ? ( + + + <IconComponent name={buttonNames[3]} width="52" height="52" /> + <h3>{t('operatorConfig-title')}</h3> + {ipAddress != 'localhost' && + <p>{ipAddress}</p> + } + - - - <IconComponent name={buttonNames[3]} width="52" height="52"></IconComponent> - <h3>Настройка оператора узла</h3> + <Content> + <div> + <p>{t('operatorConfig-instruction')}</p> + </div> + + <div> + <Input + type="password" + placeholderText={t('operatorConfig-passwordPlaceholder')} + id="password" + value={user.password} + onChange={(value) => handleChange('password', value)} + /> + + <Input + type="password" + placeholderText={t('operatorConfig-repeatPasswordPlaceholder')} + id="repeatPassword" + value={repeatPassword} + onChange={(value) => handleChange('repeatPassword', value)} + style={{ marginTop: '10px' }} + /> + {!passwordsMatch && ( + <p style={{ color: 'red', marginTop: '5px' }}> + {t('operatorConfig-passwordMismatch')} + </p> + )} + </div> + </Content> + + <ButtonContainer> + <MainButton + style={{ width: '100%' }} + title={t('operatorConfig-submit')} + type="submit" + /> + </ButtonContainer> + </Container> + + <Menu> {ipAddress != 'localhost' && - <p>{ipAddress}</p> + <MainButton + title={t('common-back')} + onClick={back} + style={{marginRight: '10px'}} + /> } - - - - - -
-

Перед началом работы необходимо создать пароль оператора приложения

-
- -
- handleChange('password', value)} - /> - - handleChange('repeatPassword', value)} - style={{ marginTop: '10px' }} - /> - {!passwordsMatch && ( -

- Пароли не совпадают! -

- )} -
- -
- - - - - - -
- - - - {ipAddress != 'localhost' && - - } - - + + -
- ): ( + + ) : ( setAbout(false)} /> )} - - ) + ); } \ No newline at end of file diff --git a/FRONT/DEV/src/components/settings/SettingItem.tsx b/FRONT/DEV/src/components/settings/SettingItem.tsx index 80014d1..69ed138 100644 --- a/FRONT/DEV/src/components/settings/SettingItem.tsx +++ b/FRONT/DEV/src/components/settings/SettingItem.tsx @@ -8,6 +8,7 @@ import {MainButton} from "../UI/buttons/mainButton"; import React from 'react'; import {useModalContext} from "../../store"; import {ModalMap} from "../modalwindows/windowsType"; +import {useTranslation} from "../../utils/i18n/hooks"; const SettingBlock = styled.div` display: flex; @@ -89,6 +90,7 @@ interface SettingItemProps { const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => { const theme = useTheme(); + const { t } = useTranslation('common'); const [error, setError] = useState(null); const { openModal } = useModalContext(); @@ -96,7 +98,35 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: if (setting.id === 'customize-nodes') { openModal(ModalMap['11']); } else if (setting.action) { - setting.action(); + // Создаем обертку для действия с переводами + const actionWithTranslations = async () => { + try { + // Временно заменяем alert на версию с переводами + const originalAlert = window.alert; + window.alert = (message: string) => { + // Применяем переводы к сообщениям + let translatedMessage = message; + if (message.startsWith('settings-dataNotDeleted')) { + translatedMessage = t('settings-dataNotDeleted') + message.replace('settings-dataNotDeleted', ''); + } else if (message === 'settings-exportSuccess') { + translatedMessage = t('settings-exportSuccess'); + } else if (message.startsWith('settings-exportError')) { + translatedMessage = t('settings-exportError') + message.replace('settings-exportError', ''); + } + originalAlert(translatedMessage); + }; + + if (setting.action) { + await setting.action(); + } + + // Восстанавливаем оригинальный alert + window.alert = originalAlert; + } catch (error) { + console.error('Action error:', error); + } + }; + actionWithTranslations(); } }; @@ -106,19 +136,19 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: let newError: string | null = null; if (inputValue.trim() === '') { - newError = 'Поле не может быть пустым'; + newError = t('settings-fieldRequired'); onChange(null); } else if (isNaN(Number(inputValue))) { - newError = 'Пожалуйста, введите число'; + newError = t('settings-enterNumber'); } else { const numValue = Number(inputValue); if (setting.min !== undefined && numValue < setting.min) { - newError = `Значение не может быть меньше ${setting.min}`; + newError = t('settings-valueTooSmall') + setting.min; } else if (setting.max !== undefined && numValue > setting.max) { - newError = `Значение не может быть больше ${setting.max}`; + newError = t('settings-valueTooLarge') + setting.max; } if (!newError) { diff --git a/FRONT/DEV/src/components/settings/Settings.tsx b/FRONT/DEV/src/components/settings/Settings.tsx index 6cdc1cd..57edc9d 100644 --- a/FRONT/DEV/src/components/settings/Settings.tsx +++ b/FRONT/DEV/src/components/settings/Settings.tsx @@ -6,6 +6,7 @@ import {Setting, useSettingsStore} from "../../store/settings/settingsStore"; import {SettingItem} from "./SettingItem"; import {IconComponent} from "../../const/buttons/icons"; import {buttonNames} from "../../const/buttons/names"; +import {useTranslation} from "../../utils/i18n/hooks"; interface Props { @@ -70,9 +71,12 @@ export const Settings = ({ onClose }: Props) => { const getSetting = useSettingsStore(state => state.getSetting); const resetToDefaults = useSettingsStore(state => state.resetToDefaults); const initializeSettings = useSettingsStore(state => state.initializeSettings); + const updateSettingsConfig = useSettingsStore(state => state.updateSettingsConfig); const theme = useTheme() + const { t, language } = useTranslation('common'); const [fieldErrors, setFieldErrors] = useState>({}); + const [currentLanguage, setCurrentLanguage] = useState(language); // Проверка действий const handleValidationError = useCallback((fieldId: string, error: string | null) => { @@ -89,22 +93,33 @@ export const Settings = ({ onClose }: Props) => { const settingsBySection = useMemo(() => { return settingsConfig.reduce((acc, setting) => { - if (!acc[setting.sectionId]) { - acc[setting.sectionId] = []; - } - acc[setting.sectionId].push({ + // Применяем переводы к настройкам + const translatedSetting = { ...setting, + sectionId: t(setting.sectionId), + label: t(setting.label), + defaultValue: typeof setting.defaultValue === 'string' && setting.defaultValue.startsWith('settings-') ? t(setting.defaultValue) : setting.defaultValue, + options: setting.options ? setting.options.map((option: any) => ({ + ...option, + label: option.label.startsWith('settings-') ? t(option.label) : option.label + })) : setting.options, value: settingsValues[setting.id] ?? setting.defaultValue - }); + }; + + if (!acc[translatedSetting.sectionId]) { + acc[translatedSetting.sectionId] = []; + } + acc[translatedSetting.sectionId].push(translatedSetting); return acc; }, {} as Record); - }, [settingsConfig, settingsValues]); + }, [settingsConfig, settingsValues, t]); const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]); useEffect(() => { + updateSettingsConfig(); initializeSettings(); - }, [initializeSettings]); + }, [updateSettingsConfig, initializeSettings]); useEffect(() => { if (sections.length > 0 && !activeSection) { @@ -112,10 +127,15 @@ export const Settings = ({ onClose }: Props) => { } }, [sections, activeSection, setActiveSection]); + // Обновляем текущий язык при изменении + useEffect(() => { + setCurrentLanguage(language); + }, [language]); + const handleSave = useCallback(() => { const hasErrors = Object.values(fieldErrors).some(error => error?.trim()); if (hasErrors) { - alert('Исправьте ошибки перед сохранением'); + alert(t('settings-fixErrors')); return; } @@ -126,13 +146,24 @@ export const Settings = ({ onClose }: Props) => { }); if (emptyRequiredFields.length > 0) { - alert('Заполните все обязательные поля'); + alert(t('settings-fillRequired')); return; } + // Проверяем, изменился ли язык + const newLanguage = pendingChanges['language'] ?? settingsValues['language']; + const languageChanged = newLanguage && newLanguage !== currentLanguage; + applyChanges(); onClose(); - }, [fieldErrors, settingsConfig, pendingChanges, settingsValues, applyChanges, onClose]); + + // Если язык изменился, перезагружаем приложение + if (languageChanged) { + setTimeout(() => { + window.location.reload(); + }, 100); + } + }, [fieldErrors, settingsConfig, pendingChanges, settingsValues, applyChanges, onClose, t, currentLanguage]); const handleCancel = useCallback(() => { discardChanges(); @@ -141,14 +172,14 @@ export const Settings = ({ onClose }: Props) => { }, [discardChanges, onClose]); const handleReset = () => { - if (window.confirm('Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?')) { + if (window.confirm(t('settings-resetConfirm'))) { resetToDefaults(); } }; // Обработка нажатий клавиш useEffect(() => { - const handleKeyPress = (e: React.KeyboardEvent) => { + const handleKeyPress = (e: KeyboardEvent) => { if (e.key === 'Enter') handleSave(); if (e.key === 'Escape') @@ -158,12 +189,13 @@ export const Settings = ({ onClose }: Props) => { return () => { document.removeEventListener('keydown', handleKeyPress); }; - }, []); + }, [handleSave, handleCancel]); + return ( -

Настройки

+

{t('settings-title')}

@@ -204,7 +236,7 @@ export const Settings = ({ onClose }: Props) => { { { diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx index 59a4c39..8309157 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/about.tsx @@ -6,6 +6,7 @@ import styled, {useTheme} from "styled-components" import ThemeButton from "../../../UI/buttons/toggletheme" import {buttonNames} from "../../../../const/buttons/names"; import {IconComponent} from "../../../../const/buttons/icons"; +import {useTranslation} from "../../../../utils/i18n/hooks"; type Props = { from: 'reg' | 'helloWindow' | 'workSpace' @@ -112,7 +113,7 @@ const LinkBlock = styled.a` const Copyright = styled.div` width: 100%; - text-align: start; + text-align: center; color: ${(props => props.theme.offlineColor)}; ` @@ -158,6 +159,7 @@ const MainTitle = styled.div` export default function About({ from, onBack }: Props) { const { setWorkModule } = useWorkSpaceStore() + const {t} = useTranslation('common') const getBackHandler = () => { switch (from) { case 'reg': @@ -177,11 +179,12 @@ export default function About({ from, onBack }: Props) { return ( -
- -

О приложении

+ {t('about-logoAlt')} +
+

{t('about-title')}

+

v 0.0.3

@@ -189,15 +192,15 @@ export default function About({ from, onBack }: Props) { - <p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p> + <p>{t('about-description')}</p> -

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

+

{t('about-featuresTitle')}:

-
  • Визуализация активных сетевых ресурсов
  • -
  • Настройка параметров локальных и удалённых сетевых ресурсов
  • -
  • Управление взаимодействием ресурсов с Клиентом ГoNets
  • +
  • {t('about-feature1')}
  • +
  • {t('about-feature2')}
  • +
  • {t('about-feature3')}
  • @@ -205,26 +208,28 @@ export default function About({ from, onBack }: Props) { -

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

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

    {t('about-disclaimer')}

    + + {t('about-goToPro')} + - Домашняя страницa + {t('about-homepage')} - Документация + {t('about-documentation')} - Лицензия + {t('about-license')} - Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены. + {t('about-copyright')} @@ -235,7 +240,6 @@ export default function About({ from, onBack }: Props) { name={buttonNames[12]} onClick={backHandler} /> - )} {from == 'reg' && @@ -243,7 +247,6 @@ export default function About({ from, onBack }: Props) { }
    -
    ) } \ No newline at end of file diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/helloWindow.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/helloWindow.tsx index 0a09826..1761515 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/helloWindow.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/helloWindow.tsx @@ -3,6 +3,7 @@ import { useWorkSpaceStore } from "../../../../store" import { workSpaceNames } from "../../../../const/workspace/names" import { buttonNames } from "../../../../const/buttons/names" + import {useTranslation} from "../../../../utils/i18n/hooks"; const Wrapper = styled.div` width: 100%; @@ -49,30 +50,33 @@ ` export const HelloWindow = () => { - + const {t} = useTranslation('common') const { setWorkModule } = useWorkSpaceStore() return ( - <img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/> + <img + src="./assets/images/logo.svg" + alt={t('main-logoAlt')} + style={{width: '152px'}} + /> - setWorkModule(workSpaceNames[3])} /> setWorkModule(workSpaceNames[4])} + onClick={() => setWorkModule(workSpaceNames[4])} /> diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx index c43a2f8..4ae94d9 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/usersScanner/modules/TableScanner.tsx @@ -5,14 +5,14 @@ import {IconComponent} from "../../../../../../const/buttons/icons"; import {buttonNames} from "../../../../../../const/buttons/names"; import {ModalMap} from "../../../../../modalwindows/windowsType"; import {useModalContext} from "../../../../../../store"; +import {useTranslation} from "../../../../../../utils/i18n/hooks"; import Pagination from "../../../../../UI/Pagination"; import { - useDisabledColumns, useLanguage, + useDisabledColumns, useNameSetting, useOrdersColumns, usePaginateSetting } from "../../../../../../hooks/useSettings"; -import {useTranslation} from "../../../../../../utils/i18n/hooks"; interface TableScannerProps { devices: DeviceUser[]; @@ -71,16 +71,19 @@ const StatusBadge = styled.span<{ status:string }>` ` export const TableScanner = ({ devices }: TableScannerProps) => { - const {t} = useTranslation('common') const { openModal } = useModalContext(); const nameSetting = useNameSetting(); const ordersColumns = useOrdersColumns(); const disabledColumns = useDisabledColumns(); const paginateSetting = usePaginateSetting(); + const {t} = useTranslation('common'); + const [currentPage, setCurrentPage] = useState(1); const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting); + console.log(ordersColumns); + useEffect(() => { setDevicesPerPage(paginateSetting); }, [paginateSetting]); @@ -98,14 +101,14 @@ export const TableScanner = ({ devices }: TableScannerProps) => { .filter(column => !disabledColumns.includes(ordersColumns.indexOf(column))) .map(column => { switch(column) { - case t('tableScanner-online'): return 'status'; - case t('tableScanner-name'): return 'name'; - case t('tableScanner-vendor'): return 'vendor'; - case t('tableScanner-ip'): return 'ip'; - case t('tableScanner-ports'): return 'ports'; - case t('tableScanner-activity'): return 'activity'; - case t('tableScanner-note'): return 'note'; - case t('tableScanner-action'): return 'action'; + case 'Онлайн': return 'status'; + case 'Имя': return 'name'; + case 'Производитель': return 'vendor'; + case 'IP адрес': return 'ip'; + case 'Открытые порты': return 'ports'; + case 'Последняя активность': return 'activity'; + case 'Заметка': return 'note'; + case 'Действие': return 'action'; default: return ''; } }) @@ -136,7 +139,7 @@ export const TableScanner = ({ devices }: TableScannerProps) => { case 'status': return ; case 'name': - return {item.hostname || (nameSetting === 'ip' ? item.ip : t('tableScanner-unknown'))}; + return {item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')}; case 'vendor': return {item.vendor}; case 'ip': @@ -168,12 +171,12 @@ export const TableScanner = ({ devices }: TableScannerProps) => { {visibleColumns.map(column => { switch (column) { case 'status': return ; - case 'name': return {t('tableScanner-name')}; - case 'vendor': return {t('tableScanner-vendor')}; - case 'ip': return {t('tableScanner-ip')}; - case 'ports': return {t('tableScanner-ports')}; - case 'activity': return {t('tableScanner-activity')}; - case 'note': return {t('tableScanner-note')}; + case 'name': return {t('settings-name')}; + case 'vendor': return {t('settings-vendor')}; + case 'ip': return {t('settings-ipAddress')}; + case 'ports': return {t('settings-openPorts')}; + case 'activity': return {t('settings-lastActivity')}; + case 'note': return {t('settings-note')}; case 'action': return ; default: return null; } 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 2177345..6978738 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/WifiScanner.tsx @@ -5,6 +5,7 @@ import WifiDot, {WifiNetworkScanner} from "./modules/WifiDot"; import ModalWifiScaner from "./modules/ModalWifiScaner"; import useNetworkStore from "../../../../../store/useDeviceStore"; import {useIpStore} from "../../../../../store"; +import {useTranslation} from "../../../../../utils/i18n/hooks"; // Анимации const ring1 = keyframes` @@ -143,27 +144,28 @@ export function WifiScanner() { const [scanInitiated, setScanInitiated] = useState(false); const { wifiNetworks, setWifiNetworks } = useNetworkStore(); const [currentNetwork, setCurrentNetwork] = useState(null); + const { t } = useTranslation('common'); - const handleScan = async () => { + const handleScan = async () => { + setWifiNetworks([]); + setCurrentNetwork(null); + setLoading(true); + setScanInitiated(true); + try { + const res = await NetworkService.scanWifi(ipAddress); + const networks = res.data.access_points || []; + setWifiNetworks(networks); + } catch (err) { + console.error(t('wifiScanner-scanError'), err); setWifiNetworks([]); - setCurrentNetwork(null) - setLoading(true); - setScanInitiated(true); - try { - const res = await NetworkService.scanWifi(ipAddress); - const networks = res.data.access_points || []; - setWifiNetworks(networks); - } catch (err) { - console.error("Ошибка сканирования сети:", err); - setWifiNetworks([]); - } finally { - setLoading(false); - } - }; + } finally { + setLoading(false); + } + }; return ( - Сканирование точек доступа Wi-Fi сети + {t('wifiScanner-title')} @@ -190,11 +192,15 @@ export function WifiScanner() { {loading ? ( - Сканирование точек доступа + {t('wifiScanner-scanning')} ) : ( - 0 }> - {wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'} + 0} + > + {wifiNetworks.length > 0 ? t('wifiScanner-refresh') : t('wifiScanner-scan')} )} diff --git a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/ModalWifiScaner.tsx b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/ModalWifiScaner.tsx index 7313f95..4fcc68d 100644 --- a/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/ModalWifiScaner.tsx +++ b/FRONT/DEV/src/components/workSpace/modules/appModules/wifiScanner/modules/ModalWifiScaner.tsx @@ -10,6 +10,7 @@ import SignalSVG from "../../../../../UI/signalSvg"; import { useTheme } from 'styled-components'; import useNetworkStore, {DeviceUser} from "../../../../../../store/useDeviceStore"; import {NetworkService} from "../../../../../../api/request"; +import {useTranslation} from "../../../../../../utils/i18n/hooks"; const Modal = styled.div` display: flex; @@ -114,6 +115,7 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC const [signal, setSignal] = useState(1); const { wifiNetworks } = useNetworkStore(); const [isConnected, setIsConnected] = useState(false); + const { t } = useTranslation('common'); useEffect(() => { const getStatus = async () => { @@ -174,10 +176,9 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC

    {network.ssid}

    - - - - + + + @@ -185,40 +186,42 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC -
    BSSID:
    +
    {t('wifiModal-bssid')}:

    {network.bssid}

    - -
    Производитель:
    -

    {network.vendor}

    -
    - -
    Сигнал:
    -

    {network.signal} dBm

    -
    - -
    Канал:
    -

    {network.channel}

    -
    - -
    Шифрование:
    -

    {network.encryption}

    -
    - -
    Частота:
    -

    {network.frequency}

    -
    - -
    Скрытая сеть:
    -

    {network.hidden ? 'Да' : 'Нет'}

    -
    - -
    Заметка:
    -

    {currentNetwork.note ? currentNetwork.note : '-'}

    -
    - {isConnected ? 'Отключиться' : 'Подключиться'} -
    - + +
    {t('wifiModal-vendor')}:
    +

    {network.vendor || t('wifiModal-unknown')}

    +
    + +
    {t('wifiModal-signal')}:
    +

    {network.signal} dBm

    +
    + +
    {t('wifiModal-channel')}:
    +

    {network.channel}

    +
    + +
    {t('wifiModal-encryption')}:
    +

    {network.encryption || t('wifiModal-none')}

    +
    + +
    {t('wifiModal-frequency')}:
    +

    {network.frequency} MHz

    +
    + +
    {t('wifiModal-hidden')}:
    +

    {network.hidden ? t('common-yes') : t('common-no')}

    +
    + +
    {t('wifiModal-note')}:
    +

    {currentNetwork.note || '-'}

    +
    + + {isConnected ? t('wifiModal-disconnect') : t('wifiModal-connect')} + + + ); }; diff --git a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx index dd5ca2b..5a43c44 100644 --- a/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx +++ b/FRONT/DEV/src/components/workSpace/sideBar/toolSpace/devicesBar.tsx @@ -11,6 +11,7 @@ import { useWorkSpaceStore } from "../../../../store" import { workSpaceNames } from "../../../../const/workspace/names" import { IconComponent } from "../../../../const/buttons/icons" import { buttonNames } from "../../../../const/buttons/names" +import {useTranslation} from "../../../../utils/i18n/hooks"; const Container = styled.div` display: flex; @@ -102,11 +103,12 @@ interface CurrentDevice { } export const DevicesBar = () => { - const { openModal } = useModalContext(); - const { setIpAddress, ipAddress } = useIpStore(); - const { devices, addDevice, deleteDevice } = useDevicesStore() - const { isOpen, collapseMenu } = useMenu(menusNames[1]); - const { setWorkModule } = useWorkSpaceStore() + const {openModal} = useModalContext(); + const {setIpAddress, ipAddress} = useIpStore(); + const {devices, addDevice, deleteDevice} = useDevicesStore() + const {isOpen, collapseMenu} = useMenu(menusNames[1]); + const {setWorkModule} = useWorkSpaceStore() + const {t} = useTranslation('common'); const [currentDevice, setCurrentDevice] = useState({ address: 'localhost', @@ -163,8 +165,8 @@ export const DevicesBar = () => { const handleDelete = (number: number, adress: string) => { deleteDevice(number) - - if(adress == ipAddress){ + + if (adress == ipAddress) { setIpAddress('localhost') } }; @@ -173,7 +175,7 @@ export const DevicesBar = () => { const isAvailable = await checkWebSocketAvailability(ip); if (!isAvailable) { openModal(ModalMap['3'], { - message: `Не удалось подключиться к серверу по адресу ${ip}`, + message: t('nodes-connectionError') + ip, }); return; } @@ -191,44 +193,46 @@ export const DevicesBar = () => { return ( - <p className="unselectable">Доступные узлы</p> + <p className="unselectable">{t('nodes-availableNodes')}</p> switchWs(currentDevice.address)}> -

    Корневой адрес

    +

    {t('nodes-rootAddress')}

    {currentDevice.hostname == '' ? ( - Название: Загрузка + + {t('nodes-name')}: {t('nodes-loading')} + ) : ( -

    Название: {currentDevice.hostname}

    +

    {t('nodes-name')}: {currentDevice.hostname}

    )}
    -

    Дополнительные узлы

    - +

    {t('nodes-additionalNodes')}

    +
    - + {devices.length > 0 && devices.map((device, index) => ( switchWs(device.address)}> -

    Узел {device.number}

    +

    {t('nodes-node')} {device.number}

    { - e.stopPropagation(); // Предотвращаем всплытие события + e.stopPropagation(); handleDelete(device.number, device.address); }} />
    -

    Адрес: {device.address}

    +

    {t('nodes-address')}: {device.address}

    )) } @@ -237,7 +241,6 @@ export const DevicesBar = () => { +
    -
    ) diff --git a/FRONT/DEV/src/store/settings/config/appearanceSettings.ts b/FRONT/DEV/src/store/settings/config/appearanceSettings.ts index f8bd3b4..e573149 100644 --- a/FRONT/DEV/src/store/settings/config/appearanceSettings.ts +++ b/FRONT/DEV/src/store/settings/config/appearanceSettings.ts @@ -1,25 +1,30 @@ -export const appearanceSettings = [ - { - id: 'theme', - sectionId: 'Внешний вид', - label: 'Цветовая тема', - type: 'select', - defaultValue: 'light', - options: [ - { value: 'light', label: 'Светлая' }, - { value: 'dark', label: 'Тёмная' }, - { value: 'system', label: 'Как в системе' } - ], - }, - { - id: 'language', - sectionId: 'Внешний вид', - label: 'Язык интерфейса', - type: 'select', - defaultValue: 'ru', - options: [ - { value: 'ru', label: 'Русский' }, - { value: 'en', label: 'Английский' }, - ], - }, -] as const; \ No newline at end of file + +const createAppearanceSettings = () => { + return [ + { + id: 'theme', + sectionId: 'settings-sections-appearance', + label: 'settings-colorTheme', + type: 'select', + defaultValue: 'light', + options: [ + { value: 'light', label: 'settings-light' }, + { value: 'dark', label: 'settings-dark' }, + { value: 'system', label: 'settings-system' } + ], + }, + { + id: 'language', + sectionId: 'settings-sections-appearance', + label: 'settings-interfaceLanguage', + type: 'select', + defaultValue: 'ru', + options: [ + { value: 'ru', label: 'settings-russian' }, + { value: 'en', label: 'settings-english' }, + ], + }, + ] as const; +}; + +export { createAppearanceSettings }; \ No newline at end of file diff --git a/FRONT/DEV/src/store/settings/config/networkSettings.ts b/FRONT/DEV/src/store/settings/config/networkSettings.ts index 014de77..9e9ce9f 100644 --- a/FRONT/DEV/src/store/settings/config/networkSettings.ts +++ b/FRONT/DEV/src/store/settings/config/networkSettings.ts @@ -8,30 +8,30 @@ const createNetworkSettings = () => { { id: 'clean-network', type: 'button', - label: 'Очистить данные сессии', - sectionId: 'Точки доступа', - defaultValue: 'Очистить', + label: 'settings-clearSessionData', + sectionId: 'settings-sections-accessPoints', + defaultValue: 'settings-clear', action: async ()=>{ try { const res = await NetworkService.clearNetwork(ipAddress); console.log(res); }catch (e){ console.error(e); - alert('Данные не были удалены: '+ e) + alert('settings-dataNotDeleted' + e) } }, }, { id: 'export-network', type: 'button', - label: 'Экспорт данных точек доступа', - sectionId: 'Точки доступа', - defaultValue: 'Экспортировать', + label: 'settings-exportNetworkData', + sectionId: 'settings-sections-accessPoints', + defaultValue: 'settings-export', action: async () => { try { const response = await NetworkService.exportNetwork(ipAddress); const { filePath } = await window.electronAPI.showSaveDialog({ - title: "Сохранить данные сетей", + title: 'settings-saveNetworksTitle', defaultPath: "network_export.json", filters: [{ name: "JSON", extensions: ["json"] }], }); @@ -41,10 +41,10 @@ const createNetworkSettings = () => { filePath, JSON.stringify(response.data, null, 2) ); - alert("Данные успешно экспортированы!"); + alert('settings-exportSuccess'); } } catch (e: any) { - alert("Ошибка экспорта: " + e.message); + alert('settings-exportError' + e.message); } }, @@ -52,4 +52,4 @@ const createNetworkSettings = () => { ] as const; }; -export const networkSettings = createNetworkSettings(); \ No newline at end of file +export { createNetworkSettings }; \ No newline at end of file diff --git a/FRONT/DEV/src/store/settings/config/nodesSettings.ts b/FRONT/DEV/src/store/settings/config/nodesSettings.ts index 82040e4..4673999 100644 --- a/FRONT/DEV/src/store/settings/config/nodesSettings.ts +++ b/FRONT/DEV/src/store/settings/config/nodesSettings.ts @@ -1,80 +1,79 @@ import {useIpStore} from "../../ipStore"; import {NetworkService} from "../../../api/request"; - const createNodesSettings = () => { const { ipAddress } = useIpStore.getState(); return [ - { - id: 'rescan-nodes', - sectionId: 'Узлы подсети', - label: 'Интервальное сканирование', - type: 'boolean', - defaultValue: false, - }, - { - id: 'interval-nodes', - sectionId: 'Узлы подсети', - label: 'Интервал сканирования узлов (мин)', - type: 'number', - defaultValue: 60, - min: 1, - max: 2880 - }, - { - id: 'netnames-nodes', - sectionId: 'Узлы подсети', - label: 'Отображаемые имена при отсутствии', - type: 'select', - defaultValue: 'ip', - options: [ - { value: 'ip', label: 'IP адрес' }, - { value: 'unknown', label: 'Неизвестно' }, - ], - }, - { - id: 'paginate-nodes', - type: 'number', - label: 'Количество строк в таблице', - sectionId: 'Узлы подсети', - defaultValue: 20, - min: 1, - }, - { - id: 'customize-nodes', - type: 'button', - label: 'Отображение узлов', - sectionId: 'Узлы подсети', - defaultValue: 'Настроить', - }, - { - id: 'clean-nodes', - type: 'button', - label: 'Очистить данные сессии', - sectionId: 'Узлы подсети', - defaultValue: 'Очистить', - action: async ()=>{ - try { - const res = await NetworkService.clearSubnet(ipAddress); - console.log(res) - }catch (e){ - console.error(e); - alert('Данные не были удалены: '+ e) - } + { + id: 'rescan-nodes', + sectionId: 'settings-sections-subnetNodes', + label: 'settings-intervalScanning', + type: 'boolean', + defaultValue: false, + }, + { + id: 'interval-nodes', + sectionId: 'settings-sections-subnetNodes', + label: 'settings-scanInterval', + type: 'number', + defaultValue: 60, + min: 1, + max: 2880 + }, + { + id: 'netnames-nodes', + sectionId: 'settings-sections-subnetNodes', + label: 'settings-displayNames', + type: 'select', + defaultValue: 'ip', + options: [ + { value: 'ip', label: 'settings-ipAddress' }, + { value: 'unknown', label: 'settings-unknown' }, + ], + }, + { + id: 'paginate-nodes', + type: 'number', + label: 'settings-rowsPerPage', + sectionId: 'settings-sections-subnetNodes', + defaultValue: 20, + min: 1, + }, + { + id: 'customize-nodes', + type: 'button', + label: 'settings-displaySettings', + sectionId: 'settings-sections-subnetNodes', + defaultValue: 'settings-configure', + }, + { + id: 'clean-nodes', + type: 'button', + label: 'settings-clearSessionData', + sectionId: 'settings-sections-subnetNodes', + defaultValue: 'settings-clear', + action: async ()=>{ + try { + const res = await NetworkService.clearSubnet(ipAddress); + console.log(res) + }catch (e){ + console.error(e); + alert('settings-dataNotDeleted' + e) + } + }, }, - }, { id: "export-nodes", type: "button", - label: "Экспорт данных узлов", - sectionId: "Узлы подсети", - defaultValue: "Экспортировать", + label: "settings-exportData", + sectionId: "settings-sections-subnetNodes", + defaultValue: "settings-export", action: async () => { try { const response = await NetworkService.exportDevices(ipAddress); const { filePath } = await window.electronAPI.showSaveDialog({ - title: "Сохранить данные узлов", + title: "settings-saveNodesTitle", defaultPath: "nodes_export.json", filters: [{ name: "JSON", extensions: ["json"] }], }); @@ -84,10 +83,10 @@ const createNodesSettings = () => { filePath, JSON.stringify(response.data, null, 2) ); - alert("Данные успешно экспортированы!"); + alert("settings-exportSuccess"); } } catch (e: any) { - alert("Ошибка экспорта: " + e.message); + alert("settings-exportError" + e.message); } }, }, @@ -95,26 +94,26 @@ const createNodesSettings = () => { id: 'table-columns-order', type: 'hidden', label: '', - sectionId: 'Узлы подсети', + sectionId: 'settings-sections-subnetNodes', defaultValue: [ - 'Онлайн', - 'Имя', - 'Производитель', - 'IP адрес', - 'Открытые порты', - 'Последняя активность', - 'Заметка', - 'Действие' + 'settings-status', + 'settings-name', + 'settings-vendor', + 'settings-ipAddress', + 'settings-openPorts', + 'settings-lastActivity', + 'settings-note', + 'settings-action' ] }, { id: 'table-columns-disabled', label: '', - sectionId: 'Узлы подсети', + sectionId: 'settings-sections-subnetNodes', type: 'hidden', defaultValue: [] } ] as const; }; -export const nodesSettings = createNodesSettings(); +export { createNodesSettings }; \ No newline at end of file diff --git a/FRONT/DEV/src/store/settings/settingsStore.ts b/FRONT/DEV/src/store/settings/settingsStore.ts index b26c98d..c54ffe0 100644 --- a/FRONT/DEV/src/store/settings/settingsStore.ts +++ b/FRONT/DEV/src/store/settings/settingsStore.ts @@ -1,8 +1,8 @@ import { create } from 'zustand'; import { persist } from 'zustand/middleware'; -import {nodesSettings} from "./config/nodesSettings"; -import { networkSettings } from './config/networkSettings'; -import {appearanceSettings} from "./config/appearanceSettings"; +import {createNodesSettings} from "./config/nodesSettings"; +import { createNetworkSettings } from './config/networkSettings'; +import {createAppearanceSettings} from "./config/appearanceSettings"; type SettingValue = any; type SettingsDictionary = Record; @@ -36,18 +36,13 @@ type SettingsState = { resetToDefaults: () => void; getSettingValue: (id: string) => T; getNumberSetting: (id: string, fallback?: number) => number; + updateSettingsConfig: () => void; }; -const defaultSettings: Setting[] = [ - ...nodesSettings, - ...networkSettings, - ...appearanceSettings, -]; - export const useSettingsStore = create()( persist( (set, get) => ({ - settingsConfig: defaultSettings, + settingsConfig: [], settingsValues: {}, activeSection: null, pendingChanges: {}, @@ -90,6 +85,15 @@ export const useSettingsStore = create()( set({ settingsValues: newValues }); }, + updateSettingsConfig: () => { + const defaultSettings: Setting[] = [ + ...createNodesSettings(), + ...createNetworkSettings(), + ...createAppearanceSettings(), + ]; + set({ settingsConfig: defaultSettings }); + }, + getSetting: (id) => { const state = get(); const setting = state.settingsConfig.find(s => s.id === id); diff --git a/SRV/main.py b/SRV/main.py index 3511099..1f01b6b 100644 --- a/SRV/main.py +++ b/SRV/main.py @@ -18,5 +18,5 @@ app.include_router(router) network_service = NetworkService() if __name__ == "__main__": - uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True, ssl_keyfile="/etc/gonets/certs/gonets.key", ssl_certfile="/etc/gonets/certs/gonets.crt") - #uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True) + #uvicorn.run("main:app", host='0.0.0.0', port=8001, reload=True, ssl_keyfile="/etc/gonets/certs/gonets.key", ssl_certfile="/etc/gonets/certs/gonets.crt") + uvicorn.run("main:app", host="0.0.0.0", port=8001, reload=True) diff --git a/SRV/storage/devices/devices.json b/SRV/storage/devices/devices.json new file mode 100644 index 0000000..f298670 --- /dev/null +++ b/SRV/storage/devices/devices.json @@ -0,0 +1,284 @@ +{ + "devices": [ + { + "ip": "192.168.1.31", + "mac": "00:0c:29:a5:fb:9a", + "hostname": "OSGOS-OSOSGOS6", + "vendor": "VMware, Inc.", + "open_ports": [], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.1", + "mac": "dc:91:66:59:0c:1c", + "hostname": "_gateway", + "vendor": "Unknown", + "open_ports": [ + "DNS (UDP & TCP)", + "HTTP", + "HTTPS" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.6", + "mac": "c0:83:c9:22:76:27", + "hostname": null, + "vendor": "Unknown", + "open_ports": [ + "HTTP", + "HTTPS" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.27", + "mac": "54:16:51:32:d0:df", + "hostname": null, + "vendor": "Ruijie Networks Co.,LTD", + "open_ports": [ + "HTTP" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.40", + "mac": "c0:83:c9:22:76:14", + "hostname": null, + "vendor": "Huawei Device Co., Ltd.", + "open_ports": [ + "HTTP", + "HTTPS" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.41", + "mac": "22:b5:3c:8e:9d:7c", + "hostname": null, + "vendor": "Unknown", + "open_ports": [ + "MySQL/MariaDB" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.45", + "mac": "38:1f:8d:d2:02:b5", + "hostname": null, + "vendor": "Tuya Smart Inc.", + "open_ports": [], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.50", + "mac": "38:1f:8d:d2:77:e5", + "hostname": null, + "vendor": "Tuya Smart Inc.", + "open_ports": [], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.52", + "mac": "38:1f:8d:d4:50:d5", + "hostname": null, + "vendor": "Tuya Smart Inc.", + "open_ports": [], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.64", + "mac": "dc:a6:32:13:68:e5", + "hostname": null, + "vendor": "Unknown", + "open_ports": [ + "SSH", + "HTTP", + "HTTPS" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.108", + "mac": "b4:4c:3b:33:c1:fa", + "hostname": null, + "vendor": "Zhejiang Dahua Technology Co., Ltd.", + "open_ports": [ + "HTTP", + "HTTPS" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.120", + "mac": "c0:39:5a:1a:9d:02", + "hostname": null, + "vendor": "Zhejiang Dahua Technology Co., Ltd.", + "open_ports": [ + "HTTP" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.121", + "mac": "c0:39:5a:1a:a3:c0", + "hostname": null, + "vendor": "Zhejiang Dahua Technology Co., Ltd.", + "open_ports": [ + "HTTP" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.122", + "mac": "c0:39:5a:1a:9c:c8", + "hostname": null, + "vendor": "Unknown", + "open_ports": [ + "HTTP" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.123", + "mac": "c0:39:5a:1a:a3:a4", + "hostname": null, + "vendor": "Zhejiang Dahua Technology Co., Ltd.", + "open_ports": [ + "HTTP" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.142", + "mac": "20:11:a2:68:02:0f", + "hostname": "OSGOS-OSOSGOS", + "vendor": "Unknown", + "open_ports": [ + "SSH", + "HTTP", + "NetBIOS Session Service", + "SMB (Microsoft-DS)" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.148", + "mac": "dc:97:ba:3a:33:0b", + "hostname": "OSGOS-OSOSGOS9", + "vendor": "Unknown", + "open_ports": [ + "SSH", + "NetBIOS Session Service", + "SMB (Microsoft-DS)", + "HTTP-Alt", + "SonarQube / PHP-FPM" + ], + "status": "offline", + "last_seen": "07-07-2025, 15:31", + "note": null + }, + { + "ip": "192.168.1.168", + "mac": "e2:ec:26:f8:6b:25", + "hostname": null, + "vendor": "Unknown", + "open_ports": [], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.253", + "mac": "5c:1b:f4:97:eb:56", + "hostname": null, + "vendor": "Unknown", + "open_ports": [ + "SSH", + "DNS (UDP & TCP)", + "SMB (Microsoft-DS)", + "VNC" + ], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + }, + { + "ip": "192.168.1.9", + "mac": "5e:28:1f:e5:7d:18", + "hostname": null, + "vendor": "Unknown", + "open_ports": [], + "status": "offline", + "last_seen": "07-07-2025, 15:31", + "note": null + }, + { + "ip": "192.168.1.10", + "mac": "f6:98:16:d2:a1:0f", + "hostname": null, + "vendor": "Unknown", + "open_ports": [ + "SSH", + "DNS (UDP & TCP)", + "SMB (Microsoft-DS)", + "VNC" + ], + "status": "offline", + "last_seen": "07-07-2025, 15:31", + "note": null + }, + { + "ip": "192.168.1.69", + "mac": "52:c4:40:b1:21:4c", + "hostname": null, + "vendor": "Unknown", + "open_ports": [], + "status": "offline", + "last_seen": "07-07-2025, 15:31", + "note": null + }, + { + "ip": "192.168.1.30", + "mac": "10:ec:81:fe:10:39", + "hostname": null, + "vendor": "Samsung Electronics Co.,Ltd", + "open_ports": [], + "status": "online", + "last_seen": "07-07-2025, 16:22", + "note": null + } + ] +} \ No newline at end of file