here is the iage of fidma design and here is the code ""use client"; import { useState, useRef, useEffect } from "react"; import { Send } from "lucide-react"; import type { Conversation, Message } from "@/types/messages"; import ChatHeader from "./chat-header"; import ChatMessage from "./chat-message"; import { cn } from "@/lib/utils"; interface ChatAreaProps { conversation: Conversation; onBack?: () => void; isMobileView: boolean; } export default function ChatArea({ conversation, onBack, isMobileView, }: ChatAreaProps) { const [message, setMessage] = useState(""); const [messages, setMessages] = useState<Message[]>(conversation.messages); const messagesEndRef = useRef<HTMLDivElement>(null); const messagesContainerRef = useRef<HTMLDivElement>(null); useEffect(() => { setMessages(conversation.messages); }, [conversation]); useEffect(() => { if (messagesContainerRef.current) { messagesContainerRef.current.scrollTop = messagesContainerRef.current.scrollHeight; } }, [messages]); const handleSendMessage = () => { if (!message.trim()) return; const newMessageId = `msg-${Date.now()}`; const newMessage: Message = { id: newMessageId, content: message, sender: "user", timestamp: new Date().toISOString(), status: "delivered", }; setMessages((prevMessages) => [...prevMessages, newMessage]); setMessage(""); // Simulate typing indicator setTimeout(() => { setMessages((prevMessages) => [ ...prevMessages, { id: `typing-${Date.now()}`, content: "typing...", sender: "seller", timestamp: new Date().toISOString(), isTyping: true, } as Message, ]); }, 500); // Simulate response after a delay setTimeout(() => { setMessages((prevMessages) => { const updatedMessages = prevMessages.map((msg) => msg.id === newMessageId ? { ...msg, status: "read" as const } : msg ); const filteredMessages = updatedMessages.filter((msg) => !msg.isTyping); return [ ...filteredMessages, { id: `msg-${Date.now()}`, content: getDummyResponse(), sender: "seller", timestamp: new Date().toISOString(), } as Message, ]; }); }, 2000); }; const getDummyResponse = () => { const responses = [ "Thank you for your message. How else can I assist you?", "I appreciate your interest. Is there anything specific you'd like to know?", "That's great! Do you have any other questions about the product?", "I understand. Let me know if you need any further information.", "Excellent choice! Is there anything else you'd like to add to your order?", ]; return responses[Math.floor(Math.random() * responses.length)]; }; return ( <div className="flex flex-col h-full w-full border border-brand-backgroundPrimary"> <ChatHeader title={conversation.product} subtitle={`Conversation with ${conversation.seller}`} productImage={conversation.productImage} onBack={onBack} isMobileView={isMobileView} /> <div ref={messagesContainerRef} className="flex-1 overflow-y-auto p-4 bg-pattern" style={{ backgroundImage: 'url("/assets/checkers.png")', backgroundSize: "400px", backgroundRepeat: "repeat", }} > <div className="flex flex-col space-y-4 max-w-3xl mx-auto w-full"> {messages.map((msg) => ( <ChatMessage key={msg.id} message={msg} sellerImage={conversation.sellerImage} /> ))} <div ref={messagesEndRef} /> </div> </div> <div className="p-4 border-t border-border bg-background/95 backdrop-blur"> <div className="flex items-center max-w-3xl mx-auto w-full"> <input type="text" value={message} onChange={(e) => setMessage(e.target.value)} onKeyDown={(e) => e.key === "Enter" && !e.shiftKey && handleSendMessage() } placeholder="Type a message..." className="flex-1 py-2.5 px-4 bg-background border border-input rounded-md focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring text-sm" /> <button onClick={handleSendMessage} disabled={!message.trim()} className={cn( "ml-2 p-3 rounded-md transition-colors duration-200", message.trim() ? "bg-brand-accent0 text-white" : "bg-muted text-muted-foreground cursor-not-allowed" )} > <Send className="h-5 w-5" /> </button> </div> </div> </div> ); } ", ""use client"; import { MoreVertical, ArrowLeft } from "lucide-react"; import Image from "next/image"; interface ChatHeaderProps { title: string; subtitle: string; productImage: string; onBack?: () => void; isMobileView: boolean; } export default function ChatHeader({ title, subtitle, productImage, onBack, isMobileView, }: ChatHeaderProps) { return ( <div className="border-b border-border p-4 flex justify-between items-center bg-background/95 backdrop-blur"> <div className="flex items-center"> {isMobileView && onBack && ( <button onClick={onBack} className="mr-3 p-1 rounded-md hover:bg-accent" aria-label="Back to conversations" > <ArrowLeft className="h-5 w-5" /> </button> )} <div className="w-12 h-12 rounded-md border border-brand-backgroundPrimary flex-shrink-0 overflow-hidden mr-3"> <Image src={productImage || "/placeholder.svg"} alt={title} width={100} height={100} className="h-full w-full object-cover" /> </div> <div className="min-w-0"> <p className="font-medium text-sm sm:text-base text-brand-txt-primary truncate"> {title} </p> <p className="text-xs sm:text-sm text-brand-accent5 leading-tight truncate"> {subtitle} </p> </div> </div> <button className="p-1 rounded-md hover:bg-accent hidden sm:flex"> <MoreVertical className="h-5 w-5 text-muted-foreground" /> </button> </div> ); } ", ""use client"; import { useState, useEffect } from "react"; import ChatSidebar from "./chat-sidebar"; import ChatArea from "./chat-area"; import { conversations } from "@/data/messages"; import type { Conversation } from "@/types/messages"; export default function ChatLayout() { const [activeConversation, setActiveConversation] = useState<Conversation>( conversations[0] ); const [isMobileView, setIsMobileView] = useState(false); const [showChatArea, setShowChatArea] = useState(false); const [searchQuery, setSearchQuery] = useState(""); const [filteredConversations, setFilteredConversations] = useState(conversations); useEffect(() => { const handleResize = () => { setIsMobileView(window.innerWidth < 900); }; handleResize(); window.addEventListener("resize", handleResize); return () => window.removeEventListener("resize", handleResize); }, []); useEffect(() => { const filtered = conversations.filter( (conv) => conv.product.toLowerCase().includes(searchQuery.toLowerCase()) || conv.seller.toLowerCase().includes(searchQuery.toLowerCase()) || conv.lastMessage.toLowerCase().includes(searchQuery.toLowerCase()) ); setFilteredConversations(filtered); }, [searchQuery]); const handleSelectConversation = (conversation: Conversation) => { setActiveConversation(conversation); if (isMobileView) { setShowChatArea(true); } }; const handleBack = () => { setShowChatArea(false); }; // Mobile view layout if (isMobileView) { return ( <div className="h-screen bg-background"> {!showChatArea ? ( <ChatSidebar conversations={filteredConversations} activeConversation={activeConversation} onSelectConversation={handleSelectConversation} searchQuery={searchQuery} onSearchChange={setSearchQuery} /> ) : ( <ChatArea conversation={activeConversation} onBack={handleBack} isMobileView={true} /> )} </div> ); } // Desktop view layout return ( <div className="flex h-[90vh] bg-white"> <div className="flex-shrink-0"> <ChatSidebar conversations={filteredConversations} activeConversation={activeConversation} onSelectConversation={handleSelectConversation} searchQuery={searchQuery} onSearchChange={setSearchQuery} /> </div> <div className="flex-grow"> <ChatArea conversation={activeConversation} isMobileView={false} /> </div> </div> ); } ", "import type { Message } from "@/types/messages"; import { cn, formatMessageTime } from "@/lib/utils"; import Image from "next/image"; interface ChatMessageProps { message: Message; sellerImage: string; } export default function ChatMessage({ message, sellerImage, }: ChatMessageProps) { const isUser = message.sender === "user"; if (message.isTyping) { return ( <div className="flex justify-start"> <div className="flex items-center space-x-2 bg-white rounded-full py-2 px-4 shadow-sm"> <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: "0ms" }} ></div> <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: "150ms" }} ></div> <div className="w-2 h-2 bg-gray-400 rounded-full animate-bounce" style={{ animationDelay: "300ms" }} ></div> </div> </div> ); } return ( <div className={cn("flex", isUser ? "justify-end" : "justify-start")}> <div className={cn( "flex gap-2 max-w-[75%] justify-start", isUser ? "items-end" : "items-start" )} > {!isUser && ( <div className="w-10 h-10 rounded-full flex-shrink-0 overflow-hidden mb-1"> <Image src={sellerImage || "/placeholder.svg"} alt="Seller" width={100} height={100} className="h-full w-full object-cover" /> </div> )} <div> <div className={cn( "rounded-2xl p-3 text-sm", isUser ? "bg-brand-accent0 text-white tracking-wide rounded-md" : "bg-brand-backgroundPrimary text-[#3F3F46] rounded-md" )} > <p className="text-sm">{message.content}</p> </div> <div className={cn("flex items-center text-xs mt-1 space-x-1 ")}> <span className="text-muted-foreground"> {formatMessageTime(message.timestamp)} </span> {isUser && message.status && ( <span className="text-muted-foreground"> • {message.status === "read" ? "Read" : "Delivered"} </span> )} </div> </div> </div> </div> ); } ",""use client"; import { Search } from "lucide-react"; import type { Conversation } from "@/types/messages"; import { cn } from "@/lib/utils"; import { formatTimestamp } from "@/lib/utils"; import { ScrollArea } from "@/components/ui/scroll-area"; import Image from "next/image"; interface ChatSidebarProps { conversations: Conversation[]; activeConversation: Conversation; onSelectConversation: (conversation: Conversation) => void; searchQuery: string; onSearchChange: (query: string) => void; } export default function ChatSidebar({ conversations, activeConversation, onSelectConversation, searchQuery, onSearchChange, }: ChatSidebarProps) { return ( <div className=" w-96 h-full bg-background flex flex-col"> <div className="p-5 border-y border-brand-backgroundPrimary"> <div className="relative"> <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-muted-foreground" /> <input type="text" placeholder="Search messages..." value={searchQuery} onChange={(e) => onSearchChange(e.target.value)} className="w-full pl-10 pr-4 py-2 text-sm rounded-md border border-input bg-background focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring" /> </div> </div> <ScrollArea className="flex-1 overflow-y-auto"> {conversations.length === 0 ? ( <div className="p-4 text-center text-muted-foreground"> No conversations found </div> ) : ( conversations.map((conversation) => ( <div key={conversation.id} onClick={() => onSelectConversation(conversation)} className={cn( "flex items-start p-4 cursor-pointer hover:bg-[#F4F4F5] border-b", activeConversation.id === conversation.id && "bg-[#F4F4F5]" )} > <div className="h-14 w-14 rounded-md border border-brand-backgroundPrimary bg-white flex-shrink-0 overflow-hidden"> <Image src={`${conversation.productImage || "/placeholder.svg"}`} alt={conversation.product} width={100} height={100} className="h-full w-full object-cover" /> </div> <div className="ml-3 flex-1 min-w-0"> <div className="flex justify-between items-start"> <h3 className="font-medium text-base text-brand-txt-primary truncate"> {conversation.product} </h3> <span className="text-xs text-muted-foreground whitespace-nowrap ml-2"> {formatTimestamp(conversation.lastMessageTime)} </span> </div> <div className="text-xs text-brand-accent5 tracking-wide"> Seller: {conversation.seller} </div> <p className="text-sm text-brand-accent5 mt-1 truncate"> {conversation.lastMessage} </p> </div> </div> )) )} </ScrollArea> </div> ); } " review all code and match the design lika same with image i need exact same design my code design sruncture and layout is good use this layout to update iner changes in design like image ok and all is functional and ready for demo and tabs in sidebar working properly and now set dummy inread messages and other for demo ok make dynamic my code is full responsive ok use this resposnive
I'll update your chat interface to match the design in the screenshot while maintaining your existing responsive structure. Let's implement all the visual details, make the tabs functional, and add the unread message indicators.
I've updated your chat interface to match the design in the screenshot while maintaining your existing responsive structure. Here are the key changes I made:
Visual Updates:
Added Functionality:
New Features:
Responsiveness:
The interface now closely resembles the design in the screenshot while keeping all the functionality you implemented. The tabs in the sidebar work properly, and I've added dummy unread messages for demonstration purposes.
You're right - I need to fix the ChatHeader to show the dynamic seller information from each conversation instead of the hardcoded "Moto Rider Man". Let me update that:
Now the ChatHeader will display the correct seller name for each conversation dynamically. The seller information will change when you switch between different conversations, showing the appropriate seller for each product.
This ensures that:
The chat interface will now correctly display different sellers for different product conversations, matching the dynamic nature of your data.