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/backups/lavocat.quebec/backup-20250730-021618/src/pages/lawyer/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/backups/lavocat.quebec/backup-20250730-021618/src/pages/lawyer/applications.tsx
import { useEffect, useState } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import LayoutWithSidebar from '@/components/LayoutWithSidebar';
import { canAccessLawyer } from '@/lib/auth-utils';
import { format } from 'date-fns';
import Link from 'next/link';
import toast from 'react-hot-toast';
import dynamic from 'next/dynamic';

const PrivateChat = dynamic(() => import('@/components/Chat/PrivateChat'), { ssr: false });

interface Registration {
  id: string;
  firstName: string;
  lastName: string;
  email: string;
  phone: string;
  status: string;
  createdAt: string;
  updatedAt: string;
  userId: string;
  detaineeInfo: {
    name: string;
    facility: string;
    inmateId: string;
  } | null;
  legalCase: {
    id: string;
    title: string;
    caseNumber: string;
    logoUrl?: string;
    leadLawyer: {
      id: string;
      name: string;
      email: string;
    };
  } | null;
  user: {
    id: string;
    name: string;
    email: string;
  } | null;
  creator: {
    id: string;
    name: string;
    email: string;
  } | null;
  documents: {
    id: string;
    name: string;
    type: string;
  }[];
}

const getStatusColor = (status: string) => {
  switch (status) {
    case 'APPROVED':
    case 'COMPLETED':
    case 'PAYMENT_RECEIVED':
      return 'bg-green-100 text-green-800';
    case 'REJECTED':
    case 'DOCUMENTS_EXPIRED':
    case 'INFORMATION_MISMATCH':
    case 'ESCALATED':
      return 'bg-red-100 text-red-800';
    case 'PENDING':
    case 'ADDITIONAL_INFO_NEEDED':
    case 'PENDING_PAYMENT':
      return 'bg-yellow-100 text-yellow-800';
    case 'MISSING_DOCUMENTS':
    case 'DOCUMENTS_INCOMPLETE':
      return 'bg-orange-100 text-orange-800';
    case 'DOCUMENTS_UNDER_REVIEW':
    case 'PENDING_LAWYER_APPROVAL':
    case 'PENDING_FACILITY_APPROVAL':
      return 'bg-blue-100 text-blue-800';
    case 'VERIFICATION_IN_PROGRESS':
    case 'FINAL_REVIEW':
      return 'bg-purple-100 text-purple-800';
    case 'ON_HOLD':
      return 'bg-gray-100 text-gray-800';
    case 'WebAd':
      return 'bg-pink-100 text-pink-800';
    default:
      return 'bg-yellow-100 text-yellow-800';
  }
};

const getStatusLabel = (status: string) => {
  const statusMap: { [key: string]: string } = {
    PENDING: 'Pending',
    APPROVED: 'Approved',
    REJECTED: 'Rejected',
    MISSING_DOCUMENTS: 'Missing Documents',
    DOCUMENTS_UNDER_REVIEW: 'Documents Under Review',
    ADDITIONAL_INFO_NEEDED: 'Additional Info Needed',
    VERIFICATION_IN_PROGRESS: 'Verification in Progress',
    LAWYER_VERIFICATION: 'Lawyer Verification',
    FACILITY_VERIFICATION: 'Facility Verification',
    DOCUMENTS_EXPIRED: 'Documents Expired',
    DOCUMENTS_INCOMPLETE: 'Documents Incomplete',
    INFORMATION_MISMATCH: 'Information Mismatch',
    PENDING_PAYMENT: 'Pending Payment',
    PAYMENT_RECEIVED: 'Payment Received',
    PENDING_LAWYER_APPROVAL: 'Pending Lawyer Approval',
    PENDING_FACILITY_APPROVAL: 'Pending Facility Approval',
    ON_HOLD: 'On Hold',
    ESCALATED: 'Escalated',
    FINAL_REVIEW: 'Final Review',
    COMPLETED: 'Completed',
    WebAd: 'Web Advertisement'
  };
  return statusMap[status] || status;
};

