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 */}
+
+ >
+ ) : (
+
+
+
+
Select a conversation to view messages
+
+
+ )}
+
+
+
+
+ );
+};
+
+export default Messages;