/** * Modul: Login-Seite * Zweck: Anmeldeformular mit Username/Passwort, Fehlerbehandlung, Session-Start * Abhängigkeiten: /api.js */ import { auth } from '/api.js'; import { t } from '/i18n.js'; const VERSION_URL = '/api/v1/version'; /** * Rendert die Login-Seite in den gegebenen Container. * @param {HTMLElement} container */ export async function render(container) { container.innerHTML = `

Oikos

`; const form = container.querySelector('#login-form'); const errorEl = container.querySelector('#login-error'); const submitBtn = container.querySelector('#login-btn'); const versionEl = container.querySelector('#login-version'); fetch(VERSION_URL) .then((r) => r.json()) .then((d) => { versionEl.textContent = t('login.version', { version: d.version }); }) .catch(() => {}); form.addEventListener('submit', async (e) => { e.preventDefault(); errorEl.hidden = true; const username = form.username.value.trim(); const password = form.password.value; if (!username || !password) { showError(errorEl, t('common.allFieldsRequired')); return; } submitBtn.disabled = true; submitBtn.textContent = t('login.loggingIn'); try { const result = await auth.login(username, password); window.oikos.navigate('/', result.user); } catch (err) { showError(errorEl, err.status === 429 ? t('login.tooManyAttempts') : t('login.invalidCredentials') ); } finally { submitBtn.disabled = false; submitBtn.textContent = t('login.loginButton'); } }); } function showError(el, message) { el.textContent = message; el.hidden = false; }