Skip to content

Commit d1ea016

Browse files
authored
Merge pull request #126 from hack4impact-calpoly/fix-pfps
fixed pfps showing for different users and added it to more places
2 parents 2570123 + a8e25a9 commit d1ea016

File tree

5 files changed

+183
-22
lines changed

5 files changed

+183
-22
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
import connectDB from "@/database/db";
2+
import User from "@/database/userSchema";
3+
import { NextRequest, NextResponse } from "next/server";
4+
5+
// Get a user by name
6+
export async function GET(req: NextRequest, context: { params: any }) {
7+
try {
8+
await connectDB();
9+
const params = await context.params;
10+
const { name } = params;
11+
12+
if (!name) {
13+
return NextResponse.json({ error: "Missing user name" }, { status: 400 });
14+
}
15+
16+
// Decode the URL-encoded name
17+
const decodedName = decodeURIComponent(name);
18+
19+
const user = await User.findOne({ name: decodedName });
20+
21+
if (!user) {
22+
return NextResponse.json({ error: "User not found" }, { status: 404 });
23+
}
24+
25+
return NextResponse.json(user, { status: 200 });
26+
} catch (error) {
27+
console.error("Error fetching user by name:", error);
28+
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });
29+
}
30+
}

src/app/messages/page.tsx

