Skip to content

Conversation

@zacjones93
Copy link
Contributor

@zacjones93 zacjones93 commented Jan 24, 2026

Summary

  • Add glass panel styling (backdrop blur, transparent backgrounds, subtle borders) to competition hero, tabs, content panels, and sidebar
  • Update color scheme from teal to orange for brand consistency
  • Add pill-style navigation tabs with icons across all competition sub-routes
  • Apply consistent rounded-xl styling to buttons throughout the app
  • Add unified layout with tabs and sidebar to workouts, schedule, and leaderboard pages
  • Remove duplicate Register button from tabs (already in sidebar)

Test plan

  • Navigate to a competition detail page and verify glassmorphism styling on hero and content panels
  • Click through all tabs (Event Details, Workouts, Schedule, Leaderboard) and verify consistent styling
  • Verify sidebar cards have glass styling and remain sticky on scroll
  • Check buttons have consistent rounded-xl styling
  • Test on both light and dark modes
  • Verify mobile responsiveness

🤖 Generated with Claude Code


Summary by cubic

Adds glassmorphism panels and pill-style icon tabs to competition pages, updates the brand color to orange, and standardizes rounded‑xl buttons. Unifies layouts across Event Details, Workouts, Schedule, and Leaderboard with sticky tabs and glass content panels, and removes the duplicate Register button.

  • New Features
    • Glass panels (backdrop blur, translucent backgrounds, subtle borders) applied to hero, tabs, content, and sidebars where used.
    • Pill-style tabs with icons; tabs moved into overview and each sub-route and are sticky.
    • Workout details moved to /workouts/$eventId; metadata/resources/judge sheets live in a sidebar on the workout details page.
    • Buttons now use rounded‑xl consistently; share/manage styles aligned to the orange theme.
    • Color scheme updated from teal to orange; removed Register button from tabs (kept in sidebar).

Written for commit ddfd72f. Summary will update on new commits.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added dedicated workout detail pages with improved navigation and metadata display
  • UI/UX Improvements

    • Redesigned competition pages with modern glassmorphism styling and backdrop effects
    • Replaced text tabs with icon-based navigation for clearer visual hierarchy
    • Implemented two-column layouts across competition views for better content organization
    • Updated color scheme with orange accent highlights
    • Enhanced card styling with solid backgrounds and refined borders
  • Navigation

    • Simplified competition navigation by streamlining registration flow from tabs
    • Reorganized route structure for improved workout accessibility

✏️ Tip: You can customize this high-level summary in your review settings.

- Add glass panel styling to hero, tabs, content, and sidebar
- Update color scheme from teal to orange for consistency
- Add pill-style navigation tabs with icons
- Apply consistent rounded-xl styling to buttons
- Add tabs and sidebar to all tabbed sub-routes (workouts, schedule, leaderboard)
- Remove duplicate Register button from tabs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 24, 2026

Walkthrough

The PR refactors the competition UI with a glassmorphic design theme, removes registration-related functionality from CompetitionTabs, restructures the workouts route into index and detail views, reorganizes page layouts to include sticky CompetitionTabs headers with two-column grids, and updates color theming from teal to orange throughout.

Changes

