auth-project-nav.tsx119 lines · main
| 1 | 'use client'; |
| 2 | |
| 3 | import Link from 'next/link'; |
| 4 | import { usePathname, useRouter } from 'next/navigation'; |
| 5 | |
| 6 | /** |
| 7 | * Tabs for one Auth project — same pattern as project-tabs: |
| 8 | * clean set by default; advanced tools behind “developer mode”. |
| 9 | */ |
| 10 | const TABS = [ |
| 11 | { href: '', label: 'overview', exact: true, dev: false }, |
| 12 | { href: '/users', label: 'users', dev: false }, |
| 13 | { href: '/sessions', label: 'sessions', dev: false }, |
| 14 | { href: '/security', label: 'security', dev: false }, |
| 15 | { href: '/keys', label: 'keys', dev: false }, |
| 16 | { href: '/providers', label: 'providers', dev: false }, |
| 17 | { href: '/branding', label: 'branding', dev: false }, |
| 18 | // Advanced / later tools |
| 19 | { href: '/idp', label: 'IdP', dev: true }, |
| 20 | { href: '/migration', label: 'import', dev: true }, |
| 21 | { href: '/ai', label: 'AI', dev: true }, |
| 22 | { href: '/enterprise', label: 'enterprise', dev: true }, |
| 23 | ] as const; |
| 24 | |
| 25 | const AUTH_ACCENT = '#FFFD74'; |
| 26 | const COOKIE = 'briven_auth_project_dev'; |
| 27 | |
| 28 | export function AuthProjectNav({ |
| 29 | projectId, |
| 30 | developerMode, |
| 31 | }: { |
| 32 | projectId: string; |
| 33 | developerMode: boolean; |
| 34 | }) { |
| 35 | const pathname = usePathname(); |
| 36 | const router = useRouter(); |
| 37 | const base = `/dashboard/auth/${projectId}`; |
| 38 | const visible = TABS.filter((tab) => developerMode || !tab.dev); |
| 39 | |
| 40 | // If user lands on a hidden dev tab while mode is off, still show that tab |
| 41 | // so they aren’t stranded. |
| 42 | const forceShow = TABS.filter( |
| 43 | (tab) => |
| 44 | tab.dev && |
| 45 | !visible.includes(tab) && |
| 46 | (pathname === `${base}${tab.href}` || |
| 47 | pathname.startsWith(`${base}${tab.href}/`)), |
| 48 | ); |
| 49 | const tabs = [...visible, ...forceShow]; |
| 50 | |
| 51 | function toggleDev() { |
| 52 | const on = document.cookie |
| 53 | .split('; ') |
| 54 | .some((c) => c === `${COOKIE}=1`); |
| 55 | document.cookie = `${COOKIE}=${on ? '0' : '1'}; path=/; max-age=31536000; samesite=lax`; |
| 56 | router.refresh(); |
| 57 | } |
| 58 | |
| 59 | return ( |
| 60 | <div className="flex items-center gap-2 border-b border-[var(--color-border-subtle)]"> |
| 61 | <nav |
| 62 | aria-label="Auth project sections" |
| 63 | className="flex flex-1 gap-1 overflow-x-auto [-ms-overflow-style:none] [scrollbar-width:none] [&::-webkit-scrollbar]:hidden" |
| 64 | > |
| 65 | {tabs.map((tab) => { |
| 66 | const href = `${base}${tab.href}`; |
| 67 | const exact = 'exact' in tab && tab.exact === true; |
| 68 | const active = exact |
| 69 | ? pathname === base || pathname === `${base}/` |
| 70 | : pathname === href || pathname.startsWith(`${href}/`); |
| 71 | return ( |
| 72 | <Link |
| 73 | key={tab.href || 'overview'} |
| 74 | href={href} |
| 75 | className={`shrink-0 whitespace-nowrap px-3 py-2 font-mono text-sm transition outline-none focus:outline-none focus-visible:outline-none ${ |
| 76 | active |
| 77 | ? 'font-medium text-[var(--color-text)]' |
| 78 | : 'text-[var(--color-text-muted)] hover:text-[var(--color-text)]' |
| 79 | }`} |
| 80 | > |
| 81 | {tab.label} |
| 82 | </Link> |
| 83 | ); |
| 84 | })} |
| 85 | </nav> |
| 86 | <button |
| 87 | type="button" |
| 88 | onClick={toggleDev} |
| 89 | title={ |
| 90 | developerMode |
| 91 | ? 'Hide advanced Auth tools' |
| 92 | : 'Show advanced tools (IdP, import, AI, enterprise…)' |
| 93 | } |
| 94 | className="flex shrink-0 items-center gap-1 whitespace-nowrap rounded-md px-2.5 py-1 font-mono text-xs transition" |
| 95 | style={ |
| 96 | developerMode |
| 97 | ? { background: AUTH_ACCENT, color: '#111' } |
| 98 | : { |
| 99 | color: `color-mix(in srgb, ${AUTH_ACCENT} 65%, transparent)`, |
| 100 | } |
| 101 | } |
| 102 | > |
| 103 | <svg |
| 104 | aria-hidden |
| 105 | viewBox="0 0 24 24" |
| 106 | fill="none" |
| 107 | stroke="currentColor" |
| 108 | strokeWidth="2" |
| 109 | strokeLinecap="round" |
| 110 | strokeLinejoin="round" |
| 111 | className="size-3.5" |
| 112 | > |
| 113 | <path d="M7 17 17 7M7 7h10v10" /> |
| 114 | </svg> |
| 115 | developer mode |
| 116 | </button> |
| 117 | </div> |
| 118 | ); |
| 119 | } |