| import React, { useState, useRef, useEffect } from 'react'; |
| import { Send, Sparkles, User, Bot, BookOpen, Quote, RefreshCw } from 'lucide-react'; |
|
|
| export const SmartResearcher = ({ allSources, onPromoteToProject, t }) => { |
| const [messages, setMessages] = useState([]); |
| const [input, setInput] = useState(''); |
| const [isLoading, setIsLoading] = useState(false); |
| const messagesEndRef = useRef(null); |
|
|
| const scrollToBottom = () => { |
| messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }); |
| }; |
|
|
| useEffect(() => { |
| scrollToBottom(); |
| }, [messages]); |
|
|
| const handleSend = async () => { |
| if (!input.trim() || isLoading) return; |
|
|
| const userMessage = { |
| role: 'user', |
| content: input, |
| timestamp: Date.now() |
| }; |
|
|
| setMessages(prev => [...prev, userMessage]); |
| setInput(''); |
| setIsLoading(true); |
|
|
| try { |
| |
| const response = await fetch('/api/chat', { |
| method: 'POST', |
| headers: { 'Content-Type': 'application/json' }, |
| body: JSON.stringify({ |
| message: input, |
| history: messages |
| }), |
| }); |
|
|
| const data = await response.json(); |
| |
| const assistantMessage = { |
| role: 'assistant', |
| content: data.text || "عذراً، واجهت مشكلة في الاتصال بالخادم العرفاني.", |
| citations: data.citations || [], |
| timestamp: Date.now() |
| }; |
|
|
| setMessages(prev => [...prev, assistantMessage]); |
| } catch (error) { |
| console.error("Error:", error); |
| } finally { |
| setIsLoading(false); |
| } |
| }; |
|
|
| return ( |
| <div className="flex flex-col h-[calc(100vh-12rem)] max-w-4xl mx-auto bg-white rounded-2xl shadow-xl overflow-hidden border border-slate-200"> |
| {/* Chat Header */} |
| <div className="bg-emerald-900 p-4 text-white flex justify-between items-center"> |
| <div className="flex items-center gap-2"> |
| <Sparkles className="w-5 h-5 text-emerald-300" /> |
| <span className="font-bold">المختبر العلمي لمنارة البحث</span> |
| </div> |
| {messages.length > 0 && ( |
| <button |
| onClick={() => onPromoteToProject(messages)} |
| className="text-xs bg-emerald-700 hover:bg-emerald-600 px-3 py-1.5 rounded-lg transition" |
| > |
| تحويل إلى مشروع بحثي |
| </button> |
| )} |
| </div> |
| |
| {/* Messages Area */} |
| <div className="flex-grow overflow-y-auto p-6 space-y-6 bg-slate-50"> |
| {messages.length === 0 && ( |
| <div className="h-full flex flex-col items-center justify-center text-center space-y-4 opacity-60"> |
| <div className="bg-white p-6 rounded-full shadow-inner"> |
| <Bot className="w-12 h-12 text-emerald-800" /> |
| </div> |
| <p className="max-w-xs text-slate-600 font-serif"> |
| مرحباً بك في رحاب البحث المريدي. اسأل عن قصائد الشيخ، مواقفه، أو أصول المنهج. |
| </p> |
| </div> |
| )} |
| |
| {messages.map((msg, idx) => ( |
| <div key={idx} className={`flex ${msg.role === 'user' ? 'justify-end' : 'justify-start'}`}> |
| <div className={`max-w-[85%] flex gap-3 ${msg.role === 'user' ? 'flex-row-reverse' : 'flex-row'}`}> |
| <div className={`w-8 h-8 rounded-full flex items-center justify-center shrink-0 ${msg.role === 'user' ? 'bg-emerald-100 text-emerald-800' : 'bg-emerald-800 text-white'}`}> |
| {msg.role === 'user' ? <User className="w-5 h-5" /> : <Bot className="w-5 h-5" />} |
| </div> |
| <div className={`p-4 rounded-2xl shadow-sm ${msg.role === 'user' ? 'bg-emerald-700 text-white rounded-tr-none' : 'bg-white border border-slate-200 text-slate-800 rounded-tl-none'}`}> |
| <div className="prose prose-sm leading-relaxed whitespace-pre-wrap font-serif"> |
| {msg.content} |
| </div> |
| {msg.citations && msg.citations.length > 0 && ( |
| <div className="mt-4 pt-3 border-t border-slate-100 space-y-2"> |
| <p className="text-[10px] font-bold text-emerald-600 uppercase tracking-widest">المصادر المرجعية:</p> |
| {msg.citations.map((cit, cIdx) => ( |
| <div key={cIdx} className="flex items-center gap-2 text-xs bg-emerald-50 text-emerald-800 p-2 rounded border border-emerald-100"> |
| <Quote className="w-3 h-3" /> |
| <span>{cit.text}</span> |
| </div> |
| ))} |
| </div> |
| )} |
| </div> |
| </div> |
| </div> |
| ))} |
| {isLoading && ( |
| <div className="flex justify-start"> |
| <div className="bg-white border p-4 rounded-2xl flex items-center gap-3"> |
| <RefreshCw className="w-4 h-4 text-emerald-600 animate-spin" /> |
| <span className="text-sm text-slate-500 italic">الباحث يتدبر في المتون...</span> |
| </div> |
| </div> |
| )} |
| <div ref={messagesEndRef} /> |
| </div> |
| |
| {/* Input Area */} |
| <div className="p-4 bg-white border-t"> |
| <div className="flex gap-3"> |
| <textarea |
| value={input} |
| onChange={(e) => setInput(e.target.value)} |
| onKeyDown={(e) => { if(e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend(); } }} |
| placeholder="اكتب سؤالك هنا عن التراث المريدي..." |
| className="flex-grow p-3 bg-slate-100 border-none rounded-xl focus:ring-2 focus:ring-emerald-500 resize-none h-12 text-sm" |
| /> |
| <button |
| onClick={handleSend} |
| disabled={isLoading || !input.trim()} |
| className="bg-emerald-800 text-white p-3 rounded-xl hover:bg-emerald-700 disabled:opacity-50 transition-colors" |
| > |
| <Send className="w-5 h-5" /> |
| </button> |
| </div> |
| </div> |
| </div> |
| ); |
| }; |