Redesign configurator UI with tabs and remove Excel export
- 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>
This commit is contained in:
82
web/templates/login.html
Normal file
82
web/templates/login.html
Normal file
@@ -0,0 +1,82 @@
|
||||
{{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" .}}
|
||||
Reference in New Issue
Block a user