Foundation - Pull in Tailwind via CDN with the full Stitch design tokens (palette, typography, spacing, radius) inline in <head>. Preflight disabled so the rest of the legacy inline CSS keeps applying until each screen is migrated. - Add Hanken Grotesk + JetBrains Mono + Material Symbols Outlined via Google Fonts. - Replace the page header with the Stitch dark-slate top bar: brand on the left, segmented tab nav in the middle, user pill + logout on the right. Floating userBadge removed; renderUserBadge() now writes the display name into #headerUserName and wires the existing logout button. Login - Single card mirroring the Stitch login mockup with icon-prefixed inputs (person/lock), focus ring, error banner and a primary CTA that includes the login glyph. Setup mode swaps the heading, shows the orange info bar with admin_panel_settings glyph and changes the button to "Administrator anlegen". Wizard (Schaltung erfassen) - Step 1: anbieter button grid in Stitch style — bordered cards with a material icon, hover lift and primary-coloured outline. - Step 2: white card with a "← Anbieter ändern" pill in the header, two-column layout (Aktivierungsdaten / Vertragsdaten), label-caps form labels, JetBrains-Mono numeric inputs for dates/ID/phone and a dashed file-drop zone. Save button gains the save glyph. Other screens (Übersicht, Provisionen, Gelöschte, Administration, Benutzer) still render in the legacy style — those migrations follow in the next steps. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
82 lines
3.1 KiB
JavaScript
82 lines
3.1 KiB
JavaScript
// Login page logic: detects first-run setup mode and swaps the form into
|
|
// admin-creation mode. Talks to /api/auth/setup or /api/auth/login.
|
|
|
|
(async function init() {
|
|
const errorBox = document.getElementById('errorBox');
|
|
const errorText = document.getElementById('errorText');
|
|
const setupNote = document.getElementById('setupNote');
|
|
const setupTopBar = document.getElementById('setupTopBar');
|
|
const brandIcon = document.getElementById('brandIcon');
|
|
const title = document.getElementById('title');
|
|
const subtitle = document.getElementById('subtitle');
|
|
const submitBtn = document.getElementById('submitBtn');
|
|
const submitLabel = document.getElementById('submitLabel');
|
|
const submitIcon = document.getElementById('submitIcon');
|
|
const form = document.getElementById('loginForm');
|
|
const passwordInput = document.getElementById('password');
|
|
|
|
let setupMode = false;
|
|
|
|
function showError(msg) {
|
|
errorText.textContent = msg;
|
|
errorBox.style.display = 'flex';
|
|
}
|
|
function clearError() {
|
|
errorText.textContent = '';
|
|
errorBox.style.display = 'none';
|
|
}
|
|
|
|
try {
|
|
const r = await fetch('/api/auth/setup-required');
|
|
if (r.ok) {
|
|
const data = await r.json();
|
|
setupMode = !!data.setupRequired;
|
|
}
|
|
} catch (_) {
|
|
// network error -> stay in login mode
|
|
}
|
|
|
|
if (setupMode) {
|
|
title.textContent = 'Ersteinrichtung';
|
|
subtitle.textContent = 'Willkommen beim Provisions-Tracker.';
|
|
setupNote.style.display = 'flex';
|
|
setupTopBar.style.display = 'block';
|
|
submitLabel.textContent = 'Administrator anlegen';
|
|
submitIcon.textContent = 'person_add';
|
|
// Brand icon switches to admin look in setup mode
|
|
brandIcon.classList.remove('bg-primary', 'text-on-primary');
|
|
brandIcon.classList.add('bg-surface-variant', 'text-on-surface');
|
|
brandIcon.querySelector('span').textContent = 'admin_panel_settings';
|
|
passwordInput.setAttribute('autocomplete', 'new-password');
|
|
passwordInput.setAttribute('minlength', '8');
|
|
}
|
|
|
|
form.addEventListener('submit', async (e) => {
|
|
e.preventDefault();
|
|
clearError();
|
|
submitBtn.disabled = true;
|
|
const username = document.getElementById('username').value.trim();
|
|
const password = passwordInput.value;
|
|
|
|
try {
|
|
const url = setupMode ? '/api/auth/setup' : '/api/auth/login';
|
|
const r = await fetch(url, {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
credentials: 'same-origin',
|
|
body: JSON.stringify({ username, password }),
|
|
});
|
|
const data = await r.json().catch(() => ({}));
|
|
if (!r.ok) {
|
|
showError(data.error || 'Anmeldung fehlgeschlagen');
|
|
submitBtn.disabled = false;
|
|
return;
|
|
}
|
|
window.location.href = '/';
|
|
} catch (err) {
|
|
showError('Netzwerkfehler: ' + err.message);
|
|
submitBtn.disabled = false;
|
|
}
|
|
});
|
|
})();
|