From 1c6126987749c7a46945ebe3bec90fcdc9a3836b Mon Sep 17 00:00:00 2001 From: Andy Oknen Date: Fri, 15 Aug 2025 19:22:07 +0000 Subject: [PATCH] Refactor configuration editor to always display line numbers and update UI elements for better user experience. Replace toggle functionality with a fixed display of line numbers. Enhance notification messages and confirmation dialogs with translation support for improved localization. --- src/webui/static/config-editor.js | 99 ++++++++++++++++++----------- src/webui/static/config.js | 44 ++++++------- src/webui/static/lang/en.js | 26 +++++++- src/webui/static/lang/ru.js | 26 +++++++- src/webui/static/style.css | 101 ++++++++++++++---------------- 5 files changed, 179 insertions(+), 117 deletions(-) diff --git a/src/webui/static/config-editor.js b/src/webui/static/config-editor.js index d0f69468..0ed612b3 100644 --- a/src/webui/static/config-editor.js +++ b/src/webui/static/config-editor.js @@ -15,6 +15,12 @@ function initJSONEditor() { // Update line numbers updateLineNumbers(); + // Always enable line numbers since toggle was removed + const editorWrapper = document.querySelector('.editor-wrapper'); + if (editorWrapper) { + editorWrapper.classList.add('with-line-numbers'); + } + // Add event listeners textarea.addEventListener('input', function() { updateLineNumbers(); @@ -58,20 +64,7 @@ function syncLineNumbers() { lineNumbersContainer.scrollTop = textarea.scrollTop; } -// Toggle line numbers visibility -function toggleLineNumbers() { - const checkbox = document.getElementById('line-numbers'); - const lineNumbersContainer = document.getElementById('line-numbers-container'); - const editorWrapper = document.querySelector('.editor-wrapper'); - - if (checkbox.checked) { - lineNumbersContainer.style.display = 'block'; - editorWrapper.classList.add('with-line-numbers'); - } else { - lineNumbersContainer.style.display = 'none'; - editorWrapper.classList.remove('with-line-numbers'); - } -} + // Handle special editor keydown events function handleEditorKeydown(event) { @@ -150,13 +143,13 @@ function updateEditorStatus() { try { if (textarea.value.trim() === '') { - statusElement.textContent = 'Пустая конфигурация'; + statusElement.innerHTML = 'Пустая конфигурация'; statusElement.className = 'status-text warning'; return; } JSON.parse(textarea.value); - statusElement.textContent = 'Валидный JSON'; + statusElement.innerHTML = ''; statusElement.className = 'status-text success'; } catch (error) { statusElement.textContent = `Ошибка JSON: ${error.message}`; @@ -176,7 +169,7 @@ function updateCursorPosition() { const line = beforeCursor.split('\n').length; const column = beforeCursor.length - beforeCursor.lastIndexOf('\n'); - cursorElement.textContent = `Строка ${line}, Столбец ${column}`; + cursorElement.innerHTML = `Строка ${line}, Столбец ${column}`; } // Format JSON with proper indentation @@ -191,7 +184,10 @@ function formatJSON() { textarea.value = formatted; updateLineNumbers(); updateEditorStatus(); - showNotification('JSON отформатирован', 'success'); + const formattedMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['json_formatted'] + ? window.translations[currentLanguage]['json_formatted'] + : 'JSON отформатирован'; + showNotification(formattedMessage, 'success'); } catch (error) { showNotification(`Ошибка форматирования: ${error.message}`, 'error'); } @@ -205,7 +201,10 @@ function validateJSON() { try { JSON.parse(textarea.value); - showNotification('JSON конфигурация валидна', 'success'); + const validationMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['json_validation_success'] + ? window.translations[currentLanguage]['json_validation_success'] + : 'JSON конфигурация валидна'; + showNotification(validationMessage, 'success'); } catch (error) { showNotification(`Ошибка валидации JSON: ${error.message}`, 'error'); } @@ -246,9 +245,15 @@ async function saveConfiguration(restart = false) { if (result.success) { currentConfigJSON = textarea.value; if (restart) { - showNotification('Конфигурация сохранена. Сервер перезапускается...', 'success'); + const restartMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['config_saved_restarting'] + ? window.translations[currentLanguage]['config_saved_restarting'] + : 'Конфигурация сохранена. Сервер перезапускается...'; + showNotification(restartMessage, 'success'); } else { - showNotification('Конфигурация сохранена успешно', 'success'); + const successMessage = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['config_saved_success'] + ? window.translations[currentLanguage]['config_saved_success'] + : 'Конфигурация сохранена успешно'; + showNotification(successMessage, 'success'); } onConfigChange(); // Update button states } else { @@ -262,11 +267,22 @@ async function saveConfiguration(restart = false) { // Save configuration and restart server async function saveAndRestartConfiguration() { - const confirmation = confirm('Сохранить конфигурацию и перезапустить сервер?\n\nВнимание: Соединение будет прервано на время перезапуска.'); + const title = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['save_and_restart_title'] + ? window.translations[currentLanguage]['save_and_restart_title'] + : 'Сохранить и перезапустить'; - if (confirmation) { - await saveConfiguration(true); - } + const message = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['save_and_restart_message'] + ? window.translations[currentLanguage]['save_and_restart_message'] + : 'Сохранить конфигурацию и перезапустить сервер?\n\nВнимание: Соединение будет прервано на время перезапуска.'; + + showConfirmModal({ + title: title, + message: message, + type: 'danger', + onConfirm: async () => { + await saveConfiguration(true); + } + }); } // Refresh configuration from server @@ -275,18 +291,31 @@ async function refreshConfiguration() { // Check if there are unsaved changes if (textarea && textarea.value !== currentConfigJSON) { - const confirmation = confirm('У вас есть несохраненные изменения. Продолжить обновление?'); - if (!confirmation) { - return; - } + const title = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['refresh_unsaved_changes_title'] + ? window.translations[currentLanguage]['refresh_unsaved_changes_title'] + : 'Несохраненные изменения'; + + const message = window.translations && window.translations[currentLanguage] && window.translations[currentLanguage]['refresh_unsaved_changes_message'] + ? window.translations[currentLanguage]['refresh_unsaved_changes_message'] + : 'У вас есть несохраненные изменения. Продолжить обновление?'; + + showConfirmModal({ + title: title, + message: message, + type: 'warning', + onConfirm: async () => { + try { + await loadConfiguration(); + showNotification('Конфигурация обновлена', 'success'); + } catch (error) { + showNotification('Ошибка обновления конфигурации', 'error'); + } + } + }); + return; } - try { - await loadConfiguration(); - showNotification('Конфигурация обновлена', 'success'); - } catch (error) { - showNotification('Ошибка обновления конфигурации', 'error'); - } + } // Update configuration status diff --git a/src/webui/static/config.js b/src/webui/static/config.js index 575c200c..9183037d 100644 --- a/src/webui/static/config.js +++ b/src/webui/static/config.js @@ -55,29 +55,10 @@ function renderConfigEditor() { ${configMeta.path} ${configMeta.format.toUpperCase()} - ${configMeta.isWritable ? '✏️ Редактируемый' : '🔒 Только чтение'} + ${configMeta.isWritable ? '✏️ Редактируемый' : '🔒 Только чтение'} -
- - - - ${configMeta.isWritable ? ` - - - ` : ''} -
@@ -85,10 +66,25 @@ function renderConfigEditor() {
JSON Конфигурация
- - - - +
+
+ Обновить +
+
+ Форматировать +
+
+ Проверить +
+ ${configMeta.isWritable ? ` +
+ Сохранить +
+
+ Сохранить и перезапустить +
+ ` : ''} +
diff --git a/src/webui/static/lang/en.js b/src/webui/static/lang/en.js index 6b7f2a31..41d65886 100644 --- a/src/webui/static/lang/en.js +++ b/src/webui/static/lang/en.js @@ -108,7 +108,7 @@ window.translations.en = { 'format': 'Format', 'validate': 'Validate', 'json_configuration': 'JSON Configuration', - 'line_numbers': 'Line Numbers', + 'config_save_success': 'Configuration saved successfully', 'config_save_error': 'Error saving configuration', 'config_load_error': 'Error loading configuration', @@ -116,5 +116,27 @@ window.translations.en = { 'config_save_confirm_title': 'Confirm Save', 'config_save_confirm_text': 'Are you sure you want to save changes to the configuration file?', 'config_backup_info': 'Backup will be created automatically', - 'config_warning': '⚠️ Warning: Incorrect configuration may cause node failure!' + 'config_warning': '⚠️ Warning: Incorrect configuration may cause node failure!', + + // Editor status translations + 'editable': 'Editable', + 'readonly': 'Read Only', + 'empty_config': 'Empty Configuration', + 'valid_json': 'Valid JSON', + 'line': 'Line', + 'column': 'Column', + + // Configuration save messages + 'config_saved_restarting': 'Configuration saved. Server is restarting...', + 'config_saved_success': 'Configuration saved successfully', + + // Validation messages + 'json_validation_success': 'JSON configuration is valid', + 'json_formatted': 'JSON formatted', + + // Confirmation dialogs + 'save_and_restart_title': 'Save and Restart', + 'save_and_restart_message': 'Save configuration and restart server?\n\nWarning: Connection will be interrupted during restart.', + 'refresh_unsaved_changes_title': 'Unsaved Changes', + 'refresh_unsaved_changes_message': 'You have unsaved changes. Continue refreshing?' }; \ No newline at end of file diff --git a/src/webui/static/lang/ru.js b/src/webui/static/lang/ru.js index b4b0d3c5..e9e0ed2f 100644 --- a/src/webui/static/lang/ru.js +++ b/src/webui/static/lang/ru.js @@ -108,7 +108,7 @@ window.translations.ru = { 'format': 'Форматировать', 'validate': 'Проверить', 'json_configuration': 'JSON Конфигурация', - 'line_numbers': 'Номера строк', + 'config_save_success': 'Конфигурация сохранена успешно', 'config_save_error': 'Ошибка сохранения конфигурации', 'config_load_error': 'Ошибка загрузки конфигурации', @@ -116,5 +116,27 @@ window.translations.ru = { 'config_save_confirm_title': 'Подтверждение сохранения', 'config_save_confirm_text': 'Вы уверены, что хотите сохранить изменения в конфигурационный файл?', 'config_backup_info': 'Резервная копия будет создана автоматически', - 'config_warning': '⚠️ Внимание: Неправильная конфигурация может привести к сбою работы узла!' + 'config_warning': '⚠️ Внимание: Неправильная конфигурация может привести к сбою работы узла!', + + // Editor status translations + 'editable': 'Редактируемый', + 'readonly': 'Только чтение', + 'empty_config': 'Пустая конфигурация', + 'valid_json': 'Валидный JSON', + 'line': 'Строка', + 'column': 'Столбец', + + // Configuration save messages + 'config_saved_restarting': 'Конфигурация сохранена. Сервер перезапускается...', + 'config_saved_success': 'Конфигурация сохранена успешно', + + // Validation messages + 'json_validation_success': 'JSON конфигурация валидна', + 'json_formatted': 'JSON отформатирован', + + // Confirmation dialogs + 'save_and_restart_title': 'Сохранить и перезапустить', + 'save_and_restart_message': 'Сохранить конфигурацию и перезапустить сервер?\n\nВнимание: Соединение будет прервано на время перезапуска.', + 'refresh_unsaved_changes_title': 'Несохраненные изменения', + 'refresh_unsaved_changes_message': 'У вас есть несохраненные изменения. Продолжить обновление?' }; \ No newline at end of file diff --git a/src/webui/static/style.css b/src/webui/static/style.css index 81630570..1ebf7e1f 100644 --- a/src/webui/static/style.css +++ b/src/webui/static/style.css @@ -1539,7 +1539,6 @@ button[onclick="copyNodeKey()"]:hover { /* Configuration Editor Styles */ .config-container { - max-width: 1200px; margin: 0 auto; padding: 20px; } @@ -1556,6 +1555,11 @@ button[onclick="copyNodeKey()"]:hover { box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); } +.config-info { + display: flex; + flex-direction: column; +} + .config-info h3 { margin: 0 0 10px 0; color: var(--text-heading); @@ -1617,31 +1621,7 @@ button[onclick="copyNodeKey()"]:hover { color: var(--text-warning); } -.config-actions { - display: flex; - gap: 10px; -} -.refresh-btn { - background: var(--bg-nav-active); - color: white; -} - -.save-btn { - background: var(--bg-success-dark); - color: white; -} - -.save-btn.modified { - background: var(--bg-warning-dark); - animation: pulse 2s infinite; -} - -@keyframes pulse { - 0% { opacity: 1; } - 50% { opacity: 0.7; } - 100% { opacity: 1; } -} /* Configuration Groups */ .config-groups { @@ -1943,17 +1923,7 @@ input:checked + .slider:before { gap: 15px; } -.line-numbers-toggle { - display: flex; - align-items: center; - gap: 6px; - font-size: 0.9em; - color: var(--text-muted); -} -.line-numbers-toggle input[type="checkbox"] { - margin: 0; -} .editor-wrapper { position: relative; @@ -2050,24 +2020,34 @@ input:checked + .slider:before { font-size: 0.8em; } -/* Additional action buttons */ -.format-btn { - background: var(--bg-nav-active); - color: white; +/* Action buttons group */ +.action-buttons-group { + display: flex; + gap: 8px; + flex-wrap: wrap; + justify-content: flex-end; } -.validate-btn { - background: var(--bg-warning-dark); - color: white; +.action-buttons-group .action-btn { + background: var(--bg-nav-item); + color: var(--text-nav); + border: none; + padding: 8px 16px; + border-radius: 4px; + cursor: pointer; + font-size: 13px; + font-weight: 500; + transition: all 0.2s ease; + margin: 0; + user-select: none; + display: flex; + align-items: center; + justify-content: center; } -.restart-btn { - background: var(--text-error); - color: white; -} - -.restart-btn:hover { - background: #c62828; +.action-buttons-group .action-btn:hover { + background: var(--bg-nav-hover); + transform: translateY(-1px); } /* JSON Syntax highlighting (basic) */ @@ -2105,13 +2085,16 @@ input:checked + .slider:before { padding-left: 45px; } - .config-actions { - flex-wrap: wrap; + .action-buttons-group { + flex-direction: row; + gap: 4px; + justify-content: center; } - .action-btn { - flex: 1; - min-width: 120px; + .action-buttons-group .action-btn { + flex: none; + min-width: auto; + justify-content: center; } } @@ -2127,4 +2110,14 @@ input:checked + .slider:before { [data-theme="dark"] .json-editor { background: var(--bg-info-card); color: var(--text-body); +} + +/* Dark theme support for action buttons */ +[data-theme="dark"] .action-buttons-group .action-btn { + color: var(--text-nav); +} + +[data-theme="dark"] .action-buttons-group .action-btn:hover { + background: var(--bg-nav-hover); + transform: translateY(-1px); } \ No newline at end of file