Cohort / File(s) Summary
Glassmorphic styling updates
apps/wodsmith-start/src/components/competition-hero.tsx, apps/wodsmith-start/src/components/registration-sidebar.tsx
Replaced gradient and image-based backgrounds with solid colors, backdrop blur effects, and rounded corners to align with glassmorphic design system. Updated border and shadow styling accordingly.
CompetitionTabs refactor
apps/wodsmith-start/src/components/competition-tabs.tsx
Removed all registration-related props (isRegistered, registrationOpen, etc.); introduced icon-enabled tabs with rounded styling; simplified component interface to accept only slug parameter.
Button styling updates
apps/wodsmith-start/src/components/ui/button.tsx
Updated border-radius from rounded-md to rounded-xl across base and size variants (sm, lg).
Route path and structure changes
apps/wodsmith-start/src/routeTree.gen.ts, apps/wodsmith-start/src/routes/compete/$slug/workouts/$eventId.tsx, apps/wodsmith-start/src/routes/compete/$slug/workouts/index.tsx, apps/wodsmith-start/src/components/competition-workout-card.tsx
Split workouts route from singular to index and eventId variants; migrated route paths from /events/$eventId to /workouts/$eventId; updated route tree exports and mappings.
Layout restructuring with CompetitionTabs
apps/wodsmith-start/src/routes/compete/$slug/index.tsx, apps/wodsmith-start/src/routes/compete/$slug/leaderboard.tsx, apps/wodsmith-start/src/routes/compete/$slug/schedule.tsx, apps/wodsmith-start/src/routes/compete/$slug/workouts/$eventId.tsx
Added sticky CompetitionTabs headers and converted layouts to two-column grids (main content + sidebar); adjusted container padding and positioning. EventDetailsContent now accepts workoutsContent prop.
Page reorganization
apps/wodsmith-start/src/routes/compete/$slug.tsx
Removed CompetitionTabs rendering and registration data (userRegistration, registrationStatus) from loader; adjusted container padding.
Color theme migration
apps/wodsmith-start/src/components/schedule-page-content.tsx
Updated color classes from teal-500 variants to orange-500 variants across borders, backgrounds, badges, and text elements; fixed padStart formatting.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Possibly related PRs

Poem

🐰✨ Tabs now dance with icons bright,
Glass and blur blend day with night,
Workouts leap to orange sheen,
Routes split where paths convene,
Layouts bloom in grids sublime! 🌟

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main objective of the PR: adding glassmorphism styling to competition detail pages, which is reflected throughout the changes in hero, tabs, buttons, panels, and sidebar components.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No issues found across 9 files

@github-actions
Copy link

github-actions bot commented Jan 24, 2026

🚀 Preview Deployed

URL: https://wodsmith-app-pr-210.zacjones93.workers.dev

Detail Value
Commit a3797fa
Stage pr-210
Deployed 2026-01-25T06:31:33.840Z

This comment is automatically updated on each push to this PR.

theianjones and others added 2 commits January 25, 2026 00:26
- Add glass panel styling to hero, tabs, content, and sidebar
- Update color scheme from teal to orange for consistency
- Add pill-style navigation tabs with icons
- Apply consistent rounded-xl styling to buttons
- Add tabs and sidebar to all tabbed sub-routes (workouts, schedule, leaderboard)
- Remove duplicate Register button from tabs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@zacjones93 zacjones93 force-pushed the zac/update-comp-detail-styles branch from 5b532b8 to 0965b64 Compare January 25, 2026 05:51
- Change border and background colors from teal to orange for consistency across the schedule page components.
- Update text colors to match the new orange theme for better visibility and aesthetics.
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
apps/wodsmith-start/src/components/competition-hero.tsx (1)

26-37: Restore a fallback background for banner-less competitions.

With text-white on the hero, dropping the fallback backdrop can hurt contrast in light mode when there’s no banner image. Consider keeping a subtle dark/gradient background for the no-banner case.

💡 Suggested fallback background
-			{competition.bannerImageUrl ? (
+			{competition.bannerImageUrl ? (
 				<>
 					<img
 						src={competition.bannerImageUrl}
 						alt=""
 						className="absolute inset-0 h-full w-full object-cover"
 					/>
 					{/* Overlay for text readability */}
 					<div className="absolute inset-0 bg-gradient-to-t from-slate-900/90 via-slate-900/60 to-slate-900/40" />
 				</>
-			) : null}
+			) : (
+				<div className="absolute inset-0 bg-gradient-to-t from-slate-900/80 via-slate-900/60 to-slate-900/40" />
+			)}
🧹 Nitpick comments (1)
apps/wodsmith-start/src/components/ui/button.tsx (1)

23-27: Redundant rounded-xl in size variants.

Since rounded-xl is already in the base string (line 8), specifying it again in the sm and lg size variants is unnecessary. Tailwind handles duplicates gracefully, but removing them improves clarity.

