Commit graph

162 commits

Author SHA1 Message Date
0c7f37fd12 feat(quota): storage quota + family-member limits for free tier
Feature A — Storage quota (1 GiB per family):
- src/lib/quota.ts: enforcement library with pure functions (fully unit-tested)
  and DB-bound helpers; isPaidFamily() is the single payment abstraction gate
- src/lib/format-bytes.ts: extracted formatBytes() — safe for client imports
- POST /api/upload: quota check before presigned URL issuance (HTTP 402 + reason code)
- POST /api/memories/[id]/confirm: HeadObject reconciles actual R2 size; deletes
  over-quota objects and marks row failed rather than silently exceeding limit
- GET /api/storage-usage: storage info endpoint for UI meter
- src/components/StorageMeter.tsx: meter bar + StorageQuotaBanner + MemberLimitBanner
- memories/page.tsx: quota banner, FAB disabled (⊘) when exceeded, compact meter in header
- settings/page.tsx: always-visible StorageMeter + MemberLimitBanner in invite section

Feature B — Member limit (2 per family, free tier):
- invites/route.ts: replaced ad-hoc inline check with checkMemberLimit() from quota lib
  Structured 403 response: { reason, currentCount, limit }
- Freeze rule: paid→free downgrade leaves all members intact; only new invites blocked

Migration:
- drizzle/0007_subscription_status.sql: ADD COLUMN subscription_status varchar(20)
- debug-migration/route.ts: step added for hot-apply without full redeploy
- src/db/schema/family.ts: subscriptionStatus field added to Drizzle schema

Tests: 44 unit tests in src/__tests__/quota.test.ts, all passing
- Pure function tests (no DB): isPaidFamily, wouldExceedQuota, isAtMemberLimit, formatBytes
- DB-bound tests (mocked @/db): getFamilyStorageUsage, checkStorageQuota,
  checkMemberLimit, getStorageInfo, tenant isolation

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-27 23:21:11 +05:30
e2a3e83638 docs+fix: overhaul CLAUDE.md + expose emailStatus in invite response
CLAUDE.md:
- Add RESEND_API_KEY, EMAIL_FROM, NEXT_PUBLIC_APP_URL to required env vars
- Document DB migration pattern (journal + hot-fix via debug-migration POST)
- Document R2 3-step proxy upload pattern (CORS note)
- Document users.image (NOT avatar_url) and two separate photo features
- Document admin auth server-component pattern
- Document family_invites fix, invite flow, cancel invite
- Full data storage table with all localStorage keys

Invite route:
- Return emailStatus in POST response so caller can see if Resend fired
  or why it failed (noKey / error message)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 15:15:53 +05:30
4dcdc5a572 feat(invites): add cancel button to revoke pending invites
- New DELETE /api/invites/[id] endpoint — only the owning family can delete
- Settings page shows expiry date on each pending invite
- Cancel button removes invite instantly from list (optimistic UI)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 14:47:43 +05:30
781dd8f1df fix(invites): hot-apply family_invites migration via debug-migration endpoint
Add ALTER TABLE steps to debug-migration POST so the columns can be applied
immediately via the running app without waiting for a full Dokploy redeploy.
Also revert invites routes to use display_name/accepted_at now that the
hot-fix path exists.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 14:43:15 +05:30
6d7feca397 fix(invites): remove dependency on missing display_name / accepted_at columns
The family_invites migration hasn't run yet on production. Work around by:
- Removing display_name from INSERT and SELECT (optional field anyway)
- Removing accepted_at IS NULL filter from GET and accept queries
- DELETE the invite row on accept instead of marking accepted_at — keeps
  invites single-use without needing the extra column

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 14:38:53 +05:30
b01e0596c1 feat(invites): auto-send invite email via Resend + register migration in journal
- Add sendFamilyInviteEmail() to email.ts using existing Resend setup
- Wire into POST /api/invites — fetches inviter name + family name, sends
  warm invite email with Accept Invitation button linking to /invite/{token}
