"use client"; import { useState } from "react"; import { useQuery, useQueryClient } from "@tanstack/react-query"; import { useBaby } from "@/contexts/baby-context"; import { format } from "date-fns"; import { fr } from "date-fns/locale"; import { Check, Trash2, X } from "lucide-react"; interface Tooth { id: string; babyId: string; code: string; appearedAt: string; notes: string | null; } const UPPER_CODES = ["u5l", "u4l", "u3l", "u2l", "u1l", "u1r", "u2r", "u3r", "u4r", "u5r"]; const LOWER_CODES = ["l5l", "l4l", "l3l", "l2l", "l1l", "l1r", "l2r", "l3r", "l4r", "l5r"]; const TOOTH_NAMES: Record = { "1": "Incisive centrale", "2": "Incisive latérale", "3": "Canine", "4": "Première molaire", "5": "Deuxième molaire", }; const ERUPTION_AGES: Record = { "1": "6–12 mois", "2": "9–16 mois", "3": "16–23 mois", "4": "14–18 mois", "5": "23–31 mois", }; function getToothNumber(code: string): string { // code is like "u1l", "l3r" — digit is at index 1 return code[1]; } function getToothLabel(code: string): string { const num = getToothNumber(code); const jaw = code[0] === "u" ? "sup." : "inf."; const side = code[2] === "l" ? "gauche" : "droite"; return `${TOOTH_NAMES[num]} ${jaw} ${side}`; } const inputCls = "w-full px-3 py-2 rounded-lg border border-slate-200 dark:border-slate-700 text-sm focus:outline-none focus:ring-2 focus:ring-emerald-400 bg-white dark:bg-slate-800 text-slate-900 dark:text-slate-100"; type ActiveState = | { type: "form"; code: string; date: string; notes: string } | { type: "detail"; code: string } | null; export default function TeethPage() { const qc = useQueryClient(); const { selectedBaby: baby } = useBaby(); const [active, setActive] = useState(null); const [saving, setSaving] = useState(false); const [deleting, setDeleting] = useState(null); const { data: teeth = [] } = useQuery({ queryKey: ["teeth", baby?.id], queryFn: () => fetch(`/api/teeth?babyId=${baby!.id}`).then((r) => r.json()), enabled: !!baby?.id, }); const appearedCodes = new Set(teeth.map((t) => t.code)); const count = appearedCodes.size; function getToothData(code: string): Tooth | undefined { return teeth.find((t) => t.code === code); } function handleToothClick(code: string) { if (appearedCodes.has(code)) { setActive(active?.type === "detail" && active.code === code ? null : { type: "detail", code }); } else { const today = format(new Date(), "yyyy-MM-dd"); setActive( active?.type === "form" && active.code === code ? null : { type: "form", code, date: today, notes: "" } ); } } async function handleSave(e: React.FormEvent) { e.preventDefault(); if (!baby || active?.type !== "form") return; setSaving(true); await fetch("/api/teeth", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ babyId: baby.id, code: active.code, appearedAt: new Date(active.date).toISOString(), notes: active.notes || null, }), }); qc.invalidateQueries({ queryKey: ["teeth", baby.id] }); setSaving(false); setActive(null); } async function handleDelete(code: string) { if (!baby) return; setDeleting(code); await fetch(`/api/teeth/${code}?babyId=${baby.id}`, { method: "DELETE" }); qc.invalidateQueries({ queryKey: ["teeth", baby.id] }); setDeleting(null); setActive(null); } function ToothSquare({ code }: { code: string }) { const appeared = appearedCodes.has(code); const num = getToothNumber(code); const isActive = active !== null && active.code === code; return ( ); } const activeToothLabel = active ? getToothLabel(active.code) : ""; const activeToothNum = active ? getToothNumber(active.code) : ""; return (
{/* Header */}

Dents

{baby?.name}

{/* Progress */}
{count} / 20 dents apparues {Math.round((count / 20) * 100)} %
{/* Diagram */}
{/* Upper jaw label */}

Mâchoire supérieure

{UPPER_CODES.map((code) => ( ))}
{LOWER_CODES.map((code) => ( ))}
{/* Lower jaw label */}

Mâchoire inférieure

{/* Legend */}
Non apparue Apparue
{/* Inline panel for form or detail */} {active && (

{activeToothLabel}

Éruption typique : {ERUPTION_AGES[activeToothNum]}

{active.type === "form" ? (
setActive((prev) => prev?.type === "form" ? { ...prev, date: e.target.value } : prev ) } className={inputCls} />
setActive((prev) => prev?.type === "form" ? { ...prev, notes: e.target.value } : prev ) } placeholder="Observations..." className={inputCls} />
) : ( (() => { const tooth = getToothData(active.code); if (!tooth) return null; return (

Apparue le{" "} {format(new Date(tooth.appearedAt), "d MMMM yyyy", { locale: fr })}

{tooth.notes && (

{tooth.notes}

)}
); })() )}
)}
); }