Neuer Hook useScrollRestoration (einmal im AppShell gemountet) erhält die
Scrollposition je Route über:
- Zurück/Vorwärts-Navigation (POP restauriert, PUSH startet oben),
- Bildschirm-Sperre / App-Hintergrund (visibilitychange, pagehide/pageshow) —
damit das Handy nach dem Entsperren nicht mehr oben landet.
Robust gegen nachladende Listen (rAF-Nachjustierung bis ~2,5 s, während die Liste
wächst), bricht bei eigener Scroll-Eingabe ab, Deep-Links (?focus=, #anchor) haben
Vorrang. Die bisher nur in der Tickets-Seite eingebaute Logik wurde durch den
globalen Hook ersetzt.
tsc/eslint/vitest grün; e2e (navigation/listen/tickets, 42) grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Die Züchterin kann auf der Tickets-Seite Push-Benachrichtigungen aktivieren
(🔔-Schalter). Service Worker ist BEWUSST push-only (kein fetch/Caching), damit das
Laden der App nie beeinträchtigt wird.
- Backend: WebPush-Bibliothek + VAPID; PushNotifier sendet an alle Abos, räumt veraltete
(404/410) auf. Endpoints: GET /push/vapid-public-key, POST /push/subscribe|unsubscribe.
- Ausgelöst über ein notify-Flag auf PUT /feedback: NUR die KI setzt es (z. B. neue
Rückfrage / Ticket gelöst) → keine Selbst-Pushes durch Aktionen der Züchterin. Text wird
aus dem Status abgeleitet, Klick öffnet das Ticket (?focus=).
- Schalter/Logik blenden sich aus, wenn das Gerät kein Push kann oder der Server keine
VAPID-Schlüssel hat (z. B. e2e-Mock).
Migration WebPushSubscriptions. VAPID-Schlüssel in appsettings.json (lokale Single-User-App
im Heimnetz — bewusste, vertretbare Vereinfachung). Tests: 264 Backend grün (+Push-Endpoints),
e2e Tickets Desktop grün, vitest 149.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Anhänge im Melde-Fenster (beim Erstellen) und direkt an bestehenden Tickets:
Vorschaubilder, Öffnen im neuen Tab, Entfernen.
- Bytes liegen in eigener Tabelle (FeedbackAttachment, lose FeedbackId ohne FK →
übersteht den Ingest-Wipe); GET /feedback liefert nur Metadaten (id/Name/Typ/Größe),
die Bytes über /feedback/attachments/{id}. Größenlimit 10 MB.
- Endpoints: POST /feedback/{id}/attachments (base64), GET /feedback/attachments/{id}
(Bytes), DELETE /feedback/attachments/{id}.
Migration FeedbackAttachments. Tests: 262 Backend grün (+Upload/Serve/Delete +Validierung),
e2e Tickets Desktop+Phone grün (+Foto-Upload), vitest 149.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Suche: Volltext über Nachricht/Frage/Antwort/Changelog/Name/Kategorie, plus
Kategorie-Filter (KI-gesetzte Kategorie als Chip + Auswahlmenü), je innerhalb der
aktiven Ansicht.
- 👍/👎 auf gelösten Tickets: 👍 speichert die Rückmeldung, 👎 öffnet das Ticket
wieder (zurück in die Rückfragen) und bittet um die fehlende Info.
- Melde-Fenster: schlägt beim Tippen ähnliche bereits gelöste Tickets vor
(Wort-Überlappung + Bonus bei gleichem Tier/Wurf/Kontakt) — Self-Service.
Backend: Feedback.Category + Feedback.Helpful (+ PUT-Handling), Migration
FeedbackCategoryAndHelpful (beide nullable). Tests: 260 Backend grün, e2e Tickets
Desktop +4 (Suche/Kategorie/👍/👎), vitest 149.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Der Herkunfts-Verlauf nennt jetzt auch, wenn ein Wert aus einer Quelle VERWORFEN
wurde — mit Grund und was stattdessen verwendet wurde (⚠-Markierung), z. B.:
- „⚠ Vater-Kandidat ‚Danielle' (*04.03.2020) verworfen — falsches Geschlecht für
die Vaterrolle; ‚Hagrid Rubeus' verwendet." (Molly)
- „⚠ Geschlecht weiblich aus ‚Wurfchronik.docx' verworfen — abweichend; männlich
aus ‚Alberto Kids.xlsx' verwendet (Mehrheit)." (Solice)
- „⚠ Vater ‚Jasper Jacob' (*2018) verworfen — unplausibel (7 Jahre älter); kein
Ersatz." / DOB-Remap: „⚠ Geburtsdatum 22.08.2018 verworfen — per manueller
Entscheidung korrigiert; 14.07.2019 verwendet." (Kazuya)
Import: generischer _format_discard()-Helfer; Verwerfungen aus Mehrheitsentscheid,
pick_parent_ref-Ablehnungen (explain_pick_rejections), Rollen-Normalisierung,
Eltern-Alter-Sanity-Check und DOB-Remaps werden über _discarded/_prov_args an die
Tiere gehängt; Gerbil-Provenance wird in einem finalen Pass gebaut (nachdem alle
Verwerfungen feststehen). Frontend hebt ⚠-Zeilen ab (+ Legende).
Tests grün (python merge/extract, vitest 129, playwright 8, dotnet 212).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Die Herkunft liest sich jetzt wie eine History: pro Feld wird genannt, aus
welcher Quelldatei der Wert kam, plus Merge-/Entscheidungs-/Wurfchronik-Schritte.
Beispiel: „In ‚X.xlsx' gefunden." → „Geburtsdatum (…) aus ‚X.xlsx'." → „Auch in
‚Y.docx' gefunden → zusammengeführt." → „Eltern über Position erkannt (Quelle …)."
Import: build_entity_provenance() um history[] erweitert; ein field_source-
Tracking im Gerbil-Dedup hält fest, welcher Datensatz/welche Datei jedes Feld
(DOB/Genotyp/Geschlecht/Farbschlag/Sterbedatum) lieferte — auch über Empty-Fill
und Mehrheitsentscheid hinweg. Kontakte und Würfe erhalten analoge, datei-
attribuierte Verläufe. Kein DB-Migration nötig (history liegt im Provenance-JSON).
Frontend: ProvenanceDialog rendert den Verlauf als nummerierte Timeline.
Tests: history wird erzeugt, nennt Dateien, überlebt Ingest (dotnet 212,
vitest 129, playwright 17, python merge/extract grün).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Neuer „Datenherkunft"-Button in der Rennmausakte öffnet einen Dialog, der zeigt,
aus welchen Quellen der Eintrag erzeugt wurde: Quelldateien (Stammbäume +
Wurfchronik), Anzahl zusammengeführter Datensätze, Eltern-Herleitung
(Methode/Konfidenz) und Hinweise (z. B. „per manueller Entscheidung zugeordnet",
„Konflikt gelöst", „aus N Datensätzen zusammengeführt").
Import: build_provenance() in merge_and_resolve.py sammelt die Herkunft über alle
deduplizierten Datensätze und schreibt sie als Provenance-JSON je Tier in
resolved_import.json. Backend: Gerbil.Provenance (nullable text) + Migration
AddGerbilProvenance, gemappt im Ingest und im GerbilDto zurückgegeben. Frontend:
ProvenanceDialog + Typen + Strings.
Tests: Ingest-Round-trip (vorhanden/abwesend), e2e provenance.spec.ts.
dotnet(212)/vitest(129)/playwright/tsc/eslint grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Rechtsklick auf eine Maus im Stammbaum öffnet ein Kontextmenü mit „ID kopieren"
(Clipboard + Toast) und „Fehler melden". Zusätzlich „Fehler melden"-Buttons in
der Rennmausakte und der Wurf-Ansicht. Der Dialog erfasst eine Beschreibung und
sendet sie samt Debug-Kontext (Ansicht, Tier-/Wurf-ID + Name, URL, Zeitstempel,
User-Agent) an POST /feedback; gespeichert in einer neuen Feedback-Tabelle.
Backend: Feedback-Entity (lose nullable GerbilId/LitterId ohne FK), Endpoints
POST/GET /feedback, EF-Migration AddFeedback. Die Tabelle wird vom Import-Ingest
NICHT geleert — Feedback überlebt Re-Ingests (Test deckt das ab).
Tests: FeedbackEndpointTests (persistiert, 400 bei leer, übersteht Ingest-Wipe);
e2e feedback.spec.ts. tsc/eslint/vitest(129)/playwright(6)/dotnet(212) grün.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Detailseite (Rennmausakte):
- Neugestaltung: Hero-Foto + Overlay, Schnellfakten-Pills, Karten-Sektionen,
Charakter als Chips, getabte Fotos/Gesundheit/Gewicht.
- Eltern (Vater/Mutter) als Links; Charakter-Karte klappt bei Status
Abgabe/Verstorben/Abgegeben ein (nur Zucht/Liebhaber offen).
- Gehege wird bei abgegebenen/verstorbenen Tieren ausgeblendet (Detail + Formular).
Listen:
- Infinite Scroll auf allen Listen (Rennmäuse, Würfe, Gehege, Verträge,
Anfragen, Kontakte) via useInfiniteList/useInfiniteSentinel; stabile
Sortierung mit id-Tiebreaker (keine doppelten Keys), Back-to-top-Button.
- Kontakte: Rolle-Filter (Züchter/Abnehmer) als Quick-Chips + Sticky-Header.
Zucht-Nachname:
- Namens-Anhängsel der Zucht in den Einstellungen + je Züchter-Kontakt
(Backend-Spalten + Migration); eigene Tiere zeigen „Name + Suffix".
- „Eigene Zucht" ist die Standard-Herkunft neuer Tiere.
Weiteres:
- Gehege-Bilder: Upload/Galerie auf der Gehege-Detailseite (Backend
EnclosurePhoto + Endpoints + Migration, geteilte Dateiablage).
- Toast-Rückmeldungen für alle Speichern-Aktionen.
- Checkboxen durch mobile-freundliche Toggle-Schalter ersetzt.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Filter-UX aus dem Design:
- Quick-Chip-Kategorien (Status/Geschlecht/Farbschlag/Herkunft) öffnen ein
Bottom-Sheet mit Mehrfachauswahl (suchbar bei Farbschlag/Herkunft); aktive
Filter als entfernbare Chips; Sortierung + "Externe Ahnen" als gleich große
Chips (Sortierung öffnet dasselbe Drawer).
- Neue zweizeilige Tierkarte (Name + Geschlechts-Kreis + Status-Badge, Meta-Zeile),
cardIn/Chip/Sheet-Animationen, Titel blendet beim Scrollen aus.
App-weit:
- Design-Palette (Creme/Braun/Terrakotta), Hanken-Grotesk-Schrift und gemeinsamer
--shadow-Token; obere "Rennmaus-Manager"-Titelleiste entfernt (mobil bleibt die
untere Aktionsleiste); fixe Shell-Höhe, nur der Hauptbereich scrollt.
Funktion:
- Infinite Scroll via useInfiniteList (seitenweise) + IntersectionObserver-Sentinel.
- Namensgenerator: Option männlich/weiblich/egal.
- Mehrfach-OR-Filter über gridify orGroup; e2e an die neue UI angepasst.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Neues FilterPanel-Muster (src/components/FilterPanel.tsx + filterPanel.css):
- Mobil (<768px): Suchfeld immer sichtbar; alle weiteren Filter hinter einem
'Filter (N)'-Button eingeklappt (Badge = Anzahl aktiver Nicht-Default-Filter).
Tippen öffnet/schließt den Drawer. 'Filter zurücksetzen' im Drawer.
- Desktop (>=768px): Toggle versteckt, Drawer als display:contents → alle Controls
inline in der bestehenden .filters-Flexzeile, wie bisher.
Auf GerbilsPage, WuerfeListPage und AnfragenPage ausgerollt.
BeckenPage/KontaktePage nur ein Suchfeld → kein Panel nötig.
e2e: filter-panel.spec.ts (8 Tests, phone+desktop); bestand/tiere/anfragen-Specs
erhalten openFilterPanel-Aufruf vor Drawer-Controls; fixtures.ts +openFilterPanel().
Vitest 78 / e2e 102 grün.
Mobil-Pass (390px, Durchsicht): Hauptproblem behoben. Beobachtete Restpunkte
für god (nicht selbst gefixed):
- .genotype-table in Genetik/Probeverpaarung: Genotyp-Strings können eng werden
- Stammbaum (react-d3-tree SVG): kein explizites mobile Viewport-Handling
- BreedingResultView Genotyp-Detail-Tabelle: ggf. horizontal scrollbar-los
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Neuer Bereich /webseite im Manager (Nav: "Mehr"-Blatt) zur Pflege der 6
Webseiten-Seiten gegen Pams WEB-0-Backend (LAN-only, keine Auth):
- Übersicht: alle Seiten mit Status (Entwurf/Veröffentlicht).
- Seiten-Editor: Titel + SEO-Beschreibung + Status speichern.
- Geordneter Block-Editor: Blöcke hinzufügen (Überschrift, Textabschnitt/
Markdown, Bild, Galerie, Kontaktangaben), bearbeiten, nach oben/unten
verschieben (PUT .../blocks/order), löschen. Die dynamische AbgabetiereList
ist nicht manuell anlegbar; nur ihr Einleitungstext ist editierbar.
Vertraege: GET /pages, GET /pages/{slug}, PUT /pages/{id},
POST /pages/{pageId}/blocks, PUT /blocks/{id}, DELETE /blocks/{id},
PUT /pages/{pageId}/blocks/order. Enums als Strings (Draft/Published, Heading...).
Deutsch via de.ts (LF beibehalten), mobile-first. e2e-Mock um die CMS-Routen
erweitert (zustandsbehaftet) + Smoke (Block anlegen/bearbeiten/verschieben,
bleibt erhalten). Gate gruen: build, eslint, vitest 69, e2e 68.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HilfePage.tsx: accordion via <details>/<summary> (native, accessible, mobile-first);
9 sections in du-Form: Erste Schritte, Rennmaeuse, Becken, Wuerfe, Genetik,
Stammbaum, Abgabe-Ablauf, Statistik, Datensicherung. All UI labels imported from
de.ts (rename-safe); SCHECKE/Rex warnings verbatim from genetics.warnings.
de.ts: de.nav.help + de.hilfe (title, subtitle, 9 section keys)
AppShell.tsx: Hilfe (heart question mark) added to SECONDARY nav (Mehr-sheet on phone,
sidebar on desktop)
App.tsx: Route path=hilfe -> <HilfePage />
hilfe.css: accordion, hint boxes, warning colour; piggybacks on existing CSS vars
e2e/hilfe.spec.ts: 3 smoke tests x 2 viewports = 6 new tests
-- Hilfe reachable via nav
-- First accordion section expands on click
-- SCHECKE warning visible in Genetik section
Full evidence: dotnet test 18/18, npm test 47/47, npm run build ok,
npm run e2e 54/54 (48 existing + 6 new hilfe tests).
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>