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/public_html/src/components/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/domains/lavocat.ca/public_html/src/components/CaseWidget.tsx
'use client';

import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import { 
  Search, 
  Filter, 
  Plus, 
  Eye, 
  Edit, 
  Calendar, 
  User, 
  Building,
  Clock,
  CheckCircle,
  XCircle,
  AlertTriangle,
  DollarSign,
  MapPin,
  FileText
} from 'lucide-react';
// UI components replaced with standard HTML elements and Tailwind CSS

interface Case {
  id: string;
  title: string;
  description: string;
  caseNumber?: string;
  caseType: string;
  status: string;
  jurisdiction: string;
  court?: string;
  priority: string;
  budget?: number;
  expectedDuration?: number;
  filingDate?: string;
  applicationDeadline?: string;
  isAcceptingApplications: boolean;
  requiresApproval: boolean;
  createdAt: string;
  updatedAt: string;
  logoUrl?: string;
  leadLawyer: {
    id: string;
    name: string;
    email: string;
    title?: string;
    specialization?: string;
  };
  creator: {
    id: string;
    name: string;
    email: string;
  };
  firmName?: string;
  _count: {
    registrations: number;
    caseAssignments: number;
    caseUpdates: number;
  };
}

interface CaseWidgetProps {
  title?: string;
  maxCases?: number;
  showCreateButton?: boolean;
  showViewAll?: boolean;
  showMyCases?: boolean;
  showAssignedCases?: boolean;
  showPublicCases?: boolean;
  showFilters?: boolean;
  className?: string;
}

