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/CaseList.tsx
import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import Link from 'next/link';
import { 
  Eye, 
  Edit, 
  FileText, 
  Calendar, 
  User, 
  MapPin, 
  Clock,
  Search,
  Filter,
  Plus
} from 'lucide-react';

interface CaseListProps {
  title?: string;
  subtitle?: string;
  showCreateButton?: boolean;
  createButtonText?: string;
  createButtonHref?: string;
  role?: string;
  userId?: string;
  className?: string;
}

interface LegalCase {
  id: string;
  title: string;
  description?: string;
  status: string;
  jurisdiction?: string;
  court?: string;
  priority?: string;
  createdAt: string;
  updatedAt: string;
  leadLawyerId?: string;
  createdBy?: string;
  clientId?: string;
  _count?: {
    registrations?: number;
    offers?: number;
    comments?: number;
  };
  leadLawyer?: {
    name?: string;
    email?: string;
  };
  client?: {
    name?: string;
    email?: string;
  };
}

const CaseList: React.FC<CaseListProps> = ({
  title = "Cases",
  subtitle = "Manage your cases",
  showCreateButton = false,
  createButtonText = "Create Case",
  createButtonHref = "/admin/cases/new",
  role,
  userId,
  className = ""
}) => {
  const { data: session } = useSession();
  const [cases, setCases] = useState<LegalCase[]>([]);
  const [loading, setLoading] = useState(true);
  const [searchQuery, setSearchQuery] = useState('');
  const [statusFilter, setStatusFilter] = useState('all');

  const isLawyer = session?.user?.role === 'LAWYER';
  const isVerified = session?.user?.isVerifiedLawyer || session?.user?.verificationStatus === 'VERIFIED_BARREAU';

  useEffect(() => {
    fetchCases();
  }, [role, userId]);

  const fetchCases = async () => {
    try {
      setLoading(true);
      let url = '/api/cases';
      
      // Add role-specific filtering
      if (role === 'LAWYER' && userId) {
        url = `/api/cases?role=lawyer&userId=${userId}`;
      } else if (role === 'CLIENT' && userId) {
        url = `/api/cases?role=client&userId=${userId}`;
      } else if (role === 'ADMIN' || role === 'SUPERADMIN') {
        url = '/api/cases?role=admin';
      }

      const response = await fetch(url);
      if (response.ok) {
        const data = await response.json();
        setCases(data.cases || data || []);
      }
    } catch (error) {
      console.error('Error fetching cases:', error);
    } finally {
      setLoading(false);
    }
  };

  const filteredCases = cases.filter(case_ => {
    const matchesSearch = case_.title.toLowerCase().includes(searchQuery.toLowerCase()) ||
                         case_.description?.toLowerCase().includes(searchQuery.toLowerCase()) ||
                         case_.jurisdiction?.toLowerCase().includes(searchQuery.toLowerCase());
    
    const matchesStatus = statusFilter === 'all' || case_.status === statusFilter;
    
    return matchesSearch && matchesStatus;
  });

  const getStatusColor = (status: string) => {
    switch (status.toLowerCase()) {
      case 'active': return 'bg-green-100 text-green-800';
      case 'pending': return 'bg-yellow-100 text-yellow-800';
      case 'closed': return 'bg-gray-100 text-gray-800';
      case 'draft': return 'bg-blue-100 text-blue-800';
      default: return 'bg-gray-100 text-gray-800';
    }
  };

  const getPriorityColor = (priority: string) => {
    switch (priority?.toLowerCase()) {
      case 'high': return 'bg-red-100 text-red-800';
      case 'medium': return 'bg-yellow-100 text-yellow-800';
      case 'low': return 'bg-green-100 text-green-800';
      default: return 'bg-gray-100 text-gray-800';
    }
  };

  if (loading) {
    return (
      <div className={`bg-white rounded-lg shadow-sm border ${className}`}>
        <div className="p-6">
          <div className="animate-pulse">
            <div className="h-4 bg-gray-200 rounded w-1/4 mb-4"></div>
            <div className="space-y-3">
              {[1, 2, 3].map((i) => (
                <div key={i} className="h-16 bg-gray-200 rounded"></div>
              ))}
            </div>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className={`bg-white rounded-lg shadow-sm border ${className}`}>
      {/* Verification Banner for Unverified Lawyers */}
      {isLawyer && !isVerified && (
        <div className="p-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-800 rounded-t">
          <strong>Account Not Verified:</strong> You must complete Barreau verification to create or manage cases. Sensitive actions are disabled until you are verified.
        </div>
      )}
      {/* Header */}
      <div className="p-6 border-b border-gray-200">
        <div className="flex items-center justify-between">
          <div>
            <h1 className="text-2xl font-bold text-gray-900">{title}</h1>
            <p className="text-gray-600 mt-1">{subtitle}</p>
          </div>
          {showCreateButton && (!isLawyer || isVerified) && (
            <Link
              href={createButtonHref}
              className="inline-flex items-center px-4 py-2 bg-blue-600 text-white text-sm font-medium rounded-lg hover:bg-blue-700 transition-colors"
            >
              <Plus className="h-4 w-4 mr-2" />
              {createButtonText}
            </Link>
          )}
          {showCreateButton && isLawyer && !isVerified && (
            <button
              className="inline-flex items-center px-4 py-2 bg-gray-300 text-gray-500 text-sm font-medium rounded-lg cursor-not-allowed"
              disabled
              title="You must be a verified lawyer to create a new case."
            >
              <Plus className="h-4 w-4 mr-2" />
              {createButtonText}
            </button>
          )}
        </div>
      </div>

      {/* Filters */}
      <div className="p-6 border-b border-gray-200">
        <div className="flex flex-col sm:flex-row gap-4">
          <div className="flex-1">
            <div className="relative">
              <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 h-4 w-4 text-gray-400" />
              <input
                type="text"
                placeholder="Search cases..."
                value={searchQuery}
                onChange={(e) => setSearchQuery(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-blue-500"
              />
            </div>
          </div>
          <div className="flex gap-2">
            <select
              value={statusFilter}
              onChange={(e) => setStatusFilter(e.target.value)}
              className="px-3 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
            >
              <option value="all">All Status</option>
              <option value="active">Active</option>
              <option value="pending">Pending</option>
              <option value="closed">Closed</option>
              <option value="draft">Draft</option>
            </select>
          </div>
        </div>
      </div>

      {/* Cases List */}
      <div className="divide-y divide-gray-200">
        {filteredCases.length === 0 ? (
          <div className="p-6 text-center">
            <FileText className="h-12 w-12 text-gray-400 mx-auto mb-4" />
            <h3 className="text-lg font-medium text-gray-900 mb-2">No cases found</h3>
            <p className="text-gray-600">
              {searchQuery || statusFilter !== 'all' 
                ? 'Try adjusting your search or filters'
                : 'Get started by creating your first case'
              }
            </p>
          </div>
        ) : (
          filteredCases.map((case_) => (
            <div key={case_.id} className="p-6 hover:bg-gray-50 transition-colors">
              <div className="flex items-start justify-between">
                <div className="flex-1">
                  <div className="flex items-center gap-3 mb-2">
                    <h3 className="text-lg font-semibold text-gray-900">
                      {case_.title}
                    </h3>
                    <span className={`px-2 py-1 text-xs font-medium rounded-full ${getStatusColor(case_.status)}`}>
                      {case_.status}
                    </span>
                    {case_.priority && (
                      <span className={`px-2 py-1 text-xs font-medium rounded-full ${getPriorityColor(case_.priority)}`}>
                        {case_.priority}
                      </span>
                    )}
                  </div>
                  
                  {case_.description && (
                    <p className="text-gray-600 mb-3 line-clamp-2">
                      {case_.description}
                    </p>
                  )}

                  <div className="flex flex-wrap items-center gap-4 text-sm text-gray-500">
                    {case_.jurisdiction && (
                      <div className="flex items-center gap-1">
                        <MapPin className="h-4 w-4" />
                        <span>{case_.jurisdiction}</span>
                      </div>
                    )}
                    {case_.court && (
                      <div className="flex items-center gap-1">
                        <FileText className="h-4 w-4" />
                        <span>{case_.court}</span>
                      </div>
                    )}
                    <div className="flex items-center gap-1">
                      <Calendar className="h-4 w-4" />
                      <span>Created {new Date(case_.createdAt).toLocaleDateString()}</span>
                    </div>
                    {case_.leadLawyer?.name && (
                      <div className="flex items-center gap-1">
                        <User className="h-4 w-4" />
                        <span>Lead: {case_.leadLawyer.name}</span>
                      </div>
                    )}
                    {case_._count?.registrations && (
                      <div className="flex items-center gap-1">
                        <span>📝 {case_._count.registrations} applications</span>
                      </div>
                    )}
                  </div>
                </div>

                <div className="flex items-center gap-2 ml-4">
                  <Link
                    href={`/public/cases/${case_.id}`}
                    className="inline-flex items-center px-3 py-1 text-sm text-blue-600 hover:text-blue-800 hover:bg-blue-50 rounded-md transition-colors"
                  >
                    <Eye className="h-4 w-4 mr-1" />
                    View
                  </Link>
                  
                  {(role === 'ADMIN' || role === 'SUPERADMIN' || 
                    (role === 'LAWYER' && case_.leadLawyerId === userId) ||
                    (role === 'CLIENT' && case_.createdBy === userId)) && (
                    <Link
                      href={`/admin/cases/${case_.id}/edit`}
                      className="inline-flex items-center px-3 py-1 text-sm text-gray-600 hover:text-gray-800 hover:bg-gray-50 rounded-md transition-colors"
                    >
                      <Edit className="h-4 w-4 mr-1" />
                      Edit
                    </Link>
                  )}
                </div>
              </div>
            </div>
          ))
        )}
      </div>
    </div>
  );
};

export default CaseList; 

CasperSecurity Mini