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