|
"use client" |
|
|
|
import type React from "react" |
|
|
|
import { useState, useRef, useEffect } from "react" |
|
import { Button } from "@/components/ui/button" |
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" |
|
import { Input } from "@/components/ui/input" |
|
import { Badge } from "@/components/ui/badge" |
|
import { MessageCircle, Send, X, Sparkles, Heart } from "lucide-react" |
|
import { motion, AnimatePresence } from "framer-motion" |
|
|
|
interface Message { |
|
id: string |
|
text: string |
|
isUser: boolean |
|
timestamp: Date |
|
} |
|
|
|
export default function MostarAIAssistant() { |
|
const [isOpen, setIsOpen] = useState(false) |
|
const [messages, setMessages] = useState<Message[]>([ |
|
{ |
|
id: "welcome", |
|
text: "Sawubona! I am Mostar, your Ubuntu AI guide. I embody the spirit of 'I am because we are.' Ask me about FlameBorn, Ubuntu philosophy, African wisdom, or healthcare tokenization. How can we grow together today?", |
|
isUser: false, |
|
timestamp: new Date(), |
|
}, |
|
]) |
|
const [inputValue, setInputValue] = useState("") |
|
const [isTyping, setIsTyping] = useState(false) |
|
const messagesEndRef = useRef<HTMLDivElement>(null) |
|
|
|
const scrollToBottom = () => { |
|
messagesEndRef.current?.scrollIntoView({ behavior: "smooth" }) |
|
} |
|
|
|
useEffect(() => { |
|
scrollToBottom() |
|
}, [messages]) |
|
|
|
const ubuntuResponses = [ |
|
"In Ubuntu philosophy, we understand that individual wellness flows from community health. Your question touches the heart of our interconnectedness.", |
|
"As the Zulu saying goes: 'Umuntu ngumuntu ngabantu' - a person is a person through other persons. This wisdom guides everything we do at FlameBorn.", |
|
"The flame of healing burns brightest when we tend it together. In our tokenization model, every reward strengthens the whole network.", |
|
"Ubuntu teaches us that when one suffers, we all suffer. When one heals, we all heal. This is why FlameBorn exists - to weave digital bonds that strengthen real healing.", |
|
"In traditional African communities, the birth of a child was celebrated by the entire village. FlameBorn brings this spirit to the digital age through our birth registration tokens.", |
|
"The ancestors whisper: 'What affects one, affects all.' Our blockchain preserves this wisdom, ensuring that every healthcare action ripples through the community.", |
|
"Ubuntu is not just philosophy - it's a way of being. When healthcare workers earn FLB tokens, they're not just being rewarded, they're being recognized as vital threads in our community fabric.", |
|
] |
|
|
|
const handleSendMessage = async () => { |
|
if (!inputValue.trim()) return |
|
|
|
const userMessage: Message = { |
|
id: Date.now().toString(), |
|
text: inputValue, |
|
isUser: true, |
|
timestamp: new Date(), |
|
} |
|
|
|
setMessages((prev) => [...prev, userMessage]) |
|
setInputValue("") |
|
setIsTyping(true) |
|
|
|
|
|
setTimeout(() => { |
|
const aiResponse: Message = { |
|
id: (Date.now() + 1).toString(), |
|
text: ubuntuResponses[Math.floor(Math.random() * ubuntuResponses.length)], |
|
isUser: false, |
|
timestamp: new Date(), |
|
} |
|
|
|
setMessages((prev) => [...prev, aiResponse]) |
|
setIsTyping(false) |
|
}, 1500) |
|
} |
|
|
|
const handleKeyPress = (e: React.KeyboardEvent) => { |
|
if (e.key === "Enter" && !e.shiftKey) { |
|
e.preventDefault() |
|
handleSendMessage() |
|
} |
|
} |
|
|
|
return ( |
|
<> |
|
{/* Floating Chat Button */} |
|
<motion.div className="fixed bottom-6 right-6 z-50" whileHover={{ scale: 1.05 }} whileTap={{ scale: 0.95 }}> |
|
<Button |
|
onClick={() => setIsOpen(true)} |
|
className="h-14 w-14 rounded-full bg-gradient-to-r from-orange-500 to-red-500 hover:from-orange-600 hover:to-red-600 shadow-lg" |
|
> |
|
<MessageCircle className="h-6 w-6 text-white" /> |
|
</Button> |
|
|
|
{/* Pulsing indicator */} |
|
<div className="absolute -top-1 -right-1"> |
|
<div className="h-4 w-4 bg-green-500 rounded-full animate-pulse" /> |
|
</div> |
|
</motion.div> |
|
|
|
{/* Chat Modal */} |
|
<AnimatePresence> |
|
{isOpen && ( |
|
<motion.div |
|
initial={{ opacity: 0 }} |
|
animate={{ opacity: 1 }} |
|
exit={{ opacity: 0 }} |
|
className="fixed inset-0 bg-black/50 backdrop-blur-sm z-50 flex items-center justify-center p-4" |
|
onClick={() => setIsOpen(false)} |
|
> |
|
<motion.div |
|
initial={{ scale: 0.9, opacity: 0 }} |
|
animate={{ scale: 1, opacity: 1 }} |
|
exit={{ scale: 0.9, opacity: 0 }} |
|
onClick={(e) => e.stopPropagation()} |
|
className="w-full max-w-md h-[600px] flex flex-col" |
|
> |
|
<Card className="flex-1 flex flex-col bg-white/95 backdrop-blur-sm border-orange-200"> |
|
<CardHeader className="flex-row items-center justify-between space-y-0 pb-4 border-b border-orange-100"> |
|
<div className="flex items-center space-x-2"> |
|
<div className="h-10 w-10 rounded-full bg-gradient-to-r from-orange-500 to-red-500 flex items-center justify-center"> |
|
<Heart className="h-5 w-5 text-white" /> |
|
</div> |
|
<div> |
|
<CardTitle className="text-lg">Mostar AI</CardTitle> |
|
<Badge variant="outline" className="text-xs"> |
|
<Sparkles className="h-3 w-3 mr-1" /> |
|
Ubuntu Guide |
|
</Badge> |
|
</div> |
|
</div> |
|
<Button variant="ghost" size="sm" onClick={() => setIsOpen(false)}> |
|
<X className="h-4 w-4" /> |
|
</Button> |
|
</CardHeader> |
|
|
|
<CardContent className="flex-1 flex flex-col p-0"> |
|
{/* Messages */} |
|
<div className="flex-1 overflow-y-auto p-4 space-y-4"> |
|
{messages.map((message) => ( |
|
<div key={message.id} className={`flex ${message.isUser ? "justify-end" : "justify-start"}`}> |
|
<div |
|
className={`max-w-[80%] p-3 rounded-lg ${ |
|
message.isUser ? "bg-orange-500 text-white" : "bg-gray-100 text-gray-900" |
|
}`} |
|
> |
|
<p className="text-sm">{message.text}</p> |
|
<p className="text-xs opacity-70 mt-1"> |
|
{message.timestamp.toLocaleTimeString([], { |
|
hour: "2-digit", |
|
minute: "2-digit", |
|
})} |
|
</p> |
|
</div> |
|
</div> |
|
))} |
|
|
|
{isTyping && ( |
|
<div className="flex justify-start"> |
|
<div className="bg-gray-100 p-3 rounded-lg"> |
|
<div className="flex space-x-1"> |
|
<div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" /> |
|
<div |
|
className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" |
|
style={{ animationDelay: "0.1s" }} |
|
/> |
|
<div |
|
className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" |
|
style={{ animationDelay: "0.2s" }} |
|
/> |
|
</div> |
|
</div> |
|
</div> |
|
)} |
|
|
|
<div ref={messagesEndRef} /> |
|
</div> |
|
|
|
{/* Input */} |
|
<div className="border-t border-orange-100 p-4"> |
|
<div className="flex space-x-2"> |
|
<Input |
|
value={inputValue} |
|
onChange={(e) => setInputValue(e.target.value)} |
|
onKeyPress={handleKeyPress} |
|
placeholder="Ask about Ubuntu, FlameBorn, or African wisdom..." |
|
className="flex-1" |
|
disabled={isTyping} |
|
/> |
|
<Button |
|
onClick={handleSendMessage} |
|
disabled={!inputValue.trim() || isTyping} |
|
className="bg-orange-500 hover:bg-orange-600" |
|
> |
|
<Send className="h-4 w-4" /> |
|
</Button> |
|
</div> |
|
</div> |
|
</CardContent> |
|
</Card> |
|
</motion.div> |
|
</motion.div> |
|
)} |
|
</AnimatePresence> |
|
</> |
|
) |
|
} |
|
|