bookwiz.io / app / dashboard / components / UserAccountCard.tsx
UserAccountCard.tsx
Raw
'use client'

import Link from 'next/link'
import { useAuth } from '@/components/AuthProvider'
import { StarIcon, ArrowRightOnRectangleIcon } from '@heroicons/react/24/outline'
import { useSubscription } from '@/lib/hooks/useSubscription'
import { useProfile } from '@/lib/hooks/useProfile'
import Image from 'next/image'

export default function UserAccountCard() {
  const { user, signingOut, signOut } = useAuth()
  const { profile, loading: profileLoading } = useProfile()
  const { getCurrentPlan, loading } = useSubscription()
  
  const currentPlan = getCurrentPlan()
  
  const getPlanBadge = () => {
    // Show loading state instead of "Free" when loading
    if (loading) {
      return (
        <div className="flex items-center space-x-1">
          <div className="w-2 h-2 bg-gray-500 rounded-full animate-pulse" />
          <span className="text-xs font-bold text-gray-400 uppercase tracking-wider">Loading</span>
        </div>
      )
    }
    
    if (!currentPlan || currentPlan.id === 'free') {
      return (
        <div className="flex items-center space-x-1">
          <div className="w-2 h-2 bg-gray-500 rounded-full" />
          <span className="text-xs font-bold text-gray-400 uppercase tracking-wider">Free</span>
        </div>
      )
    }
    
    return (
      <div className="flex items-center space-x-1">
        <StarIcon className="w-3 h-3 text-yellow-400" />
        <span className="text-xs font-bold text-yellow-400 uppercase tracking-wider">{currentPlan.name}</span>
      </div>
    )
  }

  const getInitials = () => {
    // Use profile data first, then fallback to auth metadata
    const fullName = profile?.full_name || user?.user_metadata?.full_name
    if (fullName) {
      return fullName
        .split(' ')
        .map((n: string) => n[0])
        .join('')
        .toUpperCase()
        .slice(0, 2)
    }
    if (user?.email) {
      return user.email.slice(0, 2).toUpperCase()
    }
    return 'U'
  }

  const getDisplayName = () => {
    // Use profile data first, then fallback to auth metadata
    return profile?.full_name || user?.user_metadata?.full_name || user?.email?.split('@')[0] || 'User'
  }

  const getAvatarUrl = () => {
    // Use profile avatar first, then fallback to auth metadata
    const profileAvatar = profile?.avatar_url
    const authAvatar = user?.user_metadata?.avatar_url || user?.user_metadata?.picture
    
    // Only return a URL if it's not empty/null/undefined
    if (profileAvatar && profileAvatar.trim() !== '') {
      return profileAvatar
    }
    if (authAvatar && authAvatar.trim() !== '') {
      return authAvatar
    }
    return null
  }

  return (
    <div className="bg-white/5 rounded-2xl p-4 backdrop-blur-sm border border-white/10">
      <div className="flex items-center space-x-3">
        {/* User Avatar and Info - Clickable */}
        <Link 
          href="/dashboard/profile" 
          className={`flex items-center space-x-3 flex-1 min-w-0 hover:bg-white/5 rounded-lg p-1 -m-1 transition-all duration-200 group ${signingOut ? 'pointer-events-none opacity-50' : ''}`}
        >
          {/* User Avatar */}
          <div className="flex-shrink-0 relative">
            {getAvatarUrl() ? (
              <>
                <Image 
                  src={getAvatarUrl()} 
                  alt="Profile" 
                  width={48}
                  height={48}
                  className="w-12 h-12 rounded-xl object-cover border-2 border-white/20 shadow-lg group-hover:border-white/30 transition-all"
                  onError={(e) => {
                    console.error('Avatar failed to load:', getAvatarUrl())
                    // Hide the image and show the fallback
                    e.currentTarget.style.display = 'none'
                    // Show the fallback div
                    const fallback = e.currentTarget.nextElementSibling as HTMLElement
                    if (fallback) fallback.style.display = 'flex'
                  }}
                />
                <div 
                  className="hidden w-12 h-12 rounded-xl bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 items-center justify-center shadow-lg border-2 border-white/20 group-hover:border-white/30 transition-all"
                  style={{ display: 'none' }}
                >
                  <span className="text-white font-black text-sm">{getInitials()}</span>
                </div>
              </>
            ) : (
              <div className="w-12 h-12 rounded-xl bg-gradient-to-br from-indigo-500 via-purple-500 to-pink-500 flex items-center justify-center shadow-lg border-2 border-white/20 group-hover:border-white/30 transition-all">
                <span className="text-white font-black text-sm">{getInitials()}</span>
              </div>
            )}
          </div>
          
          {/* User Info */}
          <div className="flex-1 min-w-0">
            <div className="text-white font-bold text-sm truncate mb-1 group-hover:text-gray-200 transition-colors">
              {signingOut ? 'Signing out...' : getDisplayName()}
            </div>
            {getPlanBadge()}
          </div>
        </Link>

        {/* Logout Button */}
        <button
          onClick={signOut}
          disabled={signingOut}
          className="p-2 text-slate-400 hover:text-slate-200 hover:bg-white/5 rounded-lg transition-colors duration-200 flex-shrink-0 disabled:opacity-50 disabled:cursor-not-allowed disabled:hover:bg-transparent disabled:hover:text-slate-400"
          title={signingOut ? "Signing out..." : "Log Out"}
        >
          {signingOut ? (
            <div className="animate-spin rounded-full h-5 w-5 border-b-2 border-slate-400"></div>
          ) : (
            <ArrowRightOnRectangleIcon className="w-5 h-5" />
          )}
        </button>
      </div>
    </div>
  )
}