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/ImpersonationModal.tsx
import { useState, useEffect } from 'react';
import { useImpersonation } from '@/hooks/useImpersonation';
import { Search, User, Mail, Calendar, FileText, Gavel, Loader2 } from 'lucide-react';
import Image from 'next/image';

interface User {
  id: string;
  name: string;
  email: string;
  role: string;
  image: string | null;
  createdAt: string;
  stats: {
    registrations: number;
    cases: number;
  };
}

interface ImpersonationModalProps {
  isOpen: boolean;
  onClose: () => void;
}

const getRoleIcon = (role: string) => {
  switch (role) {
    case 'SUPERADMIN':
      return '👑';
    case 'ADMIN':
      return '⚡';
    case 'LAWYER':
      return '👨‍💼';
    case 'CLIENT':
      return '👤';
    case 'JURIST':
      return '📚';
    case 'JUDGE':
      return '⚖️';
    case 'MEDIATOR':
      return '🤝';
    case 'CONSULTANT':
      return '💼';
    case 'INVESTIGATOR':
      return '🔍';
    case 'EXPERT_WITNESS':
      return '🎓';
    case 'SUPPORT_STAFF':
      return '🛠️';
    case 'STUDENT':
      return '🎒';
    case 'NOTARY':
      return '📜';
    default:
      return '👤';
  }
};

const getRoleColor = (role: string) => {
  switch (role) {
    case 'SUPERADMIN':
      return 'bg-purple-100 text-purple-800';
    case 'ADMIN':
      return 'bg-red-100 text-red-800';
    case 'LAWYER':
      return 'bg-blue-100 text-blue-800';
    case 'CLIENT':
      return 'bg-green-100 text-green-800';
    case 'JURIST':
      return 'bg-indigo-100 text-indigo-800';
    case 'JUDGE':
      return 'bg-yellow-100 text-yellow-800';
    case 'MEDIATOR':
      return 'bg-pink-100 text-pink-800';
    case 'CONSULTANT':
      return 'bg-orange-100 text-orange-800';
    case 'INVESTIGATOR':
      return 'bg-teal-100 text-teal-800';
    case 'EXPERT_WITNESS':
      return 'bg-cyan-100 text-cyan-800';
    case 'SUPPORT_STAFF':
      return 'bg-gray-100 text-gray-800';
    case 'STUDENT':
      return 'bg-emerald-100 text-emerald-800';
    case 'NOTARY':
      return 'bg-amber-100 text-amber-800';
    default:
      return 'bg-gray-100 text-gray-800';
  }
};

