-
Notifications
You must be signed in to change notification settings - Fork 2
Feature/attendee creation with team assignment #28
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature/attendee creation with team assignment #28
Conversation
…ogic and leaderboard visibility toggle
- Added lunch and dinner action types to scanner - Added Restaurant and DinnerDining Material-UI icons - Implemented duplicate prevention for lunch and dinner - Lunch uses amber color scheme, dinner uses blue - Required check-in before lunch/dinner access - Updated action selector grid to 3 columns (2 on mobile) - All actions now tracked in checkins collection - Console logging for all scan events
- Add UID input field to use Firebase Auth UID instead of random MANUAL_ prefix - Add team assignment dropdown to assign participants to teams during creation - Add team leader checkbox to set participant as team leader - Implement validation for UID uniqueness and existing team leaders - Update team memberIds and leaderId when assigning participants - Fetch and display all teams in dropdown for selection - Add helper text for admins to guide UID input from Firebase Console
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pull request overview
This pull request introduces a comprehensive admin participant creation system with team assignment capabilities, along with several event management enhancements including leaderboard visibility controls, meal tracking via QR scanner, and multiple new milestone pages for the hackathon.
Key Changes
- Admin participant creation: New UI and backend logic to manually create participant registrations with optional team assignment
- Leaderboard visibility control: Admin settings page to toggle leaderboard visibility between milestones
- Meal tracking: QR scanner now supports lunch and dinner check-ins with duplicate prevention
- Milestone pages: Added pages for milestones 5-10 (both manual grading and final submission types)
Reviewed changes
Copilot reviewed 22 out of 23 changed files in this pull request and generated 22 comments.
Show a summary per file
| File | Description |
|---|---|
update-classrooms.ts |
New script to seed classroom data and update teams with classroom assignments |
toggle-leaderboard.ts |
CLI utility to control leaderboard visibility configuration |
test-team-seed.csv |
Test team seed data with sample participants |
staff-admin-seed.csv |
Staff and admin account seed data including test users |
staff-admin-correct.csv |
Corrected version of staff/admin seed data |
src/app/staff/scanner/page.tsx |
Enhanced QR scanner with lunch/dinner tracking and duplicate prevention |
src/app/printer/client/page.tsx |
Added printer selection UI with discovery attempt (non-functional) |
src/app/leaderboard/page.tsx |
Integrated visibility toggle using config document; fixed field name reference |
src/app/hackathon/milestone_ten/page.tsx |
Final submission milestone with file upload functionality |
src/app/hackathon/milestone_six/page.tsx |
Manual grading milestone page for core features completion |
src/app/hackathon/milestone_seven/page.tsx |
Manual grading milestone page for testing and bug fixes |
src/app/hackathon/milestone_nine/page.tsx |
Manual grading milestone page for final polish |
src/app/hackathon/milestone_five/page.tsx |
Manual grading milestone page for backend integration |
src/app/hackathon/milestone_eight/page.tsx |
Manual grading milestone page for documentation and deployment |
src/app/admin/teams/page.tsx |
Major addition of participant creation form with team assignment capabilities |
src/app/admin/settings/page.tsx |
New admin settings page for controlling leaderboard visibility |
src/app/admin/page.tsx |
Added settings link to admin dashboard |
src/app/admin/milestones/[id]/page.tsx |
Updated to support manual grading milestones and conditional time penalties |
serviceAccountKey.json |
CRITICAL: Firebase service account private keys exposed in repository |
seed-firestore.ts |
Updated seeding logic to seed staff/admin and test team data |
reseed-staff-admin.ts |
New script to reseed staff and admin accounts with correct UIDs |
ADMIN_ROUTES.md |
Comprehensive documentation of admin, staff, and participant routes |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| async function main() { | ||
| console.log('🚀 Starting classroom setup and team updates...\n'); | ||
| console.log('=' .repeat(70)); |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a syntax error in the string repetition. The expression '=' .repeat(70) has incorrect spacing - the dot should be directly attached to the string without a space. This should be '='.repeat(70). The same issue appears on multiple lines (87, 98, 104).
| </h3> | ||
| <p className="text-sm text-gray-600 dark:text-gray-400"> | ||
| {uR.teamCode} • {uR.allottedClassroom || "No classroom"} | ||
| {uR.teamCode} • {uR.allotedClassrooms || "No classroom"} |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The field name "allotedClassrooms" has an inconsistent spelling. This should be "allottedClassrooms" (with double 't') to match correct English spelling and maintain consistency throughout the codebase.
| {uR.teamCode} • {uR.allotedClassrooms || "No classroom"} | |
| {uR.teamCode} • {uR.allottedClassrooms || "No classroom"} |
| if (!newParticipant.uid || !newParticipant.firstName || !newParticipant.lastName || !newParticipant.email) { | ||
| alert("Please fill in all required fields (Firebase Auth UID, First Name, Last Name, Email)"); | ||
| return; | ||
| } | ||
|
|
||
| setCreating(true); | ||
| try { | ||
| const uid = newParticipant.uid.trim(); | ||
|
|
||
| // Check if registration already exists | ||
| const existingDoc = await getDoc(doc(db, "registrations", uid)); | ||
| if (existingDoc.exists()) { | ||
| alert(`⚠️ Registration already exists for UID: ${uid}`); | ||
| setCreating(false); | ||
| return; | ||
| } |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The participant creation form lacks comprehensive input validation. While basic required field checks exist for UID, firstName, lastName, and email, there's no validation for:
- Email format validation
- UID format validation (Firebase UIDs have a specific format)
- Name length constraints
- Email uniqueness check
Consider adding client-side validation before submission and providing clear error messages to guide users when input is invalid. This will improve the user experience and prevent invalid data from being submitted to Firestore.
| if (!newParticipant.uid || !newParticipant.firstName || !newParticipant.lastName || !newParticipant.email) { | |
| alert("Please fill in all required fields (Firebase Auth UID, First Name, Last Name, Email)"); | |
| return; | |
| } | |
| setCreating(true); | |
| try { | |
| const uid = newParticipant.uid.trim(); | |
| // Check if registration already exists | |
| const existingDoc = await getDoc(doc(db, "registrations", uid)); | |
| if (existingDoc.exists()) { | |
| alert(`⚠️ Registration already exists for UID: ${uid}`); | |
| setCreating(false); | |
| return; | |
| } | |
| // Normalize and trim inputs | |
| const uid = (newParticipant.uid || "").trim(); | |
| const firstName = (newParticipant.firstName || "").trim(); | |
| const lastName = (newParticipant.lastName || "").trim(); | |
| const email = (newParticipant.email || "").trim().toLowerCase(); | |
| // Basic required field check | |
| if (!uid || !firstName || !lastName || !email) { | |
| alert("Please fill in all required fields (Firebase Auth UID, First Name, Last Name, Email)"); | |
| return; | |
| } | |
| // Email format validation | |
| const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/; | |
| if (!emailRegex.test(email)) { | |
| alert("Please enter a valid email address."); | |
| return; | |
| } | |
| // Firebase UID basic format validation: | |
| // - Reasonable length (6–128 chars) | |
| // - URL-safe characters (letters, digits, underscore, hyphen) | |
| const uidRegex = /^[A-Za-z0-9_-]{6,128}$/; | |
| if (!uidRegex.test(uid)) { | |
| alert("Please enter a valid Firebase UID (6–128 characters, using letters, numbers, underscores, or hyphens)."); | |
| return; | |
| } | |
| // Name length constraints | |
| if (firstName.length < 1 || firstName.length > 50) { | |
| alert("First Name must be between 1 and 50 characters."); | |
| return; | |
| } | |
| if (lastName.length < 1 || lastName.length > 50) { | |
| alert("Last Name must be between 1 and 50 characters."); | |
| return; | |
| } | |
| setCreating(true); | |
| try { | |
| // Check if registration already exists for this UID | |
| const existingDoc = await getDoc(doc(db, "registrations", uid)); | |
| if (existingDoc.exists()) { | |
| alert(`⚠️ Registration already exists for UID: ${uid}`); | |
| setCreating(false); | |
| return; | |
| } | |
| // Check if registration already exists for this email | |
| const emailQuery = query( | |
| collection(db, "registrations"), | |
| where("email", "==", email) | |
| ); | |
| const emailSnapshot = await getDocs(emailQuery); | |
| if (!emailSnapshot.empty) { | |
| alert(`⚠️ A registration already exists for the email address: ${email}`); | |
| setCreating(false); | |
| return; | |
| } |
| const endTime = Date.now(); | ||
| const duration = ((endTime - startTime) / 1000).toFixed(2); | ||
|
|
||
| console.log('=' .repeat(70)); |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a syntax error in the string repetition. The expression '=' .repeat(70) has incorrect spacing - the dot should be directly attached to the string without a space. This should be '='.repeat(70).
| import { useEffect } from "react"; | ||
| import { useRouter } from "next/navigation"; | ||
| import { Info } from "@mui/icons-material"; | ||
|
|
||
| export default function Page() { | ||
| const router = useRouter(); | ||
|
|
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The useRouter hook is imported and initialized but never used in this component. The router variable is declared but has no references. These unused imports and variables should be removed to keep the code clean.
| import { useEffect } from "react"; | |
| import { useRouter } from "next/navigation"; | |
| import { Info } from "@mui/icons-material"; | |
| export default function Page() { | |
| const router = useRouter(); | |
| import { Info } from "@mui/icons-material"; | |
| export default function Page() { |
| console.log(`Test Team: ${testTeamStats.successCount} seeded, ${testTeamStats.errorCount} errors`); | ||
| console.log(`Duration: ${duration}s`); | ||
| console.log('=' .repeat(70)); | ||
|
|
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a syntax error in the string repetition. The expression '=' .repeat(70) has incorrect spacing - the dot should be directly attached to the string without a space. This should be '='.repeat(70).
| @@ -0,0 +1,119 @@ | |||
| import { initializeApp, cert, getApps } from 'firebase-admin/app'; | |||
| import { getFirestore, Timestamp } from 'firebase-admin/firestore'; | |||
| import * as fs from 'fs'; | |||
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The fs module is imported but never used in this file. This unused import should be removed to keep the code clean and avoid confusion.
| import * as fs from 'fs'; |
| const teamDetailsId = snapshot.docs[0].id; | ||
| setTeamDocId(teamDetailsId); | ||
| const dt = snapshot.docs[0].data()["milestone_ten_time"]; | ||
| if (dt != undefined && dt != null) { |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This guard always evaluates to true.
| if (dt != undefined && dt != null) { | |
| if (dt !== undefined && dt !== null) { |
| @@ -0,0 +1,54 @@ | |||
| "use client"; | |||
|
|
|||
| import { useEffect } from "react"; | |||
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import useEffect.
| import { useEffect } from "react"; |
| "use client"; | ||
| import { db } from "@/lib/firebase"; | ||
| import { collection, getDocs, onSnapshot } from "firebase/firestore"; | ||
| import { collection, getDocs, onSnapshot, doc } from "firebase/firestore"; |
Copilot
AI
Jan 3, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Unused import getDocs.
| import { collection, getDocs, onSnapshot, doc } from "firebase/firestore"; | |
| import { collection, onSnapshot, doc } from "firebase/firestore"; |
No description provided.