Files
prov/public/login.js
T
kerimatilganandClaude Opus 4.7 a535dcee38 UI redesign step 1/N: Tailwind foundation + Stitch theme + Login + Wizard
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>
2026-05-15 17:38:55 +02:00

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;
}
});
})();