diff --git a/src/app/(app)/dashboard/page.tsx b/src/app/(app)/dashboard/page.tsx index 6265d65..d0c790f 100644 --- a/src/app/(app)/dashboard/page.tsx +++ b/src/app/(app)/dashboard/page.tsx @@ -70,6 +70,7 @@ function getEventSummary(event: Event): string { if ((event.type === "NAP" || event.type === "NIGHT_SLEEP") && event.endedAt) { return formatDuration(new Date(event.startedAt), new Date(event.endedAt)); } + if (event.type === "OTHER") return event.notes ? event.notes.slice(0, 50) : ""; return ""; } diff --git a/src/app/(app)/reminders/page.tsx b/src/app/(app)/reminders/page.tsx index 63a4eb7..908f440 100644 --- a/src/app/(app)/reminders/page.tsx +++ b/src/app/(app)/reminders/page.tsx @@ -3,7 +3,9 @@ import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useBaby } from "@/contexts/baby-context"; -import { Plus, Trash2, Bell, BellOff, Clock } from "lucide-react"; +import { Plus, Trash2, Bell, BellOff, Clock, CheckCircle2, RotateCcw } from "lucide-react"; +import { formatDistanceToNow } from "date-fns"; +import { fr } from "date-fns/locale"; interface Reminder { id: string; @@ -72,6 +74,24 @@ export default function RemindersPage() { qc.invalidateQueries({ queryKey: ["reminders"] }); } + async function markTaken(id: string) { + await fetch(`/api/reminders/${id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ lastSentAt: new Date().toISOString() }), + }); + qc.invalidateQueries({ queryKey: ["reminders"] }); + } + + async function unmarkTaken(id: string) { + await fetch(`/api/reminders/${id}`, { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ lastSentAt: null }), + }); + qc.invalidateQueries({ queryKey: ["reminders"] }); + } + async function deleteReminder(id: string) { if (!confirm("Supprimer ce rappel ?")) return; await fetch(`/api/reminders/${id}`, { method: "DELETE" }); @@ -175,8 +195,31 @@ export default function RemindersPage() { {nextDueLabel(r)} )} + {r.lastSentAt && ( +

+ + Pris {formatDistanceToNow(new Date(r.lastSentAt), { locale: fr, addSuffix: true })} +

+ )}
+ {r.lastSentAt ? ( + + ) : ( + + )}
+ {/* Activities */} +
+

Activités par jour

+

Bain · Balade · Plat ventre · Autre

+ + + + + [val, "Activités"]} contentStyle={tooltipStyle} cursor={{ fill: cursorFill }} /> + + + +
+ {/* Sleep */}

Sommeil par jour (heures)

diff --git a/src/app/(app)/timeline/page.tsx b/src/app/(app)/timeline/page.tsx index 4584d8a..47dd48f 100644 --- a/src/app/(app)/timeline/page.tsx +++ b/src/app/(app)/timeline/page.tsx @@ -65,6 +65,7 @@ function getEventSummary(event: Event): string { if ((event.type === "NAP" || event.type === "NIGHT_SLEEP") && event.endedAt) { return formatDuration(new Date(event.startedAt), new Date(event.endedAt)); } + if (event.type === "OTHER") return event.notes ? event.notes.slice(0, 50) : ""; return ""; } diff --git a/src/app/api/reminders/[id]/route.ts b/src/app/api/reminders/[id]/route.ts index e16bb5e..14000ba 100644 --- a/src/app/api/reminders/[id]/route.ts +++ b/src/app/api/reminders/[id]/route.ts @@ -26,6 +26,7 @@ export async function PATCH( ...(body.intervalHours !== undefined ? { intervalHours: parseFloat(body.intervalHours) } : {}), ...(body.startAt !== undefined ? { startAt: new Date(body.startAt) } : {}), ...(body.enabled !== undefined ? { enabled: body.enabled } : {}), + ...(body.lastSentAt !== undefined ? { lastSentAt: body.lastSentAt ? new Date(body.lastSentAt) : null } : {}), }, }); diff --git a/src/components/event-modal.tsx b/src/components/event-modal.tsx index 7e60476..33f3a5b 100644 --- a/src/components/event-modal.tsx +++ b/src/components/event-modal.tsx @@ -249,8 +249,8 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro async function handlePhotoChange(e: React.ChangeEvent) { const file = e.target.files?.[0]; if (!file) return; - if (file.size > 5 * 1024 * 1024) { - alert("Image trop volumineuse (max 5 Mo)"); + if (file.size > 30 * 1024 * 1024) { + alert("Image trop volumineuse (max 30 Mo)"); return; } setPhotoUploading(true); @@ -363,6 +363,9 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro }); if (diaperBoth && (type === "DIAPER_WET" || type === "DIAPER_STOOL")) { const secondType = type === "DIAPER_WET" ? "DIAPER_STOOL" : "DIAPER_WET"; + const secondMeta = type === "DIAPER_WET" + ? { color: form.stoolColor, amount: form.stoolAmount } + : null; await fetch("/api/events", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -372,7 +375,7 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro startedAt: toUTC(form.startedAt), endedAt: null, notes: null, - metadata: null, + metadata: secondMeta, }), }); } @@ -657,16 +660,47 @@ export function EventModal({ type, babyId, onClose, onSaved, initialEvent }: Pro {/* Combined diaper toggle */} {(type === "DIAPER_WET" || type === "DIAPER_STOOL") && !isEditing && ( - +
+ + {diaperBoth && type === "DIAPER_WET" && ( +
+
+ +
+ {STOOL_COLORS.map(({ value, label, bg, dot }) => ( + + ))} +
+
+
+ +
+ {(["small", "medium", "large"] as const).map((amt) => ( + + ))} +
+
+
+ )} +
)} {/* Medication — profile picker */} diff --git a/src/components/quick-add-fab.tsx b/src/components/quick-add-fab.tsx index fbc88ce..306b281 100644 --- a/src/components/quick-add-fab.tsx +++ b/src/components/quick-add-fab.tsx @@ -74,7 +74,7 @@ export function QuickAddFab() {
-
+
{QUICK_TYPES.map((type) => { const cfg = EVENT_CONFIG[type]; return (