feat: redesign CMS and unify German admin UI

This commit is contained in:
2026-09-17 16:51:24 +02:00
parent a2b3e99174
commit 7ec0c51ff5
129 changed files with 952 additions and 550 deletions
+20 -20
View File
@@ -1,20 +1,20 @@
# Anouma
Die Website von Anouma — Next.js (App Router) mit einem eingebauten [Payload CMS](https://payloadcms.com) (PostgreSQL) für Termine, Beiträge, Angebote und Seiteninhalte, plus Online-Termine mit selbst gehostetem [LiveKit](https://livekit.io) SFU-Video-Call, Anmeldungen und E-Mail-Erinnerungen.
Die Website von Anouma - Next.js (App Router) mit einem eingebauten [Payload CMS](https://payloadcms.com) (PostgreSQL) für Termine, Beiträge, Angebote und Seiteninhalte, plus Online-Termine mit selbst gehostetem [LiveKit](https://livekit.io) SFU-Video-Call, Anmeldungen und E-Mail-Erinnerungen.
## Projektstruktur
- `app/(frontend)/` — die öffentliche Website (bestehendes ANOUMA-Design, eigener Root-Layout)
- `app/(call)/` — die Video-Call-Oberfläche (`/termine/[slug]/call`), eigenes minimalistisches dunkles Layout ohne Navbar/Footer
- `app/(payload)/` — der Admin-Bereich unter `/admin`, Payloads REST/GraphQL-API sowie die Meeting-/Cron-API-Routen (`api/meetings/...`, `api/cron/...`)
- `app/global-not-found.tsx` — statische 404-Seite (siehe unten, warum sie nötig ist)
- `collections/`, `globals/`, `access/`, `fields/`, `payload.config.ts` — die CMS-Konfiguration
- `lib/payload/` — Local-API-Zugriffe für die öffentliche Website (mit `React.cache` pro Request memoisiert)
- `lib/meeting/` — Meeting-Passwort, Zeitfenster/Status, signierte Beitritts-Tokens, LiveKit-SFU-Token und Reminder-Logik
- `lib/email/` — SMTP-Versand (nodemailer) und das E-Mail-Template für Reminder
- `lib/texte.ts` — die ursprünglichen Anouma-Texte, nur noch als Seed-Quelle verwendet
- `scripts/seed.ts` — überträgt die vorhandenen Inhalte ins CMS
- `server.ts` — eigener Node-Server (statt `next start`), passend zur bestehenden Payload-Konfiguration
- `app/(frontend)/` - die öffentliche Website (bestehendes ANOUMA-Design, eigener Root-Layout)
- `app/(call)/` - die Video-Call-Oberfläche (`/termine/[slug]/call`), eigenes minimalistisches dunkles Layout ohne Navbar/Footer
- `app/(payload)/` - der Admin-Bereich unter `/admin`, Payloads REST/GraphQL-API sowie die Meeting-/Cron-API-Routen (`api/meetings/...`, `api/cron/...`)
- `app/global-not-found.tsx` - statische 404-Seite (siehe unten, warum sie nötig ist)
- `collections/`, `globals/`, `access/`, `fields/`, `payload.config.ts` - die CMS-Konfiguration
- `lib/payload/` - Local-API-Zugriffe für die öffentliche Website (mit `React.cache` pro Request memoisiert)
- `lib/meeting/` - Meeting-Passwort, Zeitfenster/Status, signierte Beitritts-Tokens, LiveKit-SFU-Token und Reminder-Logik
- `lib/email/` - SMTP-Versand (nodemailer) und das E-Mail-Template für Reminder
- `lib/texte.ts` - die ursprünglichen Anouma-Texte, nur noch als Seed-Quelle verwendet
- `scripts/seed.ts` - überträgt die vorhandenen Inhalte ins CMS
- `server.ts` - eigener Node-Server (statt `next start`), passend zur bestehenden Payload-Konfiguration
Zwei bzw. drei Root-Layouts (`(frontend)`, `(payload)`, `(call)`) bedeuten: Next.js kann daraus keine einzelne 404-Seite komponieren, deshalb gibt es `app/global-not-found.tsx` (siehe `experimental.globalNotFound` in `next.config.ts`).
@@ -47,11 +47,11 @@ Erinnerungs-Mails (60/30 Minuten vorher, pro Termin im CMS einzeln an-/abschaltb
curl -H "Authorization: Bearer $CRON_SECRET" https://anouma.org/api/cron/event-reminders
```
Der Versand ist idempotent (`reminder60Sent`/`reminder30Sent` je Anmeldung, `hostReminder60Sent`/`hostReminder30Sent` je Termin) — ein häufiger laufender Cron verschickt also nie doppelt.
Der Versand ist idempotent (`reminder60Sent`/`reminder30Sent` je Anmeldung, `hostReminder60Sent`/`hostReminder30Sent` je Termin) - ein häufiger laufender Cron verschickt also nie doppelt.
## Setup (Produktion / Deployment)
Produktion läuft komplett über GitHub Actions CI/CD — der Server baut nichts selbst, er zieht ein fertiges, getestetes Image aus GitHub Container Registry. Einmalig auf dem Server:
Produktion läuft komplett über GitHub Actions CI/CD - der Server baut nichts selbst, er zieht ein fertiges, getestetes Image aus GitHub Container Registry. Einmalig auf dem Server:
```bash
git clone https://github.com/whitehai11/anouma.git
@@ -68,7 +68,7 @@ Jedes weitere Deployment läuft danach ausschließlich über einen Git-Tag:
git tag v1.2.0 && git push origin v1.2.0
```
GitHub Actions testet, baut das Image, pusht es nach GHCR und deployt es per SSH auf den Server — inklusive Backup, Healthcheck und automatischem Rollback bei Fehlschlag. Es gibt kein separates Update-Script.
GitHub Actions testet, baut das Image, pusht es nach GHCR und deployt es per SSH auf den Server - inklusive Backup, Healthcheck und automatischem Rollback bei Fehlschlag. Es gibt kein separates Update-Script.
Für alle Details (Architektur, GitHub Secrets, CI/Release-Workflows, Reverse-Proxy- und LiveKit-Einrichtung, Environment-Variablen, Backups, Rollback, Security) siehe **[DEPLOYMENT.md](./DEPLOYMENT.md)**.
@@ -88,7 +88,7 @@ Nur Postgres per Docker, die App läuft direkt auf dem Host:
docker compose up -d postgres
```
(oder eine gehostete Postgres-Instanz, z. B. Neon/Supabase — einfach `DATABASE_URI` in `.env` entsprechend setzen).
(oder eine gehostete Postgres-Instanz, z. B. Neon/Supabase - einfach `DATABASE_URI` in `.env` entsprechend setzen).
### Entwicklung
@@ -97,7 +97,7 @@ npm run dev
```
- Website: http://localhost:3000
- Admin: http://localhost:3000/admin — beim ersten Aufruf legst du dort direkt deinen eigenen Admin-Zugang (E-Mail + Passwort) an. Es gibt kein Standardpasswort im Code.
- Admin: http://localhost:3000/admin - beim ersten Aufruf legst du dort direkt deinen eigenen Admin-Zugang (E-Mail + Passwort) an. Es gibt kein Standardpasswort im Code.
### Vorhandene Inhalte ins CMS übertragen
@@ -105,7 +105,7 @@ npm run dev
npm run seed
```
Überträgt die 7 Angebote sowie die Texte für Startseite, Über mich, Angebote-Einleitung, Aktuelles-Einleitung, Kontakt und Termin buchen aus `lib/texte.ts` ins CMS. Kann gefahrlos mehrfach ausgeführt werden. Kontaktdaten (E-Mail/Telefon/Region) werden zunächst als Platzhalter gesetzt — bitte im Admin unter „Kontakt“ durch die echten Angaben ersetzen.
Überträgt die 7 Angebote sowie die Texte für Startseite, Über mich, Angebote-Einleitung, Aktuelles-Einleitung, Kontakt und Termin buchen aus `lib/texte.ts` ins CMS. Kann gefahrlos mehrfach ausgeführt werden. Kontaktdaten (E-Mail/Telefon/Region) werden zunächst als Platzhalter gesetzt - bitte im Admin unter „Kontakt“ durch die echten Angaben ersetzen.
### Produktions-Build ohne Docker
@@ -125,8 +125,8 @@ Für einen vollständig dockerisierten Produktionsbetrieb (App + Datenbank, Heal
| `npm run generate:importmap`| Admin-Importmap neu erzeugen (nach neuen Feldtypen) |
| `npm run migrate:create` | Neue Datenbank-Migration aus Config-Änderungen bauen |
| `npm run lint` | ESLint |
| `npm test` | Test-Suite (aktuell ein Platzhalter — noch keine Tests vorhanden) |
| `npm run seo:check` | Optionale SEO-Prüfung (doppelte Slugs, fehlende Alt-Texte) — nicht produktionskritisch |
| `npm test` | Test-Suite (aktuell ein Platzhalter - noch keine Tests vorhanden) |
| `npm run seo:check` | Optionale SEO-Prüfung (doppelte Slugs, fehlende Alt-Texte) - nicht produktionskritisch |
## Design