"use client"; import { useState, useEffect } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { EVENT_CONFIG, EventType, formatTimeAgo, formatDuration } from "@/lib/event-config"; import { EventModal } from "@/components/event-modal"; import { EventIcon } from "@/components/event-icon"; import { useBaby } from "@/contexts/baby-context"; import { checkFeedAlert, getThresholds } from "@/lib/notifications"; import { format, isToday } from "date-fns"; import { fr } from "date-fns/locale"; import { AlertTriangle, Clock, CheckCircle2 } from "lucide-react"; const QUICK_LOG_TYPES: EventType[] = [ "BREASTFEED", "BOTTLE", "PUMP", "DIAPER_WET", "DIAPER_STOOL", "NAP", "NIGHT_SLEEP", "MEDICATION", "TEMPERATURE", ]; interface Event { id: string; type: EventType; startedAt: string; endedAt?: string; notes?: string; metadata?: Record; user: { id: string; name: string }; } function getBabyAge(birthDate: string): string { const birth = new Date(birthDate); const now = new Date(); const days = Math.floor((now.getTime() - birth.getTime()) / 86400000); if (days < 7) return `${days} jour${days !== 1 ? "s" : ""}`; const weeks = Math.floor(days / 7); if (weeks < 8) return `${weeks} semaine${weeks !== 1 ? "s" : ""}`; const months = Math.floor(days / 30); return `${months} mois`; } function getEventSummary(event: Event): string { const meta = event.metadata ?? {}; if (event.type === "BREASTFEED") { const side = meta.breastSide === "left" ? "gauche" : meta.breastSide === "right" ? "droite" : "les deux"; if (event.endedAt) return `${formatDuration(new Date(event.startedAt), new Date(event.endedAt))} · ${side}`; return side; } if (event.type === "BOTTLE") { return [meta.volume ? `${meta.volume} mL` : "", meta.bottleType === "maternal" ? "maternel" : "artificiel"].filter(Boolean).join(" · "); } if (event.type === "DIAPER_STOOL") return String(meta.color ?? ""); if (event.type === "TEMPERATURE") return meta.value ? `${meta.value}°${meta.unit ?? "C"}` : ""; if (event.type === "MEDICATION") return String(meta.name ?? ""); if ((event.type === "NAP" || event.type === "NIGHT_SLEEP") && event.endedAt) { return formatDuration(new Date(event.startedAt), new Date(event.endedAt)); } return ""; } export default function DashboardPage() { const qc = useQueryClient(); const [activeModal, setActiveModal] = useState(null); const { selectedBaby, isLoading: familyLoading } = useBaby(); const { data: eventsData } = useQuery({ queryKey: ["events", selectedBaby?.id], queryFn: () => fetch(`/api/events?babyId=${selectedBaby!.id}&limit=50`).then((r) => r.json()), enabled: !!selectedBaby?.id, refetchInterval: 10_000, }); const events: Event[] = eventsData?.events ?? []; useEffect(() => { const lastFeed = events.find((e) => e.type === "BREASTFEED" || e.type === "BOTTLE"); checkFeedAlert(lastFeed ? new Date(lastFeed.startedAt) : null, getThresholds()); const id = setInterval(() => { checkFeedAlert(lastFeed ? new Date(lastFeed.startedAt) : null, getThresholds()); }, 5 * 60 * 1000); return () => clearInterval(id); }, [events]); const lastByType: Partial> = {}; for (const ev of events) { if (!lastByType[ev.type]) lastByType[ev.type] = ev; } const todayEvents = events.filter((e) => isToday(new Date(e.startedAt))); const todayCounts: Partial> = {}; for (const ev of todayEvents) todayCounts[ev.type] = (todayCounts[ev.type] ?? 0) + 1; const { data: medProfiles = [] } = useQuery<{ id: string; name: string; molecule: string | null; intervalHours: number; minIntervalHours: number | null }[]>({ queryKey: ["med-profiles"], queryFn: () => fetch("/api/medication-profiles").then((r) => r.json()), enabled: !!selectedBaby?.id, }); const { data: lastIntakes = {} } = useQuery>({ queryKey: ["last-intakes", selectedBaby?.id], queryFn: () => fetch(`/api/medication-profiles/last-intakes?babyId=${selectedBaby!.id}`).then((r) => r.json()), enabled: !!selectedBaby?.id, refetchInterval: 30_000, }); if (familyLoading) { return (
); } if (!selectedBaby) { return (

Aucun bébé enregistré.

Ajouter un bébé
); } return (
{/* Page header */}

{selectedBaby.name}

{getBabyAge(selectedBaby.birthDate)}

Aujourd'hui

{format(new Date(), "EEEE d MMMM", { locale: fr })}

{/* Today's summary counters */}
{(["BREASTFEED", "BOTTLE", "DIAPER_WET", "DIAPER_STOOL"] as EventType[]).map((t) => { const cfg = EVENT_CONFIG[t]; return (
{todayCounts[t] ?? 0}
{cfg.label.split(" ")[0]}
); })}
{/* Last events */}

Dernières activités

{(["BREASTFEED", "BOTTLE", "DIAPER_WET", "DIAPER_STOOL", "NAP"] as EventType[]).map((t) => { const last = lastByType[t]; const cfg = EVENT_CONFIG[t]; return (
{cfg.label}
{last && getEventSummary(last) && (
{getEventSummary(last)}
)}
{last ? formatTimeAgo(new Date(last.startedAt)) : "—"}
); })}
{/* Quick log */}

Enregistrer

{QUICK_LOG_TYPES.map((type) => { const cfg = EVENT_CONFIG[type]; return ( ); })}
{/* Medication status */} {medProfiles.length > 0 && (() => { const activeMeds = medProfiles.filter((p) => lastIntakes[p.name]); if (activeMeds.length === 0) return null; return (

Médicaments

{activeMeds.map((p) => { const intake = lastIntakes[p.name]; const nextAt = new Date(new Date(intake.startedAt).getTime() + p.intervalHours * 3600000); const waitMs = nextAt.getTime() - Date.now(); const tooSoon = waitMs > 0; const h = Math.floor(Math.abs(waitMs) / 3600000); const m = Math.floor((Math.abs(waitMs) % 3600000) / 60000); const timeLabel = h > 0 ? `${h}h${m.toString().padStart(2, "0")}` : `${m}min`; return (
{tooSoon ? : }

{p.name}

{p.molecule &&

{p.molecule}

}
{tooSoon ? (

Dans {timeLabel}

) : (

Disponible

)}

{format(nextAt, "HH:mm")}

); })}
); })()} {/* Today's timeline */} {todayEvents.length > 0 && (

Aujourd'hui · {todayEvents.length} événement{todayEvents.length > 1 ? "s" : ""}

{todayEvents.slice(0, 10).map((ev) => { const cfg = EVENT_CONFIG[ev.type]; return (
{cfg.label} {getEventSummary(ev) && ( {getEventSummary(ev)} )}
{format(new Date(ev.startedAt), "HH:mm")}
); })}
)} {activeModal && ( setActiveModal(null)} onSaved={() => qc.invalidateQueries({ queryKey: ["events"] })} /> )}
); }