Files
Elternbeirat/CLAUDE.md
T

7.2 KiB

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

# 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 /<slug> 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/<Bereich>/.
  • 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: <summary>, dazu <param>, <returns>, <exception> (jede geworfene Bedingung), <remarks> für Kontext/Fallstricke, <example> mit <code> für nicht offensichtliche Nutzung, <seealso> auf verwandte Typen. <inheritdoc/> bei Interface-/Basis-Implementierungen.
  • Code im Text als Markup referenzieren, nicht als Prosa: <see cref="…"/>, <see langword="null"/>/<see langword="false"/>, <c>…</c> 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.