- Email is non-fatal: invite is created even if email send fails
- Register 0006_family_invites_missing_cols in _journal.json so Dokploy
  auto-applies the migration on next deploy

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 14:24:31 +05:30
cff17a079d fix(profile): use correct image column on users table (not avatar_url)
users.avatar_url doesn't exist — the column is `image`. Querying/updating
a non-existent column caused a SQL error on every profile load (blank name
& email) and on every avatar save/delete.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 14:07:49 +05:30
7816247073 fix(profile): fix parent avatar upload with 3-step proxy pattern + remove photo
- Switch from broken FormData POST to 3-step flow: POST init → PUT /api/upload proxy → PATCH save
- Add remove photo option that clears DB and deletes R2 object (DELETE /api/auth/avatar)
- Add deleteOldAvatar() helper that cleans up old R2 object on every upload
- No orphaned objects in R2, no wasted storage

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:53:49 +05:30
5235e26cad feat(homepage): remove baby photo option + fix R2 orphan cleanup
- Tapping avatar when a photo exists shows a mini menu: Change photo / Remove photo
- Tapping avatar when no photo exists goes straight to file picker (no menu)
- handleRemovePhoto: PATCHes imageUrl=null, deletes file from R2, clears UI
- PATCH /api/children/[id]: fetches old image_url before update, deletes the
  old R2 object (profiles/ prefix only) when it changes — no more orphaned files
- updateChildImage in FamilyProvider now accepts string | null

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:44:10 +05:30
afae041208 fix(homepage): fix baby profile photo upload failing due to CORS
Direct PUT to R2 presigned URL is cross-origin — browser blocks it.
Route the upload through the existing PUT /api/upload server proxy instead,
same pattern used for memories. Also return `key` from children POST so
the proxy call has the R2 object key without needing the presigned URL.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:37:36 +05:30
fa5e27bfd9 feat(profile): working profile photo upload for parent users (mama/daddy)
- New POST /api/auth/avatar — accepts multipart FormData, uploads image to
  R2 under avatars/{userId}/{ts}.ext, saves URL to users.avatar_url
- GET /api/auth/profile now returns avatarUrl field
- /profile page: real avatar display (image or initials fallback), hidden
  file input wired to "Change Photo" button, spinner overlay while uploading,
  inline success/error message; name save and photo upload are independent

NOTE: This is the parent user avatar (mama/daddy). The baby profile photo
on the homepage card is separate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:32:10 +05:30
b91595316d fix: use APP_URL for OAuth/verify redirects behind Dokploy proxy
Behind Traefik, request.url resolves to http://0.0.0.0:3000/... (the
internal Docker address). Using that as the redirect base sent browsers
to 0.0.0.0, causing ERR_SSL_PROTOCOL_ERROR. Switch all server-side
redirects in the Google callback and verify-email routes to use
NEXT_PUBLIC_APP_URL (with tia.manohargupta.com fallback).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 13:01:01 +05:30
6bdaade777 feat: email verification + Google OAuth
- Signup now creates unverified users and sends a verification email
  (Resend); dev falls back to [VERIFY-LINK] console log
- /api/auth/verify-email: single-use token handler, mints tia_session
  on success, redirects to /onboarding
- /api/auth/resend-verification: rate-limited (3/hr), enumeration-safe
- Sign-in gated on email_verified — unverified accounts get 403 with
  needsVerification flag so the UI can show the resend button
- Google OAuth via arctic v3: PKCE + state anti-CSRF, find-or-create
  user, writes accounts row, mints tia_session
- Login page: Google button, check-email screen, resend link on 403
- drizzle/0005_email_verification.sql: creates email_verifications
  table + backfills all existing users as verified (runs automatically
  on container start before app boots)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 12:56:02 +05:30
23a365309b feat: admin orphaned family management + user journey analytics
- Orphaned families (0 members) now visible with amber badge in /admin/families
  with filter tab and explicit "Delete Family + Data" cascade delete button
- Delete confirmation shows exact counts: children, logs, memories
- Delete child button added to /admin/children with count confirmation
- New DELETE /api/admin/families/[id] — full cascade delete (children, feeds,
  diapers_logs, sleeps, vaccinations, growth, memories, chat, etc.)
- New GET/DELETE /api/admin/children/[id] — cascade child delete with counts
- Extended families GET to include logCount + memoryCount per family
- New /api/admin/engagement — feature adoption %, per-family engagement table,
  AI usage stats (30d), daily activity chart using correct table names
