feat(account): Login, Registrierung, Passwort zurücksetzen

- appwrite 22.4.1 (exakt gepinnt); Client mit Endpoint aus VITE_APPWRITE_ENDPOINT
  bzw. location.origin + /v1, Projekt aus VITE_APPWRITE_PROJECT_ID bzw. aircard
- auth.ts: getCurrentUser (401 -> null, ungültige Antwort -> Fehler), login, register,
  logout, Passwort-Link anfordern/abschließen, onAuthChange, validatePassword
- errors.ts: deutsche Meldungen nach type, dann code (Tabelle aus dem Plan)
- Login-Dialog (Anmelden/Registrieren/Passwort vergessen) als gemeinsames <dialog>,
  Konto-Menü im Seitenkopf, Seite /passwort-neu
- Tests: errors, auth, Dialog, Passwort-neu-Seite

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ClaudeandClaude Opus 5.5 committed 2026-10-04 08:54:10 +00:00
1 parent d1d4d621a7
commit 10c55ca933
15 files changed
+1866 -10

No files matched your search

+21 -8
View File
@@ -1,24 +1,37 @@
import '@fontsource-variable/nunito-sans';
import './styles.css';
import { startRouter, type RouteId } from './pages/router';
import { mountAccountMenu } from './account/account-menu';
import { getCurrentUser } from './account/auth';
import { startRouter } from './pages/router';
import { mountResetPage } from './pages/reset-page';
import { mountEditor } from './editor/editor-page';
import { createStore, initialEditorState } from './editor/state';
// Platzhalter, bis die Seiten aus den Folgeaufgaben (Karten, Passwort) angebunden sind.
const PLACEHOLDER_TITLES: Record<Exclude<RouteId, 'editor'>, string> = {
cards: 'Meine Karten',
reset: 'Passwort neu setzen',
};
// Der Editor-Zustand lebt außerhalb der Seiten und überlebt Seitenwechsel.
const editorStore = createStore(initialEditorState());
// Konto-Menü im Seitenkopf. Der Anmeldestatus wird einmal beim Start geholt; schlägt das fehl
// (außer 401, das getCurrentUser selbst als „abgemeldet“ meldet), gilt der Nutzer als abgemeldet.
const accountSlot = document.getElementById('account-slot');
if (accountSlot) {
const menu = mountAccountMenu(accountSlot);
getCurrentUser()
.catch((error: unknown) => {
console.error('Anmeldestatus konnte nicht geprüft werden', error);
})
.finally(() => menu.ready());
}
startRouter((route, root) => {
if (route === 'editor') {
// Speichern wird mit den Konten (spätere Aufgabe) angebunden; bis dahin passiert nichts.
return mountEditor(root, editorStore, { onSave: () => {} });
}
if (route === 'reset') {
return mountResetPage(root);
}
// Platzhalter, bis die Karten-Seite aus der Folgeaufgabe angebunden ist.
const heading = document.createElement('h1');
heading.textContent = PLACEHOLDER_TITLES[route];
heading.textContent = 'Meine Karten';
root.replaceChildren(heading);
});