"use client"; import { useState, useEffect } from "react"; import Link from "next/link"; import { useTheme } from "./ThemeProvider"; import { useFamily } from "./FamilyProvider"; const OFFLINE_QUEUE_KEY = "tia_offline_queue"; interface AIChat { id: string; role: "user" | "assistant"; content: string; createdAt: string; } interface ChatSession { id: string; title: string; messages: AIChat[]; createdAt: string; updatedAt: string; } export interface OfflineEntry { id: string; type: "feed" | "diaper" | "sleep"; data: any; timestamp: number; } export function getOfflineQueue(): OfflineEntry[] { if (typeof window === "undefined") return []; try { const data = localStorage.getItem(OFFLINE_QUEUE_KEY); return data ? JSON.parse(data) : []; } catch { return []; } } export function addToOfflineQueue(entry: Omit) { const queue = getOfflineQueue(); queue.push({ ...entry, id: crypto.randomUUID(), timestamp: Date.now() }); localStorage.setItem(OFFLINE_QUEUE_KEY, JSON.stringify(queue)); } export async function processOfflineQueue() { const queue = getOfflineQueue(); if (queue.length === 0) return; const failed: OfflineEntry[] = []; for (const entry of queue) { try { await fetch("/api/logs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(entry.data) }); } catch { failed.push(entry); } } localStorage.setItem(OFFLINE_QUEUE_KEY, JSON.stringify(failed)); } // --- AI Session Functions (using database) --- async function getSessions(cid: string): Promise { try { const res = await fetch(`/api/chat?childId=${cid}`); const data = await res.json(); return data.sessions || []; } catch { return []; } } async function createSession(cid: string): Promise { try { const res = await fetch("/api/chat", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ childId: cid, title: "New conversation" }), }); const data = await res.json(); return data.session || null; } catch { return null; } } function LogModal({ type, childId, onClose }: { type: "feed" | "diaper" | "sleep" | null; childId: string; onClose: () => void }) { const [loading, setLoading] = useState(false); const [subType, setSubType] = useState("breast_milk"); const [amountMl, setAmountMl] = useState(""); const [notes, setNotes] = useState(""); if (!type) return null; const handleSubmit = async () => { setLoading(true); const data = { type, childId, subType, amountMl: amountMl ? Number(amountMl) : undefined, notes: notes || undefined }; try { const res = await fetch("/api/logs", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); if (!res.ok && !navigator.onLine) addToOfflineQueue({ type: type as any, data }); onClose(); } catch { addToOfflineQueue({ type: type as any, data }); onClose(); } setLoading(false); }; return (

{type === "feed" && "Log Feed"}{type === "diaper" && "Log Diaper"}{type === "sleep" && "Log Sleep"}

{type === "feed" && (<> setAmountMl(e.target.value)} className="w-full p-3 border dark:border-gray-600 rounded-xl mb-3 bg-white dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" />)} {type === "diaper" && ()} {type === "sleep" && ()} setNotes(e.target.value)} className="w-full p-3 border dark:border-gray-600 rounded-xl mb-4 bg-white dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" />
); } function calculateAge(birthDate: string) { if (!birthDate) return ""; const birth = new Date(birthDate); const now = new Date(); let years = now.getFullYear() - birth.getFullYear(); let months = now.getMonth() - birth.getMonth(); let days = now.getDate() - birth.getDate(); if (days < 0) { months--; days += new Date(now.getFullYear(), now.getMonth(), 0).getDate(); } if (months < 0) { years--; months += 12; } const parts = []; if (years > 0) parts.push(`${years} year${years > 1 ? "s" : ""}`); if (months > 0) parts.push(`${months} month${months > 1 ? "s" : ""}`); if (days > 0) parts.push(`${days} day${days > 1 ? "s" : ""}`); return parts.length > 0 ? parts.join(", ") : "Newborn"; } function getGreeting() { const hour = new Date().getHours(); if (hour < 12) return "Good morning"; if (hour < 18) return "Good afternoon"; return "Good evening"; } const QUICK_QUESTIONS = ["How much should my baby eat?", "When should baby sleep?", "Is fever normal?", "How to increase milk supply?", "Baby won't sleep", "Starting solids?"]; export default function HomePage() { const [modalType, setModalType] = useState<"feed" | "diaper" | "sleep" | null>(null); const [aiOpen, setAiOpen] = useState(false); const [aiInput, setAiInput] = useState(""); const [aiChats, setAiChats] = useState([]); const [aiLoading, setAiLoading] = useState(false); const [homeSessionId, setHomeSessionId] = useState(null); const [pendingCount, setPendingCount] = useState(0); const [lastLogs, setLastLogs] = useState([]); const [vaccineReminders, setVaccineReminders] = useState([]); const { theme, toggle: toggleTheme } = useTheme(); const { childId, child, familyId, loading } = useFamily(); useEffect(() => { if (!childId) return; getSessions(childId).then(sessions => { if (sessions.length > 0) { setAiChats(sessions[0].messages); } }); }, [childId]); useEffect(() => { if (!childId) return; const queue = getOfflineQueue(); setPendingCount(queue.length); const handleOnline = () => processOfflineQueue(); window.addEventListener("online", handleOnline); fetch(`/api/notifications?childId=${childId}`) .then(res => res.json()) .then(data => setVaccineReminders(data.notifications || [])) .catch(console.error); return () => window.removeEventListener("online", handleOnline); }, [childId]); useEffect(() => { if (!childId) return; Promise.all([ fetch(`/api/logs?type=feed&childId=${childId}&limit=1`).then(r => r.json()), fetch(`/api/logs?type=sleep&childId=${childId}&limit=1`).then(r => r.json()), fetch(`/api/logs?type=diaper&childId=${childId}&limit=1`).then(r => r.json()), ]).then(([feed, sleep, diaper]) => setLastLogs([feed.entries?.[0], sleep.entries?.[0], diaper.entries?.[0]].filter(Boolean))); }, [childId]); if (loading) { return
Loading...
; } // Not logged in - redirect to login if (!familyId) { if (typeof window !== "undefined") { window.location.href = "/login"; } return
Redirecting...
; } if (!childId) { return
No child found. Add a child in Family settings.
; } const toggleDarkMode = () => { toggleTheme(); }; // Unified AI chat — creates a fresh session per modal open, reuses it for follow-ups const handleAiChat = async (question?: string) => { const q = question || aiInput; if (!q.trim() || aiLoading) return; setAiLoading(true); setAiOpen(true); const inputVal = q.trim(); if (!question) setAiInput(""); // Reuse session within same modal open, or create fresh one let sessionId = homeSessionId; if (!sessionId) { const newSession = await createSession(childId); if (!newSession) { setAiLoading(false); return; } sessionId = newSession.id; setHomeSessionId(sessionId); setAiChats([]); } // Optimistically show user message const userMsg: AIChat = { id: "tmp-" + Date.now(), role: "user", content: inputVal, createdAt: new Date().toISOString() }; setAiChats(prev => [...prev, userMsg]); try { await fetch("/api/chat", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionId, role: "user", content: inputVal }), }); const res = await fetch("/api/ai", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ messages: [{ role: "user", content: inputVal }] }), }); const data = await res.json(); const reply = data.reply || "Sorry, I'm having trouble connecting. Please try again."; await fetch("/api/chat", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionId, role: "assistant", content: reply }), }); setAiChats(prev => [...prev, { id: "ai-" + Date.now(), role: "assistant", content: reply, createdAt: new Date().toISOString() }]); } catch (err) { console.error("AI chat error:", err); } setAiLoading(false); }; return (