const CaseWidget: React.FC<CaseWidgetProps> = ({
  title = 'Case Management',
  maxCases = 5,
  showCreateButton = true,
  showViewAll = true,
  showMyCases = false,
  showAssignedCases = false,
  showPublicCases = false,
  showFilters = true,
  className = ''
}) => {
  const { data: session } = useSession();
  const router = useRouter();
  
  const [cases, setCases] = useState<Case[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState('');
  const [searchTerm, setSearchTerm] = useState('');
  const [statusFilter, setStatusFilter] = useState('all');

  useEffect(() => {
    if (session?.user) {
      fetchCases();
    }
  }, [session]);

  const fetchCases = async () => {
    try {
      setLoading(true);
      
      // Build query parameters based on props
      const params = new URLSearchParams();
      if (showMyCases) params.append('myCases', 'true');
      if (showAssignedCases) params.append('assignedCases', 'true');
      if (showPublicCases) params.append('publicCases', 'true');
      
      const response = await fetch(`/api/cases?${params.toString()}`, {
        credentials: 'same-origin',
      });

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

  const getStatusIcon = (status: string) => {
    switch (status) {
      case 'active': return <CheckCircle className="h-4 w-4 text-green-500" />;
      case 'pending': return <Clock className="h-4 w-4 text-yellow-500" />;
      case 'closed': return <XCircle className="h-4 w-4 text-gray-500" />;
      case 'suspended': return <AlertTriangle className="h-4 w-4 text-red-500" />;
      default: return <Clock className="h-4 w-4 text-gray-400" />;
    }
  };

  const getPriorityColor = (priority: string) => {
    switch (priority) {
      case 'urgent': return 'bg-red-100 text-red-800 border-red-200';
      case 'high': return 'bg-orange-100 text-orange-800 border-orange-200';
      case 'medium': return 'bg-blue-100 text-blue-800 border-blue-200';
      case 'low': return 'bg-gray-100 text-gray-800 border-gray-200';
      default: return 'bg-gray-100 text-gray-800 border-gray-200';
    }
  };

  const filteredCases = cases
    .filter(case_ => 
      case_.title.toLowerCase().includes(searchTerm.toLowerCase()) ||
      case_.description.toLowerCase().includes(searchTerm.toLowerCase()) ||
      (case_.caseNumber && case_.caseNumber.toLowerCase().includes(searchTerm.toLowerCase()))
    )
    .filter(case_ => statusFilter === 'all' || case_.status === statusFilter)
    .slice(0, maxCases);

  const handleCreateCase = () => {
    router.push('/hire/new-case');
  };

  const handleViewCase = (caseId: string) => {
    router.push(`/admin/cases/${caseId}`);
  };

  const handleEditCase = (caseId: string) => {
    router.push(`/admin/cases/${caseId}?edit=true`);
  };

  const handleViewAll = () => {
    router.push('/admin/cases');
  };

  if (loading) {
    return (
      <div className={`bg-white rounded-lg shadow-sm border ${className}`}>
        <div className="p-6 border-b border-gray-200">
          <div className="flex items-center justify-between">
            <h3 className="text-lg font-semibold text-gray-900">{title}</h3>
            {showCreateButton && (
              <button 
                className="inline-flex items-center px-3 py-2 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
                disabled
              >
                <Plus className="h-4 w-4 mr-1" />
                Create Case
              </button>
            )}
          </div>
        </div>
        <div className="p-6">
          <div className="space-y-3">
            {[...Array(3)].map((_, i) => (
              <div key={i} className="animate-pulse">
                <div className="h-20 bg-gray-200 rounded"></div>
              </div>
            ))}
          </div>
        </div>
      </div>
    );
  }

  if (error) {
    return (
      <div className={`bg-white rounded-lg shadow-sm border ${className}`}>
        <div className="p-6 border-b border-gray-200">
          <h3 className="text-lg font-semibold text-gray-900">{title}</h3>
        </div>
        <div className="p-6">
          <div className="text-center py-4">
            <AlertTriangle className="h-8 w-8 text-red-500 mx-auto mb-2" />
            <p className="text-red-600">{error}</p>
            <button 
              className="inline-flex items-center px-3 py-2 border border-gray-300 text-gray-700 text-sm rounded-lg hover:bg-gray-50 transition-colors mt-2"
              onClick={fetchCases}
            >
              Retry
            </button>
          </div>
        </div>
      </div>
    );
  }

  return (
    <div className={`bg-white rounded-lg shadow-sm border ${className}`}>
      <div className="p-6 border-b border-gray-200">
        <div className="flex items-center justify-between">
          <div className="flex items-center gap-2">
            <Building className="h-5 w-5" />
            <h3 className="text-lg font-semibold text-gray-900">{title}</h3>
            <span className="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800 border border-gray-200">
              {cases.length}
            </span>
          </div>
          {showCreateButton && (
            <button 
              className="inline-flex items-center px-3 py-2 bg-blue-600 text-white text-sm rounded-lg hover:bg-blue-700 transition-colors"
              onClick={handleCreateCase}
              title="Create a new case"
            >
              <Plus className="h-4 w-4 mr-1" />
              Create Case
            </button>
          )}
        </div>
      </div>
      
      <div className="p-6 space-y-4">
        {/* Search and Filter */}
        {showFilters && (
          <div className="flex gap-2">
            <div className="flex-1 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={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent"
              />
            </div>
            <select 
              value={statusFilter} 
              onChange={(e) => setStatusFilter(e.target.value)}
              className="px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-32"
            >
              <option value="all">All Status</option>
              <option value="active">Active</option>
              <option value="pending">Pending</option>
              <option value="closed">Closed</option>
              <option value="suspended">Suspended</option>
            </select>
          </div>
        )}

        {/* Cases List */}
        {filteredCases.length === 0 ? (
          <div className="text-center py-8">
            <Building className="h-12 w-12 text-gray-400 mx-auto mb-3" />
            <p className="text-gray-500 mb-2">
              {searchTerm || statusFilter !== 'all' 
                ? 'No cases match your search criteria' 
                : 'No cases found'
              }
            </p>
            {showCreateButton && (
              <button 
                className="inline-flex items-center px-3 py-2 border border-gray-300 text-gray-700 text-sm rounded-lg hover:bg-gray-50 transition-colors"
                onClick={handleCreateCase}
              >
                <Plus className="h-4 w-4 mr-1" />
                Create Your First Case
              </button>
            )}
          </div>
        ) : (
          <div className="space-y-3">
            {filteredCases.map((case_) => (
              <div
                key={case_.id}
                className="border rounded-lg p-4 hover:bg-gray-50 transition-colors cursor-pointer"
                onClick={() => handleViewCase(case_.id)}
              >
                <div className="flex items-start gap-3 mb-2">
                  {/* Case Logo */}
                  <div className="flex-shrink-0">
                    {case_.logoUrl ? (
                      <img
                        src={case_.logoUrl}
                        alt={`${case_.title} Logo`}
                        className="w-12 h-12 rounded-lg object-cover border border-gray-200"
                      />
                    ) : (
                      <div className="w-12 h-12 rounded-lg bg-gradient-to-br from-blue-500/20 to-indigo-500/20 border border-gray-200 flex items-center justify-center">
                        <FileText className="w-6 h-6 text-gray-600" />
                      </div>
                    )}
                  </div>
                  
                  <div className="flex-1 min-w-0">
                    <h3 className="font-semibold text-gray-900 truncate">
                      {case_.title}
                    </h3>
                    {case_.caseNumber && (
                      <p className="text-sm text-gray-500">#{case_.caseNumber}</p>
                    )}
                  </div>
                  <div className="flex items-center gap-2 ml-2">
                    {getStatusIcon(case_.status)}
                    <span className={`inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium border ${getPriorityColor(case_.priority)}`}>
                      {case_.priority}
                    </span>
                  </div>
                </div>

                <p className="text-sm text-gray-600 mb-3 line-clamp-2">
                  {case_.description}
                </p>

                <div className="flex items-center justify-between text-xs text-gray-500">
                  <div className="flex items-center gap-4">
                    <div className="flex items-center gap-1">
                      <User className="h-3 w-3" />
                      <span>{case_.leadLawyer.name}</span>
                    </div>
                    <div className="flex items-center gap-1">
                      <MapPin className="h-3 w-3" />
                      <span>{case_.jurisdiction}</span>
                    </div>
                    <div className="flex items-center gap-1">
                      <Calendar className="h-3 w-3" />
                      <span>{new Date(case_.createdAt).toLocaleDateString()}</span>
                    </div>
                  </div>
                  
                  <div className="flex items-center gap-1">
                    <span>{case_._count.registrations} applications</span>
                  </div>
                </div>

                {/* Quick Actions */}
                <div className="flex items-center gap-2 mt-3 pt-3 border-t">
                  <button
                    className="inline-flex items-center px-3 py-2 border border-gray-300 text-gray-700 text-sm rounded-lg hover:bg-gray-50 transition-colors"
                    onClick={(e) => {
                      e.stopPropagation();
                      handleViewCase(case_.id);
                    }}
                  >
                    <Eye className="h-3 w-3 mr-1" />
                    View
                  </button>
                  <button
                    className="inline-flex items-center px-3 py-2 border border-gray-300 text-gray-700 text-sm rounded-lg hover:bg-gray-50 transition-colors"
                    onClick={(e) => {
                      e.stopPropagation();
                      handleEditCase(case_.id);
                    }}
                  >
                    <Edit className="h-3 w-3 mr-1" />
                    Edit
                  </button>
                  {case_.budget && (
                    <div className="flex items-center gap-1 text-xs text-gray-500">
                      <DollarSign className="h-3 w-3" />
                      <span>${case_.budget.toLocaleString()}</span>
                    </div>
                  )}
                </div>
              </div>
            ))}
          </div>
        )}

        {/* View All Button */}
        {showViewAll && cases.length > maxCases && (
          <div className="text-center pt-4 border-t">
            <button 
              className="inline-flex items-center px-4 py-2 border border-gray-300 text-gray-700 rounded-lg hover:bg-gray-50 transition-colors"
              onClick={handleViewAll}
            >
              View All Cases ({cases.length})
            </button>
          </div>
        )}
      </div>
    </div>
  );
};

export default CaseWidget; 

CasperSecurity Mini