Commit Graph
136 Commits
Author SHA1 Message Date
admin 4556ef4e97 fix: mention-dropdown Umrandung passt sich Zweizeiligkeit an; neuer Bug Chat-Eingabe-Fix 2026-06-27 10:12:02 +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 68503e8915 fix(mobile+comments): #4 correct query invalidation key, #6 add viewport-fit=cover 2026-06-27 07:47:41 +02:00
admin 8deede7235 fix: add parens around nullish coalescing in PostDetailDialog (build error) 2026-06-27 07:36:42 +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
admin 2568c32cf4 feat: notification polling 30s->10s, video thumbnails, @-mentions
- NotificationBell: refetchInterval 30000->10000, refetchOnWindowFocus, i18n label
- ProfileTabs: Mux thumbnail for VIDEO posts in grid, play icon overlay
- parse-caption: add mention segment type for @word parsing
- MentionTextarea: new component with @-autocomplete via search.pets
- PostCard: render @mentions as blue links to /search
- CommentSheet: parse comment bodies for mentions, use MentionTextarea
- PhotoPostForm: use MentionTextarea for caption input
2026-06-23 06:17:58 +02:00
adminandClaude Sonnet 4.6 6542c06e02 fix: auto-set first pet on login, resize detail view, fix Clerk types
- Sidebar + MobileNav: auto-call setActivePet(pets[0]) when activePetId
  is null so Follow/reaction buttons work immediately after onboarding
- PostCard detail mode: reduce image max-h from 70dvh to 50dvh so the
  footer (paw + comment buttons) stays visible within the dialog
- ProfileTabs dialog: reduce max-h from 90dvh to 85dvh
- Auth pages: remove layout.logoPlacement and colorText (not in Clerk
  Variables type, caused Docker build failure)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-23 05:58:57 +02:00
adminandClaude Sonnet 4.6 f7183f93c7 fix(clerk): remove colorText from appearance variables (not in type)
colorText is not part of Clerk Variables type in current version,
causing TypeScript build failure in Docker.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 20:48:52 +02:00
adminandClaude Sonnet 4.6 8429e88891 fix(i18n): use useTranslations in EmptyFeed (Client Component fix)
EmptyFeed is rendered inside FeedList ("use client"), so getTranslations
(server-only) caused a white screen. Switched to useTranslations hook.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-22 20:42:15 +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
admin 8645421c1e feat(legal): apply LegalLayout to Nutzungsbedingungen page 2026-06-22 05:48:08 +02:00
admin 195df0154d feat(legal): redesign Impressum and Datenschutz with PawFeed branding 2026-06-21 21:46:30 +02:00
admin ac19d0fd2c fix(explore): link avatar and name to pet profile page 2026-06-21 21:11:31 +02:00
admin e5264e3ca3 feat(legal): add Nutzungsbedingungen page and link in sidebar footer 2026-06-21 20:58:10 +02:00
admin b38c9b046e fix(invite): add secure flag to pf_invite cookie for HTTPS production 2026-06-21 20:47:19 +02:00
admin 00908545c4 docs(env): clarify Supabase vars — separate DB connection from Storage URL 2026-06-21 18:58:00 +02:00
admin 7b0baf3be8 fix(docker): lazy-init Supabase+Redis, self-host Redis, simplify build args 2026-06-21 18:51:41 +02:00
admin 24c63010b0 fix(docker): use docker/.env auto-discovery instead of --env-file flag 2026-06-21 18:40:26 +02:00
admin 88ca3ab487 fix(docker): pass all build-time env vars and add start.sh wrapper 2026-06-21 18:34:45 +02:00
admin fad016973a fix(sidebar): pin to viewport height so footer stays visible 2026-06-21 18:15:26 +02:00
admin 5800e06cd9 feat: ad engagement, invite gate, sidebar footer, docker setup
Ad interactions (reactions/comments/reposts on ads):
- Schema: AdReaction, AdComment, AdRepost models with RLS enabled
- tRPC: new ads router (getFeedData, toggleReaction, addComment, listComments, toggleRepost)
- AdCard: fully interactive card with paw, comment sheet, repost — mirrors PostCard style
- getActiveAds includes _count for advertiser engagement metrics

Invite system enforcement:
- proxy.ts: /sign-up blocked without pf_invite cookie
- INVITE_REQUIRED=false env var disables gate for post-beta live launch
- Full invite flow: /join → cookie → /sign-up → consume-invite → 3 codes issued to new user
- Admin: listInviteCodes, createRootInvite, revokeInvite procedures

Sidebar mini-footer:
- Impressum + Datenschutz links always visible at sidebar bottom
- PawFeed Alpha-Test copyright line

