diff --git a/app/(dashboard)/chat/page.tsx b/app/(dashboard)/chat/page.tsx index a855f8c..3f62e52 100644 --- a/app/(dashboard)/chat/page.tsx +++ b/app/(dashboard)/chat/page.tsx @@ -1,339 +1,281 @@ "use client"; -import { useEffect, useState, useRef } from "react"; -import { Brain, Send, MessageSquare, Plus, Trash2, Loader2, Wrench } from "lucide-react"; import { createClient } from "@/lib/supabase/client"; -import { Button } from "poyraz-ui/atoms"; import { useChat } from "@ai-sdk/react"; +import { DefaultChatTransport, type UIMessage } from "ai"; +import { Brain, Loader2, MessageSquare, Plus, Send, Trash2 } from "lucide-react"; +import { Button } from "poyraz-ui/atoms"; +import { useEffect, useRef, useState } from "react"; import toast from "react-hot-toast"; -interface ChatSession { +type ChatSession = { id: string; title: string; created_at: string; -} +}; export default function AIChatPage() { const [supabase] = useState(() => createClient()); const [sessions, setSessions] = useState([]); const [activeSessionId, setActiveSessionId] = useState(null); + const [input, setInput] = useState(""); const messagesEndRef = useRef(null); - const [input, setInput] = useState(""); - const { messages, append, setMessages, isLoading, stop } = useChat({ - api: "/api/chat", - body: { - sessionId: activeSessionId + const { messages, sendMessage, setMessages, status, stop } = useChat({ + transport: new DefaultChatTransport({ api: "/api/chat" }), + onError: (error) => { + console.error(error); + toast.error(error.message || "Yapay zeka ile iletişim kurulurken bir hata oluştu."); }, - onError: (err) => { - console.error(err); - toast.error(err.message || "Yapay zeka ile iletişim kurulurken bir hata oluştu."); - } }); - - const handleInputChange = (e: React.ChangeEvent) => { - setInput(e.target.value); - }; - - // Auto-scroll to bottom - const scrollToBottom = () => { - messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); - }; + const isLoading = status === "submitted" || status === "streaming"; useEffect(() => { - scrollToBottom(); + messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); }, [messages]); - // 1. Fetch Sessions on mount useEffect(() => { - const fetchSessions = async () => { - const { data: { user } } = await supabase.auth.getUser(); + async function fetchSessions() { + const { + data: { user }, + } = await supabase.auth.getUser(); + if (!user) return; const { data } = await supabase .from("chat_sessions") - .select("*") + .select("id, title, created_at") .eq("user_id", user.id) - .order("updated_at", { ascending: false }); + .order("created_at", { ascending: false }); if (data) { setSessions(data); - if (data.length > 0) { - setActiveSessionId(data[0].id); - } + setActiveSessionId(data[0]?.id || null); } - }; - fetchSessions(); - }, [supabase]); - - // 2. Fetch Messages when session changes - useEffect(() => { - if (!activeSessionId) { - setMessages([]); - return; } - const fetchMessages = async () => { + void fetchSessions(); + }, [supabase]); + + useEffect(() => { + async function fetchMessages() { + if (!activeSessionId) { + setMessages([]); + return; + } + const { data } = await supabase .from("chat_messages") - .select("*") + .select("id, role, content") .eq("session_id", activeSessionId) .order("created_at", { ascending: true }); - if (data) { - // Map Supabase messages to AI SDK format - const formattedMessages = data.map((msg: any) => ({ - id: msg.id, - role: msg.role as 'user' | 'assistant' | 'system', - content: msg.content, - })); - setMessages(formattedMessages); - } else { - setMessages([]); - } - }; - - fetchMessages(); - }, [activeSessionId, supabase, setMessages]); + const formattedMessages: UIMessage[] = (data || []).map((message) => ({ + id: message.id, + role: message.role as UIMessage["role"], + parts: [{ type: "text", text: message.content || "" }], + })); - const handleNewChat = async () => { + setMessages(formattedMessages); + } + + void fetchMessages(); + }, [activeSessionId, setMessages, supabase]); + + async function handleNewChat() { setActiveSessionId(null); setMessages([]); - }; + } - const handleDeleteSession = async (id: string, e: React.MouseEvent) => { - e.stopPropagation(); + async function handleDeleteSession(id: string, event: React.MouseEvent) { + event.stopPropagation(); await supabase.from("chat_sessions").delete().eq("id", id); - const updatedSessions = sessions.filter(s => s.id !== id); - setSessions(updatedSessions); - if (activeSessionId === id) { - setActiveSessionId(updatedSessions.length > 0 ? updatedSessions[0].id : null); - if (updatedSessions.length === 0) setMessages([]); - } - }; - const customHandleSubmit = async (e: React.FormEvent) => { - e.preventDefault(); - if (!(input || "").trim() || isLoading) return; + const nextSessions = sessions.filter((session) => session.id !== id); + setSessions(nextSessions); + + if (activeSessionId === id) { + setActiveSessionId(nextSessions[0]?.id || null); + if (nextSessions.length === 0) setMessages([]); + } + } + + async function handleSubmit(event: { preventDefault: () => void }) { + event.preventDefault(); + + const currentInput = input.trim(); + if (!currentInput || isLoading) return; let sessionId = activeSessionId; - const currentInput = input || ""; setInput(""); if (!sessionId) { - const { data: { user } } = await supabase.auth.getUser(); + const { + data: { user }, + } = await supabase.auth.getUser(); + if (!user) return; - const { data: newSess } = await supabase + const { data: newSession } = await supabase .from("chat_sessions") - .insert({ user_id: user.id, title: currentInput.slice(0, 30) + "..." }) - .select() + .insert({ + user_id: user.id, + title: currentInput.length > 32 ? `${currentInput.slice(0, 32)}...` : currentInput, + }) + .select("id, title, created_at") .single(); - - if (newSess) { - sessionId = newSess.id; - setActiveSessionId(sessionId); - setSessions([newSess, ...sessions]); - } else { - return; - } + + if (!newSession) return; + + sessionId = newSession.id; + setActiveSessionId(sessionId); + setSessions((currentSessions) => [newSession, ...currentSessions]); } - // Append using AI SDK with explicit body to ensure sessionId is passed immediately - append({ role: 'user', content: currentInput }, { body: { sessionId } }); - }; + await sendMessage({ text: currentInput }, { body: { sessionId } }); + } return ( -
- - {/* Sidebar - Sessions List */} -
-
-

+
+ - {/* Main Chat Area */} -
- {/* Chat Header */} -
+
+
-
+
-

Ajan Asistan

-

- - Sisteme görev ve veri ekleyebilir -

+

AI Asistan

+

Kayıtlı verilerin hakkında soru sor.

- -
+
- {/* Messages */} -
+
{messages.length === 0 ? ( -
-
- -
-

Ajan Asistan'a Hoşgeldiniz

-

Sadece sorularınızı cevaplamakla kalmaz, komutlarınızla projeler, görevler ve finans kayıtları da oluşturabilir.

- -
- - - - +
+
+
+

Verilerine danış

+

+ Görevler, projeler, müşteriler, finans ve günlük kayıtların hakkında soru sorabilirsin. +

) : ( - messages.map((msg) => ( -
-
-
- {msg.role === 'user' ? ME : } -
- -
- {msg.content && ( -
- {msg.content} -
- )} + messages.map((message) => { + const text = getMessageText(message); - {/* Render Tool Invocations for AI Messages */} - {msg.toolInvocations && msg.toolInvocations.map((tool) => ( -
- {tool.state === 'result' ? ( - <> - - {tool.toolName} aracı başarıyla çalıştırıldı. - - ) : ( - <> - - {tool.toolName} aracı çalıştırılıyor... - - )} -
- ))} + return ( +
+
+ {text}
-
- )) - )} - - {isLoading && messages[messages.length - 1]?.role === 'user' && ( -
-
-
- -
-
- - - -
-
-
+ ); + }) )} + + {isLoading ? ( +
+ + Yanıt hazırlanıyor... +
+ ) : null}
- {/* Input Area */} -
-
-
-