# Phase 2.7 Testing Instructions **Phase:** 2.7 - Simple Template Expansion (16, 32 teams) **Status:** Completed **Date:** 2025-01-04 ## Overview Phase 2.7 adds support for larger bracket templates (16-team and 32-team) with non-scoring rounds. Only participants who reach the Elite Eight (top 8) score fantasy points. Early eliminations receive 0 points per Q20. ## Phase 2.7 Testing Checklist ### 1. **Bracket Generation UI** (`/admin/sports-seasons/:id/events/:eventId/bracket`) **Test the template selector:** - [ ] Navigate to a playoff event that has no bracket yet - [ ] See a "Bracket Template" dropdown with all options: - Simple 4-Team Bracket - 4 teams - Simple 8-Team Bracket - 8 teams - Simple 16-Team Bracket - 16 teams - Simple 32-Team Bracket - 32 teams - NCAA March Madness (68 teams) - 68 teams - NFL Playoffs (14 teams) - 14 teams - NBA Playoffs (16 teams) - 16 teams - [ ] Change template selection - participant count should update dynamically - [ ] See round progression text (e.g., "Rounds: Round of 16 → Quarterfinals → Semifinals → Finals") ### 2. **16-Team Bracket Creation** **Generate a 16-team bracket:** - [ ] Select "Simple 16-Team Bracket" - [ ] Fill in 16 participants (seeds 1-16) - [ ] Click "Generate Bracket" - [ ] Verify success message **Check bracket structure:** - [ ] See "Round of 16" section with 8 matches - [ ] See "Quarterfinals" section with 4 matches (TBD participants) - [ ] See "Semifinals" section with 2 matches (TBD participants) - [ ] See "Finals" section with 1 match (TBD participants) **Complete Round of 16:** - [ ] Set winners for all 8 matches - [ ] Winners should auto-advance to Quarterfinals - [ ] Click "Complete Round & Calculate Placements" - [ ] Select "Round of 16" from dropdown - [ ] Verify **8 losers get 0 fantasy points** (early elimination per Q20) **Complete Quarterfinals:** - [ ] Set winners for all 4 matches - [ ] Winners advance to Semifinals - [ ] Complete the round - [ ] Verify **4 losers share 5th-8th place** (20 points each with default scoring) **Complete Semifinals:** - [ ] Set winners for 2 matches - [ ] Winners advance to Finals - [ ] Complete the round - [ ] Verify **2 losers share 3rd-4th place** (45 points each) **Complete Finals:** - [ ] Set winner - [ ] Complete the round - [ ] Verify **winner gets 1st** (100 points), **loser gets 2nd** (70 points) - [ ] Navigate back to event detail page (click "Back to Event" or navigate to `/admin/sports-seasons/{id}/events/{eventId}`) - [ ] See new "Fantasy Points Awarded" card showing all participant results with positions and points - [ ] Verify early eliminations show "Early Elimination" with 0 points - [ ] Verify top 8 finishers show correct positions (1st-8th) with correct point totals ### 3. **Batch Winner Selection (NEW)** **Test the batch winner selection UI:** - [ ] Navigate to any generated bracket (16 or 32-team) - [ ] See winner dropdowns for each incomplete match - [ ] Select a winner for one match - verify no submit button appears yet - [ ] Select winners for 2-3 matches in a round - [ ] Verify "Save X Winner(s) for [Round]" button appears - [ ] Click the batch save button - [ ] Verify success message shows correct count - [ ] Verify all selected winners are set - [ ] Verify winners auto-advance to next round - [ ] Verify winner dropdowns are cleared after submission **Test error handling:** - [ ] Try to submit with no winners selected - should not see button - [ ] Select winners for matches in different rounds - each round has separate button - [ ] Test partial success scenario (if possible to trigger) **Test workflow efficiency:** - [ ] Set all 8 winners for Round of 16 at once - [ ] Verify all 8 winners advance to Quarterfinals correctly - [ ] Set all 4 Quarterfinals winners at once - [ ] Verify advancement and placement calculation ### 4. **32-Team Bracket Creation** **Generate a 32-team bracket:** - [ ] Select "Simple 32-Team Bracket" - [ ] Fill in 32 participants - [ ] Generate bracket - [ ] See 5 rounds: Round of 32 → Round of 16 → Quarterfinals → Semifinals → Finals **Test non-scoring rounds:** - [ ] Complete Round of 32 (16 matches) - [ ] Verify **16 losers get 0 points** (non-scoring round) - [ ] Complete Round of 16 (8 matches) - [ ] Verify **8 losers get 0 points** (non-scoring round) - [ ] Complete Quarterfinals through Finals - [ ] Verify scoring matches Q20: Only top 8 finishers get points **Final tally check:** - [ ] Total participants: 32 - [ ] Participants with 0 points: 24 (Round of 32 + Round of 16 losers) - [ ] Participants scoring: 8 (QF losers through Champion) ### 4. **Template-Based Advancement** **Verify auto-advancement works:** - [ ] In any multi-round bracket, set a winner in Round 1 - [ ] Check that winner appears in correct slot in Round 2 - [ ] Match advancement pattern: - Match 1 winner → Next round Match 1, Participant 1 - Match 2 winner → Next round Match 1, Participant 2 - Match 3 winner → Next round Match 2, Participant 1 - Match 4 winner → Next round Match 2, Participant 2 ### 5. **Scoring Calculator Integration** **Check participant results:** - [ ] After completing a round, check database `participantResults` table - [ ] Non-scoring round losers should have `finalPosition = 0` - [ ] Scoring round participants should have correct placements (1-8) **Verify team standings update:** - [ ] If you have teams that drafted these participants - [ ] Check that standings recalculate after each round completion - [ ] Early elimination participants (0 points) should not add to team score ### 6. **Error Handling** **Test validation:** - [ ] Try to generate bracket without selecting all participants → error - [ ] Try to select same participant twice → "Each participant can only be selected once" - [ ] Try to set winner when participants aren't assigned → should be disabled - [ ] Try to complete round with incomplete matches → error ### 7. **Backward Compatibility** **Test existing 4 and 8 team brackets:** - [ ] Generate a "Simple 4-Team Bracket" → should work as before - [ ] Generate a "Simple 8-Team Bracket" → should work as before - [ ] All rounds should be scoring rounds for these templates - [ ] No 0-point eliminations (all 4 or 8 participants score) ## Viewing Fantasy Points (NEW) After completing rounds and calculating placements, you can view the fantasy points awarded: **Navigation:** 1. From the bracket management page, click "Back to Event" 2. Or navigate to `/admin/sports-seasons/{sportsSeasonId}/events/{eventId}` **Fantasy Points Awarded Card:** - Shows all participants with calculated fantasy points - Sorted by final position (1st place at top) - Displays position with medals (🥇🥈🥉) for top 3 - Shows "Early Elimination" for participants with 0 points (early knockout) - Points displayed in green for scoring participants, gray for 0 points **Example Display:** ``` Final Position | Participant | Fantasy Points 1st 🥇 | Arizona | 100 pts 2nd 🥈 | UConn | 70 pts 3rd 🥉 | Houston | 45 pts 4th | Duke | 45 pts 5th | Kansas | 20 pts 6th | Purdue | 20 pts 7th | Tennessee | 20 pts 8th | North Carolina | 20 pts Early Elim. | UCLA | 0 pts Early Elim. | Alabama | 0 pts ... ``` **Note:** Team standings (total points across all events) are not yet displayed in the UI but are being calculated and stored in the database. ## Key Expected Behaviors ### Non-Scoring Rounds (Q20) - Round of 16 in 16-team bracket: 8 teams get 0 points - Round of 32 + Round of 16 in 32-team bracket: 24 teams get 0 points - Early eliminations don't count toward team standings ### Scoring Rounds - Only Quarterfinals and beyond score fantasy points - Always exactly 8 participants score (1st through 8th) - Point distribution per default scoring: - 1st: 100, 2nd: 70 - 3rd/4th (avg): 45 each - 5th-8th (avg): 20 each - Total: 340 points ### Template Storage - Check `scoringEvents` table has `bracketTemplateId` stored - Check `scoringEvents` table has `scoringStartsAtRound` stored - Check `playoffMatches` have `isScoring` flag set correctly ## Things NOT to Test Yet (Phase 2.8+) - ❌ NCAA 68-team bracket generation (won't assign participants correctly) - ❌ NFL 14-team with bye weeks - ❌ First Four play-in games - ❌ TBD slot handling ## Database Verification Queries ```sql -- Check event has template ID stored SELECT id, name, bracketTemplateId, scoringStartsAtRound FROM scoring_events WHERE id = 'your-event-id'; -- Check matches have isScoring flag SELECT round, matchNumber, isScoring, participant1Id, participant2Id, winnerId FROM playoff_matches WHERE scoringEventId = 'your-event-id' ORDER BY round, matchNumber; -- Check participant results after round completion SELECT p.name, pr.finalPosition, pr.qualifyingPoints FROM participant_results pr JOIN participants p ON p.id = pr.participantId WHERE pr.sportsSeasonId = 'your-sports-season-id' ORDER BY pr.finalPosition; -- Check team standings SELECT t.name, ts.totalPoints, ts.currentRank, ts.firstPlaceCount, ts.secondPlaceCount, ts.thirdPlaceCount, ts.fourthPlaceCount, ts.fifthPlaceCount, ts.sixthPlaceCount, ts.seventhPlaceCount, ts.eighthPlaceCount FROM team_standings ts JOIN teams t ON t.id = ts.teamId WHERE ts.seasonId = 'your-season-id' ORDER BY ts.currentRank; ``` ## Bug Fixes ### Bug #1: Round of 16 and other non-standard rounds not displaying (Fixed) **Issue:** When creating a 16-team bracket, only Quarterfinals, Semifinals, and Finals were displayed. Round of 16 was missing. **Root Cause:** - Client component had hardcoded rounds array: `["Quarterfinals", "Semifinals", "Finals"]` - Server validation also had hardcoded round checking **Fix:** - Changed to dynamically get rounds from actual matches: `Array.from(new Set(matches.map(m => m.round)))` - Updated server validation to check if round exists in matches instead of hardcoding allowed values - Files changed: - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx` (line 54) - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts` (lines 178-196) **Status:** ✅ Fixed and tested. ### Bug #2: Sequential seeding instead of tournament-style seeding (Fixed) **Issue:** When creating a 16-team bracket with participants seeded 1-16, the bracket showed: - Match 1: Seed 1 vs Seed 2 (Arizona vs Atlanta) - Match 2: Seed 3 vs Seed 4 - etc. Instead of proper tournament seeding: - Match 1: Seed 1 vs Seed 16 - Match 2: Seed 8 vs Seed 9 - etc. **Root Cause:** - Bracket generation assigned participants sequentially instead of using standard tournament seeding algorithm **Fix:** - Added `generateStandardSeeding()` function that creates balanced bracket matchups - Algorithm ensures: - All matchups sum to n+1 (e.g., 1v16, 2v15, 3v14 all sum to 17 for 16 teams) - Top seeds are separated into different bracket regions - Balanced pods for each quarterfinal section - Added `seedInfo` field to store display text (e.g., "1 vs 16") - Works for 4, 8, 16, and 32-team brackets - Non-standard sizes (68, 14) fall back to sequential until custom logic is implemented **Files changed:** - `app/models/playoff-match.ts` (lines 255-369) - New test file: `app/models/__tests__/bracket-seeding.test.ts` (14 tests) **Status:** ✅ Fixed and tested. All 221 tests passing. ### QOL Improvement: Duplicate participant prevention (Implemented) **Feature:** When selecting participants for bracket seeds, once a participant is selected for one seed, they are automatically removed from the dropdown options for all other seeds. **How it works:** - Each seed dropdown only shows participants that haven't been selected yet - If you select "Arizona" as Seed 1, "Arizona" disappears from all other seed dropdowns - Changing the bracket template resets all selections - Real-time updates as you make selections **Benefits:** - Prevents duplicate selections (the server already validated this, but now it's prevented in the UI) - Clearer UX - you can see which participants are still available - Faster bracket setup - no need to remember which teams you've already picked **Files changed:** - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx` (lines 40-68, 141, 167-190) - Added `selectedParticipants` state to track selections - Added `handleTemplateChange` to reset selections on template change - Added `handleParticipantChange` to update selections - Added `getAvailableParticipants` to filter out selected participants - Updated Select components to use controlled state and filtered options **Status:** ✅ Implemented and tested. All 221 tests passing. ### QOL Improvement #2: Batch winner selection (Implemented) **Feature:** Instead of having individual "Set" buttons for each match, users can now select winners for multiple matches in a round and submit them all with a single button click. **How it works:** - Each match in a round has a winner dropdown (controlled component) - Select winners for one or more matches - A "Save X Winner(s) for [Round]" button appears at the bottom of the round card - Click the button to submit all selected winners at once - Winners are automatically advanced to the next round - Selected winners are cleared after successful submission **Benefits:** - Faster workflow - set multiple winners at once instead of one at a time - Better UX - clear visual indication of pending selections - Fewer page reloads - batch submission reduces server round trips - Consistent advancement - all winners advance in a single operation **Implementation details:** - Client-side state tracking with `selectedWinners` state - Server-side batch processing with new "set-round-winners" intent - Automatic state cleanup after successful submission via useEffect - Error handling for partial success scenarios **Files changed:** - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx` (lines 1-2, 40-51, 294-334) - Added useEffect import - Added batch winner state management - Replaced Form-wrapped Select with controlled Select components - Added batch submit button per round - Added useEffect to clear selections after success - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.server.ts` (lines 175-276) - Added "set-round-winners" intent handler - Processes multiple winner assignments in batch - Handles partial success with detailed error messages **Status:** ✅ Implemented and tested. All 221 tests passing. ### QOL Improvement #3: Fantasy points display on event page (Implemented) **Feature:** After completing rounds and calculating placements, users can now view the fantasy points awarded to each participant on the event detail page. **How it works:** - Navigate to the event detail page (`/admin/sports-seasons/{id}/events/{eventId}`) - See "Fantasy Points Awarded" card with all participant results - Sorted by final position (1st place at top) - Shows position, participant name, and fantasy points - Early eliminations (0 points) clearly marked **Benefits:** - Instant verification of point calculations - Visual confirmation of bracket completion - Easy reference for standings verification - Clear distinction between scoring and non-scoring participants **Implementation details:** - Loader fetches `participant_results` from database - Includes participant relation for name display - Client sorts by `finalPosition` ascending - Displays medals for top 3 finishers - Color-codes points (green for scoring, gray for 0) **Files changed:** - `app/routes/admin.sports-seasons.$id.events.$eventId.server.ts` (lines 1, 16, 31-43) - Added import for `findParticipantResultsBySportsSeasonId` - Added `participantResults` to loader return data - `app/routes/admin.sports-seasons.$id.events.$eventId.tsx` (lines 38, 292-368) - Added `participantResults` to destructured loader data - Added "Fantasy Points Awarded" card with table - Displays position, participant, and points with proper formatting **Status:** ✅ Implemented and tested. All 221 tests passing. ### Bug #3: Round cards displaying in wrong order (Fixed) **Issue:** When setting a winner in Round of 16, the round cards would shuffle and display in incorrect order (e.g., "Semifinals, Quarterfinals, Round of 16, Finals" instead of chronological order). **Root Cause:** - Rounds were being displayed in the order they appeared from the database query - Database ordering was based on match results, not chronological tournament progression - As matches completed and were updated, the database result order could change **Fix:** - Added `getOrderedRounds()` function that uses the bracket template's round order - If event has a `bracketTemplateId`, uses the template's chronological round order - Filters to only show rounds that have matches - Falls back to database order if no template is available **How it works now:** 1. Get unique rounds from matches 2. If event has bracket template, get template 3. Use template's round order (first round → finals) 4. Filter to only rounds that exist in matches 5. Display cards in correct chronological order **Example:** - **16-team bracket:** Round of 16 → Quarterfinals → Semifinals → Finals - **32-team bracket:** Round of 32 → Round of 16 → Quarterfinals → Semifinals → Finals - Order remains stable even as matches are completed **Files changed:** - `app/routes/admin.sports-seasons.$id.events.$eventId.bracket.tsx` (lines 23, 79-98, 221, 330) - Imported `getBracketTemplate` - Added `getOrderedRounds()` function - Updated round mapping to include type annotations **Status:** ✅ Fixed and tested. All 221 tests passing. ## Known Issues None at this time. All 221 tests passing (207 original + 14 seeding tests). ## Notes - Phase 2.8 will add full NCAA 68-team bracket support with First Four play-in games - NFL 14-team and NBA 16-team templates exist but need bye week logic (Phase 2.9)