-
Notifications
You must be signed in to change notification settings - Fork 1
feat(compete): add glassmorphism styling to competition detail pages #210
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
Conversation
- 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>
WalkthroughThe 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
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~22 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ 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.
No issues found across 9 files
🚀 Preview DeployedURL: https://wodsmith-app-pr-210.zacjones93.workers.dev
This comment is automatically updated on each push to this PR. |
…/update-comp-detail-styles
- 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>
5b532b8 to
0965b64
Compare
- 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.
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: 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-whiteon 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: Redundantrounded-xlin size variants.Since
rounded-xlis already in the base string (line 8), specifying it again in thesmandlgsize 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>
fbe5c47 to
7320f9a
Compare
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>
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
🤖 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 thecnimport path across the component.Both
@/lib/utilsand@/utils/cnare valid, but the codebase currently has mixed usage—some files import from@/lib/utilswhile 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 byworkoutIdswould scale better.
144-155: ClampselectedDivisionIdto known divisions.
A staledivisionsearch 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
| {/* 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}> | ||
| )} |
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.
🧩 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 -5Repository: 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 5Repository: 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 2Repository: wodsmith/thewodapp
Length of output: 89
🏁 Script executed:
#!/bin/bash
# Find the specific file
fd "$eventId.tsx" apps/wodsmith-startRepository: 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 -50Repository: 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 -80Repository: 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 dRepository: 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 5Repository: 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 -100Repository: 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 5Repository: 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.tsxRepository: 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.tsxRepository: 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.tsxRepository: 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 20Repository: 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 -60Repository: 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 -10Repository: 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 -15Repository: 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 -80Repository: 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 2Repository: 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 3Repository: 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 15Repository: 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.
| {/* 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.
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
🤖 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
| <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> | ||
| )} |
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.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Find the Button component in the codebase
fd -a 'button\.tsx' apps/wodsmith-start/src/components/ui -type fRepository: 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 -150Repository: 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/uiRepository: 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.tsRepository: 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.
| <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>
Summary
rounded-xlstyling to buttons throughout the appTest plan
🤖 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.
Written for commit ddfd72f. Summary will update on new commits.
Summary by CodeRabbit
Release Notes
New Features
UI/UX Improvements
Navigation
✏️ Tip: You can customize this high-level summary in your review settings.