- v1/events GET: offset now clamped ≥0, limit fallback prevents NaN
being passed to Prisma take/skip
- v1/growth POST: validate date is a valid ISO date before new Date()
to avoid Invalid Date silently propagating into DB
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
All collection routes (events, growth, doctor-notes, journal, milestones,
vaccinations, milk, reminders, teeth, search, export) now verify the requested
babyId belongs to the authenticated user's family before querying or writing.
All [id] mutation routes verify record ownership via nested baby→familyId
before any PATCH/DELETE. Additional fixes: admin config masks sensitive
secrets in GET response, invite send-email enforces PARENT role, photo
serving requires authentication, baby PATCH restricted to own-family PARENT.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
When endedAt is omitted, the computed end time was stored as a UTC
string then passed through toUTC() which re-parsed it as local time,
applying the timezone offset twice. Express resolvedEndedAt as a local
datetime string to match other form fields.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Photos were lost on redeploy because they wrote to public/uploads
inside the container with no volume mount. Now stored in uploads_data
named volume via UPLOAD_DIR env var (already supported by upload route).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The deletePhoto function fetched /api/events/:id which has no GET handler
(404). This gave undefined metadata, so the PATCH would set metadata to
{photo: null} only, losing other fields — and on a 404 it likely didn't
execute correctly at all. Now reads metadata from the already-loaded
allEvents array and deletes the photo key before PATCHing.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- search: date range (7j/30j/90j/Tout) + type (Repas/Sommeil/Couche/Santé) filter chips
- stats: missing-day gaps filled with 0 — lines no longer break on no-data days
- notes: beforeunload handler warns before closing tab with unsaved changes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- nav: aria-label on baby switcher select
- dashboard: active timers show baby name
- growth: PDF export includes percentile band per weight row
- settings: invite link note (permanent, reset if compromised)
- timeline: select mode toggle shows "Annuler" when active
- photos: delete photo from lightbox (PATCHes event metadata)
- notes: invisible date input overlay for date-jump via calendar picker
- IMPROVEMENTS.md: updated checkboxes, 8 remaining → 0
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Photos:
- Serve uploads via /api/photos/[filename] from UPLOAD_DIR env var
- Default UPLOAD_DIR=/data/uploads (mount as Docker volume for persistence)
- Upload route creates dir on demand, no root-permission issue
- Dockerfile creates /data/uploads owned by nextjs user
Growth:
- Show chart with single measurement (was hidden until 2+ points)
- PC label expanded to "PC — Périmètre crânien (cm)"
- WHO percentile ⓘ tooltip explaining P50/P3-P97
- Date field defaults to today in add-measurement form
Stats:
- Heatmap legend shows actual counts (0 1 2 3 4+) not just Moins/Plus
- Sleep timeline labeled "Fenêtre 19h – 11h"
Timeline:
- Filter type persisted in URL param (?type=...) — survives navigation
- Inline note save shows ✓ flash after blur
- Photo thumbnails open in new tab
Photos:
- Lightbox wraps: next on last → first, prev on first → last
Search:
- Result count header "X événements · Y notes"
- Event results link to /timeline?date=...&type=...
- Note results link to /notes?date=...
Notes:
- Unsaved indicator (amber dot) while textarea dirty, green ✓ after save
Milestones:
- Sort by date ascending within each month group
Settings:
- Pushover key field has eye toggle (show/hide)
- API key delete shows confirmation with key name
- All sections wrapped in collapsible SectionCard accordion
Dashboard:
- Empty state callout for new users with no events today
Event modal:
- Milk lot selection shows "Sélectionnez un lot — il sera marqué comme utilisé"
- Photo upload validates 5MB client-side before sending
- Timer resume shows "Reprise du chronomètre" when loaded from localStorage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
- 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>
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>
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>
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>
- 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>
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>
- 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>
- 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>
- 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>
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>
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>
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>
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>
- /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>
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>
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>
- 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>
- 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>