registry:component

Profile Dropdown

v0.1.0alpha

Avatar button with profile, settings, and sign-out menu. Manages its own Supabase auth state.

Registry Endpoint
Open JSON

https://registry.aavya.com/r/profile-dropdown.json

npx shadcn@latest add https://registry.aavya.com/r/profile-dropdown.json

Live Preview
Dependencies
avatarbuttondropdown-menu
Usage Snippet
import ProfileDropdown from '@/components/blocks/profile-dropdown.tsx'

export default function Example() {
  return (
    <div className="p-6">
      <ProfileDropdown />
    </div>
  )
}
Source Preview
'use client'

import type { ReactNode } from 'react'
import Link from 'next/link'
import { useRouter } from 'next/navigation'
import { useEffect, useState } from 'react'
import {
  BarChart3Icon,
  DownloadIcon,
  LogInIcon,
  LogOutIcon,
  SettingsIcon,
  UserIcon,
  UsersIcon,
  WandSparklesIcon,
} from 'lucide-react'
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'
import { Button } from '@/components/ui/button'
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuGroup,
  DropdownMenuItem,
  DropdownMenuLabel,
  DropdownMenuSeparator,
  DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu'
import { createClient } from '@/lib/supabase/client'

const supabaseEnabled =
  process.env.NEXT_PUBLIC_AUTH_DISABLED !== 'true' &&
  Boolean(process.env.NEXT_PUBLIC_SUPABASE_URL) &&
  Boolean(process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY)

function hasAdminAccess(user: {
  app_metadata?: Record<string, unknown>
  user_metadata?: Record<string, unknown>
}): boolean {
  const roleCandidates = [
    user.app_metadata?.role,
    user.user_metadata?.role,
    user.app_metadata?.roles,
    user.user_metadata?.roles,
  ]
  return roleCandidates.some((candidate) => {
    if (typeof candidate === 'string') return candidate.toLowerCase() === 'admin'
    if (Array.isArray(candidate)) {
      return candidate.some((r) => typeof r === 'string' && r.toLowerCase()