From fc4e5b4ed45251ace23241a6ff3135f65d831132 Mon Sep 17 00:00:00 2001 From: Ethan Swan Date: Mon, 6 Apr 2026 05:21:59 -0500 Subject: [PATCH 1/2] Split competitions dropdown into Private and Public sections Competitions in the navbar dropdown are now grouped under "Private" and "Public" headings. If the user has no private competitions, that section is omitted entirely. Applies to both desktop and mobile navigation. Co-Authored-By: Claude Opus 4.6 (1M context) --- components/navbar/dropdown-navbar-item.tsx | 63 ++++++++++++++-------- components/navbar/mobile-dropdown-item.tsx | 47 ++++++++++------ components/navbar/nav-types.ts | 8 ++- components/navbar/navbar.tsx | 24 ++++++--- 4 files changed, 99 insertions(+), 43 deletions(-) diff --git a/components/navbar/dropdown-navbar-item.tsx b/components/navbar/dropdown-navbar-item.tsx index 71615c6c..fb7bdc25 100644 --- a/components/navbar/dropdown-navbar-item.tsx +++ b/components/navbar/dropdown-navbar-item.tsx @@ -7,14 +7,32 @@ import { NavigationMenuLink, NavigationMenuTrigger, } from "@/components/ui/navigation-menu"; -import { NavLinkGroup } from "./nav-types"; +import { NavLink, NavLinkGroup } from "./nav-types"; interface DropdownNavbarItemProps { group: NavLinkGroup; } +function LinkItem({ href, label, icon }: NavLink) { + return ( +
  • + + +
    + {icon} +
    {label}
    +
    + +
    +
  • + ); +} + export function DropdownNavbarItem({ - group: { label, links }, + group: { label, links, sections }, }: DropdownNavbarItemProps) { return ( @@ -22,25 +40,28 @@ export function DropdownNavbarItem({ {label} -
      - {links.map(({ href, label, icon }) => ( -
    • - - -
      - {icon} -
      - {label} -
      -
      - -
      -
    • - ))} -
    + {sections ? ( +
    + {sections.map((section) => ( +
    +

    + {section.heading} +

    +
      + {section.links.map((link) => ( + + ))} +
    +
    + ))} +
    + ) : ( +
      + {links?.map((link) => ( + + ))} +
    + )}
    ); diff --git a/components/navbar/mobile-dropdown-item.tsx b/components/navbar/mobile-dropdown-item.tsx index 65876383..f96e446e 100644 --- a/components/navbar/mobile-dropdown-item.tsx +++ b/components/navbar/mobile-dropdown-item.tsx @@ -4,7 +4,7 @@ import Link from "next/link"; import { ChevronDown } from "lucide-react"; import { Button } from "@/components/ui/button"; import { SheetClose } from "@/components/ui/sheet"; -import { NavLinkGroup } from "./nav-types"; +import { NavLink, NavLinkGroup } from "./nav-types"; interface MobileDropdownItemProps { group: NavLinkGroup; @@ -12,8 +12,24 @@ interface MobileDropdownItemProps { onToggle: () => void; } +function MobileLinkItem({ href, label, icon }: NavLink) { + return ( + + + + + + ); +} + export function MobileDropdownItem({ - group: { label, links }, + group: { label, links, sections }, isExpanded, onToggle, }: MobileDropdownItemProps) { @@ -33,19 +49,20 @@ export function MobileDropdownItem({ {isExpanded && (
    - {links.map(({ href, label, icon }) => ( - - - - - - ))} + {sections + ? sections.map((section) => ( +
    +
    + {section.heading} +
    + {section.links.map((link) => ( + + ))} +
    + )) + : links?.map((link) => ( + + ))}
    )} diff --git a/components/navbar/nav-types.ts b/components/navbar/nav-types.ts index 9af54e66..70d89330 100644 --- a/components/navbar/nav-types.ts +++ b/components/navbar/nav-types.ts @@ -6,7 +6,13 @@ export type NavLink = { icon?: ReactElement; }; +export type NavLinkSection = { + heading: string; + links: NavLink[]; +}; + export type NavLinkGroup = { label: string; - links: NavLink[]; + links?: NavLink[]; + sections?: NavLinkSection[]; }; diff --git a/components/navbar/navbar.tsx b/components/navbar/navbar.tsx index 150fe426..6a6f0552 100644 --- a/components/navbar/navbar.tsx +++ b/components/navbar/navbar.tsx @@ -33,7 +33,7 @@ import { useCurrentUser } from "@/hooks/useCurrentUser"; import { getCompetitions } from "@/lib/db_actions/competitions"; import { Competition } from "@/types/db_types"; import { getCompetitionStatusFromObject } from "@/lib/competition-status"; -import { NavLink, NavLinkGroup } from "./nav-types"; +import { NavLink, NavLinkGroup, NavLinkSection } from "./nav-types"; import { DropdownNavbarItem } from "./dropdown-navbar-item"; import { MobileDropdownItem } from "./mobile-dropdown-item"; @@ -78,13 +78,25 @@ export default function NavBar() { // Add competitions section if there are any competitions if (competitions.length > 0) { + const privateComps = competitions.filter((c) => c.is_private); + const publicComps = competitions.filter((c) => !c.is_private); + const toNavLink = (competition: Competition): NavLink => ({ + href: `/competitions/${competition.id}`, + label: competition.name, + icon: , + }); + + const sections: NavLinkSection[] = []; + if (privateComps.length > 0) { + sections.push({ heading: "Private", links: privateComps.map(toNavLink) }); + } + if (publicComps.length > 0) { + sections.push({ heading: "Public", links: publicComps.map(toNavLink) }); + } + links.push({ label: "Competitions", - links: competitions.map((competition) => ({ - href: `/competitions/${competition.id}`, - label: competition.name, - icon: , - })), + sections, }); } From 4971348d6c383dd2cebf0dd44c519791d683489a Mon Sep 17 00:00:00 2001 From: Ethan Swan Date: Mon, 6 Apr 2026 05:39:49 -0500 Subject: [PATCH 2/2] Make competitions dropdown width dynamic and improve line spacing Use w-max with a max-w cap instead of fixed widths so the dropdown panel shrinks to fit one section or expands for two. Also change link label leading from none to snug for better readability when competition names wrap to two lines. Co-Authored-By: Claude Opus 4.6 (1M context) --- components/navbar/dropdown-navbar-item.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/components/navbar/dropdown-navbar-item.tsx b/components/navbar/dropdown-navbar-item.tsx index fb7bdc25..354624b3 100644 --- a/components/navbar/dropdown-navbar-item.tsx +++ b/components/navbar/dropdown-navbar-item.tsx @@ -23,7 +23,7 @@ function LinkItem({ href, label, icon }: NavLink) { >
    {icon} -
    {label}
    +
    {label}
    @@ -41,7 +41,7 @@ export function DropdownNavbarItem({ {sections ? ( -
    +
    {sections.map((section) => (