Commit graph

10 commits

Author SHA1 Message Date
3273cf1bcb claude/settings-menu-urls-kbp6x8 (#116)
All checks were successful
🚀 Deploy / ʦ🔍 Typecheck & Lint (push) Successful in 1m17s
🚀 Deploy / 🐳 Build (push) Successful in 1m9s
🚀 Deploy / 🧪 Test (push) Successful in 3m17s
🚀 Deploy / 🚀 Deploy (push) Successful in 14s
Co-authored-by: Claude <noreply@anthropic.com>
Reviewed-on: #116
2026-06-30 16:01:36 +00:00
Chris Parsons
73f0256ff5 Fix QP standings: filter to drafted/points-earning participants with correct global ranks
Some checks failed
🚀 Deploy / 🧪 Test (pull_request) Failing after 2m33s
🚀 Deploy / ʦ🔍 Typecheck & Lint (pull_request) Successful in 1m20s
🚀 Deploy / 🐳 Build (pull_request) Has been skipped
🚀 Deploy / 🚀 Deploy (pull_request) Has been skipped
- Filter the QP table to show only participants with QP > 0 or drafted by any team in the league (undrafted 0-QP participants hidden)
- Compute global ranks with tie handling across the full field before filtering so displayed rank numbers and the top-8 Points Line remain correct after rows are removed
- Fix flaky CI timeouts: world-cup simulator test (100 → 50 iterations), SportsSection userEvent test (explicit 15s timeout)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 09:38:47 -07:00
Chris Parsons
fbcecb490e
Add Discord draft pick announcements (#460)
* Add Discord draft pick announcements

Posts a message to the league Discord webhook each time a pick is made, announcing the picked participant and pinging the next team on the clock if their owner has opted into Discord notifications. Enabled via a separate toggle in league settings (independent from standings update notifications).

https://claude.ai/code/session_01Tvwsv3LfL9JUqxoLct8dTn

* Address code review feedback on Discord pick announcements

- Fix "on the clock" timing: read currentPickNumber fresh from DB post-autodraft-chain (matches sendOnTheClockEmail guarantee)
- Remove outer try/catch from notifyPickMadeOnDiscord so callers' .catch() is not dead code
- Add missing draft-discord.server.test.ts with 12 tests covering all early-exit and happy paths
- Fix silent empty-string fallback for missing pickedSlot: warn and skip instead
- Eliminate sequential season→league DB queries by accepting leagueId as a direct param
- Show "save webhook URL to configure options" hint when URL is typed but not yet saved
- Remove block-scope braces at both call sites (plain const declarations)
- Remove redundant "Round N, Pick M" description line (title already carries this info)
- Inline pickInRoundFor helper to avoid circular import with draft-utils

https://claude.ai/code/session_01Tvwsv3LfL9JUqxoLct8dTn

* Fix lint errors from review fixes

- Remove unused logger import (no longer needed after removing try/catch)
- Remove unused OWNER_ID constant in test fixture
- Use toSorted() instead of sort() in sendDraftOrderNotification

https://claude.ai/code/session_01Tvwsv3LfL9JUqxoLct8dTn

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-20 19:55:48 -07:00
Chris Parsons
3c757c1f73
Replace settings redirect with toast notification (#445)
* Show toast instead of redirecting after league settings save

Commishes now stay on the settings page when saving, with a Sonner
toast confirming the save rather than being navigated to the league
homepage.

https://claude.ai/code/session_01PewdKxVfAbqasJbmk2UGw4

* Fix toast firing for unrelated intents after settings save

Tag update responses with intent: "update" so the success useEffect
only fires for the main settings form, not for owner/commissioner/
reset-draft actions which also return { success: true }. Also replaces
the remaining ?updated=true redirect (no-season edge case) with the
same response shape for consistency.

https://claude.ai/code/session_01PewdKxVfAbqasJbmk2UGw4

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-18 16:08:10 -07:00
Chris Parsons
4bbcac1949
fix: resolve all 48 WCAG 2.2 AA accessibility issues (#439)
* fix: resolve all 48 WCAG 2.2 AA accessibility issues

Critical fixes:
- Add aria-label to all unlabeled inputs/selects in draft dialogs (ParticipantSelectionDialog, TimeBankAdjustmentDialog, AvailableParticipantsSection)
- Add role="dialog" + aria-modal + focus trap to ConnectionOverlay and AuthRecoveryOverlay
- Add aria-live region and connection status announcement to ConnectionOverlay

Serious fixes:
- Add skip-to-content link in root.tsx with id="main-content" on <main>
- Add aria-label to UserMenu trigger button
- Add aria-describedby + role="alert" to all auth form error messages (login, register, onboarding, forgot-password, reset-password)
- Replace emoji column headers in StandingsTable with aria-label + aria-hidden spans
- Add aria-live="assertive" to "It's your turn" desktop and mobile on-clock indicators
- Add aria-live="polite" to draft room countdown timer
- Add pause button to SportTicker (WCAG 2.2.2); add aria-hidden to ticker content
- Fix Footer text contrast (changed from 28% to text-muted-foreground)
- Fix OvernightPauseSettings: add htmlFor/id pairs and role="radiogroup"+aria-checked to mode buttons
- Fix DraftSetupSection: replace broken htmlFor with aria-label on date picker button
- Add aria-label to PeopleSection owner and commissioner selects
- Add labels to ScoringPresetPicker score inputs; add role="radiogroup"+aria-checked to preset buttons
- Add role="radiogroup"+aria-checked to AutodraftSettings option buttons
- Add accessible names, aria-current="step", and <ol> list semantics to WizardStepper

Moderate fixes:
- Add aria-controls to RecentPicksFeed toggle button; wrap picks list in aria-live region
- Add role="tab"+aria-selected+aria-controls to mobile board sub-tabs + role="tabpanel"
- Add role="radiogroup"+aria-checked to TimerModeSelector
- Add aria-current="page" + aria-label to SettingsDesktopNav
- Add aria-label="Admin navigation" to admin sidebar nav
- Add scope="col" + <caption> to StandingsTable and ScoringTables
- Add ARIA table roles (role="table/rowgroup/row/columnheader/rowheader/cell") to DraftSummaryView CSS grid

Minor fixes:
- Add aria-hidden="true" to decorative trend icons in StandingsTable
- Add aria-hidden="true" to desktop column header labels row in AvailableParticipantsSection
- Replace title with aria-label on all icon-only buttons (watchlist, queue) in AvailableParticipantsSection
- Add aria-label to NotificationSettings switchOnly Switch
- Add prefers-reduced-motion check to SlotMachineHeadline JS animation
- Bump --muted-foreground from 55% to 62% opacity for improved contrast margin

https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3

* Fix code review findings from WCAG compliance pass

- Add Arrow key navigation + roving tabindex to all role=radiogroup
  components (AutodraftSettings x2, TimerModeSelector,
  OvernightPauseSettings, ScoringPresetPicker) per ARIA radio pattern
- Extract shared focus-trap logic into useFocusTrap hook; update
  ConnectionOverlay and AuthRecoveryOverlay to use it
- Add tabIndex={-1} to ConnectionOverlay Card so focus can land in
  spinner-only state (no interactive children)
- Replace aria-live on loading dots container with sr-only span so
  status changes are announced by text content, not aria-label
- Remove contradictory aria-hidden+role=columnheader from
  AvailableParticipantsSection visual-only header row
- Remove invalid scope="col" from div[role=columnheader] in
  DraftSummaryView (scope is only valid on <th>)
- Remove redundant aria-label from ParticipantSelectionDialog sport
  select (htmlFor label is sufficient)
- Change WizardStepper connector <li> to role=presentation
- Revert muted-foreground from 62% to 55% (original already passes
  contrast; footer was fixed separately via text-muted-foreground)

https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3

* Fix lint error and update tests for WCAG role changes

- Replace el! non-null assertion with optional chaining in useFocusTrap
- Update AutodraftSettings tests to query role="radio" instead of
  role="button" (buttons have an explicit radio role since the WCAG pass)
- Update AvailableParticipantsSection watchlist tests to use
  getByRole/getAllByRole instead of getByTitle/getAllByTitle (watchlist
  buttons now use aria-label instead of title)

https://claude.ai/code/session_01JXajpFxhqLf8aPCncP81k3

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-17 20:11:38 -07:00
Chris Parsons
d468385d90
Add auto-start draft at scheduled time (#437) (#438)
Commissioners can now opt in to having a draft start automatically at
its scheduled draftDateTime rather than requiring a manual "Start Draft"
click. The 1-second timer loop checks for eligible seasons each tick and
calls the new shared startDraft() service, which is also used by the
existing commissioner HTTP route.

- Add autoStartDraft boolean to seasons table (migration 0108)
- Extract core start logic into app/services/draft-autostart.ts so both
  the API route and the timer can call it without AsyncLocalStorage
- Add checkAndAutoStartDrafts() to the timer tick; disables autoStartDraft
  and stops retrying if no draft order is set at fire time
- Guard against null draftDateTime in both the timer query (isNotNull)
  and server actions (autoStartDraft forced false when datetime is null)
- Add "Auto-start at scheduled time" checkbox to league creation wizard
  (step 3) and league settings, gated on date+time being set
- Show countdown + "Start Now" button in draft room when auto-start is
  scheduled; reuses the existing nowForPauseCheck 1-second ticker
- Show orange warning banner on league homepage to commissioners when
  auto-start is within 1 hour and no draft order has been configured

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 23:37:29 -07:00
Chris Parsons
55828e9f42
Simplify sports season display and add local timezone hints (#434)
* Show season name instead of year/bracket format; add local timezone hint to draft time

Replace the raw year number and scoringType string in the league creation
sport picker, review step, and league settings sports section with the
human-readable season name (e.g. "2025 NBA Season"), matching how the
league homepage sports list already displays seasons.

Add a client-side local timezone label below the draft date & time fields
on both the league creation wizard and the league settings page, so users
know that draft times are in their local timezone.

https://claude.ai/code/session_01GtJowGruAc1A4jURkSDVjX

* Address code review: shared tz hook, short abbrev, sort fix, dead type cleanup, test update

- Extract timezone detection into useLocalTimezone hook to avoid duplication
  across DraftSetupSection and Step3DraftSettings
- Use Intl short timezone abbreviation (e.g. "EST") instead of raw IANA name,
  matching DraftInfoCard's existing pattern
- Sort review step sport list by season name instead of sport.name so the
  visible order matches the displayed text
- Remove unused scoringType and year fields from SportsSection's local type
- Update SportsSection tests to use realistic season names and the new
  display format

https://claude.ai/code/session_01GtJowGruAc1A4jURkSDVjX

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 16:04:07 -07:00
Chris Parsons
777315541b
Use sport icons in league views (#394) 2026-05-07 23:06:49 -07:00
Chris Parsons
6df4f86920
Improve flag config validation and remove settings completion tracking (#390)
* Fix mobile league settings: prevent sports scroll and remove completion checkmarks

- Add overflow-hidden to sport label text span so flex truncation is properly contained
- Add min-w-0 to SettingsSection header inner flex item to prevent title/description overflow
- Remove completion check marks from mobile grid nav buttons (league settings has no completion concept)
- Remove "X of Y set" counter from mobile nav header
- Remove isComplete field from SettingsGridSection type and all data

https://claude.ai/code/session_01T7iTb9YZLuWJFtKV753tdB

* Fix race window in user creation and restore FlagSvg safety fallback

auth.server.ts: switch generateId to application-generated UUIDs so the
ID is known before the insert, then move flagConfig assignment from
create.after (a separate UPDATE) to create.before so it lands in the
initial INSERT with no race window.

FlagSvg.tsx: validate the config with parseFlagConfig before rendering;
corrupt or missing data falls back to a neutral gray triband flag rather
than silently rendering blank SVG shapes.

https://claude.ai/code/session_01T7iTb9YZLuWJFtKV753tdB

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-07 10:09:45 -07:00
Chris Parsons
05fe1493a3
Refactor league settings into per-section components (#379)
* Refactor league settings into per-section components (#347)

Extract all settings sections from the monolithic 1009-line route file into
individual components under app/components/league/settings/. Route file drops
to ~300 lines. Separates draft-order dirty state from general settings dirty
state, deduplicates section-change handling, and fixes several bugs found
during review (typo in pointsFor5th, wrong mock in tests, lint violations).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Fix Stop hook loop: suppress output on typecheck success

The Stop hook was producing stdout on every run, causing Claude Code to
feed it back as context and rewake the model each turn. Now emits a
systemMessage JSON only on failure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* Typescript fix

* Remove type asserting

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 14:19:50 -07:00