import React, { useEffect, useState } from 'react'; import api from '../../utils/api'; import { Card } from '../../components/ui/card'; import { Button } from '../../components/ui/button'; import { Badge } from '../../components/ui/badge'; import { Input } from '../../components/ui/input'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../components/ui/select'; import { Table, TableHeader, TableBody, TableHead, TableRow, TableCell, } from '../../components/ui/table'; import { Pagination, PaginationContent, PaginationLink, PaginationItem, PaginationPrevious, PaginationNext, PaginationEllipsis, } from '../../components/ui/pagination'; import { toast } from 'sonner'; import { CheckCircle, Clock, Search, ArrowUp, ArrowDown } from 'lucide-react'; import PaymentActivationDialog from '../../components/PaymentActivationDialog'; const AdminApprovals = () => { const [pendingUsers, setPendingUsers] = useState([]); const [filteredUsers, setFilteredUsers] = useState([]); const [loading, setLoading] = useState(true); const [actionLoading, setActionLoading] = useState(null); const [paymentDialogOpen, setPaymentDialogOpen] = useState(false); const [selectedUserForPayment, setSelectedUserForPayment] = useState(null); // Filtering state const [searchQuery, setSearchQuery] = useState(''); const [statusFilter, setStatusFilter] = useState('all'); // Pagination state const [currentPage, setCurrentPage] = useState(1); const [itemsPerPage, setItemsPerPage] = useState(10); // Sorting state const [sortBy, setSortBy] = useState('created_at'); const [sortOrder, setSortOrder] = useState('desc'); useEffect(() => { fetchPendingUsers(); }, []); useEffect(() => { filterAndSortUsers(); }, [pendingUsers, searchQuery, statusFilter, sortBy, sortOrder]); useEffect(() => { setCurrentPage(1); }, [searchQuery, statusFilter]); const fetchPendingUsers = async () => { try { const response = await api.get('/admin/users'); const pending = response.data.filter(user => ['pending_email', 'pending_approval', 'pre_approved', 'payment_pending'].includes(user.status) ); setPendingUsers(pending); } catch (error) { toast.error('Failed to fetch pending users'); } finally { setLoading(false); } }; const filterAndSortUsers = () => { let filtered = [...pendingUsers]; // Apply status filter if (statusFilter !== 'all') { filtered = filtered.filter(user => user.status === statusFilter); } // Apply search query if (searchQuery) { const query = searchQuery.toLowerCase(); filtered = filtered.filter(user => user.first_name.toLowerCase().includes(query) || user.last_name.toLowerCase().includes(query) || user.email.toLowerCase().includes(query) || user.phone?.toLowerCase().includes(query) ); } // Apply sorting filtered.sort((a, b) => { let aVal = a[sortBy]; let bVal = b[sortBy]; if (sortBy === 'created_at') { aVal = new Date(aVal); bVal = new Date(bVal); } else if (sortBy === 'first_name') { aVal = `${a.first_name} ${a.last_name}`; bVal = `${b.first_name} ${b.last_name}`; } if (sortOrder === 'asc') { return aVal > bVal ? 1 : -1; } else { return aVal < bVal ? 1 : -1; } }); setFilteredUsers(filtered); }; const handleApprove = async (userId) => { setActionLoading(userId); try { await api.put(`/admin/users/${userId}/approve`); toast.success('User validated and approved! Payment email sent.'); fetchPendingUsers(); } catch (error) { toast.error('Failed to approve user'); } finally { setActionLoading(null); } }; const handleBypassAndApprove = async (userId) => { if (!window.confirm( 'This will bypass email verification and approve the user. ' + 'Are you sure you want to proceed?' )) { return; } setActionLoading(userId); try { await api.put(`/admin/users/${userId}/approve?bypass_email_verification=true`); toast.success('User email verified and approved! Payment email sent.'); fetchPendingUsers(); } catch (error) { toast.error(error.response?.data?.detail || 'Failed to approve user'); } finally { setActionLoading(null); } }; const handleActivatePayment = (user) => { setSelectedUserForPayment(user); setPaymentDialogOpen(true); }; const handlePaymentSuccess = () => { fetchPendingUsers(); // Refresh list }; const getStatusBadge = (status) => { const config = { pending_email: { label: 'Awaiting Email', className: 'bg-orange-100 text-orange-700' }, pending_approval: { label: 'Pending', className: 'bg-gray-200 text-gray-700' }, pre_approved: { label: 'Pre-Approved', className: 'bg-[#81B29A] text-white' }, payment_pending: { label: 'Payment Pending', className: 'bg-orange-500 text-white' } }; const statusConfig = config[status]; return ( {statusConfig.label} ); }; const handleSort = (column) => { if (sortBy === column) { setSortOrder(sortOrder === 'asc' ? 'desc' : 'asc'); } else { setSortBy(column); setSortOrder('asc'); } }; // Pagination calculations const totalPages = Math.ceil(filteredUsers.length / itemsPerPage); const paginatedUsers = filteredUsers.slice( (currentPage - 1) * itemsPerPage, currentPage * itemsPerPage ); const renderSortIcon = (column) => { if (sortBy !== column) return null; return sortOrder === 'asc' ? : ; }; return ( <> {/* Header */}

Approval Queue

Review and approve pending membership applications.

{/* Stats Card */}

Total Pending

{pendingUsers.length}

Awaiting Email

{pendingUsers.filter(u => u.status === 'pending_email').length}

Pending Approval

{pendingUsers.filter(u => u.status === 'pending_approval').length}

Pre-Approved

{pendingUsers.filter(u => u.status === 'pre_approved').length}

Payment Pending

{pendingUsers.filter(u => u.status === 'payment_pending').length}

{/* Filter Card */}
setSearchQuery(e.target.value)} className="pl-12 h-14 rounded-xl border-2 border-[#ddd8eb] focus:border-[#664fa3]" />
{/* Table */} {loading ? (

Loading pending applications...

) : filteredUsers.length > 0 ? ( <> handleSort('first_name')} > Name {renderSortIcon('first_name')} Email Phone handleSort('status')} > Status {renderSortIcon('status')} handleSort('created_at')} > Registered {renderSortIcon('created_at')} Referred By Actions {paginatedUsers.map((user) => ( {user.first_name} {user.last_name} {user.email} {user.phone} {getStatusBadge(user.status)} {new Date(user.created_at).toLocaleDateString()} {user.referred_by_member_name || '-'}
{user.status === 'pending_email' ? ( ) : user.status === 'payment_pending' ? ( ) : ( )}
))}
{/* Pagination Controls */}
{/* Page size selector */}

Show

entries (showing {(currentPage - 1) * itemsPerPage + 1}- {Math.min(currentPage * itemsPerPage, filteredUsers.length)} of {filteredUsers.length})

{/* Pagination */} {totalPages > 1 && ( setCurrentPage(p => Math.max(1, p - 1))} className={currentPage === 1 ? 'pointer-events-none opacity-50' : 'cursor-pointer'} /> {[...Array(totalPages)].map((_, i) => { const showPage = i < 2 || i >= totalPages - 2 || Math.abs(i - currentPage + 1) <= 1; if (!showPage && i === 2) { return ( ); } if (!showPage) return null; return ( setCurrentPage(i + 1)} isActive={currentPage === i + 1} className="cursor-pointer" > {i + 1} ); })} setCurrentPage(p => Math.min(totalPages, p + 1))} className={currentPage === totalPages ? 'pointer-events-none opacity-50' : 'cursor-pointer'} /> )}
) : (

No Pending Approvals

{searchQuery || statusFilter !== 'all' ? 'Try adjusting your filters' : 'All applications have been reviewed!'}

)} {/* Payment Activation Dialog */} ); }; export default AdminApprovals;