import React, { useEffect, useState } from 'react';
import { useNavigate, useLocation, Link } from 'react-router-dom';
import { useAuth } from '../../context/AuthContext';
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 {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from '../../components/ui/dropdown-menu';
import { toast } from 'sonner';
import { Users, Search, User, CreditCard, Eye, CheckCircle, Calendar, AlertCircle, Clock, Mail, UserPlus, Upload, Download, FileDown, ChevronDown } from 'lucide-react';
import PaymentActivationDialog from '../../components/PaymentActivationDialog';
import ConfirmationDialog from '../../components/ConfirmationDialog';
import CreateMemberDialog from '../../components/CreateMemberDialog';
import InviteStaffDialog from '../../components/InviteStaffDialog';
import WordPressImportWizard from '../../components/WordPressImportWizard';
const AdminMembers = () => {
const navigate = useNavigate();
const location = useLocation();
const { hasPermission } = useAuth();
const [users, setUsers] = useState([]);
const [filteredUsers, setFilteredUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [searchQuery, setSearchQuery] = useState('');
const [statusFilter, setStatusFilter] = useState('active');
const [paymentDialogOpen, setPaymentDialogOpen] = useState(false);
const [selectedUserForPayment, setSelectedUserForPayment] = useState(null);
const [statusChanging, setStatusChanging] = useState(null);
const [confirmDialogOpen, setConfirmDialogOpen] = useState(false);
const [pendingStatusChange, setPendingStatusChange] = useState(null);
const [createDialogOpen, setCreateDialogOpen] = useState(false);
const [inviteDialogOpen, setInviteDialogOpen] = useState(false);
const [importDialogOpen, setImportDialogOpen] = useState(false);
const [exporting, setExporting] = useState(false);
useEffect(() => {
fetchMembers();
}, []);
useEffect(() => {
filterUsers();
}, [users, searchQuery, statusFilter]);
const fetchMembers = async () => {
try {
const response = await api.get('/admin/users');
// Filter to only members
const members = response.data.filter(user => user.role === 'member');
setUsers(members);
} catch (error) {
toast.error('Failed to fetch members');
} finally {
setLoading(false);
}
};
const filterUsers = () => {
let filtered = users;
if (statusFilter && statusFilter !== 'all') {
filtered = filtered.filter(user => user.status === statusFilter);
}
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)
);
}
setFilteredUsers(filtered);
};
const handleActivatePayment = (user) => {
setSelectedUserForPayment(user);
setPaymentDialogOpen(true);
};
const handlePaymentSuccess = () => {
fetchMembers(); // Refresh list
};
const handleStatusChangeRequest = (userId, currentStatus, newStatus, user) => {
// Skip confirmation if status didn't actually change
if (currentStatus === newStatus) return;
setPendingStatusChange({ userId, newStatus, user });
setConfirmDialogOpen(true);
};
const confirmStatusChange = async () => {
if (!pendingStatusChange) return;
const { userId, newStatus } = pendingStatusChange;
setStatusChanging(userId);
setConfirmDialogOpen(false);
try {
await api.put(`/admin/users/${userId}/status`, { status: newStatus });
toast.success('Member status updated successfully');
fetchMembers(); // Refresh list
} catch (error) {
toast.error(error.response?.data?.detail || 'Failed to update status');
} finally {
setStatusChanging(null);
setPendingStatusChange(null);
}
};
const handleExport = async (filterType) => {
setExporting(true);
try {
let params = {};
if (filterType === 'current') {
if (statusFilter && statusFilter !== 'all') {
params.status = statusFilter;
}
if (searchQuery) {
params.search = searchQuery;
}
}
// filterType === 'all' will export all members without filters
const response = await api.get('/admin/users/export', {
params,
responseType: 'blob'
});
// Create download link
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute('download', `members_export_${new Date().toISOString().split('T')[0]}.csv`);
document.body.appendChild(link);
link.click();
link.remove();
toast.success('Members exported successfully');
} catch (error) {
toast.error('Failed to export members');
} finally {
setExporting(false);
}
};
const getStatusChangeMessage = () => {
if (!pendingStatusChange) return {};
const { newStatus, user } = pendingStatusChange;
const userName = `${user.first_name} ${user.last_name}`;
const messages = {
payment_pending: {
title: 'Revert to Payment Pending?',
description: `This will change ${userName}'s status back to Payment Pending. They will need to complete payment again to become active.`,
variant: 'warning',
confirmText: 'Yes, Revert Status',
},
active: {
title: 'Activate Member?',
description: `This will activate ${userName}'s membership. They will gain full access to member features and resources.`,
variant: 'success',
confirmText: 'Yes, Activate',
},
inactive: {
title: 'Deactivate Member?',
description: `This will deactivate ${userName}'s membership. They will lose access to member-only features but their data will be preserved.`,
variant: 'warning',
confirmText: 'Yes, Deactivate',
},
canceled: {
title: 'Cancel Membership?',
description: `This will mark ${userName}'s membership as canceled. This indicates they voluntarily ended their membership. Their subscription will not auto-renew.`,
variant: 'danger',
confirmText: 'Yes, Cancel Membership',
},
expired: {
title: 'Mark Membership as Expired?',
description: `This will mark ${userName}'s membership as expired. This indicates their subscription period has ended without renewal.`,
variant: 'warning',
confirmText: 'Yes, Mark as Expired',
},
};
return messages[newStatus] || {
title: 'Confirm Status Change',
description: `Are you sure you want to change ${userName}'s status to ${newStatus}?`,
variant: 'warning',
confirmText: 'Confirm',
};
};
const getStatusBadge = (status) => {
const config = {
pending_email: { label: 'Pending Email', variant: 'orange2' },
pending_validation: { label: 'Pending Validation', variant: 'gray' },
pre_validated: { label: 'Pre-Validated', variant: 'green' },
payment_pending: { label: 'Payment Pending', variant: 'orange' },
active: { label: 'Active', variant: 'green' },
inactive: { label: 'Inactive', variant: 'gray2' },
canceled: { label: 'Canceled', variant: 'red' },
expired: { label: 'Expired', variant: 'red2' },
abandoned: { label: 'Abandoned', variant: 'gray3' }
};
const statusConfig = config[status] || config.inactive;
return (
Manage paying members and their subscriptions.
Total Members
{users.length}
Active
{users.filter(u => u.status === 'active').length}
Payment Pending
{users.filter(u => u.status === 'payment_pending').length}
Inactive
{users.filter(u => u.status === 'inactive').length}
Loading members...
Email: {user.email}
Phone: {user.phone}
Joined: {new Date(user.created_at).toLocaleDateString()}
{user.referred_by_member_name && (Referred by: {user.referred_by_member_name}
)}
Last reminder: {new Date(reminderInfo.lastReminderAt).toLocaleDateString()} at {new Date(reminderInfo.lastReminderAt).toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' })}
)}{searchQuery || statusFilter !== 'all' ? 'Try adjusting your filters' : 'No members yet'}