{getGreeting()} 👋

How is {child?.name || "your baby"} doing today?

👶
{child?.name || "Baby"}
{calculateAge(child?.birthDate || "")}
{pendingCount > 0 &&
{pendingCount} pending log{pendingCount > 1 ? "s" : ""}
} {vaccineReminders.length > 0 && (
💊 Vaccine Reminder
{vaccineReminders[0].status === "overdue" ? `${vaccineReminders[0].message}` : `${vaccineReminders[0].vaccineName} due today`}
)}

Quick Log

💊Medical

Ask AI

View all chats →
setAiInput(e.target.value)} onKeyDown={e => e.key === "Enter" && handleAiChat()} placeholder="Ask anything..." className="flex-1 p-2 border dark:border-gray-600 rounded-xl text-sm bg-white dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" disabled={aiLoading} />
{QUICK_QUESTIONS.map((q, i) => )}

Recent Activity

{lastLogs.length === 0 ?

No logs yet today

: lastLogs.filter(Boolean).map((log: any, i: number) => (
{log.type === "feed" && "🍼"}{log.type === "sleep" && "😴"}{log.type === "diaper" && "👶"}
{log.type}
{new Date(log.logged_at).toLocaleTimeString([], { hour: "2-digit", minute: "2-digit" })}
{log.amount_ml && {log.amount_ml}ml}
))}
setModalType(null)} /> {aiOpen && (
{ setAiOpen(false); setHomeSessionId(null); }}>
e.stopPropagation()}>

Ask AI

{aiChats.map((chat) => (
{chat.content}
))} {aiLoading &&
Thinking...
}
setAiInput(e.target.value)} onKeyDown={e => e.key === "Enter" && handleAiChat()} placeholder="Ask a question..." className="flex-1 p-2 border dark:border-gray-600 rounded-xl text-sm dark:bg-gray-700 dark:text-white dark:placeholder-gray-400" disabled={aiLoading} />
)}
); }