feat: teeth tracker, feeding analysis, sidebar groups, timeline polish

Features:
- Teeth tracker: Tooth model + migration, API routes (GET/POST upsert/DELETE),
  page at /teeth with 20-tooth grid, progress bar, inline mark/unmark flow
- Dashboard feeding analysis: avg interval + last feed + next expected time widget
- Fix dashboard baby?.name crash (selectedBaby?.name)

UI/UX:
- Sidebar: grouped into Suivi / Santé / Vie du bébé / Outils sections
- Nav: active highlight works on child routes (startsWith)
- Timeline: filter chips sticky on mobile; skeleton cards replace spinner
- Stats: 500-event limit warning banner
- Medications: 3 states — "Dans Xh" (orange), "Disponible" (green), "En retard de Xh" (amber)
- Notes: skip auto-save on empty/whitespace blur

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-15 21:51:32 +02:00
parent 8bb048ff56
commit cd3efa98fc
11 changed files with 596 additions and 25 deletions
+48
View File
@@ -0,0 +1,48 @@
import { NextResponse } from "next/server";
import { auth } from "@/lib/auth";
import { prisma } from "@/lib/prisma";
export async function GET(req: Request) {
const session = await auth();
if (!session?.user?.id) return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
const { searchParams } = new URL(req.url);
const babyId = searchParams.get("babyId");
if (!babyId) return NextResponse.json({ error: "babyId requis" }, { status: 400 });
const teeth = await prisma.tooth.findMany({
where: { babyId },
orderBy: { appearedAt: "asc" },
});
return NextResponse.json(teeth);
}
export async function POST(req: Request) {
const session = await auth();
if (!session?.user?.id) return NextResponse.json({ error: "Non autorisé" }, { status: 401 });
const body = await req.json();
const { babyId, code, appearedAt, notes } = body;
if (!babyId || !code || !appearedAt) {
return NextResponse.json({ error: "Champs requis manquants" }, { status: 400 });
}
const tooth = await prisma.tooth.upsert({
where: { babyId_code: { babyId, code } },
update: {
appearedAt: new Date(appearedAt),
notes: notes ?? null,
},
create: {
babyId,
code,
appearedAt: new Date(appearedAt),
notes: notes ?? null,
},
});
return NextResponse.json(tooth, { status: 201 });
}