Commit graph

2 commits

Author SHA1 Message Date
Chris Parsons
4a875e7628
fix: league settings sports swap and audit log detail improvements (#294)
- Fix sports seasons not updating when saving league settings. The form
  submits intent="update" but sports were only handled under the dead
  intent="update-sports" branch, which nothing called.

- Fix spurious audit log entries (scoring rules, draft settings) firing
  on every settings save. Change detection now compares submitted values
  against current DB values before adding to seasonUpdates.

- Add previousValues to scoring_rules_changed and draft_settings_changed
  audit log entries so the detail formatter can show old → new diffs
  (e.g. "Scoring updated: 1st: 100 → 105").

- Add sports_changed audit action (migration 0076) with sport names
  resolved at log time. Batch-fetch added sports in one query instead
  of N individual queries.

- Improve audit log display for all action types: field-level old→new
  diffs, readable labels, truncated sport lists (+N more).

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-14 21:41:05 -07:00
Chris Parsons
442b392461
Add audit logging for commissioner actions (#293)
Closes #144

* feat: add commissioner audit log for league transparency (issue #144)

Adds a complete audit log system so league members can verify that
settings, draft order, picks, and time banks have not been quietly
changed without their awareness.

Changes:
- database/schema.ts: new `audit_action` enum + `commissioner_audit_log`
  table (seasonId, leagueId, actorClerkId, actorDisplayName, action,
  affectedTeamIds[], details jsonb, createdAt)
- drizzle/0075: generated migration for the new table
- app/models/audit-log.ts: createAuditLogEntry, getAuditLogForSeason
  (paginated), logCommissionerAction (resolves display name automatically)
- app/lib/audit-log-display.ts: shared formatAuditDetail() helper used by
  both the league home widget and the full audit log page
- app/routes/leagues/$leagueId.audit-log.tsx: new read-only route at
  /leagues/:id/audit-log, accessible to all league members, with
  action-type filter and pagination
- app/routes.ts: registers the new route
- League home page ($leagueId.server.ts / $leagueId.tsx): "Recent Activity"
  summary card showing the last 5 entries with "View all" link
- Settings page ($leagueId.settings.tsx): "View Full Audit Log" link card;
  audit log calls added for league/draft settings changes, draft order
  set/randomized, and draft reset
- API routes: audit log calls added to draft.start, draft.pause,
  draft.resume, draft.rollback, draft.adjust-time-bank, draft.force-autopick,
  draft.force-manual-pick, draft.replace-pick
- Tests: 11 new unit tests for the audit-log model; mocks added to 3
  existing route test files to account for the new logCommissionerAction call

https://claude.ai/code/session_01NdiwK2fbtKhAD3XuD58fTm

* fix: validate action filter URL param against known enum values

The action filter on the audit log route was cast directly from the URL
search param to AuditAction without validation. An invalid value would
be passed into the Drizzle inArray() call, potentially throwing a
PostgreSQL enum type error. Now validates against the actual enum values
before using the filter.

https://claude.ai/code/session_01NdiwK2fbtKhAD3XuD58fTm

* Fix lint errors: use !== instead of != and toSorted instead of sort

https://claude.ai/code/session_01NdiwK2fbtKhAD3XuD58fTm

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-04-13 15:45:39 -07:00