From 228b7a2b17e13c45a847f83e4a2fe721213bf2f3 Mon Sep 17 00:00:00 2001 From: Subrojit-Roy <57319006+Subrojit-Roy@users.noreply.github.com> Date: Mon, 10 Nov 2025 11:50:29 +0000 Subject: [PATCH 1/3] feat: add new extracurricular activities and validation for sign-up --- src/app.py | 44 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 43 insertions(+), 1 deletion(-) diff --git a/src/app.py b/src/app.py index 4ebb1d9..efe1674 100644 --- a/src/app.py +++ b/src/app.py @@ -38,6 +38,45 @@ "schedule": "Mondays, Wednesdays, Fridays, 2:00 PM - 3:00 PM", "max_participants": 30, "participants": ["john@mergington.edu", "olivia@mergington.edu"] + }, + # Sports-related activities + "Soccer Team": { + "description": "Join the school soccer team for practices and matches", + "schedule": "Mondays, Wednesdays, 4:30 PM - 6:00 PM", + "max_participants": 22, + "participants": ["liam@mergington.edu", "ava@mergington.edu"] + }, + "Basketball Club": { + "description": "Pickup games and skill development for all levels", + "schedule": "Tuesdays, Thursdays, 5:00 PM - 6:30 PM", + "max_participants": 18, + "participants": ["noah@mergington.edu", "mia@mergington.edu"] + }, + # Artistic activities + "Art Club": { + "description": "Explore drawing, painting, and mixed media projects", + "schedule": "Wednesdays, 3:30 PM - 5:00 PM", + "max_participants": 16, + "participants": ["isabella@mergington.edu", "lucas@mergington.edu"] + }, + "Music Ensemble": { + "description": "Practice and perform ensemble pieces across genres", + "schedule": "Fridays, 4:00 PM - 6:00 PM", + "max_participants": 25, + "participants": ["amelia@mergington.edu", "ethan@mergington.edu"] + }, + # Intellectual activities + "Debate Team": { + "description": "Develop public speaking and argumentation skills; compete in tournaments", + "schedule": "Thursdays, 3:30 PM - 5:00 PM", + "max_participants": 14, + "participants": ["harper@mergington.edu", "jack@mergington.edu"] + }, + "Robotics Club": { + "description": "Design, build, and program robots for competitions and projects", + "schedule": "Tuesdays, 4:00 PM - 6:00 PM", + "max_participants": 12, + "participants": ["charlotte@mergington.edu", "logan@mergington.edu"] } } @@ -61,7 +100,10 @@ def signup_for_activity(activity_name: str, email: str): # Get the specific activity activity = activities[activity_name] - + +# Validate student is not already signed up + if email in activity["participants"]: + raise HTTPException(status_code=400, detail="Student already signed up for this activity") # Add student activity["participants"].append(email) return {"message": f"Signed up {email} for {activity_name}"} From 0ee5192b78392f34d984b2dd0017426475a08824 Mon Sep 17 00:00:00 2001 From: Subrojit-Roy <57319006+Subrojit-Roy@users.noreply.github.com> Date: Mon, 10 Nov 2025 12:18:17 +0000 Subject: [PATCH 2/3] demo --- src/static/app.js | 73 ++++++++++++++++++++++++++++++++++++++--- src/static/index.html | 41 +++++++++++++++++++++++ src/static/styles.css | 75 +++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 185 insertions(+), 4 deletions(-) diff --git a/src/static/app.js b/src/static/app.js index dcc1e38..73616e0 100644 --- a/src/static/app.js +++ b/src/static/app.js @@ -4,6 +4,20 @@ document.addEventListener("DOMContentLoaded", () => { const signupForm = document.getElementById("signup-form"); const messageDiv = document.getElementById("message"); + // Function to create a display name and initials from an email or name + function nameFromIdentifier(id) { + if (!id) return { display: "Unknown", initials: "?" }; + // if it's an email, use part before @ + const raw = id.includes("@") ? id.split("@")[0] : id; + // replace dots/underscores with spaces and split to words + const parts = raw.replace(/[._\-]+/g, " ").split(" ").filter(Boolean); + const display = parts.map(p => p.charAt(0).toUpperCase() + p.slice(1)).join(" "); + const initials = parts.length === 1 + ? parts[0].substring(0, 2).toUpperCase() + : (parts[0][0] + parts[parts.length - 1][0]).toUpperCase(); + return { display: display || raw, initials }; + } + // Function to fetch activities from API async function fetchActivities() { try { @@ -13,20 +27,69 @@ document.addEventListener("DOMContentLoaded", () => { // Clear loading message activitiesList.innerHTML = ""; + // Reset select (keep placeholder if present) + const placeholderOption = activitySelect.querySelector('option[value=""]'); + activitySelect.innerHTML = ""; + if (placeholderOption) { + activitySelect.appendChild(placeholderOption); + } else { + // ensure a default placeholder exists + const opt = document.createElement("option"); + opt.value = ""; + opt.textContent = "-- Select an activity --"; + activitySelect.appendChild(opt); + } + // Populate activities list Object.entries(activities).forEach(([name, details]) => { const activityCard = document.createElement("div"); activityCard.className = "activity-card"; - const spotsLeft = details.max_participants - details.participants.length; + const spotsLeft = details.max_participants - (details.participants?.length || 0); - activityCard.innerHTML = ` + // Basic info + const infoHtml = `

