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 (
+
+ )
+ }
+
+ if (error) {
+ return (
+
+
+
{error}
+
+
+
+ )
+ }
+
+ return (
+
+
+
+ {/* Back button */}
+
+
+ {/* Profile Header */}
+
+ {/* Profile Picture */}
+
+ {userData?.profilePic ? (
+

+ ) : (
+
+ {(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