File size: 9,086 Bytes
24d40b9 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
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";
// Sample data for messages
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;
|