feature: events pages (LAN mini-seasons)#484
Open
Flegma wants to merge 2 commits into
Open
Conversation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Web side of the Events feature (LAN mini-seasons). Design: docs/plans/2026-07-03-events-feature-design.md; implementation plan: docs/plans/2026-07-04-events-feature-implementation-plan.md.
Depends on the api PR (5stackgg/api#337). Merge the api PR first: the regenerated GraphQL types and every query here assume the events schema exists in Hasura.
What's included
generated/zeusregenerated with the events types.graphql/simpleEventFields.ts,composables/useEventContext.ts,utilities/eventDisplay.ts.pages/events/index.vue(list),create.vue,manage.vue, sharedcomponents/events/EventCard.vue,components/events/EventForm.vue.pages/events/[eventId]/index.vue(detail) with tabs: Leaderboard, Standings, Tournaments, Teams, plus an organizer-only Settings tab (edit name/description/dates and move the event between Setup/Live/Finished).components/events/EventLeaderboard.vue(get_event_leaderboard; categories rating/adr/kdr/kills/wins),EventStandings.vue(member-tournament trophies + medal table),EventMembershipPanel.vue(organizer-only attach/detach tournaments/teams/players + import-players-from-rosters).pages/events.vue"coming soon" stub removed.Post-review fixes, round 1 (2026-07-04)
f61cb96create/manage gates aligned with the backend'screate_events_role(neweventCreateRolestore getter); Manage Events list broadened for privileged roles.685f54dtournaments-tab null guard; medal table keeps ad-hoc teams; Min. Rounds blank coerced to 0; removed a wasted fallback query at mount.d8dba9esubscriptionerror()handlers + malformed-uuid guard (no stuck skeleton); breadcrumb no longer shows a raw uuid or literal create/manage segment.Post-review fixes, round 2 (2026-07-07)
A second full-PR review (8 finder angles, per-candidate adversarial verify, then an adversarial regression pass on the fixes, which itself caught and fixed one Minor regression):
2918f87+487782cstandings trophy shapes. Placement trophies are stored as one canonical team row plus one row per roster player, and MVP rows also carry the winning team's id. The standings component treated every row independently: placement slots stacked roster-size + 1 duplicate badges, the medal table credited one tournament win as roster-size + 1 golds, and the MVP slot showed the winning team's name/link instead of the player. Rows now collapse to one entry per team per placement, medal counts dedupe per (tournament, team, placement), MVP is player-scoped, and manually awarded player trophies keep their player link.0a8b956organizer Settings tab (fixes unreachable edit mode). EventForm shipped with a complete edit mode that nothing mounted, so no UI could move an event out of Setup; newly created events stayed hidden from the public forever. The detail page now mounts the form in an organizer-gated Settings tab (mirroring the tournament detail page), andpopulateFromEventcorrectly sets/clears dates.a0b8c79membership panel. The tournament attach search was scoped to tournaments the viewer personally organizes, locking out co-organizers and privileged roles (the server allows attaching any tournament); the roster-import mutation usedon_conflict, which Hasura does not expose without an update permission, so it failed validation for every non-admin organizer.1f13265isRoleAbove fail-closed. An unknown role gate (settings still loading) mapped to indexOf -1 and granted every logged-in user; the Create Event/Tournament buttons flashed for users the server would reject.8c495ccshared EventCard + eventDisplay helpers. The card markup and three display helpers were copy-pasted across the list page, manage page, and detail header; extracted to one component + one utility module. Also drops dead code (unusedisOrganizercontext field, unused imports).Verification
Every task was built and reviewed against an ephemeral TimescaleDB + Hasura v2.49.2 stack loaded with the api events schema and dev fixtures (round-1 details in the commit history). The round-2 fixes were verified by a 4-agent adversarial regression pass: every trophy-row shape traced through both the placement cards and the medal table (7 scenarios), the settings-tab tab-state wiring (
useRouteTabwhitelist), the update mutation against the Hasura update permission columns, the attach-search/import mutations against the insert permissions, an audit of everyisRoleAbovecall site, and the EventCard extraction (i18n keys, no dangling references,simpleEventFieldscovers the card's fields). One regression was found and fixed (487782c). A live typecheck could not be re-run because the checked-in codegen targets the ephemeral schema (see below).MUST DO before merge (needs the real production Hasura)
yarn codegenagainst production Hasura and recommitgenerated/. The committed regen was generated against the ephemeral verification Hasura, which had schema drift: it drops unrelated, unused columns (draft_games.scheduled_at,game_server_nodes.cs2_launch_options), addsplayers.faceit_synced_at, flips nullability onplayer_faceit_rank_history.elo/skill_level, and sets the zeusHOSTconstant tohttp://localhost:58080(dead code, unused by the Apollo client). None affect the events code, and all are restored by the prod re-codegen. Run this AFTER the api PR merges, then merge this PR.nuxi typecheckafter the re-codegen (expect zero new errors over baseline).Notes / follow-ups (not blockers)
pages/tournaments/manage.vueshows all tournaments to tournament_organizer/admin roles via itsis_organizerfilter (pre-existing);events/manage.vuehandles the equivalent explicitly. Worth a ticket to make tournaments consistent.user, and those roles genuinely manage all events; the create gate inside the pages usescreate_events_role).public.create_events_roleyet (DB-only; defaults to any logged-in user until set). Adding a control next tocreate_tournaments_roleis a small follow-up.EventMembershipPanel.vue/EventStandings.vueuse raw gql / loosely-typed$apollo.queryto sidestep a zeusExtractVariablesinference gap (documented workaround).