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

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

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

import React, { useState, useEffect } from 'react';
import { toast } from 'react-hot-toast';
import {
  Shield,
  Clock,
  CheckCircle,
  AlertCircle,
  DollarSign,
  Calendar,
  Lock,
  Unlock,
  Eye,
  Download
} from 'lucide-react';

interface EscrowAccount {
  id: string;
  caseId: string;
  caseTitle: string;
  amount: number;
  currency: string;
  status: string;
  createdAt: string;
  autoReleaseDate: string;
  releasedAt?: string;
  releasedBy?: string;
  releaseReason?: string;
  milestones: EscrowMilestone[];
}

interface EscrowMilestone {
  id: string;
  title: string;
  description: string;
  amount: number;
  status: string;
  dueDate: string;
  completedAt?: string;
}

interface EscrowManagementProps {
  userId: string;
  userRole: string;
}

const EscrowManagement: React.FC<EscrowManagementProps> = ({ userId, userRole }) => {
  const [escrowAccounts, setEscrowAccounts] = useState<EscrowAccount[]>([]);
  const [loading, setLoading] = useState(true);
  const [selectedAccount, setSelectedAccount] = useState<EscrowAccount | null>(null);
  const [showDetailsModal, setShowDetailsModal] = useState(false);
  const [showReleaseModal, setShowReleaseModal] = useState(false);
  const [releaseReason, setReleaseReason] = useState('');
  const [filterStatus, setFilterStatus] = useState('all');

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

  const fetchEscrowAccounts = async () => {
    try {
      setLoading(true);
      const response = await fetch('/api/user/escrow-accounts');
      
      if (response.ok) {
        const data = await response.json();
        setEscrowAccounts(data.escrowAccounts || []);
      } else {
        console.error('Failed to fetch escrow accounts');
      }
    } catch (error) {
      console.error('Error fetching escrow accounts:', error);
      toast.error('Failed to load escrow accounts');
    } finally {
      setLoading(false);
    }
  };

  const handleReleaseEscrow = async (accountId: string) => {
    if (!releaseReason.trim()) {
      toast.error('Please provide a reason for the release');
      return;
    }

    try {
      const response = await fetch(`/api/user/escrow-accounts/${accountId}/release`, {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          reason: releaseReason
        })
      });

      if (response.ok) {
        toast.success('Escrow funds released successfully');
        setShowReleaseModal(false);
        setReleaseReason('');
        fetchEscrowAccounts(); // Refresh data
      } else {
        const error = await response.json();
        toast.error(error.message || 'Failed to release escrow funds');
      }
    } catch (error) {
      console.error('Error releasing escrow:', error);
      toast.error('Failed to release escrow funds');
    }
  };

  const getStatusIcon = (status: string) => {
    switch (status.toLowerCase()) {
      case 'active':
        return <Lock className="h-4 w-4 text-blue-500" />;
      case 'released':
        return <Unlock className="h-4 w-4 text-green-500" />;
      case 'pending':
        return <Clock className="h-4 w-4 text-yellow-500" />;
      case 'disputed':
        return <AlertCircle className="h-4 w-4 text-red-500" />;
      default:
        return <Shield className="h-4 w-4 text-gray-500" />;
    }
  };

  const getStatusColor = (status: string) => {
    switch (status.toLowerCase()) {
      case 'active':
        return 'text-blue-600 bg-blue-50';
      case 'released':
        return 'text-green-600 bg-green-50';
      case 'pending':
        return 'text-yellow-600 bg-yellow-50';
      case 'disputed':
        return 'text-red-600 bg-red-50';
      default:
        return 'text-gray-600 bg-gray-50';
    }
  };

  const formatCurrency = (amount: number, currency: string = 'CAD') => {
    return new Intl.NumberFormat('en-CA', {
      style: 'currency',
      currency: currency
    }).format(amount);
  };

  const formatDate = (dateString: string) => {
    return new Date(dateString).toLocaleDateString('en-CA', {
      year: 'numeric',
      month: 'short',
      day: 'numeric'
    });
  };

  const getDaysUntilRelease = (releaseDate: string) => {
    const now = new Date();
    const release = new Date(releaseDate);
    const diffTime = release.getTime() - now.getTime();
    const diffDays = Math.ceil(diffTime / (1000 * 60 * 60 * 24));
    return diffDays;
  };

  const filteredAccounts = escrowAccounts.filter(account => {
    if (filterStatus === 'all') return true;
    return account.status.toLowerCase() === filterStatus;
  });

  if (loading) {
    return (
      <div className="flex items-center justify-center p-8">
        <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-blue-600"></div>
        <span className="ml-3 text-gray-600">Loading escrow accounts...</span>
      </div>
    );
  }

  return (
    <div className="space-y-6">
      {/* Header */}
      <div className="flex justify-between items-center">
        <div>
          <h2 className="text-2xl font-bold text-gray-900">Escrow Management</h2>
          <p className="text-gray-600">Manage escrow accounts and fund releases</p>
        </div>
        <div className="flex space-x-2">
          <select
            value={filterStatus}
            onChange={(e) => setFilterStatus(e.target.value)}
            className="px-3 py-2 border border-gray-300 rounded-md text-sm"
          >
            <option value="all">All Status</option>
            <option value="active">Active</option>
            <option value="pending">Pending</option>
            <option value="released">Released</option>
            <option value="disputed">Disputed</option>
          </select>
        </div>
      </div>

      {/* Summary Cards */}
      <div className="grid grid-cols-1 md:grid-cols-4 gap-6">
        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center">
            <div className="p-2 bg-blue-100 rounded-lg">
              <Shield className="h-6 w-6 text-blue-600" />
            </div>
            <div className="ml-4">
              <p className="text-sm font-medium text-gray-600">Total Escrow</p>
              <p className="text-2xl font-bold text-gray-900">
                {formatCurrency(
                  escrowAccounts.reduce((sum, account) => sum + account.amount, 0)
                )}
              </p>
            </div>
          </div>
        </div>

        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center">
            <div className="p-2 bg-green-100 rounded-lg">
              <Lock className="h-6 w-6 text-green-600" />
            </div>
            <div className="ml-4">
              <p className="text-sm font-medium text-gray-600">Active Accounts</p>
              <p className="text-2xl font-bold text-gray-900">
                {escrowAccounts.filter(a => a.status === 'ACTIVE').length}
              </p>
            </div>
          </div>
        </div>

        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center">
            <div className="p-2 bg-yellow-100 rounded-lg">
              <Clock className="h-6 w-6 text-yellow-600" />
            </div>
            <div className="ml-4">
              <p className="text-sm font-medium text-gray-600">Pending Release</p>
              <p className="text-2xl font-bold text-gray-900">
                {escrowAccounts.filter(a => a.status === 'PENDING').length}
              </p>
            </div>
          </div>
        </div>

        <div className="bg-white rounded-lg shadow p-6">
          <div className="flex items-center">
            <div className="p-2 bg-purple-100 rounded-lg">
              <Unlock className="h-6 w-6 text-purple-600" />
            </div>
            <div className="ml-4">
              <p className="text-sm font-medium text-gray-600">Released</p>
              <p className="text-2xl font-bold text-gray-900">
                {escrowAccounts.filter(a => a.status === 'RELEASED').length}
              </p>
            </div>
          </div>
        </div>
      </div>

      {/* Escrow Accounts Table */}
      <div className="bg-white rounded-lg shadow">
        <div className="px-6 py-4 border-b border-gray-200">
          <h3 className="text-lg font-medium text-gray-900">Escrow Accounts</h3>
        </div>
        <div className="overflow-x-auto">
          <table className="min-w-full divide-y divide-gray-200">
            <thead className="bg-gray-50">
              <tr>
                <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                  Case
                </th>
                <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                  Amount
                </th>
                <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                  Status
                </th>
                <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                  Auto Release
                </th>
                <th className="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
                  Actions
                </th>
              </tr>
            </thead>
            <tbody className="bg-white divide-y divide-gray-200">
              {filteredAccounts.map((account) => {
                const daysUntilRelease = getDaysUntilRelease(account.autoReleaseDate);
                return (
                  <tr key={account.id}>
                    <td className="px-6 py-4 whitespace-nowrap">
                      <div>
                        <div className="text-sm font-medium text-gray-900">
                          {account.caseTitle}
                        </div>
                        <div className="text-sm text-gray-500">
                          ID: {account.caseId}
                        </div>
                      </div>
                    </td>
                    <td className="px-6 py-4 whitespace-nowrap">
                      <div className="text-sm text-gray-900">
                        {formatCurrency(account.amount, account.currency)}
                      </div>
                    </td>
                    <td className="px-6 py-4 whitespace-nowrap">
                      <span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(account.status)}`}>
                        {getStatusIcon(account.status)}
                        <span className="ml-1">{account.status}</span>
                      </span>
                    </td>
                    <td className="px-6 py-4 whitespace-nowrap">
                      <div className="text-sm text-gray-900">
                        {formatDate(account.autoReleaseDate)}
                      </div>
                      {account.status === 'ACTIVE' && daysUntilRelease > 0 && (
                        <div className="text-xs text-gray-500">
                          {daysUntilRelease} days remaining
                        </div>
                      )}
                      {account.status === 'ACTIVE' && daysUntilRelease <= 0 && (
                        <div className="text-xs text-red-500">
                          Overdue for release
                        </div>
                      )}
                    </td>
                    <td className="px-6 py-4 whitespace-nowrap text-sm font-medium">
                      <button
                        onClick={() => {
                          setSelectedAccount(account);
                          setShowDetailsModal(true);
                        }}
                        className="text-blue-600 hover:text-blue-900 mr-3"
                      >
                        <Eye className="h-4 w-4" />
                      </button>
                      {account.status === 'ACTIVE' && userRole === 'LAWYER' && (
                        <button
                          onClick={() => {
                            setSelectedAccount(account);
                            setShowReleaseModal(true);
                          }}
                          className="text-green-600 hover:text-green-900"
                        >
                          <Unlock className="h-4 w-4" />
                        </button>
                      )}
                    </td>
                  </tr>
                );
              })}
            </tbody>
          </table>
        </div>

        {filteredAccounts.length === 0 && (
          <div className="text-center py-12">
            <Shield className="mx-auto h-12 w-12 text-gray-400" />
            <h3 className="mt-2 text-sm font-medium text-gray-900">No escrow accounts</h3>
            <p className="mt-1 text-sm text-gray-500">
              Escrow accounts will appear here when payments are made.
            </p>
          </div>
        )}
      </div>

      {/* Account Details Modal */}
      {showDetailsModal && selectedAccount && (
        <div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
          <div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
            <div className="mt-3">
              <h3 className="text-lg font-medium text-gray-900 mb-4">Escrow Account Details</h3>
              <div className="space-y-3">
                <div>
                  <label className="text-sm font-medium text-gray-500">Case</label>
                  <p className="text-sm text-gray-900">{selectedAccount.caseTitle}</p>
                </div>
                <div>
                  <label className="text-sm font-medium text-gray-500">Amount</label>
                  <p className="text-sm text-gray-900">
                    {formatCurrency(selectedAccount.amount, selectedAccount.currency)}
                  </p>
                </div>
                <div>
                  <label className="text-sm font-medium text-gray-500">Status</label>
                  <span className={`inline-flex items-center px-2 py-1 rounded-full text-xs font-medium ${getStatusColor(selectedAccount.status)}`}>
                    {getStatusIcon(selectedAccount.status)}
                    <span className="ml-1">{selectedAccount.status}</span>
                  </span>
                </div>
                <div>
                  <label className="text-sm font-medium text-gray-500">Created</label>
                  <p className="text-sm text-gray-900">{formatDate(selectedAccount.createdAt)}</p>
                </div>
                <div>
                  <label className="text-sm font-medium text-gray-500">Auto Release Date</label>
                  <p className="text-sm text-gray-900">{formatDate(selectedAccount.autoReleaseDate)}</p>
                </div>
                {selectedAccount.releasedAt && (
                  <>
                    <div>
                      <label className="text-sm font-medium text-gray-500">Released At</label>
                      <p className="text-sm text-gray-900">{formatDate(selectedAccount.releasedAt)}</p>
                    </div>
                    <div>
                      <label className="text-sm font-medium text-gray-500">Release Reason</label>
                      <p className="text-sm text-gray-900">{selectedAccount.releaseReason}</p>
                    </div>
                  </>
                )}
              </div>
              <div className="mt-6 flex justify-end">
                <button
                  onClick={() => setShowDetailsModal(false)}
                  className="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
                >
                  Close
                </button>
              </div>
            </div>
          </div>
        </div>
      )}

      {/* Release Modal */}
      {showReleaseModal && selectedAccount && (
        <div className="fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
          <div className="relative top-20 mx-auto p-5 border w-96 shadow-lg rounded-md bg-white">
            <div className="mt-3">
              <h3 className="text-lg font-medium text-gray-900 mb-4">Release Escrow Funds</h3>
              <div className="space-y-4">
                <div>
                  <label className="text-sm font-medium text-gray-500">Case</label>
                  <p className="text-sm text-gray-900">{selectedAccount.caseTitle}</p>
                </div>
                <div>
                  <label className="text-sm font-medium text-gray-500">Amount</label>
                  <p className="text-sm text-gray-900">
                    {formatCurrency(selectedAccount.amount, selectedAccount.currency)}
                  </p>
                </div>
                <div>
                  <label className="block text-sm font-medium text-gray-700 mb-2">
                    Release Reason *
                  </label>
                  <textarea
                    value={releaseReason}
                    onChange={(e) => setReleaseReason(e.target.value)}
                    className="w-full px-3 py-2 border border-gray-300 rounded-md text-sm"
                    rows={3}
                    placeholder="Explain why you're releasing these funds..."
                  />
                </div>
              </div>
              <div className="mt-6 flex justify-end space-x-3">
                <button
                  onClick={() => {
                    setShowReleaseModal(false);
                    setReleaseReason('');
                  }}
                  className="px-4 py-2 text-sm font-medium text-gray-700 bg-gray-100 rounded-md hover:bg-gray-200"
                >
                  Cancel
                </button>
                <button
                  onClick={() => handleReleaseEscrow(selectedAccount.id)}
                  className="px-4 py-2 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700"
                >
                  Release Funds
                </button>
              </div>
            </div>
          </div>
        </div>
      )}
    </div>
  );
};

export default EscrowManagement; 

CasperSecurity Mini