|
|
|
import { useState } from "react";
|
|
import { Send, Search } from "lucide-react";
|
|
import AppLayout from "@/components/layout/AppLayout";
|
|
import { Input } from "@/components/ui/input";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Avatar } from "@/components/ui/avatar";
|
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
|
import { cn } from "@/lib/utils";
|
|
import { motion } from "framer-motion";
|
|
import { ThemeToggle } from "@/components/theme/ThemeToggle";
|
|
|
|
|
|
const initialContacts = [
|
|
{ id: 1, name: "AI Assistant", avatar: "A", lastMessage: "How can I help with your finances?", time: "10:30 AM", unread: 2 },
|
|
{ id: 2, name: "Budget Bot", avatar: "B", lastMessage: "Your weekly spending report is ready", time: "Yesterday", unread: 0 },
|
|
{ id: 3, name: "Investment Advisor", avatar: "I", lastMessage: "Consider these stocks for your portfolio", time: "Yesterday", unread: 1 },
|
|
{ id: 4, name: "Expense Tracker", avatar: "E", lastMessage: "You've exceeded your dining budget", time: "Monday", unread: 0 },
|
|
{ id: 5, name: "Financial Coach", avatar: "F", lastMessage: "Let's review your saving goals", time: "08/12/23", unread: 0 },
|
|
];
|
|
|
|
const initialMessages = [
|
|
{ id: 1, sender: "client", text: "Hello! I need some help understanding my recent transactions.", time: "10:30 AM" },
|
|
{ id: 2, sender: "me", text: "Hi there! I'd be happy to help you analyze your spending patterns. What specifically would you like to know?", time: "10:32 AM" },
|
|
{ id: 3, sender: "client", text: "I noticed some unusual activity in my account", time: "10:33 AM" },
|
|
{ id: 4, sender: "me", text: "I can check that for you. Could you tell me which transactions look suspicious?", time: "10:35 AM" },
|
|
{ id: 5, sender: "me", text: "Based on your spending history, the transaction at 'Tech Store' for $349.99 is unusual for you.", time: "10:36 AM" },
|
|
{ id: 6, sender: "client", text: "Yes, that's the one I was concerned about. I don't remember making that purchase.", time: "10:38 AM" },
|
|
];
|
|
|
|
const Messages = () => {
|
|
const [contacts, setContacts] = useState(initialContacts);
|
|
const [selectedContact, setSelectedContact] = useState(initialContacts[0]);
|
|
const [messages, setMessages] = useState(initialMessages);
|
|
const [newMessage, setNewMessage] = useState("");
|
|
const [searchTerm, setSearchTerm] = useState("");
|
|
|
|
const handleSendMessage = () => {
|
|
if (newMessage.trim() === "") return;
|
|
|
|
const message = {
|
|
id: messages.length + 1,
|
|
sender: "me",
|
|
text: newMessage,
|
|
time: new Date().toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })
|
|
};
|
|
|
|
setMessages([...messages, message]);
|
|
setNewMessage("");
|
|
};
|
|
|
|
const filteredContacts = contacts.filter(contact =>
|
|
contact.name.toLowerCase().includes(searchTerm.toLowerCase())
|
|
);
|
|
|
|
return (
|
|
<AppLayout>
|
|
<div className="max-w-6xl mx-auto h-full p-4">
|
|
<div className="flex justify-between items-center mb-4">
|
|
<h1 className="text-xl font-bold text-slate-800 dark:text-white">Messages</h1>
|
|
<ThemeToggle />
|
|
</div>
|
|
|
|
<div className="flex h-[calc(100vh-180px)] rounded-2xl overflow-hidden bg-white/80 dark:bg-violet-darker/90 backdrop-blur-lg border border-slate-200 dark:border-violet-muted/20 shadow-lg">
|
|
{/* Contacts sidebar */}
|
|
<div className="w-full max-w-xs border-r border-slate-200 dark:border-violet-muted/20 hidden md:flex flex-col">
|
|
<div className="p-3 border-b border-slate-200 dark:border-violet-muted/20">
|
|
<div className="relative">
|
|
<Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-slate-400 dark:text-violet-light/50 h-4 w-4" />
|
|
<Input
|
|
placeholder="Search contacts..."
|
|
className="pl-10 bg-slate-50 dark:bg-violet-darker border-slate-200 dark:border-violet-muted/30 text-slate-800 dark:text-white"
|
|
value={searchTerm}
|
|
onChange={(e) => setSearchTerm(e.target.value)}
|
|
/>
|
|
</div>
|
|
</div>
|
|
|
|
<ScrollArea className="flex-1">
|
|
{filteredContacts.map(contact => (
|
|
<div
|
|
key={contact.id}
|
|
className={cn(
|
|
"p-3 flex items-center gap-3 cursor-pointer hover:bg-slate-50 dark:hover:bg-violet-muted/10 transition-colors",
|
|
selectedContact.id === contact.id ? "bg-slate-50 dark:bg-violet-muted/10" : ""
|
|
)}
|
|
onClick={() => setSelectedContact(contact)}
|
|
>
|
|
<Avatar className={cn(
|
|
"h-10 w-10 text-white",
|
|
selectedContact.id === contact.id
|
|
? "bg-primary dark:bg-violet"
|
|
: "bg-blue-600 dark:bg-violet-muted"
|
|
)}>
|
|
<div>{contact.avatar}</div>
|
|
</Avatar>
|
|
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex justify-between items-center">
|
|
<span className="font-medium truncate text-slate-800 dark:text-white">{contact.name}</span>
|
|
<span className="text-xs text-slate-500 dark:text-violet-light/70">{contact.time}</span>
|
|
</div>
|
|
<p className="text-sm text-slate-500 dark:text-violet-light/50 truncate">{contact.lastMessage}</p>
|
|
</div>
|
|
|
|
{contact.unread > 0 && (
|
|
<div className="bg-blue-600 dark:bg-violet text-white text-xs rounded-full h-5 w-5 flex items-center justify-center">
|
|
{contact.unread}
|
|
</div>
|
|
)}
|
|
</div>
|
|
))}
|
|
</ScrollArea>
|
|
</div>
|
|
|
|
{/* Chat area */}
|
|
<div className="flex-1 flex flex-col">
|
|
<div className="p-3 border-b border-slate-200 dark:border-violet-muted/20 flex items-center gap-3">
|
|
<Avatar className="h-8 w-8 bg-blue-600 dark:bg-violet text-white md:hidden">
|
|
<div>{selectedContact.avatar}</div>
|
|
</Avatar>
|
|
<div>
|
|
<h3 className="font-medium text-slate-800 dark:text-white">{selectedContact.name}</h3>
|
|
</div>
|
|
</div>
|
|
|
|
<ScrollArea className="flex-1 p-4">
|
|
<div className="space-y-4">
|
|
{messages.map((message, index) => (
|
|
<motion.div
|
|
key={message.id}
|
|
initial={{ opacity: 0, y: 10 }}
|
|
animate={{ opacity: 1, y: 0 }}
|
|
transition={{ duration: 0.3, delay: index * 0.1 }}
|
|
className={cn(
|
|
"flex",
|
|
message.sender === "me" ? "justify-end" : "justify-start"
|
|
)}
|
|
>
|
|
<div
|
|
className={cn(
|
|
"max-w-[80%] p-3 rounded-2xl shadow-sm",
|
|
message.sender === "me"
|
|
? "bg-blue-600 dark:bg-violet text-white rounded-tr-none"
|
|
: "bg-slate-100 dark:bg-violet-muted/30 text-slate-800 dark:text-white rounded-tl-none"
|
|
)}
|
|
>
|
|
<p>{message.text}</p>
|
|
<span className={cn(
|
|
"text-xs block mt-1",
|
|
message.sender === "me"
|
|
? "text-white/70"
|
|
: "text-slate-500 dark:text-violet-light/70"
|
|
)}>
|
|
{message.time}
|
|
</span>
|
|
</div>
|
|
</motion.div>
|
|
))}
|
|
</div>
|
|
</ScrollArea>
|
|
|
|
<div className="p-3 border-t border-slate-200 dark:border-violet-muted/20 flex items-center gap-2">
|
|
<Input
|
|
placeholder="Type a message..."
|
|
value={newMessage}
|
|
onChange={(e) => setNewMessage(e.target.value)}
|
|
onKeyDown={(e) => {
|
|
if (e.key === "Enter") {
|
|
handleSendMessage();
|
|
}
|
|
}}
|
|
className="flex-1 bg-slate-50 dark:bg-violet-darker border-slate-200 dark:border-violet-muted/30 text-slate-800 dark:text-white"
|
|
/>
|
|
<Button size="icon" className="bg-blue-600 hover:bg-blue-700 dark:bg-violet dark:hover:bg-violet-dark" onClick={handleSendMessage}>
|
|
<Send size={18} />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</AppLayout>
|
|
);
|
|
};
|
|
|
|
export default Messages;
|
|
|