POST /api/events/duplicate-day — copies all events from sourceDate to
targetDate, shifting timestamps by the day offset while preserving
time-of-day, duration, notes, and metadata. Ownership-gated per family.
In the timeline, each day header gains a copy icon button. Clicking it
expands an inline date picker; confirming triggers the API and
invalidates the events query.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add isBreastfed and hasPump flags to Baby model. When disabled,
hide breastfeeding/pump options from quick-add FAB, dashboard
counters and quick-log groups, and the milk stock nav link.
Settings page gains toggle UI to configure per baby.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Inline edit form per stock (volume, location, expiry, notes)
- Assembly mode: select 2+ lots, merge into one with combined volume
and earliest expiry date, deletes originals
- POST /api/milk now accepts expiresAt override directly
- Fix OTHER event type icon (add FileText to EventIcon map)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Remove quick-template feature from dashboard
- Increase photo upload limit from 5MB to 30MB
- Add combined diaper toggle (wet + stool in one save) to event modal
- Show all event types in quick-add FAB and dashboard quick-log grid
- Add OTHER generic event type for freeform notes (schema + migration)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- webhooks: block SSRF — reject localhost/127.x/169.254.x/10.x/192.168.x
URLs at creation and silently skip at dispatch time
- medication-profiles/last-intakes: add family ownership check on babyId
- invite/send-email: build invite URL from NEXTAUTH_URL env only;
drop req.headers.get("origin") to prevent host header injection
- baby POST: validate birthDate is a real date; add required-fields check
- milk POST: validate storedAt before new Date()
- journal GET: validate date filter before new Date()
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- 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>