import React, { useState, useEffect, useCallback } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '../../components/ui/card'; import { Badge } from '../../components/ui/badge'; import { Button } from '../../components/ui/button'; import { Input } from '../../components/ui/input'; import { Label } from '../../components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from '../../components/ui/select'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from '../../components/ui/alert-dialog'; import { toast } from 'sonner'; import { useAuth } from '../../context/AuthContext'; import useMemberTiers from '../../hooks/use-member-tiers'; import { TIER_ICON_OPTIONS, BADGE_COLOR_PRESETS } from '../../config/MemberTiers'; import { getTierIcon } from '../../config/memberTierIcons'; import { Save, RotateCcw, Plus, Trash2, GripVertical, AlertTriangle, Users } from 'lucide-react'; const AdminMemberTiers = () => { const { user } = useAuth(); const { tiers, loading, saving, updateTiers, resetToDefaults } = useMemberTiers({ isAdmin: true }); const [editedTiers, setEditedTiers] = useState([]); const [hasChanges, setHasChanges] = useState(false); const [showResetDialog, setShowResetDialog] = useState(false); const isSuperAdmin = user?.role === 'superadmin'; // Initialize edited tiers when tiers load useEffect(() => { if (tiers && tiers.length > 0) { setEditedTiers(JSON.parse(JSON.stringify(tiers))); setHasChanges(false); } }, [tiers]); // Check for changes useEffect(() => { if (tiers && editedTiers.length > 0) { const changed = JSON.stringify(tiers) !== JSON.stringify(editedTiers); setHasChanges(changed); } }, [tiers, editedTiers]); const handleTierChange = useCallback((index, field, value) => { setEditedTiers(prev => { const updated = [...prev]; updated[index] = { ...updated[index], [field]: value }; return updated; }); }, []); const handleAddTier = useCallback(() => { const newTier = { id: `tier_${Date.now()}`, label: 'New Tier', minYears: editedTiers.length > 0 ? Math.max(...editedTiers.map(t => t.maxYears || 0)) + 0.001 : 0, maxYears: 999, iconKey: 'star', badgeClass: 'bg-gray-100 text-gray-800 border-gray-200', }; setEditedTiers(prev => [...prev, newTier]); }, [editedTiers]); const handleRemoveTier = useCallback((index) => { if (editedTiers.length <= 1) { toast.error('You must have at least one tier'); return; } setEditedTiers(prev => prev.filter((_, i) => i !== index)); }, [editedTiers.length]); const validateTiers = useCallback(() => { for (let i = 0; i < editedTiers.length; i++) { const tier = editedTiers[i]; if (!tier.label?.trim()) { toast.error(`Tier ${i + 1} must have a label`); return false; } if (tier.minYears < 0) { toast.error(`Tier "${tier.label}" has invalid minimum years`); return false; } if (tier.maxYears <= tier.minYears) { toast.error(`Tier "${tier.label}" max years must be greater than min years`); return false; } } // Check for overlapping ranges const sorted = [...editedTiers].sort((a, b) => a.minYears - b.minYears); for (let i = 0; i < sorted.length - 1; i++) { if (sorted[i].maxYears >= sorted[i + 1].minYears) { toast.error(`Tier ranges overlap between "${sorted[i].label}" and "${sorted[i + 1].label}"`); return false; } } return true; }, [editedTiers]); const handleSave = async () => { if (!validateTiers()) return; const success = await updateTiers(editedTiers); if (success) { toast.success('Member tiers saved successfully'); } else { toast.error('Failed to save member tiers'); } }; const handleReset = async () => { const success = await resetToDefaults(); if (success) { toast.success('Member tiers reset to defaults'); setShowResetDialog(false); } else { toast.error('Failed to reset member tiers'); } }; const handleDiscardChanges = () => { setEditedTiers(JSON.parse(JSON.stringify(tiers))); setHasChanges(false); }; if (loading) { return (
); } return (
{/* Header and Actions */}

Configure tier names, time ranges, and badges displayed in the members directory.

{hasChanges && ( )} {isSuperAdmin && ( )}
{/* Tier Cards */}
{editedTiers.map((tier, index) => { const IconComponent = getTierIcon(tier.iconKey); return (
{/* Drag Handle & Remove */}
{/* Tier Configuration */}
{/* Label */}
handleTierChange(index, 'label', e.target.value)} placeholder="Tier Name" />
{/* Min Years */}
handleTierChange(index, 'minYears', parseFloat(e.target.value) || 0)} />
{/* Max Years */}
handleTierChange(index, 'maxYears', parseFloat(e.target.value) || 0)} />
{/* Icon */}
{/* Badge Color */}
{/* Preview */}
{tier.label || 'Tier Name'}
); })}
{/* Add Tier Button */} {/* Info Card */} How Member Tiers Work

Member tiers are automatically assigned based on how long a member has been active. The tier badge appears on member profiles and in the member directory.

  • Tiers are matched based on membership duration in years
  • Each tier should have non-overlapping year ranges
  • The last tier typically uses a high max value (e.g., 999) to catch all long-term members
{/* Reset Confirmation Dialog */} Reset Tiers to Defaults? This will delete all custom tier configurations and restore the default member tiers. This action cannot be undone. Cancel Reset to Defaults
); }; export default AdminMemberTiers;