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';
import ConfirmationDialog from '../../components/ConfirmationDialog';
const AdminValidations = () => {
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);
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
const [pendingAction, setPendingAction] = 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_validation', 'pre_validated', '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 handleValidateRequest = (user) => {
setPendingAction({ type: 'validate', user });
setConfirmDialogOpen(true);
};
const handleBypassAndValidateRequest = (user) => {
setPendingAction({ type: 'bypass_and_validate', user });
setConfirmDialogOpen(true);
};
const confirmAction = async () => {
if (!pendingAction) return;
const { type, user } = pendingAction;
setActionLoading(user.id);
setConfirmDialogOpen(false);
try {
if (type === 'validate') {
await api.put(`/admin/users/${user.id}/validate`);
toast.success('User validated! Payment email sent.');
} else if (type === 'bypass_and_validate') {
await api.put(`/admin/users/${user.id}/validate?bypass_email_verification=true`);
toast.success('User email verified and validated! Payment email sent.');
}
fetchPendingUsers();
} catch (error) {
toast.error(error.response?.data?.detail || 'Failed to validate user');
} finally {
setActionLoading(null);
setPendingAction(null);
}
};
const getActionMessage = () => {
if (!pendingAction) return {};
const { type, user } = pendingAction;
const userName = `${user.first_name} ${user.last_name}`;
if (type === 'validate') {
return {
title: 'Validate User?',
description: `This will validate ${userName} and send them a payment link email. They will be able to complete payment and become an active member.`,
variant: 'success',
confirmText: 'Yes, Validate User',
};
}
if (type === 'bypass_and_validate') {
return {
title: 'Bypass Email & Validate User?',
description: `This will bypass email verification for ${userName} and validate them immediately. A payment link email will be sent. Use this only if you've confirmed their email through other means.`,
variant: 'warning',
confirmText: 'Yes, Bypass & Validate',
};
}
return {};
};
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_validation: { label: 'Pending Validation', className: 'bg-gray-200 text-gray-700' },
pre_validated: { label: 'Pre-Validated', className: 'bg-[#81B29A] text-white' },
payment_pending: { label: 'Payment Pending', className: 'bg-orange-500 text-white' }
};
const statusConfig = config[status];
return (
Review and validate pending membership applications.
Total Pending
{pendingUsers.length}
Awaiting Email
{pendingUsers.filter(u => u.status === 'pending_email').length}
Pending Validation
{pendingUsers.filter(u => u.status === 'pending_validation').length}
Pre-Validated
{pendingUsers.filter(u => u.status === 'pre_validated').length}
Payment Pending
{pendingUsers.filter(u => u.status === 'payment_pending').length}
Loading pending applications...
Show
entries (showing {(currentPage - 1) * itemsPerPage + 1}- {Math.min(currentPage * itemsPerPage, filteredUsers.length)} of {filteredUsers.length})
{searchQuery || statusFilter !== 'all' ? 'Try adjusting your filters' : 'All applications have been reviewed!'}