- /admin/analytics fully redesigned: adoption funnel bars, per-family engagement
  table (sortable, filterable by activity), AI cost tab with INR breakdown
- Fixes wrong table names in old analytics (activity_logs, growth_records → real tables)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 09:38:34 +05:30
c2695435f2 Fix broken admin actions: delete cascade, error feedback, loading states
Users page:
- Delete was silently failing with FK constraint violations — sessions,
  accounts, support_tickets and family_members all have ON DELETE no action
  refs to users. Now cascades in correct order before deleting the user.
- Added error/success toast notifications so failures are visible
- Delete button shows loading spinner while in-flight; all buttons
  disabled during operation to prevent double-submit
- Always optimistically removes row on success (no full refetch needed)

Families page:
- Replaced browser prompt() for "New Family" with inline form — prompt()
  is blocked in some environments (CSP, iframes, browser settings)
- Fixed role-before-email bug: role dropdown was silently lost when changed
  before typing email, because onChange reset the whole addMember state.
  Now uses per-family form state with stable field updates.
- Remove member button shows loading spinner; disabled during operation
- Tier change button shows loading; disabled during other tier changes
- Added error/success toast notifications for all actions

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 08:59:20 +05:30
23010e9d90 Admin panel overhaul: activity monitor, real DB wiring, tier management
- New /admin/activity page: live login events, failed attempts, active
  sessions from audit_log + sessions tables; auto-refresh toggle
- New /api/admin/activity route: queries audit_log + sessions for
  stats (active sessions, logins/failures 24h, signups 7d) and events
- Fix /api/admin/stats: real growth charts (families/users by day),
  real children-by-age, real conversion rate, active sessions count,
  and login/failure counts — was all hardcoded empty arrays before
- Fix /api/admin/analytics: avg logs per family now divides by actual
  family count instead of hardcoded 1
- Dashboard: 6-card grid adding Active Sessions + Failed Logins 24h
  with links to Activity Monitor; bar charts now show hover counts
- Families: inline tier upgrade/downgrade button (Pro ↑ / Free ↓)
  wired to existing PATCH API; member panel polished
- Support: admin reply thread using support_responses table; Cmd+Enter
  to send; conversation view with original message + admin replies;
  auto-moves ticket to in_progress on first reply
- Settings: honest read-only display for env-var-controlled settings
  (pricing, AI config); editable free-tier limits that write to DB
- New /api/admin/families/limits route for bulk free-tier limit update
- Sidebar: added Activity Monitor nav item

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 08:35:01 +05:30
581fdb074d fix: post menu clipping, add edit post, fix image display + fullscreen
- Remove overflow-hidden from PostCard root so the ⋯ dropdown menu
  is no longer clipped by the card boundary
- Add Edit Post option to menu (own posts only) with inline textarea
  and Save/Cancel; calls new PATCH /api/circles/[id]/posts/[postId]
- Add PATCH endpoint: author-only text edit
- Fix image display: object-contain (no crop) instead of object-cover
- Add tap-to-fullscreen lightbox: clicking any post image opens a
  full-screen black overlay with the image at natural size, ✕ to close

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 07:56:10 +05:30
27125047bb fix: route circle post image upload through server to avoid R2 CORS
Direct browser PUT to R2's S3 endpoint is blocked by CORS. Replace the
presigned-URL client-side upload with a server-side upload endpoint:
client sends FormData → server uploads to R2 with PutObjectCommand →
returns tmpKey. No browser-to-R2 connection needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 07:35:59 +05:30
66a765e75f fix: image upload in circle posts - handle empty content type + show errors
- Server: normalise empty/missing MIME type by sniffing file extension so
  iOS HEIC/HEIF and camera photos (which send empty type) are accepted
- Server: add image/heif and image/gif to allowed types
- Server: return normalised contentType in presign response
- Client: check presignRes.ok before uploading; use server contentType
  for the PUT to R2 so the header matches what was signed
- Client: show error message in modal instead of silent catch

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 02:18:14 +05:30
c24392f0a1 feat: email-based circle invites with in-app notifications
- Admin invites by entering email instead of copying a link
- If email matches existing Tia user → creates pending invite visible
  on their Circles page with Accept/Decline buttons
- If email is not registered → sends Resend email with signup link
  that lands them directly in the circle after account creation