${name}

-

${details.description}

-

Schedule: ${details.schedule}

+

${details.description || ""}

+

Schedule: ${details.schedule || "TBA"}

Availability: ${spotsLeft} spots left

`; + activityCard.innerHTML = infoHtml; + + // Participants section + const participantsDiv = document.createElement("div"); + participantsDiv.className = "participants"; + participantsDiv.setAttribute("aria-label", `Participants for ${name}`); + + const title = document.createElement("h5"); + title.textContent = "Participants"; + participantsDiv.appendChild(title); + + const participants = details.participants || []; + + if (participants.length === 0) { + const none = document.createElement("p"); + none.className = "info"; + none.textContent = "No participants yet"; + participantsDiv.appendChild(none); + } else { + const list = document.createElement("ul"); + participants.forEach((p) => { + const { display, initials } = nameFromIdentifier(p); + const li = document.createElement("li"); + + const span = document.createElement("span"); + span.className = "participant-initials"; + span.textContent = initials; + + li.appendChild(span); + li.appendChild(document.createTextNode(" " + display)); + list.appendChild(li); + }); + participantsDiv.appendChild(list); + } + activityCard.appendChild(participantsDiv); activitiesList.appendChild(activityCard); // Add option to select dropdown @@ -62,6 +125,8 @@ document.addEventListener("DOMContentLoaded", () => { messageDiv.textContent = result.message; messageDiv.className = "success"; signupForm.reset(); + // Refresh activities to show updated participants & availability + await fetchActivities(); } else { messageDiv.textContent = result.detail || "An error occurred"; messageDiv.className = "error"; diff --git a/src/static/index.html b/src/static/index.html index 3074f6e..3ae8250 100644 --- a/src/static/index.html +++ b/src/static/index.html @@ -39,6 +39,47 @@

Sign Up for an Activity

+ +
+

Example Activities

+
+

Morning Hike

+

Saturday, 8:00 AM · Easy

+ +
+
Participants
+
    +
  • + AL + Alice +
  • +
  • + Bob's avatar + Bob +
  • +
  • + CM + Casey +
  • +
+
+
+ +
+

Photography Walk

+

Sunday, 4:00 PM · Photo tips included

+ +
+
Participants
+
    +
  • EM Emma
  • +
  • RK Raj
  • +
  • SO Simone
  • +
  • +3 More
  • +
+
+
+