Participants are created with externalId=null by default, causing the
LLWS simulator to crash at runtime. Fix in two parts:
1. Name-prefix inference: if externalId is null, participants whose
name starts with "US " or equals "US" are assigned to the US side;
all others are assigned to Intl. Pools are always randomized when
inferred (no pool suffix).
2. Admin UI: add an inline-editable "Group" column to the Manage
Participants page so admins can explicitly set externalId for any
simulator that uses it (LLWS, and future cases like NHL conferences).
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#224
* Add inline edit for participant names on admin sports season page
Adds a pencil icon button next to each participant's delete button. Clicking
it switches the row into edit mode with an input field, a save (checkmark) and
cancel (X) button. Escape also cancels. Submits via intent="update-name" and
validates uniqueness within the season before persisting.
https://claude.ai/code/session_01Q94DWQ1HZEB9PJhGMArU8D
* Simplify edit state and surface edit errors to user
- Combine editingId + editingName into single editing: { id, name } | null state object
- Extract cancelEdit() helper called in 3 places instead of repeating setEditing(null)
- Tag all update-name action responses with intent: "update-name" so errors can be
identified and routed correctly
- Show update-name errors inline below the edit input
- Guard single-add and bulk-add error displays against showing update-name errors
https://claude.ai/code/session_01Q94DWQ1HZEB9PJhGMArU8D
---------
Co-authored-by: Claude <noreply@anthropic.com>
- Add unique index on (sports_season_id, name) in participants table
- findParticipantByName uses case-insensitive lower() comparison
- Single add: check for existing name before insert, return clear error
- Bulk add: load existing names once upfront (1 query vs N), dedup
input case-insensitively, report skipped names in UI
- Fix golf-skills and surface-elo routes which called createParticipant
without any duplicate guard (would have thrown DB constraint errors)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add app/lib/logger.ts: dev passes through to console; prod routes errors
to Sentry.captureException and warnings to Sentry.captureMessage, with
extra context preserved. Uses captureMessage (not captureException) for
string-only args to avoid fabricated stack traces.
- Add server/logger.ts: dev passes through; prod silences log/info but
keeps warn/error on stderr (Sentry not initialized in that process).
- Replace all console.* calls across 44 app files and 4 server files.
- Upgrade no-console from warn → error in oxlint; exempt logger files and
scripts/** via overrides.
- Add typescript/no-inferrable-types rule; fix violations in services and
simulators. Exempt test files (intentional string widening for switch/if
tests would break under literal type inference).
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
* Add oxlint and fix all lint errors
- Install oxlint, add .oxlintrc.json with rules for TypeScript/React
- Add npm run lint / lint:fix scripts
- Add Claude PostToolUse hook to run oxlint on every edited file
- Fix 101 errors: unused vars/imports, eqeqeq, prefer-const, no-new-array
- Fix no-array-index-key (use stable keys or suppress positional cases)
- Fix exhaustive-deps missing dependency in useEffect
- Promote exhaustive-deps and no-array-index-key to errors
- Fix Map.get() !== null bug in $leagueId.server.ts (should be !== undefined)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix no-explicit-any warnings and upgrade tsconfig to ES2023
- Replace all `any` types with proper types or `unknown` across ~20 files
- Add typed socket payload interfaces in draft route and useDraftSocket
- Use any[] with eslint-disable for socket.io callbacks (legitimate escape hatch)
- Bump all tsconfigs from ES2022 → ES2023 to support toSorted/toReversed
- Fix cascading type errors uncovered by removing any: Map.get narrowing,
participant relation types, ChartDataPoint, Partial<NewSeason> indexing
- Add ParticipantResultWithParticipant type to participant-result model
- Fix test fixtures to match updated interfaces (DraftCell, ParticipantResult)
- Fix duplicate getQPStandings import in sportsSeasonId.server.ts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Promote no-explicit-any to error
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Every route now exports a meta function so the browser title bar reflects
the current page. Static pages use fixed titles; dynamic pages pull names
from loader data with a sensible fallback (e.g. league name, sport season
name, team name).
Titles follow the pattern "Page Name - Brackt" for user-facing routes and
"Page Name - Brackt Admin" for admin routes.
Fixes#74
Co-authored-by: Claude Sonnet 4.6 <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>