Commit Graph
328 Commits
Author SHA1 Message Date
adminandClaude Opus 5.5 c474cd64ed feat: App-Store-Link und -Badge (iOS-App live)
APP_STORE_URL gesetzt (/app leitet iPhones jetzt in den App Store),
offizielles Apple-Badge neben Google Play auf Landingpage, /r/[code]
und im Footer; Texte nennen iPhone und Android.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-08 15:46:41 +02:00
adminandClaude Opus 5.5 1c6c95955e refactor: relative Zeitangaben in einer Funktion, Stripe-Client per getStripe() (Deploy B)
- W6: formatRelative(date, t, "compact" | "verbose") ersetzt
  formatRelativeCompact/formatRelativeVerbose (nur die Übersetzungsschlüssel
  unterschieden sich). Test hält beide Formen und die Grenzen fest.
- W7: getStripe() statt eines Proxy-Objekts um den faulen Singleton; der
  Client entsteht weiterhin erst beim ersten Aufruf (fehlender Key bricht den
  Import nicht). Sponsor-Router, Webhook und Mock angepasst.

W5 (Bild-/Musik-URLs zusammenführen) bewusst nicht umgesetzt.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 14:21:11 +02:00
adminandClaude Opus 5.5 5dbde04c6f refactor: Cron-Prüfung gemeinsam, node:crypto, tote Dateien und vercel.json raus (Deploy A)
- W1: isCronAuthorized(req) in src/lib/timing-safe-compare.ts statt fünf
  gleicher authorized()-Kopien in den Cron-Routen.
- W2: src/lib/auth.ts (getOwnerId) und src/hooks/useActivePet.ts gelöscht —
  keine Aufrufer.
- W3: vercel.json gelöscht (Crons laufen per NAS-Crontab), Vercel-Kommentare
  in den Cron-Routen entfernt, CLAUDE.md/README angepasst.
- W4: timingSafeStringEqual nutzt crypto.timingSafeEqual (Byte-Vergleich);
  proxy.ts läuft in Next 16 auf Node.

Tests für Vergleich und Cron-Prüfung; Produktions-Build lokal grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 13:27:33 +02:00
adminandClaude Opus 5.5 32ed02d153 refactor(legal): Anti-Crawl-Schutz der Betreiberdaten lint-sauber (W9)
Impressum, Datenschutz und Kinderschutz zeigen Name/Adresse/E-Mail weiterhin
erst im Browser (Server-HTML nur Platzhalter). Statt setState im useEffect
jetzt useSyncExternalStore (useRevealed, src/components/legal/
revealed-contact.ts) — gleiche Wirkung, keine react-hooks-Lint-Fehler mehr;
decode-Helfer nur noch einmal. Test hält den Schutz fest (Server-HTML ohne
Klardaten, Browser mit). Dazu falsche schließende Anführungszeichen in
Kinderschutz korrigiert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 08:25:33 +02:00
adminandClaude Opus 5.5 5cf3deb006 fix(admin): Warteschlange nach Schwere, Verifizierung atomar, weniger Doppelcode
- listReports lädt die schwersten Meldungen zuerst und füllt je Stufe bis
  100 auf — eine ältere schwere Meldung fällt nicht mehr hinter viele neue
  leichte zurück.
- reviewVerificationRequest: der Status-Wechsel (updateMany auf PENDING) ist
  die Sperre; entscheiden zwei Moderatoren gleichzeitig, bricht der zweite
  ohne Haken, Log und Nachricht ab.
- countCreatedOverTime für die drei gleichen Dashboard-Zeitreihen.
- clerkProfiles (src/lib/admin/clerk-profiles.ts) statt drei eigener
  Clerk-Abfragen in Nutzerliste, Team & Rollen und Protokoll.