Docker self-hosting:
- docker/Dockerfile: 3-stage build using Next.js standalone output
- docker/docker-compose.yml: env_file + build-args for NEXT_PUBLIC_ vars
- .dockerignore at project root
- next.config.ts: output standalone for minimal image
- .env.example: documented INVITE_REQUIRED
2026-06-21 18:12:13 +02:00
admin a9793a5ad2 fix(ads): AdCard matches PostCard layout — same header, image, footer structure 2026-06-21 16:23:24 +02:00
admin 6f671cfc63 fix(ads): render image in AdCard feed, add image upload to ad dialog 2026-06-21 16:21:38 +02:00
admin 0f34a2c9c6 feat(admin): show post preview in reports — images, caption, pet, counts 2026-06-21 16:10:01 +02:00
admin e2d14654c8 feat(admin): reports page with dismiss and delete-post actions 2026-06-21 16:00:33 +02:00
admin ee658b4788 fix(middleware): migrate to proxy.ts for Next.js 16, enable Supabase RLS on all tables 2026-06-21 15:56:32 +02:00
admin b5f02fe7a4 feat(admin): add hidden admin panel, ban system, ads, and legal pages
- Admin panel at /p/[ADMIN_SECRET]/* — middleware returns 404 for wrong/missing secret
- tRPC adminRouter: getStats, listPosts, deletePost, listComments, deleteComment,
  listUsers, banUser/unbanUser, listAds, createAd/updateAd/deleteAd, getActiveAds,
  listModerators, grantRole/revokeRole, getModerationLog, getActiveBan
- Prisma schema: AdminRole, UserBan, Advertisement, ModerationLog models added
- Ban enforcement: (app)/layout.tsx checks active ban on every request → /banned
- Feed ad injection: active ads shown every 7th post in FeedList (AdCard component)
- Clerk middleware.ts: protects all routes; /p/[secret] validated before auth check
- Impressum + Datenschutz pages: contact data base64-obfuscated, decoded client-side only
- shadcn Switch component added
- ADMIN_SECRET + ADMIN_OWNER_ID documented in .env.example
2026-06-21 15:21:07 +02:00
admin e407064866 feat(explore): add trending tab with hot posts and hashtags
- explore.getTrending: top-12 photo posts by reaction count (48h window)
  and top-10 hashtags by post count (7d window)
- Explore page opens on Trending tab by default; species/breed tabs
  preserved with breed filter hidden when not applicable
- Trending UI: hashtag chips linking to /hashtag/[tag] + 3-col post
  grid with paw-count overlay and PostCard detail dialog

feat(follows): auto-trigger follower milestone posts

- After each follow, count followers for followeePetId and check against
  all thresholds (500 / 1K / 2.5K / 5K / 10K / 25K / 50K / 100K /
  250K / 500K / 1M)
- Guard against duplicate awards via existing Milestone lookup
- Fire-and-forget: milestone post created + fanned out to follower feeds
  without blocking the follow response
2026-06-21 14:41:20 +02:00
adminandClaude Sonnet 4.6 38952b1563 docs: update README with v2 features, full schema overview, and next-session roadmap
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 18:03:01 +02:00
adminandClaude Sonnet 4.6 23c570b2af fix(health): enforce privacy — remove emergency vet from public health card
Emergency vet contact data (phone, address, map) was visible on the
token-shared health card URL. Health data is owner-private; the health
card is the single sharing exception but should not include internal
emergency contact details.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:52:58 +02:00
adminandClaude Sonnet 4.6 d17d71e55a feat(health): emergency vet with OSM search and map
- EmergencyVet model: name, address, phone, website, lat, lng (1:1 to Pet)
- health router: getEmergencyVet, setEmergencyVet, deleteEmergencyVet
- EmergencyVetSection: Nominatim search (debounced 500ms, OSM attribution),
  result dropdown auto-fills address + coordinates, manual override for all fields
- OSM iframe map (no API key) with "In Karte öffnen" link; shown in edit
  mode as coordinate preview and in view mode after save
- Health card (public): red-accented emergency vet block with phone link,
  website link and OSM map; map hidden on print (print:hidden)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:39:46 +02:00
adminandClaude Sonnet 4.6 c9f385a9fa fix(profile): replace bottom sheet with centered dialog for post detail
- Sheet side=bottom was anchored to screen bottom edge
- Default SheetContent X button overlapped PostCard 3-dot menu
- Dialog renders centered, showCloseButton=false removes the X
  (backdrop click / Escape still closes; 3-dots remain the only top-right control)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:23:21 +02:00
adminandClaude Sonnet 4.6 b0d6a4d7d1 fix(cron): birthday notifications require 1+ year like adoptedAt
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:14:47 +02:00
adminandClaude Sonnet 4.6 f6aa825ad1 feat(v2): algorithmic feed toggle, anniversary notifications, welcome card
Algorithmic Feed:
- feed.getFeed gains `mode: chrono | algo` param (default: chrono)
- Algo scoring: reactions*3 + comments*5 + reposts*4 + recency bonus
- FeedList: pill toggle Fuer dich (algo) / Aktuell (chrono), saved to localStorage

Anniversary Notifications:
- Pet schema: birthday Date?, adoptedAt Date?
- NotificationType enum: +BIRTHDAY, +ADOPTION_DAY
- /api/cron/anniversaries: daily cron (08:00 UTC via vercel.json)
- Pet edit form: birthday + adoptedAt date pickers
- Notifications page: Cake/Home icons, German copy, links to health page

New-User Welcome Card:
- WelcomeCard shown at top of feed while localStorage flag unset
- 5 feature tiles: Posts/Videos, Stories, Milestones, DMs, Health
- Dismissable via X or Verstanden; per-pet flag in localStorage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:10:50 +02:00
adminandClaude Sonnet 4.6 80559c3b3f feat(health): health card share link, PDF export, weight chart in card
- ShareDialog: expiry selector (7/14/30/60/90 days, default 14), shows
  expiry date after generation, "Als PDF öffnen / drucken" button
- WeightChart extracted to standalone component (WeightChart.tsx) so it
  can be shared between HealthDashboard and the public health card page
- Health card page: renders WeightChart when 2+ weight entries exist
- AutoPrint client component: auto-triggers window.print() when card is
  opened with ?print=1 (600ms delay for render to settle)
- README: Phase 6 + Phase 7 marked complete with full feature summaries;
  project structure and router list updated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 06:06:28 +02:00
adminandClaude Sonnet 4.6 b9f786c87f feat(health): add SVG weight trend chart
Renders a smooth bezier line chart above the weight log list when
2+ entries exist. No external library — pure SVG with:
- Orange-500 line + gradient area fill (PawFeed accent)
- Hover tooltip with dark bg, orange weight value, muted date
- Glow ring + dot state change on hover
- Bezier curve interpolation for smooth line
- Responsive via viewBox (maxHeight 190px)
- Y-axis: auto-scaled with 4 grid ticks
- X-axis: up to 5 date labels (DD.MM), always shows first + last
- Trend delta label (neutral, context-dependent for pets)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 05:49:29 +02:00
adminandClaude Sonnet 4.6 f1c798a4b0 feat(07): Phase 7 — Health & Vet Tracking
Schema: WeightLog, VetVisit, Vaccine, HealthCard models
Router: health.ts — CRUD for weight/vet/vaccines + health card token management
Pages:
  - /pets/[petId]/health — owner-only dashboard (weight, vet visits, vaccines)
  - /health-card/[token] — public shareable health card (no auth required)
Profile: Health button added for own pets; Edit + Health side by side

Health data is owner-private; the Health Card link is the only public
access point, shareable via token URL (vet, friends, sitters).
Token can be regenerated to revoke access.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 05:07:56 +02:00
adminandClaude Sonnet 4.6 5d396a7bb4 fix: make pet profiles clickable from feed, search, and notifications
- Add pets.getProfile procedure (no ownership check) so any auth'd user can view any pet profile
- Profile page now uses getProfile; Edit link and owner attribution only shown for own pets
- PostCard header: wrap PawRing + pet name in Link to /pets/[petId]
- Search Pets tab + People tab pet rows: wrap avatar/name in Link to /pets/[petId]

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 04:52:45 +02:00
adminandClaude Sonnet 4.6 e2e932804b feat(06): Phase 6 — Direct Messages
- Schema: DmPolicy enum (EVERYONE/FOLLOWERS_ONLY), dmPolicy on Pet, Conversation + Message models
- conversationsRouter: list (inbox), getOrCreate (with DmPolicy + block check), unreadCount
- messagesRouter: list (3s poll, auto mark-read), send (+ MESSAGE notification), markRead
- /messages inbox page with unread badges + last message preview
- /messages/[conversationId] thread with bubble UI, day dividers, Enter to send
- Message button on pet profiles (ProfileActions) → getOrCreate → navigate to thread
- Sidebar: Messages link activated
- Pet edit page: DM Privacy setting (Everyone / Only pets I follow)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 09:45:45 +02:00
adminandClaude Sonnet 4.6 1fc69add4d docs: update README to reflect Phases 1-5 complete
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 08:44:48 +02:00
adminandClaude Sonnet 4.6 da0f9f2bb7 fix: resolve 5 bugs from dev testing
- PostCard: add variant="detail" (object-contain + black bg) for profile sheet — full image in original format
- VideoCard: getByPostId polls Mux API directly when PROCESSING, self-heals without webhooks in dev
- PawRing: gray ring for all-seen stories, orange for unseen; reads viewerPetId from ActivePetContext
- Search: add People tab via new search.byOwner (Clerk username search → owner's pets)
- Notifications: rows now navigate to actor profile (FOLLOW) or pet profile (REACTION/COMMENT)
- Notifications router: include post.petId for navigation target

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-14 08:41:46 +02:00
adminandClaude Sonnet 4.6 60366691e8 feat(05): Phase 5 — short-form video via Mux
Schema:
- Add VideoPost model (muxUploadId, muxAssetId, muxPlaybackId,
  status, durationSecs, aspectRatio) linked 1-to-1 with Post
- Add VideoStatus enum (PROCESSING/READY/ERROR)
- Add PostType.VIDEO

Backend:
- src/lib/mux.ts — singleton Mux client + webhook secret
- videos router: createUpload (Mux direct upload URL + PROCESSING post),
  getByPostId (status polling), updateCaption
- POST /api/webhooks/mux: verifies signature; handles
  video.upload.asset_created (store muxAssetId),
  video.asset.ready (set READY + playbackId + fan-out to feed),
  video.asset.errored (set ERROR)
- feed.ts + posts.byPetId: include videoPost in all post queries

UI:
- VideoUploadForm: drop zone → XHR PUT to Mux upload URL with
  progress bar; caption field; transitions to "Processing…" on success
- VideoCard: PROCESSING shows spinner; READY renders MuxPlayer
  (HLS, orange accent); polls every 5s until status changes
- PostTypeSheet: "Video Post" option added (between Photo and Milestone)
- PostCard: VIDEO type renders VideoCard instead of image carousel

Env vars: MUX_TOKEN_ID, MUX_TOKEN_SECRET, MUX_WEBHOOK_SECRET,
NEXT_PUBLIC_APP_URL — placeholders added to .env.local

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 13:27:43 +02:00
adminandClaude Sonnet 4.6 08fd0dfb71 feat(04): Phase 4 — in-app notifications
Schema:
- Add NotificationType enum (FOLLOW, REACTION, COMMENT, MESSAGE)
- Add Notification model with recipientPet, actorPet, post, comment
  relations; index on [recipientPetId, read, createdAt desc]

Backend:
- notifications router: list (cursor-paginated), unreadCount,
  markRead, markAllRead
- follows.create: fire-and-forget FOLLOW notification to followee
- reactions.toggle: REACTION notification on create (skip self)
- comments.create: COMMENT notification with commentId (skip self)
  All triggers use .catch(()=>{}) to never fail the main action

UI:
- NotificationBell component with orange badge, polls every 30s
- /notifications page: list with avatar, type icon, text, thumbnail,
  read/unread dot; auto-marks all read on visit
- Sidebar: Notifications link activated with NotificationBell
- MobileNav: Bell replaces Search tab (Search still at /search)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:59:23 +02:00
adminandClaude Sonnet 4.6 7fafc6d17f fix(StoryTray): align Add Story circle with PawRing items
PawRing containers are size+RING_PAD*2=72px tall while the Add Story
button was only 56px, causing vertical misalignment. Wrap the button
in a matching 72px container so all tray items share the same height.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:44:57 +02:00
adminandClaude Sonnet 4.6 4b2760a08d fix(PawRing): center main pad ring at cy=50 so avatar sits inside it
mainCy=58 shifted the ring below the flex-centered avatar, causing the
image to appear above/half-inside the ring. Setting cy=50 aligns the
ring with the container center; toe beans (cy≈1-10) overflow upward via
SVG overflow:visible, which is the correct paw-print anatomy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:43:27 +02:00
adminandClaude Sonnet 4.6 a9886948b8 fix: empty feed + unclickable profile grid
- feed.getFeed: when Redis is unavailable, fall back to a direct
  Postgres query (followed pets + own posts, cursor-based) instead of
  returning an empty array — feed now works in dev without Upstash
- ProfileTabs: wrap grid thumbnails in <button> + open a bottom Sheet
  with the full PostCard on tap; previously tapping a photo did nothing

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:38:53 +02:00
adminandClaude Sonnet 4.6 5af2a40bb3 fix: resolve three runtime errors found in dev smoke test
- MultiImageUpload: move onKeysChange out of setImages updater into
  useEffect to fix React "Cannot update a component while rendering"
  warning; same fix applied to handleRemove
- feed.getFeed: wrap getFeedPage in try/catch so Redis failure
  (Upstash not configured) returns empty feed instead of 500
- posts.create + reposts.create: wrap fanOutPost with .catch(() => {})
  so Redis failure never rolls back an already-committed post

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:33:14 +02:00
adminandClaude Sonnet 4.6 01b4f18a97 fix: wrap useSearchParams in Suspense on reset-password page
Next.js static rendering requires useSearchParams() to be inside a
Suspense boundary. Splits page into ResetPasswordContent (inner) +
ResetPasswordPage (Suspense wrapper). Caught by npm run build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 12:21:07 +02:00