* Fix flex spot count in draft room Teams Drafted view
The flexSpots column in the database defaults to 0 and was never
populated, causing the Teams Drafted grid to always show "0 of 0 flex".
Calculate numFlexPicks dynamically in the loader as
max(0, draftRounds - seasonSports.length) so teams see the correct
flex count (e.g. 7 rounds - 5 sports = 2 flex).
https://claude.ai/code/session_01CSzF4aWuyppGBDqM4MJmhc
* Clean up draft room flex calculation area
- Fix bug: force pick dialog now uses its own isolated search/sport
filter state so it no longer mutates the main participants tab filters
- TeamsDraftedGrid: remove dead first pass in flexPicksByTeam memo,
trim season prop to numFlexPicks only, drop unused type field from
picks sport shape
- Loader: derive userAutodraftSettings from already-loaded
autodraftSettings list, eliminating a redundant DB query
- Extract shared transformedPicks, transformedParticipants, and
seasonSportsData into their own useMemos so both eligibility memos
reuse them instead of duplicating the work
- Memoize draftGrid, draftedParticipantIds, and uniqueSports which
were being recomputed on every render
https://claude.ai/code/session_01CSzF4aWuyppGBDqM4MJmhc
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Add push notifications implementation plan
Documents the approach for adding a browser Notification API toggle
to the draft page sidebar, including files to create/modify and
edge cases to handle.
https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69
* Add browser push notifications toggle to draft page
Adds a Notifications toggle in the draft sidebar (below Autodraft)
that uses the Browser Notification API to alert users when a pick is
made or when it's their turn, while the tab is not focused.
- New useDraftNotifications hook for state, permission, and localStorage
- New NotificationSettings component with Switch toggle
- Fires "It's your turn to pick!" when the next pick is the user's
- Fires "{Team} picked {Player}" for all other picks
- Gracefully hides toggle when Notification API is unavailable
https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69
* Fix all code review issues with push notifications
- Extract snake draft order calculation to shared getTeamForPick()
helper in lib/draft-order.ts, removing the duplicated logic
- Fix notification firing on user's own picks (guard with team id check)
- Fix notification firing after draft is complete (early return)
- Use a ref for sendNotification to prevent full socket handler
re-registration every time the toggle is changed
- Add permission drift listener via Permissions API so the UI reacts
if the user revokes notification permission in browser settings
- Add SSR guard for document.hidden in sendNotification
- Remove redundant isSupported prop from NotificationSettings;
derive unsupported state from permissionState === "unsupported"
- Move NotificationSettings out of QueueSection prop-drilling path;
render it via a new settingsSection slot on DraftSidebar instead
https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69
* Fix second round of notification code review issues
- Add My Turn Only / All Picks mode granularity so users can choose
to only be notified when it's their pick, or for every pick
- Remove double top-border by stripping leftover border-t/pt-4/mt-4
wrapper from NotificationSettings (now provided by DraftSidebar)
- Remove unused isSupported from hook return value
- Add n.onclick = () => window.focus() so clicking a notification
brings the draft tab back into focus
- Scope localStorage keys to userId to avoid shared-browser conflicts
(keys now: draftNotifications-{userId}-{seasonId})
- Add notificationsModeRef alongside sendNotificationRef so mode
changes don't trigger full socket handler re-registration
https://claude.ai/code/session_0149MvVUYDY6pFUAV1fL4K69
---------
Co-authored-by: Claude <noreply@anthropic.com>
The loader was grabbing seasonSports[0] blindly instead of filtering by
the current league, causing a 404 when the first linked fantasy season
belonged to a different league.
https://claude.ai/code/session_01Mq6BqFhiYFuJyDcc8ueVJc
Co-authored-by: Claude <noreply@anthropic.com>
When draftSlots is empty (pre-draft state), dividing by zero caused
Math.ceil to return Infinity. Guard the division to fall back to round 1.
https://claude.ai/code/session_017mLmGc5wTrESaDeRQHkSPy
Co-authored-by: Claude <noreply@anthropic.com>
* Add implementation plan for commissioner-without-team feature
https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3
* Allow commissioners to exist without owning a team
- Add opt-out checkbox on league creation ("I want to play in this league")
so the creator can be commissioner-only without claiming a team
- Add Commissioner Management card to league settings with add/remove
commissioner UI; guards against removing the last commissioner
- Add countCommissionersByLeagueId model helper for the last-commissioner guard
- Show "No team" indicator on the league homepage next to commissioners
who don't own a team in the current season
https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3
* Fix code review issues in commissioner management
- Add success/error feedback banners to commissioner add/remove actions
- Gate allUsers query to admin-only (prevents exposing all users to non-admin commissioners)
- Prevent commissioner self-removal in both action (server) and UI (client)
- Add "(you)" label next to current user in commissioner list
- Remove all unnecessary `any` type casts in favor of inferred types
https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3
* Allow commissioners to add league members as co-commissioners
Non-admin commissioners can now add users who already own a team in
the league as co-commissioners. Admins still see the full user list.
Previously the add-commissioner form was admin-only.
https://claude.ai/code/session_01NSRMSYtb7jSFbngDS8okn3
---------
Co-authored-by: Claude <noreply@anthropic.com>
* Hide EV column from draft board available participants table
https://claude.ai/code/session_016oJK1gmWZ48YWEZmVqm6f9
* Clean up dead expectedValue references after hiding EV column
- Remove expectedValue from AvailableParticipantsSection interface
- Remove EV column from Force Manual Pick commissioner dialog
- Remove expectedValue from participants loader SELECT and parsing step
- Retain ORDER BY expectedValue for default sort order
https://claude.ai/code/session_016oJK1gmWZ48YWEZmVqm6f9
---------
Co-authored-by: Claude <noreply@anthropic.com>
* feat: sync odds-based EV to participants table for draft room ranking
When admin enters futures odds or manual probabilities, the calculated EV
is now synced from participantExpectedValues to participants.expectedValue.
This closes the gap where EVs were calculated but never reflected in the
draft room ranking. Also changes expectedValue from integer to decimal(10,2)
for better precision, and displays "—" for participants with no odds data.
https://claude.ai/code/session_01JWG2zg2EMzCn6RgEufPC1T
* fix: correct expectedValue type to string in participants route
Missed instance of `expectedValue: 0` (number) that failed typecheck
after schema change from integer to decimal.
https://claude.ai/code/session_01JWG2zg2EMzCn6RgEufPC1T
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Add FIFA_48 bracket template with 12 groups of 4, knockout stage of 32
- Add tournament groups schema, model, and GroupStageDisplay component
- Add projected/expected value scoring to standings and team breakdowns
- Add docker-compose for local PostgreSQL development
- Move DRAFT_ORDER_IMPLEMENTATION.md to plans directory
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Added recharts dependency to package.json for data visualization.
- Implemented PointProgressionChart component to display team point progression over time.
- Updated scoring-system.md to reflect completion of historical views and added implementation notes.
- Created unit tests for PointProgressionChart to ensure correct rendering and data handling.
- Developed season completion detection and percentage calculation functions in season-helpers.server.ts.
- Added tests for season completion logic and point progression data formatting.
- Enhanced league loader to fetch necessary data for current season and teams.
- Created `TeamScoreBreakdown` component to display all drafted participants, their placements, and points.
- Added new route for team breakdown at `/leagues/:leagueId/standings/:seasonId/teams/:teamId`.
- Enhanced `getTeamScoreBreakdown` model to include `sportsSeasonId` for grouping.
- Updated `StandingsTable` to include clickable team name links.
- Added functionality to finalize brackets in playoff events, including error handling and recalculating standings.
- Implemented tests for `TeamScoreBreakdown` component to ensure proper rendering and functionality.
- Updated routing to support new team breakdown feature and ensure seamless navigation.
- Added a new route for league standings: `/leagues/:leagueId/standings/:seasonId`
- Implemented the `StandingsTable` component to display team standings with ranking, points, and placement breakdown.
- Integrated tiebreaker logic for ranking teams based on total points and placements.
- Enhanced the league home page with a link to view standings.
- Updated scoring system documentation to reflect the new standings features.
- Added comprehensive tests for tiebreaker logic and `StandingsTable` component.
- Created shared types for standings to be used in both server and client code.
- Created ScoringRulesEditor component with 8 placement point inputs
- Added scoring-types.ts for shared client/server types
- Integrated scoring rules into league settings page
- Integrated scoring rules into league creation form
- Added validation for point values (0-1000 range)
- Disabled editing in settings after draft starts
- Included helpful tips and preview display
Phase 1.3 complete
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Add scoringPattern and eventType enums for 4 scoring patterns
(single_elimination_playoff, page_playoff, season_standings, qualifying_points)
- Add 8 placement point fields to seasons table (1st-8th) with defaults
- Add scoring pattern fields to sportsSeasons table (scoringPattern, totalMajors, etc.)
- Create 10 new tables:
- scoring_events: Individual games, tournaments, races
- event_results: Participant results per event
- playoff_matches: Bracket tracking for playoffs
- participant_qualifying_totals: QP aggregation for golf/tennis
- qualifying_point_config: Configurable QP values per sport
- team_sport_scores: Aggregated scores per sport
- team_standings: Current standings with tiebreakers
- team_standings_snapshots: Daily snapshots for 7-day tracking
- participant_expected_values: EV calculations per league
- participant_season_results: F1 current points tracking
- Add all necessary relations for new tables
- Update test fixtures with new required scoring fields
- Added AvailableParticipantsSection for participant search and filtering.
- Created DraftGridSection for displaying the draft grid with team timers and context menu.
- Developed QueueSection for managing participant queue and autodraft settings.
- Introduced RecentPicksSection to show recent draft picks.
- Implemented TeamsDraftedGrid to visualize drafted participants by team and sport.
- Added collapsible UI components for better layout management.
- Established tabbed navigation for draft board, recent picks, and teams drafted.
- Enhanced responsiveness for mobile and desktop views.
- Updated styles for a cohesive design across components.
- Created draft eligibility calculation logic in `app/lib/draft-eligibility.ts`
- Added interfaces for sport availability and draft eligibility
- Implemented `calculateDraftEligibility` function to determine eligible sports based on team picks and global availability
- Developed `getEligibilitySummary` function for human-readable eligibility status
- Updated API endpoints to validate eligibility before making picks
- Enhanced frontend draft room UI to reflect eligibility status and provide visual indicators for ineligible participants
- Comprehensive implementation summary and testing documentation added
- All changes type-checked and unit tests passed successfully