sub-nav.tsx62 lines · main
1'use client';
2
3import Link from 'next/link';
4import { usePathname } from 'next/navigation';
5
6interface Props {
7 projectId: string;
8}
9
10const TABS: Array<{ slug: string; label: string }> = [
11 { slug: '', label: 'overview' },
12 { slug: '/providers', label: 'providers' },
13 { slug: '/branding', label: 'branding' },
14 { slug: '/domains', label: 'app domains' },
15 { slug: '/users', label: 'users' },
16 { slug: '/audit', label: 'audit' },
17 { slug: '/api-keys', label: 'api keys' },
18 { slug: '/webhooks', label: 'webhooks' },
19 { slug: '/usage', label: 'usage' },
20 { slug: '/enterprise', label: 'enterprise' },
21];
22
23/**
24 * Sub-nav for the auth section. `usePathname` decides which tab is active.
25 * A tab matches when the current path is either the tab's exact path or a
26 * deeper sub-route of it (so `/auth/users/abc123` lights up the `users`
27 * tab).
28 */
29export function AuthSubNav({ projectId }: Props) {
30 const pathname = usePathname() ?? '';
31 const base = `/dashboard/projects/${projectId}/auth`;
32
33 return (
34 <nav className="flex flex-wrap items-center gap-1 border-b border-[var(--color-border-subtle)] pb-2">
35 {TABS.map((tab) => {
36 const href = `${base}${tab.slug}`;
37 // Overview tab matches only when the path is exactly `/auth`. Other
38 // tabs match when the path starts with `<base><slug>` AND the next
39 // character is `/` or end-of-string — prevents `/auth/api-keys`
40 // accidentally lighting up `/auth/api` (no such tab today, but
41 // belt-and-braces).
42 const active =
43 tab.slug === ''
44 ? pathname === base
45 : pathname === href || pathname.startsWith(`${href}/`);
46 return (
47 <Link
48 key={tab.slug}
49 href={href}
50 className={`rounded-md px-3 py-1.5 font-mono text-xs ${
51 active
52 ? 'bg-[var(--color-surface-raised)] text-[var(--color-text)]'
53 : 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]'
54 }`}
55 >
56 {tab.label}
57 </Link>
58 );
59 })}
60 </nav>
61 );
62}