feat: Simplify team registration with referral codes#10
Conversation
- Created /create-team page for team leads with 6-char referral code generation - Added team registration section to /register with lead/member selection - Implemented referral code validation and auto-join functionality - Removed tshirt size and LinkedIn profile requirements - All registration paths now redirect to /dashboard - Team size limits enforced (max 4 members) - Bypassed confirmation page for streamlined flow
There was a problem hiding this comment.
Pull request overview
This PR simplifies the team registration flow by introducing a referral code system, replacing the previous complex autocomplete search mechanism. Team leads can now create teams and receive unique codes, while team members can join using these codes during registration.
Key Changes:
- New team creation page with referral code generation and sharing functionality
- Enhanced registration page with team lead/member selection and referral code input
- Removed profile completion requirements (t-shirt size, LinkedIn) to streamline the flow
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 12 comments.
| File | Description |
|---|---|
src/app/create-team/page.tsx |
New page for team leads to create teams and generate referral codes with dark mode support |
src/app/register/page.tsx |
Added team registration section with radio buttons for lead/member selection and referral code validation |
src/utils/getUserProgress.ts |
Removed profile completion checks (t-shirt size, LinkedIn) to simplify registration requirements |
src/app/confirmation/page.tsx |
Fixed React key prop warning in autocomplete rendering and updated navigation method |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
src/app/create-team/page.tsx
Outdated
| }, [user, router]); | ||
|
|
||
| const generateReferralCode = () => { | ||
| return Math.random().toString(36).substring(2, 8).toUpperCase(); |
There was a problem hiding this comment.
The referral code generation using Math.random().toString(36).substring(2, 8) may not always produce 6 characters. When converting to base-36, the result can be shorter than 6 characters after removing the "0." prefix, leading to codes shorter than expected. Consider generating a code with a guaranteed length of 6 characters.
| return Math.random().toString(36).substring(2, 8).toUpperCase(); | |
| let code = ""; | |
| // Ensure we always generate at least 6 characters before slicing | |
| while (code.length < 6) { | |
| code += Math.random().toString(36).substring(2); | |
| } | |
| return code.substring(0, 6).toUpperCase(); |
src/app/create-team/page.tsx
Outdated
| // Generate referral code | ||
| const code = generateReferralCode(); | ||
| setReferralCode(code); |
There was a problem hiding this comment.
There's no check to ensure referral code uniqueness when creating a team. Multiple teams could theoretically end up with the same referral code since Math.random() can produce collisions. Consider adding validation to check if the generated code already exists in the database, or use a more robust unique identifier generation approach.
src/app/create-team/page.tsx
Outdated
| // Get team number | ||
| const teamNumber = (await getCountFromServer(query(teamsRef))).data().count + 1; |
There was a problem hiding this comment.
Race condition: The team number calculation using getCountFromServer followed by count + 1 is not atomic. If two teams are created simultaneously, they could end up with the same team number. Consider using Firestore's serverTimestamp or a counter transaction to ensure uniqueness.
src/app/create-team/page.tsx
Outdated
|
|
||
| useEffect(() => { | ||
| if (!user) { | ||
| alert("Please login to create a team"); |
There was a problem hiding this comment.
The alert message says "Please login to create a team" but the user is not logged in yet. The terminology is inconsistent with typical authentication flows. Consider changing to "You must be logged in to create a team" or "Please sign in to create a team".
| alert("Please login to create a team"); | |
| alert("You must be logged in to create a team"); |
| if (teamData.participants && teamData.participants.length >= 4) { | ||
| alert("This team is full (maximum 4 members). Please use a different referral code."); | ||
| setLoadingState(false); | ||
| return; | ||
| } | ||
|
|
||
| // Check if user is already in this team | ||
| if (teamData.participants && teamData.participants.includes(user.uid)) { | ||
| alert("You are already in this team!"); | ||
| setLoadingState(false); | ||
| return; | ||
| } | ||
|
|
||
| // Add user to team | ||
| await updateDoc(doc(db, "teams", teamDoc.id), { | ||
| participants: arrayUnion(user.uid), | ||
| }); |
There was a problem hiding this comment.
Race condition: There's a time-of-check to time-of-use (TOCTOU) issue between checking team size at line 131 and adding the user at line 145. Multiple users could join simultaneously and exceed the 4-member limit. Consider using a Firestore transaction to atomically check and update the participants array.
src/app/register/page.tsx
Outdated
| if (isTeamLead) { | ||
| window.location.href = "/create-team"; | ||
| } else if (teamInfo) { | ||
| // Successfully joined team, go to dashboard | ||
| window.location.href = "/dashboard"; | ||
| } else { | ||
| // No team yet, but registration complete - go to dashboard | ||
| window.location.href = "/dashboard"; |
There was a problem hiding this comment.
Using window.location.href for navigation instead of Next.js router.push() causes a full page reload, which is less efficient and loses client-side state. Consider using router.push() consistently for better performance and user experience.
src/app/create-team/page.tsx
Outdated
| // Update user's registration | ||
| await updateDoc(doc(db, "registrations", user.uid), { | ||
| isTeamLead: 1, | ||
| isTeamMember: 1, | ||
| teamName: teamName, | ||
| }); |
There was a problem hiding this comment.
Missing error handling for updateDoc. If the user document doesn't exist in the registrations collection, this will fail silently or throw an error. Consider adding try-catch around this specific operation or verifying the document exists before updating.
| await updateDoc(doc(db, "teams", teamDoc.id), { | ||
| participants: arrayUnion(user.uid), | ||
| }); | ||
|
|
||
| teamInfo = { | ||
| teamName: teamData.teamName, | ||
| isTeamMember: 1, | ||
| }; | ||
| } catch (error) { | ||
| console.error("Error validating referral code:", error); | ||
| alert("Failed to join team. Please try again."); | ||
| setLoadingState(false); | ||
| return; | ||
| } | ||
| } | ||
|
|
||
| // Save registration data | ||
| await addData("registrations", user.uid, { | ||
| ...formState, | ||
| ["displayPicture"]: user?.photoURL ?? "", | ||
| ["isTeamMember"]: -1, | ||
| ["isTeamLead"]: 0, | ||
| ["isTeamMember"]: teamInfo ? 1 : 0, | ||
| ["isTeamLead"]: isTeamLead ? 1 : 0, | ||
| ["coc"]: 1, | ||
| ["terms"]: 1, | ||
| ["payment_status"]: "captured", // Skip payment for now | ||
| ["payment_status"]: "captured", | ||
| ["teamName"]: teamInfo?.teamName || "", | ||
| }); |
There was a problem hiding this comment.
The user is added to the team's participants array but if the subsequent addData call at line 162 fails, the user will be in the team but won't have a registration document, leading to data inconsistency. Consider wrapping both operations in a transaction or implementing rollback logic.
| <div className="flex items-center space-x-4 mb-4"> | ||
| <label className="flex items-center space-x-2 cursor-pointer"> | ||
| <input | ||
| type="radio" | ||
| name="isLead" | ||
| checked={isTeamLead === true} | ||
| onChange={() => { | ||
| setIsTeamLead(true); | ||
| setReferralCode(""); | ||
| }} | ||
| required | ||
| className="w-4 h-4" | ||
| /> | ||
| <span className="text-gray-900 dark:text-gray-300">Yes, I'll create a team</span> | ||
| </label> | ||
| <label className="flex items-center space-x-2 cursor-pointer"> | ||
| <input | ||
| type="radio" | ||
| name="isLead" | ||
| checked={isTeamLead === false} | ||
| onChange={() => setIsTeamLead(false)} | ||
| required | ||
| className="w-4 h-4" | ||
| /> | ||
| <span className="text-gray-900 dark:text-gray-300">No, I'll join a team</span> | ||
| </label> | ||
| </div> |
There was a problem hiding this comment.
The isTeamLead radio button group has the required attribute, but there's no validation preventing form submission if neither option is selected. The HTML5 required attribute on radio buttons requires at least one in the group to be checked, but the initial state is undefined which could allow submission. Consider adding explicit validation in handleSubmit.
src/app/register/page.tsx
Outdated
| ["terms"]: 1, | ||
| ["payment_status"]: "captured", // Skip payment for now | ||
| ["payment_status"]: "captured", | ||
| ["teamName"]: teamInfo?.teamName || "", |
There was a problem hiding this comment.
The teamInfo.teamName could be undefined since teamInfo is typed as null or an object with optional teamName. When isTeamMember is 0 (no team), passing an empty string is correct, but when teamInfo exists, accessing teamInfo.teamName without null-checking could cause issues if teamName is missing from the Firestore document. Consider adding proper null-checking: teamInfo?.teamName ?? "".
| ["teamName"]: teamInfo?.teamName || "", | |
| ["teamName"]: teamInfo?.teamName ?? "", |
|
@copilot Can we have the expereince that we are now giving in the /create-team page within the registration page, and instead of only coying the referal code, can we generate the link like /register?referral=code so that people can click that link and automatically the referral link is autofilled and disabled for editing making him a team member? |
|
@ManasMalla I've opened a new pull request, #11, to work on those changes. Once the pull request is ready, I'll request review from you. |
Co-authored-by: ManasMalla <38750492+ManasMalla@users.noreply.github.com>
Co-authored-by: ManasMalla <38750492+ManasMalla@users.noreply.github.com>
…R safety Co-authored-by: ManasMalla <38750492+ManasMalla@users.noreply.github.com>
Integrate team creation into registration flow with URL-based referral links
🎯 Summary
This PR completely overhauls the team registration system to use simple referral codes instead of the complex autocomplete search flow.
✨ What's New
1. New
/create-teamPageABC123)2. Enhanced
/registerPage3. Simplified Flow
/dashboardwith immediate access to QR code🔄 User Flows
Team Lead:
Team Member (with code):
Team Member (no code):
📝 Changes Made
src/app/create-team/page.tsxsrc/app/register/page.tsxwith team selection UIsrc/utils/getUserProgress.tsto remove profile checks🎨 Features
🧪 Testing Checklist
📦 Related
This simplifies the registration process based on user feedback that the previous autocomplete flow was "too complex and too many steps".