* Show "Syncing Draft State..." overlay during draft reconnection
When a user reconnects to a draft room (socket reconnect, tab visibility
return, etc.), there was a gap between when the socket connected and when
the full draft state sync arrived. The ConnectionOverlay vanished instantly
on reconnect, showing potentially stale data.
Add an isSyncing flag that keeps the overlay visible with "Syncing Draft
State..." messaging until the actual data arrives via socket or HTTP
revalidation. A 5-second safety timeout prevents the overlay from getting
stuck.
Fixes#78
* Add team names utility tests
* Fix BetterAuth field mapping and add owner-prefixed team names
- Fix auth.server.ts: use camelCase Drizzle field names for BetterAuth
adapter (was snake_case, causing user inserts to fail); add
generateId: "uuid" so PostgreSQL UUID columns accept generated IDs
- Add prependOwnerToTeamName / stripOwnerFromTeamName utilities
- Invite join, league creation, and settings assign/remove all now
manage the owner-prefix on team names atomically
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Complete BetterAuth migration: auth flows, rename, and cleanup
- Add /forgot-password and /reset-password pages (full password reset flow)
- Add 'Forgot password?' link on login page
- Fix register.tsx: add explicit window.location fallback after signUp
- Rename commissionerAuditLog.actorClerkId → actorUserId (migration 0084)
and update all references in model, routes, components, and tests
- Rewrite docs/agents/auth.md to document BetterAuth (removes all Clerk refs)
- Update test fixtures and schema comments to remove Clerk ID references;
use UUID-format IDs throughout test data
- Update docs/agents/domain-models.md ownerId description
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* Fix BetterAuth ID generation, clean up review issues
- Set generateId: false so BetterAuth omits id from inserts; add
$defaultFn(crypto.randomUUID) to accounts/sessions/verifications
so Drizzle fills it in (fixes null id constraint violation on signup)
- Move renameTeam to static import; rename before removeTeamOwner so
a failed rename leaves owner intact rather than leaving a stale prefix
- Clarify stripOwnerFromTeamName toTitleCase assumption in comment
- Annotate reset-password token fallback to explain loader guarantee
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Fixes#262
* Replace isDraftable boolean with draftOn/draftOff date fields on sport seasons
Admins can now schedule when a sport season becomes available for drafting
by setting explicit open and close dates instead of a manual toggle.
https://claude.ai/code/session_01LHYgpyimF8v8odUB6kj8Qc
* Address code review feedback on draft window implementation
- sports-data-sync: use unambiguous past placeholder dates for synced seasons,
with a comment explaining the intent
- sports-season model: remove redundant top-level lte/gte imports (callback
form already supplies them)
- _journal.json: add missing trailing newline
- sports-season test: mock ~/database/schema instead of stubbing all drizzle
builder functions, matching the established pattern in the codebase
- admin list: compute today once in component body instead of per-row
https://claude.ai/code/session_01LHYgpyimF8v8odUB6kj8Qc
* Fix: restore lte/gte imports removed in review cleanup
The relational query where-callback in this version of Drizzle does not
expose lte/gte as helper args, so they must be imported from drizzle-orm.
Removing them broke CI TypeScript.
https://claude.ai/code/session_01LHYgpyimF8v8odUB6kj8Qc
---------
Co-authored-by: Claude <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>
* Include participant EV/futures data in data sync export/import
The export now includes the full participantExpectedValues records
(placement probabilities, source, sourceOdds) alongside participants.
On import, EV records are upserted in merge mode and created fresh in
replace mode (cascade handles cleanup). Backward-compatible with older
exports that lack the participantExpectedValues field.
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
* Address all data-sync code review findings
- Wrap importSportsDataFromJSON in a db.transaction() so any mid-import
failure rolls back cleanly instead of leaving partial data
- Add zod validation of imported JSON before any DB writes, giving a
clear error on malformed/incompatible files
- Fix N+1 queries: build participantIdMap during participant import so
the EV and results sections resolve IDs from memory, not extra queries
- Fix merge mode silently skipping existing participants — now updates
shortName, externalId, and expectedValue
- Add participantResults to export/import (was deleted in replace mode
but never exported, so results were permanently lost)
- Restore calculatedAt timestamp on EV import instead of defaulting to
now(); stored as ISO string in the export for portability
- Document that onDelete:cascade covers participantExpectedValues and
participantResults when participants is deleted; remove the now-
redundant explicit participantResults delete from replace mode
- Bump export version to 1.1; old v1.0 files still import cleanly since
participantExpectedValues, participantResults, and calculatedAt are
all optional in the zod schema
- Collapse all six DB fetches in exportSportsDataToJSON into one
Promise.all for parallel execution
- Add countAllParticipants() and countAllParticipantEVs() model funcs
- Show Participants and EV Records counts on the dashboard stat cards
- Replace raw DOM form creation/submit in handleImport with useFetcher,
giving proper loading state, no full-page reload, and type-safe data
- Remove dead export intent handler from the action function
https://claude.ai/code/session_01TVzuuDR7jPtmihaXTrsX7B
---------
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>