Commit graph

27 commits

Author SHA1 Message Date
Claude
516b4e7131 Fix group-stage match times being stored as local-wall-time-as-UTC
The admin group-stage schedule form submitted the raw datetime-local
string, which the server parsed with new Date() in the server's timezone
(UTC), so an admin's local wall-clock time was stored as if it were UTC.
Downstream displays then correctly converted UTC->local, shifting times
by the admin's UTC offset (e.g. 7 PM PDT showed as 12 PM).

Mirror the existing playoff "add-game" form: convert local->UTC on the
client via localDateTimeToUtcIso into a hidden field on submit, and
render the stored UTC value back into the input in the browser's local
timezone via a client-only effect (avoids SSR hydration mismatch).

Adds utcIsoToLocalDateTime helper (inverse of localDateTimeToUtcIso)
with unit tests covering nullish/invalid input and a tz-independent
round-trip.

Note: matches saved before this fix must be re-saved once in admin to
correct their stored values.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0125nZohVD2Cpoq3Q9f4jeds
2026-06-18 02:19:31 +00:00
Chris Parsons
0ba2b391f8
Fix draft order initialization for teams added mid-season (#449)
* Fix draft order showing only new team when league size increased before order was set

When a commissioner increased the team count on a league that had teams
but no draft order set yet, the server created draft slots only for the
newly-added teams. This left the DB with N+K teams but only K slots,
causing the drag-and-drop list to display only the K new teams.

Two fixes:
1. Server: only append new draft slots when an order was already set
   (existingSlots.length > 0). If no order exists yet, skip slot creation
   so the page correctly treats the order as unset for all teams.
2. Frontend: buildDraftOrderTeams() appends any unslotted teams after the
   slotted ones, so a partially-corrupt DB state still shows all teams.

https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo

* Address code review feedback on draft order bug fix

- Move buildDraftOrderTeams to app/lib/draft-order.ts so it is importable
  and testable; remove the local copy from the settings component
- Add unit tests for buildDraftOrderTeams covering the empty, full, and
  partial-slot cases
- Tighten the draft slot guard from existingSlots.length > 0 to
  existingSlots.length === currentTeamCount so partial legacy states are
  treated the same as "order not set"
- Condense the 3-line server comment to a single line per project style
- Rename getNumTeamsInSeason → getNumDraftSlotsBySeasonId to reflect what
  the function actually counts, and update its one call site
- Add tests for the server-side slot-creation guard logic

https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo

* Fix lint: move shouldAppendDraftSlots to outer scope

oxlint (consistent-function-scoping) requires functions that don't close
over any variables to be defined at the outer scope rather than inside a
describe block.

https://claude.ai/code/session_01M3H55gnMxRztJK9KMXqqZo

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-19 18:22:47 -07:00
Chris Parsons
d92f73e449
Extract chess clock presets to shared constant (#432)
* Fix custom chess-clock timer detection and save settings blocker

- Extract getInitialDraftSpeed() helper so both useState init and
  resetSettingsFormState use the same logic; unrecognized presets
  now produce a "custom:{bank}:{incr}" string instead of falling
  back to "standard", so the custom section auto-expands correctly
  (e.g. 8 hr bank + 30 min increment is no longer shown as Standard)
- resetSettingsFormState was not resetting draftSpeed at all; fixed
- Clear hasUnsavedSettingsChanges in the Form's onSubmit so the
  useBlocker check is false at the moment the save navigation starts,
  preventing the "Leave without saving?" dialog from firing on save
- Replace the cleared-on-success useEffect with one that re-marks
  dirty when the action returns an error, so the warning reappears
  after a failed save

https://claude.ai/code/session_01DgHNkvJXGizx41CE2tkVS1

* Address code review: single source of truth for presets, useEffect guard, tests

- Extract CHESS_CLOCK_PRESETS (pure data) into draft-timer.ts as the
  canonical source of truth; DraftSpeedPicker.tsx now spreads those
  values into CHESS_PRESETS rather than duplicating the numbers
- getInitialDraftSpeed moved to draft-timer.ts and rewritten to use
  CHESS_CLOCK_PRESETS.find() — no more hardcoded bankSec/incrSec values
  in three separate places
- parseDraftSpeed switch replaced with CHESS_CLOCK_PRESETS.find() and
  an explicit fallback comment; eliminates the silent default-handles-
  "standard" fragility called out in review
- useEffect that re-marks settings dirty now guards against non-settings
  errors (draft-order, commissioner actions) which carry a `section`
  field — previously any error actionData would spuriously set
  hasUnsavedSettingsChanges=true
- Add parseDraftSpeed and getInitialDraftSpeed test suites to
  draft-timer.test.ts, including parametrised preset coverage via
  it.each(CHESS_CLOCK_PRESETS)

https://claude.ai/code/session_01DgHNkvJXGizx41CE2tkVS1

* Fix lint: use !== null/undefined instead of != null

oxlint enforces eqeqeq; the null check in getInitialDraftSpeed used
!= which triggered two errors.

https://claude.ai/code/session_01DgHNkvJXGizx41CE2tkVS1

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-05-15 14:02:49 -07:00
Chris Parsons
9e9a37d4d2
Improve unmatched team reconciliation UX (#425) 2026-05-14 16:45:02 -07:00
Chris Parsons
04a92ec7de
Add branded dark-themed HTML email template (#402)
Introduces a shared email module that wraps all transactional emails in a
cohesive dark-themed template matching the site's visual identity (Barlow
font, #14171e background, #adf661 CTA buttons, logomark PNG header).

- app/lib/email.server.ts: new module with sendEmail, wrapInEmailTemplate,
  emailButton, emailParagraph, and escapeHtml helpers; Resend instance is
  lazily initialized as a singleton
- public/email-logo.png: 96×124px PNG of the logomark for email use (SVG
  gradients are not supported in Outlook)
- auth.server.ts: password reset and email verification now use the branded
  template; errors are thrown so Better Auth can surface failures
- support.tsx: internal notification uses sendEmail; adds a user-facing
  branded confirmation email (fire-and-forget so failures don't surface to
  the user); plain text fallback included
- scripts/sync-prod-db.sh: remove first_name/last_name from sanitize query
  (columns were dropped in #399)
- 13 unit tests covering all template helpers

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 11:18:57 -07:00
Chris Parsons
e64cdabb17
Harden sport icon cleanup (#397) 2026-05-08 21:19:09 -07:00
Chris Parsons
20685bae6b
Add Cloudinary sports icon uploads (#393) 2026-05-07 16:07:34 -07:00
Chris Parsons
a447ba54de
Add Cloudinary-backed avatar system (#385) 2026-05-06 14:47:37 -07:00
Chris Parsons
049ec8a596
Show why a participant is ineligible to draft (#378)
Closes #72

Upgrades ineligibleReasons from Record<string, string> to a structured
{code, message} type so the UI can render the human-readable reason
inline below the Ineligible badge in both AvailableParticipantsSection
and ParticipantSelectionDialog. API routes surface the message in 400
responses unchanged.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 21:54:14 -07:00
Chris Parsons
dbc23f14ce
Add overnight pause for draft timers (#335)
* Add overnight pause feature for draft timers

Protects players from having their pick timer expire while asleep.
Admins configure a nightly window (league-wide or per-user timezone);
the timer freezes during that window while autodraft can still fire.

Fixes #66

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

* Fix TS error: guard getUserDisplayName against undefined user

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-26 22:31:52 -07:00
Chris Parsons
3566a97a1e
feat: add projected pick dividers to draft participant list (fixes #82) (#322)
Show visual dividers in the available participants list indicating where
your future draft picks fall relative to the current pick position.
Dividers are suppressed when any filter is active (search, sport filter,
hide drafted, etc.) since positional references are meaningless in a
filtered view.

- Add getProjectedPicks() to draft-order.ts for computing future pick
  positions in a snake draft
- Interleave divider items into the virtualized list in
  AvailableParticipantsSection
- Extract ListItem type to module scope
- Add tests for getProjectedPicks, divider rendering, and filter
  suppression
2026-04-24 09:32:11 -07:00
Chris Parsons
1798020c74
feat: batch qualifying results entry (#290)
Fixes #289

* docs: add batch qualifying results entry design spec

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

* docs: add batch qualifying results entry implementation plan

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

* chore: ignore .worktrees directory

* feat: add qualifying results text parser with tests

* fix: handle no-space after dot separator, tighten T-prefix to uppercase only

* feat: add batch-add-results server intent with duplicate filtering

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

* feat: add create-participant intent for inline participant creation

* fix: trim sportsSeasonId validation in create-participant intent

* feat: fill 0-QP rows for unplaced participants on qualifying event finalize

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

* perf: use bulk insert for 0-QP rows on qualifying event finalize

* feat: add BatchResultEntry component for paste-and-parse batch result import

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

* fix: disable add participant button while create request is in flight

* feat: render BatchResultEntry on qualifying event pages

Add BatchResultEntry component import and render it on event pages when
the event is a qualifying event, not a final_standings/playoff_game type,
and not yet complete.

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

* fix: address final review issues (sportsSeasonId authority, participant validation, CRLF, unused prop)

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-12 14:52:22 -07:00
Chris Parsons
611e1ccf0a
Show tied ranks with T prefix across standings and Discord (#239)
* Show tied ranks with T prefix across standings and Discord notifications, fixes #197

- Add buildTiedRankChecker() utility to standings-display.ts; replaces
  four copies of inline rank-count logic spread across components,
  the league home route, and the Discord service
- Prefix shared ranks with "T" (e.g. "T3") in StandingsTable (league
  panel), StandingsTable (full standings), the league home preview, and
  Discord webhook notifications
- Add useMemo wrapping in StandingsTable components so tie detection
  does not recompute on every render
- Fix compareTeamsForRanking to round total points to hundredths before
  comparing, preventing floating-point noise from producing spurious
  non-ties in the standings

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

* Fix discord test to expect escaped period in rank display

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 20:45:15 -07:00
Chris Parsons
3c4ed67946
Add upcoming events pages and fix timezone filtering, fixes #213 (#235)
- Fix UTC midnight rollover bug: server now queries from yesterday UTC
  as a buffer; UpcomingCalendarPanel filters to local-today client-side
  via useEffect + Intl.DateTimeFormat, removing the need for any
  cookie or server-side timezone detection
- Cap homepage and league page panels at 6 events with a "View all" link
- Add /upcoming-events page (60-day view across all leagues)
- Add /leagues/:leagueId/upcoming-events page (60-day per-league view)
- Add emptyMessage prop to UpcomingCalendarPanel for context-specific copy
- Change getUpcomingEventsForDraftedParticipants to accept pre-computed
  date strings instead of Date objects

fixes #213

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-27 00:49:16 -07:00
Chris Parsons
4bffa40606
Fix oxlint warnings: no-shadow, consistent-function-scoping, no-non-null-assertion, and others (#196)
* Fix no-shadow and consistent-function-scoping lint violations

Resolves all 11 no-shadow and 16 consistent-function-scoping oxlint
warnings and promotes both rules to errors in .oxlintrc.json.

no-shadow: renamed Drizzle callback params (sports→s, matches→m,
seasons→s) to avoid shadowing outer imports; removed shadowed
destructures (eq, inArray) from where callbacks; renamed inner
template→bracketTemplate, prev→currentTimers, season→ss, name→teamName
(with name: teamName fix to preserve semantics).

consistent-function-scoping: moved formatDate, getRankBadge,
getMovementIndicator, getPositionBadge, getStatusBadge, toDateStr,
elo (×2), weightedPick, sortByMatchNumber (×2) to module scope;
moved formatTime (×2), isValidLeagueName, getDraftTimes,
makeSeasonQueues to file scope in test files.

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

* Fix no-non-null-assertion lint violations and promote to error

Eliminates all 208 no-non-null-assertion warnings across 38 files.
Promotes typescript/no-non-null-assertion from warn to error in
.oxlintrc.json.

Fix patterns applied:
- Map.get(key)! after .has() check → extract with get() + null guard
- Map.get(key)! on pre-populated count maps → ?? 0 default
- .set(id, map.get(id)! + 1) increment → ?? 0 before adding
- participant1Id!/participant2Id! on DB matches → ?? "" fallback
- array.find()! in tests → guard + throw or expect().toBeDefined()
- bracketTemplateCache.get(id)! → null guard extract
- Various nullable field accesses → optional chain or ?? default

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

* Fix prefer-add-event-listener, no-unassigned-import, require-module-specifiers

Resolves all 9 remaining non-console lint warnings and promotes all
three rules to errors in .oxlintrc.json.

- prefer-add-event-listener: converted onchange/onclick/onload
  assignments to addEventListener in useDraftNotifications.ts and
  admin.data-sync.tsx; stored changeHandler ref for proper cleanup
  with removeEventListener
- no-unassigned-import: configured rule with allow list for legitimate
  side-effect imports (*.css, @testing-library/jest-dom,
  @testing-library/cypress/add-commands)
- require-module-specifiers: removed redundant `export {}` from
  cypress/support/e2e.ts (file already has an import)

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

* Fix TypeScript errors from no-non-null-assertion fixes

Two fixes introduced by the non-null assertion cleanup produced type
errors:

- scoring-event.ts: `?? ""` was wrong type for a participant object map;
  restructured to explicit null guards so TypeScript can narrow correctly
- standings-sync/index.ts: `?? null` after name-match lookup lost the
  truthy guarantee, causing TS18047 on the write-back block; added
  `participant &&` guard before accessing its properties

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

* Add npm run typecheck as Stop hook in Claude settings

Runs a full project typecheck at the end of each Claude turn so type
errors surface as feedback before the next message.

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

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 10:59:51 -07:00
Chris Parsons
e2b178221a
Add oxlint linting setup with zero errors (#194)
* 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>
2026-03-21 09:44:05 -07:00
Chris Parsons
bcca8b76fa
Add regular season standings for NBA/NHL (fixes #89) (#192)
Adds live standings sync and display for bracket-based sports (NBA/NHL),
so league members can see W/L tables and which teams their opponents drafted
during the regular season — not just after the playoff bracket is set.

- New `regular_season_standings` table with upsert-on-conflict sync
- Standings sync service with NHL (api-web.nhle.com) and NBA (ESPN) adapters,
  externalId write-back for future syncs, and unmatched-team resolution UI
- `RegularSeasonStandings` component: flat (NBA) + division/wild-card (NHL) modes,
  playoff line, TeamOwnerBadge, projected Brackt points (EV), mobile horizontal scroll
- Admin "Sync Standings" card + "Resolve Unmatched" UI on sports season page
- Admin manual standings edit hatch at /admin/sports-seasons/:id/regular-standings
- Show standings above bracket until matches exist; below once bracket is set
- `normalize-team-name` utility extracted to shared lib

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 00:12:01 -07:00
Chris Parsons
d546585fb6
Fix event date display off-by-one for late-night events in UTC-negative timezones (#147)
Events saved at 10 PM PDT (UTC-7) cross UTC midnight, so eventDate is stored
as the next UTC calendar day (e.g. March 28 10 PM PDT → eventDate "2026-03-29").
Displaying that date string via parseISO() gives March 29 local midnight, so
the events list showed "Mar 29" when the user expected "Mar 28".

Fix: when eventStartsAt is available, derive the display date from it directly
(format(new Date(eventStartsAt), ...)) rather than from the stored eventDate
string. This correctly converts the UTC timestamp to the user's local calendar
date. Date-only events (no eventStartsAt) are unchanged and continue to use
parseISO(eventDate).

Also tighten the getDisplayDate() guard: replace a misleading try/catch (new
Date() never throws) with an explicit isNaN check, and replace a non-null
assertion (eventDate!) with null-coalescing in the admin events list.

Tests cover both UTC and PDT environments and are timezone-independent.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 11:18:16 -07:00
Chris Parsons
dc13d99f39
Add event_starts_at timestamp to scoring events for sub-day ordering (#146)
- New `event_starts_at timestamptz` column on `scoring_events`; backfilled
  from `event_date` (midnight UTC) via migration for all existing rows
- Admin create/edit forms consolidated from separate Date + Time inputs into
  a single `datetime-local` field; server derives `eventDate` from the UTC
  date portion of `eventStartsAt` so calendar-window queries continue to work
- Edit form pre-fill computed client-side via useEffect to avoid server-timezone
  mismatch for non-UTC admins
- Sort fix: replaced `eventDate ?? earliestGameTime.split("T")[0]` with
  `toEventSortKey` helper that prefers `earliestGameTime ?? eventDate` so
  bracket events with only per-game timestamps sort in the correct calendar position
- DB sort queries updated to use `eventStartsAt` as a tiebreaker after `eventDate`
- F1/IndyCar/golf events now populate `earliestGameTime` from `eventStartsAt`,
  giving time display in `UpcomingCalendarPanel`
- Bulk import gains optional `HH:MM` (UTC) third column
- New unit tests for `toEventSortKey` and `eventStartsAt` model behavior

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-15 10:22:42 -07:00
Chris Parsons
ed4e99b40e
Fix datetime-local to UTC conversion for game scheduling (#138)
* fix: use hidden input for UTC date value in add-game form

The onSubmit handler was setting the datetime-local input's value to an
ISO UTC string (e.g. "2026-03-11T17:00:00.000Z"). datetime-local inputs
only accept the format "YYYY-MM-DDTHH:MM" — values with a 'Z' timezone
suffix are invalid and the browser silently clears the field to "". This
caused the form to submit an empty scheduledAt, which the server treated
as null, so games were always saved without a date.

Fix by keeping the datetime-local input for user interaction (renamed
scheduledAtLocal, not submitted) and writing the converted UTC ISO string
into a separate hidden input named scheduledAt in the onSubmit handler.

Also adds app/lib/date-utils.ts with a localDateTimeToUtcIso helper and
matching tests to document and verify the datetime-local ↔ ISO conversion
behaviour.

https://claude.ai/code/session_0148fgZiXpyvGX8ZX3BFRGCs

* refactor: use localDateTimeToUtcIso in component and clean up tests

- Import and call localDateTimeToUtcIso in the add-game onSubmit handler
  instead of duplicating the conversion inline; also applies the null/
  invalid-date safety from the utility to the component
- Remove the redundant "incompatible with datetime-local" test case (its
  intent is better expressed as a code comment than a test assertion)
- Move the datetime-local incompatibility explanation into a NOTE comment
  on the utility function's JSDoc

https://claude.ai/code/session_0148fgZiXpyvGX8ZX3BFRGCs

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-03-11 16:22:29 -07:00
Chris Parsons
b0960c3c30
feat: improve league home standings and sports seasons UX (#65)
- Sort sports seasons by status (active → upcoming → completed), with
  season_standings pattern last within active, then alphabetical
- Fix rank display for teams without standings: T1 when no scores exist,
  T{n+1} when some teams are scored (avoids misleading T1 for unranked teams)
- Extract rank logic to getDisplayRank() in standings-display.ts with tests
- Pre-compute standingsMap and sortedTeams to eliminate O(n²) render lookups
- Fix invite URL SSR flash by deriving origin in the loader
- Guard toast useEffect with processedParamsRef to prevent double-fire
- Hide Teams Filled and Draft Order Set post-draft; show Standings sidebar
  link only when active/completed
- Show "Final standings" label when season is completed
- Remove duplicate Season Status from sidebar; add Flex Spots description
- Fix status type cast to occur at the model mapping layer, not in JSX

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-07 15:57:51 -08:00
Chris Parsons
7a8ea60e77
Add draft clock UI, Fischer increment timer logic, and security fixes (#19)
- Add prominent clock badge to tab bar (lights up on your turn) with
  correct Fischer increment chess-clock model: bank starts at initialTime,
  += incrementTime after each pick, other teams' banks untouched
- Extract pure timer helpers to app/lib/draft-timer.ts and add 29 unit
  tests covering formatClockTime, calculateTimeAfterPick, getTimerColorClass,
  and full snake-draft lifecycle regression scenarios
- Fix make-pick.ts: add status !== 'draft' and draftPaused server guards
- Fix draft-utils.ts: replace (global as any).__socketIO with getSocketIO(),
  fix timeUsed to store actual timeRemaining at pick moment (not always 120),
  add null timer warning, move timer fetch before pick insert
- Fix draft.start.ts: batch timer inserts, guard against empty draftSlots
- Fix DraftGridSection: memoize currentTeamId, widen teamTimers type to
  Record<string, number | undefined>
- Fix duplicate animate-pulse (getTimerColorClass already includes it)
- Clamp negative seconds in formatClockTime to guard against timer drift

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-02-21 16:51:12 -08:00
Chris Parsons
6ef829f667 feat: add recharts library and implement PointProgressionChart component for historical views
- 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.
2025-11-14 21:18:34 -08:00
Chris Parsons
74aea1677b refactor: update import paths to use absolute paths for better clarity and maintainability 2025-10-24 21:20:19 -07:00
Chris Parsons
decb28dc19 Implement Omni League Draft Eligibility Rules
- 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
2025-10-24 21:12:07 -07:00
Chris Parsons
fdc94b16ac ci: reorganize workflow dependencies and remove standalone test workflow 2025-10-21 12:57:03 -07:00
Chris Parsons
0d4ce1d339 ci: add test job to GitHub workflow and setup testing dependencies 2025-10-21 12:47:11 -07:00