✨ Suggested cleanup
 		size: {
 			default: "h-10 px-4 py-2",
-			sm: "h-9 rounded-xl px-3",
-			lg: "h-11 rounded-xl px-8",
+			sm: "h-9 px-3",
+			lg: "h-11 px-8",
 			icon: "h-10 w-10",
 		},

…port

- Move event details route from /events/$eventId to /workouts/$eventId
- Add light mode support to competition hero component
- Update workout details page to match workouts list layout
- Add glassmorphism container with header and division switcher
- Move metadata, resources, and judge sheets to sidebar

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@theianjones theianjones force-pushed the zac/update-comp-detail-styles branch from fbe5c47 to 7320f9a Compare January 25, 2026 06:08
zacjones93 and others added 2 commits January 24, 2026 23:15
Resolved conflicts by keeping lightmode conditional styling
(hasBanner checks) while maintaining glassmorphism styles.
Added RegistrationSidebar grid layout to inner pages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Sidebar grid layout now only on workouts/$eventId.tsx.
Leaderboard, schedule, and workouts index use simple layout.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Fix all issues with AI agents
In `@apps/wodsmith-start/src/routes/compete/`$slug/workouts/$eventId.tsx:
- Around line 361-399: The comparison that calls .getTime() on heatTimes fields
can fail if those fields are serialized strings; update the conditional that
checks heatTimes.firstHeatStartTime.getTime() !==
heatTimes.lastHeatEndTime.getTime() to instead normalize both values with new
Date(...) before calling getTime (e.g. new
Date(heatTimes.firstHeatStartTime).getTime() !== new
Date(heatTimes.lastHeatEndTime).getTime()), and ensure you do this consistently
wherever you compare or compute with heatTimes timestamps (formatHeatTime
already wraps dates similarly).
🧹 Nitpick comments (3)
apps/wodsmith-start/src/components/competition-hero.tsx (1)

6-6: Standardize the cn import path across the component.

Both @/lib/utils and @/utils/cn are valid, but the codebase currently has mixed usage—some files import from @/lib/utils while many others use @/utils/cn. Choose one and apply consistently. For new components being added to this app, consider aligning with the existing convention to reduce maintenance burden.

apps/wodsmith-start/src/routes/compete/$slug/workouts/index.tsx (2)

87-109: Consider batching division-description fetches to avoid N+1 calls.
One server call per workout can add noticeable latency on larger competitions; a batched server fn keyed by workoutIds would scale better.


144-155: Clamp selectedDivisionId to known divisions.
A stale division search param can leave the Select in an invalid state and fallback descriptions in cards.

♻️ Suggested guard
- const selectedDivisionId = search.division || defaultDivisionId
+ const selectedDivisionId =
+ 	divisions?.some((division) => division.id === search.division)
+ 		? search.division
+ 		: defaultDivisionId

Comment on lines +361 to +399
{/* Schedule Card */}
{heatTimes && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Schedule</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div className="flex items-start gap-3">
<Trophy className="h-4 w-4 text-muted-foreground mt-0.5" />
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Points Multiplier
First Heat Starts
</p>
<p className="font-medium text-sm">
{event.pointsMultiplier / 100}x
{formatHeatTime(
heatTimes.firstHeatStartTime,
competition.timezone,
)}
</p>
</div>
</div>
)}
</CardContent>
</Card>

