Light pastel bg colors were unreadable on dark backgrounds. Add darkBg
variants for each stool color and a MutationObserver-based isDark hook
to switch between them at runtime.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
session.user is narrowed inside the early-return guard but TypeScript
does not carry that narrowing into the Prisma transaction callback.
Extract to a const immediately after the guard.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
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>
- 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>
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>
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>
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>