Commit Graph
36 Commits
Author SHA1 Message Date
admin 1965179d36 fix(landing): update securityheaders.com footer badge to A+
Rescan came back A+ (up from A) after the strict-CSP change.
2026-08-22 15:32:28 +02:00
admin 39a72ae45a fix(landing): use official Cloudflare badge artwork instead of custom text
Cloudflare's brand guidelines flag a hand-written "Protected by
Cloudflare" text label as misleading — replace with their official
badge PNG (public/badges/cloudflare-protected.png), same fixed-size
<img> pattern already used for AiDisclosureBadge.
2026-08-22 15:22:41 +02:00
admin 8d058b00a1 feat(landing): add Cloudflare + security-headers trust badges to footer
Was implemented and locally verified earlier this session but never
committed/deployed before moving on to the CSP work — closing that gap.
2026-08-22 15:12:45 +02:00
admin fa3f5faa58 fix(i18n): translate milestone type labels
MILESTONE_META in src/lib/milestone-meta.ts held hardcoded English
labels ("Birthday", "Vet Visit", ...) consumed directly by
MilestoneCard and MilestoneForm -- bypassing next-intl entirely, so no
de.json entry could ever have translated them (there was no key to
translate, not a missing one).

Adds a MilestoneTypes namespace to both message catalogs and switches
the two user-facing consumers to useTranslations("MilestoneTypes")
for the label, keeping MILESTONE_META only for the icon lookup. The
admin posts page (p/[secret]/posts) still reads MILESTONE_META.label
directly and is left as-is -- the admin panel is English-only by
existing project convention, not part of the localized surface.
2026-08-21 22:33:03 +02:00
admin 8186c36b9a feat(stories): support 30s video uploads, mirroring normal video posts
Story gains mediaType (IMAGE/VIDEO) plus the same Mux fields as
VideoPost (muxUploadId/AssetId/PlaybackId, videoStatus, durationSecs,
aspectRatio); storageKey becomes optional since video stories don't
use Supabase Storage.

- 30s duration cap (deliberately shorter than the 180s normal-post
  cap) enforced client-side before upload (reads the file's own
  duration) and again server-side in the mux webhook and
  stories.getVideoStatus's self-heal path, so a bypassed client check
  or a direct API call can't produce an oversized story video.
- MAX_STORY_VIDEO_DURATION_SECS lives in a new client-safe
  src/lib/story-video-limits.ts rather than stories.ts, which is
  server-only — importing from a server-only module into StoryForm
  (a client component) pulled the pg driver into the client bundle
  and broke the build.
- Mux webhook now resolves an asset to either a VideoPost or a Story;
  video stories never fan out to the feed (mirrors the normal-post
  webhook branch, minus fanOutPost).
- StoryViewer: video stories autoplay muted (tap to unmute — browsers
  block autoplay-with-sound past the story that was directly clicked
  open) with a real playback-time progress bar (image stories keep
  the fixed 5s bar), rendered via object-contain to correctly handle
  the ~9:16 portrait ratio typical of phone-shot story videos instead
  of a fixed 16:9 box.
2026-08-16 14:36:44 +02:00
admin 9c55e18d4e feat(sponsor): show Stripe wordmark above the one-time/monthly toggle
Adds a subtle "payments processed by Stripe" indicator (official
wordmark SVG from Simple Icons, muted/low-opacity) above the
contribution-type selector on the Unterstützen page, so visitors know
which payment provider handles the transaction before choosing an
option.
2026-08-16 13:57:27 +02:00
admin 1293f7c879 feat(pets): add optional nickname/Rufname shown alongside the pet's real name
Adds Pet.nickname (nullable, max 30 chars) and a shared formatPetName()
helper so the display format ("Rex (Rexi)") stays consistent across
feed, admin panel, messages, sidebar/nav, and mentions instead of
duplicating the formatting logic at each read site.
2026-08-15 13:25:54 +02:00
admin 1ee2b53a57 feat(species): expand taxonomy to 14 species, add Arachnophobie-Helfer
Add Horse, Rabbit, Guinea Pig, Hamster, Mouse, Rat, Snake, Lizard, Turtle,
Gecko, and Spider as Species (with breed taxonomies + DE translations),
alongside the existing Dog/Cat/Bird. The species/breed system was already
fully data-driven (pet creation, onboarding, ad targeting all read
pets.listSpecies/listBreeds dynamically), so this is mostly seed data.

