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/consultations.tsx
import React, { useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import { useRouter } from 'next/router';
import LayoutWithSidebar from '@/components/LayoutWithSidebar';
import { toast } from 'react-hot-toast';
import { 
  Calendar, 
  Clock, 
  User, 
  MessageSquare, 
  CheckCircle, 
  XCircle, 
  AlertCircle,
  Filter,
  Search,
  Plus,
  Phone,
  Video,
  MapPin,
  DollarSign,
  Edit,
  Eye,
  RefreshCw,
  CalendarDays,
  Users,
  TrendingUp,
  FileText
} from 'lucide-react';

interface Consultation {
  id: string;
  clientId: string;
  clientName: string;
  clientEmail: string;
  preferredDate: string;
  preferredTime: string;
  duration: number;
  consultationType: string;
  status: string;
  message?: string;
  hourlyRate?: number;
  totalAmount?: number;
  meetingLink?: string;
  meetingPlatform?: string;
  notes?: string;
  lawyerNotes?: string;
  clientNotes?: string;
  followUpDate?: string;
  followUpNotes?: string;
  createdAt: string;
  updatedAt: string;
}

const LawyerConsultations: React.FC = () => {
  const { data: session, status } = useSession();
  const router = useRouter();
  const [consultations, setConsultations] = useState<Consultation[]>([]);
  const [loading, setLoading] = useState(true);
  const [stats, setStats] = useState({
    total: 0,
    pending: 0,
    confirmed: 0,
    completed: 0,
    cancelled: 0
  });
  const [selectedStatus, setSelectedStatus] = useState('all');
  const [searchTerm, setSearchTerm] = useState('');
  const [selectedConsultation, setSelectedConsultation] = useState<Consultation | null>(null);
  const [showDetailsModal, setShowDetailsModal] = useState(false);
  const [updatingStatus, setUpdatingStatus] = useState<string | null>(null);

  useEffect(() => {
    if (status === 'loading') return;
    if (!session || !['LAWYER', 'ADMIN', 'SUPERADMIN'].includes(session.user.role)) {
      router.push('/');
      return;
    }
    fetchConsultations();
  }, [session, status, router]);

  const fetchConsultations = async () => {
    try {
      setLoading(true);
      const response = await fetch('/api/lawyer/consultations');
      if (response.ok) {
        const data = await response.json();
        setConsultations(data.consultations || []);
        calculateStats(data.consultations || []);
      } else {
        toast.error('Failed to load consultations');
      }
    } catch (error) {
      console.error('Error fetching consultations:', error);
      toast.error('Error loading consultations');
    } finally {
      setLoading(false);
    }
  };

  const calculateStats = (consultations: Consultation[]) => {
    const stats = {
      total: consultations.length,
      pending: consultations.filter(c => c.status === 'PENDING').length,
      confirmed: consultations.filter(c => c.status === 'CONFIRMED').length,
      completed: consultations.filter(c => c.status === 'COMPLETED').length,
      cancelled: consultations.filter(c => c.status === 'CANCELLED').length
    };
    setStats(stats);
  };

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

      if (response.ok) {
        toast.success(`Consultation ${newStatus.toLowerCase()}`);
        fetchConsultations(); // Refresh the list
        setShowDetailsModal(false);
      } else {
        const error = await response.json();
        toast.error(error.error || 'Failed to update status');
      }
    } catch (error) {
      console.error('Error updating consultation status:', error);
      toast.error('Error updating status');
    } finally {
      setUpdatingStatus(null);
    }
  };

  const filteredConsultations = consultations.filter(consultation => {
    const matchesStatus = selectedStatus === 'all' || consultation.status === selectedStatus;
    const matchesSearch = consultation.clientName.toLowerCase().includes(searchTerm.toLowerCase()) ||
                         consultation.clientEmail.toLowerCase().includes(searchTerm.toLowerCase()) ||
                         consultation.consultationType.toLowerCase().includes(searchTerm.toLowerCase());
    return matchesStatus && matchesSearch;
  });

  const getStatusColor = (status: string) => {
    switch (status) {
      case 'PENDING': return 'bg-yellow-100 text-yellow-800';
      case 'CONFIRMED': return 'bg-blue-100 text-blue-800';
      case 'COMPLETED': return 'bg-green-100 text-green-800';
      case 'CANCELLED': return 'bg-red-100 text-red-800';
      case 'RESCHEDULED': return 'bg-purple-100 text-purple-800';
      default: return 'bg-gray-100 text-gray-800';
    }
  };

  const getConsultationTypeIcon = (type: string) => {
    switch (type) {
      case 'GENERAL': return <MessageSquare className="h-4 w-4" />;
      case 'CASE_REVIEW': return <FileText className="h-4 w-4" />;
      case 'DOCUMENT_REVIEW': return <FileText className="h-4 w-4" />;
      case 'CONTRACT_REVIEW': return <FileText className="h-4 w-4" />;
      case 'LITIGATION_STRATEGY': return <TrendingUp className="h-4 w-4" />;
      case 'SETTLEMENT_NEGOTIATION': return <Users className="h-4 w-4" />;
      default: return <MessageSquare className="h-4 w-4" />;
    }
  };

  if (status === 'loading') {
    return (
      <LayoutWithSidebar>
        <div className="flex items-center justify-center min-h-screen">
          <div className="animate-spin rounded-full h-12 w-12 border-b-2 border-green-600"></div>
        </div>
      </LayoutWithSidebar>
    );
  }

  if (!session || !['LAWYER', 'ADMIN', 'SUPERADMIN'].includes(session.user.role)) {
    router.push('/');
    return null;
  }

  return (
    <LayoutWithSidebar>
      <div className="max-w-7xl mx-auto px-4 py-8">
        {/* Header */}
        <div className="mb-8">
          <div className="flex items-center justify-between">
            <div>
              <h1 className="text-3xl font-bold text-gray-900">Consultations</h1>
              <p className="text-gray-600 mt-1">Manage your legal consultations and bookings</p>
            </div>
            <button
              onClick={fetchConsultations}
              className="inline-flex items-center px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition-colors"
            >
              <RefreshCw className="h-4 w-4 mr-2" />
              Refresh
            </button>
          </div>
        </div>

        {/* Stats Cards */}
        <div className="grid grid-cols-2 md:grid-cols-5 gap-4 mb-8">
          <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
            <div className="flex items-center">
              <div className="p-2 bg-blue-100 rounded-lg">
                <Calendar className="h-6 w-6 text-blue-600" />
              </div>
              <div className="ml-3">
                <p className="text-sm font-medium text-gray-600">Total</p>
                <p className="text-2xl font-bold text-gray-900">{stats.total}</p>
              </div>
            </div>
          </div>
          <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
            <div className="flex items-center">
              <div className="p-2 bg-yellow-100 rounded-lg">
                <AlertCircle className="h-6 w-6 text-yellow-600" />
              </div>
              <div className="ml-3">
                <p className="text-sm font-medium text-gray-600">Pending</p>
                <p className="text-2xl font-bold text-gray-900">{stats.pending}</p>
              </div>
            </div>
          </div>
          <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
            <div className="flex items-center">
              <div className="p-2 bg-blue-100 rounded-lg">
                <CheckCircle className="h-6 w-6 text-blue-600" />
              </div>
              <div className="ml-3">
                <p className="text-sm font-medium text-gray-600">Confirmed</p>
                <p className="text-2xl font-bold text-gray-900">{stats.confirmed}</p>
              </div>
            </div>
          </div>
          <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
            <div className="flex items-center">
              <div className="p-2 bg-green-100 rounded-lg">
                <CheckCircle className="h-6 w-6 text-green-600" />
              </div>
              <div className="ml-3">
                <p className="text-sm font-medium text-gray-600">Completed</p>
                <p className="text-2xl font-bold text-gray-900">{stats.completed}</p>
              </div>
            </div>
          </div>
          <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-4">
            <div className="flex items-center">
              <div className="p-2 bg-red-100 rounded-lg">
                <XCircle className="h-6 w-6 text-red-600" />
              </div>
              <div className="ml-3">
                <p className="text-sm font-medium text-gray-600">Cancelled</p>
                <p className="text-2xl font-bold text-gray-900">{stats.cancelled}</p>
              </div>
            </div>
          </div>
        </div>

        {/* Filters */}
        <div className="bg-white rounded-lg shadow-sm border border-gray-200 p-6 mb-8">
          <div className="flex flex-col md:flex-row gap-4">
            <div className="flex-1">
              <div className="relative">
                <Search className="absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 h-4 w-4" />
                <input
                  type="text"
                  placeholder="Search consultations..."
                  value={searchTerm}
                  onChange={(e) => setSearchTerm(e.target.value)}
                  className="w-full pl-10 pr-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
                />
              </div>
            </div>
            <div className="flex gap-2">
              <select
                value={selectedStatus}
                onChange={(e) => setSelectedStatus(e.target.value)}
                className="px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-green-500 focus:border-transparent"
              >
                <option value="all">All Status</option>
                <option value="PENDING">Pending</option>
                <option value="CONFIRMED">Confirmed</option>
                <option value="COMPLETED">Completed</option>
                <option value="CANCELLED">Cancelled</option>
                <option value="RESCHEDULED">Rescheduled</option>
              </select>
            </div>
          </div>
        </div>

        {/* Consultations List */}
        <div className="bg-white rounded-lg shadow-sm border border-gray-200">
          {loading ? (
            <div className="p-8 text-center">
              <div className="animate-spin rounded-full h-8 w-8 border-b-2 border-green-600 mx-auto"></div>
              <p className="mt-2 text-gray-600">Loading consultations...</p>
            </div>
          ) : filteredConsultations.length === 0 ? (
            <div className="p-8 text-center">
              <MessageSquare className="h-12 w-12 text-gray-400 mx-auto mb-4" />
              <h3 className="text-lg font-medium text-gray-900 mb-2">No consultations found</h3>
              <p className="text-gray-600">
                {searchTerm || selectedStatus !== 'all' 
                  ? 'Try adjusting your search or filters'
                  : 'You don\'t have any consultations yet'
                }
              </p>
            </div>
          ) : (
            <div className="divide-y divide-gray-200">
              {filteredConsultations.map((consultation) => (
                <div key={consultation.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">
                          {consultation.clientName}
                        </h3>
                        <span className={`px-2 py-1 text-xs font-medium rounded-full ${getStatusColor(consultation.status)}`}>
                          {consultation.status}
                        </span>
                        <div className="flex items-center gap-1 text-gray-500">
                          {getConsultationTypeIcon(consultation.consultationType)}
                          <span className="text-sm">{consultation.consultationType.replace('_', ' ')}</span>
                        </div>
                      </div>
                      
                      <div className="grid grid-cols-1 md:grid-cols-3 gap-4 text-sm text-gray-600">
                        <div className="flex items-center gap-2">
                          <User className="h-4 w-4" />
                          <span>{consultation.clientEmail}</span>
                        </div>
                        <div className="flex items-center gap-2">
                          <Calendar className="h-4 w-4" />
                          <span>{new Date(consultation.preferredDate).toLocaleDateString()}</span>
                        </div>
                        <div className="flex items-center gap-2">
                          <Clock className="h-4 w-4" />
                          <span>{consultation.preferredTime} ({consultation.duration} min)</span>
                        </div>
                      </div>

                      {consultation.message && (
                        <p className="text-gray-600 mt-2 text-sm">
                          "{consultation.message}"
                        </p>
                      )}

                      {consultation.hourlyRate && (
                        <div className="flex items-center gap-2 mt-2 text-sm text-gray-600">
                          <DollarSign className="h-4 w-4" />
                          <span>${consultation.hourlyRate}/hour</span>
                          {consultation.totalAmount && (
                            <span>• Total: ${consultation.totalAmount}</span>
                          )}
                        </div>
                      )}
                    </div>

                    <div className="flex items-center gap-2 ml-4">
                      <button
                        onClick={() => {
                          setSelectedConsultation(consultation);
                          setShowDetailsModal(true);
                        }}
                        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
                      </button>
                      
                      {consultation.status === 'PENDING' && (
                        <button
                          onClick={() => updateConsultationStatus(consultation.id, 'CONFIRMED')}
                          disabled={updatingStatus === consultation.id}
                          className="inline-flex items-center px-3 py-1 text-sm text-green-600 hover:text-green-800 hover:bg-green-50 rounded-md transition-colors disabled:opacity-50"
                        >
                          <CheckCircle className="h-4 w-4 mr-1" />
                          {updatingStatus === consultation.id ? 'Confirming...' : 'Confirm'}
                        </button>
                      )}
                      
                      {consultation.status === 'CONFIRMED' && (
                        <button
                          onClick={() => updateConsultationStatus(consultation.id, 'COMPLETED')}
                          disabled={updatingStatus === consultation.id}
                          className="inline-flex items-center px-3 py-1 text-sm text-green-600 hover:text-green-800 hover:bg-green-50 rounded-md transition-colors disabled:opacity-50"
                        >
                          <CheckCircle className="h-4 w-4 mr-1" />
                          {updatingStatus === consultation.id ? 'Completing...' : 'Complete'}
                        </button>
                      )}
                    </div>
                  </div>
                </div>
              ))}
            </div>
          )}
        </div>

        {/* Consultation Details Modal */}
        {showDetailsModal && selectedConsultation && (
          <div className="fixed inset-0 bg-black bg-opacity-50 z-50 flex items-center justify-center p-4">
            <div className="bg-white rounded-xl shadow-xl max-w-2xl w-full max-h-[90vh] overflow-y-auto">
              <div className="p-6 border-b border-gray-200">
                <div className="flex items-center justify-between">
                  <h2 className="text-xl font-bold text-gray-900">Consultation Details</h2>
                  <button
                    onClick={() => setShowDetailsModal(false)}
                    className="text-gray-400 hover:text-gray-600"
                  >
                    <XCircle className="h-6 w-6" />
                  </button>
                </div>
              </div>
              
              <div className="p-6 space-y-6">
                {/* Client Information */}
                <div>
                  <h3 className="text-lg font-semibold text-gray-900 mb-3">Client Information</h3>
                  <div className="bg-gray-50 rounded-lg p-4">
                    <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                      <div>
                        <p className="text-sm font-medium text-gray-600">Name</p>
                        <p className="text-gray-900">{selectedConsultation.clientName}</p>
                      </div>
                      <div>
                        <p className="text-sm font-medium text-gray-600">Email</p>
                        <p className="text-gray-900">{selectedConsultation.clientEmail}</p>
                      </div>
                    </div>
                  </div>
                </div>

                {/* Consultation Details */}
                <div>
                  <h3 className="text-lg font-semibold text-gray-900 mb-3">Consultation Details</h3>
                  <div className="bg-gray-50 rounded-lg p-4">
                    <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                      <div>
                        <p className="text-sm font-medium text-gray-600">Type</p>
                        <p className="text-gray-900">{selectedConsultation.consultationType.replace('_', ' ')}</p>
                      </div>
                      <div>
                        <p className="text-sm font-medium text-gray-600">Status</p>
                        <span className={`px-2 py-1 text-xs font-medium rounded-full ${getStatusColor(selectedConsultation.status)}`}>
                          {selectedConsultation.status}
                        </span>
                      </div>
                      <div>
                        <p className="text-sm font-medium text-gray-600">Date</p>
                        <p className="text-gray-900">{new Date(selectedConsultation.preferredDate).toLocaleDateString()}</p>
                      </div>
                      <div>
                        <p className="text-sm font-medium text-gray-600">Time</p>
                        <p className="text-gray-900">{selectedConsultation.preferredTime} ({selectedConsultation.duration} min)</p>
                      </div>
                      {selectedConsultation.hourlyRate && (
                        <div>
                          <p className="text-sm font-medium text-gray-600">Rate</p>
                          <p className="text-gray-900">${selectedConsultation.hourlyRate}/hour</p>
                        </div>
                      )}
                      {selectedConsultation.totalAmount && (
                        <div>
                          <p className="text-sm font-medium text-gray-600">Total Amount</p>
                          <p className="text-gray-900">${selectedConsultation.totalAmount}</p>
                        </div>
                      )}
                    </div>
                  </div>
                </div>

                {/* Message */}
                {selectedConsultation.message && (
                  <div>
                    <h3 className="text-lg font-semibold text-gray-900 mb-3">Client Message</h3>
                    <div className="bg-gray-50 rounded-lg p-4">
                      <p className="text-gray-900">{selectedConsultation.message}</p>
                    </div>
                  </div>
                )}

                {/* Meeting Information */}
                {selectedConsultation.meetingLink && (
                  <div>
                    <h3 className="text-lg font-semibold text-gray-900 mb-3">Meeting Information</h3>
                    <div className="bg-gray-50 rounded-lg p-4">
                      <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
                        <div>
                          <p className="text-sm font-medium text-gray-600">Platform</p>
                          <p className="text-gray-900">{selectedConsultation.meetingPlatform || 'Virtual'}</p>
                        </div>
                        <div>
                          <p className="text-sm font-medium text-gray-600">Meeting Link</p>
                          <a 
                            href={selectedConsultation.meetingLink} 
                            target="_blank" 
                            rel="noopener noreferrer"
                            className="text-blue-600 hover:text-blue-800 underline"
                          >
                            Join Meeting
                          </a>
                        </div>
                      </div>
                    </div>
                  </div>
                )}

                {/* Actions */}
                <div className="flex gap-3 pt-4 border-t border-gray-200">
                  {selectedConsultation.status === 'PENDING' && (
                    <>
                      <button
                        onClick={() => updateConsultationStatus(selectedConsultation.id, 'CONFIRMED')}
                        disabled={updatingStatus === selectedConsultation.id}
                        className="flex-1 bg-green-600 text-white py-2 px-4 rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50"
                      >
                        {updatingStatus === selectedConsultation.id ? 'Confirming...' : 'Confirm Consultation'}
                      </button>
                      <button
                        onClick={() => updateConsultationStatus(selectedConsultation.id, 'CANCELLED')}
                        disabled={updatingStatus === selectedConsultation.id}
                        className="flex-1 bg-red-600 text-white py-2 px-4 rounded-lg hover:bg-red-700 transition-colors disabled:opacity-50"
                      >
                        {updatingStatus === selectedConsultation.id ? 'Cancelling...' : 'Cancel'}
                      </button>
                    </>
                  )}
                  
                  {selectedConsultation.status === 'CONFIRMED' && (
                    <button
                      onClick={() => updateConsultationStatus(selectedConsultation.id, 'COMPLETED')}
                      disabled={updatingStatus === selectedConsultation.id}
                      className="flex-1 bg-green-600 text-white py-2 px-4 rounded-lg hover:bg-green-700 transition-colors disabled:opacity-50"
                    >
                      {updatingStatus === selectedConsultation.id ? 'Completing...' : 'Mark as Completed'}
                    </button>
                  )}
                  
                  <button
                    onClick={() => setShowDetailsModal(false)}
                    className="flex-1 bg-gray-600 text-white py-2 px-4 rounded-lg hover:bg-gray-700 transition-colors"
                  >
                    Close
                  </button>
                </div>
              </div>
            </div>
          </div>
        )}
      </div>
    </LayoutWithSidebar>
  );
};

export default LawyerConsultations; 

CasperSecurity Mini