export default function ImpersonationModal({ isOpen, onClose }: ImpersonationModalProps) {
  const [users, setUsers] = useState<User[]>([]);
  const [loading, setLoading] = useState(false);
  const [searchTerm, setSearchTerm] = useState('');
  const [roleFilter, setRoleFilter] = useState('all');
  const { impersonateUser } = useImpersonation();
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    if (isOpen) {
      fetchUsers();
    }
  }, [isOpen]);

  const fetchUsers = async () => {
    try {
      setLoading(true);
      const response = await fetch('/api/admin/users');
      console.log('API Response status:', response.status);
      console.log('API Response ok:', response.ok);
      
      if (!response.ok) {
        const errorData = await response.text();
        console.error('API Error response:', errorData);
        throw new Error(`Failed to fetch users: ${response.status} - ${errorData}`);
      }
      const data = await response.json();
      setUsers(data);
    } catch (error) {
      console.error('Error fetching users:', error);
      setError(error instanceof Error ? error.message : 'Failed to load users');
    } finally {
      setLoading(false);
    }
  };

  const handleImpersonate = async (user: User) => {
    try {
      console.log('🔄 Impersonation modal - Starting impersonation for user:', user);
      const success = await impersonateUser(user.id, user.name);
      if (success) {
        console.log('🔄 Impersonation modal - Success, closing modal');
        onClose();
      } else {
        console.error('🔄 Impersonation modal - Failed to impersonate user');
      }
    } catch (error) {
      console.error('🔄 Impersonation modal - Error during impersonation:', error);
      // Don't close modal on error, let user try again
    }
  };

  const filteredUsers = users.filter(user => {
    const matchesSearch = 
      user.name.toLowerCase().includes(searchTerm.toLowerCase()) ||
      user.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
      user.role.toLowerCase().includes(searchTerm.toLowerCase());
    
    const matchesRole = roleFilter === 'all' || user.role === roleFilter;
    
    return matchesSearch && matchesRole;
  });

  const uniqueRoles = Array.from(new Set(users.map(user => user.role))).sort();

  if (!isOpen) return null;

  return (
    <div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
      <div className="bg-white rounded-xl shadow-2xl max-w-4xl w-full mx-4 max-h-[80vh] flex flex-col">
        {/* Header */}
        <div className="flex items-center justify-between p-6 border-b border-gray-200">
          <div>
            <h2 className="text-2xl font-bold text-gray-900">Impersonate User</h2>
            <p className="text-gray-600 mt-1">Select a user to impersonate their account</p>
          </div>
          <button
            onClick={onClose}
            className="text-gray-400 hover:text-gray-600 transition-colors"
          >
            <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
            </svg>
          </button>
        </div>

        {/* Filters */}
        <div className="p-6 border-b border-gray-200 bg-gray-50">
          <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
            <div className="relative">
              <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-4 h-4" />
              <input
                type="text"
                placeholder="Search users by name, email, or role..."
                value={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              />
            </div>
            <div>
              <select
                value={roleFilter}
                onChange={(e) => setRoleFilter(e.target.value)}
                className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              >
                <option value="all">All Roles</option>
                {uniqueRoles.map(role => (
                  <option key={role} value={role}>
                    {getRoleIcon(role)} {role}
                  </option>
                ))}
              </select>
            </div>
          </div>
        </div>

        {/* Content */}
        <div className="flex-1 overflow-y-auto p-6">
          {loading ? (
            <div className="flex items-center justify-center py-12">
              <Loader2 className="w-8 h-8 animate-spin text-blue-600" />
              <span className="ml-2 text-gray-600">Loading users...</span>
            </div>
          ) : filteredUsers.length === 0 ? (
            <div className="text-center py-12">
              <User className="w-12 h-12 text-gray-400 mx-auto mb-4" />
              <h3 className="text-lg font-medium text-gray-900 mb-2">No users found</h3>
              <p className="text-gray-500">Try adjusting your search or filters.</p>
            </div>
          ) : (
            <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
              {filteredUsers.map((user) => (
                <div
                  key={user.id}
                  className="bg-white border border-gray-200 rounded-lg p-4 hover:shadow-md transition-shadow cursor-pointer"
                  onClick={() => handleImpersonate(user)}
                >
                  <div className="flex items-start space-x-3">
                    <div className="flex-shrink-0">
                      {user.image ? (
                        <Image
                          src={user.image}
                          alt={user.name}
                          width={48}
                          height={48}
                          className="rounded-full"
                        />
                      ) : (
                        <div className="w-12 h-12 bg-gray-200 rounded-full flex items-center justify-center">
                          <User className="w-6 h-6 text-gray-500" />
                        </div>
                      )}
                    </div>
                    <div className="flex-1 min-w-0">
                      <div className="flex items-center space-x-2 mb-1">
                        <h3 className="text-sm font-semibold text-gray-900 truncate">
                          {user.name}
                        </h3>
                        <span className={`px-2 py-1 rounded-full text-xs font-medium ${getRoleColor(user.role)}`}>
                          {getRoleIcon(user.role)} {user.role}
                        </span>
                      </div>
                      <div className="flex items-center text-xs text-gray-500 mb-2">
                        <Mail className="w-3 h-3 mr-1" />
                        <span className="truncate">{user.email}</span>
                      </div>
                      <div className="flex items-center text-xs text-gray-500 mb-2">
                        <Calendar className="w-3 h-3 mr-1" />
                        <span>Joined {new Date(user.createdAt).toLocaleDateString()}</span>
                      </div>
                      <div className="flex items-center space-x-4 text-xs text-gray-500">
                        <div className="flex items-center">
                          <FileText className="w-3 h-3 mr-1" />
                          <span>{user.stats.registrations} apps</span>
                        </div>
                        <div className="flex items-center">
                          <Gavel className="w-3 h-3 mr-1" />
                          <span>{user.stats.cases} cases</span>
                        </div>
                      </div>
                    </div>
                  </div>
                  <div className="mt-3 pt-3 border-t border-gray-100">
                    <button
                      className="w-full bg-blue-600 hover:bg-blue-700 text-white text-sm font-medium py-2 px-3 rounded-md transition-colors"
                    >
                      Impersonate
                    </button>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

        {/* Footer */}
        <div className="p-6 border-t border-gray-200 bg-gray-50">
          <div className="flex justify-between items-center">
            <div className="text-sm text-gray-600">
              {filteredUsers.length} user{filteredUsers.length !== 1 ? 's' : ''} found
            </div>
            <button
              onClick={onClose}
              className="px-4 py-2 bg-gray-300 hover:bg-gray-400 text-gray-800 rounded-md transition-colors"
            >
              Cancel
            </button>
          </div>
        </div>
      </div>
    </div>
  );
} 

CasperSecurity Mini