From b1e4fe54db15bfb22c494d5bf35940d9da3816f8 Mon Sep 17 00:00:00 2001 From: Kieran McKenna Date: Sun, 19 Oct 2025 19:44:23 +1300 Subject: [PATCH] Made it so you can see other users profiles --- frontend/src/App.js | 2 + frontend/src/firebase/firestore.js | 11 +- frontend/src/pages/ItemDetail.js | 13 +- frontend/src/pages/PublicProfile.js | 177 ++++++++++++++++++++++++++++ 4 files changed, 197 insertions(+), 6 deletions(-) create mode 100644 frontend/src/pages/PublicProfile.js diff --git a/frontend/src/App.js b/frontend/src/App.js index 489dea3..552b113 100644 --- a/frontend/src/App.js +++ b/frontend/src/App.js @@ -14,6 +14,7 @@ import AddAnnouncementPage from './pages/AddAnnouncement'; import EditAnnouncementPage from './pages/EditAnnouncement'; import MessagesPage from './pages/Messages'; import NotificationSettingsPage from './pages/NotificationSettingsPage'; +import PublicProfile from './pages/PublicProfile'; // Route configuration for better maintainability const ROUTES = [ @@ -21,6 +22,7 @@ const ROUTES = [ { path: '/feed', element: FeedPage }, { path: '/items/:id', element: ItemDetailPage }, { path: '/profile', element: ProfilePage }, + { path: '/profile/:userId', element: PublicProfile }, { path: '/login', element: LoginPage }, { path: '/signup', element: SignUpPage }, { path: '/items/new', element: ReportPage }, diff --git a/frontend/src/firebase/firestore.js b/frontend/src/firebase/firestore.js index 627504b..02929d2 100644 --- a/frontend/src/firebase/firestore.js +++ b/frontend/src/firebase/firestore.js @@ -13,13 +13,16 @@ export async function getUserPosts(userId) { const querySnapshot = await getDocs(q); const posts = []; - querySnapshot.forEach((doc) => { - const itemData = doc.data(); + querySnapshot.forEach((docSnapshot) => { + const itemData = docSnapshot.data(); + // Only include items that are not resolved (exclude closed posts) if (itemData.status?.toLowerCase() !== 'resolved') { posts.push({ - id: doc.id, - ...itemData + id: docSnapshot.id, + ...itemData, + // Normalize: if kind is missing, use status field as fallback + kind: itemData.kind || (itemData.status?.toLowerCase() === 'found' || itemData.status?.toLowerCase() === 'lost' ? itemData.status : undefined) }); } }); diff --git a/frontend/src/pages/ItemDetail.js b/frontend/src/pages/ItemDetail.js index 3ca67a7..ab263ee 100644 --- a/frontend/src/pages/ItemDetail.js +++ b/frontend/src/pages/ItemDetail.js @@ -288,9 +288,18 @@ const ItemDetailPage = () => { Posted: {` ${new Date(item.date).toLocaleString()}`}

-

+

Reporter: - {` ${userInfo?.name || 'Unknown User'}`} + {userInfo?.id ? ( + + {userInfo.name} + + ) : ( + {userInfo?.name || 'Unknown User'} + )}

diff --git a/frontend/src/pages/PublicProfile.js b/frontend/src/pages/PublicProfile.js new file mode 100644 index 0000000..0d7572e --- /dev/null +++ b/frontend/src/pages/PublicProfile.js @@ -0,0 +1,177 @@ +import React, { useState, useEffect } from 'react' +import { useParams, useNavigate } from 'react-router-dom' +import { doc, getDoc, collection, query, where, onSnapshot, orderBy } from 'firebase/firestore' +import { db } from '../firebase/config' +import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/card' +import { ArrowLeft } from 'lucide-react' +import ItemCard from '../components/ItemCard' +import { normalizeFirestoreItem } from '../lib/utils' + +const PublicProfile = () => { + const { userId } = useParams() + const navigate = useNavigate() + const [userData, setUserData] = useState(null) + const [userPosts, setUserPosts] = useState([]) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + // Fetch user data + useEffect(() => { + if (!userId) { + setError('No user ID provided') + setLoading(false) + return + } + + const fetchUserData = async () => { + try { + const userDocRef = doc(db, 'users', userId) + const userDocSnap = await getDoc(userDocRef) + + if (!userDocSnap.exists()) { + setError('User not found') + setLoading(false) + return + } + + const data = userDocSnap.data() + setUserData(data) + } catch (err) { + console.error('Error fetching user data:', err) + setError('Failed to load user profile') + } + } + + fetchUserData() + }, [userId]) + + // Fetch user posts with real-time listener (exactly like Feed page) + useEffect(() => { + if (!userId) return + + setLoading(true) + setError(null) + + try { + const itemsRef = collection(db, 'items') + const userDocRef = doc(db, 'users', userId) + const itemsQuery = query( + itemsRef, + where('postedBy', '==', userDocRef), + orderBy('date', 'desc') + ) + + const unsubscribe = onSnapshot( + itemsQuery, + (snapshot) => { + const fetchedItems = snapshot.docs.map((doc) => + normalizeFirestoreItem(doc.data() || {}, doc.id) + ) + setUserPosts(fetchedItems) + setLoading(false) + }, + (err) => { + console.error('Error fetching user posts:', err) + setError('Failed to load posts') + setLoading(false) + } + ) + + return () => unsubscribe() + } catch (err) { + console.error('Error setting up posts listener:', err) + setError('Failed to connect to database') + setLoading(false) + } + }, [userId]) + + if (loading) { + return ( +
+
+
+

Loading profile...

+
+
+ ) + } + + if (error) { + return ( +
+
+

{error}

+ +
+
+ ) + } + + return ( +
+
+
+ {/* Back button */} + + + {/* Profile Header */} +
+ {/* Profile Picture */} +
+ {userData?.profilePic ? ( + {`${userData.name}'s + ) : ( +
+ {(userData?.name || 'U')[0].toUpperCase()} +
+ )} +
+ + {/* User Info */} +
+

{userData?.name || 'User'}

+

+ {userPosts.length} {userPosts.length === 1 ? 'post' : 'posts'} +

+
+
+ + {/* User's Posts */} + + + Posts by {userData?.name || 'this user'} + + + {userPosts.length === 0 ? ( +

No posts yet

+ ) : ( +
+ {userPosts.map((post) => ( + + ))} +
+ )} +
+
+
+
+
+ ) +} + +export default PublicProfile \ No newline at end of file