tia/src/app/ai/page.tsx
Mannu fb2527f4b3 Fix AI chat: disable send without session
- Disable send button when no active session exists

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 01:42:37 +05:30

172 lines
No EOL
5.6 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { useFamily } from "../FamilyProvider";
interface AIChat {
id: string;
role: "user" | "assistant";
content: string;
createdAt: string;
}
interface ChatSession {
id: string;
title: string;
messages: AIChat[];
createdAt: string;
updatedAt: string;
}
export default function AIChatPage() {
const { childId } = useFamily();
const [sessions, setSessions] = useState<ChatSession[]>([]);
const [currentSessionId, setCurrentSessionId] = useState<string>("");
const [input, setInput] = useState("");
const [loading, setLoading] = useState(false);
const [sidebarOpen, setSidebarOpen] = useState(true);
useEffect(() => {
if (childId) {
fetchSessions();
}
}, [childId]);
const fetchSessions = async () => {
if (!childId) return;
try {
const res = await fetch(`/api/chat?childId=${childId}`);
const data = await res.json();
setSessions(data.sessions || []);
} catch (err) {
console.error("Failed to fetch:", err);
}
};
const createNewSession = async () => {
try {
const res = await fetch("/api/chat", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ childId, title: "New conversation" }),
});
const data = await res.json();
if (data.session) {
setSessions([data.session, ...sessions]);
setCurrentSessionId(data.session.id);
}
} catch (err) {
console.error("Failed to create:", err);
}
};
const currentSession = sessions.find(s => s.id === currentSessionId);
const handleSend = async () => {
if (!input.trim() || loading || !currentSession) return;
setLoading(true);
const userContent = input.trim();
setInput("");
try {
// Save user message
await fetch("/api/chat", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sessionId: currentSessionId, role: "user", content: userContent }),
});
// Get AI response
const aiRes = await fetch("/api/ai", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ messages: currentSession.messages.concat({ id: "", role: "user", content: userContent, createdAt: "" }) }),
});
const aiData = await aiRes.json();
// Save AI response
if (aiData.reply) {
await fetch("/api/chat", {
method: "PATCH",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ sessionId: currentSessionId, role: "assistant", content: aiData.reply }),
});
}
// Refresh sessions
fetchSessions();
} catch (err) {
console.error("Failed to send:", err);
}
setLoading(false);
};
const deleteSession = async (id: string) => {
try {
await fetch(`/api/chat?id=${id}`, { method: "DELETE" });
setSessions(sessions.filter(s => s.id !== id));
} catch (err) {
console.error("Failed to delete:", err);
}
};
return (
<div className="flex h-screen bg-gradient-to-br from-rose-50 to-amber-50">
{/* Sidebar */}
<div className={`${sidebarOpen ? "w-64" : "w-0"} overflow-hidden transition-all`}>
<div className="w-64 p-4 border-r bg-white h-full overflow-y-auto">
<div className="flex justify-between items-center mb-4">
<h1 className="font-bold">AI Chat</h1>
<button onClick={createNewSession} className="text-rose-400">+</button>
</div>
<div className="space-y-2">
{sessions.map(session => (
<div
key={session.id}
onClick={() => setCurrentSessionId(session.id)}
className={`p-2 rounded-lg cursor-pointer ${session.id === currentSessionId ? "bg-rose-100" : ""}`}
>
<div className="text-sm font-medium truncate">{session.title}</div>
<div className="text-xs text-gray-400">{new Date(session.updatedAt).toLocaleDateString()}</div>
</div>
))}
</div>
</div>
</div>
{/* Main chat */}
<div className="flex-1 flex flex-col">
<div className="p-4 flex items-center border-b bg-white">
<a href="/menu" className="p-2"></a>
<button onClick={() => setSidebarOpen(!sidebarOpen)} className="p-2"></button>
<span className="ml-2 font-medium">{currentSession?.title || "AI Chat"}</span>
</div>
<div className="flex-1 overflow-y-auto p-4 space-y-4">
{currentSession?.messages.map((msg, i) => (
<div key={i} className={`${msg.role === "user" ? "ml-auto bg-rose-100" : "mr-auto bg-gray-100"} p-3 rounded-lg max-w-[80%] whitespace-pre-wrap`}>
{msg.content}
</div>
))}
</div>
<div className="p-4 border-t bg-white">
<div className="flex gap-2">
<input
value={input}
onChange={e => setInput(e.target.value)}
onKeyDown={e => e.key === "Enter" && handleSend()}
placeholder="Ask about your baby..."
className="flex-1 p-3 border rounded-lg"
disabled={loading}
/>
<button onClick={handleSend} disabled={loading || !currentSession} className={`px-4 py-2 text-white rounded-lg ${loading || !currentSession ? "bg-gray-300 cursor-not-allowed" : "bg-rose-400"}`}>
{loading ? "..." : "Send"}
</button>
</div>
</div>
</div>
</div>
);
}