Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 44 additions & 12 deletions apps/wodsmith-start/src/components/cohost-sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,13 @@ const getNavigation = (
label: "Competition Setup",
items: [
...(permissions?.divisions
? [{ label: "Divisions", href: `${basePath}/divisions`, icon: Layers }]
? [
{
label: "Divisions",
href: `${basePath}/divisions`,
icon: Layers,
},
]
: []),
...(permissions?.editEvents
? [
Expand All @@ -98,13 +104,31 @@ const getNavigation = (
]
: []),
...(permissions?.locations
? [{ label: "Locations", href: `${basePath}/locations`, icon: MapPin }]
? [
{
label: "Locations",
href: `${basePath}/locations`,
icon: MapPin,
},
]
: []),
...(permissions?.scoringConfig
? [{ label: "Scoring", href: `${basePath}/scoring`, icon: Calculator }]
? [
{
label: "Scoring",
href: `${basePath}/scoring`,
icon: Calculator,
},
]
: []),
...(permissions?.viewRegistrations
? [{ label: "Registrations", href: `${basePath}/athletes`, icon: Users }]
? [
{
label: "Registrations",
href: `${basePath}/athletes`,
icon: Users,
},
]
: []),
...(permissions?.waivers
? [
Expand Down Expand Up @@ -189,7 +213,13 @@ const getNavigation = (
]
: []),
...(permissions?.sponsors
? [{ label: "Sponsors", href: `${basePath}/sponsors`, icon: Sparkles }]
? [
{
label: "Sponsors",
href: `${basePath}/sponsors`,
icon: Sparkles,
},
]
: []),
],
},
Expand All @@ -215,8 +245,9 @@ function NavMenuItem({ item, isActive }: { item: NavItem; isActive: boolean }) {
function CohostSidebarHeader({ competitionName }: { competitionName: string }) {
return (
<SidebarHeader className="border-b px-3 py-3 group-data-[collapsible=icon]:px-2 group-data-[collapsible=icon]:py-3 group-data-[collapsible=icon]:justify-center">
{/* @lat: [[architecture#Route Groups#compete]] */}
<Link
to="/compete"
to="/"
className="flex items-center gap-2 min-w-0 group-data-[collapsible=icon]:hidden"
>
<img
Expand All @@ -238,10 +269,8 @@ function CohostSidebarHeader({ competitionName }: { competitionName: string }) {
</span>
</div>
</Link>
<Link
to="/compete"
className="hidden group-data-[collapsible=icon]:block"
>
{/* @lat: [[architecture#Route Groups#compete]] */}
<Link to="/" className="hidden group-data-[collapsible=icon]:block">
<img
src="/wodsmith-logo-no-text.png"
alt="wodsmith compete"
Expand Down Expand Up @@ -334,15 +363,18 @@ export function CohostSidebar({
<Menu className="h-5 w-5" />
</SidebarTrigger>
<div className="flex items-center gap-2 min-w-0">
<Link to="/compete" className="flex items-center gap-2 shrink-0">
{/* @lat: [[architecture#Route Groups#compete]] */}
<Link to="/" className="flex items-center gap-2 shrink-0">
<img
src="/wodsmith-logo-no-text.png"
alt="wodsmith compete"
width={24}
height={24}
/>
</Link>
<span className="truncate text-sm font-medium">{competitionName}</span>
<span className="truncate text-sm font-medium">
{competitionName}
</span>
</div>
</header>
<div className="h-14 md:hidden" />
Expand Down
9 changes: 8 additions & 1 deletion apps/wodsmith-start/src/components/compete-breadcrumb.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ interface BreadcrumbSegment {
href?: string
}

// @lat: [[architecture#Route Groups#compete]]
const COMPETITION_DISCOVERY_PATH = "/"

/**
* Route segment to human-readable label mapping
*/
Expand Down Expand Up @@ -128,9 +131,13 @@ export function CompeteBreadcrumb({

// Don't add href for the last segment (current page)
const isLast = i === pathSegments.length - 1
const href =
segment === "compete" && currentPath === "/compete"
? COMPETITION_DISCOVERY_PATH
: currentPath
segments.push({
label,
href: isLast ? undefined : currentPath,
href: isLast ? undefined : href,
})
}

Expand Down
48 changes: 36 additions & 12 deletions apps/wodsmith-start/src/components/compete-mobile-nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ interface AthleteProfileMissingFields {
interface CompeteMobileNavProps {
session: SessionValidationResult | null
invitations?: PendingInvitation[]
canOrganize?: boolean
hasOrganizerApplication?: boolean
missingProfileFields?: AthleteProfileMissingFields | null
}

Expand Down Expand Up @@ -63,12 +63,23 @@ function shouldHideBrand(pathname: string) {
export default function CompeteMobileNav({
session,
invitations = [],
canOrganize = false,
hasOrganizerApplication = false,
missingProfileFields = null,
}: CompeteMobileNavProps) {
const [open, setOpen] = useState(false)
const router = useRouterState()
const pathname = router.location.pathname
const isCompetitionIndex = pathname === "/"
const isManageCompetitionsActive =
pathname === "/compete/organizer" ||
pathname.startsWith("/compete/organizer/")
const competitionsLinkClass = isCompetitionIndex
? "font-semibold text-foreground underline decoration-primary decoration-2 underline-offset-4 dark:text-dark-foreground"
: "hover:text-primary"
const manageCompetitionsLinkClass = isManageCompetitionsActive
? "flex items-center gap-2 font-semibold text-foreground underline decoration-primary decoration-2 underline-offset-4 dark:text-dark-foreground"
: "flex items-center gap-2 hover:text-primary"
const showManageCompetitionsLink = hasOrganizerApplication

const isProfileIncomplete =
missingProfileFields &&
Expand All @@ -95,9 +106,10 @@ export default function CompeteMobileNav({
<SheetContent side="left" background="sidebar">
<SheetTitle className="sr-only">Navigation Menu</SheetTitle>
<nav className="grid gap-6 font-medium text-lg">
{/* @lat: [[architecture#Route Groups#compete]] */}
{!shouldHideBrand(pathname) && (
<Link
to="/compete"
to="/"
className="mb-4 flex items-center gap-2 font-semibold text-lg"
onClick={handleLinkClick}
>
Expand Down Expand Up @@ -126,20 +138,31 @@ export default function CompeteMobileNav({
{session?.user ? (
<>
<Link
to="/compete"
className="hover:text-primary"
to="/"
aria-current={isCompetitionIndex ? "page" : undefined}
className={competitionsLinkClass}
onClick={handleLinkClick}
>
Events
Competitions
</Link>
{canOrganize && (
{showManageCompetitionsLink && (
<a
href="/compete/organizer"
className="flex items-center gap-2 hover:text-primary"
aria-current={isManageCompetitionsActive ? "page" : undefined}
className={manageCompetitionsLinkClass}
onClick={handleLinkClick}
>
<Settings className="h-5 w-5" />
<span>Organizer</span>
<span>MANAGE COMPETITIONS</span>
</a>
)}
{!hasOrganizerApplication && (
<a
href="/compete/organizer/onboard"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: Internal nav to organizer onboarding uses <a href> instead of TanStack Link, causing hard reload and inconsistent SPA behavior.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At apps/wodsmith-start/src/components/compete-mobile-nav.tsx, line 161:

<comment>Internal nav to organizer onboarding uses `<a href>` instead of TanStack `Link`, causing hard reload and inconsistent SPA behavior.</comment>

<file context>
@@ -127,23 +137,32 @@ export default function CompeteMobileNav({
                 <a
-                  href="/compete/organizer"
-                  className="flex items-center gap-2 hover:text-primary"
+                  href="/compete/organizer/onboard"
+                  className="hover:text-primary"
                   onClick={handleLinkClick}
</file context>

className="hover:text-primary"
onClick={handleLinkClick}
>
HOST A COMP
</a>
)}
<hr className="my-2" />
Expand Down Expand Up @@ -200,11 +223,12 @@ export default function CompeteMobileNav({
) : (
<>
<Link
to="/compete"
className="hover:text-primary"
to="/"
aria-current={isCompetitionIndex ? "page" : undefined}
className={competitionsLinkClass}
onClick={handleLinkClick}
>
Events
Competitions
</Link>
<a
href="/sign-in"
Expand Down
3 changes: 2 additions & 1 deletion apps/wodsmith-start/src/components/compete-nav-brand.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { Link } from "@tanstack/react-router"

export function CompeteNavBrand() {
return (
<Link to="/compete" className="flex items-center gap-2">
// @lat: [[architecture#Route Groups#compete]]
<Link to="/" className="flex items-center gap-2">
Comment thread
coderabbitai[bot] marked this conversation as resolved.
<img
src="/wodsmith-logo-no-text.png"
alt="wodsmith compete"
Expand Down
68 changes: 49 additions & 19 deletions apps/wodsmith-start/src/components/compete-nav.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client"

import { Link } from "@tanstack/react-router"
import { Link, useRouterState } from "@tanstack/react-router"
import { User } from "lucide-react"
import CompeteMobileNav from "@/components/compete-mobile-nav"
import { CompeteNavBrand } from "@/components/compete-nav-brand"
Expand All @@ -10,35 +10,68 @@ import type { SessionValidationResult } from "@/types"

interface CompeteNavProps {
session: SessionValidationResult
canOrganize: boolean
hasOrganizerApplication: boolean
}

export default function CompeteNav({ session, canOrganize }: CompeteNavProps) {
export default function CompeteNav({
session,
hasOrganizerApplication,
}: CompeteNavProps) {
// For now, we don't have these other features implemented in wodsmith-start
const pendingInvitations: never[] = []
const missingProfileFields = null
const pathname = useRouterState({
select: (state) => state.location.pathname,
})
const isCompetitionIndex = pathname === "/"
const isManageCompetitionsActive =
pathname === "/compete/organizer" ||
pathname.startsWith("/compete/organizer/")
const competitionsLinkClass = isCompetitionIndex
? "font-bold text-foreground uppercase underline decoration-primary decoration-2 underline-offset-4 dark:text-dark-foreground"
: "font-bold text-foreground uppercase hover:underline dark:text-dark-foreground"
const manageCompetitionsLinkClass = isManageCompetitionsActive
? "flex items-center gap-1 font-bold text-foreground underline decoration-primary decoration-2 underline-offset-4 dark:text-dark-foreground"
: "flex items-center gap-1 font-bold text-foreground hover:underline dark:text-dark-foreground"
const showManageCompetitionsLink = hasOrganizerApplication

return (
<header className="border-black border-b-2 bg-background dark:border-dark-border dark:bg-dark-background">
<div className="container mx-auto flex items-center p-4">
<CompeteNavBrand />
<nav className="ml-auto hidden items-center gap-4 md:flex">
{/* @lat: [[architecture#Route Groups#compete]] */}
{session?.user ? (
<>
<Link
to="/compete"
className="font-bold text-foreground uppercase hover:underline dark:text-dark-foreground"
to="/"
aria-current={isCompetitionIndex ? "page" : undefined}
className={competitionsLinkClass}
>
Events
Competitions
</Link>
{canOrganize && (
{showManageCompetitionsLink && (
<>
<div className="h-6 border-black border-l-2 dark:border-dark-border" />
<Link
to="/compete/organizer"
className="flex items-center gap-1 font-bold text-foreground uppercase hover:underline dark:text-dark-foreground"
aria-current={
isManageCompetitionsActive ? "page" : undefined
}
className={manageCompetitionsLinkClass}
>
Organize
MANAGE COMPETITIONS
</Link>
</>
)}
{!hasOrganizerApplication && (
<>
<div className="h-6 border-black border-l-2 dark:border-dark-border" />
<Link
to="/compete/organizer/onboard"
className="font-bold text-foreground hover:underline dark:text-dark-foreground"
>
HOST A COMP
</Link>
</>
)}
Expand All @@ -55,23 +88,20 @@ export default function CompeteNav({ session, canOrganize }: CompeteNavProps) {
) : (
<div className="flex items-center gap-2">
<Link
to="/compete"
className="font-bold text-foreground uppercase hover:underline dark:text-dark-foreground"
to="/"
aria-current={isCompetitionIndex ? "page" : undefined}
className={competitionsLinkClass}
>
Events
Competitions
</Link>
<Link
to="/sign-in"
search={{ redirect: "/compete" }}
search={{ redirect: "/" }}
className="btn-outline"
>
Login
</Link>
<Link
to="/sign-up"
search={{ redirect: "/compete" }}
className="btn"
>
<Link to="/sign-up" search={{ redirect: "/" }} className="btn">
Sign Up
</Link>
<DarkModeToggle />
Expand All @@ -82,7 +112,7 @@ export default function CompeteNav({ session, canOrganize }: CompeteNavProps) {
<CompeteMobileNav
session={session}
invitations={pendingInvitations}
canOrganize={canOrganize}
hasOrganizerApplication={hasOrganizerApplication}
missingProfileFields={missingProfileFields}
/>
</div>
Expand Down
Loading
Loading