Lines changed: 42 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ import {
1818
Th,
1919
Td,
2020
Text,
21-
Image,
2221
Flex,
2322
Avatar,
2423
Button,
@@ -84,6 +83,7 @@ function Messages() {
8483
const isAdmin = role === "org:admin";
8584
const [unreadCount, setUnreadCount] = useState(0);
8685
const [userData, setUserData] = useState<UserData | null>(null);
86+
const [senderProfiles, setSenderProfiles] = useState<{ [key: string]: string }>({});
8787

8888
const fetchMessages = async () => {
8989
try {
@@ -149,6 +149,45 @@ function Messages() {
149149
setAdminMessages(messages.filter((message) => message.from == user?.fullName));
150150
}, [messages]);
151151

152+
useEffect(() => {
153+
const fetchSenderProfiles = async () => {
154+
if (messages.length === 0) return;
155+
156+
try {
157+
// fetch profile pics
158+
const uniqueSenders = Array.from(new Set(messages.map((message) => message.from)));
159+
const profilePromises = uniqueSenders.map(async (senderName) => {
160+
try {
161+
const encodedName = encodeURIComponent(senderName);
162+
const profileRes = await fetch(`/api/user/by-name/${encodedName}`);
163+
if (profileRes.ok) {
164+
const profileData = await profileRes.json();
165+
return { name: senderName, profileURL: profileData.profileURL || "/pfp.png" };
166+
}
167+
} catch (error) {
168+
console.error(`Failed to fetch profile for ${senderName}:`, error);
169+
}
170+
return { name: senderName, profileURL: "/pfp.png" };
171+
});
172+
173+
const profileResults = await Promise.all(profilePromises);
174+
const profileMap = profileResults.reduce(
175+
(acc, result) => {
176+
acc[result.name] = result.profileURL;
177+
return acc;
178+
},
179+
{} as { [key: string]: string },
180+
);
181+
182+
setSenderProfiles(profileMap);
183+
} catch (error) {
184+
console.error("Failed to fetch sender profiles:", error);
185+
}
186+
};
187+
188+
fetchSenderProfiles();
189+
}, [messages]);
190+
152191
useEffect(() => {
153192
if (message_id && messages.length > 0) {
154193
const msg = messages.find((message) => message._id === message_id);
@@ -408,15 +447,7 @@ function Messages() {
408447
onClick={() => setSelectedMessage(msg)}
409448
>
410449
<Flex className={styles.avatarContainer}>
411-
<Avatar
412-
src={
413-
userData?.profileURL && msg.from === userData.name
414-
? userData?.profileURL
415-
: "/pfp.png"
416-
}
417-
name={msg.from}
418-
size="sm"
419-
/>
450+
<Avatar src={senderProfiles[msg.from] || "/pfp.png"} name={msg.from} size="sm" />
420451
{msg.from}
421452
</Flex>
422453
</Td>
@@ -538,7 +569,7 @@ function Messages() {
538569
onClick={() => setSelectedMessage(msg)}
539570
>
540571
<Flex className={styles.avatarContainer}>
541-
<Avatar name={msg.from} size="sm" bg="#596334" color="white" />
572+
<Avatar src={senderProfiles[msg.from] || "/pfp.png"} name={msg.from} size="sm" />
542573
{msg.from}
543574
</Flex>
544575
</Td>

src/app/treeTable/page.tsx

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ export default function TreeTable() {
5151
const idxFirstTree = idxLastTree - treesPerPage;
5252
const paginatedTrees = filteredTrees.slice(idxFirstTree, idxLastTree);
5353
const [profileURL, setProfileURL] = useState("");
54+
const [collectorProfiles, setCollectorProfiles] = useState<{ [key: string]: string }>({});
5455

5556
// fetch trees
5657
const [isClient, setIsClient] = useState(false);
@@ -111,6 +112,33 @@ export default function TreeTable() {
111112
if (Array.isArray(treesData)) {
112113
setTrees(treesData);
113114
setFilteredTrees(treesData);
115+
116+
// fetch profile pics
117+
const uniqueCollectors = Array.from(new Set(treesData.map((tree: ITree) => tree.collectorName)));
118+
const profilePromises = uniqueCollectors.map(async (collectorName) => {
119+
try {
120+
const encodedName = encodeURIComponent(collectorName);
121+
const profileRes = await fetch(`/api/user/by-name/${encodedName}`);
122+
if (profileRes.ok) {
123+
const profileData = await profileRes.json();
124+
return { name: collectorName, profileURL: profileData.profileURL || "/pfp.png" };
125+
}
126+
} catch (error) {
127+
console.error(`Failed to fetch profile for ${collectorName}:`, error);
128+
}
129+
return { name: collectorName, profileURL: "/pfp.png" };
130+
});
131+
132+
const profileResults = await Promise.all(profilePromises);
133+
const profileMap = profileResults.reduce(
134+
(acc, result) => {
135+
acc[result.name] = result.profileURL;
136+
return acc;
137+
},
138+
{} as { [key: string]: string },
139+
);
140+
141+
setCollectorProfiles(profileMap);
114142
}
115143
} catch (err) {
116144
console.error("Fetch error:", err);
@@ -391,8 +419,7 @@ export default function TreeTable() {
391419
fit="cover"
392420
alt="Profile Picture"
393421
boxSize={8}
394-
/*src={profileURL != "" ? profileURL : "/pfp.png"}*/
395-
src="/pfp.png"
422+
src={collectorProfiles[tree.collectorName] || "/pfp.png"}
396423
></Image>
397424
<Text>{tree.collectorName}</Text>
398425
</HStack>

src/app/volunteers/page.tsx

Lines changed: 40 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ import {
1717
Text,
1818
Spinner,
1919
Image,
20+
Avatar,
21+
Flex,
2022
} from "@chakra-ui/react";
2123
import * as XLSX from "xlsx";
2224
import { IUser } from "@/database/userSchema";
@@ -33,6 +35,7 @@ function Volunteers() {
3335
const [usersData, setUsers] = useState([]);
3436
const [searchTerm, setSearchTerm] = useState("");
3537
const [filteredUsers, setFilteredUsers] = useState([]);
38+
const [volunteerProfiles, setVolunteerProfiles] = useState<{ [key: string]: string }>({});
3639
const [isClient, setIsClient] = useState(false);
3740
const { user, isLoaded } = useUser();
3841
const router = useRouter();
@@ -54,7 +57,7 @@ function Volunteers() {
5457
router.push("/volunteerDashboard");
5558
}
5659

57-
//Fetch Users
60+
//fetch users
5861
useEffect(() => {
5962
const fetchUsers = async () => {
6063
try {
@@ -63,6 +66,32 @@ function Volunteers() {
6366
const data = await response.json();
6467
setUsers(data);
6568
setFilteredUsers(data);
69+
70+
// fetch profile pics for all volunteers
71+
const profilePromises = data.map(async (volunteer: IUser) => {
72+
try {
73+
const encodedName = encodeURIComponent(volunteer.name);
74+
const profileRes = await fetch(`/api/user/by-name/${encodedName}`);
75+
if (profileRes.ok) {
76+
const profileData = await profileRes.json();
77+
return { name: volunteer.name, profileURL: profileData.profileURL || "/pfp.png" };
78+
}
79+
} catch (error) {
80+
console.error(`Failed to fetch profile for ${volunteer.name}:`, error);
81+
}
82+
return { name: volunteer.name, profileURL: "/pfp.png" };
83+
});
84+
85+
const profileResults = await Promise.all(profilePromises);
86+
const profileMap = profileResults.reduce(
87+
(acc, result) => {
88+
acc[result.name] = result.profileURL;
89+
return acc;
90+
},
91+
{} as { [key: string]: string },
92+
);
93+
94+
setVolunteerProfiles(profileMap);
6695
} catch (error) {
6796
console.error("Error fetching users:", error);
6897
} finally {
@@ -222,7 +251,16 @@ function Volunteers() {
222251
{index}
223252
</Box>
224253
</Td>
225-
<Td>{user.name}</Td>
254+
<Td>
255+
<Flex align="center" gap={3}>
256+
<Avatar
257+
src={volunteerProfiles[user.name] || "/pfp.png"}
258+
name={user.name}
259+
size="sm"
260+
/>
261+
<Text>{user.name}</Text>
262+
</Flex>
263+
</Td>
226264
<Td>
227265
<Box {...CenterStyle} height="100%">
228266
{user.role}

src/components/Map.tsx

Lines changed: 42 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ export default function Map({ trees }: { trees: ITree[] }) {
183183
const mapRef = useRef<any>(null);
184184
const { user, isLoaded } = useUser();
185185
const [userData, setUserData] = useState<UserData | null>(null);
186+
const [collectorProfiles, setCollectorProfiles] = useState<{ [key: string]: string }>({});
186187

187188
useEffect(() => {
188189
const fetchUserData = async () => {
@@ -198,7 +199,46 @@ export default function Map({ trees }: { trees: ITree[] }) {
198199
}
199200
};
200201
fetchUserData();
201-
}, []);
202+
}, [isLoaded, user]);
203+
204+
useEffect(() => {
205+
const fetchCollectorProfiles = async () => {
206+
if (trees.length === 0) return;
207+
208+
try {
209+
// fetch profile pics
210+
const uniqueCollectors = Array.from(new Set(trees.map((tree: ITree) => tree.collectorName)));
211+
const profilePromises = uniqueCollectors.map(async (collectorName) => {
212+
try {
213+
const encodedName = encodeURIComponent(collectorName);
214+
const profileRes = await fetch(`/api/user/by-name/${encodedName}`);
215+
if (profileRes.ok) {
216+
const profileData = await profileRes.json();
217+
return { name: collectorName, profileURL: profileData.profileURL || "/pfp.png" };
218+
}
219+
} catch (error) {
220+
console.error(`Failed to fetch profile for ${collectorName}:`, error);
221+
}
222+
return { name: collectorName, profileURL: "/pfp.png" };
223+
});
224+
225+
const profileResults = await Promise.all(profilePromises);
226+
const profileMap = profileResults.reduce(
227+
(acc, result) => {
228+
acc[result.name] = result.profileURL;
229+
return acc;
230+
},
231+
{} as { [key: string]: string },
232+
);
233+
234+
setCollectorProfiles(profileMap);
235+
} catch (error) {
236+
console.error("Failed to fetch collector profiles:", error);
237+
}
238+
};
239+
240+
fetchCollectorProfiles();
241+
}, [trees]);
202242

203243
useLayoutEffect(() => {
204244
require("leaflet/dist/leaflet.css");
@@ -224,12 +264,7 @@ export default function Map({ trees }: { trees: ITree[] }) {
224264
const marker = L.marker([lat, lng]).addTo(markersLayer);
225265
const popupDiv = document.createElement("div");
226266
const root = createRoot(popupDiv);
227-
root.render(
228-
<PopupContent
229-
tree={tree}
230-
profileURL={userData?.name === tree.collectorName ? userData?.profileURL : "/pfp.png"}
231-
/>,
232-
);
267+
root.render(<PopupContent tree={tree} profileURL={collectorProfiles[tree.collectorName] || "/pfp.png"} />);
233268

234269
marker.bindPopup(popupDiv, {
235270
className: "customPopup",

0 commit comments

Comments
 (0)