// Status icon component
const StatusIcon = ({ status }: { status: string }) => {
  const iconMap: { [key: string]: JSX.Element } = {
    APPROVED: <span className="text-green-500 text-2xl">βœ”οΈ</span>,
    REJECTED: <span className="text-red-500 text-2xl">❌</span>,
    PENDING: <span className="text-yellow-500 text-2xl">⏳</span>,
    DOCUMENTS_UNDER_REVIEW: <span className="text-blue-500 text-2xl">πŸ“„</span>,
    MISSING_DOCUMENTS: <span className="text-orange-500 text-2xl">πŸ“„</span>,
    PENDING_FACILITY_APPROVAL: <span className="text-blue-400 text-2xl">🏒</span>,
    WebAd: <span className="text-pink-500 text-2xl">🌐</span>,
  };
  return iconMap[status] || <span className="text-gray-400 text-2xl">πŸ“</span>;
};

const LawyerApplications = () => {
  const { data: session, status } = useSession();
  const router = useRouter();
  const [registrations, setRegistrations] = useState<Registration[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);
  const [searchTerm, setSearchTerm] = useState('');
  const [statusFilter, setStatusFilter] = useState('all');
  const [caseFilter, setCaseFilter] = useState('all');
  const [sortBy, setSortBy] = useState('createdAt');
  const [sortOrder, setSortOrder] = useState<'asc' | 'desc'>('desc');
  const [showPrivateChat, setShowPrivateChat] = useState<{ open: boolean; registrationId: string | null }>({
    open: false,
    registrationId: null,
  });

  useEffect(() => {
    if (status === 'loading') return;
    
    if (status === 'unauthenticated' || (status === 'authenticated' && !canAccessLawyer(session))) {
      router.push('/auth/login');
      return;
    }

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

  const fetchApplications = async () => {
    try {
      setLoading(true);
      const response = await fetch('/api/lawyer/applications');
      
      if (!response.ok) {
        throw new Error('Failed to fetch applications');
      }
      
      const data = await response.json();
      setRegistrations(data);
    } catch (err) {
      setError('Failed to load applications');
      console.error('Error fetching applications:', err);
    } finally {
      setLoading(false);
    }
  };

  const handleStatusUpdate = async (registrationId: string, newStatus: string) => {
    try {
      const response = await fetch(`/api/lawyer/applications/${registrationId}/status`, {
        method: 'PATCH',
        headers: {
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({ status: newStatus }),
      });

      if (!response.ok) {
        throw new Error('Failed to update status');
      }

      // Update the registration in the list
      setRegistrations(prev => prev.map(reg => 
        reg.id === registrationId ? { ...reg, status: newStatus } : reg
      ));

      toast.success('Status updated successfully');
    } catch (err) {
      toast.error('Failed to update status');
      console.error('Error updating status:', err);
    }
  };

  // Filter and sort registrations
  const filteredRegistrations = registrations
    .filter(reg => {
      const matchesSearch = 
        reg.firstName.toLowerCase().includes(searchTerm.toLowerCase()) ||
        reg.lastName.toLowerCase().includes(searchTerm.toLowerCase()) ||
        reg.email.toLowerCase().includes(searchTerm.toLowerCase()) ||
        reg.detaineeInfo?.name.toLowerCase().includes(searchTerm.toLowerCase());
      
      const matchesStatus = statusFilter === 'all' || reg.status === statusFilter;
      const matchesCase = caseFilter === 'all' || reg.legalCase?.id === caseFilter;
      
      return matchesSearch && matchesStatus && matchesCase;
    })
    .sort((a, b) => {
      let aValue: any, bValue: any;
      
      switch (sortBy) {
        case 'name':
          aValue = `${a.firstName} ${a.lastName}`;
          bValue = `${b.firstName} ${b.lastName}`;
          break;
        case 'email':
          aValue = a.email;
          bValue = b.email;
          break;
        case 'status':
          aValue = a.status;
          bValue = b.status;
          break;
        case 'createdAt':
        default:
          aValue = new Date(a.createdAt);
          bValue = new Date(b.createdAt);
          break;
      }
      
      if (sortOrder === 'asc') {
        return aValue > bValue ? 1 : -1;
      } else {
        return aValue < bValue ? 1 : -1;
      }
    });

  // Get unique cases and statuses for filters
  const uniqueCases = Array.from(new Set(registrations.map(reg => reg.legalCase?.id).filter(Boolean)));
  const uniqueStatuses = Array.from(new Set(registrations.map(reg => reg.status)));

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

  return (
    <LayoutWithSidebar>
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
        {/* Header */}
        <div className="mb-8">
          <div className="flex justify-between items-center">
            <div>
              <h1 className="text-3xl font-bold text-gray-900">My Applications</h1>
              <p className="mt-2 text-gray-600">Manage applications for cases where you are the lead lawyer</p>
            </div>
            <div className="flex gap-2">
              <Link
                href="/hire/new-case"
                className="bg-blue-600 hover:bg-blue-700 text-white px-4 py-2 rounded-lg transition-colors font-semibold shadow"
              >
                + New Application
              </Link>
              <Link
                href="/lawyer/dashboard"
                className="bg-gray-600 hover:bg-gray-700 text-white px-4 py-2 rounded-lg transition-colors"
              >
                Back to Dashboard
              </Link>
            </div>
          </div>
        </div>

        {/* Stats */}
        <div className="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="text-lg font-semibold text-gray-900">Total Applications</h3>
            <p className="text-3xl font-bold text-blue-600">{registrations.length}</p>
          </div>
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="text-lg font-semibold text-gray-900">Pending Review</h3>
            <p className="text-3xl font-bold text-yellow-600">
              {registrations.filter(r => r.status === 'PENDING').length}
            </p>
          </div>
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="text-lg font-semibold text-gray-900">Approved</h3>
            <p className="text-3xl font-bold text-green-600">
              {registrations.filter(r => r.status === 'APPROVED').length}
            </p>
          </div>
          <div className="bg-white rounded-lg shadow p-6">
            <h3 className="text-lg font-semibold text-gray-900">Active Cases</h3>
            <p className="text-3xl font-bold text-purple-600">
              {uniqueCases.length}
            </p>
          </div>
        </div>

        {/* Filters */}
        <div className="bg-white rounded-lg shadow p-6 mb-8">
          <h2 className="text-xl font-semibold text-gray-900 mb-4">Filters</h2>
          <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
            {/* Search */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Search</label>
              <input
                type="text"
                placeholder="Search by name, email, or detainee..."
                value={searchTerm}
                onChange={(e) => setSearchTerm(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
              />
            </div>

            {/* Status Filter */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Status</label>
              <select
                value={statusFilter}
                onChange={(e) => setStatusFilter(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
              >
                <option value="all">All Statuses</option>
                {uniqueStatuses.map(status => (
                  <option key={status} value={status}>{getStatusLabel(status)}</option>
                ))}
              </select>
            </div>

            {/* Sort By */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Sort By</label>
              <select
                value={sortBy}
                onChange={(e) => setSortBy(e.target.value)}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
              >
                <option value="createdAt">Date Created</option>
                <option value="name">Name</option>
                <option value="email">Email</option>
                <option value="status">Status</option>
              </select>
            </div>

            {/* Sort Order */}
            <div>
              <label className="block text-sm font-medium text-gray-700 mb-2">Order</label>
              <select
                value={sortOrder}
                onChange={(e) => setSortOrder(e.target.value as 'asc' | 'desc')}
                className="w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500"
              >
                <option value="desc">Newest First</option>
                <option value="asc">Oldest First</option>
              </select>
            </div>
          </div>
        </div>

        {/* Applications Cards */}
        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex justify-between items-center mb-6">
            <h2 className="text-xl font-semibold text-gray-900">
              Applications ({filteredRegistrations.length})
            </h2>
          </div>
          
          {error && (
            <div className="p-4 bg-red-50 border-b border-red-200 mb-6">
              <p className="text-red-600">{error}</p>
            </div>
          )}

          {filteredRegistrations.length === 0 ? (
            <div className="text-center py-12">
              <div className="bg-gray-100 rounded-full w-16 h-16 flex items-center justify-center mx-auto mb-4">
                <svg className="w-8 h-8 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
                </svg>
              </div>
              <h3 className="text-lg font-medium text-gray-900 mb-2">No applications found</h3>
              <p className="text-gray-500">Try adjusting your filters to see more results.</p>
            </div>
          ) : (
            <div className="grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-6">
              {filteredRegistrations.map(registration => (
                <div key={registration.id} className="bg-white rounded-2xl shadow-xl flex flex-col border-2 p-6 min-h-[400px] border-primary/10">
                  {/* Status Icon & Badge */}
                  <div className="flex items-center justify-between mb-4">
                    <div className="flex items-center">
                      <StatusIcon status={registration.status} />
                      <span className={`ml-3 px-3 py-1 rounded-full text-sm font-bold ${getStatusColor(registration.status)}`}>
                        {getStatusLabel(registration.status)}
                      </span>
                    </div>
                    {registration.status === 'WebAd' && (
                      <span className="px-2 py-1 rounded-full bg-pink-200 text-pink-800 text-xs font-bold">
                        Web Ad
                      </span>
                    )}
                  </div>

                  {/* Main Info */}
                  <div className="mb-3">
                    <div className="font-bold text-xl">{registration.firstName} {registration.lastName}</div>
                    <div className="text-sm text-gray-500">{registration.email}</div>
                    <div className="text-sm text-gray-500">{registration.phone}</div>
                  </div>

                  {/* Detainee Info */}
                  <div className="mb-3">
                    <div className="text-sm">
                      <span className="font-semibold">Detainee:</span> {registration.detaineeInfo?.name || '-'}
                    </div>
                    <div className="text-sm text-gray-600">
                      {registration.detaineeInfo?.facility || '-'}
                    </div>
                    {registration.detaineeInfo?.inmateId && (
                      <div className="text-sm text-gray-600">ID: {registration.detaineeInfo.inmateId}</div>
                    )}
                  </div>

                  {/* Case Info */}
                  {registration.legalCase && (
                    <div className="mb-3">
                      <div className="flex items-center gap-2 mb-2">
                        {registration.legalCase.logoUrl && (
                          <img
                            src={registration.legalCase.logoUrl}
                            alt={`${registration.legalCase.title} Logo`}
                            className="w-8 h-8 rounded object-cover border border-gray-200"
                          />
                        )}
                        <div className="text-sm">
                          <span className="font-semibold">Case:</span> {registration.legalCase.title}
                        </div>
                      </div>
                      <div className="text-sm text-gray-600">
                        {registration.legalCase.caseNumber}
                      </div>
                    </div>
                  )}

                  {/* Documents */}
                  {registration.documents && registration.documents.length > 0 && (
                    <div className="mb-4">
                      <h4 className="text-sm font-semibold mb-2">Documents ({registration.documents.length})</h4>
                      <div className="space-y-1">
                        {registration.documents.slice(0, 3).map((doc) => (
                          <div key={doc.id} className="flex items-center text-blue-600 text-sm">
                            <svg className="w-3 h-3 mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M9 12h6m-6 4h6m2 5H7a2 2 0 01-2-2V5a2 2 0 012-2h5.586a1 1 0 01.707.293l5.414 5.414a1 1 0 01.293.707V19a2 2 0 01-2 2z" />
                            </svg>
                            {doc.name}
                          </div>
                        ))}
                        {registration.documents.length > 3 && (
                          <div className="text-xs text-gray-500">+{registration.documents.length - 3} more</div>
                        )}
                      </div>
                    </div>
                  )}

                  {/* Status Dropdown */}
                  <div className="mt-4 bg-gray-50 p-3 rounded-lg border-2 border-dashed">
                    <label className="block text-sm font-semibold text-gray-700 mb-2">Quick Status Update</label>
                    <select
                      value={registration.status}
                      onChange={(e) => handleStatusUpdate(registration.id, e.target.value)}
                      className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm focus:outline-none focus:ring-2 focus:ring-primary bg-white font-medium"
                    >
                      <option value="PENDING">⏳ Pending</option>
                      <option value="APPROVED">βœ… Approved</option>
                      <option value="REJECTED">❌ Rejected</option>
                      <option value="MISSING_DOCUMENTS">πŸ“„ Missing Documents</option>
                      <option value="DOCUMENTS_UNDER_REVIEW">πŸ“‹ Documents Under Review</option>
                      <option value="ADDITIONAL_INFO_NEEDED">ℹ️ Additional Info Needed</option>
                      <option value="VERIFICATION_IN_PROGRESS">πŸ” Verification in Progress</option>
                      <option value="LAWYER_VERIFICATION">πŸ‘¨β€πŸ’Ό Lawyer Verification</option>
                      <option value="FACILITY_VERIFICATION">🏒 Facility Verification</option>
                      <option value="DOCUMENTS_EXPIRED">⏰ Documents Expired</option>
                      <option value="DOCUMENTS_INCOMPLETE">πŸ“ Documents Incomplete</option>
                      <option value="INFORMATION_MISMATCH">⚠️ Information Mismatch</option>
                      <option value="PENDING_PAYMENT">πŸ’° Pending Payment</option>
                      <option value="PAYMENT_RECEIVED">πŸ’³ Payment Received</option>
                      <option value="PENDING_LAWYER_APPROVAL">πŸ‘¨β€πŸ’Ό Pending Lawyer Approval</option>
                      <option value="PENDING_FACILITY_APPROVAL">🏒 Pending Facility Approval</option>
                      <option value="ON_HOLD">⏸️ On Hold</option>
                      <option value="ESCALATED">🚨 Escalated</option>
                      <option value="FINAL_REVIEW">πŸ” Final Review</option>
                      <option value="COMPLETED">βœ… Completed</option>
                      <option value="WebAd">🌐 Web Advertisement</option>
                    </select>
                  </div>

                  {/* Actions */}
                  <div className="mt-auto pt-4 grid grid-cols-3 gap-2">
                    <Link
                      href={`/lawyer/registrations/${registration.id}`}
                      className="font-semibold text-sm px-4 py-2.5 rounded-lg flex items-center justify-center transition-all duration-200 shadow-md hover:shadow-lg transform hover:scale-105 text-white"
                      style={{
                        background: `linear-gradient(135deg, #8b5cf6, #7c3aed)`,
                      }}
                      onMouseEnter={(e) => {
                        e.currentTarget.style.background = `linear-gradient(135deg, #7c3aed, #8b5cf6)`;
                      }}
                      onMouseLeave={(e) => {
                        e.currentTarget.style.background = `linear-gradient(135deg, #8b5cf6, #7c3aed)`;
                      }}
                    >
                      <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z" />
                      </svg>
                      View
                    </Link>
                    <button
                      onClick={() => setShowPrivateChat({ open: true, registrationId: registration.id })}
                      className="font-semibold text-sm px-4 py-2.5 rounded-lg flex items-center justify-center transition-all duration-200 shadow-md hover:shadow-lg transform hover:scale-105 text-white"
                      style={{
                        background: `linear-gradient(135deg, #3b82f6, #2563eb)`,
                      }}
                      onMouseEnter={(e) => {
                        e.currentTarget.style.background = `linear-gradient(135deg, #2563eb, #3b82f6)`;
                      }}
                      onMouseLeave={(e) => {
                        e.currentTarget.style.background = `linear-gradient(135deg, #3b82f6, #2563eb)`;
                      }}
                    >
                      <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z" />
                      </svg>
                      Chat
                    </button>
                    <Link
                      href={`/user/registrations/${registration.id}`}
                      className="bg-gradient-to-r from-green-500 to-green-600 hover:from-green-600 hover:to-green-700 text-white font-semibold text-sm px-4 py-2.5 rounded-lg flex items-center justify-center transition-all duration-200 shadow-md hover:shadow-lg transform hover:scale-105"
                    >
                      <svg className="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
                        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
                      </svg>
                      Edit
                    </Link>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

        {/* Private Chat Modal */}
        {showPrivateChat.open && showPrivateChat.registrationId && (
          <PrivateChat
            registrationId={showPrivateChat.registrationId}
            onClose={() => setShowPrivateChat({ open: false, registrationId: null })}
          />
        )}
      </div>
    </LayoutWithSidebar>
  );
};

export default LawyerApplications; 

CasperSecurity Mini