- DB migration adds invited_email + invited_family_id to circle_invites
- New GET /api/circles/invites endpoint for pending invite banners
- Remove clipboard-copy approach entirely

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 02:05:10 +05:30
9f7ab870ba fix: pass ISO string not Date object to sql.unsafe for expires_at
postgres.js sql.unsafe() doesn't serialize Date objects in parameterized
queries — caused TypeError crashing the invite creation endpoint.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 01:55:48 +05:30
21f88459d7 fix: wrap entire invite POST handler in top-level try-catch
Catches errors from the circle_members SELECT query and auth
that were escaping the narrower try-catch and returning empty 500s.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 01:54:11 +05:30
3d7ff9adb5 fix: surface invite creation error + fix form field id/name attributes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 01:51:51 +05:30
b7fb34fdde temp: auth-protected one-shot migration endpoint 2026-05-24 01:45:07 +05:30
7fe60dc1af temp: debug migration status endpoint 2026-05-24 01:36:10 +05:30
acb9d66815 temp: surface DB error in circles POST for diagnostics 2026-05-24 01:31:26 +05:30
5fdb69679d Circle feature: C0–C9 multi-tenant social groups (Sprint 9 + 10)
Adds full circle functionality — private social groups for trusted families
to share milestones, memories, and posts with reactions and comments.

- 7-table DB migration: circles, members, invites, posts, comments, reactions, reports
- 11 API routes: create/list circles, posts feed, comments, emoji reactions, invite tokens, join flow, member management, reporting
- 3 new pages: /circle (list), /circle/[id] (feed + PostCard + CreatePostModal), /circle/join/[token]
- Copy-on-share for memory photos (independent R2 objects, never references originals)
- Admin controls: invite generation, member promote/demote/remove, last-admin guard
- C9 privacy consent screen before first post
- Menu entry added

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 01:04:50 +05:30
c732d2d7c2 Add baby profile photo change from home screen
- New PATCH /api/children/[id] — updates image_url in children table
- New POST /api/children/[id] — returns presigned R2 URL for profile
  photos (stored under profiles/{childId}/ prefix, no memories row)
- FamilyProvider: expose updateChildImage() so UI updates instantly
  without a full re-fetch after upload
- Home page baby card: photo avatar is now a separate tap target from
  the growth link. Tap photo → file picker → upload to R2 → save URL.
  Camera overlay (📷) appears on hover/tap;  shown while uploading.
  Tapping name/age/arrow still navigates to growth as before.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-24 00:51:45 +05:30
164206c023 Activity page overhaul — 9 UX improvements
- FAB raised to bottom-20 to clear fixed bottom nav
- Branded loading: bouncing 🍼 😴 🚼 emojis
- Back button: white pill with shadow (matches other pages)
- Generate History moved to ⋯ overflow menu (keeps header clean)
- Filter pills: emoji labels (🍼 Feed / 😴 Sleep / 🚼 Diaper) + scrollbar-hide
- Daily summary bar: today's feed/diaper/sleep counts at a glance
- 4-day overview strip: quick multi-day snapshot above timeline
- Collapsible guidelines card: collapsed shows x/target fractions,
  expands to progress bars with actual/target display
- Today/Yesterday/weekday labels in timeline; Today styled in rose
- Better empty state with emoji and "Tap + to start logging" CTA
- Tap any log row → action sheet with Edit and Delete
  Edit: pre-fills LogModal with same values, deletes old log on save
  Delete: inline confirmation (no browser confirm()), then refresh
- New DELETE + PATCH handlers at /api/logs/[id] with family ownership check

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 23:07:02 +05:30
70ff02c930 feat(home): overhaul home screen with bottom nav and UX improvements
- Add persistent bottom navigation bar (Home / Activity / AI / Menu)
- Fix TodaySummary bug: last-log times now show today's events only
- Replace 6 hardcoded AI chips with 3 AI-generated context-aware chips
- Show child's real profile photo in baby card (fallback to 👶 emoji)
- Recent Activity limited to 3 items with "See all →" link to /activity
- "Suggested now" promoted to prominent amber banner with "Log it →" CTA
- Offline pending banner is now a tappable retry button
- Branded loading state with bouncing emoji (🍼 😴 🚼 👶)
- Remove unused Button import from page.tsx
- Expose image_url via /api/children and Child type/FamilyProvider

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 19:08:58 +05:30
1994725101 feat(wardrobe): add complete wardrobe feature (W0–W9)
Schema (W0):
- Add garments, garment_wears, outfits tables with Drizzle migrations
- Drizzle migrations 0001 (garments/wears) and 0002 (outfits) auto-apply on deploy
- RLS policies in drizzle/manual/06-wardrobe-rls.sql (apply via superuser in prod)