- Tier-Detail: eigenes Neuladen entfällt (AdminAutoRefresh übernimmt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:39:02 +02:00
adminandClaude Opus 5.5 de6b36e3d3 refactor(admin): admin.ts nach Bereichen aufteilen, getActiveAds in den ads-Router
- Die 63 Admin-Prozeduren liegen jetzt nach Bereich in
  src/trpc/routers/admin-procedures/ (system, dashboard, posts, blacklist,
  users, verification, team, reports, ads, music) plus guards.ts und
  post-actions.ts. admin.ts setzt trpc.admin.* daraus zusammen (34 statt
  ~1800 Zeilen); API, Importe und Test-Mocks von assertAdmin bleiben gleich.
- getActiveAds war eine Nutzer-Prozedur für den Feed: jetzt
  trpc.ads.getActiveAds (FeedList, Tests nach ads.test.ts).

Inhalt der Prozeduren unverändert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:32:30 +02:00
adminandClaude Opus 5.5 fc740987cd docs(legal): Mindestalter einheitlich 16 Jahre (Datenschutz, AGB, Kinderschutz)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:18:44 +02:00
adminandClaude Opus 5.5 77cdebec0f feat(admin): Melde-Statistik — wer oft gemeldet wird und wer oft falsch meldet
- Neue Tabelle ReportDecision (nur anhängen, ohne Fremdschlüssel): jede
  Moderationsentscheidung zu einer Meldung (bestätigt/abgewiesen) mit
  Melder und gemeldeter Seite als Momentaufnahme.
- Geschrieben von dismissReport, deletePostAsModerator, dem neuen
  upholdReport (Profil-Meldungen bestätigen) und petAdmin.delete.
- Auswertung: Abzeichen und Zahlen in der Nutzerliste, im Tier-Detail und
  in der Warteschlange („Melder: häufige Fehlmeldungen“), Tab „Auswertung“
  auf der Meldungen-Seite mit Ranglisten.
- Datenschutz: Tier gelöscht → Tier-/Beitrags-IDs anonymisiert, Konto
  gelöscht → Owner-IDs anonymisiert, Zählung der Gegenseite bleibt. Teil der
  Art.-15-Auskunft (ohne Gegenseite), Abschnitt 4e in der
  Datenschutzerklärung.

Braucht vor dem Deploy: prisma db push + prisma/enable_rls_report_decisions.sql.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 07:16:08 +02:00
adminandClaude Opus 5.5 0880d87f31 fix(admin): Werbe-Dialog wächst bei vielen Rassen nicht mehr über den Bildschirm
Der Dialog scrollt jetzt selbst (max. 90 % der Fensterhöhe), und die
Rassenliste je Tierart hat einen eigenen Scrollbereich. Bei Hunden (~300
Rassen) lagen Speichern und Abbrechen bisher außerhalb des Bildschirms.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 06:55:37 +02:00
adminandClaude Opus 5.5 5f75998512 feat(admin): nach jeder Aktion automatisch neu laden
AdminAutoRefresh im Admin-Layout lädt nach jeder erfolgreichen Mutation
alle sichtbaren Daten neu (invalidateQueries, nur aktive Queries holen
sofort neu). Damit braucht keine Admin-Seite mehr F5, auch die, die bisher
nach manchen Aktionen gar nicht neu geladen haben. Die ~35 einzelnen
invalidateQueries-Aufrufe in den Seiten entfallen. Gilt nur im Admin-Panel.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-07 05:50:05 +02:00
adminandClaude Opus 5.5 20d6888449 refactor(admin): gemeinsamer Log-Helfer, eine Rollenprüfung, weniger Doppelcode
- modLog (src/lib/admin/mod-log.ts) ersetzt 43 handgeschriebene
  moderationLog.create-Blöcke in admin, pet-admin, referral-admin,
  curator-admin und lost-report-admin.
- resolveAdminRole ersetzt die dreifache Prüfung ADMIN_OWNER_ID/AdminRole
  (isAdmin, assertAdmin, getMyRole).
- updatePostLogged für hidePost, unhidePost, setAiDisclosure, pinPost,
  unpinPost; deletePostAsModerator für deletePost und deleteReportedPost.

Keine Verhaltensänderung, bestehende Tests unverändert grün.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:43:56 +02:00
adminandClaude Opus 5.5 42eff68a29 fix(admin): Beiträge mit Paw-Backs löschbar, Rangschutz, saubere Fehler
- postDeleteOps (src/lib/delete-post.ts): Paw-Backs anderer Tiere und deren
  Meldungen fallen beim Löschen mit weg. Repost.originalPost hat kein Cascade
  und blockierte bisher das Löschen solcher Beiträge, im Admin-Panel
  (deletePost, deleteReportedPost) wie in posts.delete.
- assertCanModerate: sperren, shadowbannen, verwarnen und Konto löschen nur
  gegen niedrigere Ränge; das Bootstrap-Konto (ADMIN_OWNER_ID) ist tabu.
- unbanUser, removeShadowban, revokeRole: fehlender Eintrag gibt NOT_FOUND
  statt eines 500-Fehlers.
- deleteBlacklistEntry schreibt einen Audit-Log-Eintrag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 20:30:11 +02:00
adminandClaude Opus 5.5 6d2d2f19a5 fix: Vorschau-Bots nicht als /app-Klick zählen, Meldungen beim Löschen eines Beitrags mitlöschen
- /app: Link-Vorschauen (Meta/Threads/Instagram, TikTok, WhatsApp, Crawler)
  werden weitergeleitet, aber nicht mehr in der Download-Statistik gezählt
- Reports zu einem Beitrag werden jetzt auch beim Admin-Löschen, beim Löschen
  durch den Besitzer und beim Entfernen einer Pfote zurück mitgelöscht

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 14:20:13 +02:00
adminandClaude Opus 5.5 cccda31525 feat: Mitbetreuer – ein zweites Konto kümmert sich mit um ein Tier
- Neue Tabelle PetCaretaker (max. 1 pro Tier): Code einladen (7 Tage) ->
  einlösen (PENDING) -> Freigabe durch den Haupt-Besitzer (ACTIVE)
- assertPetOwnership erlaubt aktiven Mitbetreuer; assertPetMainOwner für
  Löschen, Privatsphäre, Gedenkmodus, Gesundheits-Einwilligung
- pets.list/byId inkl. mitbetreuter Tiere mit Rolle; Push auch an die Geräte
  des Mitbetreuers; Konto-Löschung übergibt das Tier an ihn
- Web: Verwaltung in "Profil bearbeiten", Code einlösen unter "Meine Tiere"
  und im Onboarding, Link /mitbetreuen/<code>, Mitteilung CARETAKER_REQUEST
- App: dieselben Abläufe (OTA-fähig, Teilen statt Kopieren)
- Datenschutz: Absatz zum Mitbetreuer

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 12:23:34 +02:00
adminandClaude Opus 5.5 55e5ab747a docs(legal): Datenschutz um Push-Mitteilungen und Vermisst-Seite ergänzt
- Neue Abschnitte 4c (Push-Mitteilungen: Push-Token pro Gerät, Versand über
  Expo/Apple/Google, Einwilligung im Systemdialog, Löschung) und 4d
  (Vermisst-Meldung, öffentliche Seite, Suchmaschinen nur nach Einwilligung,
  Kontaktdaten freiwillig und nur eigene, Löschung bei Ende der Meldung)
- Abschnitt 2, 4 (Expo, Apple, Google mit DPF) und 6 ergänzt
- Formular-Hinweis: nur eigene Kontaktdaten eintragen (Web + App)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:46:56 +02:00
adminandClaude Opus 5.5 95d39ca1b8 feat: Push an mehrere Geräte, Abmelden trägt das Gerät aus
- Neue Tabelle PushToken (ein Eintrag pro Gerät, Token eindeutig, max. 10
  pro Konto); owner.registerPushToken trägt zusätzlich ein statt zu
  überschreiben, optional mit Plattform (alte App-Versionen funktionieren)
- Versand an alle Geräte in einem Expo-Batch; Tokens mit
  „DeviceNotRegistered“ werden gelöscht; Owner.expoPushToken wird bis zur
  Übernahme nur noch mitgelesen (Übernahme per enable_rls_push_tokens.sql)
- App: meldet Plattform mit und trägt das Gerät beim Abmelden aus
  (clearPushToken mit Token), damit kein Push mehr fürs alte Konto kommt
- Vermisst: Telefon/E-Mail werden beim Finden bzw. Schließen gelöscht
  (Datensparsamkeit); Altbestand per cleanup_lost_report_contacts.sql

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 11:30:01 +02:00
adminandClaude Opus 5.5 ce8d478ecd feat(admin): Verwaltungs-Seiten im neuen Design (Etappe 3, Paket 3)
- Team & Rollen: Personen mit Name/E-Mail statt Clerk-ID, Rollen-
  Erklärung, „Person hinzufügen“ per Suche nach Name/E-Mail statt
  eingefügter user_-ID, deutsche Dialoge; listModerators liefert Namen
- Mitteilungen, Rechtstexte, Begrüßung, Empfehlungen: einheitlicher
  Seitenkopf, „Halter“ statt „Owner“

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:56:10 +02:00
adminandClaude Opus 5.5 c474e66106 feat(admin): Inhalts-Seiten im neuen Design (Etappe 3, Paket 2)
- Beiträge: Suche + Spaltenwahl beschriftet, „Kommentare“ plus Menü „…“
  mit beschrifteten Aktionen statt sechs Symbolen und Hilfe-Popover,
  Pfoten/Kommentare/Meldungen als Symbole statt Emojis, komplett deutsch;
  Spaltenzahl per useSyncExternalStore (behebt Lint-Fehler setState im Effect)
- Tiere: Seitenkopf, „Halter“ statt „Owner“; Detailfenster zeigt Name und
  E-Mail des Halters (Clerk) und deutsche Tierart/Rasse
- Werbung, Musik: Seitenkopf mit Aktion, Statusabzeichen, deutsche Texte

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:48:49 +02:00
adminandClaude Opus 5.5 cc9db2ea05 feat(admin): Moderations-Seiten im neuen Design (Etappe 3, Paket 1)
- Meldungen: Seitenkopf, Statusabzeichen für Schwere/Grund, beschriftete
  Knöpfe „Abweisen“/„Beitrag löschen“, gemeldete Profile mit Tiername
  statt ID (listReports liefert targetPet), Pfoten statt Likes
- Verifizierung: komplett deutsch, Status-Filter mit Beschriftung,
  Tierart/Rasse deutsch aus den Übersetzungstabellen (taxonomy-name)
- Vermisst: Seitenkopf, Status-Tabs, Statusabzeichen
- Nachrichten: passende Höhe im neuen Rahmen, am Handy Liste ODER
  Verlauf mit Zurück-Knopf, Menü-Zähler sofort aktualisiert
- Sperrliste: deutsch, Filter beschriftet, kompakte Liste

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:39:23 +02:00
adminandClaude Opus 5.5 ed0b4666bc fix(admin): Protokoll formuliert Aktionen am eigenen Konto natürlich
„hat die eigenen Daten exportiert“ statt „die Daten von sich selbst“.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:28:27 +02:00
adminandClaude Opus 5.5 0504d284e2 fix(admin): Halter-Spalten verrutschen nicht mehr, Lade-Kreis ohne Scrollbalken
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:25:24 +02:00
adminandClaude Opus 5.5 30473f886d fix(admin): Protokoll schreibt „sich selbst“ statt doppeltem Namen
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:08:41 +02:00
adminandClaude Opus 5.5 4187a1a519 feat(admin): Protokoll in Klartext und Halter als Tabelle
Protokoll (/log):
- Jeder Eintrag als deutscher Satz mit Namen statt Codes/IDs, z. B.
  „Daniel hat Jessi den Rang Botschafter verliehen“; Begründungen
  bereinigt (Rang-IDs, Feldlisten, JSON, true/false übersetzt)
- Nach Tagen gruppiert, Filter nach Bereich (Chips) und Teammitglied
  (Auswahl mit Namen); Codes, IDs und IP unter „Technische Details“
- Neuer Router adminAudit (list, moderators) löst Tiere, Halter (Clerk),
  Beitrags-Autoren, Werbung, Musik und Ränge auf; formatAuditEntry
  übersetzt alle rund 50 Aktionen. admin.getModerationLog entfernt (ungenutzt)

Halter (/users):
- Tabelle mit Name/E-Mail, Tieren, Status-Abzeichen und Rolle statt
  Clerk-ID und sechs Knöpfen pro Zeile; Eingriffe im Menü „…“,
  Details (Registrierung, Sperrgrund, Konto-ID) zum Aufklappen
- Dialoge deutsch und in eigener Komponente UserActionDialogs

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 08:05:41 +02:00
adminandClaude Opus 5.5 e42831cfa9 fix(admin): Feinschliff nach Live-Prüfung des neuen Admin-Panels
- Website-Theme (next-themes) überschrieb dark/color-scheme auf <html>
  nachträglich → dunkler Scrollbalken, schwarzer Schalter im Hellmodus;
  Admin-Wahl wird jetzt per MutationObserver durchgesetzt
- Trend bei kleiner Vergleichsbasis absolut (+15 statt +1500 %)
- Diagramm: ganzzahlige y-Achse, keine überlappenden Datumslabels, breiter
- Suche: Tierart als Unterzeile, Kürzel „/“ nur bei leerem Feld
- Kopfzeile: Platzhalter statt „Admin“, solange der Name lädt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 07:49:56 +02:00
adminandClaude Opus 5.5 c4fb9aefb7 feat(admin): neuer Rahmen und Startseite für das Admin-Panel
- Sidebar nach Aufgaben gruppiert (Moderation, Inhalte, Community,
  Verwaltung), komplett deutsch, mit Zählern für offene Meldungen,
  Verifizierungen, Vermisst-Meldungen und ungelesene Team-Nachrichten
- Kopfzeile mit Breadcrumb, globaler Suche (Tiere + Halter), Hell/Dunkel
  und eigenem Namen + Rolle; hell als Standard, Wahl im Cookie
- Startseite: Kennzahlen mit Vorwochen-Vergleich, Moderationsstatus mit
  Wartezeit, letzte Eingänge, Schnellzugriff, Notschalter als Schalter,
  ein Aktivitäts-Diagramm mit Umschalter statt fünf Karten
- Eigene Admin-Farbtokens (Orange in beiden Modi, Statusfarben); alle
  Admin-Seiten von festen Dunkel-Klassen auf Tokens umgestellt
- Neuer Router adminOverview (getQueues, getKpis, getInbox, search)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 07:30:16 +02:00
adminandClaude Opus 5.5 0fc7503c08 feat: Vermisst — Admin-Übersicht und Ansicht direkt in der App
- Admin „Vermisst“ (/p/[secret]/lost): alle Meldungen mit Status, Ort/Zeit,
  Reichweite (benachrichtigte Tiere), freigegebenen Kontaktwegen; Moderatoren
  schließen vergessene Meldungen (ohne Entwarnung) oder löschen Missbrauch,
  beides im Audit-Log (CLOSE_/DELETE_LOST_REPORT)
- App: eigener Vermisst-Bildschirm /lost/[petId] mit Foto, Merkmalen,
  Kontakt (Anrufen, E-Mail, Nachricht in PawFeed), Fotos und Teilen —
  Profil-Banner und Push-Tipp auf PET_LOST führen dorthin statt in den Browser
- lostReports.byPet + gemeinsamer Lader loadLostReportView für App und
  öffentliche Seite

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 19:17:04 +02:00
adminandClaude Opus 5.5 9b52a20a15 feat: Vermisst-Meldung mit öffentlicher Teilen-Seite
- LostReport (eine aktive Meldung je Tier) + Pet.lostSince als Cache fürs
  Abzeichen, NotificationType PET_LOST/PET_FOUND
- lostReports.create/update/markFound/mine: nur Besitzer, nicht im
  Gedenkmodus, höchstens eine Meldung pro 24 h, Zeitpunkt nicht in der Zukunft
- Mitteilung + Push an Follower und gefolgte Tiere, je Besitzer einmal, eigene
  Tiere nie; optionale Entwarnung bei „Wieder da!“
- Öffentliche Seite /vermisst/<code> ohne Login: Foto, Merkmale, zuletzt
  gesehen, Kontakt nach Wahl des Besitzers (Telefon/E-Mail/PawFeed),
  Fotos aus dem Feed, Vorschaubild; Suchmaschinen nur wenn erlaubt;
  nach dem Finden „Wieder sicher zu Hause“ ohne Kontaktdaten
- Web + App: Melden ganz oben in „Profil bearbeiten“, Vermisst-Banner im
  Profil, Abzeichen neben dem Namen, Mitteilungen führen zum Profil

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:53:31 +02:00
adminandClaude Opus 5.5 1ea3a7172d fix: Gedenkmodus-Texte behutsamer formuliert
„Über die Regenbogenbrücke gegangen“ statt „verstorben“, Anteilnahme im
Bestätigungsdialog, „Tag des Abschieds“, „Profil als Erinnerung bewahren“ —
Web, App, Mitteilung und Push-Text.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:41:24 +02:00
adminandClaude Opus 5.5 9216f0c702 feat: Gedenkmodus „In Erinnerung“ für verstorbene Tiere
- Pet.deceasedAt + NotificationType.PET_DECEASED (additiv)
- pets.setDeceased: nur Besitzer, kein Datum in der Zukunft, rückgängig
  machbar; beim ersten Setzen einmalig stille Mitteilung (ohne Push) an
  Follower anderer Besitzer
- Verstorbene Tiere bekommen keine Geburtstags-/Adoptionstag-Mitteilungen,
  Impf-Erinnerungen und Tier-Vorschläge mehr und werden nicht vorgeschlagen
- Abzeichen „In Erinnerung“ (Ribbon) überall neben dem Namen, Gedenk-Banner
  im Profil, Bereich „Gedenkmodus“ in „Profil bearbeiten“ (Web + App)
- Mitteilung führt zum Profil des Tiers (Liste + Push-Tap)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 18:24:08 +02:00
adminandClaude Opus 5.5 4cf28213ff feat: Jede Mitteilung fuehrt direkt zum Ziel (Liste + Push)
Tester-Wunsch 04.10.: „Benachrichtigung bringt dich auf kuerzestem Weg
dorthin, wo es wichtig ist“.
- Nachrichten: notifications.list liefert die conversationId (ueber das
  sortierte Tier-Paar), App und Web oeffnen direkt das Gespraech.
- Push-Mitteilungen: Antippen fuehrte bisher nur in die App. Jetzt
  useNotificationTapRouting (useLastNotificationResponse, auch Kaltstart),
  wechselt bei Bedarf aufs betroffene eigene Tier. Push-Daten tragen dafuer
  petId, actorPetId, storyId, conversationId.
- Eine gemeinsame Zielregel fuer Liste und Push (mobile/src/lib/
  notification-route.ts, getestet): Nachricht → Gespraech, Kommentar/Pfote/
  Erwaehnung/Paw-Back → Beitrag, Story → Story, Follow/Vorschlag → Profil,
  Geburtstag/Adoption/Impfung → Gesundheit, Rang → Einladen.
- Bei Nachrichten und Storys fuehrt auch der Name zum Ziel; Story-Viewer
  laedt Name/Bild nach, wenn er per Push geoeffnet wird.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:52:23 +02:00
adminandClaude Opus 5.5 8aca1bc28e fix: Story-Mitteilung oeffnet direkt die Story, nie mehr Code-Namen als Text
Tester-Feedback 04.10. (STORY_MENTION):
- Web: Klick oeffnet die Story als Overlay an der richtigen Stelle
  (initialStoryId) statt ueber das Tierprofil; abgelaufen → Profil.
- App: ganzer Eintrag inkl. Name oeffnet die Story, Viewer springt per
  storyId direkt zur markierten Story.
- Unbekannte Mitteilungstypen zeigen einen neutralen Text statt des
  Code-Namens (der rohe Text kam von einem Geraet mit aelterem App-Stand).
- Keine Markier-Mitteilung an eigene Tiere des Erstellers (petsOfOtherOwners).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 17:28:15 +02:00
adminandClaude Opus 5.5 5676065400 feat(web): Story-Overlays und Text-Storys im Web-Viewer anzeigen
Web zeigt Story-Editor-Inhalte nur an (Entscheidung: kein Web-Editor):
StoryOverlayLayer mit gleicher Geometrie/Groessen wie die App, Text-Storys
mit Farbverlauf, Markierungen fuehren ins Tierprofil, Notschalter beachtet.
Mitteilungen STORY_MENTION/COMMENT/REACTION, Hinweistexte: Follower-
Sichtbarkeit regelt jetzt auch das Markieren.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 16:47:25 +02:00
adminandClaude Opus 5.5 795a39355e feat(stories): Overlay-Spiegel fuer die App + Paritaetstest
- mobile/src/lib/story-overlays.ts + story-stickers.ts (identisch zu src/lib,
  plus Gesten-Geometrie fuer den Editor); Paritaetstest liegt in mobile/
  (Docker-Build kennt mobile/ nicht)
- Rueckgaben von create/createText/getVideoStatus ohne overlays-JSON und
  flacher Eingabetyp: Prismas rekursiver JsonValue- bzw. der tiefe Zod-Typ
  sprengten sonst den App-Compiler (TS2589)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 16:31:14 +02:00
adminandClaude Opus 5.5 8c640e2837 feat(stories): Server fuer Story-Editor (Overlays, Markierungen, Text-Storys, GIFs)
- Schema: Story.overlays/background/mediaWidth/mediaHeight, StoryMediaType.TEXT,
  StoryMention, Notification.storyId, STORY_MENTION/COMMENT/REACTION
- Markieren nach Pet.followerVisibility (Entscheidung 04.10.), Block-Pruefung,
  eigene Tiere immer erlaubt; unzulaessige Markierungen werden verworfen
- stories.createText, searchMentionable, searchGifs (GIPHY-Proxy, rating g,
  Redis-Cache), featureFlags; Kommentar/Pfote benachrichtigen den Besitzer
- Video-Storys benachrichtigen Markierte erst bei READY (Mux-Webhook)
- Notschalter Story-Editor im Admin-Dashboard (Redis, ModerationLog)
- RLS-Skript fuer StoryMention

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 16:27:34 +02:00
adminandClaude Opus 5.5 c7c1473e2a feat: Profil-Ring (PROFILE_RING) ueberall am Tierbild + Story-Ring in Rangfarbe
found.md: Testnutzer mit "Goldene Pfote" (PROFILE_RING) sah keinen Ring —
der Ring war bewusst nur im Profilkopf. Auf Wunsch jetzt ueberall:
- petIdentitySelect liefert owner.referralRankId/referralPerks mit
  (referralOwnerSelect), ebenso search, explore.listPets und pets.list.
- Web: RankRing (box-shadow, keine Layoutverschiebung) um Avatare in Feed,
  Kommentaren, Suche, Explore, Nachrichten, Mitteilungen, Follower-Listen,
  Meilenstein-/Repost-Karten, Story-Zuschauern/-Kommentaren; PetAvatar
  nimmt owner.
- App: PetAvatar mit owner zeichnet den Ring als Overlay; an allen
  Listen/Karten uebergeben.
- PawRing (Web + App): ungesehene Story-Pfote in Rangfarbe statt Orange,
  ohne Story schlichter Ring in Rangfarbe; gesehene Storys bleiben grau.
Sponsor-Felder im Kartentyp optional (Identity-owner traegt nur den Rang).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:12:07 +02:00
adminandClaude Opus 5.5 dbc416c43c feat(admin): Tierliste zeigt alle erreichten Raenge
found.md: Admin -> Tiere zeigte nur den hoechsten Rang (Cache referralRankId).
Die Liste laedt jetzt alle ReferralRankAchievements des Owners (nach Schwelle)
und zeigt sie als Symbole, unabhaengig vom Vorteil NAME_BADGE; entzogene
Raenge ausgegraut, Tooltip mit 'manuell vergeben' / 'entzogen'.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 15:01:51 +02:00
adminandClaude Opus 5.5 81e617ece5 fix(admin): Melder-Nachricht grammatisch korrekt (zum Beitrag/Profil)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:16:48 +02:00
adminandClaude Opus 5.5 f51b158989 feat(admin): Team-Nachricht an Melder bei abgewiesenem/bestaetigtem Report
found.md: Abweisen schickt dem Melder eine Team-Nachricht mit optionaler
Begruendung (auch im ModerationLog), Loeschen eines gemeldeten Beitrags
dankt jedem Melder einmal. Beides per Schalter im Admin-Dialog abschaltbar,
best-effort nach der Moderation. Die interne Loesch-Begruendung geht nicht
an den Melder.

Fix nebenbei: deleteReportedPost loescht jetzt die Reports des Beitrags
(targetPostId ist keine Relation, sie blieben bisher verwaist in der Liste).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:09:39 +02:00
adminandClaude Opus 5.5 7ab74f8077 fix: Datums-Test nach mobile/ verschieben (Docker-Build ohne mobile/)
Der Test in src/__tests__ importierte aus mobile/, das .dockerignore und
die Web-tsconfig ausschliessen -> next build brach beim Typecheck ab.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:50:35 +02:00
adminandClaude Opus 5.5 c74bbffccb feat: Datumseingabe TT.MM.JJJJ (App) + Erinnerung an Folgeimpfung
Datum (found.md, iOS-Feedback): neue DateField-Komponente mit Ziffern-
tastatur und automatisch eingefügten Punkten (TT.MM.JJJJ) für Geburtstag,
Adoptionstag, Gewicht, Tierarzt und Impfungen. Parser akzeptiert weiter
"-" und "/" sowie einstellige Tage/Monate. Wheel-Picker bleibt in
PENDING-NATIVE-BUILD.md (natives Modul).

Impf-Erinnerung (found.md, iOS-Feedback): täglicher Cron
/api/cron/vaccine-reminders erinnert einmalig 30 Tage vor
Vaccine.nextDueDate per Mitteilung (VACCINE_DUE) + Push mit Impfname und
Datum. Schema: NotificationType.VACCINE_DUE, Notification.vaccineId
(Cascade), Vaccine.reminderSentAt (Claim gegen Doppelversand, wird bei
Fehler wieder freigegeben). Web + App zeigen die Mitteilung an, Tippen
öffnet die Gesundheitsseite. App: Schnellauswahl +1/+3 Jahre für den
Folgetermin plus Hinweis auf die Erinnerung.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 12:42:20 +02:00
adminandClaude Opus 5.5 f112d36f4a feat: neues Tier folgt dem Kurator-Tier zurück
Bisher folgte nur "PawFeed Team" dem neuen Tier, der Feed des neuen
Nutzers startete leer. welcomeNewPet legt jetzt auch den Follow in
Gegenrichtung an und befüllt den Redis-Feed per backfillOnFollow mit den
letzten Team-Posts. Gleicher Admin-Schalter (autoFollowEnabled),
Beschriftung im Kurator-Panel angepasst.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-03 19:49:46 +02:00
adminandClaude Opus 5.5 f1ccc78005 feat: Dashboard-Kachel Tierarten klappbar (standardmäßig zu, Zustand gemerkt)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:31:09 +02:00
adminandClaude Opus 5.5 9adba560a7 feat: Admin-Dashboard — Verteilung der Tiere nach Tierart und Rasse
- petAdmin.speciesDistribution: Anzahl pro Tierart (auch Arten ohne Tiere)
  und pro Rasse, deutsche Namen, ohne das Kurator-Tier „PawFeed Team“
- Karte "Tierarten" auf dem Dashboard: sortiertes Balkendiagramm (eine
  Reihe, Markenorange), Anzahl + Anteil als Text, Tooltip bei Hover/Fokus,
  Klick klappt die Rassen der Art auf

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:17:21 +02:00
adminandClaude Opus 5.5 566a2eedd0 feat: Admin-Tierverwaltung + vollständiges Löschen von Tieren
Admin /p/[secret]/pets:
- Liste mit Suche (Name, Spitzname, Empfehlungscode, ID), Filtern und
  Sortierung; Detail-Panel mit Kennzahlen, Owner, Profiltexten, Beiträgen
- Verifiziert / Gründungsmitglied pro Tier umschalten
- Empfehlungsränge des Owners: dauerhaft entziehen (bis zur Freigabe),
  freigeben oder manuell vergeben (besondere Leistung)
- Inhalte leeren (Profilbild, Bio, Adoptionsgeschichte, Spitzname) und Tier
  löschen — jeweils mit Begründung, Owner per Team-DM informiert bzw. per
  E-Mail, wenn es sein einziges Tier war

Ränge: ReferralRankAchievement bekommt revokedAt/revokeReason/
grantedManually; entzogene Ränge werden weder neu vergeben noch im Cache
berücksichtigt (awardRanks, recomputeReferralRanks, referrals.me, Export).

Fix: deletePetCompletely (src/lib/delete-pet.ts) für Owner-Löschen,
Kontolöschung und Admin — vorher konnte das Löschen an StoryView/Report/
Repost-Fremdschlüsseln ohne Cascade scheitern, und die Zähler an fremden
Beiträgen/Storys wurden nicht verringert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 21:03:05 +02:00
adminandClaude Opus 5.5 94c143e17d feat: Download-Weiterleitung /app für Flyer und QR-Codes (D3)
- Android → Google Play, UTM-Werte als Play-referrer (Play Console)
- iPhone → App Store, sobald APP_STORE_URL gesetzt ist (D5); bis dahin Web
- sonst → Web-Startseite; ?ref=<Code> führt auf die Einladungsseite /r/<Code>
- nur sichere UTM-Werte werden übernommen, nie gecacht (302, no-store)
- Zählung pro Tag/Quelle/Gerät in Redis, Anzeige im Admin unter
  Empfehlungen → Übersicht (30 Tage)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:39:40 +02:00
adminandClaude Opus 5.5 307b76e8fd feat: Gründungsmitglieder (erste 250 Tiere) und Kurator-Begrüßung
- Pet.isFoundingMember: in pets.create per bewachter SQL-Anweisung für die
  ersten 250 Tiere gesetzt, nie entzogen; Abzeichen (Spross) in Feed und
  Profil, Web + App, unabhängig vom Empfehlungsprogramm
- Kurator "PawFeed Team": folgt jedem neuen Tier und schickt eine
  Begrüßungs-Direktnachricht (DE/EN nach Sprache beim Anlegen), im
  Hintergrund — blockiert das Onboarding nicht
- CuratorSettings + Admin-Seite /p/[secret]/curator: Schalter, Texte mit
  Pflicht-Platzhalter {name} und Vorschau, Zähler der Gründungsmitglieder
- prisma/backfill_founding_members.sql, prisma/enable_rls_curator.sql

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 20:26:43 +02:00
adminandClaude Opus 5.5 2c0680cef9 feat: 7-Tage-Ansicht der eigenen Storys für den Ersteller (Web + App)
- stories.listActive: der Owner bekommt seine Storys der letzten 7 Tage mit
  isExpired, alle anderen weiterhin nur die aktiven 24 Stunden
- stories.hasActiveStory: optional includeOwnRecent → grauer Ring
  (expiredOnly) nur in der Story-Leiste, nicht an Profilbildern im Feed
- Viewer: Hinweis "Abgelaufen – nur für dich sichtbar", Kommentare bei
  abgelaufenen Storys nur lesbar, Pfote nur als Zähler; Löschen und
  Zuschauerliste bleiben

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 19:30:22 +02:00
adminandClaude Opus 5.5 a01c58e5f0 feat: Aufräum-Job für Storys älter als 7 Tage
- /api/cron/cleanup-stories (CRON_SECRET): löscht Storys älter als 7 Tage
  samt Aufrufen, Pfoten, Kommentaren, Supabase-Bild und Mux-Asset; max. 200
  pro Lauf, Fehler einzelner Storys brechen den Lauf nicht ab
- gemeinsame Funktion deleteStoryWithMedia (src/lib/story-cleanup.ts), auch
  von stories.delete genutzt
- Datenschutzerklärung Abschnitt 6: Storys spätestens nach 7 Tagen gelöscht
- Datenexport: Story-Block heißt "stories" statt "activeStories"
- CLAUDE.md: Cron-Jobs laufen über die NAS-Crontab + docker/run-cron.sh
  (der alte Hinweis "nichts ruft trim-feeds auf" war veraltet)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-02 16:43:07 +02:00
adminandClaude Opus 5.5 81d9c6da30 feat: eigene Story löschen (Web + App)
- stories.delete: nur Owner; Aufrufe, Story (Pfoten/Kommentare per Cascade),
  danach Bild in Supabase Storage bzw. Mux-Asset best-effort
- Web: Papierkorb neben der Zuschauerzahl + Rückfrage, Timer pausiert,
  gelöscht wird die beim Antippen gemerkte Story
- App: gleicher Papierkorb mit nativer Rückfrage, Timer pausiert
- found.md: Nutzer-Feedback "Story löschbar" erledigt, 7-Tage-Wunsch mit
  Befund als offener Punkt

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 21:17:16 +02:00
adminandClaude Opus 5.5 ccebfa46a6 feat: Story-Kommentare, Pfoten und Zuschauerliste in der App
- App-Viewer: Pfote, Kommentar-Sheet (schreiben, eigene löschen) und für die
  eigene Story die Zuschauerliste; Timer pausiert, solange ein Sheet offen ist
- stories.listActive: optionales viewerPetId → reactedByViewer, damit die
  Pfote gefüllt startet (abwärtskompatibel für alte App-Builds)
- Web-StoryPawButton nutzt reactedByViewer ebenfalls und wird pro Story
  neu initialisiert

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-01 20:59:02 +02:00