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/pages/lawyer/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/pages/lawyer/team.tsx
import React, { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import LayoutWithSidebar from '@/components/LayoutWithSidebar';
import { toast } from 'react-hot-toast';
import { User, Shield, Search, UserPlus, UserMinus, Edit, Eye, CheckCircle, XCircle } from 'lucide-react';
import { useRequireRole, USER_ROLES } from '../../lib/auth-utils';

interface TeamMember {
  id: string;
  name: string;
  email: string;
  role: string;
  specialization?: string;
  yearsOfExperience?: number;
  isVerified?: boolean;
  isProfilePublic?: boolean;
  profilePicture?: string;
  phone?: string;
  lastActive?: string;
  status?: 'ONLINE' | 'OFFLINE' | 'AWAY';
}

const LawyerTeam: React.FC = () => {
  const { data: session, status } = useSession();
  const router = useRouter();
  const [team, setTeam] = useState<TeamMember[]>([]);
  const [loading, setLoading] = useState(true);
  const [search, setSearch] = useState('');
  const [filter, setFilter] = useState('all');
  const [updating, setUpdating] = useState(false);

  const { isAuthorized } = useRequireRole([
    USER_ROLES.LAWYER,
    USER_ROLES.ADMIN,
    USER_ROLES.SUPERADMIN
  ], '/auth/login');

  useEffect(() => {
    if (status === 'loading') return;
    if (!session || !['LAWYER', 'ADMIN', 'SUPERADMIN'].includes(session.user.role)) {
      router.push('/');
      return;
    }
    fetchTeam();
  }, [session, status, router]);

  const fetchTeam = async () => {
    try {
      setLoading(true);
      const response = await fetch('/api/lawyer/team');
      if (response.ok) {
        const data = await response.json();
        setTeam(data.team || []);
      } else {
        toast.error('Failed to load team');
      }
    } catch (error) {
      toast.error('Failed to load team');
    } finally {
      setLoading(false);
    }
  };

  const handleRoleChange = async (userId: string, newRole: string) => {
    try {
      setUpdating(true);
      const response = await fetch(`/api/lawyer/team/${userId}/role`, {
        method: 'PATCH',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ role: newRole })
      });
      if (response.ok) {
        toast.success('Role updated');
        fetchTeam();
      } else {
        toast.error('Failed to update role');
      }
    } catch (error) {
      toast.error('Failed to update role');
    } finally {
      setUpdating(false);
    }
  };

  const handleRemove = async (userId: string) => {
    try {
      setUpdating(true);
      const response = await fetch(`/api/lawyer/team/${userId}`, {
        method: 'DELETE'
      });
      if (response.ok) {
        toast.success('Team member removed');
        fetchTeam();
      } else {
        toast.error('Failed to remove member');
      }
    } catch (error) {
      toast.error('Failed to remove member');
    } finally {
      setUpdating(false);
    }
  };

  const filteredTeam = team.filter(member => {
    const matchesSearch = member.name?.toLowerCase().includes(search.toLowerCase()) || member.email?.toLowerCase().includes(search.toLowerCase());
    const matchesFilter = filter === 'all' || member.role === filter;
    return matchesSearch && matchesFilter;
  });

  if (status === 'loading' || loading) {
    return (
      <LayoutWithSidebar>
        <div className="flex items-center justify-center min-h-screen">
          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600"></div>
        </div>
      </LayoutWithSidebar>
    );
  }

  return (
    <LayoutWithSidebar>
      <div className="max-w-7xl mx-auto px-4 py-8">
        <div className="mb-8 flex items-center justify-between">
          <div>
            <h1 className="text-3xl font-bold text-gray-900">Team Management</h1>
            <p className="text-gray-600 mt-1">Manage your legal team, assign roles, and track status</p>
          </div>
          <button
            onClick={() => router.push('/lawyer/team/invite')}
            className="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
          >
            <UserPlus className="h-4 w-4 mr-2" /> Invite Member
          </button>
        </div>

        <div className="flex flex-col md:flex-row gap-4 mb-6">
          <div className="flex-1">
            <div className="relative">
              <Search className="h-4 w-4 absolute left-3 top-3 text-gray-400" />
              <input
                type="text"
                placeholder="Search team..."
                value={search}
                onChange={e => setSearch(e.target.value)}
                className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500"
              />
            </div>
          </div>
          <select
            value={filter}
            onChange={e => setFilter(e.target.value)}
            className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-green-500"
          >
            <option value="all">All Roles</option>
            <option value="LAWYER">Lawyer</option>
            <option value="ADMIN">Admin</option>
            <option value="SUPERADMIN">Super Admin</option>
            <option value="ASSISTANT">Assistant</option>
            <option value="PARALEGAL">Paralegal</option>
          </select>
        </div>

        <div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-hidden">
          <div className="px-6 py-4 border-b border-gray-200">
            <h2 className="text-lg font-semibold text-gray-900">
              Team Members ({filteredTeam.length})
            </h2>
          </div>
          {filteredTeam.length === 0 ? (
            <div className="p-12 text-center">
              <div className="max-w-sm mx-auto">
                <User className="h-12 w-12 text-gray-400 mx-auto mb-4" />
                <h3 className="text-lg font-medium text-gray-900 mb-2">No team members found</h3>
                <p className="text-gray-500">Try adjusting your search or filters</p>
              </div>
            </div>
          ) : (
            <div className="divide-y divide-gray-200">
              {filteredTeam.map(member => (
                <div key={member.id} className="p-6 hover:bg-gray-50 flex items-center justify-between">
                  <div className="flex items-center gap-4">
                    <div className="h-12 w-12 rounded-full bg-gray-200 flex items-center justify-center overflow-hidden">
                      {member.profilePicture ? (
                        <img src={member.profilePicture} alt={member.name} className="h-12 w-12 object-cover" />
                      ) : (
                        <User className="h-8 w-8 text-gray-400" />
                      )}
                    </div>
                    <div>
                      <div className="flex items-center gap-2">
                        <span className="font-semibold text-gray-900">{member.name}</span>
                        {member.isVerified && <CheckCircle className="h-4 w-4 text-green-500" />}
                        {member.isProfilePublic && <Shield className="h-4 w-4 text-blue-500" />}
                      </div>
                      <div className="text-sm text-gray-600">{member.email}</div>
                      <div className="text-xs text-gray-500">{member.role} {member.specialization && `• ${member.specialization}`}</div>
                      {member.phone && <div className="text-xs text-gray-400">{member.phone}</div>}
                    </div>
                  </div>
                  <div className="flex items-center gap-2">
                    <button
                      onClick={() => router.push(`/lawyer/team/${member.id}`)}
                      className="p-2 text-gray-600 hover:text-gray-800 hover:bg-gray-100 rounded-lg transition-colors"
                      title="View Profile"
                    >
                      <Eye className="h-4 w-4" />
                    </button>
                    <button
                      onClick={() => router.push(`/lawyer/team/${member.id}/tasks`)}
                      className="p-2 text-purple-600 hover:text-purple-800 hover:bg-purple-100 rounded-lg transition-colors"
                      title="Assign/View Tasks"
                    >
                      <Edit className="h-4 w-4" />
                    </button>
                    <select
                      value={member.role}
                      onChange={e => handleRoleChange(member.id, e.target.value)}
                      disabled={updating}
                      className="px-2 py-1 border border-gray-300 rounded-lg text-xs"
                    >
                      <option value="LAWYER">Lawyer</option>
                      <option value="ADMIN">Admin</option>
                      <option value="SUPERADMIN">Super Admin</option>
                      <option value="ASSISTANT">Assistant</option>
                      <option value="PARALEGAL">Paralegal</option>
                    </select>
                    <button
                      onClick={() => handleRemove(member.id)}
                      disabled={updating}
                      className="p-2 text-red-600 hover:text-red-800 hover:bg-red-100 rounded-lg transition-colors"
                      title="Remove from Team"
                    >
                      <UserMinus className="h-4 w-4" />
                    </button>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>
      </div>
    </LayoutWithSidebar>
  );
};

export default LawyerTeam; 

CasperSecurity Mini