{/* Schedule Card */}
{heatTimes && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Schedule</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
{heatTimes.firstHeatStartTime.getTime() !==
heatTimes.lastHeatEndTime.getTime() && (
<div className="flex items-start gap-3">
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
First Heat Starts
Last Heat Ends
</p>
<p className="font-medium text-sm">
{formatHeatTime(
heatTimes.firstHeatStartTime,
heatTimes.lastHeatEndTime,
competition.timezone,
)}
</p>
</div>
</div>
{heatTimes.firstHeatStartTime.getTime() !==
heatTimes.lastHeatEndTime.getTime() && (
<div className="flex items-start gap-3">
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Last Heat Ends
</p>
<p className="font-medium text-sm">
{formatHeatTime(
heatTimes.lastHeatEndTime,
competition.timezone,
)}
</p>
</div>
</div>
)}
</div>
<Separator />
<p className="text-xs text-muted-foreground">
Timezone: {competition.timezone}
</p>
</CardContent>
</Card>
)}

{/* Event Resources Card */}
{resources && resources.length > 0 && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Event Resources</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-3">
{resources.map((resource) => (
<li key={resource.id}>
{resource.url ? (
<a
href={resource.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 text-sm hover:text-primary transition-colors group"
>
<Link className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
<span className="flex-1">{resource.title}</span>
<ExternalLink className="h-3 w-3 text-muted-foreground" />
</a>
) : (
<div className="flex items-center gap-3 text-sm">
<FileText className="h-4 w-4 text-muted-foreground" />
<span className="flex-1">{resource.title}</span>
</div>
)}
</li>
))}
</ul>
</CardContent>
</Card>
)}

{/* Judge Sheets Card */}
{judgingSheets && judgingSheets.length > 0 && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Judge Sheets</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-3">
{judgingSheets.map((sheet) => (
<li key={sheet.id}>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# First, let's find the file and understand its context
find apps/wodsmith-start -name "$eventId.tsx" -type f 2>/dev/null | head -5

Repository: wodsmith/thewodapp

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Search for getPublicEventDetailsFn to understand the return type
rg -n "getPublicEventDetailsFn" apps/wodsmith-start/src --type ts --type tsx -A 5

Repository: wodsmith/thewodapp

Length of output: 89


🏁 Script executed:

#!/bin/bash
# Search for heatTimes type definition and usage
rg -n "heatTimes" apps/wodsmith-start/src --type ts --type tsx -B 2 -A 2

Repository: wodsmith/thewodapp

Length of output: 89


🏁 Script executed:

#!/bin/bash
# Find the specific file
fd "$eventId.tsx" apps/wodsmith-start

Repository: wodsmith/thewodapp

Length of output: 22097


🏁 Script executed:

#!/bin/bash
# Search for getPublicEventDetailsFn without type filter
rg "getPublicEventDetailsFn" apps/wodsmith-start/src -A 5 | head -50

Repository: wodsmith/thewodapp

Length of output: 1963


🏁 Script executed:

#!/bin/bash
# Search for heatTimes more broadly
rg "heatTimes" apps/wodsmith-start/src -B 2 -A 2 | head -80

Repository: wodsmith/thewodapp

Length of output: 5224


🏁 Script executed:

#!/bin/bash
# List the directory structure to find server-fns
fd "server" apps/wodsmith-start/src -type d

Repository: wodsmith/thewodapp

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Get more context around the heatTimes definition and return statement
rg -n "heatTimes" apps/wodsmith-start/src/server-fns/competition-workouts-fns.ts -B 5 -A 5

Repository: wodsmith/thewodapp

Length of output: 1569


🏁 Script executed:

#!/bin/bash
# Check the actual file to see the return type annotation
cat apps/wodsmith-start/src/server-fns/competition-workouts-fns.ts | head -100

Repository: wodsmith/thewodapp

Length of output: 2935


🏁 Script executed:

#!/bin/bash
# Search for the return statement and type signature
rg "return {" apps/wodsmith-start/src/server-fns/competition-workouts-fns.ts -A 5

Repository: wodsmith/thewodapp

Length of output: 141


🏁 Script executed:

#!/bin/bash
# Read the component file around the problematic lines
sed -n '1,50p' apps/wodsmith-start/src/routes/compete/\$slug/workouts/\$eventId.tsx

Repository: wodsmith/thewodapp

Length of output: 1664


🏁 Script executed:

#!/bin/bash
# Read around lines 340-410 to see the context
sed -n '340,410p' apps/wodsmith-start/src/routes/compete/\$slug/workouts/\$eventId.tsx

Repository: wodsmith/thewodapp

Length of output: 2218


🏁 Script executed:

#!/bin/bash
# Look for the loader function and how data is handled
sed -n '1,150p' apps/wodsmith-start/src/routes/compete/\$slug/workouts/\$eventId.tsx

Repository: wodsmith/thewodapp

Length of output: 4563


🏁 Script executed:

#!/bin/bash
# Search for date serialization or transformation
rg "deserialize|serialize|toJSON|fromJSON" apps/wodsmith-start/src --max-count 20

Repository: wodsmith/thewodapp

Length of output: 163


🏁 Script executed:

#!/bin/bash
# Check the server function configuration
rg "createServerFn" apps/wodsmith-start/src/server-fns -A 10 | head -60

Repository: wodsmith/thewodapp

Length of output: 5117


🏁 Script executed:

#!/bin/bash
# Look for any middleware or config for date handling
fd "config" apps/wodsmith-start/src -type f | head -10

Repository: wodsmith/thewodapp

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Look for entry.server file or main config
fd "entry\|config\|main" apps/wodsmith-start/src --max-depth 3 -type f | grep -E "\.(ts|tsx)$" | head -15

Repository: wodsmith/thewodapp

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Check if there's any type transformation in the loader context
rg "loader:" apps/wodsmith-start/src/routes/compete/\$slug/workouts/\$eventId.tsx -A 50 | head -80

Repository: wodsmith/thewodapp

Length of output: 1558


🏁 Script executed:

#!/bin/bash
# Search for Date-related type definitions or utilities
fd "date\|Date" apps/wodsmith-start/src/lib --max-depth 2

Repository: wodsmith/thewodapp

Length of output: 44


🏁 Script executed:

#!/bin/bash
# Look at the actual formatHeatTime usage pattern more carefully
rg "formatHeatTime" apps/wodsmith-start/src/routes/compete/\$slug/workouts/\$eventId.tsx -B 3 -A 3

Repository: wodsmith/thewodapp

Length of output: 688


🏁 Script executed:

#!/bin/bash
# Double-check: search for other uses of .getTime() in the codebase to see if there's a pattern
rg "\.getTime\(\)" apps/wodsmith-start/src/routes --max-count 15

Repository: wodsmith/thewodapp

Length of output: 1621


🏁 Script executed:

#!/bin/bash
# Check if TanStack Start has any automatic date deserialization
fd "package.json" apps/wodsmith-start | head -1 | xargs cat | grep -A 5 -B 5 "react-start\|tanstack"

Repository: wodsmith/thewodapp

Length of output: 1067


Guard heat-time values before calling .getTime().
When heatTimes fields are serialized through the server boundary (TanStack Start converts Date objects to JSON strings), calling .getTime() directly will throw a TypeError. Wrap dates in new Date() for safe comparison, consistent with how formatHeatTime() already handles serialization.

🐛 Proposed fix
-				{heatTimes.firstHeatStartTime.getTime() !==
-					heatTimes.lastHeatEndTime.getTime() && (
+				{new Date(heatTimes.firstHeatStartTime).getTime() !==
+					new Date(heatTimes.lastHeatEndTime).getTime() && (
📝 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.

Suggested change
{/* Schedule Card */}
{heatTimes && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Schedule</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div className="flex items-start gap-3">
<Trophy className="h-4 w-4 text-muted-foreground mt-0.5" />
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Points Multiplier
First Heat Starts
</p>
<p className="font-medium text-sm">
{event.pointsMultiplier / 100}x
{formatHeatTime(
heatTimes.firstHeatStartTime,
competition.timezone,
)}
</p>
</div>
</div>
)}
</CardContent>
</Card>
{/* Schedule Card */}
{heatTimes && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Schedule</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
{heatTimes.firstHeatStartTime.getTime() !==
heatTimes.lastHeatEndTime.getTime() && (
<div className="flex items-start gap-3">
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
First Heat Starts
Last Heat Ends
</p>
<p className="font-medium text-sm">
{formatHeatTime(
heatTimes.firstHeatStartTime,
heatTimes.lastHeatEndTime,
competition.timezone,
)}
</p>
</div>
</div>
{heatTimes.firstHeatStartTime.getTime() !==
heatTimes.lastHeatEndTime.getTime() && (
<div className="flex items-start gap-3">
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Last Heat Ends
</p>
<p className="font-medium text-sm">
{formatHeatTime(
heatTimes.lastHeatEndTime,
competition.timezone,
)}
</p>
</div>
</div>
)}
</div>
<Separator />
<p className="text-xs text-muted-foreground">
Timezone: {competition.timezone}
</p>
</CardContent>
</Card>
)}
{/* Event Resources Card */}
{resources && resources.length > 0 && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Event Resources</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-3">
{resources.map((resource) => (
<li key={resource.id}>
{resource.url ? (
<a
href={resource.url}
target="_blank"
rel="noopener noreferrer"
className="flex items-center gap-3 text-sm hover:text-primary transition-colors group"
>
<Link className="h-4 w-4 text-muted-foreground group-hover:text-primary" />
<span className="flex-1">{resource.title}</span>
<ExternalLink className="h-3 w-3 text-muted-foreground" />
</a>
) : (
<div className="flex items-center gap-3 text-sm">
<FileText className="h-4 w-4 text-muted-foreground" />
<span className="flex-1">{resource.title}</span>
</div>
)}
</li>
))}
</ul>
</CardContent>
</Card>
)}
{/* Judge Sheets Card */}
{judgingSheets && judgingSheets.length > 0 && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Judge Sheets</CardTitle>
</CardHeader>
<CardContent>
<ul className="space-y-3">
{judgingSheets.map((sheet) => (
<li key={sheet.id}>
)}
{/* Schedule Card */}
{heatTimes && (
<Card>
<CardHeader className="pb-3">
<CardTitle className="text-lg">Schedule</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-3">
<div className="flex items-start gap-3">
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
First Heat Starts
</p>
<p className="font-medium text-sm">
{formatHeatTime(
heatTimes.firstHeatStartTime,
competition.timezone,
)}
</p>
</div>
</div>
{new Date(heatTimes.firstHeatStartTime).getTime() !==
new Date(heatTimes.lastHeatEndTime).getTime() && (
<div className="flex items-start gap-3">
<Clock className="h-4 w-4 text-muted-foreground mt-0.5" />
<div>
<p className="text-xs text-muted-foreground uppercase tracking-wider">
Last Heat Ends
</p>
<p className="font-medium text-sm">
{formatHeatTime(
heatTimes.lastHeatEndTime,
competition.timezone,
)}
</p>
</div>
</div>
)}
🤖 Prompt for AI Agents
In `@apps/wodsmith-start/src/routes/compete/`$slug/workouts/$eventId.tsx around
lines 361 - 399, The comparison that calls .getTime() on heatTimes fields can
fail if those fields are serialized strings; update the conditional that checks
heatTimes.firstHeatStartTime.getTime() !== heatTimes.lastHeatEndTime.getTime()
to instead normalize both values with new Date(...) before calling getTime (e.g.
new Date(heatTimes.firstHeatStartTime).getTime() !== new
Date(heatTimes.lastHeatEndTime).getTime()), and ensure you do this consistently
wherever you compare or compute with heatTimes timestamps (formatHeatTime
already wraps dates similarly).

…ility

- Adjusted indentation and spacing for consistency across the component.
- Enhanced readability by aligning props and return statements.
- Maintained existing functionality while improving code structure.
…layout

- Reduced vertical padding in the content area from 8 to 4 for a more compact design.
- Maintained overall structure and functionality of the CompetitionDetailLayout component.
Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

🤖 Fix all issues with AI agents
In `@apps/wodsmith-start/src/components/competition-hero.tsx`:
- Around line 106-118: The Manage action currently nests a Button inside an
anchor which is invalid; change it to render the link as the button using the
Button component's asChild prop: replace the <a
href={`/compete/organizer/${competition.id}`}> wrapper and instead pass asChild
to Button and move the href into a child Link/Anchor element (or directly render
an <a> as the Button child) so the Button (component name "Button") becomes the
interactive anchor for the Manage action while keeping the Settings icon and
classNames unchanged and still conditioned on canManage and competition.id.
🧹 Nitpick comments (1)
apps/wodsmith-start/src/routes/compete/$slug/workouts/index.tsx (1)

146-163: Reduce duplicated wrapper markup in the empty/non-empty branches. Both branches repeat the same sticky tabs + glass container. Consider rendering the wrapper once and swapping only the inner content to avoid drift.

Also applies to: 168-172

Comment on lines +106 to +118
<div className="flex shrink-0 items-center gap-2">
{canManage && (
<a href={`/compete/organizer/${competition.id}`}>
<Button
variant="secondary"
size="sm"
className="bg-orange-600 text-white hover:bg-orange-500"
>
<Settings className="mr-1 h-4 w-4" />
Manage
</Button>
</a>
)}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the Button component in the codebase
fd -a 'button\.tsx' apps/wodsmith-start/src/components/ui -type f

Repository: wodsmith/thewodapp

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Check the competition-hero.tsx file to confirm current state
cat -n apps/wodsmith-start/src/components/competition-hero.tsx | head -150

Repository: wodsmith/thewodapp

Length of output: 7190


🏁 Script executed:

#!/bin/bash
# Verify the import path "@/lib/utils" exists
ls -la apps/wodsmith-start/src/lib/utils* 2>&1 || echo "Path check result above"

Repository: wodsmith/thewodapp

Length of output: 147


🏁 Script executed:

#!/bin/bash
# Find the Button component (correct fd syntax)
fd 'button\.tsx' apps/wodsmith-start/src/components/ui

Repository: wodsmith/thewodapp

Length of output: 110


🏁 Script executed:

#!/bin/bash
# Read the Button component file
find apps/wodsmith-start/src/components/ui -name 'button.tsx' -exec cat -n {} \;

Repository: wodsmith/thewodapp

Length of output: 2292


🏁 Script executed:

#!/bin/bash
# Verify cn is exported from utils
cat -n apps/wodsmith-start/src/lib/utils.ts

Repository: wodsmith/thewodapp

Length of output: 4275


Avoid nesting interactive elements for the Manage action.
Wrapping a Button inside an <a> tag is invalid HTML and breaks accessibility/navigation. Use the Button's asChild prop to render the link as the button itself.

Suggested fix
-                  {canManage && (
-                    <a href={`/compete/organizer/${competition.id}`}>
-                      <Button
-                        variant="secondary"
-                        size="sm"
-                        className="bg-orange-600 text-white hover:bg-orange-500"
-                      >
-                        <Settings className="mr-1 h-4 w-4" />
-                        Manage
-                      </Button>
-                    </a>
-                  )}
+                  {canManage && (
+                    <Button
+                      asChild
+                      variant="secondary"
+                      size="sm"
+                      className="bg-orange-600 text-white hover:bg-orange-500"
+                    >
+                      <a href={`/compete/organizer/${competition.id}`}>
+                        <Settings className="mr-1 h-4 w-4" />
+                        Manage
+                      </a>
+                    </Button>
+                  )}
📝 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.

Suggested change
<div className="flex shrink-0 items-center gap-2">
{canManage && (
<a href={`/compete/organizer/${competition.id}`}>
<Button
variant="secondary"
size="sm"
className="bg-orange-600 text-white hover:bg-orange-500"
>
<Settings className="mr-1 h-4 w-4" />
Manage
</Button>
</a>
)}
<div className="flex shrink-0 items-center gap-2">
{canManage && (
<Button
asChild
variant="secondary"
size="sm"
className="bg-orange-600 text-white hover:bg-orange-500"
>
<a href={`/compete/organizer/${competition.id}`}>
<Settings className="mr-1 h-4 w-4" />
Manage
</a>
</Button>
)}
🤖 Prompt for AI Agents
In `@apps/wodsmith-start/src/components/competition-hero.tsx` around lines 106 -
118, The Manage action currently nests a Button inside an anchor which is
invalid; change it to render the link as the button using the Button component's
asChild prop: replace the <a href={`/compete/organizer/${competition.id}`}>
wrapper and instead pass asChild to Button and move the href into a child
Link/Anchor element (or directly render an <a> as the Button child) so the
Button (component name "Button") becomes the interactive anchor for the Manage
action while keeping the Settings icon and classNames unchanged and still
conditioned on canManage and competition.id.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@zacjones93 zacjones93 merged commit 86b4e9c into main Jan 25, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants