diff --git a/app/(dashboard)/analytics/analytics-client.tsx b/app/(dashboard)/analytics/analytics-client.tsx index 2c1230f..552d6b4 100644 --- a/app/(dashboard)/analytics/analytics-client.tsx +++ b/app/(dashboard)/analytics/analytics-client.tsx @@ -154,7 +154,13 @@ export function AnalyticsClient({ data }: AnalyticsClientProps) { borderRadius: '0.375rem', }} /> - + diff --git a/app/(dashboard)/chat/page.tsx b/app/(dashboard)/chat/page.tsx index a46bc51..04ccea7 100644 --- a/app/(dashboard)/chat/page.tsx +++ b/app/(dashboard)/chat/page.tsx @@ -1,9 +1,10 @@ "use client"; import { useEffect, useState, useRef } from "react"; -import { Brain, Send, Paperclip, MoreHorizontal, Search, MessageSquare, Plus, FileText, CheckCircle2, Trash2 } from "lucide-react"; +import { Brain, Send, MessageSquare, Plus, Trash2, Loader2, Wrench } from "lucide-react"; import { createClient } from "@/lib/supabase/client"; -import { v4 as uuidv4 } from "uuid"; +import { Button } from "poyraz-ui/atoms"; +import { useChat } from "ai/react"; interface ChatSession { id: string; @@ -11,23 +12,22 @@ interface ChatSession { created_at: string; } -interface ChatMessage { - id: string; - session_id: string; - role: "user" | "assistant" | "system"; - content: string; - created_at: string; -} - export default function AIChatPage() { const [supabase] = useState(() => createClient()); const [sessions, setSessions] = useState([]); const [activeSessionId, setActiveSessionId] = useState(null); - const [messages, setMessages] = useState([]); - const [input, setInput] = useState(""); - const [isLoading, setIsLoading] = useState(false); const messagesEndRef = useRef(null); + const { messages, input, setInput, handleInputChange, handleSubmit, append, setMessages, isLoading, stop } = useChat({ + api: "/api/chat", + body: { + sessionId: activeSessionId + }, + onError: (err) => { + console.error(err); + } + }); + // Auto-scroll to bottom const scrollToBottom = () => { messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); @@ -61,7 +61,10 @@ export default function AIChatPage() { // 2. Fetch Messages when session changes useEffect(() => { - if (!activeSessionId) return; + if (!activeSessionId) { + setMessages([]); + return; + } const fetchMessages = async () => { const { data } = await supabase @@ -71,308 +74,256 @@ export default function AIChatPage() { .order("created_at", { ascending: true }); if (data) { - setMessages(data as ChatMessage[]); + // 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(); - - // Subscribe to real-time changes - const channel = supabase - .channel(`chat:${activeSessionId}`) - .on('postgres_changes', { - event: 'INSERT', - schema: 'public', - table: 'chat_messages', - filter: `session_id=eq.${activeSessionId}` - }, (payload) => { - setMessages(prev => { - if (prev.find(m => m.id === payload.new.id)) return prev; - return [...prev, payload.new as ChatMessage]; - }); - }) - .subscribe(); - - return () => { - supabase.removeChannel(channel); - }; - }, [activeSessionId, supabase]); + }, [activeSessionId, supabase, setMessages]); const handleNewChat = async () => { - const { data: { user } } = await supabase.auth.getUser(); - if (!user) return; + setActiveSessionId(null); + setMessages([]); + }; - const newSession = { - user_id: user.id, - title: "Yeni Sohbet", - updated_at: new Date().toISOString() - }; - - const { data, error } = await supabase - .from("chat_sessions") - .insert(newSession) - .select() - .single(); - - if (data) { - setSessions([data, ...sessions]); - setActiveSessionId(data.id); - setMessages([]); + const handleDeleteSession = async (id: string, e: React.MouseEvent) => { + e.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 handleSend = async (e?: React.FormEvent) => { - if (e) e.preventDefault(); + const customHandleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); if (!input.trim() || isLoading) return; - const userMessageContent = input.trim(); - setInput(""); - - // Ensure we have a session let sessionId = activeSessionId; - const { data: { user } } = await supabase.auth.getUser(); - if (!user) return; + const currentInput = input; + setInput(""); if (!sessionId) { + const { data: { user } } = await supabase.auth.getUser(); + if (!user) return; + const { data: newSess } = await supabase .from("chat_sessions") - .insert({ user_id: user.id, title: userMessageContent.slice(0, 30) + "..." }) + .insert({ user_id: user.id, title: currentInput.slice(0, 30) + "..." }) .select() .single(); + if (newSess) { sessionId = newSess.id; setActiveSessionId(sessionId); setSessions([newSess, ...sessions]); - } else return; - } - - const resolvedSessionId = sessionId; - if (!resolvedSessionId) return; - - setIsLoading(true); - - // 1. Optimistic Update: Add User Message locally - const optimisticUserMsg: ChatMessage = { - id: uuidv4(), - session_id: resolvedSessionId, - role: "user", - content: userMessageContent, - created_at: new Date().toISOString() - }; - setMessages(prev => [...prev, optimisticUserMsg]); - - // 2. Save User Message to Supabase - await supabase.from("chat_messages").insert({ - id: optimisticUserMsg.id, - session_id: resolvedSessionId, - role: "user", - content: userMessageContent - }); - - try { - // 3. Fetch AI preferences for API call - const { data: settings } = await supabase - .from("user_settings") - .select("*") - .eq("user_id", user.id) - .single(); - - const provider = settings?.ai_model || localStorage.getItem("mindspace_ai_provider") || "gemini"; - const apiKey = settings?.api_key || localStorage.getItem("mindspace_api_key") || ""; - - // 4. Call AI API - const res = await fetch("/api/chat", { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify({ provider, apiKey, userMessageContent }), - }); - - if (!res.ok) throw new Error("AI API Error"); - const data = await res.json(); - - // 5. Optimistic Update: Add Assistant Message locally - const assistantMsg: ChatMessage = { - id: uuidv4(), - session_id: resolvedSessionId, - role: "assistant", - content: data.reply || "Cevap üretilemedi.", - created_at: new Date().toISOString() - }; - setMessages(prev => [...prev, assistantMsg]); - - // 6. Save Assistant Message to Supabase - await supabase.from("chat_messages").insert({ - id: assistantMsg.id, - session_id: resolvedSessionId, - role: "assistant", - content: assistantMsg.content - }); - - // 7. Update session title if it was default - const currentSession = sessions.find(s => s.id === resolvedSessionId); - if (currentSession?.title === "Yeni Sohbet") { - const newTitle = userMessageContent.slice(0, 40); - await supabase.from("chat_sessions").update({ title: newTitle }).eq("id", resolvedSessionId); - setSessions(prev => prev.map(s => s.id === resolvedSessionId ? { ...s, title: newTitle } : s)); + } else { + return; } - - } catch (error) { - console.error(error); - const errorMsg: ChatMessage = { - id: uuidv4(), - session_id: resolvedSessionId, - role: "assistant", - content: "Hata oluştu. Lütfen Ayarlar sayfasından API anahtarınızı kontrol edin.", - created_at: new Date().toISOString() - }; - setMessages(prev => [...prev, errorMsg]); - await supabase.from("chat_messages").insert({ - id: errorMsg.id, - session_id: resolvedSessionId, - role: "assistant", - content: errorMsg.content - }); - } finally { - setIsLoading(false); } - }; - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === "Enter" && !e.shiftKey) { - e.preventDefault(); - handleSend(); - } - }; - - const deleteSession = async (id: string, e: React.MouseEvent) => { - e.stopPropagation(); - await supabase.from("chat_sessions").delete().eq("id", id); - setSessions(prev => prev.filter(s => s.id !== id)); - if (activeSessionId === id) { - setActiveSessionId(null); - setMessages([]); - } + // Append using AI SDK with explicit body to ensure sessionId is passed immediately + append({ role: 'user', content: currentInput }, { body: { sessionId } }); }; return ( -
+
- {/* Top Header */} -
-

- - MindSpace / AI Assistant -

-
- + +
+ +
+ {sessions.length === 0 ? ( +
Henüz sohbet yok.
+ ) : ( + sessions.map(session => ( +
setActiveSessionId(session.id)} + className={`group flex items-center justify-between p-3 rounded-md cursor-pointer transition-colors ${activeSessionId === session.id ? 'bg-primary/10 text-primary' : 'hover:bg-muted/50 text-foreground'}`} + > +
+ {session.title || "İsimsiz Sohbet"} +
+ +
+ )) + )}
- {/* Main Chat Interface */} -
- - {/* Left Sidebar: Chat Sessions */} -
-
-
- - + {/* Main Chat Area */} +
+ {/* Chat Header */} +
+
+
+ +
+
+

Ajan Asistan

+

+ + Sisteme görev ve veri ekleyebilir +

- -
-
Recent Conversations
- {sessions.map((session) => ( -
setActiveSessionId(session.id)} - className={`group flex items-center gap-3 p-3 rounded-sm cursor-pointer transition-colors ${activeSessionId === session.id ? 'bg-[#1F172B] border border-primary/30 text-primary' : 'hover:bg-white/5 text-muted-foreground hover:text-foreground border border-transparent'}`} - > - -
-
{session.title}
-
{new Date(session.created_at).toLocaleDateString()}
-
- -
- ))} -
+
- {/* Right Side: Active Chat Area */} -
- - {/* Chat Messages Area */} -
- {messages.length === 0 && !isLoading && ( -
- -

Hello! I am your MindSpace AI Assistant.

-

Start a conversation or select a session from history.

+ {/* Messages */} +
+ {messages.length === 0 ? ( +
+
+
- )} - - {messages.map((m) => ( -
- {m.role === "assistant" && ( -
- -
- )} -
-

{m.content}

-
- {m.role === "user" && ( -
ME
- )} -
- ))} - {isLoading && ( -
- - - - - MindSpace AI is thinking... -
- )} -
-
- - {/* Chat Input Box */} -
-
-