T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/domains/lavocat.ca/private_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/components/UserAvatar.tsx
import React, { useState } from 'react';
import { motion, AnimatePresence } from 'framer-motion';

interface User {
  id: string;
  name: string;
  email?: string;
  role?: string;
  profilePicture?: string;
  title?: string;
  specialization?: string;
  availability?: string;
  lastActive?: string;
  bio?: string;
}

interface UserAvatarProps {
  user: User;
  size?: 'xs' | 'sm' | 'md' | 'lg' | 'xl';
  showName?: boolean;
  showRole?: boolean;
  showTooltip?: boolean;
  showStatus?: boolean;
  clickable?: boolean;
  className?: string;
  onClick?: () => void;
}

const sizeClasses = {
  xs: 'w-6 h-6 text-xs',
  sm: 'w-8 h-8 text-sm',
  md: 'w-10 h-10 text-base',
  lg: 'w-12 h-12 text-lg',
  xl: 'w-16 h-16 text-xl'
};

const UserAvatar: React.FC<UserAvatarProps> = ({
  user,
  size = 'md',
  showName = false,
  showRole = false,
  showTooltip = false,
  showStatus = false,
  clickable = false,
  className = '',
  onClick
}) => {
  const [showProfileCard, setShowProfileCard] = useState(false);
  const [imageError, setImageError] = useState(false);

  const getRoleIcon = (role?: string) => {
    switch (role) {
      case 'ADMIN':
        return '⚖️';
      case 'LAWYER':
        return '👩‍⚖️';
      default:
        return '👤';
    }
  };

  const getRoleName = (role?: string) => {
    switch (role) {
      case 'ADMIN':
        return 'Legal Team';
      case 'LAWYER':
        return 'Lawyer';
      default:
        return 'Client';
    }
  };

  const getStatusColor = (availability?: string) => {
    switch (availability) {
      case 'Available':
        return 'bg-green-400';
      case 'Busy':
        return 'bg-yellow-400';
      case 'Away':
        return 'bg-orange-400';
      case 'Do Not Disturb':
        return 'bg-red-400';
      default:
        return 'bg-gray-400';
    }
  };

  const getInitials = (name: string) => {
    return name
      .split(' ')
      .map(n => n[0])
      .join('')
      .toUpperCase()
      .slice(0, 2);
  };

  const isOnline = (lastActive?: string) => {
    if (!lastActive) return false;
    const lastActiveDate = new Date(lastActive);
    const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1000);
    return lastActiveDate > fiveMinutesAgo;
  };

  const formatLastActive = (lastActive?: string) => {
    if (!lastActive) return 'Unknown';
    const date = new Date(lastActive);
    const now = new Date();
    const diffInMinutes = Math.floor((now.getTime() - date.getTime()) / (1000 * 60));
    
    if (diffInMinutes < 1) return 'Just now';
    if (diffInMinutes < 60) return `${diffInMinutes}m ago`;
    if (diffInMinutes < 1440) return `${Math.floor(diffInMinutes / 60)}h ago`;
    return date.toLocaleDateString();
  };

  const handleClick = () => {
    if (onClick) {
      onClick();
    } else if (showTooltip) {
      setShowProfileCard(!showProfileCard);
    }
  };

  const avatarContent = (
    <div className={`relative ${clickable ? 'cursor-pointer' : ''} ${className}`}>
      <div
        className={`${sizeClasses[size]} rounded-full overflow-hidden bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center relative ${
          clickable ? 'hover:scale-105 transition-transform' : ''
        }`}
        onClick={handleClick}
      >
        {user.profilePicture && !imageError ? (
          <img
            src={user.profilePicture}
            alt={user.name}
            className="w-full h-full object-cover"
            onError={() => setImageError(true)}
            onLoad={() => setImageError(false)}
          />
        ) : (
          <span className="text-white font-semibold">
            {getInitials(user.name)}
          </span>
        )}
        
        {/* Status indicator */}
        {showStatus && (
          <div className="absolute -bottom-0.5 -right-0.5">
            <div
              className={`w-3 h-3 rounded-full border-2 border-white ${
                isOnline(user.lastActive) ? 'bg-green-400' : getStatusColor(user.availability)
              }`}
            />
          </div>
        )}
      </div>

      {/* Profile card tooltip */}
      <AnimatePresence>
        {showProfileCard && showTooltip && (
          <motion.div
            initial={{ opacity: 0, scale: 0.9, y: 10 }}
            animate={{ opacity: 1, scale: 1, y: 0 }}
            exit={{ opacity: 0, scale: 0.9, y: 10 }}
            className="absolute z-50 top-full mt-2 left-1/2 transform -translate-x-1/2 bg-white dark:bg-gray-800 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 w-64"
          >
            <div className="flex items-start space-x-3">
              <div className="w-12 h-12 rounded-full overflow-hidden bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center flex-shrink-0">
                {user.profilePicture && !imageError ? (
                  <img
                    src={user.profilePicture}
                    alt={user.name}
                    className="w-full h-full object-cover"
                    onError={() => setImageError(true)}
                    onLoad={() => setImageError(false)}
                  />
                ) : (
                  <span className="text-white font-semibold">
                    {getInitials(user.name)}
                  </span>
                )}
              </div>
              
              <div className="flex-1 min-w-0">
                <div className="flex items-center space-x-2">
                  <h3 className="font-semibold text-gray-900 dark:text-white truncate">
                    {user.name}
                  </h3>
                  <span className="text-sm">{getRoleIcon(user.role)}</span>
                </div>
                
                {user.title && (
                  <p className="text-sm text-gray-600 dark:text-gray-400 truncate">
                    {user.title}
                  </p>
                )}
                
                {user.specialization && (
                  <p className="text-xs text-gray-500 dark:text-gray-500 truncate">
                    {user.specialization}
                  </p>
                )}
                
                <div className="flex items-center space-x-2 mt-2">
                  <div
                    className={`w-2 h-2 rounded-full ${
                      isOnline(user.lastActive) ? 'bg-green-400' : getStatusColor(user.availability)
                    }`}
                  />
                  <span className="text-xs text-gray-500 dark:text-gray-500">
                    {isOnline(user.lastActive) 
                      ? 'Online' 
                      : user.availability || formatLastActive(user.lastActive)
                    }
                  </span>
                </div>

                {user.bio && (
                  <p className="text-xs text-gray-600 dark:text-gray-400 mt-2 line-clamp-2">
                    {user.bio}
                  </p>
                )}
              </div>
            </div>

            {/* Close button */}
            <button
              onClick={() => setShowProfileCard(false)}
              className="absolute top-2 right-2 text-gray-400 hover:text-gray-600 dark:hover:text-gray-300"
            >
              <svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
              </svg>
            </button>
          </motion.div>
        )}
      </AnimatePresence>
    </div>
  );

  if (showName || showRole) {
    return (
      <div className={`flex items-center space-x-2 ${className}`}>
        {avatarContent}
        <div className="flex-1 min-w-0">
          {showName && (
            <p className="text-sm font-medium text-gray-900 dark:text-white truncate">
              {user.name}
            </p>
          )}
          {showRole && (
            <p className="text-xs text-gray-500 dark:text-gray-400 truncate">
              {getRoleIcon(user.role)} {getRoleName(user.role)}
            </p>
          )}
        </div>
      </div>
    );
  }

  return avatarContent;
};

export default UserAvatar; 

CasperSecurity Mini