From 8bb048ff5679a74b5d74524b9d5d01a2a739ff9b Mon Sep 17 00:00:00 2001 From: Arnaud Nelissen Date: Mon, 15 Jun 2026 21:02:31 +0200 Subject: [PATCH] =?UTF-8?q?feat:=20finish=20remaining=20improvements=20?= =?UTF-8?q?=E2=80=94=20search=20filters,=20stats=20zero-fill,=20notes=20na?= =?UTF-8?q?v=20warning?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- src/app/(app)/notes/page.tsx | 8 ++++++ src/app/(app)/search/page.tsx | 47 +++++++++++++++++++++++++++++++++-- src/app/(app)/stats/page.tsx | 4 +-- 3 files changed, 55 insertions(+), 4 deletions(-) diff --git a/src/app/(app)/notes/page.tsx b/src/app/(app)/notes/page.tsx index 040e13c..ad17985 100644 --- a/src/app/(app)/notes/page.tsx +++ b/src/app/(app)/notes/page.tsx @@ -103,6 +103,14 @@ export default function NotesPage() { setDirty(false); }, [currentNote?.id, dateStr, isDayLoading]); + useEffect(() => { + const handler = (e: BeforeUnloadEvent) => { + if (dirty) { e.preventDefault(); e.returnValue = ""; } + }; + window.addEventListener("beforeunload", handler); + return () => window.removeEventListener("beforeunload", handler); + }, [dirty]); + function goToPrevDay() { setCurrentDate((d) => startOfDay(subDays(d, 1))); } diff --git a/src/app/(app)/search/page.tsx b/src/app/(app)/search/page.tsx index d88c5f1..e69731d 100644 --- a/src/app/(app)/search/page.tsx +++ b/src/app/(app)/search/page.tsx @@ -60,6 +60,8 @@ export default function SearchPage() { const { selectedBaby } = useBaby(); const [q, setQ] = useState(""); const [debouncedQ, setDebouncedQ] = useState(""); + const [filterDays, setFilterDays] = useState(null); + const [filterType, setFilterType] = useState(null); const inputRef = useRef(null); useEffect(() => { inputRef.current?.focus(); }, []); @@ -77,8 +79,26 @@ export default function SearchPage() { placeholderData: { events: [], notes: [] }, }); - const events = data?.events ?? []; - const notes = data?.notes ?? []; + const typeMap: Record = { + "Repas": ["BREASTFEED", "BOTTLE"], + "Sommeil": ["NAP", "NIGHT_SLEEP"], + "Couche": ["DIAPER_WET", "DIAPER_STOOL"], + "Santé": ["TEMPERATURE", "MEDICATION", "SYMPTOM"], + }; + const cutoff = filterDays ? new Date(Date.now() - filterDays * 86400000) : null; + + const filteredEvents = (data?.events ?? []).filter(ev => { + if (cutoff && new Date(ev.startedAt) < cutoff) return false; + if (filterType && !typeMap[filterType]?.includes(ev.type)) return false; + return true; + }); + const filteredNotes = (data?.notes ?? []).filter(note => { + if (cutoff && new Date(note.date) < cutoff) return false; + return true; + }); + + const events = filteredEvents; + const notes = filteredNotes; const hasResults = events.length > 0 || notes.length > 0; const searched = debouncedQ.length >= 2; @@ -101,6 +121,29 @@ export default function SearchPage() { )} + {q.length >= 2 && ( +
+ {([["7j", 7], ["30j", 30], ["90j", 90], ["Tout", null]] as [string, number|null][]).map(([label, days]) => ( + + ))} + · + {["Repas", "Sommeil", "Couche", "Santé"].map((label) => { + const active = filterType === label; + return ( + + ); + })} +
+ )} + {!searched && (

Saisissez au moins 2 caractères

)} diff --git a/src/app/(app)/stats/page.tsx b/src/app/(app)/stats/page.tsx index 3d48a07..247e921 100644 --- a/src/app/(app)/stats/page.tsx +++ b/src/app/(app)/stats/page.tsx @@ -109,12 +109,12 @@ function buildDurationTrend(events: Event[], days: number) { e.endedAt && format(new Date(e.startedAt), "yyyy-MM-dd") === key ); - if (dayEvents.length === 0) return { label, nap: null, night: null }; + if (dayEvents.length === 0) return { label, nap: 0, night: 0 }; const napEvents = dayEvents.filter((e) => e.type === "NAP"); const nightEvents = dayEvents.filter((e) => e.type === "NIGHT_SLEEP"); const avg = (evs: Event[]) => evs.length === 0 - ? null + ? 0 : Math.round( evs.reduce( (acc, e) => acc + (new Date(e.endedAt!).getTime() - new Date(e.startedAt).getTime()) / 60000,