Commit Graph

41 Commits

Author SHA1 Message Date
arnaudne f59a6c5d8a feat: REST API v1 + API key management + Swagger docs
**API system**
- ApiKey model: SHA-256-hashed tokens (gw_<hex>), per-family, scoped
- Migration: 20260615210000_api_keys
- src/lib/api-auth.ts: verifyApiKey(), hasScope(), generateApiKey(), prepareKey()

**V1 endpoints** (all require Bearer gw_ token):
- GET  /api/v1/babies              — list family babies (any read scope)
- GET  /api/v1/events              — query events (events:read), babyId/type/from/to/limit/offset
- POST /api/v1/events              — log event (events:write), full metadata support
- GET  /api/v1/growth              — growth logs (growth:read)
- POST /api/v1/growth              — add measurement (growth:write), weight in grams
- GET  /api/v1/summary             — today's counts + sleep + last feed (summary:read)
- GET  /api/v1/milk                — stock lots + totalMl (milk:read)

**API key management**
- GET  /api/api-keys               — list keys (session auth)
- POST /api/api-keys               — create key, returns raw token once (session auth)
- DELETE /api/api-keys/[id]        — revoke key (session auth)

**Documentation**
- GET /api/v1/openapi.json         — OpenAPI 3.0 spec (CORS open)
- GET /api-docs                    — Swagger UI (CDN, dark themed)

**Settings UI** — "Clés API" section: create key with scope checkboxes, copy token banner (shown once), revoke, link to /api-docs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:33:25 +02:00
arnaudne 009090b381 feat: running timers, symptom log, search, PDF export
- dashboard: ActiveTimers widget — live h:mm:ss for any in-progress timed event (breastfeed, nap, sleep, walk…), Terminer button patches endedAt
- dashboard: reorganised quick-log groups; Santé group (Température, Traitement, Symptôme)
- events: SYMPTOM type — tag chips (Fièvre, Reflux, Colique, Éruption, Toux, Congestion, Diarrhée, Vomissement, Irritabilité, Pleurs excessifs), stored in metadata.tags
- prisma: migration adding SYMPTOM to EventType enum
- search: GET /api/search (events by notes ILIKE, journal by content ILIKE), /search page with debounced input + keyword highlight
- nav: Recherche link added to LINKS + More drawer
- growth: PDF export button — generates formatted table with all measurements, zebra rows, downloads croissance-{name}.pdf

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:27:01 +02:00
arnaudne cd677d9244 fix(growth): show distinct points for same-week measurements
Use fractional week values (float) for actual data points instead of
floor'd integers. Two measurements on different days within the same
week now appear at distinct x positions. XAxis set to type="number"
with continuous domain so the chart renders them correctly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:11:08 +02:00
arnaudne e27f42942c feat: feeding heatmap, diaper streak, quick note, handoff summary, weight percentile alert, growth curve labels
- stats: 7×24 feed density heatmap (Mon–Sun × 24h, indigo intensity, dark mode)
- stats: diaper streak card — current consecutive days ≥4 couches + best streak for period
- dashboard: QuickNoteWidget — inline journal note (sky card) without leaving dashboard
- dashboard: HandoffWidget — localStorage "since last visit" digest (feeds/diapers/sleep) shown after 2h gap
- growth: replace S0/S1/S2 x-axis with human labels (Naiss., 1sem, 2sem, 1m, 2m … 1 an)
- growth: tooltip shows age in full words (e.g. "3 mois 1 sem.")
- cron: POST /api/cron/weight-percentile-alert — alerts when baby weight exits configurable WHO band (weight_percentile_min / weight_percentile_max SystemConfig keys, deduped per measurement)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 17:05:04 +02:00
arnaudne b417eb648c fix(build): disable webpack build workers to reduce memory
Next.js spawns per-worker webpack processes — each has its own heap.
On low-RAM servers total usage exceeds available memory → SIGKILL (silent).
webpackBuildWorker: false runs compilation in main process only.
Also lower JS heap cap to 512MB to leave room for OS + pnpm.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:21:01 +02:00
arnaudne 1c837a3aae fix(build): use webpack instead of Turbopack for production build
Turbopack (Next.js 16 default) OOM-kills on low-memory servers — BuildKit
loses the connection and reports EOF. Webpack uses significantly less RAM.
Also bumped NODE_OPTIONS heap limit to 2048MB.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:10:49 +02:00
arnaudne 18074524e8 fix(docker): cap Node.js heap to 1536MB during build
Prevents OOM kill on low-memory servers during Next.js + Workbox build.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 16:05:31 +02:00
arnaudne 6714e26716 fix(build): remove sharp devDep — breaks Docker --ignore-scripts install
sharp uses postinstall to download native binaries. Docker build uses
pnpm install --ignore-scripts which skips this → build fails on server.
PNG icons already generated and committed, sharp no longer needed.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:55:25 +02:00
arnaudne ef2ee3cf7a fix(pwa): add turbopack config to silence next-pwa webpack warning
next-pwa registers a webpack config; Next.js 16 requires turbopack: {}
when a webpack config exists and no turbopack config is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:52:22 +02:00
arnaudne 95e02aca57 feat(pwa): full PWA setup with service worker and PNG icons
- Add @ducanh2912/next-pwa with NetworkFirst runtime caching (24h, 200 entries)
- Disabled in development to avoid SW interference
- Generate icon-192.png, icon-512.png, apple-touch-icon.png from SVG
- Update manifest.json to use PNG icons (required for Android install prompt)
- Add apple-touch-icon + appleWebApp metadata to layout
- Add /offline fallback page shown when network unavailable

