-
Notifications
You must be signed in to change notification settings - Fork 1
feat(compete): add Game Day timeline for live competition view #159
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add Game Day page with real-time heat schedule and event progress - Show Live Now banner when events are in progress with current/next heat - Display full schedule timeline with continuous vertical line design - Use theme primary color (orange) consistently for live indicators - Responsive layout: horizontal on desktop, stacked on mobile - Add division badges with color coding per division type - Expandable heat schedules showing individual heat times - Add progress bar showing heats completed per event - Fix competition edit form to clear endDate when unchecking multi-day
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds a Game Day feature: a new gameday route and loader, GameDayTimeline component, Progress UI component, sidebar navigation item, shadcn/ui configuration, route type wiring, and a small form behavior change for multi-day competitions. Changes
Sequence Diagram(s)sequenceDiagram
participant Browser as Browser (Client)
participant Route as /gameday Route
participant Loader as Route Loader
participant API as Backend APIs
participant Page as GameDayPage
participant Timeline as GameDayTimeline
participant UI as Timeline UI
Browser->>Route: Navigate to /compete/organizer/{id}/gameday
Route->>Loader: invoke loader
Loader->>API: fetch competition by id
API-->>Loader: competition (with organizingTeamId)
par Parallel data fetches
Loader->>API: fetch venues
API-->>Loader: venues[]
Loader->>API: fetch events/workouts
API-->>Loader: events[]
Loader->>API: fetch heats
API-->>Loader: heats[]
Loader->>API: fetch divisions (with counts)
API-->>Loader: divisions[]
Loader->>API: fetch sponsors (groups & ungrouped)
API-->>Loader: sponsors[]
end
Loader-->>Page: consolidated payload
Page->>Timeline: provide competition, venues, events, heats, divisions, sponsors
Timeline->>Timeline: process data (group heats, compute times/status, compute days)
Timeline->>UI: render header, day tabs, live banners, timeline sections, division legend
UI-->>Timeline: user interactions (change day, expand events)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
1 issue found across 11 files
Prompt for AI agents (all issues)
Check if these issues are valid — if so, understand the root cause of each and fix them.
<file name="apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx">
<violation number="1" location="apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx:89">
P2: Remove debug `console.log` statements before merging. These 6 console.log calls appear to be development artifacts that should not be committed to production code.</violation>
</file>
Reply with feedback, questions, or to request a fix. Tag @cubic-dev-ai to re-run a review.
| // Get competition from parent route | ||
| const { competition } = parentRoute.useLoaderData() | ||
|
|
||
| console.log("competition", competition) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P2: Remove debug console.log statements before merging. These 6 console.log calls appear to be development artifacts that should not be committed to production code.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx, line 89:
<comment>Remove debug `console.log` statements before merging. These 6 console.log calls appear to be development artifacts that should not be committed to production code.</comment>
<file context>
@@ -0,0 +1,106 @@
+ // Get competition from parent route
+ const { competition } = parentRoute.useLoaderData()
+
+ console.log("competition", competition)
+ console.log("venues", venues)
+ console.log("events", events)
</file context>
✅ Addressed in b476900
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (3)
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx (1)
26-34: Consider leveraging parent route context for competition data.The competition is fetched here to obtain
organizingTeamId, but the parent route already loads this. While TanStack Router loaders can't directly access parent loader data, you could potentially use route context or restructure to avoid the duplicate fetch.However, the current approach is functional and follows common TanStack Router patterns.
apps/wodsmith-start/src/components/gameday/gameday-timeline.tsx (2)
159-169: Division color matching uses substring matching.The
label.includes("rx")pattern could match unintended strings (e.g., "Arx" or "proxy"). Consider using word boundary matching if this becomes an issue, though for typical division names like "RX", "Scaled", "Masters" it should work well.🔎 Alternative with stricter matching
function getDivisionColor(divisionLabel: string): string { const label = divisionLabel.toLowerCase() // More precise matching using word boundaries or exact patterns if (/\brx\b/.test(label) || label === "rx") return "bg-rose-500" // ... rest of patterns }
649-654: Consider adding aria-expanded for accessibility.The expandable event sections use a button but don't communicate the expanded state to screen readers.
🔎 Proposed accessibility improvement
<button type="button" onClick={() => setExpandedEvent(isExpanded ? null : eventData.event.id) } className="w-full text-left group" + aria-expanded={isExpanded} >
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (2)
apps/wodsmith-start/src/components/gameday/image.pngis excluded by!**/*.pngpnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (9)
apps/wodsmith-start/components.jsonapps/wodsmith-start/src/components/competition-sidebar.tsxapps/wodsmith-start/src/components/gameday/gameday-timeline.tsxapps/wodsmith-start/src/components/ui/progress.tsxapps/wodsmith-start/src/routeTree.gen.tsapps/wodsmith-start/src/routes/compete/organizer/$competitionId.tsxapps/wodsmith-start/src/routes/compete/organizer/$competitionId/-components/organizer-competition-edit-form.tsxapps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsxapps/wodsmith-start/vite.config.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript everywhere, prefer interfaces over types
Use functional components, avoid classes
Files:
apps/wodsmith-start/vite.config.tsapps/wodsmith-start/src/routes/compete/organizer/$competitionId/-components/organizer-competition-edit-form.tsxapps/wodsmith-start/src/components/competition-sidebar.tsxapps/wodsmith-start/src/routes/compete/organizer/$competitionId.tsxapps/wodsmith-start/src/components/ui/progress.tsxapps/wodsmith-start/src/components/gameday/gameday-timeline.tsxapps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsxapps/wodsmith-start/src/routeTree.gen.ts
🧠 Learnings (3)
📚 Learning: 2025-12-30T22:46:18.054Z
Learnt from: CR
Repo: wodsmith/thewodapp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:46:18.054Z
Learning: Applies to src/**/*.tsx : Use Shadcn UI components from `src/components/ui/`
Applied to files:
apps/wodsmith-start/vite.config.tsapps/wodsmith-start/components.json
📚 Learning: 2025-12-30T22:46:18.054Z
Learnt from: CR
Repo: wodsmith/thewodapp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:46:18.054Z
Learning: Applies to src/**/*.tsx : Implement mobile-first responsive design with Tailwind CSS
Applied to files:
apps/wodsmith-start/components.json
📚 Learning: 2025-12-30T22:46:18.054Z
Learnt from: CR
Repo: wodsmith/thewodapp PR: 0
File: AGENTS.md:0-0
Timestamp: 2025-12-30T22:46:18.054Z
Learning: Applies to src/**/*.tsx : Support both light and dark modes in UI components
Applied to files:
apps/wodsmith-start/components.json
🧬 Code graph analysis (1)
apps/wodsmith-start/src/components/ui/progress.tsx (1)
apps/wodsmith/src/lib/utils.ts (1)
cn(10-12)
🔇 Additional comments (15)
apps/wodsmith-start/vite.config.ts (1)
57-57: LGTM! Correct SSR optimization for the new Progress component.The addition of
@radix-ui/react-progressto the SSR pre-bundle list is consistent with the pattern for other Radix UI components and aligns with the new Progress component introduced in this PR.apps/wodsmith-start/src/routes/compete/organizer/$competitionId/-components/organizer-competition-edit-form.tsx (1)
383-389: Good fix for the multi-day toggle behavior.Clearing
endDatewhen switching from multi-day to single-day competition prevents stale data from being retained and ensures the form state remains consistent with the toggle state. This aligns well with the form validation logic that conditionally requiresendDatefor multi-day competitions.apps/wodsmith-start/components.json (1)
1-21: LGTM! Standard shadcn/ui configuration.The configuration correctly sets up shadcn/ui integration with:
- TypeScript React support (tsx: true)
- Tailwind CSS variables for theming
- Appropriate path aliases for the project structure
- Lucide icons library
This enables the new UI components added in this PR, including the Progress component.
apps/wodsmith-start/src/components/ui/progress.tsx (1)
8-25: LGTM! Well-implemented Progress component.The component correctly wraps Radix UI Progress primitives with:
- Proper ref forwarding using
forwardRef- Correct TypeScript types inferred from ProgressPrimitive
- Smooth transform-based animation for the progress indicator
- Safe fallback to 0 when value is undefined
The transform calculation
translateX(-${100 - (value || 0)}%)correctly creates a left-to-right fill effect for progress visualization.apps/wodsmith-start/src/components/competition-sidebar.tsx (2)
16-16: LGTM! Flame icon added for Game Day navigation.The Flame icon import is correctly added to support the new Game Day navigation item.
87-87: LGTM! Game Day navigation item added.The new navigation item is correctly structured and positioned within the "Run Competition" group. The Flame icon is an appropriate visual indicator for live competition monitoring.
apps/wodsmith-start/src/routes/compete/organizer/$competitionId.tsx (1)
71-71: LGTM!The route label addition follows the established pattern and correctly maps the "gameday" path segment to the "Game Day" breadcrumb label.
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx (1)
1-106: Solid route implementation with parallel data fetching.The loader efficiently fetches all required data in parallel using
Promise.all, and the component correctly accesses both its own loader data and parent route data. The structure follows TanStack Router conventions.apps/wodsmith-start/src/components/gameday/gameday-timeline.tsx (6)
33-78: Well-structured type definitions.The interfaces follow TypeScript best practices and provide clear contracts for the component's data requirements.
258-263: Timer implementation is correct with proper cleanup.The 1-second interval for live time updates is appropriate for a real-time dashboard, and the cleanup function properly clears the interval on unmount.
380-426: Event status functions are well-implemented.The status calculation logic correctly handles edge cases with null checks and provides sensible defaults (15 minutes for unspecified duration). The functions appropriately close over
currentTimestate for real-time updates.
602-605: Progress calculation is safe from division by zero.The
totalHeatsis guaranteed to be at least 1 here because events are filtered to only include those withheats.length > 0on line 333.
246-253: Well-structured component with comprehensive Game Day functionality.The component handles complex requirements including real-time updates, multi-day navigation, live event tracking, and expandable heat schedules. The use of
useMemofor expensive computations and proper effect cleanup demonstrates good React patterns.
346-357: Potential infinite loop if competition dates are invalid.If
competition.endDateis beforecompetition.startDate(due to data corruption or edge case), thewhile (current <= end)loop would never terminate. Consider adding a guard or maximum iteration limit.🔎 Proposed safeguard
const competitionDays = useMemo(() => { const days: Date[] = [] let current = startOfUTCDay(new Date(competition.startDate)) const end = startOfUTCDay(new Date(competition.endDate)) + // Guard against invalid date ranges + if (current > end) { + return [current] + } + + const maxDays = 30 // Reasonable limit for competitions while (current <= end) { days.push(current) current = addUTCDays(current, 1) + if (days.length >= maxDays) break } return days }, [competition.startDate, competition.endDate])Likely an incorrect or invalid review comment.
apps/wodsmith-start/src/routeTree.gen.ts (1)
1-10: Auto-generated file - no review required.This file is automatically generated by TanStack Router and the changes correctly reflect the new gameday route addition. The route is properly wired under the competition organizer path.
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx
Outdated
Show resolved
Hide resolved
| onCheckedChange={(checked) => { | ||
| field.onChange(checked) | ||
| // Clear endDate when switching from multi-day to single-day | ||
| if (!checked) { | ||
| form.setValue("endDate", "") | ||
| } | ||
| }} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sets the end date to empty if switching to a single day comp
🚀 Preview DeployedURL: https://wodsmith-app-pr-159.zacjones93.workers.dev
This comment is automatically updated on each push to this PR. |
- Remove debug console.log statements from gameday route - Add defensive check for division by zero in Progress component - Fix inconsistent heat end time calculation (use 15-min default) - Add useEffect to reset selectedDayIndex when competitionDays changes
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx (1)
28-34: Consider accessing parent loader data to avoid duplicate competition fetch.The parent route already fetches the competition, but this loader re-fetches it to obtain
organizingTeamId. While TanStack Router child loaders don't have straightforward synchronous access to parent data during execution, you could explore patterns like dependency injection through context or restructuring the data flow to reduce redundant queries.That said, the current approach is defensive, simple, and maintainable—the duplicate fetch is a fast ID lookup and ensures data consistency.
📜 Review details
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (2)
apps/wodsmith-start/src/components/gameday/gameday-timeline.tsxapps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/wodsmith-start/src/components/gameday/gameday-timeline.tsx
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
**/*.{ts,tsx}: Use TypeScript everywhere, prefer interfaces over types
Use functional components, avoid classes
Files:
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx
🧬 Code graph analysis (1)
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx (3)
apps/wodsmith-start/src/routes/compete/organizer/$competitionId.tsx (1)
Route(23-64)apps/wodsmith-start/src/server-fns/competition-heats-fns.ts (2)
getCompetitionVenuesFn(411-425)getHeatsForCompetitionFn(219-405)apps/wodsmith-start/src/server-fns/sponsor-fns.ts (1)
getCompetitionSponsorsFn(172-208)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
- GitHub Check: Deploy to Cloudflare Workers
- GitHub Check: cubic · AI code reviewer
- GitHub Check: build
- GitHub Check: e2e (1, 2)
- GitHub Check: e2e (2, 2)
🔇 Additional comments (4)
apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx (4)
1-20: LGTM! Clean imports and route setup.The file documentation is clear, imports are well-organized, and the parent route API pattern is correct for accessing competition data from the parent loader.
37-71: LGTM! Efficient parallel data fetching and proper sponsor flattening.The use of
Promise.allfor parallel fetches is a good performance pattern, and the sponsor flattening logic correctly consolidates grouped and ungrouped sponsors as expected by theGameDayTimelinecomponent.
73-79: LGTM! Return payload correctly structured for GameDayTimeline.The loader return shape properly maps to the
GameDayTimelinecomponent's expected props, with appropriate extraction from server function results.
83-99: LGTM! Clean functional component following best practices.The component correctly uses TanStack Router hooks to access both child and parent loader data, then passes everything to the
GameDayTimelinecomponent. The past review comments about debugconsole.logstatements have been successfully addressed—none are present in the current code.
| if (!competition) { | ||
| throw new Error("Competition not found") | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Use notFound() for consistency with parent route error handling.
The parent route uses notFound() when a competition is not found (see parent route loader in the relevant snippets). For consistency and proper HTTP status code handling, use the same pattern here.
🔎 Proposed fix
+import { notFound } from "@tanstack/react-router"
+
// ... existing imports ...
export const Route = createFileRoute(
"/compete/organizer/$competitionId/gameday",
)({
component: GameDayPage,
loader: async ({ params }) => {
// First get competition to know the teamId
const { competition } = await getCompetitionByIdFn({
data: { competitionId: params.competitionId },
})
if (!competition) {
- throw new Error("Competition not found")
+ throw notFound()
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| if (!competition) { | |
| throw new Error("Competition not found") | |
| } | |
| import { notFound } from "@tanstack/react-router" | |
| // ... existing imports ... | |
| export const Route = createFileRoute( | |
| "/compete/organizer/$competitionId/gameday", | |
| )({ | |
| component: GameDayPage, | |
| loader: async ({ params }) => { | |
| // First get competition to know the teamId | |
| const { competition } = await getCompetitionByIdFn({ | |
| data: { competitionId: params.competitionId }, | |
| }) | |
| if (!competition) { | |
| throw notFound() | |
| } |
🤖 Prompt for AI Agents
In apps/wodsmith-start/src/routes/compete/organizer/$competitionId/gameday.tsx
around lines 32-34, replace the explicit throw new Error("Competition not
found") with a call to notFound() to match the parent route's error handling;
ensure you import notFound from the same location the parent route uses (add the
import if missing) so the loader returns the standardized 404 response instead
of throwing a generic Error.
Summary
Adds a new Game Day page for competition organizers to monitor live competition progress with real-time heat schedules and event status.
Features
Design
Other Changes
endDatewhen unchecking "Multi-day competition"@radix-ui/react-progressto Vite SSR optimizeDepsScreenshots
The Game Day page shows:
Summary by cubic
Adds a Game Day timeline to the organizer view to track live competition progress with real-time heats and a clear schedule. Improves visibility with a Live Now banner, day tabs, and division-coded timeline.
New Features
Bug Fixes
Written for commit b476900. Summary will update on new commits.
Summary by CodeRabbit
New Features
Bug Fixes
✏️ Tip: You can customize this high-level summary in your review settings.