diff --git a/frontend/src/App.js b/frontend/src/App.js index 7d84c25..c487d79 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -8,6 +8,7 @@ import Register from './pages/Register'; import CreateListing from './pages/CreateListing'; import ListingDetail from './pages/ListingDetail'; import Profile from './pages/Profile'; +import Messages from './pages/Messages'; function App() { return ( @@ -22,6 +23,7 @@ function App() { } /> } /> } /> + } /> diff --git a/frontend/src/pages/Messages.js b/frontend/src/pages/Messages.js new file mode 100644 index 0000000..ba69598 --- /dev/null +++ b/frontend/src/pages/Messages.js @@ -0,0 +1,261 @@ +import React, { useState, useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import { messagesAPI } from '../services/api'; +import { useAuth } from '../context/AuthContext'; + +const Messages = () => { + const navigate = useNavigate(); + const { user } = useAuth(); + const [conversations, setConversations] = useState([]); + const [selectedConversation, setSelectedConversation] = useState(null); + const [messages, setMessages] = useState([]); + const [newMessage, setNewMessage] = useState(''); + const [loading, setLoading] = useState(true); + const [sendingMessage, setSendingMessage] = useState(false); + + useEffect(() => { + if (!user) { + navigate('/login'); + return; + } + loadConversations(); + }, [user, navigate]); + + const loadConversations = async () => { + setLoading(true); + try { + const response = await messagesAPI.getConversations(); + setConversations(response.data.conversations); + } catch (error) { + console.error('Failed to load conversations:', error); + } finally { + setLoading(false); + } + }; + + const loadMessages = async (conversation) => { + setSelectedConversation(conversation); + try { + const response = await messagesAPI.getMessages( + conversation.listing_id, + conversation.other_user_id + ); + setMessages(response.data.messages); + } catch (error) { + console.error('Failed to load messages:', error); + } + }; + + const handleSendMessage = async (e) => { + e.preventDefault(); + if (!newMessage.trim() || !selectedConversation) return; + + setSendingMessage(true); + try { + await messagesAPI.send({ + listing_id: selectedConversation.listing_id, + receiver_id: selectedConversation.other_user_id, + message: newMessage, + }); + setNewMessage(''); + // Reload messages + await loadMessages(selectedConversation); + } catch (error) { + alert('Failed to send message'); + } finally { + setSendingMessage(false); + } + }; + + const formatDate = (dateString) => { + const date = new Date(dateString); + const now = new Date(); + const diffMs = now - date; + const diffMins = Math.floor(diffMs / 60000); + const diffHours = Math.floor(diffMs / 3600000); + const diffDays = Math.floor(diffMs / 86400000); + + if (diffMins < 1) return 'Just now'; + if (diffMins < 60) return `${diffMins}m ago`; + if (diffHours < 24) return `${diffHours}h ago`; + if (diffDays < 7) return `${diffDays}d ago`; + return date.toLocaleDateString(); + }; + + if (loading) { + return ( +
+
+

Loading messages...

+
+ ); + } + + return ( +
+

Messages

+ +
+
+ {/* Conversations List */} +
+ {conversations.length === 0 ? ( +
+

No messages yet

+

Start a conversation by contacting a seller on a listing!

+
+ ) : ( + conversations.map((conv) => ( +
loadMessages(conv)} + className={`p-4 border-b border-gray-200 cursor-pointer hover:bg-gray-50 transition-colors ${ + selectedConversation?.listing_id === conv.listing_id && + selectedConversation?.other_user_id === conv.other_user_id + ? 'bg-primary-50' + : '' + }`} + > +
+
+ {conv.other_user_name?.charAt(0).toUpperCase()} +
+
+
+

+ {conv.other_user_name} +

+ + {formatDate(conv.created_at)} + +
+

+ {conv.listing_title} +

+

+ {conv.message} +

+ {!conv.is_read && ( + + )} +
+
+
+ )) + )} +
+ + {/* Messages Area */} +
+ {selectedConversation ? ( + <> + {/* Conversation Header */} +
+
+
+

+ {selectedConversation.other_user_name} +

+

+ Re: {selectedConversation.listing_title} +

+
+ +
+
+ + {/* Messages */} +
+ {messages.length === 0 ? ( +
+

No messages yet. Start the conversation!

+
+ ) : ( +
+ {messages.map((msg) => { + const isSender = msg.sender_id === user?.id; + return ( +
+
+

+ {isSender ? 'You' : msg.sender_name} +

+

{msg.message}

+

+ {formatDate(msg.created_at)} +

+
+
+ ); + })} +
+ )} +
+ + {/* Message Input */} +
+
+