Offline behavior: cached reads work on all platforms; background sync
for new events not supported (iOS Safari has no Background Sync API).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:50:29 +02:00
arnaudne 78fc31252e fix(settings): guard Notification API for iOS Safari
Notification is undefined on iOS Safari — bare access crashes on render.
Added "Notification" in window check before accessing .permission.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:47:34 +02:00
arnaudne eb1e5ee3fe fix(ui): lift modal above tab bar, remove duplicate last activities
- Modal container pb = 68px + safe-area-inset-bottom so footer always
  above the tab bar on mobile (was hidden behind it)
- Remove "Dernières activités" from dashboard (duplicates today's feed)
- Quick log grid goes full-width, 6 cols on sm+

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:46:18 +02:00
arnaudne 8152c02995 feat(pump): show all available lots for merge, add expiry input
- Replace today-only lot filter with all available lots (fridge + freezer)
- Show lot date (d MMM HH:mm) not just time for clarity
- Add expiry input when creating new lot (hidden when merging)
- Expiry unit label adapts: heures/jours/mois per storage location

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:42:16 +02:00
arnaudne 9fb1a726dd feat(pump): link pump events to milk stock, fix expiry units
- PUMP modal: "Ajouter au stock" toggle with location selector
- Merge option: pick existing today's lot to add volume to (expiry kept)
- On save: POST new MilkStock or PATCH existing volume
- Milk page: expiry input now in hours/days/months per location
- Milk page: "soon" alert threshold scales per location (freezer=7d, fridge=24h, room=2h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 15:34:35 +02:00
arnaudne 142d27e85c fix(modal): proper mobile bottom sheet — sticky header/footer, scrollable body
- rounded-t-2xl on mobile, full-width (no side padding)
- maxHeight = 100dvh - safe-area-top - 68px nav - safe-area-bottom
- header and footer flex-shrink-0, body flex-1 overflow-y-auto overflow-x-hidden
- save buttons in pinned footer, larger touch targets (py-3, rounded-xl)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 19:02:23 +02:00
arnaudne 493f6ab7fc fix(compose): drop service_healthy, use service_started + sleep
pg_isready healthchecks broken server-wide (root_db, hoppscotch-db also
unhealthy). Remove healthcheck block, depend on service_started only.
migrate sleeps 5s to give postgres time to accept connections.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:38:05 +02:00
arnaudne 750c2c2f60 fix(compose): use CMD form for pg_isready healthcheck, add start_period
CMD-SHELL drops pg_isready from PATH on some Alpine setups. Switch to
explicit CMD array form with -h 127.0.0.1. start_period gives postgres
time to initialize before failures count against retries.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 18:02:15 +02:00
arnaudne 8ed0113d6d fix(build): add force-dynamic to server components with auth/DB access
Next.js "Collecting page data" worker crashes silently when a server
component calls auth() or prisma without DATABASE_URL at build time.
force-dynamic skips static generation for these pages entirely.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:50:03 +02:00
arnaudne 25fe1b8e36 chore(docker): add CACHE_BUST arg to force clean builds
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:42:34 +02:00
arnaudne 4c198e3020 fix(modal): clear bottom nav on mobile, cap max-h to 75vh
pb accounts for 64px tab bar + safe-area-inset-bottom so modal never
slides under the nav. max-h-[75vh] on mobile prevents overflow.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 17:01:58 +02:00
arnaudne 8ce5220520 fix(i18n): translate TUMMY_TIME label to "Plat ventre"
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:58:11 +02:00
arnaudne e9c8d7e97e feat: daily digest, photo timeline, milk expiry cron, batch delete, duration chart
- /api/cron/daily-digest: last feed/diaper, 24h sleep total, next med due
- /photos: photo grid grouped by day, full-screen lightbox with prev/next
- /api/cron/milk-expiry: push alert for lots expiring within 24h
- Timeline: select mode with checkboxes, confirm + bulk DELETE
- Stats: avg NAP/NIGHT_SLEEP duration trend dual-line chart (30d)
- Nav: Photos link added under "Vie du bébé" drawer group

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:31:47 +02:00
arnaudne c34796452c chore: add new feature ideas to backlog
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:26:13 +02:00
arnaudne 4825604a52 fix(settings): move Section out of render to fix input focus loss
Section defined inside the component was recreated as a new type on
every keystroke, causing React to unmount/remount it and drop focus.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:16:51 +02:00
arnaudne 899d6e6e19 fix(settings): merge Bébé + Bébés into single section
Each baby row is tappable; inline edit form expands under the selected
baby. Passes baby data directly to avoid stale context on first click.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 16:15:04 +02:00
arnaudne 81d7e5ad73 feat: sleep chart, bottle-from-stock, growth alert cron
- SleepTimeline component in stats: 7-night bar chart, 19h–11h window,
  NIGHT_SLEEP (indigo) and NAP (violet) blocks, pure CSS positioning
- Bottle modal: fetch available maternal milk lots, chip picker,
  auto-selects oldest, marks selected lot as used after save
- /api/cron/growth-alert: Bearer-auth POST, configurable threshold via
  growth_alert_days SystemConfig key (default 30 days), Pushover alert
  to all family users per baby with no recent weight log
- Admin config ALLOWED_KEYS: add cron_secret + growth_alert_days

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:58:13 +02:00
arnaudne 24daa5eb15 fix(build): type-narrow metadata.photo, add prisma generate to build
- timeline: guard ev.metadata.photo with typeof === "string" (unknown not assignable to ReactNode)
- Dockerfile/CI must run prisma generate before pnpm build — EventTemplate missing from client

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:49:10 +02:00
arnaudne 63effbf85b ux: dashboard grouping, responsive grids, grouped nav drawer, modal polish
Dashboard:
- Split 12-item quick-log into two labeled groups (Alimentation & Soins / Sommeil & Activités)
- "Voir tout →" link on today's timeline when events present
- Quick-log button: hover highlight indigo instead of grey for clearer affordance

Event modal:
- Timer: text-3xl on mobile, text-4xl on sm+ (prevents overflow on narrow screens)
- Date/time inputs: CalendarDays + Clock icons inline in each field for clarity

Stats:
- Today summary 3-col: gap-2 md:gap-3, p-3 md:p-4, text-xl md:text-2xl (no text clipping on mobile)
- Sleep/feeding summary grids: gap-2 md:gap-4

Calendar:
- Cell min-height: 52px mobile, 64px sm+ (reduces grid height on small screens)
- Day detail: events sorted by time, duration shown as HH:mm → HH:mm for ranged events
- Empty day state uses card container instead of bare text

Medications:
- Empty state: Pill icon + subtitle copy, centred card

Milk:
- Used/archived section: "… et N lots de plus" indicator when > 20 items

Nav drawer (mobile):
- Grouped secondary links: Suivi / Santé / Vie du bébé sections with section headers
- Medications: correct Pill icon (was duplicating Bell/Stethoscope)
- Ungrouped items fall through to a full-width list row

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:36:54 +02:00
arnaudne 42c580d7e2 feat: maintenance banner, activities log, pinned note, event templates, weekly summary cron
Maintenance mode banner:
- App layout reads SystemConfig maintenance_mode at server render time
- Amber sticky banner for regular users, dimmed indicator for superadmin

Baby activities (3 new event types):
- BATH (Bath icon, sky, timer+duration)
- WALK / Balade (Footprints icon, lime)
- TUMMY_TIME (Baby icon, amber)
- Added to EventType enum, EVENT_CONFIG, event-icon.tsx, dashboard quick-log

Pinned family note:
- Shared across all family members, shown at top of dashboard
- Inline edit with author + timestamp; /api/family/pinned-note GET/PATCH
- pinnedNote + pinnedNoteUpdatedAt + pinnedNoteAuthor fields on Family model

Event templates:
- Named quick-log presets per family (type + label + metadata)
- Dashboard row: tap to open pre-filled EventModal; hover ✕ to delete
- Dropdown to create new template; /api/event-templates GET/POST + /[id] PATCH/DELETE
- EventTemplate model in schema

Weekly summary cron:
- POST /api/cron/weekly-summary — auth via Bearer cron_secret (SystemConfig or env)
- Per family, per baby: feeds count, sleep total+avg, diaper count, latest weight
- Sends Pushover to all family members with pushoverUserKey
- Optional body.familyId to target one family

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:08:54 +02:00
arnaudne 020539e2e2 feat: pump tracker, photo log, milk stock, medication profiles, calendar, reminders, audit log, edit events
New pages:
- /calendar — monthly grid with event dots, day detail on tap
- /medications — CRUD for medication profiles (presets + custom, dose/unit/intervals/crisis mode)
- /milk — milk stock management with location-based expiry, alerts, mark-as-used
- /reminders — recurring medication reminder CRUD with Pushover notifications

New APIs:
- /api/medication-profiles — profiles CRUD + last-intakes endpoint (auto-seeds 10 presets per family)
- /api/milk — milk stock CRUD with auto-calculated expiry
- /api/reminders + /api/reminders/check — reminder CRUD + cron-callable check endpoint
- /api/upload — multipart photo upload to public/uploads/
- /api/invite/send-email — email invitation with family invite link
- /api/admin/audit — last 100 audit log entries (superadmin only)

Event modal improvements:
- PUMP event type (side selector + volume + timer)
- MEDICATION: profile chip picker, next-dose timing status, crisis mode toggle
- Photo attachment (upload + thumbnail preview)
- Datetime inputs split into date + time (iOS Safari datetime-local fix)
- Edit mode via initialEvent prop (pre-fills all fields, saves via PATCH)
- Timer now shows h:mm:ss when >= 1 hour

Timeline:
- Modify button opens pre-filled edit modal per event
- Photo thumbnail in expanded view

Dashboard:
- PUMP in quick-log
- Medication status card (too-soon / available with next-dose time)

Schema additions: MedicationProfile, MedcationReminder, MilkStock, AuditLog models

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-13 15:01:38 +02:00
arnaudne 091af949ee fix(docker): pass AUTH_SECRET not NEXTAUTH_SECRET (NextAuth v5 env var name) 2026-06-13 12:28:11 +02:00
arnaudne 612534a59e fix(auth): check signIn result after register, redirect to login on failure 2026-06-13 12:25:30 +02:00
arnaudne a0371132dd fix(auth): add trustHost: true for reverse proxy deployment 2026-06-13 12:21:39 +02:00
arnaudne 44bb103eb4 fix(docker): construct DATABASE_URL from POSTGRES_PASSWORD variable 2026-06-13 12:12:34 +02:00
arnaudne 3e5e046b59 fix(docker): use DATABASE_URL from env directly, don't construct from raw password 2026-06-13 11:41:24 +02:00
arnaudne f83a74cb53 fix(docker): migrate service uses builder target (has node_modules) 2026-06-13 11:38:08 +02:00
arnaudne be868b70c8 fix(docker): remove --skip-generate flag (not in Prisma 7), use local prisma bin 2026-06-13 11:35:13 +02:00
arnaudne 898a317fc7 fix(docker): use --ignore-scripts on install, fix pnpm 11 build approval error 2026-06-13 10:41:36 +02:00
arnaudne f12512401c fix(docker): copy .npmrc into deps stage to honour ignore-scripts=true 2026-06-13 09:13:37 +02:00
arnaudne bffe51da74 fix(docker): pin pnpm@11.6.0 via npm, fix runner stage prisma copy 2026-06-13 02:02:51 +02:00
arnaudne cd16c354c0 Initial commit — Grow baby tracker
Next.js 16 App Router, Prisma + PostgreSQL, NextAuth v5 JWT.

Features: dashboard quick-log, timeline, growth charts (WHO percentiles),
stats, journal/notes, doctor notes, milestones, vaccinations, settings,
superadmin panel. Mobile-first with sidebar nav + bottom nav + quick-add FAB.
Dark mode, PWA push notifications, multi-family invite system.

Docker: multi-stage Dockerfile + docker-compose with postgres service.
2026-06-13 01:52:46 +02:00