"use client"; import { useState, useEffect } from "react"; import { useRouter } from "next/navigation"; import { useFamily } from "@/app/FamilyProvider"; import { getGuideline, getAgeInMonths } from "@/lib/guidelines"; import { api } from "@/lib/api"; import { CalendarView } from "@/components/CalendarView"; import { LogModal, type LogType as ModalLogType, type SmartDefault } from "@/components/LogModal"; import type { Log, LogType } from "@/types"; type ViewMode = "timeline" | "calendar"; interface DayLogs { date: string; logs: Log[]; } function getIcon(type: LogType) { if (type === "feed") return "🍼"; if (type === "sleep") return "😴"; if (type === "diaper") return "🚼"; return "πŸ“"; } function formatDayLabel(dateStr: string): string { const d = new Date(dateStr); const today = new Date().toDateString(); const yesterday = new Date(Date.now() - 86_400_000).toDateString(); if (d.toDateString() === today) return "Today"; if (d.toDateString() === yesterday) return "Yesterday"; return d.toLocaleDateString("en-IN", { weekday: "short", month: "short", day: "numeric" }); } export default function ActivityPage() { const router = useRouter(); const { child, childId: providerChildId } = useFamily(); const [view, setView] = useState("timeline"); const [logs, setLogs] = useState([]); const [loading, setLoading] = useState(true); const [filter, setFilter] = useState("all"); const [showSuggested, setShowSuggested] = useState(true); const [guideExpanded, setGuideExpanded] = useState(false); const [generating, setGenerating] = useState(false); const [fabOpen, setFabOpen] = useState(false); const [menuOpen, setMenuOpen] = useState(false); const [modalType, setModalType] = useState(null); const [selectedLog, setSelectedLog] = useState(null); const [deleteConfirm, setDeleteConfirm] = useState(false); const [smartDefault, setSmartDefault] = useState(null); const [pendingDeleteId, setPendingDeleteId] = useState<{ id: string; type: string } | null>(null); const [daySheetDate, setDaySheetDate] = useState<{ date: string; type: LogType } | null>(null); const childId = providerChildId ?? ""; useEffect(() => { if (providerChildId) fetchLogs(); }, [providerChildId]); const fetchLogs = async () => { if (!childId) return; try { const data = await api.get<{ entries: Log[] }>(`/api/logs?childId=${childId}&limit=200`); setLogs(data.entries || []); } catch (err) { console.error("Failed to fetch logs:", err); } setLoading(false); }; const generateHistory = async () => { if (!child) return; setMenuOpen(false); setGenerating(true); try { const data = await api.post<{ success: boolean }>("/api/history", { childId: child.id, birthDate: child.birthDate }); if (data.success) fetchLogs(); } catch (err) { console.error("Failed to generate history:", err); } setGenerating(false); }; const deleteLog = async (log: Log) => { try { await fetch(`/api/logs/${log.id}?type=${log.type}`, { method: "DELETE" }); setSelectedLog(null); setDeleteConfirm(false); fetchLogs(); } catch (err) { console.error("Failed to delete log:", err); } }; const handleEdit = (log: Log) => { // Store the old log to delete after the new one is saved setPendingDeleteId({ id: log.id, type: log.type }); setSmartDefault({ subType: log.subType ?? "", amountMl: log.amount ?? undefined } as SmartDefault); setModalType(log.type as ModalLogType); setSelectedLog(null); }; // Today's stats (computed from loaded logs β€” no extra fetch) const todayStr = new Date().toDateString(); const todayLogs = logs.filter(l => new Date(l.loggedAt).toDateString() === todayStr); const todayCounts = { feed: todayLogs.filter(l => l.type === "feed").length, sleep: todayLogs.filter(l => l.type === "sleep").length, diaper: todayLogs.filter(l => l.type === "diaper").length, }; // Last 4 calendar days (computed from loaded logs β€” no extra fetch) const last4Days = Array.from({ length: 4 }, (_, i) => { const d = new Date(Date.now() - i * 86_400_000); const dateStr = d.toDateString(); const dayLogs = logs.filter(l => new Date(l.loggedAt).toDateString() === dateStr); return { date: dateStr, label: i === 0 ? "Today" : i === 1 ? "Yest." : d.toLocaleDateString("en-IN", { weekday: "short" }), feed: dayLogs.filter(l => l.type === "feed").length, sleep: dayLogs.filter(l => l.type === "sleep").length, diaper: dayLogs.filter(l => l.type === "diaper").length, }; }); const filteredLogs = filter === "all" ? logs : logs.filter(l => l.type === filter); const groupedByDay = filteredLogs.reduce((acc, log) => { const date = new Date(log.loggedAt).toDateString(); const existing = acc.find(d => d.date === date); if (existing) existing.logs.push(log); else acc.push({ date, logs: [log] }); return acc; }, []); const guide = child ? getGuideline(child.birthDate) : null; const ageMonths = child ? getAgeInMonths(child.birthDate) : 0; const guideItems = guide ? [ { icon: "🍼", label: "Feeds/day", count: todayCounts.feed, target: guide.feeds.times, barClass: "bg-rose-400", textClass: "text-rose-600 dark:text-rose-400" }, { icon: "😴", label: "Sleep (hrs)", count: todayCounts.sleep, target: guide.sleep.totalHours, barClass: "bg-amber-400", textClass: "text-amber-600 dark:text-amber-400" }, { icon: "🚼", label: "Diapers", count: todayCounts.diaper, target: guide.diapers.count, barClass: "bg-blue-400", textClass: "text-blue-600 dark:text-blue-400" }, ] : []; return (
{/* Header */}

Activity

{/* β‹― overflow menu */}
{menuOpen && ( <>
setMenuOpen(false)} />
)}
{/* View toggle */}
{(["timeline", "calendar"] as const).map(v => ( ))}
{/* Filter pills */}
{([ { value: "all", label: "All" }, { value: "feed", label: "🍼 Feed" }, { value: "sleep", label: "😴 Sleep" }, { value: "diaper", label: "🚼 Diaper" }, ] as { value: LogType | "all"; label: string }[]).map(f => ( ))}
{!loading && ( <> {/* Collapsible guidelines card β€” above strip */} {child && guide && showSuggested && (
{guideExpanded && (
{guideItems.map(item => (
{item.label}
{item.count} /{item.target}
0 ? (item.count / item.target) * 100 : 0)}%` }} />
))}
)}
)} {/* 4-day overview strip β€” oldest β†’ newest, each row independently tappable */}
{[...last4Days].reverse().map(d => { const isToday = d.label === "Today"; const rows: { type: LogType; icon: string; count: number }[] = [ { type: "feed", icon: "🍼", count: d.feed }, { type: "sleep", icon: "😴", count: d.sleep }, { type: "diaper", icon: "🚼", count: d.diaper }, ]; return (
{/* Day label β€” non-interactive header */}
{d.label}
{/* Each row is its own tap target */} {rows.map((row, idx) => ( ))}
); })}
)} {/* Content */}
{loading ? (
{["🍼", "😴", "🚼"].map((e, i) => ( {e} ))}

Loading activity…

) : view === "calendar" ? ( ) : groupedByDay.length === 0 ? (
πŸ“‹

No {filter !== "all" ? filter : ""} logs yet

Tap + to start logging

) : (
{groupedByDay.map(day => { const label = formatDayLabel(day.date); const isToday = label === "Today"; return (
{label}
{day.logs .sort((a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()) .map(log => ( ))}
); })}
)}
{/* FAB */}
{fabOpen && (["feed", "sleep", "diaper"] as ModalLogType[]).map(t => ( ))}
{fabOpen &&
setFabOpen(false)} />} {/* Log action sheet */} {selectedLog && ( <>
{ setSelectedLog(null); setDeleteConfirm(false); }} />
{/* Summary row */}
{getIcon(selectedLog.type)}
{selectedLog.type}
{[ selectedLog.subType?.replace(/_/g, " "), selectedLog.amount ? `${selectedLog.amount}ml` : null, new Date(selectedLog.loggedAt).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" }), ].filter(Boolean).join(" Β· ")}
{!deleteConfirm ? (
) : (

Delete this {selectedLog.type} log?

)}
)} {/* Day-type detail sheet β€” tap a specific row (feed/sleep/diaper) on a day chip */} {daySheetDate && (() => { const { date: sheetDateStr, type: sheetType } = daySheetDate; const sheetLogs = logs .filter(l => new Date(l.loggedAt).toDateString() === sheetDateStr && l.type === sheetType ) .sort((a, b) => new Date(b.loggedAt).getTime() - new Date(a.loggedAt).getTime()); const sheetDayLabel = formatDayLabel(sheetDateStr); const sheetTypeIcon = getIcon(sheetType); const sheetTypeLabel = sheetType.charAt(0).toUpperCase() + sheetType.slice(1); return ( <>
setDaySheetDate(null)} />
{/* Sheet header */}
{sheetTypeIcon}

{sheetTypeLabel} Β· {sheetDayLabel}

{sheetLogs.length === 0 ? "Nothing logged β€” tap + to add" : `${sheetLogs.length} entr${sheetLogs.length === 1 ? "y" : "ies"} Β· tap to edit or delete`}

{/* Log list */}
{sheetLogs.length === 0 ? (

{sheetTypeIcon}

No {sheetType} logged for {sheetDayLabel}

Tap + below to add one, or use β‹― β†’ Generate sample history

) : ( sheetLogs.map(log => ( )) )}
{/* Single add button for this specific type */}
); })()} { setModalType(null); setSmartDefault(null); setPendingDeleteId(null); }} onSaved={async () => { // If editing, delete the old log after the new one is saved if (pendingDeleteId) { try { await fetch(`/api/logs/${pendingDeleteId.id}?type=${pendingDeleteId.type}`, { method: "DELETE" }); } catch (err) { console.error("Failed to delete old log during edit:", err); } setPendingDeleteId(null); } setSmartDefault(null); fetchLogs(); }} smartDefault={smartDefault} />
); }