API (W1–W9):
- POST /api/garments/upload — direct upload to R2 garments/ prefix with sharp thumbnail
- POST /api/garments/tag — vision tagging via LiteLLM, defensive parse, category validated
- GET/POST /api/garments — list with composable filters, create
- GET/PATCH/DELETE /api/garments/[id] — detail, edit, delete
- POST /api/garments/[id]/wear — log worn date
- GET /api/garments/outgrowth — pure SQL, explicit size ordering (no lexicographic sort)
- GET /api/garments/packing — active garments grouped by category
- GET /api/garments/outfit — Open-Meteo weather + deterministic outfit pairing, no LLM
- GET/POST /api/garments/outfits + DELETE [id] — saved outfits

Pages:
- /wardrobe — grid with status/category/size/season filters + outgrowth nudge
- /wardrobe/add — 3-step capture→vision→form, size required, batch-friendly
- /wardrobe/[id] — detail/edit/status lifecycle + wear history
- /wardrobe/packing — packing checklist by category
- /wardrobe/outfit — weather-aware suggestions with shown basis
- /wardrobe/saved-outfits — view/delete saved combinations

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-23 18:09:22 +05:30
3ebb3055a5 feat(logging): time presets, FAB on activity, today summary, smart defaults
- Extract offline queue to src/lib/offline-queue.ts
- Extract shared LogModal with time presets (Just now/5/15/30min/Custom)
  and smart default pre-fill from last log of same type
