"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 rounded-xl mb-3" />)} {type === "diaper" && ()} {type === "sleep" && ()} setNotes(e.target.value)} className="w-full p-3 border rounded-xl mb-4" />
); } function calculateAge(birthDate: string) { const birth = new Date(birthDate); const now = new Date(); const days = Math.floor((now.getTime() - birth.getTime()) / (1000 * 60 * 60 * 24)); const months = Math.floor(days / 30); const years = Math.floor(months / 12); if (years > 0) return `${years} year${years > 1 ? "s" : ""} old`; if (months > 0) return `${months} month${months > 1 ? "s" : ""} old`; return `${days} day${days > 1 ? "s" : ""} old`; } 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 [pendingCount, setPendingCount] = useState(0); const [lastLogs, setLastLogs] = useState([]); const { theme, toggle: toggleTheme } = useTheme(); const { childId, child, loading } = useFamily(); if (loading) { return
Loading...
; } if (!childId) { return
No child found. Add a child in Family settings.
; } useEffect(() => { getSessions(childId).then(sessions => { if (sessions.length > 0) { setAiChats(sessions[0].messages); } }); }, []); useEffect(() => { const queue = getOfflineQueue(); setPendingCount(queue.length); const handleOnline = () => processOfflineQueue(); window.addEventListener("online", handleOnline); if ("serviceWorker" in navigator) navigator.serviceWorker.register("/sw.js").catch(console.error); return () => window.removeEventListener("online", handleOnline); }, []); useEffect(() => { 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]); const toggleDarkMode = () => { toggleTheme(); }; // Unified AI chat that saves to sessions (database) const handleAiChat = async (question?: string) => { const q = question || aiInput; if (!q.trim() || aiLoading) return; setAiLoading(true); setAiOpen(true); const sessions = await getSessions(childId); let currentSession: ChatSession | null = sessions[0] || null; // Create new session if none exists if (!currentSession) { currentSession = await createSession(childId); if (currentSession) { sessions.unshift(currentSession); } } if (!currentSession) { setAiLoading(false); return; } const userMsg: AIChat = { id: crypto.randomUUID(), role: "user", content: q, createdAt: new Date().toISOString() }; const inputVal = q.trim(); if (!question) setAiInput(""); try { // Save user message to DB await fetch("/api/chat", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionId: currentSession.id, role: "user", content: inputVal }), }); // Get AI response 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 couldn't help with that."; // Save AI response to DB await fetch("/api/chat", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionId: currentSession.id, role: "assistant", content: reply }), }); // Refresh sessions const updatedSessions = await getSessions(childId); setAiChats(updatedSessions[0]?.messages || []); } 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" : ""}
}

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 rounded-xl text-sm" 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 && aiChats.length > 0 && (
setAiOpen(false)}>
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 rounded-xl text-sm" disabled={aiLoading} />
)}
); }