# CLAUDE.md Website des Elternbeirats der IGMH. Blazor Web App mit statischem Server-Side-Rendering, .NET 10. Läuft als Container auf Unraid hinter dem Nginx Proxy Manager. Die Inhalte liegen in **PocketBase** (zweiter Container); die App liest sie pro Request über einen typisierten `PocketBaseClient`. Architekturentscheidungen und deren Begründung: `docs/architektur.md`. Dort nachlesen, bevor eine davon in Frage gestellt wird. ## Wann welches Dokument | Thema | Datei | |---|---| | Warum SSR statt WASM, warum PocketBase als Datenschicht (AE-1 bis AE-4) | `docs/architektur.md` | | Stack lokal starten, Tests, compose-Overlays | `docs/entwicklung.md` | | Unraid, compose, NPM-Proxy-Host, Registry, Rollback | `docs/deployment.md` | | Inhalte anlegen und ändern (PocketBase-Admin) | `docs/redaktion.md` | | Impressum, Datenschutz, Fotos | `docs/recht.md` | | Offene Arbeit, Meilensteine, offene Punkte | Gitea-Issues (Milestone „Elternbeirat-Website") | Offene Aufgaben liegen als **Gitea-Issues**, nicht als Markdown im Repo. So löst das Anlegen oder Ändern eines Tickets keinen Build aus. Die vier Arbeitsstränge (Redaktionssystem, Deployment, Inhalte, Infrastruktur) sind Parent-Issues mit Sub-Issues als Checkliste im Body, alle am Milestone „Elternbeirat-Website". ## Kommandos ```bash # Lokaler Stack (App + PocketBase) über das Dev-Overlay, siehe docs/entwicklung.md: docker compose -f compose.yaml -f compose.dev.yaml up --build # App :5000, PocketBase-Admin :8090/_/ dotnet test # Smoke-Tests (starten eigene PocketBase) + Unit-Tests ``` Die App liest ihren Inhalt pro Request aus PocketBase; sie hält selbst keinen Inhalt und liest nichts beim Start ein. Kein EF Core, keine Migrationen, kein Seeding im Code. Ein `git clone` ist **nicht** der vollständige Stand — der Inhalt liegt in PocketBase (`pb_data`), das separat gesichert wird (siehe `docs/deployment.md`). ## Nicht offensichtlich - **Inhalte liegen in PocketBase** (`pb_data`-Volume), nicht im Image. Eine Textänderung ist ein Eintrag im PocketBase-Admin und sofort live — kein Commit, kein Rebuild (`docs/architektur.md`, AE-2/AE-3). Nur die statischen Assets unter `wwwroot/` (CSS, Fonts, PDFs) sind ins Image gebacken. - **Kein `UseHttpsRedirection()`, kein `UseHsts()`.** NPM terminiert TLS; beides erzeugt hinter dem Proxy eine Redirect-Schleife. `UseForwardedHeaders` mit geleerten `KnownNetworks`/`KnownProxies` ist korrekt so, weil der Container kein Port-Mapping hat und nur über NPM erreichbar ist. - **Das chiseled-Runtime-Image hat keine Shell.** `HEALTHCHECK` mit `curl` oder `sh` schlägt dort fehl. - Neue Seite / neuer Beitrag / neuer Termin / neue Frage = **Record in der passenden PocketBase-Collection** (`pages` / `posts` / `events` / `faqs`), nicht eine Datei und **kein** `.razor`. `pages` mit `slug` ist sofort unter `/` erreichbar; `location`+`order` steuern das Menü, `embed` bettet Listen ein (`docs/redaktion.md`). Eine **neue Art von Liste** (eigene Route/Logik) ist dagegen Code: neue Collection + `PocketBaseClient`-Methode + Komponente (`docs/entwicklung.md`). - Slugs ohne Umlaute (`ueber-uns`, nicht `über-uns`). ## Regeln - **Statisches SSR ist der Default.** Kein `@rendermode` ohne konkreten Anlass, und wenn, dann an genau der einen Komponente — nie global. - **Keine externen Ressourcen.** Keine CDN-Skripte, keine Google Fonts, keine Maps- oder Video-Embeds. Schriften werden selbst ausgeliefert. Grund ist Datenschutz, nicht Geschmack (`docs/recht.md`). - **Kein EF Core, kein ORM, keine Migrationen.** Inhalte kommen aus PocketBase über den `PocketBaseClient` (REST), nicht aus einer relationalen DB im Code. - **Inhalte gehören nach PocketBase**, nicht als Markup in Komponenten und nicht in Dateien im Repo. - Kein eigenes CMS, kein Login und keine Benutzerkonten **in der Website** — gepflegt wird ausschließlich im PocketBase-Admin (Redakteure sind Superuser). - UI-Texte auf Deutsch, ohne Anglizismen. `Termine`, nicht `Events`. - Neue NuGet-Abhängigkeiten vorher begründen. Genutzt wird nur `Markdig` (Markdown → HTML). `YamlDotNet` stammt aus der abgelösten Datei-Schicht, wird nicht mehr verwendet und soll aus dem `csproj` entfernt werden. ## Stil - Nullable aktiviert, `ImplicitUsings` an, File-scoped Namespaces. - Datenzugriff über den typisierten `PocketBaseClient` (registriert via `AddHttpClient`), der pro Request liest — kein Start-Cache, kein Singleton mit Inhalten. Komponenten liegen feature-basiert unter `Features//`. - JSON-Konverter liegen in `Contracts` und werden per `[JsonConverter]` **am Feld** zugeordnet, nicht global auf den `JsonSerializerOptions` im Client registriert. So ist am Contract sichtbar, wie ein Feld behandelt wird (z. B. `Event.Start` als Zeitpunkt, `Post.Date` als Kalendertag), und `Contracts` bekommt keine Referenz auf `PocketBase` (Zirkel). Datumsfelder: `DateTime`/`DateTime?` = Zeitpunkt (Europe/Berlin), `DateOnly` = Kalendertag. - **Code durchgängig auf Englisch** — Typen, Member, Variablen, Kommentare, Skripte und Doku. Das gilt **auch für Domänenbegriffe**: im Code `Event`, nicht `Termin`; `Post`, nicht `Beitrag`. Deutsch bleibt ausschließlich, was ein Besucher liest oder ein Redakteur pflegt: UI-Texte sowie die **Werte** der PocketBase-Records (z. B. `title: Vorstandsteam`, der Markdown-`body`). Die Feldnamen und Slugs bleiben dagegen englisch (`title`, `slug`, `/board`). ## Coding Convention - **Ausdruckskörper (`=>`) sind Pflicht, wo syntaktisch möglich** — Methoden, Properties, Konstruktoren, Operatoren, lokale Funktionen. Ein `if/else`, das einen Wert liefert, wird zum ternären Ausdruck oder zur `switch`-Expression, kein Block-Körper mit `return`. Ein Block-Körper nur, wo ein Ausdruck sprachlich nicht geht (mehrere Anweisungen ohne Rückgabe, `ref`/`out`, `yield`). - Ternäre und `switch`-Expressions dürfen dafür mehrzeilig umgebrochen werden; Lesbarkeit entsteht durch Einrückung, nicht durch einen Block. - Nullable aktiv nutzen: `?`, `??`, `??=` statt Nullprüfungen im Block. Ein ungültiger `null`-Fall wird als Ausdruck geworfen (`?? throw new …`). - Argumente/Rückgaben früh und knapp validieren, bevorzugt als Ausdruck. ## Documentation Convention Vorbild ist `Elternbeirat.PocketBase/LocalDateTimeConverter.cs` — daran ausrichten. - **Jeder öffentliche (`public`/`protected`) Typ und Member bekommt XML-Doc** — nicht nur `PocketBaseClient` und `Contracts`. Interne Helfer, die Teil der fachlichen Erklärung sind (wie `WallClock`), ebenfalls. Razor-Markup nicht. - Voller Umfang, wo zutreffend: ``, dazu ``, ``, `` (jede geworfene Bedingung), `` für Kontext/Fallstricke, `` mit `` für nicht offensichtliche Nutzung, `` auf verwandte Typen. `` bei Interface-/Basis-Implementierungen. - Code im Text als Markup referenzieren, nicht als Prosa: ``, ``/``, `…` für Literale. - Einrückung: der Textinhalt steht mit vier Leerzeichen unter dem `///`-Tag (`/// Text`), Tags sauber verschachtelt. - Englisch, leicht verständlich, erklärt **warum**, nicht was der Code ohnehin zeigt.