- Replace ActivityScroller with TodaySummary (today's counts + last time)
- Fix activity page: GET /api/logs without type param now returns all logs merged
- Fix field naming: log.loggedAt / log.amount (camelCase throughout)
- Add FAB to activity page for zero-navigation quick logging
- Recent Activity shows 5 most recent entries with correct field names

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 11:21:00 +05:30
515e93aae1 fix(activity): repair full log flow — quick log → activity page
Three bugs fixed:

1. GET /api/logs required a `type` param; activity page calls without one,
   always receiving {error} → empty list. Fixed: when type is omitted,
   merge feeds + diapers_logs + sleeps, sort by time, return camelCase
   fields (loggedAt, subType, amount) matching the activity page interface.

2. Type-specific queries (used by homepage) still return raw snake_case
   rows unchanged so the homepage "Recent Activity" section is unaffected.

3. LogModal initialised subType as "breast_milk" regardless of log type.
   Adding a useEffect to reset it (breast_milk / wet / nap) when type
   changes prevents bad enum values from being sent for diaper/sleep logs.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 10:49:00 +05:30
291eb4793b feat(g5-g6): age-aware UX + mama affiliate page
G5 — Age-Aware UX:
- useStageCheck hook: maps birth date → BabyStage (newborn/infant/sitter/crawler/toddler/walker)
- Time-of-day fast-log suggestion chip on home page (time × stage matrix)
- Milestones page: 25 WHO/AAP milestones, category filter, progress bar, inline date picker
- Milestones API: GET (merged definitions + achievements), POST (upsert), DELETE (un-mark)
- DB: milestone_achievements table with unique(child_id, milestone_key)
- Milestones 🌟 added to menu

G6 — Mama Affiliate Page:
- member_profiles, recommended_products, product_clicks tables
- /api/profile CRUD (GET/PUT), /api/profile/products (GET/POST/PATCH/DELETE)
- Public routes: /api/profile/[slug] and /api/profile/[slug]/click (IP hashed)
- /settings/profile: slug + bio editor, product list with ↑↓ reorder + click counts
- /m/[slug]: beautiful public page (gradient bg, product grid, Shop → click tracking)
- Settings page link to profile setup

DB migrations: 0014_milestones, 0015_affiliate.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:59:17 +05:30
c2cabc01d3 feat(g1-g4): design system, memories pipeline, medical tracking, AI brain
G1 — Design System: 14 UI primitives (Button, Card, Modal, Sheet, Input,
Textarea, Select, EmptyState, LoadingShimmer, ConfirmDialog, WashiTape,
Badge, Avatar, Tabs), PageTransition with Framer Motion, sun/moon CSS vars,
Caveat font, /dev/components visual showcase.

G2 — Memories Pipeline: R2 presigned uploads, Sharp thumbnail generation,
LiteLLM vision captions + pgvector embeddings, CSS masonry gallery with
infinite scroll, private toggle, semantic search fallback to ILIKE.

G3 — Medical: dose log + correction audit trail, IAP vaccine bulk import,
emergency escalation page, pediatrician phone in settings.

G4 — AI Brain: keyword guardrail → LLM classifier → structured DB tool-use
(7 tools) → memory search → general parenting handler; ai_usage table;
22-case medical bypass safety test suite.

DB migrations: 0011_memories, 0012_medical_doses, 0013_ai_usage.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 17:48:34 +05:30
c787caa821 fix: add Resend for password reset emails + install F1-F2 deps
- Wire Resend to /api/auth/reset-request with fallback for dev
- Install: sharp, recharts, next-pwa, resend, @react-pdf/renderer, @types/sharp

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 16:42:45 +05:30
c459b4411a fix: secure /api/ai endpoint and remove debug routes
- Add auth to /api/ai via requireFamily middleware
- Remove /api/ai and /api/auth/debug from public routes
- Delete debug/test routes that expose internal state

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 16:41:47 +05:30
855541f4e2 fix(growth): convert dates to strings before SQL query
Postgres driver expects strings, not Date objects

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 14:00:42 +05:30
318b277e44 feat(growth): add edit/delete, goals, CSV export, and WHO percentile enhancements
- Add PUT and DELETE endpoints for growth records
- Add CSV export for pediatrician visits
- Add goal tracking with localStorage persistence
- Color-coded percentiles (green/yellow/red zones)
- Show WHO percentile lines (15th, 50th, 85th) on chart
- Growth velocity indicator (kg/month between readings)
- Enhanced WHO standards card with actual vs target + goal progress
- Better empty state with encouraging prompt
- Fix UUID type for growth record IDs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 13:52:02 +05:30
1628588d5a fix(ai): correct env var names and fresh session per home modal open
AI route was reading LITELLM_URL/LITELLM_KEY but env vars are named
LITELLM_BASE_URL/LITELLM_API_KEY — causing 503 on every request.

Home page chat now creates a fresh session each time the modal opens
and resets homeSessionId on close, so conversations don't pile into
the same old session.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 13:31:30 +05:30
50a6827bfd fix(admin): use bcrypt in admin password set to match signin verification
Simple hash_ format stored by admin PATCH was incompatible with bcrypt
verification in /api/auth/signin, causing "Invalid password" on login.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:57:33 +05:30
6ce459e4f6 fix(admin): restore stats API from debug stub
Route was returning { test: "ok", count: N } instead of the expected
overview/conversions/growth/childrenByAge structure, crashing the dashboard.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 12:27:36 +05:30
5e36c8a848 remove: /api/setup route — bootstrap should be done via psql, not HTTP
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:58:01 +05:30
07bb149dd8 fix: notifications IDOR — verify child belongs to caller's family
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:57:57 +05:30
389f66955c fix: stop leaking password reset tokens in response
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:57:54 +05:30
d94a15e38e Fix admin login: redirect path, add logout, remove unused code
- Fix redirect from /admin/login to /admin-login
- Add DELETE endpoint for logout
- Connect logout button to API
- Remove unused admin state/localStorage

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 10:54:15 +05:30
ffaa92cd13 Revert: Remove token from response (HttpOnly cookie is sufficient)
Login page checks cookie on load via useEffect, no need for
localStorage token. More secure this way.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 10:52:08 +05:30
73cfd2a761 Fix admin login response missing token
The API was setting the session token as a cookie but not
returning it in the JSON response. Login page expects token
in the response to store in localStorage.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 10:50:35 +05:30
d18213fc7d Test route 2026-05-17 10:47:35 +05:30
dc54fd31c6 Debug route 2026-05-17 10:45:11 +05:30