- Add tab-based configurator (Base, Storage, PCI, Power, Accessories, Other) - Base tab: single-select with autocomplete for MB, CPU, MEM - Other tabs: multi-select with autocomplete and quantity input - Table view with LOT, Description, Price, Quantity, Total columns - Add configuration list page with create modal (opportunity number) - Remove Excel export functionality and excelize dependency - Increase component list limit from 100 to 5000 - Add web templates (base, index, configs, login, admin_pricing) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
83 lines
3.2 KiB
HTML
83 lines
3.2 KiB
HTML
{{define "title"}}Вход - QuoteForge{{end}}
|
||
|
||
{{define "content"}}
|
||
<div class="max-w-sm mx-auto mt-16">
|
||
<div class="bg-white rounded-lg shadow p-6">
|
||
<h1 class="text-xl font-bold text-center mb-6">Вход в систему</h1>
|
||
|
||
<form id="login-form">
|
||
<div class="mb-4">
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Логин</label>
|
||
<input type="text" name="username" id="username" required
|
||
class="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
value="admin">
|
||
</div>
|
||
<div class="mb-4">
|
||
<label class="block text-sm font-medium text-gray-700 mb-1">Пароль</label>
|
||
<input type="password" name="password" id="password" required
|
||
class="w-full px-3 py-2 border rounded focus:outline-none focus:ring-2 focus:ring-blue-500"
|
||
value="admin123">
|
||
</div>
|
||
<div id="error" class="text-red-600 text-sm mb-4 hidden"></div>
|
||
<button type="submit" id="submit-btn"
|
||
class="w-full bg-blue-600 text-white py-2 rounded hover:bg-blue-700">
|
||
Войти
|
||
</button>
|
||
</form>
|
||
|
||
<p class="text-center text-sm text-gray-500 mt-4">
|
||
<a href="/" class="text-blue-600">← На главную</a>
|
||
</p>
|
||
</div>
|
||
</div>
|
||
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const form = document.getElementById('login-form');
|
||
if (!form) return;
|
||
|
||
form.addEventListener('submit', async function(e) {
|
||
e.preventDefault();
|
||
|
||
const username = document.getElementById('username').value;
|
||
const password = document.getElementById('password').value;
|
||
const errorEl = document.getElementById('error');
|
||
const btn = document.getElementById('submit-btn');
|
||
|
||
errorEl.classList.add('hidden');
|
||
btn.disabled = true;
|
||
btn.textContent = 'Вход...';
|
||
|
||
try {
|
||
const resp = await fetch('/api/auth/login', {
|
||
method: 'POST',
|
||
headers: {'Content-Type': 'application/json'},
|
||
body: JSON.stringify({username, password})
|
||
});
|
||
|
||
const data = await resp.json();
|
||
|
||
if (resp.ok && data.access_token) {
|
||
localStorage.setItem('token', data.access_token);
|
||
localStorage.setItem('refresh_token', data.refresh_token);
|
||
localStorage.setItem('user', JSON.stringify(data.user));
|
||
window.location.href = '/configs';
|
||
} else {
|
||
errorEl.textContent = data.error || 'Неверный логин или пароль';
|
||
errorEl.classList.remove('hidden');
|
||
btn.disabled = false;
|
||
btn.textContent = 'Войти';
|
||
}
|
||
} catch(err) {
|
||
errorEl.textContent = 'Ошибка соединения с сервером';
|
||
errorEl.classList.remove('hidden');
|
||
btn.disabled = false;
|
||
btn.textContent = 'Войти';
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
{{end}}
|
||
|
||
{{template "base" .}}
|