Restore connection settings modal from top nav

This commit is contained in:
2026-02-08 10:54:31 +03:00
parent 8fbdcb1d86
commit 319400106c
2 changed files with 313 additions and 6 deletions

View File

@@ -21,7 +21,7 @@
<div class="hidden md:flex space-x-4">
<a id="admin-pricing-link" href="/admin/pricing" class="text-gray-600 hover:text-gray-900 px-3 py-2 text-sm">Администратор цен</a>
<a href="/pricelists" class="text-gray-600 hover:text-gray-900 px-3 py-2 text-sm">Прайслисты</a>
<a href="/admin/pricing" class="text-gray-600 hover:text-gray-900 px-3 py-2 text-sm">Настройки</a>
<button type="button" onclick="openConnectionSettingsModal()" class="text-gray-600 hover:text-gray-900 px-3 py-2 text-sm">Настройки</button>
</div>
</div>
<div class="flex items-center space-x-4">
@@ -39,14 +39,55 @@
{{template "content" .}}
</main>
<div id="connection-settings-modal" class="fixed inset-0 bg-black bg-opacity-50 hidden items-center justify-center z-50 px-4">
<div class="bg-white rounded-lg shadow-lg w-full max-w-md">
<div class="flex items-center justify-between px-5 py-4 border-b">
<h2 class="text-lg font-semibold text-gray-900">Параметры подключения</h2>
<button type="button" onclick="closeConnectionSettingsModal()" class="text-gray-400 hover:text-gray-700 text-xl leading-none">&times;</button>
</div>
<form id="connection-settings-form" class="p-5 space-y-3">
<div>
<label class="block text-sm text-gray-700 mb-1">Хост</label>
<input id="connection-host" name="host" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500">
</div>
<div>
<label class="block text-sm text-gray-700 mb-1">Порт</label>
<input id="connection-port" name="port" type="number" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500">
</div>
<div>
<label class="block text-sm text-gray-700 mb-1">База данных</label>
<input id="connection-database" name="database" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500">
</div>
<div>
<label class="block text-sm text-gray-700 mb-1">Пользователь</label>
<input id="connection-user" name="user" type="text" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500">
</div>
<div>
<label class="block text-sm text-gray-700 mb-1">Пароль</label>
<input id="connection-password" name="password" type="password" autocomplete="new-password" class="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-orange-500">
<p class="text-xs text-gray-500 mt-1">Оставьте пустым, чтобы сохранить текущий пароль.</p>
</div>
<div id="connection-settings-status" class="hidden text-sm rounded-md px-3 py-2"></div>
<div class="flex items-center justify-end gap-2 pt-2">
<button type="button" onclick="testConnectionSettings()" class="px-3 py-2 border border-gray-300 rounded-md text-sm hover:bg-gray-50">Проверить</button>
<button type="submit" class="px-3 py-2 bg-orange-600 text-white rounded-md text-sm hover:bg-orange-700">Сохранить</button>
</div>
</form>
</div>
</div>
<div id="toast" class="fixed bottom-4 right-4 z-50"></div>
<script>
function showToast(msg, type) {
const colors = { success: 'bg-green-500', error: 'bg-red-500', info: 'bg-orange-500' };
const el = document.getElementById('toast');
el.innerHTML = '<div class="' + (colors[type] || colors.info) + ' text-white px-4 py-2 rounded shadow">' + msg + '</div>';
setTimeout(() => el.innerHTML = '', 3000);
el.replaceChildren();
const toast = document.createElement('div');
toast.className = (colors[type] || colors.info) + ' text-white px-4 py-2 rounded shadow';
toast.textContent = String(msg || '');
el.appendChild(toast);
setTimeout(() => el.replaceChildren(), 3000);
}
function renderDBStatus(connected, errorText) {
@@ -68,14 +109,114 @@
renderDBStatus(data.connected === true, data.error || '');
const userEl = document.getElementById('db-user');
if (data.connected && data.db_user) {
userEl.innerHTML = '<span class="text-gray-500">@</span>' + data.db_user;
userEl.textContent = '@' + String(data.db_user);
} else if (userEl) {
userEl.textContent = '';
}
} catch (e) {
renderDBStatus(false, 'Status request failed');
}
}
function closeConnectionSettingsModal() {
const modal = document.getElementById('connection-settings-modal');
if (!modal) return;
modal.classList.add('hidden');
modal.classList.remove('flex');
}
function showConnectionSettingsStatus(message, type) {
const el = document.getElementById('connection-settings-status');
if (!el) return;
el.classList.remove('hidden', 'bg-green-100', 'text-green-800', 'bg-red-100', 'text-red-800', 'bg-orange-100', 'text-orange-800');
if (type === 'success') {
el.classList.add('bg-green-100', 'text-green-800');
} else if (type === 'error') {
el.classList.add('bg-red-100', 'text-red-800');
} else {
el.classList.add('bg-orange-100', 'text-orange-800');
}
el.textContent = message;
}
async function openConnectionSettingsModal() {
const modal = document.getElementById('connection-settings-modal');
if (!modal) return;
modal.classList.remove('hidden');
modal.classList.add('flex');
showConnectionSettingsStatus('Загрузка текущих параметров...', 'info');
try {
const resp = await fetch('/api/connection-settings');
const data = await resp.json();
document.getElementById('connection-host').value = data.host || '';
document.getElementById('connection-port').value = data.port || 3306;
document.getElementById('connection-database').value = data.database || '';
document.getElementById('connection-user').value = data.user || '';
document.getElementById('connection-password').value = '';
document.getElementById('connection-settings-status').classList.add('hidden');
} catch (e) {
showConnectionSettingsStatus('Не удалось загрузить параметры: ' + e.message, 'error');
}
}
async function testConnectionSettings() {
const form = document.getElementById('connection-settings-form');
if (!form) return;
showConnectionSettingsStatus('Проверка подключения...', 'info');
try {
const resp = await fetch('/api/connection-settings/test', {
method: 'POST',
body: new FormData(form),
});
const data = await resp.json();
if (data.success) {
showConnectionSettingsStatus('Подключение успешно.', 'success');
} else {
showConnectionSettingsStatus(data.error || 'Ошибка проверки подключения.', 'error');
}
} catch (e) {
showConnectionSettingsStatus('Ошибка сети: ' + e.message, 'error');
}
}
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('connection-settings-form');
if (form) {
form.addEventListener('submit', async function (e) {
e.preventDefault();
showConnectionSettingsStatus('Сохранение параметров...', 'info');
try {
const resp = await fetch('/api/connection-settings', {
method: 'POST',
body: new FormData(form),
});
const data = await resp.json();
if (!resp.ok || !data.success) {
showConnectionSettingsStatus(data.error || 'Ошибка сохранения параметров.', 'error');
return;
}
showConnectionSettingsStatus('Параметры сохранены. Выполняется перезапуск...', 'success');
setTimeout(async function () {
try {
await fetch('/api/restart', { method: 'POST' });
} catch (_) {
}
}, 300);
} catch (e) {
showConnectionSettingsStatus('Ошибка сети: ' + e.message, 'error');
}
});
}
const modal = document.getElementById('connection-settings-modal');
if (modal) {
modal.addEventListener('click', function (e) {
if (e.target === modal) closeConnectionSettingsModal();
});
}
refreshDBConnectionStatus();
setInterval(refreshDBConnectionStatus, 10000);
});