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:
1 parent
d1d4d621a7
commit
10c55ca933
15 files changed
+1866
-10
No files matched your search
@@ -0,0 +1,64 @@
|
||||
// Konto-Menü im Seitenkopf: abgemeldet „Anmelden“, angemeldet E-Mail und „Abmelden“.
|
||||
import './account.css';
|
||||
import { el } from '../editor/dom';
|
||||
import { currentUser, logout, onAuthChange } from './auth';
|
||||
import { errorMessage } from './errors';
|
||||
import { requireLogin } from './login-dialog';
|
||||
|
||||
export interface AccountMenu {
|
||||
/** Zeigt das Menü erst, wenn der Anmeldestatus bekannt ist (kein Aufblitzen von „Anmelden“ bei angemeldeten Nutzern). */
|
||||
ready(): void;
|
||||
dispose(): void;
|
||||
}
|
||||
|
||||
export function mountAccountMenu(slot: HTMLElement): AccountMenu {
|
||||
let isReady = false;
|
||||
|
||||
function render(): void {
|
||||
if (!isReady) {
|
||||
slot.replaceChildren();
|
||||
return;
|
||||
}
|
||||
const user = currentUser();
|
||||
if (!user) {
|
||||
const signIn = el('button', { type: 'button', class: 'auth-btn auth-btn-small' }, 'Anmelden');
|
||||
signIn.addEventListener('click', () => void requireLogin('login'));
|
||||
slot.replaceChildren(el('div', { class: 'account-menu' }, signIn));
|
||||
return;
|
||||
}
|
||||
const email = el('span', { class: 'account-email', title: user.email }, user.email);
|
||||
const signOut = el('button', { type: 'button', class: 'auth-btn auth-btn-small' }, 'Abmelden');
|
||||
const error = el('span', { class: 'account-error', role: 'alert' });
|
||||
signOut.addEventListener('click', () => {
|
||||
signOut.disabled = true;
|
||||
error.textContent = '';
|
||||
// Bei Erfolg zeichnet onAuthChange das Menü neu; nur der Fehlerfall braucht Handarbeit.
|
||||
logout().catch((err: unknown) => {
|
||||
error.textContent = errorMessage(err, 'load');
|
||||
signOut.disabled = false;
|
||||
});
|
||||
});
|
||||
slot.replaceChildren(
|
||||
el(
|
||||
'div',
|
||||
{ class: 'account-menu', role: 'group', 'aria-label': 'Konto' },
|
||||
el('div', { class: 'account-row' }, email, signOut),
|
||||
error,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
const off = onAuthChange(render);
|
||||
render();
|
||||
|
||||
return {
|
||||
ready() {
|
||||
isReady = true;
|
||||
render();
|
||||
},
|
||||
dispose() {
|
||||
off();
|
||||
slot.replaceChildren();
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user