From 299e878e38a5f47219c6bb3b0c36bcbfeb689739 Mon Sep 17 00:00:00 2001 From: Mannu Date: Sun, 17 May 2026 13:24:33 +0530 Subject: [PATCH] fix(ai): proper chat layout, mobile sidebar, empty states, loading dots - h-screen with overflow-hidden so messages scroll within viewport - Sidebar is slide-over on mobile (fixed + overlay) with translate animation - Sidebar defaults closed; auto-selects first session on load - Empty state when no session selected with "New Chat" CTA - Typing indicator with animated bouncing dots - Chat bubbles properly aligned (user right, AI left) with rounded corners - Delete confirm moved to its own modal (not nested in session list item) - Removed stale debug console.log - Dark mode fixes: border, hover states, disabled button Co-Authored-By: Claude Sonnet 4.6 --- src/app/ai/page.tsx | 176 ++++++++++++++++++++++++++++++-------------- 1 file changed, 120 insertions(+), 56 deletions(-) diff --git a/src/app/ai/page.tsx b/src/app/ai/page.tsx index e9e952f..cfb3239 100644 --- a/src/app/ai/page.tsx +++ b/src/app/ai/page.tsx @@ -24,12 +24,11 @@ export default function AIChatPage() { const [currentSessionId, setCurrentSessionId] = useState(""); const [input, setInput] = useState(""); const [loading, setLoading] = useState(false); - const [sidebarOpen, setSidebarOpen] = useState(true); + const [sidebarOpen, setSidebarOpen] = useState(false); const [error, setError] = useState(""); const [deleteConfirm, setDeleteConfirm] = useState(null); useEffect(() => { - console.log("AI page: childId from useFamily:", childId); if (childId) { fetchSessions(); } @@ -42,11 +41,14 @@ export default function AIChatPage() { const data = await res.json(); if (data.error) { setError(data.error); - console.error("API error:", data.error); return; } - setSessions(data.sessions || []); + const fetched = data.sessions || []; + setSessions(fetched); setError(""); + if (fetched.length > 0 && !currentSessionId) { + setCurrentSessionId(fetched[0].id); + } } catch (err) { console.error("Failed to fetch:", err); } @@ -63,6 +65,7 @@ export default function AIChatPage() { if (data.session) { setSessions([data.session, ...sessions]); setCurrentSessionId(data.session.id); + setSidebarOpen(false); } } catch (err) { console.error("Failed to create:", err); @@ -78,7 +81,6 @@ export default function AIChatPage() { const userContent = input.trim(); setInput(""); - // Show user message immediately in UI const tempUserMsg = { id: "temp-" + Date.now(), role: "user" as const, content: userContent, createdAt: new Date().toISOString() }; if (currentSessionId) { const sessionIdx = sessions.findIndex(s => s.id === currentSessionId); @@ -89,7 +91,6 @@ export default function AIChatPage() { } } - // Auto-create session if none selected let sessionId = currentSessionId; if (!sessionId && childId) { try { @@ -118,14 +119,12 @@ export default function AIChatPage() { } try { - // Save user message await fetch("/api/chat", { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ sessionId, role: "user", content: userContent }), }); - // Get AI response const aiRes = await fetch("/api/ai", { method: "POST", headers: { "Content-Type": "application/json" }, @@ -133,7 +132,6 @@ export default function AIChatPage() { }); const aiData = await aiRes.json(); - // Save AI response if (aiData.reply) { await fetch("/api/chat", { method: "PATCH", @@ -142,7 +140,6 @@ export default function AIChatPage() { }); } - // Refresh sessions fetchSessions(); } catch (err) { console.error("Failed to send:", err); @@ -153,59 +150,63 @@ export default function AIChatPage() { const deleteSession = async (id: string) => { try { await fetch(`/api/chat?id=${id}`, { method: "DELETE" }); - setSessions(sessions.filter(s => s.id !== id)); + const remaining = sessions.filter(s => s.id !== id); + setSessions(remaining); setDeleteConfirm(null); + if (currentSessionId === id) { + setCurrentSessionId(remaining[0]?.id || ""); + } } catch (err) { console.error("Failed to delete:", err); } }; return ( -
- {error && ( -
- Error: {error} -
+
+ + {/* Sidebar overlay on mobile */} + {sidebarOpen && ( +
setSidebarOpen(false)} /> )} - {/* Sidebar - sessions list on left */} -
-
-
+ {/* Sidebar */} +
+
+

Chats

- +
-
- {(sessions || []).map(session => ( +
+ {sessions.length === 0 ? ( +

No chats yet

+ ) : sessions.map(session => (
setCurrentSessionId(session.id)} - className={`p-2 rounded-lg cursor-pointer flex justify-between items-center dark:text-gray-100 ${session.id === currentSessionId ? "bg-rose-100 dark:bg-rose-900" : ""}`} + onClick={() => { setCurrentSessionId(session.id); setSidebarOpen(false); }} + className={`p-3 rounded-lg cursor-pointer flex justify-between items-center group transition-colors + ${session.id === currentSessionId + ? "bg-rose-100 dark:bg-rose-900/50" + : "hover:bg-gray-100 dark:hover:bg-gray-700" + }`} > -
-
{session.title}
-
{new Date(session.updatedAt).toLocaleDateString()}
+
+
{session.title}
+
{new Date(session.updatedAt).toLocaleDateString()}
- {deleteConfirm === session.id && ( -
-
-

Delete this conversation?

-
- - -
-
-
- )}
))}
@@ -213,39 +214,102 @@ export default function AIChatPage() {
{/* Main chat */} -
-
-
- - {currentSession?.title || "AI Chat"} +
+ {/* Header */} +
+
+ + {currentSession?.title || "AI Chat"}
- + ← Home
-
- {currentSession?.messages.map((msg, i) => ( -
- {msg.content} + {error && ( +
+ {error} +
+ )} + + {/* Messages */} +
+ {!currentSession ? ( +
+
🤱
+

Ask anything about your baby

+

Tap ☰ to see past chats, or just type below to start

+
- ))} + ) : currentSession.messages.length === 0 ? ( +
+

Type a question below to get started

+
+ ) : ( + currentSession.messages.map((msg, i) => ( +
+
+ {msg.content} +
+
+ )) + )} + {loading && ( +
+
+
+ + + +
+
+
+ )}
-
+ {/* Input */} +
setInput(e.target.value)} - onKeyDown={e => e.key === "Enter" && handleSend()} + onKeyDown={e => e.key === "Enter" && !e.shiftKey && handleSend()} placeholder="Ask about your baby..." - className="flex-1 p-3 border rounded-lg dark:bg-gray-700 dark:text-white" + className="flex-1 p-3 border dark:border-gray-600 rounded-xl dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 text-sm focus:outline-none focus:ring-2 focus:ring-rose-300" disabled={loading} /> -
+ + {/* Delete confirm modal */} + {deleteConfirm && ( +
+
+

Delete this conversation?

+

This can't be undone.

+
+ + +
+
+
+ )}
); -} \ No newline at end of file +}