Полная интернационализация
This commit is contained in:
parent
3315123029
commit
4286f28d1e
26 changed files with 1241 additions and 465 deletions
|
|
@ -1,22 +1,186 @@
|
||||||
{
|
{
|
||||||
"userScanner-title": "Subnet nodes scanning",
|
"userScanner-title": "Subnet Node Scanning",
|
||||||
"userScanner-visible": "Display mode",
|
"userScanner-visible": "Display Method",
|
||||||
"userScanner-loading": "Scanning nodes",
|
"userScanner-loading": "Scanning nodes",
|
||||||
"userScanner-refresh": "Refresh list",
|
"userScanner-refresh": "Refresh List",
|
||||||
"userScanner-scan": "Scan nodes",
|
"userScanner-scan": "Scan Nodes",
|
||||||
"userScanner-notFound": "No nodes found",
|
"userScanner-notFound": "Nodes not found",
|
||||||
"tableScanner-online": "Online",
|
"tableScanner-online": "Online",
|
||||||
"tableScanner-name": "Name",
|
"tableScanner-name": "Name",
|
||||||
"tableScanner-vendor": "Manufacturer",
|
"tableScanner-vendor": "Vendor",
|
||||||
"tableScanner-ip": "IP address",
|
"tableScanner-ip": "IP Address",
|
||||||
"tableScanner-ports": "Open ports",
|
"tableScanner-ports": "Open Ports",
|
||||||
"tableScanner-activity": "Last activity",
|
"tableScanner-activity": "Last Activity",
|
||||||
"tableScanner-note": "Note",
|
"tableScanner-note": "Note",
|
||||||
"tableScanner-action": "Action",
|
"tableScanner-action": "Action",
|
||||||
"tableScanner-unknown": "Unknown",
|
"tableScanner-unknown": "Unknown",
|
||||||
"tableScanner-noData": "No data to display",
|
"tableScanner-noData": "No data to display",
|
||||||
"listUsers-activeNodes": "Active nodes",
|
"listUsers-activeNodes": "Active Nodes",
|
||||||
"listUsers-inactiveNodes": "Inactive nodes",
|
"listUsers-inactiveNodes": "Inactive Nodes",
|
||||||
"ipInput-placeholder": "Enter IP",
|
"ipInput-placeholder": "Enter IP",
|
||||||
"ipInput-error": "Please enter a valid IP address"
|
"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"
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,22 +1,192 @@
|
||||||
{
|
{
|
||||||
"userScanner-title": "Сканирование узлов подсети",
|
"userScanner-title": "Сканирование узлов подсети",
|
||||||
"userScanner-visible": "Способ отображения",
|
"userScanner-visible": "Способ отображения",
|
||||||
"userScanner-loading": "Cканирование узлов",
|
"userScanner-loading": "Cканирование узлов",
|
||||||
"userScanner-refresh": "Обновить список",
|
"userScanner-refresh": "Обновить список",
|
||||||
"userScanner-scan": "Сканировать узлы",
|
"userScanner-scan": "Сканировать узлы",
|
||||||
"userScanner-notFound": "Узлы не найдены",
|
"userScanner-notFound": "Узлы не найдены",
|
||||||
"tableScanner-online": "Онлайн",
|
"tableScanner-online": "Онлайн",
|
||||||
"tableScanner-name": "Имя",
|
"tableScanner-name": "Имя",
|
||||||
"tableScanner-vendor": "Производитель",
|
"tableScanner-vendor": "Производитель",
|
||||||
"tableScanner-ip": "IP адрес",
|
"tableScanner-ip": "IP адрес",
|
||||||
"tableScanner-ports": "Открытые порты",
|
"tableScanner-ports": "Открытые порты",
|
||||||
"tableScanner-activity": "Последняя активность",
|
"tableScanner-activity": "Последняя активность",
|
||||||
"tableScanner-note": "Заметка",
|
"tableScanner-note": "Заметка",
|
||||||
"tableScanner-action": "Действие",
|
"tableScanner-action": "Действие",
|
||||||
"tableScanner-unknown": "Неизвестно",
|
"tableScanner-unknown": "Неизвестно",
|
||||||
"tableScanner-noData": "Нет данных для отображения",
|
"tableScanner-noData": "Нет данных для отображения",
|
||||||
"listUsers-activeNodes": "Активные узлы",
|
"listUsers-activeNodes": "Активные узлы",
|
||||||
"listUsers-inactiveNodes": "Неактивные узлы",
|
"listUsers-inactiveNodes": "Неактивные узлы",
|
||||||
"ipInput-placeholder": "Введите IP",
|
"ipInput-placeholder": "Введите IP",
|
||||||
"ipInput-error": "Введите корректный 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": "Английский"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,7 @@ import useNetworkStore from "../../../store/useDeviceStore";
|
||||||
import RouterLoader from "../../UI/routerLoader";
|
import RouterLoader from "../../UI/routerLoader";
|
||||||
import {useTheme} from "styled-components";
|
import {useTheme} from "styled-components";
|
||||||
import {useIpStore} from "../../../store";
|
import {useIpStore} from "../../../store";
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
address: string;
|
address: string;
|
||||||
|
|
@ -19,53 +20,55 @@ interface Props {
|
||||||
export const ConnectNetwork = ({address, onClose}: Props) => {
|
export const ConnectNetwork = ({address, onClose}: Props) => {
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const { ipAddress } = useIpStore();
|
const {ipAddress} = useIpStore();
|
||||||
const [error, setError] = useState('');
|
const [error, setError] = useState('');
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
setError('')
|
setError('');
|
||||||
const res = await NetworkService.connectNetwork(ipAddress, password, address);
|
const res = await NetworkService.connectNetwork(ipAddress, password, address);
|
||||||
if(res.status === 200) {
|
if (res.status === 200) {
|
||||||
onClose()
|
onClose();
|
||||||
}else{
|
} else {
|
||||||
setError(res.data.detail)
|
setError(res.data.detail);
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error connection:', error);
|
console.error(t('connectionErrorLog'), error);
|
||||||
setError('Ошибка подключения к сети')
|
setError(t('connectionError'));
|
||||||
}finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContainer style={{ height: '230px' }}>
|
<ModalContainer style={{height: '230px'}}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Подключение к сети</h3>
|
<h3>{t('networkConnection')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '8px'}}>
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
placeholderText='Введите пароль...'
|
placeholderText={t('enterPassword')}
|
||||||
value={password}
|
value={password}
|
||||||
id='deviceNote'
|
id='deviceNote'
|
||||||
onChange={(value) => setPassword(value)}
|
onChange={(value) => setPassword(value)}
|
||||||
/>
|
/>
|
||||||
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
|
{error && (
|
||||||
{error}
|
<p style={{color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right'}}>
|
||||||
</p>
|
{error}
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title="Отмена"
|
title={t('cancel')}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<MainButton
|
<MainButton
|
||||||
|
|
@ -73,12 +76,12 @@ export const ConnectNetwork = ({address, onClose}: Props) => {
|
||||||
size="small"
|
size="small"
|
||||||
color={theme.successColor}
|
color={theme.successColor}
|
||||||
hoverColor={theme.successColor}
|
hoverColor={theme.successColor}
|
||||||
title='Подключиться'
|
title={t('connect')}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
|
||||||
{isLoading && <RouterLoader loaderText='Попытка подключения к сети'/>}
|
{isLoading && <RouterLoader loaderText={t('connectingAttempt')}/>}
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
@ -1,4 +1,3 @@
|
||||||
// components/modalContainers/NoteDevice.tsx
|
|
||||||
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
|
import { ModalContainer, ModalTitle, ModalContent, ButtonContainer } from "../style";
|
||||||
import { MainButton } from "../../UI/buttons/mainButton";
|
import { MainButton } from "../../UI/buttons/mainButton";
|
||||||
import Input from "../../UI/inputs/input";
|
import Input from "../../UI/inputs/input";
|
||||||
|
|
@ -6,6 +5,7 @@ import { useState } from "react";
|
||||||
import {useIpStore} from "../../../store";
|
import {useIpStore} from "../../../store";
|
||||||
import {NetworkService} from "../../../api/request";
|
import {NetworkService} from "../../../api/request";
|
||||||
import useNetworkStore from "../../../store/useDeviceStore";
|
import useNetworkStore from "../../../store/useDeviceStore";
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
address: string;
|
address: string;
|
||||||
|
|
@ -20,6 +20,7 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
|
||||||
const [note, setNote] = useState(currentNote || '');
|
const [note, setNote] = useState(currentNote || '');
|
||||||
const { ipAddress } = useIpStore();
|
const { ipAddress } = useIpStore();
|
||||||
const { devices, setDevices } = useNetworkStore();
|
const { devices, setDevices } = useNetworkStore();
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -50,13 +51,13 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
|
||||||
return (
|
return (
|
||||||
<ModalContainer style={{ height: '250px' }}>
|
<ModalContainer style={{ height: '250px' }}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}</h3>
|
<h3>{currentNote ? t('note-edit') : t('note-add')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholderText='Введите заметку...'
|
placeholderText={t('note-enter')}
|
||||||
value={note}
|
value={note}
|
||||||
id='deviceNote'
|
id='deviceNote'
|
||||||
onChange={(value) => setNote(value)}
|
onChange={(value) => setNote(value)}
|
||||||
|
|
@ -67,13 +68,13 @@ export const NoteDevice = ({ address, onClose, currentNote, type}: Props) => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title="Отмена"
|
title={t('cancel')}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Сохранить'
|
title={t('save')}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import { useState } from "react";
|
||||||
import {NetworkService} from "../../../api/request";
|
import {NetworkService} from "../../../api/request";
|
||||||
import useNetworkStore from "../../../store/useDeviceStore";
|
import useNetworkStore from "../../../store/useDeviceStore";
|
||||||
import {useIpStore} from "../../../store";
|
import {useIpStore} from "../../../store";
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
address: string;
|
address: string;
|
||||||
|
|
@ -20,6 +21,7 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
|
||||||
const [note, setNote] = useState(currentNote || '');
|
const [note, setNote] = useState(currentNote || '');
|
||||||
const { ipAddress } = useIpStore();
|
const { ipAddress } = useIpStore();
|
||||||
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const handleSave = async () => {
|
const handleSave = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -50,13 +52,13 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
|
||||||
return (
|
return (
|
||||||
<ModalContainer style={{ height: '220px' }}>
|
<ModalContainer style={{ height: '220px' }}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>{currentNote ? 'Редактирование заметки' : 'Добавление заметки'}</h3>
|
<h3>{currentNote ? t('note-edit') : t('note-add')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholderText='Введите заметку...'
|
placeholderText={t('note-enter')}
|
||||||
value={note}
|
value={note}
|
||||||
id='deviceNote'
|
id='deviceNote'
|
||||||
onChange={(value) => setNote(value)}
|
onChange={(value) => setNote(value)}
|
||||||
|
|
@ -67,13 +69,13 @@ export const NoteWifi = ({ address, onClose, currentNote, type}: Props) => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title="Отмена"
|
title={t('cancel')}
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
/>
|
/>
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Сохранить'
|
title={t('save')}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import { MainButton } from "../../UI/buttons/mainButton";
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
|
import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer } from "../style"
|
||||||
import { validateIPAddress } from "../../../utils/nodeController/validator";
|
import { validateIPAddress } from "../../../utils/nodeController/validator";
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onSave?: (data: string) => void;
|
onSave?: (data: string) => void;
|
||||||
|
|
@ -12,6 +13,7 @@ interface Props {
|
||||||
export const AddDevice = ({ onSave, onClose }: Props) => {
|
export const AddDevice = ({ onSave, onClose }: Props) => {
|
||||||
const [ipAddress, setIpAddress] = useState('')
|
const [ipAddress, setIpAddress] = useState('')
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const handleSubmit = () => {
|
const handleSubmit = () => {
|
||||||
if (!validateIPAddress(ipAddress)) {
|
if (!validateIPAddress(ipAddress)) {
|
||||||
|
|
@ -28,31 +30,39 @@ export const AddDevice = ({ onSave, onClose }: Props) => {
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContainer style={{ height: '230px' }}>
|
<ModalContainer style={{ height: '230px' }}>
|
||||||
{/* <form onSubmit={handleSubmit}> */}
|
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Добавление узла</h3>
|
<h3>{t('addNode')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
|
<ModalContent style={{ display: 'flex', flexDirection: 'column', gap: '5px' }}>
|
||||||
<Input
|
<Input
|
||||||
type="text"
|
type="text"
|
||||||
placeholderText='Введите адрес узла...'
|
placeholderText={t('enterNodeAddress')}
|
||||||
id="newIp"
|
id="newIp"
|
||||||
value={ipAddress}
|
value={ipAddress}
|
||||||
onChange={(value) => setIpAddress(value)}
|
onChange={(value) => setIpAddress(value)}
|
||||||
/>
|
/>
|
||||||
{error && (
|
{error && (
|
||||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
<p style={{ color: 'red', marginTop: '5px' }}>
|
||||||
Некорректный адрес узла
|
{t('invalidAddress')}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton type="button" size="small" title="Отмена" onClick={onClose} />
|
<MainButton
|
||||||
<MainButton type="submit" size="small" title="Сохранить" onClick={handleSubmit} />
|
type="button"
|
||||||
|
size="small"
|
||||||
|
title={t('cancel')}
|
||||||
|
onClick={onClose}
|
||||||
|
/>
|
||||||
|
<MainButton
|
||||||
|
type="submit"
|
||||||
|
size="small"
|
||||||
|
title={t('save')}
|
||||||
|
onClick={handleSubmit}
|
||||||
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
{/* </form> */}
|
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -2,6 +2,7 @@ import { ModalContainer, ModalTitle, ModalContent, ModalText, ButtonContainer }
|
||||||
import { useModalContext } from "../../../store"
|
import { useModalContext } from "../../../store"
|
||||||
import { useIpStore } from "../../../store"
|
import { useIpStore } from "../../../store"
|
||||||
import { MainButton } from "../../UI/buttons/mainButton"
|
import { MainButton } from "../../UI/buttons/mainButton"
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
type Props={
|
type Props={
|
||||||
message: string
|
message: string
|
||||||
|
|
@ -10,28 +11,37 @@ type Props={
|
||||||
export const ConnectionError = ({message}: Props)=>{
|
export const ConnectionError = ({message}: Props)=>{
|
||||||
const {closeModal} = useModalContext()
|
const {closeModal} = useModalContext()
|
||||||
const { setIpAddress } = useIpStore();
|
const { setIpAddress } = useIpStore();
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const close = ()=>{
|
const close = ()=>{
|
||||||
setIpAddress('localhost')
|
setIpAddress('localhost')
|
||||||
closeModal()
|
closeModal()
|
||||||
}
|
}
|
||||||
|
|
||||||
return(
|
return (
|
||||||
<ModalContainer>
|
<ModalContainer>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Соединение прервано</h3>
|
<h3>{t('connectionLost')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalText>
|
<ModalText>
|
||||||
<h3>Произошёл разрыв с сервером</h3>
|
<h3>{t('serverDisconnected')}</h3>
|
||||||
<p>Пожалуйста, проверьте доступ к адресу и попробуйте снова.</p>
|
<p>{t('checkAddressAndRetry')}</p>
|
||||||
</ModalText>
|
</ModalText>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton title="Закрыть" onClick={close} size="small"/>
|
<MainButton
|
||||||
<MainButton title="Повторить" onClick={closeModal} size="small"/>
|
title={t('close')}
|
||||||
|
onClick={close}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<MainButton
|
||||||
|
title={t('retry')}
|
||||||
|
onClick={closeModal}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {ModalMap} from "../windowsType";
|
||||||
import {useModalContext} from "../../../store";
|
import {useModalContext} from "../../../store";
|
||||||
import styled from "styled-components";
|
import styled from "styled-components";
|
||||||
import { useSettingsStore } from '../../../store/settings/settingsStore';
|
import { useSettingsStore } from '../../../store/settings/settingsStore';
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
const StyledTableList = styled.ul`
|
const StyledTableList = styled.ul`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -42,7 +43,7 @@ const CustomizeNodes = () => {
|
||||||
const updateSetting = useSettingsStore(state => state.updateSetting);
|
const updateSetting = useSettingsStore(state => state.updateSetting);
|
||||||
const getSetting = useSettingsStore(state => state.getSetting);
|
const getSetting = useSettingsStore(state => state.getSetting);
|
||||||
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
||||||
|
const {t} = useTranslation('common');
|
||||||
const [dragIndex, setDragIndex] = useState<number | null>(null);
|
const [dragIndex, setDragIndex] = useState<number | null>(null);
|
||||||
|
|
||||||
// Получаем текущие значения из хранилища
|
// Получаем текущие значения из хранилища
|
||||||
|
|
@ -88,11 +89,22 @@ const CustomizeNodes = () => {
|
||||||
initializeSettings();
|
initializeSettings();
|
||||||
}, [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 (
|
return (
|
||||||
<ModalContainer style={{ height: '35vh', width: '80vw'}}>
|
<ModalContainer style={{ height: '35vh', width: '80vw'}}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Кастомизация таблицы узлов подсети</h3>
|
<h3>{t('customizeTable-title')}</h3>
|
||||||
|
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent style={{ display: 'flex', height: "100%", gap: '24px', padding: 0 }}>
|
<ModalContent style={{ display: 'flex', height: "100%", gap: '24px', padding: 0 }}>
|
||||||
|
|
@ -110,7 +122,7 @@ const CustomizeNodes = () => {
|
||||||
onDragEnd={handleDrop}
|
onDragEnd={handleDrop}
|
||||||
onClick={() => handleSwitch(index)}
|
onClick={() => handleSwitch(index)}
|
||||||
>
|
>
|
||||||
{item}
|
{getItemText(item)}
|
||||||
</StyledTableItem>
|
</StyledTableItem>
|
||||||
))}
|
))}
|
||||||
</StyledTableList>
|
</StyledTableList>
|
||||||
|
|
@ -120,7 +132,7 @@ const CustomizeNodes = () => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Назад'
|
title={t('common-back')}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {useState} from "react";
|
||||||
import {NetworkService} from "../../../api/request";
|
import {NetworkService} from "../../../api/request";
|
||||||
import RouterLoader from "../../UI/routerLoader";
|
import RouterLoader from "../../UI/routerLoader";
|
||||||
import {useTheme} from "styled-components";
|
import {useTheme} from "styled-components";
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
type Props={
|
type Props={
|
||||||
address: string,
|
address: string,
|
||||||
|
|
@ -16,6 +17,7 @@ export const DisconnectConfirm = ({address, onClose}: Props)=>{
|
||||||
const { ipAddress } = useIpStore();
|
const { ipAddress } = useIpStore();
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const handleDisconnect = async () => {
|
const handleDisconnect = async () => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -34,21 +36,32 @@ export const DisconnectConfirm = ({address, onClose}: Props)=>{
|
||||||
return(
|
return(
|
||||||
<ModalContainer>
|
<ModalContainer>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Отключение от Wi-Fi сети</h3>
|
<h3>{t('wifiDisconnect-title')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalText>
|
<ModalText>
|
||||||
<h3>Подтверждение отключения</h3>
|
<h3>{t('wifiDisconnect-confirm')}</h3>
|
||||||
|
<p>{t('wifiDisconnect-message1')} + {address}. {t('wifiDisconnect-message2')}</p>
|
||||||
<p>Вы собираетесь отключиться от сети "{address}". Все активные подключения будут прерваны.</p>
|
<p>Вы собираетесь отключиться от сети "{address}". Все активные подключения будут прерваны.</p>
|
||||||
</ModalText>
|
</ModalText>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton title="Отменить" onClick={onClose} size="small"/>
|
<MainButton
|
||||||
<MainButton title="Отключиться" color={theme.errorColor} hoverColor={theme.errorColor} onClick={handleDisconnect} size="small"/>
|
title={t('cancel')}
|
||||||
|
onClick={onClose}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
|
<MainButton
|
||||||
|
title={t('wifiDisconnect-disconnect')}
|
||||||
|
color={theme.errorColor}
|
||||||
|
hoverColor={theme.errorColor}
|
||||||
|
onClick={handleDisconnect}
|
||||||
|
size="small"
|
||||||
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
{isLoading && <RouterLoader loaderText='Отключение от сети'/>}
|
{isLoading && <RouterLoader loaderText={t('wifiDisconnect-progress')}/>}
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import { useIpStore } from "../../../store";
|
||||||
import { MainButton } from "../../UI/buttons/mainButton";
|
import { MainButton } from "../../UI/buttons/mainButton";
|
||||||
import { useAdminStore } from "../../../store/adminStore";
|
import { useAdminStore } from "../../../store/adminStore";
|
||||||
import { useDevicesStore } from "../../../store";
|
import { useDevicesStore } from "../../../store";
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
ipData: changeIp
|
ipData: changeIp
|
||||||
|
|
@ -52,6 +53,7 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
||||||
const { ipAddress, setIpAddress } = useIpStore();
|
const { ipAddress, setIpAddress } = useIpStore();
|
||||||
const {isAdmin} = useAdminStore()
|
const {isAdmin} = useAdminStore()
|
||||||
const { devices, addDevice, deleteDevice } = useDevicesStore();
|
const { devices, addDevice, deleteDevice } = useDevicesStore();
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ipData && isAdmin) {
|
if (ipData && isAdmin) {
|
||||||
|
|
@ -146,40 +148,48 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{loader && isAdmin ? (
|
{loader && isAdmin ? (
|
||||||
<Loader loaderText="Идет проверка корректности IP адреса" />
|
<Loader loaderText={t('ipChange-checkingIp')} />
|
||||||
) : (
|
) : (
|
||||||
<ModalContainer {...(checkStatus ? { height: "unset" } : {})}>
|
<ModalContainer {...(checkStatus ? { height: "unset" } : {})}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Изменение IP-Адреса</h3>
|
<h3>{t('ipChange-title')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
{checkStatus ? (
|
{checkStatus ? (
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<Information>
|
<Information>
|
||||||
<h3>Общие сведения</h3>
|
<h3>{t('ipChange-generalInfo')}</h3>
|
||||||
<Info>
|
<Info>
|
||||||
<p>Интерфейс: {ipData.device}</p>
|
<p>{t('ipChange-interface')}: {ipData.device}</p>
|
||||||
<p>--{'>'}</p>
|
<p>--{'>'}</p>
|
||||||
<p>{ipData.connection}</p>
|
<p>{ipData.connection}</p>
|
||||||
</Info>
|
</Info>
|
||||||
<h3>Сведения об IP</h3>
|
<h3>{t('ipChange-ipInfo')}</h3>
|
||||||
<DataContainer>
|
<DataContainer>
|
||||||
<CurrentData>
|
<CurrentData>
|
||||||
<p>Текущий ip: {ipData.currentIp}</p>
|
<p>{t('ipChange-currentIp')}: {ipData.currentIp}</p>
|
||||||
<p>Текущий шлюз: {ipData.currentGate}</p>
|
<p>{t('ipChange-currentGateway')}: {ipData.currentGate}</p>
|
||||||
<p>Текущий DNS: {ipData.currentDns}</p>
|
<p>{t('ipChange-currentDns')}: {ipData.currentDns}</p>
|
||||||
</CurrentData>
|
</CurrentData>
|
||||||
|
|
||||||
<NewData>
|
<NewData>
|
||||||
<p>Новый ip: {ipData.newIp}</p>
|
<p>{t('ipChange-newIp')}: {ipData.newIp}</p>
|
||||||
<p>Новый шлюз: {ipData.newGate}</p>
|
<p>{t('ipChange-newGateway')}: {ipData.newGate}</p>
|
||||||
<p>Новый DNS: {ipData.newDns}</p>
|
<p>{t('ipChange-newDns')}: {ipData.newDns}</p>
|
||||||
</NewData>
|
</NewData>
|
||||||
</DataContainer>
|
</DataContainer>
|
||||||
</Information>
|
</Information>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton title="Отмена" size='small' onClick={() => cancelIp(currentIp)}/>
|
<MainButton
|
||||||
<MainButton title="Сохранить" size='small' onClick={() => saveIp(newIP)}/>
|
title={t('cancel')}
|
||||||
|
size='small'
|
||||||
|
onClick={() => cancelIp(currentIp)}
|
||||||
|
/>
|
||||||
|
<MainButton
|
||||||
|
title={t('save')}
|
||||||
|
size='small'
|
||||||
|
onClick={() => saveIp(newIP)}
|
||||||
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -188,19 +198,21 @@ export const IpConfirm = ({ ipData, onClose, smoothClose }: Props) => {
|
||||||
<ModalText>
|
<ModalText>
|
||||||
{isAdmin ? (
|
{isAdmin ? (
|
||||||
<>
|
<>
|
||||||
<h3>Произошла ошибка проверки корректности</h3>
|
<h3>{t('ipChange-checkError')}</h3>
|
||||||
<ErrorMessage>{message}</ErrorMessage>
|
<ErrorMessage>{message}</ErrorMessage>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
):(
|
):(
|
||||||
<h3>Для изменения сетевых настроек необходимо авторизироваться под администратором</h3>
|
<h3>{t('ipChange-adminRequired')}</h3>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</ModalText>
|
</ModalText>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton title="Закрыть" size='small' onClick={smoothClose}/>
|
<MainButton
|
||||||
|
title={t('close')}
|
||||||
|
size='small'
|
||||||
|
onClick={smoothClose}
|
||||||
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
|
|
|
||||||
|
|
@ -9,6 +9,7 @@ import { useCheckAdmin } from "../../../utils/checkAdmin"
|
||||||
|
|
||||||
import { buttonNames } from "../../../const/buttons/names"
|
import { buttonNames } from "../../../const/buttons/names"
|
||||||
import { IconComponent } from "../../../const/buttons/icons"
|
import { IconComponent } from "../../../const/buttons/icons"
|
||||||
|
import {useTranslation} from "../../../utils/i18n/hooks";
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
|
|
@ -17,9 +18,10 @@ interface Props {
|
||||||
export const RightsConfirm = ({ onClose }: Props) => {
|
export const RightsConfirm = ({ onClose }: Props) => {
|
||||||
|
|
||||||
const { ipAddress } = useIpStore();
|
const { ipAddress } = useIpStore();
|
||||||
const { isAdmin, setAdmin } = useAdminStore()
|
const { isAdmin } = useAdminStore()
|
||||||
const checkAdmin = useCheckAdmin();
|
const checkAdmin = useCheckAdmin();
|
||||||
const [error, setError] = useState(false)
|
const [error, setError] = useState(false)
|
||||||
|
const {t} = useTranslation('common');
|
||||||
const [success, setSuccess] = useState({
|
const [success, setSuccess] = useState({
|
||||||
message: '',
|
message: '',
|
||||||
status: false
|
status: false
|
||||||
|
|
@ -45,7 +47,7 @@ export const RightsConfirm = ({ onClose }: Props) => {
|
||||||
setError(false)
|
setError(false)
|
||||||
setSuccess(prevData => ({
|
setSuccess(prevData => ({
|
||||||
...prevData,
|
...prevData,
|
||||||
message: 'Успешный вход',
|
message: t('auth-successLogin'),
|
||||||
status: true
|
status: true
|
||||||
}));
|
}));
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
|
|
@ -54,20 +56,20 @@ export const RightsConfirm = ({ onClose }: Props) => {
|
||||||
setError(true)
|
setError(true)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('Error:', err);
|
console.error(t('auth-error'), err);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const logout = async () => {
|
const logout = async () => {
|
||||||
await NetworkService.logout(ipAddress);
|
await NetworkService.logout(ipAddress);
|
||||||
await checkAdmin();
|
await checkAdmin();
|
||||||
setSuccess(prevData => ({
|
setSuccess(prevData => ({
|
||||||
...prevData,
|
...prevData,
|
||||||
message: 'Успешный выход',
|
message: t('auth-successLogout'),
|
||||||
status: true
|
status: true
|
||||||
}));
|
}));
|
||||||
await new Promise(resolve => setTimeout(resolve, 3000));
|
await new Promise(resolve => setTimeout(resolve, 3000));
|
||||||
onClose();
|
onClose();
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
@ -76,60 +78,55 @@ export const RightsConfirm = ({ onClose }: Props) => {
|
||||||
!isAdmin ? (
|
!isAdmin ? (
|
||||||
<>
|
<>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Получение прав доступа</h3>
|
<h3>{t('auth-getAccess')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '10px'}}>
|
<ModalContent style={{display: 'flex', flexDirection: 'column', gap: '10px'}}>
|
||||||
|
<h3 style={{marginLeft: '5px', fontSize: '14px'}}>{t('auth-enableAdmin')}</h3>
|
||||||
<h3 style={{marginLeft: '5px', fontSize: '14px'}}>Включить административный доступ</h3>
|
|
||||||
<Input
|
<Input
|
||||||
type="password"
|
type="password"
|
||||||
placeholderText='Введите пароль администратора...'
|
placeholderText={t('auth-enterAdminPass')}
|
||||||
id="password"
|
id="password"
|
||||||
value={user.password}
|
value={user.password}
|
||||||
onChange={(value) => handleChange('password', value)}
|
onChange={(value) => handleChange('password', value)}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
{error && (
|
{error && (
|
||||||
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
|
<p style={{ color: 'red', marginLeft: '10px', fontSize: '14px', textAlign: 'right' }}>
|
||||||
Неверный пароль оператора
|
{t('auth-wrongPass')}
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton size="small" title="Отмена" onClick={onClose} />
|
<MainButton size="small" title={t('cancel')} onClick={onClose} />
|
||||||
<MainButton size="small" title="Отправить" onClick={() => login(user)} />
|
<MainButton size="small" title={t('submit')} onClick={() => login(user)} />
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Переключиться на ограниченный доступ</h3>
|
<h3>{t('auth-switchToLimited')}</h3>
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
|
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<p>В данном режиме доступа ограничиваются полномочия администратора.</p>
|
<p>{t('auth-limitedModeDesc')}</p>
|
||||||
</ModalContent>
|
</ModalContent>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton size="small" title="Отмена" onClick={onClose} />
|
<MainButton size="small" title={t('cancel')} onClick={onClose} />
|
||||||
<MainButton size="small" title="Ограничить доступ" onClick={logout} />
|
<MainButton size="small" title={t('auth-restrictAccess')} onClick={logout} />
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
|
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
):(
|
):(
|
||||||
<ModalContainer style={{display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', marginTop: '15px'}}>
|
<ModalContainer style={{display: 'flex', justifyContent: 'center', alignItems: 'center', flexDirection: 'column', marginTop: '15px'}}>
|
||||||
<IconComponent name={buttonNames[8]} width="50" height="50"/>
|
<IconComponent name={buttonNames[8]} width="50" height="50"/>
|
||||||
<h3>{success.message}</h3>
|
<h3>{success.message}</h3>
|
||||||
<p style={{fontSize: '12px'}}>Это окно можно закрыть, либо оно пропадёт само</p>
|
<p style={{fontSize: '12px'}}>{t('auth-closeHint')}</p>
|
||||||
<MainButton size="small" title="Закрыть" onClick={onClose} style={{width: '30%'}}/>
|
<MainButton size="small" title={t('close')} onClick={onClose} style={{width: '30%'}}/>
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|
||||||
</ModalContainer>
|
</ModalContainer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -9,6 +9,7 @@ import { IconComponent } from "../const/buttons/icons"
|
||||||
import { buttonNames } from "../const/buttons/names"
|
import { buttonNames } from "../const/buttons/names"
|
||||||
import ThemeButton from "./UI/buttons/toggletheme"
|
import ThemeButton from "./UI/buttons/toggletheme"
|
||||||
import About from "./workSpace/modules/appModules/about"
|
import About from "./workSpace/modules/appModules/about"
|
||||||
|
import {useTranslation} from "../utils/i18n/hooks";
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -73,6 +74,7 @@ export const CreateUser = () => {
|
||||||
const [repeatPassword, setRepeatPassword] = useState('');
|
const [repeatPassword, setRepeatPassword] = useState('');
|
||||||
const [passwordsMatch, setPasswordsMatch] = useState(true);
|
const [passwordsMatch, setPasswordsMatch] = useState(true);
|
||||||
const [about, setAbout] = useState(false)
|
const [about, setAbout] = useState(false)
|
||||||
|
const {t} = useTranslation('common')
|
||||||
|
|
||||||
const [user, setUser] = useState({
|
const [user, setUser] = useState({
|
||||||
username: 'operator',
|
username: 'operator',
|
||||||
|
|
@ -118,77 +120,81 @@ export const CreateUser = () => {
|
||||||
setAbout(true)
|
setAbout(true)
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
|
|
||||||
<>
|
<>
|
||||||
{!about ? (
|
{!about ? (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
|
<Container onSubmit={handleSubmit}>
|
||||||
|
<Title>
|
||||||
|
<IconComponent name={buttonNames[3]} width="52" height="52" />
|
||||||
|
<h3>{t('operatorConfig-title')}</h3>
|
||||||
|
{ipAddress != 'localhost' &&
|
||||||
|
<p>{ipAddress}</p>
|
||||||
|
}
|
||||||
|
</Title>
|
||||||
|
|
||||||
<Container onSubmit={handleSubmit}>
|
<Content>
|
||||||
<Title>
|
<div>
|
||||||
<IconComponent name={buttonNames[3]} width="52" height="52"></IconComponent>
|
<p>{t('operatorConfig-instruction')}</p>
|
||||||
<h3>Настройка оператора узла</h3>
|
</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' &&
|
{ipAddress != 'localhost' &&
|
||||||
<p>{ipAddress}</p>
|
<MainButton
|
||||||
|
title={t('common-back')}
|
||||||
|
onClick={back}
|
||||||
|
style={{marginRight: '10px'}}
|
||||||
|
/>
|
||||||
}
|
}
|
||||||
|
<ThemeButton />
|
||||||
</Title>
|
<Button>
|
||||||
|
<IconContainer>
|
||||||
<Content>
|
<IconComponent
|
||||||
|
name={buttonNames[4]}
|
||||||
<div>
|
width="30"
|
||||||
<p>Перед началом работы необходимо создать пароль оператора приложения</p>
|
height="30"
|
||||||
</div>
|
onClick={toAbout}
|
||||||
|
/>
|
||||||
<div>
|
</IconContainer>
|
||||||
<Input
|
</Button>
|
||||||
type="password"
|
|
||||||
placeholderText='Введите пароль...'
|
|
||||||
id="password"
|
|
||||||
value={user.password}
|
|
||||||
onChange={(value) => handleChange('password', value)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<Input
|
|
||||||
type="password"
|
|
||||||
placeholderText='Повторите пароль...'
|
|
||||||
id="repeatPassword"
|
|
||||||
value={repeatPassword}
|
|
||||||
onChange={(value) => handleChange('repeatPassword', value)}
|
|
||||||
style={{ marginTop: '10px' }}
|
|
||||||
/>
|
|
||||||
{!passwordsMatch && (
|
|
||||||
<p style={{ color: 'red', marginTop: '5px' }}>
|
|
||||||
Пароли не совпадают!
|
|
||||||
</p>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</Content>
|
|
||||||
|
|
||||||
<ButtonContainer>
|
|
||||||
<MainButton style={{ width: '100%' }} title="Отправить" type="submit" />
|
|
||||||
</ButtonContainer>
|
|
||||||
|
|
||||||
|
|
||||||
</Container>
|
|
||||||
|
|
||||||
|
|
||||||
<Menu>
|
|
||||||
{ipAddress != 'localhost' &&
|
|
||||||
<MainButton title="Назад" onClick={back} style={{marginRight: '10px'}}/>
|
|
||||||
}
|
|
||||||
<ThemeButton />
|
|
||||||
<Button>
|
|
||||||
<IconContainer>
|
|
||||||
<IconComponent name={buttonNames[4]} width="30" height="30" onClick={toAbout}/>
|
|
||||||
</IconContainer>
|
|
||||||
</Button>
|
|
||||||
</Menu>
|
</Menu>
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
): (
|
) : (
|
||||||
<About from="reg" onBack={() => setAbout(false)} />
|
<About from="reg" onBack={() => setAbout(false)} />
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
|
);
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
@ -8,6 +8,7 @@ import {MainButton} from "../UI/buttons/mainButton";
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import {useModalContext} from "../../store";
|
import {useModalContext} from "../../store";
|
||||||
import {ModalMap} from "../modalwindows/windowsType";
|
import {ModalMap} from "../modalwindows/windowsType";
|
||||||
|
import {useTranslation} from "../../utils/i18n/hooks";
|
||||||
|
|
||||||
const SettingBlock = styled.div`
|
const SettingBlock = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -89,6 +90,7 @@ interface SettingItemProps {
|
||||||
|
|
||||||
const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => {
|
const SettingItem = React.memo(({ setting, value, onChange, onValidationError }: SettingItemProps) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
const { t } = useTranslation('common');
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
const { openModal } = useModalContext();
|
const { openModal } = useModalContext();
|
||||||
|
|
||||||
|
|
@ -96,7 +98,35 @@ const SettingItem = React.memo(({ setting, value, onChange, onValidationError }:
|
||||||
if (setting.id === 'customize-nodes') {
|
if (setting.id === 'customize-nodes') {
|
||||||
openModal(ModalMap['11']);
|
openModal(ModalMap['11']);
|
||||||
} else if (setting.action) {
|
} 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;
|
let newError: string | null = null;
|
||||||
|
|
||||||
if (inputValue.trim() === '') {
|
if (inputValue.trim() === '') {
|
||||||
newError = 'Поле не может быть пустым';
|
newError = t('settings-fieldRequired');
|
||||||
onChange(null);
|
onChange(null);
|
||||||
}
|
}
|
||||||
else if (isNaN(Number(inputValue))) {
|
else if (isNaN(Number(inputValue))) {
|
||||||
newError = 'Пожалуйста, введите число';
|
newError = t('settings-enterNumber');
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
const numValue = Number(inputValue);
|
const numValue = Number(inputValue);
|
||||||
if (setting.min !== undefined && numValue < setting.min) {
|
if (setting.min !== undefined && numValue < setting.min) {
|
||||||
newError = `Значение не может быть меньше ${setting.min}`;
|
newError = t('settings-valueTooSmall') + setting.min;
|
||||||
}
|
}
|
||||||
else if (setting.max !== undefined && numValue > setting.max) {
|
else if (setting.max !== undefined && numValue > setting.max) {
|
||||||
newError = `Значение не может быть больше ${setting.max}`;
|
newError = t('settings-valueTooLarge') + setting.max;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!newError) {
|
if (!newError) {
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import {Setting, useSettingsStore} from "../../store/settings/settingsStore";
|
||||||
import {SettingItem} from "./SettingItem";
|
import {SettingItem} from "./SettingItem";
|
||||||
import {IconComponent} from "../../const/buttons/icons";
|
import {IconComponent} from "../../const/buttons/icons";
|
||||||
import {buttonNames} from "../../const/buttons/names";
|
import {buttonNames} from "../../const/buttons/names";
|
||||||
|
import {useTranslation} from "../../utils/i18n/hooks";
|
||||||
|
|
||||||
|
|
||||||
interface Props {
|
interface Props {
|
||||||
|
|
@ -70,9 +71,12 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
const getSetting = useSettingsStore(state => state.getSetting);
|
const getSetting = useSettingsStore(state => state.getSetting);
|
||||||
const resetToDefaults = useSettingsStore(state => state.resetToDefaults);
|
const resetToDefaults = useSettingsStore(state => state.resetToDefaults);
|
||||||
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
const initializeSettings = useSettingsStore(state => state.initializeSettings);
|
||||||
|
const updateSettingsConfig = useSettingsStore(state => state.updateSettingsConfig);
|
||||||
|
|
||||||
const theme = useTheme()
|
const theme = useTheme()
|
||||||
|
const { t, language } = useTranslation('common');
|
||||||
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
|
const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
|
||||||
|
const [currentLanguage, setCurrentLanguage] = useState(language);
|
||||||
|
|
||||||
// Проверка действий
|
// Проверка действий
|
||||||
const handleValidationError = useCallback((fieldId: string, error: string | null) => {
|
const handleValidationError = useCallback((fieldId: string, error: string | null) => {
|
||||||
|
|
@ -89,22 +93,33 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
|
|
||||||
const settingsBySection = useMemo(() => {
|
const settingsBySection = useMemo(() => {
|
||||||
return settingsConfig.reduce((acc, setting) => {
|
return settingsConfig.reduce((acc, setting) => {
|
||||||
if (!acc[setting.sectionId]) {
|
// Применяем переводы к настройкам
|
||||||
acc[setting.sectionId] = [];
|
const translatedSetting = {
|
||||||
}
|
|
||||||
acc[setting.sectionId].push({
|
|
||||||
...setting,
|
...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
|
value: settingsValues[setting.id] ?? setting.defaultValue
|
||||||
});
|
};
|
||||||
|
|
||||||
|
if (!acc[translatedSetting.sectionId]) {
|
||||||
|
acc[translatedSetting.sectionId] = [];
|
||||||
|
}
|
||||||
|
acc[translatedSetting.sectionId].push(translatedSetting);
|
||||||
return acc;
|
return acc;
|
||||||
}, {} as Record<string, Setting[]>);
|
}, {} as Record<string, Setting[]>);
|
||||||
}, [settingsConfig, settingsValues]);
|
}, [settingsConfig, settingsValues, t]);
|
||||||
|
|
||||||
const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]);
|
const sections = useMemo(() => Object.keys(settingsBySection), [settingsBySection]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
updateSettingsConfig();
|
||||||
initializeSettings();
|
initializeSettings();
|
||||||
}, [initializeSettings]);
|
}, [updateSettingsConfig, initializeSettings]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (sections.length > 0 && !activeSection) {
|
if (sections.length > 0 && !activeSection) {
|
||||||
|
|
@ -112,10 +127,15 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
}
|
}
|
||||||
}, [sections, activeSection, setActiveSection]);
|
}, [sections, activeSection, setActiveSection]);
|
||||||
|
|
||||||
|
// Обновляем текущий язык при изменении
|
||||||
|
useEffect(() => {
|
||||||
|
setCurrentLanguage(language);
|
||||||
|
}, [language]);
|
||||||
|
|
||||||
const handleSave = useCallback(() => {
|
const handleSave = useCallback(() => {
|
||||||
const hasErrors = Object.values(fieldErrors).some(error => error?.trim());
|
const hasErrors = Object.values(fieldErrors).some(error => error?.trim());
|
||||||
if (hasErrors) {
|
if (hasErrors) {
|
||||||
alert('Исправьте ошибки перед сохранением');
|
alert(t('settings-fixErrors'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -126,13 +146,24 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
if (emptyRequiredFields.length > 0) {
|
if (emptyRequiredFields.length > 0) {
|
||||||
alert('Заполните все обязательные поля');
|
alert(t('settings-fillRequired'));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Проверяем, изменился ли язык
|
||||||
|
const newLanguage = pendingChanges['language'] ?? settingsValues['language'];
|
||||||
|
const languageChanged = newLanguage && newLanguage !== currentLanguage;
|
||||||
|
|
||||||
applyChanges();
|
applyChanges();
|
||||||
onClose();
|
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(() => {
|
const handleCancel = useCallback(() => {
|
||||||
discardChanges();
|
discardChanges();
|
||||||
|
|
@ -141,14 +172,14 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
}, [discardChanges, onClose]);
|
}, [discardChanges, onClose]);
|
||||||
|
|
||||||
const handleReset = () => {
|
const handleReset = () => {
|
||||||
if (window.confirm('Вы уверены, что хотите сбросить все настройки к значениям по умолчанию?')) {
|
if (window.confirm(t('settings-resetConfirm'))) {
|
||||||
resetToDefaults();
|
resetToDefaults();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Обработка нажатий клавиш
|
// Обработка нажатий клавиш
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleKeyPress = (e: React.KeyboardEvent<HTMLElement>) => {
|
const handleKeyPress = (e: KeyboardEvent) => {
|
||||||
if (e.key === 'Enter')
|
if (e.key === 'Enter')
|
||||||
handleSave();
|
handleSave();
|
||||||
if (e.key === 'Escape')
|
if (e.key === 'Escape')
|
||||||
|
|
@ -158,12 +189,13 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
return () => {
|
return () => {
|
||||||
document.removeEventListener('keydown', handleKeyPress);
|
document.removeEventListener('keydown', handleKeyPress);
|
||||||
};
|
};
|
||||||
}, []);
|
}, [handleSave, handleCancel]);
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalContainer style={{ height: '60vh', width: '50vw', minWidth: "800px" }}>
|
<ModalContainer style={{ height: '60vh', width: '50vw', minWidth: "800px" }}>
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h3>Настройки</h3>
|
<h3>{t('settings-title')}</h3>
|
||||||
<CloseBtn>
|
<CloseBtn>
|
||||||
<IconComponent name={buttonNames[12]} width="30" height="30" onClick={handleCancel} />
|
<IconComponent name={buttonNames[12]} width="30" height="30" onClick={handleCancel} />
|
||||||
</CloseBtn>
|
</CloseBtn>
|
||||||
|
|
@ -204,7 +236,7 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Сбросить настройки'
|
title={t('settings-resetSettings')}
|
||||||
onClick={handleReset}
|
onClick={handleReset}
|
||||||
color={theme.offlineColor}
|
color={theme.offlineColor}
|
||||||
hoverColor={theme.borderColor}
|
hoverColor={theme.borderColor}
|
||||||
|
|
@ -213,7 +245,7 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Отменить'
|
title={t('settings-cancel')}
|
||||||
onClick={handleCancel}
|
onClick={handleCancel}
|
||||||
color={theme.offlineColor}
|
color={theme.offlineColor}
|
||||||
hoverColor={theme.borderColor}
|
hoverColor={theme.borderColor}
|
||||||
|
|
@ -221,7 +253,7 @@ export const Settings = ({ onClose }: Props) => {
|
||||||
<MainButton
|
<MainButton
|
||||||
type="button"
|
type="button"
|
||||||
size="small"
|
size="small"
|
||||||
title='Сохранить'
|
title={t('settings-save')}
|
||||||
onClick={handleSave}
|
onClick={handleSave}
|
||||||
/>
|
/>
|
||||||
</span>
|
</span>
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import styled, {useTheme} from "styled-components"
|
||||||
import ThemeButton from "../../../UI/buttons/toggletheme"
|
import ThemeButton from "../../../UI/buttons/toggletheme"
|
||||||
import {buttonNames} from "../../../../const/buttons/names";
|
import {buttonNames} from "../../../../const/buttons/names";
|
||||||
import {IconComponent} from "../../../../const/buttons/icons";
|
import {IconComponent} from "../../../../const/buttons/icons";
|
||||||
|
import {useTranslation} from "../../../../utils/i18n/hooks";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
from: 'reg' | 'helloWindow' | 'workSpace'
|
from: 'reg' | 'helloWindow' | 'workSpace'
|
||||||
|
|
@ -112,7 +113,7 @@ const LinkBlock = styled.a`
|
||||||
|
|
||||||
const Copyright = styled.div`
|
const Copyright = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
text-align: start;
|
text-align: center;
|
||||||
color: ${(props => props.theme.offlineColor)};
|
color: ${(props => props.theme.offlineColor)};
|
||||||
`
|
`
|
||||||
|
|
||||||
|
|
@ -158,6 +159,7 @@ const MainTitle = styled.div`
|
||||||
|
|
||||||
export default function About({ from, onBack }: Props) {
|
export default function About({ from, onBack }: Props) {
|
||||||
const { setWorkModule } = useWorkSpaceStore()
|
const { setWorkModule } = useWorkSpaceStore()
|
||||||
|
const {t} = useTranslation('common')
|
||||||
const getBackHandler = () => {
|
const getBackHandler = () => {
|
||||||
switch (from) {
|
switch (from) {
|
||||||
case 'reg':
|
case 'reg':
|
||||||
|
|
@ -177,11 +179,12 @@ export default function About({ from, onBack }: Props) {
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Content>
|
<Content>
|
||||||
|
|
||||||
<MainTitle>
|
<MainTitle>
|
||||||
<div className={'title'}>
|
<div className={'title'}>
|
||||||
<img src="./assets/images/gossys.png" alt="" />
|
<img src="./assets/images/gossys.png" alt={t('about-logoAlt')} />
|
||||||
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}><h1>О приложении</h1></div>
|
<div style={{width: '100%', display: "flex", justifyContent: 'center'}}>
|
||||||
|
<h1>{t('about-title')}</h1>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className={'version'}>v 0.0.3</p>
|
<p className={'version'}>v 0.0.3</p>
|
||||||
</MainTitle>
|
</MainTitle>
|
||||||
|
|
@ -189,15 +192,15 @@ export default function About({ from, onBack }: Props) {
|
||||||
<AboutLine/>
|
<AboutLine/>
|
||||||
|
|
||||||
<Title>
|
<Title>
|
||||||
<p>ГоNets — минимально работоспособный кроссплатформенный продукт (MVP) для демонстрации нашего подхода к управлению настройками ПК в корпоративной среде.</p>
|
<p>{t('about-description')}</p>
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
<Listing>
|
<Listing>
|
||||||
<p>Возможности:</p>
|
<p>{t('about-featuresTitle')}:</p>
|
||||||
<List>
|
<List>
|
||||||
<li>Визуализация активных сетевых ресурсов</li>
|
<li>{t('about-feature1')}</li>
|
||||||
<li>Настройка параметров локальных и удалённых сетевых ресурсов</li>
|
<li>{t('about-feature2')}</li>
|
||||||
<li>Управление взаимодействием ресурсов с Клиентом ГoNets</li>
|
<li>{t('about-feature3')}</li>
|
||||||
</List>
|
</List>
|
||||||
</Listing>
|
</Listing>
|
||||||
|
|
||||||
|
|
@ -205,26 +208,28 @@ export default function About({ from, onBack }: Props) {
|
||||||
<svg className="icon" viewBox="0 0 24 24">
|
<svg className="icon" viewBox="0 0 24 24">
|
||||||
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
|
<path d="M12 2L1 21h22M12 6l7.53 13H4.47M11 10v4h2v-4m-2 6v2h2v-2"/>
|
||||||
</svg>
|
</svg>
|
||||||
<p>Программное обеспечение исключительно для ознакомления и домашнего использования.</p>
|
<p>{t('about-disclaimer')}</p>
|
||||||
<a href={'https://gossys.dev/pages/projects/gonets'}>Перейти на ГоNets-PRO</a>
|
<a href={'https://gossys.dev/pages/projects/gonets'}>
|
||||||
|
{t('about-goToPro')}
|
||||||
|
</a>
|
||||||
</SubTitle>
|
</SubTitle>
|
||||||
|
|
||||||
<Links>
|
<Links>
|
||||||
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
|
<LinkBlock href={'https://gossys.dev/pages/projects/gonets'}>
|
||||||
Домашняя страницa
|
{t('about-homepage')}
|
||||||
</LinkBlock>
|
</LinkBlock>
|
||||||
|
|
||||||
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
|
<LinkBlock href={'https://wiki.gossys.dev/ru/gonets'}>
|
||||||
Документация
|
{t('about-documentation')}
|
||||||
</LinkBlock>
|
</LinkBlock>
|
||||||
|
|
||||||
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
|
<LinkBlock href={'https://wiki.gossys.dev/ru/lic/GosysPUB'}>
|
||||||
Лицензия
|
{t('about-license')}
|
||||||
</LinkBlock>
|
</LinkBlock>
|
||||||
</Links>
|
</Links>
|
||||||
|
|
||||||
<Copyright>
|
<Copyright>
|
||||||
Разработано и поддерживается ООО «Гибридные Офисные Системы» 2024-2025 г. Все права защищены.
|
{t('about-copyright')}
|
||||||
</Copyright>
|
</Copyright>
|
||||||
|
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
|
|
@ -235,7 +240,6 @@ export default function About({ from, onBack }: Props) {
|
||||||
name={buttonNames[12]}
|
name={buttonNames[12]}
|
||||||
onClick={backHandler}
|
onClick={backHandler}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{from == 'reg' &&
|
{from == 'reg' &&
|
||||||
|
|
@ -243,7 +247,6 @@ export default function About({ from, onBack }: Props) {
|
||||||
}
|
}
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</Content>
|
</Content>
|
||||||
|
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
import { useWorkSpaceStore } from "../../../../store"
|
import { useWorkSpaceStore } from "../../../../store"
|
||||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||||
import { buttonNames } from "../../../../const/buttons/names"
|
import { buttonNames } from "../../../../const/buttons/names"
|
||||||
|
import {useTranslation} from "../../../../utils/i18n/hooks";
|
||||||
|
|
||||||
const Wrapper = styled.div`
|
const Wrapper = styled.div`
|
||||||
width: 100%;
|
width: 100%;
|
||||||
|
|
@ -49,30 +50,33 @@
|
||||||
`
|
`
|
||||||
|
|
||||||
export const HelloWindow = () => {
|
export const HelloWindow = () => {
|
||||||
|
const {t} = useTranslation('common')
|
||||||
const { setWorkModule } = useWorkSpaceStore()
|
const { setWorkModule } = useWorkSpaceStore()
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper>
|
<Wrapper>
|
||||||
<Container className="unselectable">
|
<Container className="unselectable">
|
||||||
<Title>
|
<Title>
|
||||||
<img src="./assets/images/logo.svg" alt="" style={{width: '152px'}}/>
|
<img
|
||||||
|
src="./assets/images/logo.svg"
|
||||||
|
alt={t('main-logoAlt')}
|
||||||
|
style={{width: '152px'}}
|
||||||
|
/>
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
|
|
||||||
<ButtonWrapper>
|
<ButtonWrapper>
|
||||||
<ButtonContainer>
|
<ButtonContainer>
|
||||||
<MainButton
|
<MainButton
|
||||||
title="Настройки сети"
|
title={t('main-networkSettings')}
|
||||||
icon={buttonNames[3]}
|
icon={buttonNames[3]}
|
||||||
iconSize={{ width: '32', height: '32' }}
|
iconSize={{ width: '32', height: '32' }}
|
||||||
onClick={() => setWorkModule(workSpaceNames[3])}
|
onClick={() => setWorkModule(workSpaceNames[3])}
|
||||||
/>
|
/>
|
||||||
<MainButton
|
<MainButton
|
||||||
title="О программе"
|
title={t('main-about')}
|
||||||
icon={buttonNames[4]}
|
icon={buttonNames[4]}
|
||||||
iconSize={{ width: '32', height: '32' }}
|
iconSize={{ width: '32', height: '32' }}
|
||||||
onClick={()=>setWorkModule(workSpaceNames[4])}
|
onClick={() => setWorkModule(workSpaceNames[4])}
|
||||||
/>
|
/>
|
||||||
</ButtonContainer>
|
</ButtonContainer>
|
||||||
</ButtonWrapper>
|
</ButtonWrapper>
|
||||||
|
|
|
||||||
|
|
@ -5,14 +5,14 @@ import {IconComponent} from "../../../../../../const/buttons/icons";
|
||||||
import {buttonNames} from "../../../../../../const/buttons/names";
|
import {buttonNames} from "../../../../../../const/buttons/names";
|
||||||
import {ModalMap} from "../../../../../modalwindows/windowsType";
|
import {ModalMap} from "../../../../../modalwindows/windowsType";
|
||||||
import {useModalContext} from "../../../../../../store";
|
import {useModalContext} from "../../../../../../store";
|
||||||
|
import {useTranslation} from "../../../../../../utils/i18n/hooks";
|
||||||
import Pagination from "../../../../../UI/Pagination";
|
import Pagination from "../../../../../UI/Pagination";
|
||||||
import {
|
import {
|
||||||
useDisabledColumns, useLanguage,
|
useDisabledColumns,
|
||||||
useNameSetting,
|
useNameSetting,
|
||||||
useOrdersColumns,
|
useOrdersColumns,
|
||||||
usePaginateSetting
|
usePaginateSetting
|
||||||
} from "../../../../../../hooks/useSettings";
|
} from "../../../../../../hooks/useSettings";
|
||||||
import {useTranslation} from "../../../../../../utils/i18n/hooks";
|
|
||||||
|
|
||||||
interface TableScannerProps {
|
interface TableScannerProps {
|
||||||
devices: DeviceUser[];
|
devices: DeviceUser[];
|
||||||
|
|
@ -71,16 +71,19 @@ const StatusBadge = styled.span<{ status:string }>`
|
||||||
`
|
`
|
||||||
|
|
||||||
export const TableScanner = ({ devices }: TableScannerProps) => {
|
export const TableScanner = ({ devices }: TableScannerProps) => {
|
||||||
const {t} = useTranslation('common')
|
|
||||||
const { openModal } = useModalContext();
|
const { openModal } = useModalContext();
|
||||||
const nameSetting = useNameSetting();
|
const nameSetting = useNameSetting();
|
||||||
const ordersColumns = useOrdersColumns();
|
const ordersColumns = useOrdersColumns();
|
||||||
const disabledColumns = useDisabledColumns();
|
const disabledColumns = useDisabledColumns();
|
||||||
const paginateSetting = usePaginateSetting();
|
const paginateSetting = usePaginateSetting();
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
|
|
||||||
const [currentPage, setCurrentPage] = useState(1);
|
const [currentPage, setCurrentPage] = useState(1);
|
||||||
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
|
const [devicesPerPage, setDevicesPerPage] = useState(paginateSetting);
|
||||||
|
|
||||||
|
console.log(ordersColumns);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setDevicesPerPage(paginateSetting);
|
setDevicesPerPage(paginateSetting);
|
||||||
}, [paginateSetting]);
|
}, [paginateSetting]);
|
||||||
|
|
@ -98,14 +101,14 @@ export const TableScanner = ({ devices }: TableScannerProps) => {
|
||||||
.filter(column => !disabledColumns.includes(ordersColumns.indexOf(column)))
|
.filter(column => !disabledColumns.includes(ordersColumns.indexOf(column)))
|
||||||
.map(column => {
|
.map(column => {
|
||||||
switch(column) {
|
switch(column) {
|
||||||
case t('tableScanner-online'): return 'status';
|
case 'Онлайн': return 'status';
|
||||||
case t('tableScanner-name'): return 'name';
|
case 'Имя': return 'name';
|
||||||
case t('tableScanner-vendor'): return 'vendor';
|
case 'Производитель': return 'vendor';
|
||||||
case t('tableScanner-ip'): return 'ip';
|
case 'IP адрес': return 'ip';
|
||||||
case t('tableScanner-ports'): return 'ports';
|
case 'Открытые порты': return 'ports';
|
||||||
case t('tableScanner-activity'): return 'activity';
|
case 'Последняя активность': return 'activity';
|
||||||
case t('tableScanner-note'): return 'note';
|
case 'Заметка': return 'note';
|
||||||
case t('tableScanner-action'): return 'action';
|
case 'Действие': return 'action';
|
||||||
default: return '';
|
default: return '';
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
@ -136,7 +139,7 @@ export const TableScanner = ({ devices }: TableScannerProps) => {
|
||||||
case 'status':
|
case 'status':
|
||||||
return <TableCell key="status"><StatusBadge status={item.status} /></TableCell>;
|
return <TableCell key="status"><StatusBadge status={item.status} /></TableCell>;
|
||||||
case 'name':
|
case 'name':
|
||||||
return <TableCell key="name">{item.hostname || (nameSetting === 'ip' ? item.ip : t('tableScanner-unknown'))}</TableCell>;
|
return <TableCell key="name">{item.hostname || (nameSetting === 'ip' ? item.ip : 'Неизвестно')}</TableCell>;
|
||||||
case 'vendor':
|
case 'vendor':
|
||||||
return <TableCell key="vendor">{item.vendor}</TableCell>;
|
return <TableCell key="vendor">{item.vendor}</TableCell>;
|
||||||
case 'ip':
|
case 'ip':
|
||||||
|
|
@ -168,12 +171,12 @@ export const TableScanner = ({ devices }: TableScannerProps) => {
|
||||||
{visibleColumns.map(column => {
|
{visibleColumns.map(column => {
|
||||||
switch (column) {
|
switch (column) {
|
||||||
case 'status': return <TableHeaderCell key="status"></TableHeaderCell>;
|
case 'status': return <TableHeaderCell key="status"></TableHeaderCell>;
|
||||||
case 'name': return <TableHeaderCell key="name">{t('tableScanner-name')}</TableHeaderCell>;
|
case 'name': return <TableHeaderCell key="name">{t('settings-name')}</TableHeaderCell>;
|
||||||
case 'vendor': return <TableHeaderCell key="vendor">{t('tableScanner-vendor')}</TableHeaderCell>;
|
case 'vendor': return <TableHeaderCell key="vendor">{t('settings-vendor')}</TableHeaderCell>;
|
||||||
case 'ip': return <TableHeaderCell key="ip">{t('tableScanner-ip')}</TableHeaderCell>;
|
case 'ip': return <TableHeaderCell key="ip">{t('settings-ipAddress')}</TableHeaderCell>;
|
||||||
case 'ports': return <TableHeaderCell key="ports">{t('tableScanner-ports')}</TableHeaderCell>;
|
case 'ports': return <TableHeaderCell key="ports">{t('settings-openPorts')}</TableHeaderCell>;
|
||||||
case 'activity': return <TableHeaderCell key="activity">{t('tableScanner-activity')}</TableHeaderCell>;
|
case 'activity': return <TableHeaderCell key="activity">{t('settings-lastActivity')}</TableHeaderCell>;
|
||||||
case 'note': return <TableHeaderCell key="note">{t('tableScanner-note')}</TableHeaderCell>;
|
case 'note': return <TableHeaderCell key="note">{t('settings-note')}</TableHeaderCell>;
|
||||||
case 'action': return <TableHeaderCell key="action"></TableHeaderCell>;
|
case 'action': return <TableHeaderCell key="action"></TableHeaderCell>;
|
||||||
default: return null;
|
default: return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import WifiDot, {WifiNetworkScanner} from "./modules/WifiDot";
|
||||||
import ModalWifiScaner from "./modules/ModalWifiScaner";
|
import ModalWifiScaner from "./modules/ModalWifiScaner";
|
||||||
import useNetworkStore from "../../../../../store/useDeviceStore";
|
import useNetworkStore from "../../../../../store/useDeviceStore";
|
||||||
import {useIpStore} from "../../../../../store";
|
import {useIpStore} from "../../../../../store";
|
||||||
|
import {useTranslation} from "../../../../../utils/i18n/hooks";
|
||||||
|
|
||||||
// Анимации
|
// Анимации
|
||||||
const ring1 = keyframes`
|
const ring1 = keyframes`
|
||||||
|
|
@ -143,27 +144,28 @@ export function WifiScanner() {
|
||||||
const [scanInitiated, setScanInitiated] = useState(false);
|
const [scanInitiated, setScanInitiated] = useState(false);
|
||||||
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
const { wifiNetworks, setWifiNetworks } = useNetworkStore();
|
||||||
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
|
const [currentNetwork, setCurrentNetwork] = useState<WifiNetworkScanner | null>(null);
|
||||||
|
const { 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([]);
|
setWifiNetworks([]);
|
||||||
setCurrentNetwork(null)
|
} finally {
|
||||||
setLoading(true);
|
setLoading(false);
|
||||||
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);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
<Wrapper scanInitiated={scanInitiated} loading={loading}>
|
||||||
<TitleWrapper>Сканирование точек доступа Wi-Fi сети</TitleWrapper>
|
<TitleWrapper>{t('wifiScanner-title')}</TitleWrapper>
|
||||||
|
|
||||||
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
<UserLocate loading={loading} scanInitiated={scanInitiated}>
|
||||||
<span className="center circel"></span>
|
<span className="center circel"></span>
|
||||||
|
|
@ -190,11 +192,15 @@ export function WifiScanner() {
|
||||||
|
|
||||||
{loading ? (
|
{loading ? (
|
||||||
<LoadingText>
|
<LoadingText>
|
||||||
Сканирование точек доступа <ScanningText/>
|
{t('wifiScanner-scanning')} <ScanningText/>
|
||||||
</LoadingText>
|
</LoadingText>
|
||||||
) : (
|
) : (
|
||||||
<ScanBtn onClick={handleScan} disabled={loading} scanInitiated={wifiNetworks.length > 0 }>
|
<ScanBtn
|
||||||
{wifiNetworks.length > 0 ? 'Обновить список' : 'Сканировать точки доступа'}
|
onClick={handleScan}
|
||||||
|
disabled={loading}
|
||||||
|
scanInitiated={wifiNetworks.length > 0}
|
||||||
|
>
|
||||||
|
{wifiNetworks.length > 0 ? t('wifiScanner-refresh') : t('wifiScanner-scan')}
|
||||||
</ScanBtn>
|
</ScanBtn>
|
||||||
)}
|
)}
|
||||||
</Wrapper>
|
</Wrapper>
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,7 @@ import SignalSVG from "../../../../../UI/signalSvg";
|
||||||
import { useTheme } from 'styled-components';
|
import { useTheme } from 'styled-components';
|
||||||
import useNetworkStore, {DeviceUser} from "../../../../../../store/useDeviceStore";
|
import useNetworkStore, {DeviceUser} from "../../../../../../store/useDeviceStore";
|
||||||
import {NetworkService} from "../../../../../../api/request";
|
import {NetworkService} from "../../../../../../api/request";
|
||||||
|
import {useTranslation} from "../../../../../../utils/i18n/hooks";
|
||||||
|
|
||||||
const Modal = styled.div`
|
const Modal = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -114,6 +115,7 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
||||||
const [signal, setSignal] = useState(1);
|
const [signal, setSignal] = useState(1);
|
||||||
const { wifiNetworks } = useNetworkStore();
|
const { wifiNetworks } = useNetworkStore();
|
||||||
const [isConnected, setIsConnected] = useState(false);
|
const [isConnected, setIsConnected] = useState(false);
|
||||||
|
const { t } = useTranslation('common');
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const getStatus = async () => {
|
const getStatus = async () => {
|
||||||
|
|
@ -174,10 +176,9 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
||||||
<ModalTitle>
|
<ModalTitle>
|
||||||
<h4>{network.ssid}</h4>
|
<h4>{network.ssid}</h4>
|
||||||
<div className={'functionalIcons'}>
|
<div className={'functionalIcons'}>
|
||||||
|
<span onClick={openModalNote} style={{cursor: "pointer"}}>
|
||||||
<span onClick={openModalNote} style={{cursor: "pointer"}}>
|
<IconComponent name={buttonNames[16]} width="30" height="30" />
|
||||||
<IconComponent name={buttonNames[16]} width="30" height="30" />
|
</span>
|
||||||
</span>
|
|
||||||
<Status>
|
<Status>
|
||||||
<SignalSVG signal={signal} size={'36px'} color={theme.accent} />
|
<SignalSVG signal={signal} size={'36px'} color={theme.accent} />
|
||||||
</Status>
|
</Status>
|
||||||
|
|
@ -185,40 +186,42 @@ const ModalWifiScaner = ({ network,onClose }: { network: WifiNetworkScanner, onC
|
||||||
</ModalTitle>
|
</ModalTitle>
|
||||||
<ModalContent>
|
<ModalContent>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>BSSID:</h6>
|
<h6>{t('wifiModal-bssid')}:</h6>
|
||||||
<p>{network.bssid}</p>
|
<p>{network.bssid}</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Производитель:</h6>
|
<h6>{t('wifiModal-vendor')}:</h6>
|
||||||
<p>{network.vendor}</p>
|
<p>{network.vendor || t('wifiModal-unknown')}</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Сигнал:</h6>
|
<h6>{t('wifiModal-signal')}:</h6>
|
||||||
<p>{network.signal} dBm</p>
|
<p>{network.signal} dBm</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Канал:</h6>
|
<h6>{t('wifiModal-channel')}:</h6>
|
||||||
<p>{network.channel}</p>
|
<p>{network.channel}</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Шифрование:</h6>
|
<h6>{t('wifiModal-encryption')}:</h6>
|
||||||
<p>{network.encryption}</p>
|
<p>{network.encryption || t('wifiModal-none')}</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Частота:</h6>
|
<h6>{t('wifiModal-frequency')}:</h6>
|
||||||
<p>{network.frequency}</p>
|
<p>{network.frequency} MHz</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Скрытая сеть:</h6>
|
<h6>{t('wifiModal-hidden')}:</h6>
|
||||||
<p>{network.hidden ? 'Да' : 'Нет'}</p>
|
<p>{network.hidden ? t('common-yes') : t('common-no')}</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalRow>
|
<ModalRow>
|
||||||
<h6>Заметка:</h6>
|
<h6>{t('wifiModal-note')}:</h6>
|
||||||
<p>{currentNetwork.note ? currentNetwork.note : '-'}</p>
|
<p>{currentNetwork.note || '-'}</p>
|
||||||
</ModalRow>
|
</ModalRow>
|
||||||
<ModalConnect onClick={openModalNetwork}>{isConnected ? 'Отключиться' : 'Подключиться'}</ModalConnect>
|
<ModalConnect onClick={openModalNetwork}>
|
||||||
</ModalContent>
|
{isConnected ? t('wifiModal-disconnect') : t('wifiModal-connect')}
|
||||||
</Modal>
|
</ModalConnect>
|
||||||
|
</ModalContent>
|
||||||
|
</Modal>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@ import { useWorkSpaceStore } from "../../../../store"
|
||||||
import { workSpaceNames } from "../../../../const/workspace/names"
|
import { workSpaceNames } from "../../../../const/workspace/names"
|
||||||
import { IconComponent } from "../../../../const/buttons/icons"
|
import { IconComponent } from "../../../../const/buttons/icons"
|
||||||
import { buttonNames } from "../../../../const/buttons/names"
|
import { buttonNames } from "../../../../const/buttons/names"
|
||||||
|
import {useTranslation} from "../../../../utils/i18n/hooks";
|
||||||
|
|
||||||
const Container = styled.div`
|
const Container = styled.div`
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|
@ -102,11 +103,12 @@ interface CurrentDevice {
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DevicesBar = () => {
|
export const DevicesBar = () => {
|
||||||
const { openModal } = useModalContext();
|
const {openModal} = useModalContext();
|
||||||
const { setIpAddress, ipAddress } = useIpStore();
|
const {setIpAddress, ipAddress} = useIpStore();
|
||||||
const { devices, addDevice, deleteDevice } = useDevicesStore()
|
const {devices, addDevice, deleteDevice} = useDevicesStore()
|
||||||
const { isOpen, collapseMenu } = useMenu(menusNames[1]);
|
const {isOpen, collapseMenu} = useMenu(menusNames[1]);
|
||||||
const { setWorkModule } = useWorkSpaceStore()
|
const {setWorkModule} = useWorkSpaceStore()
|
||||||
|
const {t} = useTranslation('common');
|
||||||
|
|
||||||
const [currentDevice, setCurrentDevice] = useState<CurrentDevice>({
|
const [currentDevice, setCurrentDevice] = useState<CurrentDevice>({
|
||||||
address: 'localhost',
|
address: 'localhost',
|
||||||
|
|
@ -163,8 +165,8 @@ export const DevicesBar = () => {
|
||||||
const handleDelete = (number: number, adress: string) => {
|
const handleDelete = (number: number, adress: string) => {
|
||||||
deleteDevice(number)
|
deleteDevice(number)
|
||||||
|
|
||||||
|
|
||||||
if(adress == ipAddress){
|
if (adress == ipAddress) {
|
||||||
setIpAddress('localhost')
|
setIpAddress('localhost')
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
@ -173,7 +175,7 @@ export const DevicesBar = () => {
|
||||||
const isAvailable = await checkWebSocketAvailability(ip);
|
const isAvailable = await checkWebSocketAvailability(ip);
|
||||||
if (!isAvailable) {
|
if (!isAvailable) {
|
||||||
openModal(ModalMap['3'], {
|
openModal(ModalMap['3'], {
|
||||||
message: `Не удалось подключиться к серверу по адресу ${ip}`,
|
message: t('nodes-connectionError') + ip,
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -191,44 +193,46 @@ export const DevicesBar = () => {
|
||||||
return (
|
return (
|
||||||
<Container>
|
<Container>
|
||||||
<Title>
|
<Title>
|
||||||
<p className="unselectable">Доступные узлы</p>
|
<p className="unselectable">{t('nodes-availableNodes')}</p>
|
||||||
</Title>
|
</Title>
|
||||||
|
|
||||||
<DeviceBlock>
|
<DeviceBlock>
|
||||||
<DeviceControl className="unselectable" onClick={() => switchWs(currentDevice.address)}>
|
<DeviceControl className="unselectable" onClick={() => switchWs(currentDevice.address)}>
|
||||||
<p>Корневой адрес</p>
|
<p>{t('nodes-rootAddress')}</p>
|
||||||
</DeviceControl>
|
</DeviceControl>
|
||||||
{currentDevice.hostname == '' ? (
|
{currentDevice.hostname == '' ? (
|
||||||
<Text className="paddingP">Название: Загрузка<AnimDot /></Text>
|
<Text className="paddingP">
|
||||||
|
{t('nodes-name')}: {t('nodes-loading')}<AnimDot/>
|
||||||
|
</Text>
|
||||||
) : (
|
) : (
|
||||||
<p className="paddingP">Название: {currentDevice.hostname}</p>
|
<p className="paddingP">{t('nodes-name')}: {currentDevice.hostname}</p>
|
||||||
)}
|
)}
|
||||||
</DeviceBlock>
|
</DeviceBlock>
|
||||||
|
|
||||||
<DevicesContainer>
|
<DevicesContainer>
|
||||||
<DevicesHeader className="unselectable" onClick={collapseMenu}>
|
<DevicesHeader className="unselectable" onClick={collapseMenu}>
|
||||||
<p>Дополнительные узлы</p>
|
<p>{t('nodes-additionalNodes')}</p>
|
||||||
<IconComponent name={isOpen ? buttonNames[5] : buttonNames[6]} />
|
<IconComponent name={isOpen ? buttonNames[5] : buttonNames[6]}/>
|
||||||
</DevicesHeader>
|
</DevicesHeader>
|
||||||
|
|
||||||
<DevicesContent $isactive={isOpen.toString()} >
|
<DevicesContent $isactive={isOpen.toString()}>
|
||||||
{devices.length > 0 &&
|
{devices.length > 0 &&
|
||||||
devices.map((device, index) => (
|
devices.map((device, index) => (
|
||||||
<DeviceBlock key={index}>
|
<DeviceBlock key={index}>
|
||||||
<DeviceControl className="unselectable" onClick={() => switchWs(device.address)}>
|
<DeviceControl className="unselectable" onClick={() => switchWs(device.address)}>
|
||||||
<p>Узел {device.number}</p>
|
<p>{t('nodes-node')} {device.number}</p>
|
||||||
<IconComponent
|
<IconComponent
|
||||||
width="20"
|
width="20"
|
||||||
height="20"
|
height="20"
|
||||||
name={buttonNames[2]}
|
name={buttonNames[2]}
|
||||||
setColor="white"
|
setColor="white"
|
||||||
onClick={(e: any) => {
|
onClick={(e: any) => {
|
||||||
e.stopPropagation(); // Предотвращаем всплытие события
|
e.stopPropagation();
|
||||||
handleDelete(device.number, device.address);
|
handleDelete(device.number, device.address);
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</DeviceControl>
|
</DeviceControl>
|
||||||
<p className="paddingP">Адрес: {device.address}</p>
|
<p className="paddingP">{t('nodes-address')}: {device.address}</p>
|
||||||
</DeviceBlock>
|
</DeviceBlock>
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
@ -237,7 +241,6 @@ export const DevicesBar = () => {
|
||||||
+
|
+
|
||||||
</PlusBlock>
|
</PlusBlock>
|
||||||
</DevicesContent>
|
</DevicesContent>
|
||||||
|
|
||||||
</DevicesContainer>
|
</DevicesContainer>
|
||||||
</Container>
|
</Container>
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,25 +1,30 @@
|
||||||
export const appearanceSettings = [
|
|
||||||
{
|
const createAppearanceSettings = () => {
|
||||||
id: 'theme',
|
return [
|
||||||
sectionId: 'Внешний вид',
|
{
|
||||||
label: 'Цветовая тема',
|
id: 'theme',
|
||||||
type: 'select',
|
sectionId: 'settings-sections-appearance',
|
||||||
defaultValue: 'light',
|
label: 'settings-colorTheme',
|
||||||
options: [
|
type: 'select',
|
||||||
{ value: 'light', label: 'Светлая' },
|
defaultValue: 'light',
|
||||||
{ value: 'dark', label: 'Тёмная' },
|
options: [
|
||||||
{ value: 'system', label: 'Как в системе' }
|
{ value: 'light', label: 'settings-light' },
|
||||||
],
|
{ value: 'dark', label: 'settings-dark' },
|
||||||
},
|
{ value: 'system', label: 'settings-system' }
|
||||||
{
|
],
|
||||||
id: 'language',
|
},
|
||||||
sectionId: 'Внешний вид',
|
{
|
||||||
label: 'Язык интерфейса',
|
id: 'language',
|
||||||
type: 'select',
|
sectionId: 'settings-sections-appearance',
|
||||||
defaultValue: 'ru',
|
label: 'settings-interfaceLanguage',
|
||||||
options: [
|
type: 'select',
|
||||||
{ value: 'ru', label: 'Русский' },
|
defaultValue: 'ru',
|
||||||
{ value: 'en', label: 'Английский' },
|
options: [
|
||||||
],
|
{ value: 'ru', label: 'settings-russian' },
|
||||||
},
|
{ value: 'en', label: 'settings-english' },
|
||||||
] as const;
|
],
|
||||||
|
},
|
||||||
|
] as const;
|
||||||
|
};
|
||||||
|
|
||||||
|
export { createAppearanceSettings };
|
||||||
|
|
@ -8,30 +8,30 @@ const createNetworkSettings = () => {
|
||||||
{
|
{
|
||||||
id: 'clean-network',
|
id: 'clean-network',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Очистить данные сессии',
|
label: 'settings-clearSessionData',
|
||||||
sectionId: 'Точки доступа',
|
sectionId: 'settings-sections-accessPoints',
|
||||||
defaultValue: 'Очистить',
|
defaultValue: 'settings-clear',
|
||||||
action: async ()=>{
|
action: async ()=>{
|
||||||
try {
|
try {
|
||||||
const res = await NetworkService.clearNetwork(ipAddress);
|
const res = await NetworkService.clearNetwork(ipAddress);
|
||||||
console.log(res);
|
console.log(res);
|
||||||
}catch (e){
|
}catch (e){
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert('Данные не были удалены: '+ e)
|
alert('settings-dataNotDeleted' + e)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'export-network',
|
id: 'export-network',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Экспорт данных точек доступа',
|
label: 'settings-exportNetworkData',
|
||||||
sectionId: 'Точки доступа',
|
sectionId: 'settings-sections-accessPoints',
|
||||||
defaultValue: 'Экспортировать',
|
defaultValue: 'settings-export',
|
||||||
action: async () => {
|
action: async () => {
|
||||||
try {
|
try {
|
||||||
const response = await NetworkService.exportNetwork(ipAddress);
|
const response = await NetworkService.exportNetwork(ipAddress);
|
||||||
const { filePath } = await window.electronAPI.showSaveDialog({
|
const { filePath } = await window.electronAPI.showSaveDialog({
|
||||||
title: "Сохранить данные сетей",
|
title: 'settings-saveNetworksTitle',
|
||||||
defaultPath: "network_export.json",
|
defaultPath: "network_export.json",
|
||||||
filters: [{ name: "JSON", extensions: ["json"] }],
|
filters: [{ name: "JSON", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
|
|
@ -41,10 +41,10 @@ const createNetworkSettings = () => {
|
||||||
filePath,
|
filePath,
|
||||||
JSON.stringify(response.data, null, 2)
|
JSON.stringify(response.data, null, 2)
|
||||||
);
|
);
|
||||||
alert("Данные успешно экспортированы!");
|
alert('settings-exportSuccess');
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
alert("Ошибка экспорта: " + e.message);
|
alert('settings-exportError' + e.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
@ -52,4 +52,4 @@ const createNetworkSettings = () => {
|
||||||
] as const;
|
] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const networkSettings = createNetworkSettings();
|
export { createNetworkSettings };
|
||||||
|
|
@ -1,80 +1,79 @@
|
||||||
import {useIpStore} from "../../ipStore";
|
import {useIpStore} from "../../ipStore";
|
||||||
import {NetworkService} from "../../../api/request";
|
import {NetworkService} from "../../../api/request";
|
||||||
|
|
||||||
|
|
||||||
const createNodesSettings = () => {
|
const createNodesSettings = () => {
|
||||||
const { ipAddress } = useIpStore.getState();
|
const { ipAddress } = useIpStore.getState();
|
||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
id: 'rescan-nodes',
|
id: 'rescan-nodes',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
label: 'Интервальное сканирование',
|
label: 'settings-intervalScanning',
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
defaultValue: false,
|
defaultValue: false,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'interval-nodes',
|
id: 'interval-nodes',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
label: 'Интервал сканирования узлов (мин)',
|
label: 'settings-scanInterval',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
defaultValue: 60,
|
defaultValue: 60,
|
||||||
min: 1,
|
min: 1,
|
||||||
max: 2880
|
max: 2880
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'netnames-nodes',
|
id: 'netnames-nodes',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
label: 'Отображаемые имена при отсутствии',
|
label: 'settings-displayNames',
|
||||||
type: 'select',
|
type: 'select',
|
||||||
defaultValue: 'ip',
|
defaultValue: 'ip',
|
||||||
options: [
|
options: [
|
||||||
{ value: 'ip', label: 'IP адрес' },
|
{ value: 'ip', label: 'settings-ipAddress' },
|
||||||
{ value: 'unknown', label: 'Неизвестно' },
|
{ value: 'unknown', label: 'settings-unknown' },
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'paginate-nodes',
|
id: 'paginate-nodes',
|
||||||
type: 'number',
|
type: 'number',
|
||||||
label: 'Количество строк в таблице',
|
label: 'settings-rowsPerPage',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
defaultValue: 20,
|
defaultValue: 20,
|
||||||
min: 1,
|
min: 1,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'customize-nodes',
|
id: 'customize-nodes',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Отображение узлов',
|
label: 'settings-displaySettings',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
defaultValue: 'Настроить',
|
defaultValue: 'settings-configure',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'clean-nodes',
|
id: 'clean-nodes',
|
||||||
type: 'button',
|
type: 'button',
|
||||||
label: 'Очистить данные сессии',
|
label: 'settings-clearSessionData',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
defaultValue: 'Очистить',
|
defaultValue: 'settings-clear',
|
||||||
action: async ()=>{
|
action: async ()=>{
|
||||||
try {
|
try {
|
||||||
const res = await NetworkService.clearSubnet(ipAddress);
|
const res = await NetworkService.clearSubnet(ipAddress);
|
||||||
console.log(res)
|
console.log(res)
|
||||||
}catch (e){
|
}catch (e){
|
||||||
console.error(e);
|
console.error(e);
|
||||||
alert('Данные не были удалены: '+ e)
|
alert('settings-dataNotDeleted' + e)
|
||||||
}
|
}
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
|
||||||
{
|
{
|
||||||
id: "export-nodes",
|
id: "export-nodes",
|
||||||
type: "button",
|
type: "button",
|
||||||
label: "Экспорт данных узлов",
|
label: "settings-exportData",
|
||||||
sectionId: "Узлы подсети",
|
sectionId: "settings-sections-subnetNodes",
|
||||||
defaultValue: "Экспортировать",
|
defaultValue: "settings-export",
|
||||||
action: async () => {
|
action: async () => {
|
||||||
try {
|
try {
|
||||||
const response = await NetworkService.exportDevices(ipAddress);
|
const response = await NetworkService.exportDevices(ipAddress);
|
||||||
const { filePath } = await window.electronAPI.showSaveDialog({
|
const { filePath } = await window.electronAPI.showSaveDialog({
|
||||||
title: "Сохранить данные узлов",
|
title: "settings-saveNodesTitle",
|
||||||
defaultPath: "nodes_export.json",
|
defaultPath: "nodes_export.json",
|
||||||
filters: [{ name: "JSON", extensions: ["json"] }],
|
filters: [{ name: "JSON", extensions: ["json"] }],
|
||||||
});
|
});
|
||||||
|
|
@ -84,10 +83,10 @@ const createNodesSettings = () => {
|
||||||
filePath,
|
filePath,
|
||||||
JSON.stringify(response.data, null, 2)
|
JSON.stringify(response.data, null, 2)
|
||||||
);
|
);
|
||||||
alert("Данные успешно экспортированы!");
|
alert("settings-exportSuccess");
|
||||||
}
|
}
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
alert("Ошибка экспорта: " + e.message);
|
alert("settings-exportError" + e.message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
@ -95,26 +94,26 @@ const createNodesSettings = () => {
|
||||||
id: 'table-columns-order',
|
id: 'table-columns-order',
|
||||||
type: 'hidden',
|
type: 'hidden',
|
||||||
label: '',
|
label: '',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
defaultValue: [
|
defaultValue: [
|
||||||
'Онлайн',
|
'settings-status',
|
||||||
'Имя',
|
'settings-name',
|
||||||
'Производитель',
|
'settings-vendor',
|
||||||
'IP адрес',
|
'settings-ipAddress',
|
||||||
'Открытые порты',
|
'settings-openPorts',
|
||||||
'Последняя активность',
|
'settings-lastActivity',
|
||||||
'Заметка',
|
'settings-note',
|
||||||
'Действие'
|
'settings-action'
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'table-columns-disabled',
|
id: 'table-columns-disabled',
|
||||||
label: '',
|
label: '',
|
||||||
sectionId: 'Узлы подсети',
|
sectionId: 'settings-sections-subnetNodes',
|
||||||
type: 'hidden',
|
type: 'hidden',
|
||||||
defaultValue: []
|
defaultValue: []
|
||||||
}
|
}
|
||||||
] as const;
|
] as const;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const nodesSettings = createNodesSettings();
|
export { createNodesSettings };
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
import { create } from 'zustand';
|
import { create } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
import {nodesSettings} from "./config/nodesSettings";
|
import {createNodesSettings} from "./config/nodesSettings";
|
||||||
import { networkSettings } from './config/networkSettings';
|
import { createNetworkSettings } from './config/networkSettings';
|
||||||
import {appearanceSettings} from "./config/appearanceSettings";
|
import {createAppearanceSettings} from "./config/appearanceSettings";
|
||||||
|
|
||||||
type SettingValue = any;
|
type SettingValue = any;
|
||||||
type SettingsDictionary = Record<string, SettingValue>;
|
type SettingsDictionary = Record<string, SettingValue>;
|
||||||
|
|
@ -36,18 +36,13 @@ type SettingsState = {
|
||||||
resetToDefaults: () => void;
|
resetToDefaults: () => void;
|
||||||
getSettingValue: <T = any>(id: string) => T;
|
getSettingValue: <T = any>(id: string) => T;
|
||||||
getNumberSetting: (id: string, fallback?: number) => number;
|
getNumberSetting: (id: string, fallback?: number) => number;
|
||||||
|
updateSettingsConfig: () => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultSettings: Setting[] = [
|
|
||||||
...nodesSettings,
|
|
||||||
...networkSettings,
|
|
||||||
...appearanceSettings,
|
|
||||||
];
|
|
||||||
|
|
||||||
export const useSettingsStore = create<SettingsState>()(
|
export const useSettingsStore = create<SettingsState>()(
|
||||||
persist(
|
persist(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
settingsConfig: defaultSettings,
|
settingsConfig: [],
|
||||||
settingsValues: {},
|
settingsValues: {},
|
||||||
activeSection: null,
|
activeSection: null,
|
||||||
pendingChanges: {},
|
pendingChanges: {},
|
||||||
|
|
@ -90,6 +85,15 @@ export const useSettingsStore = create<SettingsState>()(
|
||||||
set({ settingsValues: newValues });
|
set({ settingsValues: newValues });
|
||||||
},
|
},
|
||||||
|
|
||||||
|
updateSettingsConfig: () => {
|
||||||
|
const defaultSettings: Setting[] = [
|
||||||
|
...createNodesSettings(),
|
||||||
|
...createNetworkSettings(),
|
||||||
|
...createAppearanceSettings(),
|
||||||
|
];
|
||||||
|
set({ settingsConfig: defaultSettings });
|
||||||
|
},
|
||||||
|
|
||||||
getSetting: (id) => {
|
getSetting: (id) => {
|
||||||
const state = get();
|
const state = get();
|
||||||
const setting = state.settingsConfig.find(s => s.id === id);
|
const setting = state.settingsConfig.find(s => s.id === id);
|
||||||
|
|
|
||||||
|
|
@ -18,5 +18,5 @@ app.include_router(router)
|
||||||
network_service = NetworkService()
|
network_service = NetworkService()
|
||||||
|
|
||||||
if __name__ == "__main__":
|
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, 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)
|
||||||
|
|
|
||||||
284
SRV/storage/devices/devices.json
Normal file
284
SRV/storage/devices/devices.json
Normal file
|
|
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue