feat(website): add Group Rides Hub navigation links for authenticated riders

This commit is contained in:
Developer 2026-09-25 15:35:55 -04:00
parent 30539108fa
commit 126f2c6c5f
5 changed files with 168 additions and 61 deletions

View File

@ -78,6 +78,13 @@ export default function AccountPage() {
<div className="flex flex-wrap items-center gap-3 pt-2 lg:pt-0">
{user ? (
<>
<Link
href="/group-rides"
className="px-4 py-2 rounded-xl bg-gradient-to-r from-brand-cyan/20 to-blue-600/20 hover:from-brand-cyan/30 hover:to-blue-600/30 text-brand-cyan text-sm font-semibold border border-brand-cyan/40 hover:border-brand-cyan transition flex items-center space-x-2"
>
<span className="material-symbols-outlined text-[18px]">groups</span>
<span>Group Rides Hub</span>
</Link>
<button
type="button"
onClick={handleQuickSync}
@ -241,6 +248,13 @@ export default function AccountPage() {
</div>
</div>
<div className="flex flex-wrap gap-3 pt-2">
<Link
href="/group-rides"
className="px-4 py-2 rounded-lg bg-brand-cyan/15 text-brand-cyan border border-brand-cyan/40 hover:bg-brand-cyan/25 text-xs font-semibold transition inline-flex items-center gap-1.5"
>
<span className="material-symbols-outlined text-[16px]">groups</span>
<span>Group Rides Hub</span>
</Link>
<Link
href="/clubs/manage"
className="px-4 py-2 rounded-lg bg-secondary/15 text-secondary border border-secondary/40 hover:bg-secondary/25 text-xs font-semibold transition inline-flex items-center gap-1.5"

View File

@ -123,6 +123,14 @@ export function FounderRides({
</p>
</div>
<div className="flex items-center gap-2">
<Link
href="/group-rides"
className="flex items-center gap-2 px-3.5 py-2 rounded-xl bg-brand-cyan/15 hover:bg-brand-cyan/25 border border-brand-cyan/40 text-brand-cyan font-body text-xs font-semibold transition"
>
<span className="material-symbols-outlined text-[17px]">groups</span>
<span>Live Pack Radar &amp; Hub</span>
</Link>
<div
className="flex items-center gap-2 px-3.5 py-2 rounded-xl bg-surface-container/50 border border-brand-border/40 text-slate-400 font-body text-xs cursor-not-allowed select-none"
title="Founder Portal ride planning is not wired yet"
@ -135,6 +143,7 @@ export function FounderRides({
</span>
</div>
</div>
</div>
{/* Architecture & Privacy Notice Banner */}
<div className="rounded-2xl bg-surface-container-low border border-brand-border/60 p-4 space-y-2">

View File

@ -247,6 +247,18 @@ export function FounderTopBar({
<span>Rider Account</span>
</Link>
<Link
href="/group-rides"
onClick={() => setUserMenuOpen(false)}
role="menuitem"
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-slate-200 hover:text-brand-cyan hover:bg-surface-container transition-colors text-xs font-semibold"
>
<span className="material-symbols-outlined text-[18px] text-brand-cyan">
groups
</span>
<span>Group Rides</span>
</Link>
<Link
href="/clubs/manage"
onClick={() => setUserMenuOpen(false)}

View File

@ -109,6 +109,16 @@ export function SiteHeaderClient({ signedIn, role }: SiteHeaderProps) {
</div>
{signedIn ? (
<div className="flex items-center gap-2">
<Link
href="/group-rides"
className="hidden sm:inline-flex items-center gap-1.5 px-3 py-1.5 rounded-xl bg-surface-container-high hover:bg-surface-container border border-outline-variant/30 text-xs font-semibold text-on-surface hover:text-primary-container transition-colors"
title="Group Rides Hub"
>
<span className="material-symbols-outlined text-[16px] text-primary-container">groups</span>
<span>Group Rides</span>
</Link>
<div className="relative" ref={userMenuRef}>
<button
type="button"
@ -155,6 +165,18 @@ export function SiteHeaderClient({ signedIn, role }: SiteHeaderProps) {
<span>Rider Account</span>
</Link>
<Link
href="/group-rides"
onClick={() => setUserMenuOpen(false)}
role="menuitem"
className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-on-surface hover:text-primary-container hover:bg-surface-container transition-colors text-xs font-semibold"
>
<span className="material-symbols-outlined text-[18px] text-primary-container">
groups
</span>
<span>Group Rides</span>
</Link>
<Link
href="/clubs/manage"
onClick={() => setUserMenuOpen(false)}
@ -205,6 +227,7 @@ export function SiteHeaderClient({ signedIn, role }: SiteHeaderProps) {
</div>
)}
</div>
</div>
) : (
<div className="flex items-center gap-1.5 sm:gap-space-sm">
<Link
@ -289,6 +312,14 @@ export function SiteHeaderClient({ signedIn, role }: SiteHeaderProps) {
<span className="material-symbols-outlined text-[20px] text-primary-container">person</span>
<span>Rider Account</span>
</Link>
<Link
href="/group-rides"
onClick={() => setMobileMenuOpen(false)}
className="px-space-md py-2.5 rounded-xl font-label-md text-label-md transition-colors min-h-[44px] flex items-center gap-2 text-on-surface hover:bg-surface-container"
>
<span className="material-symbols-outlined text-[20px] text-primary-container">groups</span>
<span>Group Rides</span>
</Link>
<Link
href="/clubs/manage"
onClick={() => setMobileMenuOpen(false)}

View File

@ -7,6 +7,12 @@ import { JoinRideModal } from '../components/group-rides/join-ride-modal';
import { CreateRideModal } from '../components/group-rides/create-ride-modal';
import { GroupRidesDirectory } from '../components/group-rides/group-rides-directory';
import { INITIAL_GROUP_RIDES } from '../components/group-rides/fixtures';
import { SiteHeaderClient } from '../components/site-header-client';
vi.mock('next/navigation', () => ({
usePathname: () => '/',
useRouter: () => ({ push: vi.fn(), replace: vi.fn(), back: vi.fn() }),
}));
describe('Group Rides Interactive Page & Components', () => {
beforeEach(() => {
@ -303,4 +309,39 @@ describe('Group Rides Interactive Page & Components', () => {
});
});
});
// ============================================================
// 6. Authenticated Navigation to Group Rides
// ============================================================
describe('Authenticated Navigation to Group Rides', () => {
it('renders Group Rides quick button and user dropdown link for signed-in users', () => {
render(<SiteHeaderClient signedIn={true} role="rider" />);
// Quick header button for signed-in users
const quickGroupRidesBtn = screen.getByTitle('Group Rides Hub');
expect(quickGroupRidesBtn).toBeDefined();
expect(quickGroupRidesBtn.getAttribute('href')).toBe('/group-rides');
// Open Rider Menu dropdown
const riderMenuBtn = screen.getByRole('button', { name: /open rider menu/i });
fireEvent.click(riderMenuBtn);
// Verify Group Rides link exists inside dropdown menu
const groupRidesLinks = screen.getAllByRole('menuitem', { name: /group rides/i });
expect(groupRidesLinks.length).toBeGreaterThan(0);
expect(groupRidesLinks[0].getAttribute('href')).toBe('/group-rides');
});
it('renders Group Rides in mobile navigation drawer for signed-in users', () => {
render(<SiteHeaderClient signedIn={true} role="rider" />);
const hamburgerBtn = screen.getByRole('button', { name: /open navigation menu/i });
fireEvent.click(hamburgerBtn);
const groupRidesNavLinks = screen.getAllByRole('link', { name: /group rides/i });
const mobileDrawerLink = groupRidesNavLinks.find(link => link.getAttribute('href') === '/group-rides');
expect(mobileDrawerLink).toBeDefined();
});
});
});