Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
150 changes: 150 additions & 0 deletions code/frontend/src/components/ParticipantsList.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
import { useState } from "react";
import { Collapse } from "@mui/material";
import { activityService } from "../services/activityService";

const ParticipantsList = ({ activityId, onError }) => {
const [participants, setParticipants] = useState(null);
const [isExpanded, setIsExpanded] = useState(false);
const [isLoading, setIsLoading] = useState(false);

const fetchParticipants = async () => {
if (participants) return; // Already loaded

setIsLoading(true);
try {
const response = await activityService.getActivityParticipants(activityId);
setParticipants(response.content || []);
} catch (error) {
console.error("Failed to fetch participants:", error);
onError?.("Failed to load participants");
} finally {
setIsLoading(false);
}
};

const toggleExpand = async () => {
const wasExpanded = isExpanded;
setIsExpanded(!wasExpanded);

if (!wasExpanded && !participants) {
await fetchParticipants();
}
};

return (
<>
<button style={styles.participantsButton} onClick={toggleExpand}>
{isExpanded ? "Hide Participants" : "Show Participants"}
</button>
<Collapse in={isExpanded}>
<div style={styles.participantsSection}>
<h4 style={styles.participantsTitle}>Participants:</h4>
{isLoading ? (
<div style={styles.loadingText}>Loading participants...</div>
) : participants && participants.length > 0 ? (
<div style={styles.participantsList}>
{participants.map((participant, index) => (
<div key={index} style={styles.participantItem}>
<div style={styles.participantAvatar}>
{participant.username.charAt(0).toUpperCase()}
</div>
<div style={styles.participantInfo}>
<span style={styles.participantName}>{participant.username}</span>
{participant.roleType === "ADMIN" && (
<span style={styles.adminBadge}>Admin</span>
)}
</div>
</div>
))}
</div>
) : participants && participants.length === 0 ? (
<div style={styles.loadingText}>No participants found</div>
) : null}
</div>
</Collapse>
</>
);
};

const styles = {
participantsButton: {
backgroundColor: "#6b7280",
color: "white",
border: "none",
padding: "0.75rem 1rem",
borderRadius: "0.5rem",
fontSize: "0.85rem",
fontWeight: "600",
cursor: "pointer",
transition: "background-color 0.2s",
flex: "1",
whiteSpace: "nowrap",
},
participantsSection: {
marginTop: "1rem",
padding: "1rem",
backgroundColor: "#f9fafb",
borderRadius: "0.5rem",
border: "1px solid #e5e7eb",
},
participantsTitle: {
margin: "0 0 0.75rem 0",
color: "#374151",
fontSize: "1rem",
fontWeight: "600",
},
participantsList: {
display: "flex",
flexDirection: "column",
gap: "0.5rem",
},
participantItem: {
display: "flex",
alignItems: "center",
gap: "0.75rem",
padding: "0.5rem",
backgroundColor: "white",
borderRadius: "0.375rem",
border: "1px solid #e5e7eb",
},
participantAvatar: {
width: "32px",
height: "32px",
backgroundColor: "#3b82f6",
color: "white",
borderRadius: "50%",
display: "flex",
alignItems: "center",
justifyContent: "center",
fontWeight: "bold",
fontSize: "0.875rem",
},
participantInfo: {
display: "flex",
alignItems: "center",
gap: "0.5rem",
flex: "1",
},
participantName: {
color: "#1f2937",
fontSize: "0.9rem",
fontWeight: "500",
},
adminBadge: {
backgroundColor: "#fbbf24",
color: "#92400e",
padding: "0.125rem 0.5rem",
borderRadius: "0.25rem",
fontSize: "0.75rem",
fontWeight: "600",
},
loadingText: {
color: "#6b7280",
fontSize: "0.875rem",
fontStyle: "italic",
textAlign: "center",
padding: "1rem",
},
};

export default ParticipantsList;
43 changes: 32 additions & 11 deletions code/frontend/src/pages/Home.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { styled } from "@mui/material/styles";
import LogoutIcon from "@mui/icons-material/Logout";
import { activityService } from "../services/activityService";
import AvatarUpload from "../components/Avator";
import ParticipantsList from "../components/ParticipantsList";

const StyledTab = styled(Tab)({
textTransform: "none",
Expand Down Expand Up @@ -388,14 +389,24 @@ export default function Home() {
<span>{activity.location}</span>
</div>
</div>
<button
style={styles.joinButton}
onClick={() =>
isAdmin ? handleDeleteActivity(activity.activityId) : handleLeaveActivity(activity.activityId)
}
>
{isAdmin ? "Delete Activity" : "Leave Activity"}
</button>
<div style={styles.activityActions}>
<ParticipantsList
activityId={activity.activityId}
onError={(message) => setNotification({
open: true,
message,
severity: "error",
})}
/>
<button
style={styles.joinButton}
onClick={() =>
isAdmin ? handleDeleteActivity(activity.activityId) : handleLeaveActivity(activity.activityId)
}
>
{isAdmin ? "Delete Activity" : "Leave Activity"}
</button>
</div>
</div>
);
})}
Expand Down Expand Up @@ -643,18 +654,28 @@ const styles = {
metaIcon: {
fontSize: "1rem",
},
activityActions: {
display: "flex",
flexDirection: "column",
gap: "0.5rem",
marginTop: "auto",
alignItems: "stretch",
},
joinButton: {
backgroundColor: "#3b82f6",
color: "white",
border: "none",
padding: "0.75rem 1.5rem",
padding: "0.5rem 0.75rem",
borderRadius: "0.5rem",
fontSize: "0.9rem",
fontSize: "0.95rem",
fontWeight: "600",
cursor: "pointer",
transition: "background-color 0.2s",
width: "100%",
minWidth: "120px",
maxWidth: "200px",
alignSelf: "center",
marginTop: "auto",
margin: "0 auto",
boxSizing: "border-box",
},
};
6 changes: 6 additions & 0 deletions code/frontend/src/services/activityService.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,4 +52,10 @@ export const activityService = {
const response = await api.post("/activity", activityData);
return response.data;
},
getActivityParticipants: async (activityId, page = 0, size = 20) => {
const response = await api.get(`/activities/${activityId}/participants`, {
params: { page, size }
});
return response.data.data;
},
};
Loading