- Profile Picture\
Donation Tracking\ Validation Rejection\ Subscription Data Export\ Admin Dashboard Logo\ Admin Navbar Reorganization
This commit is contained in:
@@ -29,9 +29,10 @@ import {
|
||||
PaginationEllipsis,
|
||||
} from '../../components/ui/pagination';
|
||||
import { toast } from 'sonner';
|
||||
import { CheckCircle, Clock, Search, ArrowUp, ArrowDown } from 'lucide-react';
|
||||
import { CheckCircle, Clock, Search, ArrowUp, ArrowDown, X } from 'lucide-react';
|
||||
import PaymentActivationDialog from '../../components/PaymentActivationDialog';
|
||||
import ConfirmationDialog from '../../components/ConfirmationDialog';
|
||||
import RejectionDialog from '../../components/RejectionDialog';
|
||||
|
||||
const AdminValidations = () => {
|
||||
const [pendingUsers, setPendingUsers] = useState([]);
|
||||
@@ -42,6 +43,8 @@ const AdminValidations = () => {
|
||||
const [selectedUserForPayment, setSelectedUserForPayment] = useState(null);
|
||||
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
|
||||
const [pendingAction, setPendingAction] = useState(null);
|
||||
const [rejectionDialogOpen, setRejectionDialogOpen] = useState(false);
|
||||
const [userToReject, setUserToReject] = useState(null);
|
||||
|
||||
// Filtering state
|
||||
const [searchQuery, setSearchQuery] = useState('');
|
||||
@@ -193,6 +196,28 @@ const AdminValidations = () => {
|
||||
fetchPendingUsers(); // Refresh list
|
||||
};
|
||||
|
||||
const handleRejectUser = (user) => {
|
||||
setUserToReject(user);
|
||||
setRejectionDialogOpen(true);
|
||||
};
|
||||
|
||||
const confirmRejection = async (reason) => {
|
||||
if (!userToReject) return;
|
||||
|
||||
setActionLoading(userToReject.id);
|
||||
try {
|
||||
await api.post(`/admin/users/${userToReject.id}/reject`, { reason });
|
||||
toast.success(`${userToReject.first_name} ${userToReject.last_name} has been rejected`);
|
||||
fetchPendingUsers();
|
||||
setRejectionDialogOpen(false);
|
||||
setUserToReject(null);
|
||||
} catch (error) {
|
||||
toast.error(error.response?.data?.detail || 'Failed to reject user');
|
||||
} finally {
|
||||
setActionLoading(null);
|
||||
}
|
||||
};
|
||||
|
||||
const getStatusBadge = (status) => {
|
||||
const config = {
|
||||
pending_email: { label: 'Awaiting Email', className: 'bg-orange-100 text-orange-700' },
|
||||
@@ -360,32 +385,68 @@ const AdminValidations = () => {
|
||||
<TableCell>
|
||||
<div className="flex gap-2">
|
||||
{user.status === 'pending_email' ? (
|
||||
<Button
|
||||
onClick={() => handleBypassAndValidateRequest(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
|
||||
>
|
||||
{actionLoading === user.id ? 'Validating...' : 'Bypass & Validate'}
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
onClick={() => handleBypassAndValidateRequest(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
|
||||
>
|
||||
{actionLoading === user.id ? 'Validating...' : 'Bypass & Validate'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleRejectUser(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="border-2 border-red-500 text-red-500 hover:bg-red-50"
|
||||
>
|
||||
<X className="h-4 w-4 mr-1" />
|
||||
Reject
|
||||
</Button>
|
||||
</>
|
||||
) : user.status === 'payment_pending' ? (
|
||||
<Button
|
||||
onClick={() => handleActivatePayment(user)}
|
||||
size="sm"
|
||||
className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-1" />
|
||||
Activate Payment
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
onClick={() => handleActivatePayment(user)}
|
||||
size="sm"
|
||||
className="bg-[#DDD8EB] text-[#422268] hover:bg-white"
|
||||
>
|
||||
<CheckCircle className="h-4 w-4 mr-1" />
|
||||
Activate Payment
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleRejectUser(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="border-2 border-red-500 text-red-500 hover:bg-red-50"
|
||||
>
|
||||
<X className="h-4 w-4 mr-1" />
|
||||
Reject
|
||||
</Button>
|
||||
</>
|
||||
) : (
|
||||
<Button
|
||||
onClick={() => handleValidateRequest(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
className="bg-[#81B29A] text-white hover:bg-[#6FA087]"
|
||||
>
|
||||
{actionLoading === user.id ? 'Validating...' : 'Validate'}
|
||||
</Button>
|
||||
<>
|
||||
<Button
|
||||
onClick={() => handleValidateRequest(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
className="bg-[#81B29A] text-white hover:bg-[#6FA087]"
|
||||
>
|
||||
{actionLoading === user.id ? 'Validating...' : 'Validate'}
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => handleRejectUser(user)}
|
||||
disabled={actionLoading === user.id}
|
||||
size="sm"
|
||||
variant="outline"
|
||||
className="border-2 border-red-500 text-red-500 hover:bg-red-50"
|
||||
>
|
||||
<X className="h-4 w-4 mr-1" />
|
||||
Reject
|
||||
</Button>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</TableCell>
|
||||
@@ -502,6 +563,15 @@ const AdminValidations = () => {
|
||||
loading={actionLoading !== null}
|
||||
{...getActionMessage()}
|
||||
/>
|
||||
|
||||
{/* Rejection Dialog */}
|
||||
<RejectionDialog
|
||||
open={rejectionDialogOpen}
|
||||
onOpenChange={setRejectionDialogOpen}
|
||||
onConfirm={confirmRejection}
|
||||
user={userToReject}
|
||||
loading={actionLoading !== null}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user