Spiders get dedicated handling: an opt-in Owner.hideSpiders preference
(offered once during onboarding via ArachnophobiaOptIn, always
re-toggleable on /pets via HideSpidersToggle) fully excludes Species=Spider
pets/posts from feed, explore, and search when enabled.
2026-08-15 11:57:24 +02:00
admin 5660a3a09d feat(admin): system broadcast banner, Phase 1 (Gitea #10)
Admins can send in-app announcements to all owners from /p/[secret]/broadcasts;
owners see them as a dismissible banner and confirm with "Gelesen". Tracks a
per-broadcast BroadcastReceipt (not a single rolling timestamp) so delivered/
read counts are visible per message and a later email-escalation phase can
target exactly the owners who haven't read a given broadcast.
2026-08-15 11:27:11 +02:00
admin 7496d900f3 feat(about): add motion/depth WOW effects to the "Über uns" page
- AnimatedNumber: stat tiles count up from 0 when scrolled into view
  instead of appearing static, so "live numbers" actually feels live.
- Pull quote: the story's strongest line is pulled out into its own large
  editorial-style callout between the hero paragraphs and the "Von Tier
  zu Tier" section, giving the wall of text a visual beat. paragraph2
  trimmed (DE+EN) so the line isn't read twice back to back.
- PawPrintTrail: a scattered trail of low-opacity paw prints behind the
  story content, each "stamping" into place via a pure-CSS scroll-driven
  animation (animation-timeline: view(), same technique as the existing
  hero-parallax-icon on the landing page) — no JS, respects
  prefers-reduced-motion.

Fixed a real bug while building the trail: -z-10 on its wrapper had no
local stacking context to be scoped to (the parent had `relative` but no
z-index), so it escaped past the page content and rendered behind the
document background — invisible regardless of opacity. Giving the page's
outer wrapper `z-0` fixes it.

Verified locally via dev server + Chrome DevTools MCP: light + dark theme,
375px mobile, quote/trail/stat-tile rendering all confirmed visually.
2026-08-14 19:22:14 +02:00
admin 9071bf3c9a feat(about): add "Über uns" page with founding story and live stats (Gitea #23)
New public /ueber-uns page — deliberately doesn't preview or link into the
platform itself (no phone mockup, no post previews), just the founding
story plus live member/pet/sponsor counts for transparency. Fully
bilingual via the existing LanguageSwitcher.

- src/trpc/routers/stats.ts: new public stats.getPublicStats procedure,
  Redis-cached (5min TTL), sponsor count includes lapsed subscriptions
  (not just currently-active badges) per discussion on the issue.
- src/proxy.ts: /ueber-uns registered as a public route, exempt from
  maintenance mode (doesn't touch the platform, so no reason to gate it).
- Extracted LandingHeader/LandingFooter from src/app/page.tsx into shared
  components under src/components/landing/ — this page reuses the same
  navbar, and duplicating it risked the responsive header fixes from
  earlier today drifting out of sync between two copies. The "Bald
  verfügbar" placeholder nav link now points at the real page.
- src/lib/stripe.ts: made the Stripe client a lazy singleton instead of
  constructing it at module scope. Eager construction crashed the entire
  tRPC appRouter (and anything importing it, e.g. createTRPCCaller() on
  any public page) whenever STRIPE_SECRET_KEY is unset — invisible until
  now because no public page had used createTRPCCaller() before.
- stats.getPublicStats wraps its Redis cache read/write in .catch() so a
  Redis outage degrades to uncached rather than breaking the page,
  matching the existing pattern in src/lib/maintenance.ts.

Verified locally via dev server + Chrome DevTools MCP emulation (DE/EN,
mobile 375px, desktop) — this is a public unauthenticated page so, unlike
most of today's other fixes, testable outside of Clerk auth.
2026-08-14 18:51:42 +02:00
admin a9a6a975a9 copy(sponsor): reframe subtitle as private financing, not ad replacement
The old copy positioned sponsoring as a substitute for ads ("instead
of ads"), but the platform still runs ads when advertising partners
are available — sponsoring and ads aren't mutually exclusive. Reframe
around the actual motivation: PawFeed is entirely privately financed,
and sponsoring helps cover ongoing costs (server hardware, software
licenses, tooling).
2026-08-14 15:23:08 +02:00
adminandClaude Sonnet 5 91b7662419 fix(sponsor): show Verified badge in feed, add sidebar link, mark sponsors in admin
Post-launch fixes from live testing (Gitea #22):
- Feed/explore postInclude never selected Pet.isVerified, so PostCard only
  ever rendered the new Sponsor badge, silently dropping Verified next to
  it. Both now render side by side, matching the pet profile page.
- /unterstuetzen had no navigation entry anywhere -- added to the desktop
  Sidebar (mobile bottom nav is a fixed 5-tab bar, left alone per scope).
- Admin users list showed no sponsor status at all -- SPONSOR badge added
  next to the user_XXXX id, same pattern as the existing BANNED/
  SHADOWBANNED badges.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 10:50:35 +02:00
adminandClaude Sonnet 5 c6ffc1336e feat(sponsor): Stripe-based voluntary sponsoring with Sponsor badge (Gitea #22)
Replaces the (never-launched) ad business direction with a freiwilliger
Beitrag/Community-Abo model: owners can support PawFeed via Stripe with
a one-time payment or a monthly SEPA/card subscription (2 EUR minimum),
declared as Sponsoring/Community-Abo, not a tax-deductible donation. The
Sponsor badge (only perk, no feature unlocks) is Owner-scoped and shows
on every post of every one of their pets, plus their profile.

- Schema: Owner.stripeCustomerId/sponsorSince/sponsorPeriodEnd/
  sponsorSubscriptionId/sponsorSubscriptionStatus, new
  SponsorContribution model (payment history)
- src/lib/stripe.ts (singleton client, mux.ts pattern) + src/lib/sponsor.ts
  (isActiveSponsor: one-time givers keep the badge permanently, subscribers
  through sponsorPeriodEnd even after cancelling -- no mid-period cliff)
- src/trpc/routers/sponsor.ts: getStatus, createCheckoutSession (SEPA +
  card for subscriptions, card for one-time, price_data since amounts are
  user-chosen), cancelSubscription (cancel_at_period_end, no hard stop)
- src/app/api/webhooks/stripe: checkout.session.completed, invoice.paid,
  customer.subscription.updated/deleted (mux webhook route pattern)
- src/app/(app)/unterstuetzen: amount chips (3/5/10 EUR) prefilling an
  editable field, one-time/subscription toggle, status + contribution
  history, in-app cancel button (no Stripe-hosted portal, per requirement)
- Badge surfaced in feed/explore postInclude + PostCard, and on the pet
  profile page next to the existing isVerified badge
- Nutzungsbedingungen Sec. 12 (draft, needs sign-off before launch) +
  Stripe added to the Datenschutzerklaerung's Auftragsverarbeiter list
- src/__tests__/sponsor.test.ts (7 tests), Stripe client fully mocked

STRIPE_WEBHOOK_SECRET intentionally not yet configured -- the webhook
endpoint can only be registered in the Stripe dashboard once this is
deployed and reachable at https://pawfeed.org/api/webhooks/stripe.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-14 10:24:08 +02:00
admin 50e2848312 feat(legal): AI-content disclosure for user uploads (Gitea #18)
Owners can now optionally mark uploaded content as AI_MODIFIED or
AI_GENERATED (mutually exclusive, defaults to NONE) across every upload
surface — photo posts, video posts, milestones, stories, and pet avatars.
Once set, the choice is owner-immutable; only a moderator can correct it
via a new admin.setAiDisclosure mutation (Posts admin page), which
requires a reason and writes to ModerationLog like every other
moderation action.

The disclosure renders as a UI-only overlay badge (the two provided
label assets) wherever the content is shown — feed, post detail, story
viewer, milestone cards, and the pet profile avatar — never baked into
the image pixels.

New shared components: AiDisclosureSelect (the 3-way picker used in all
five upload forms) and AiDisclosureBadge (the overlay renderer).
2026-08-13 13:44:16 +02:00
admin b4a49f8ed3 fix: address findings from full security/functionality audit (Gitea #17)
Rate-limiting on messages.send, reports.create, reactions.toggle,
follows.create, reposts.create. Unique constraints on Report to stop
report-flooding. Mux webhook now fails hard in production without a
signing secret instead of skipping verification. Admin-panel outline
buttons fixed on 4 more pages (same white-on-dark bug as the Users page).
Timing-safe comparison for ADMIN_SECRET/CRON_SECRET. Fixed duplicate HSTS
header (Cloudflare's stronger one was being shadowed by the app's own).
tRPC now returns clean JSON 401s instead of HTML redirects when
unauthenticated, and no longer blocks public procedures for logged-out
visitors. 14 previously-silent catch blocks now log to GlitchTip.
invite/page.tsx and EmergencyVetSection.tsx fully localized (were 100%
hardcoded German). Stale TODO comments removed, auth.test.ts scaffold
rewritten to match the actual lazy-upsert pattern instead of a Clerk
webhook flow that was never built.
2026-08-13 12:42:49 +02:00
admin d670cf9173 feat(theme): account-bound Dark Mode toggle (Gitea #8)
Wires up the already-present next-themes dependency and globals.css
:root/.dark token infrastructure. Persists the choice on Owner.themePreference
so it survives relogin on a new device, not just localStorage. Toggle lives
in the Sidebar (desktop) and the pet-switcher dropdown (mobile); Clerk's own
UI now follows the same theme via a small ClerkThemeProvider wrapper.
2026-08-13 10:54:23 +02:00
admin 6b79dfb570 fix(landing): correct German copy and restore feature-card hover animation
Finishes the manual edits from Gitea #16: fixes capitalization on
"Jetzt starten" and grammar on the hero badge text, updates the
banned-page contact address to team@pawfeed.org, and adds the
missing `group` class so the feature-card icon's `group-hover:scale-105`
actually fires. Keeps the RelativeTime strings in their original
compact form ({count}m/h/d) since that format is documented as
intentional for tight UI spots (comment lists, notifications, ad cards).
2026-08-12 17:27:59 +02:00
admin 9f9db9e742 feat(landing): interactive redesign with scroll reveals and parallax
Rebuilds the public landing page per the structural reference in
Gitea #11 (navbar layout, rounded hero card with phone mockup +
floating badges, catchphrase section, second detail-feature card,
footer repeating the navbar) — colors/typography adapted to PawFeed's
existing orange brand instead of the generic SaaS reference palette.

New:
- Sticky navbar (logo, Home/About-coming-soon/Impressum, language
  switcher, Login + Sign-up pill)
- Hero: rounded-4xl gradient card, phone-framed app preview with
  three floating badge cards (idle bob animation), scroll-linked
  parallax on the background paw watermark via native CSS
  animation-timeline: view() (progressive enhancement, no JS)
- Catchphrase section reusing the existing 4 feature translations,
  each card staggered into view via a new ScrollReveal client
  component (IntersectionObserver, no scroll-handler churn)
- Second rounded detail-feature card with two checklist points
- Footer repeats the navbar links + tagline + "designed by" line

All reveal/parallax/float animations respect prefers-reduced-motion
and are pure CSS — no new animation dependency. Verified in a real
browser at 390px and ~1568px (mobile nav collapse, hero stacking,
floating badges hidden on mobile, scroll reveals firing correctly).

Gitea #11
2026-08-12 14:04:57 +02:00
admin 5fbf39593d feat(ui): initials-color avatars and collapsible desktop sidebar
Adds a PetAvatar wrapper (hash(name) mod 360 -> HSL background) and
migrates the four most-visible avatar call sites (sidebar, mobile nav,
post card, post detail incl. comments) to use it instead of duplicated
inline initials logic.

Desktop sidebar can now collapse to an icon-only 76px rail, persisted
in localStorage (same client-init pattern as ActivePetContext). All
nav rows, the new-post button, and the owner row adapt; secondary
sections (language switcher, legal footer) hide when collapsed.

Gitea #12, #14
2026-08-12 12:52:21 +02:00
admin 3a5b72b0f7 fix(verification): profile context in review, i18n on edit page, badge in pet switcher
Three follow-ups from testing the verification flow:
- listVerificationRequests now pulls bio, join date, post/follower
  counts, and the owner's ban+warning status onto the review card, so a
  moderator can check the checklist without leaving the page
- Edit-pet page was entirely hardcoded English regardless of locale —
  converted to next-intl (new EditPet namespace, en+de)
- the pet switcher (Sidebar + MobileNav) didn't show the verified badge,
  only the full profile page did — added it to both
2026-08-12 10:19:16 +02:00
admin 54fb0d55bc feat(admin): warning system, pet verification badges, report severity, admin GDPR erasure
Gitea #10 Phase 2:
- Warning model (append-only, no delete — Owner history), issueWarning +
  listWarnings, surfaced on the Users page with a badge + dialog
- Pet.isVerified checkmark (lives on Pet, not Owner — pets are the social
  identity per CLAUDE.md), toggleable from the Users page, shown on the
  public profile page
- listReports now derives a HIGH/MEDIUM/LOW severity from ReportReason
  and sorts severity-first; Reports page shows the derived badge
- Admin-triggered GDPR Art. 17 erasure: extracted the self-service
  /api/account/delete logic into a shared deleteOwnerAccount() helper,
  new admin.deleteUserAccount (SUPER_ADMIN only, requires typing the
  exact ownerId to confirm, blocks self-deletion) reuses it — Delete
  button only renders for SUPER_ADMIN/bootstrap callers
- all four new/changed mutations log to ModerationLog with role+IP
2026-08-12 09:32:33 +02:00
admin f899ed5f7c feat(landing): translate landing page copy (Landing i18n namespace)
The landing page was hardcoded English, so switching languages had no
visible effect on it — added a "Landing" namespace to both message
catalogs and wired src/app/page.tsx up via getTranslations, matching
the pattern already used by other server-rendered pages.
2026-08-11 20:09:13 +02:00
admin 17f560eb46 feat(legal): add mandatory upload-rights and data-processing consent to onboarding
Gitea #3: harden Datenschutz for the now-public site. Adds a blocking
first-pet-onboarding step (two checkboxes) confirming the owner holds
rights to uploaded photos/videos and consents to data transfer to
Clerk/Cloudflare/Mux/Supabase. Consent timestamps are stored separately
on Owner so either declaration can be re-requested independently later.
Also documents GlitchTip as a processor and adds an explicit consent
section to the Datenschutzerklärung.
2026-08-11 09:33:39 +02:00
admin bb1515cb93 feat(onboarding): add community-guidelines acknowledgment step before pet creation
New users landed directly on pet creation with no prompt to acknowledge
community guidelines — the existing LegalUpdateBanner/LegalDocumentVersion
mechanism only ever ran *after* the first pet existed (since the Owner
row wasn't created until pets.create's owner.upsert), and turned out to
be entirely dormant (zero LegalDocumentVersion rows had ever been
published in prod).

Adds a step 0 to /onboarding/pet: OnboardingGuidelines queries the same
legal.getPendingUpdate the in-app update banner uses and shows the
latest published summary before letting the user continue to pet
creation. Reuses the existing LegalDocumentVersion/admin-publish
infrastructure (/p/[secret]/legal) instead of hardcoding copy, so
content stays editable without a deploy. If nothing has been published
yet, the step is skipped automatically — no dead-end for a fresh
deployment.

legal.acknowledge switched from owner.update to owner.upsert since it
can now be called before the Owner row exists (pre-first-pet).

Deliberately did NOT add a cookie-consent checkbox: Datenschutz §5
already discloses that only strictly-necessary session cookies are used
and no tracking/ad cookies are set without explicit consent — under
GDPR/ePrivacy, strictly-necessary cookies don't require active consent,
only disclosure. The guidelines step links to both Nutzungsbedingungen
and Datenschutz instead of a redundant separate consent mechanism.

Side effect (intentional): existing owners who never explicitly
acknowledged anything will see the same content once via the in-app
LegalUpdateBanner the next time a version is published, since it's the
same underlying data model.
2026-07-23 19:37:40 +02:00
admin ff607a604d feat(feed): cold-start discovery feed for new pets with no follows
A brand-new pet with zero posts of its own and no followees with posts
saw a completely empty feed on first visit. Instead of that dead-feeling
first impression, getFeed now falls back to recent posts from other pets
of the same species, ranked by the existing algoScore (engagement +
recency) — same idea as a "For You" feed, scoped by species instead of
follows.

Only kicks in on the first page (no cursor) of the primary Redis-backed
path, so paginating an already-empty follow feed doesn't keep serving
discovery pages. Response carries a `source: "following" | "discovery"`
flag; FeedList shows a small dismissless banner explaining the posts
aren't from follows yet, otherwise the feed renders identically
(WelcomeCard, feed-mode toggle, PostCard list all unchanged).

Added 2 regression tests covering the discovery-fallback trigger and
the "don't discovery-fallback while paginating" edge case.
2026-07-23 18:21:03 +02:00
admin b7719397e6 feat(i18n): translate pet-profile area (Edit Profile, Health, stats, dropdowns, ReportSheet, BlockDialog)
The entire /pets area (My Pets grid, pet profile page, followers/following
lists, the profile options dropdown, ReportSheet, BlockDialog) had zero
i18n despite ProfileTabs right next to it being fully translated — user
reported still seeing English after switching the app to German.

Adds 7 new message namespaces (PetsPage, PetProfile, FollowersPage,
FollowingPage, ProfileActions, ReportSheet, BlockDialog) with full EN/DE
parity, plus bio/adoptionStory placeholder keys to the existing PetForm
namespace (two placeholders were hardcoded English template literals
despite the rest of that form already being translated).

Server Components (pets/page.tsx, pets/[petId]/page.tsx,
followers/following pages) use getTranslations from next-intl/server
instead of the useTranslations client hook.
2026-07-23 15:02:37 +02:00
admin 4ba6bbc0c3 feat(i18n): translate remaining hardcoded strings (audit finding #14)
Adds 6 new message namespaces (DeleteAccount, NotificationsPage,
PhotoPostForm, StoryForm, MilestoneForm, VideoUploadForm, StoryViewer,
StoryTray, RepostCard) plus a Health.share sub-namespace, with full
EN/DE parity (verified programmatically, 0 keys missing on either side):

- DeleteAccountDialog.tsx: was 100% hardcoded German for a destructive,
  irreversible action — now fully translated.
- notifications/page.tsx: mixed hardcoded English (FOLLOW/REACTION/...)
  with hardcoded German (BIRTHDAY/ADOPTION_DAY) in the same list.
- HealthDashboard.tsx + WeightChart.tsx: date formatting was hardcoded
  to toLocaleDateString("de-DE", ...) regardless of locale, unlike the
  DateWheelPicker in the same forms which correctly used useLocale().
  ShareDialog was entirely untranslated German alongside fully-translated
  sibling tabs.
- PhotoPostForm/MilestoneForm/StoryForm/VideoUploadForm: the entire
  post-creation flow had zero i18n despite the parent PostTypeSheet
  being fully translated.
- StoryViewer/StoryTray/RepostCard: no i18n at all.

health-card/[token]/page.tsx passes a fixed "de-DE" locale to the now
locale-aware WeightChart, since that whole page is a separate,
out-of-audit-scope public page that's intentionally all-German.
2026-07-22 19:55:01 +02:00
adminandClaude Sonnet 5 70115de483 feat(legal): admin-published legal updates + owner acknowledgement banner
Adds a pull-based legal-update flow instead of fanning out through the
petId-scoped Notification model (ToS/Datenschutz acceptance is an
Owner-level concern, not a per-pet one):

- Prisma: LegalDocType enum, LegalDocumentVersion model,
  Owner.legalAcknowledgedAt.
- trpc/routers/legal.ts: admin publish/listVersions, owner-facing
  getPendingUpdate/acknowledge. assertAdmin exported from admin.ts
  instead of duplicated.
- Admin panel /legal: publish form (doc type + change summary) + history.
  Publishing IS sending — every owner sees it on next load until
  acknowledged; republishing re-surfaces it for everyone.
- LegalUpdateBanner mounted in (app)/layout.tsx: non-dismissable sheet
  with the stored change summary, "Verstanden" (acknowledge) and
  "Konto löschen" (routes into DeleteAccountDialog via new
  ?deleteAccount=1 auto-open support) actions.

Closes the last DEBUG-List.md item.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 20:08:49 +02:00
adminandClaude Sonnet 5 f14af09b58 feat: replace calendar date inputs with a wheel-style date picker
Adds DateWheelPicker (src/components/ui/date-wheel-picker.tsx) — an
iOS-style bottom-sheet with scroll-snap day/month/year columns — and
swaps out the 6 remaining <input type="date"> usages in PetForm
(birthday, adoptedAt) and HealthDashboard (weight log, vet visit,
vaccine given/due dates). Closes the last open DEBUG-List.md item.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:44:50 +02:00
adminandClaude Sonnet 5 f14be58c06 feat: engagement-counter denormalization, pagination fixes, dead-code cleanup
Bundles the 2026-07-12 code-audit session (Clusters A/B/C/D partial/E/F):
denormalized Post/Advertisement reaction/comment/repost counters synced
transactionally instead of live _count queries; real cursor-based pagination
for followers/following/blocks lists; assertPetOwnership + formatRelativeTime
centralized; dead r2.ts + AWS SDK deps removed; missing DB indexes added;
account-deletion flow, mention notifications, pull-to-refresh feed, and
mobile UI/i18n fixes from the surrounding sessions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-16 19:22:23 +02:00
admin 31602b08eb feat: Sprint 2 — i18n Search+Messages, Mention-Dropdown, Story-Tray, Explore Grid, Legal-Links
- Search-Seite vollständig übersetzt (Search-Namespace in EN+DE)
- Messages-Listenseite übersetzt (pageTitle, noMessages, you, relativeTime etc.)
- @-Mention Dropdown: Avatar 6→9, zwei-zeilig Name+Tierart, orange Fallback
- StoryTray: -mx-6 entfernt, Add-Story-Button jetzt korrekt ausgerichtet
- ExploreCard: lange Rassenamen umbrechen statt aus Grid herausdrängen
- Impressum/Datenschutz/AGB-Links in Profil-Seite (/pets) ergänzt
2026-06-27 08:07:04 +02:00
admin 58e2840fae fix(mobile+comments): Sprint 1 — nav visibility, messages button, optimistic comments
- MobileNav: fixed mail icon top-right corner (always reachable on mobile)
- MobileNav: iOS safe-area-inset-bottom padding so nav is never hidden
- MobileNav: larger icons (h-6) + better contrast on inactive tabs (text-foreground/60)
- PostDetailDialog: optimistic comment update — comment appears immediately on submit
  with pending indicator; rolls back input on error
- i18n: added Comments.sending key (EN/DE)
- PostCard: minor style cleanup (pre-existing session changes)
- Explore: pre-existing session changes
2026-06-27 07:27:47 +02:00
admin dc4c196721 feat: i18n translations, PostDetailDialog 2-col layout, feeding info health module
- Translate ProfileTabs, ExploreCard, PostDetailDialog, HealthDashboard (EN + DE)
- PostDetailDialog: Instagram-style two-column layout (image left, comments right)
- Fix sm:max-w-[960px] override for dialog width on all breakpoints
- ActivePetInitializer: auto-set first pet on login if activePetId is null
- Onboarding: set activePetId immediately after first pet creation
- Add PetFeedingInfo model (foodType, feedingTimes, dailyAmountG, foodBrand, specialDiet)
- FeedingSection component with view/edit/delete, shown in HealthDashboard
- Health Card public page includes feeding info block
- Delete CommentSheet.tsx (dead code)
2026-06-23 14:39:21 +02:00
adminandClaude Sonnet 4.6 376ea74b36 feat(i18n): translate FeedList, PostCard, CommentSheet, FollowButton, MessageThread
Adds PostCard, Comments, Follow, Messages, FeedList namespaces to en/de
messages. Moves formatRelativeTime/formatDay into components so they can
use the t() hook. All hardcoded strings replaced with next-intl calls.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 14:18:18 +02:00
admin 4b17537a9a feat(i18n): add EN/DE language support with next-intl
- Install next-intl@4 with cookie-based locale switching (NEXT_LOCALE)
- Add LanguageSwitcher component in Sidebar footer
- Translate Nav, PostTypeSheet, Feed, Explore, WelcomeCard, PetForm,
  AvatarUpload, and Onboarding pages (EN + DE)
- Add SpeciesTranslation and BreedTranslation DB models for locale-aware
  taxonomy; listSpecies and listBreeds accept optional locale param
- Seed German translations for all 3 species and 45 breeds
- Add SQL migration script for Supabase (add_i18n_translations.sql)
- fix(invite): secure cookie flag and set-invite route for HTTPS
2026-06-22 14:01:16 +02:00