import { useEffect, useState } from "react"; import { useNavigate } from "react-router-dom"; import { supabase } from "@/integrations/supabase/client"; import { useAuth } from "@/hooks/useAuth"; import { AppLayout } from "@/components/AppLayout"; import { Card, CardContent } from "@/components/ui/card"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog"; import { Skeleton } from "@/components/ui/skeleton"; import { Input } from "@/components/ui/input"; import { formatDateTime } from "@/lib/format"; import { Eye, Shield, ShieldOff, Search, X, Trash2 } from "lucide-react"; import { toast } from "@/hooks/use-toast"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle, } from "@/components/ui/alert-dialog"; interface Member { id: string; email: string | null; name: string | null; created_at: string; isAdmin?: boolean; } interface UserAccess { id: string; granted_at: string; product: { title: string }; } export default function AdminMembers() { const { user, isAdmin, loading: authLoading } = useAuth(); const navigate = useNavigate(); const [members, setMembers] = useState([]); const [adminIds, setAdminIds] = useState>(new Set()); const [loading, setLoading] = useState(true); const [selectedMember, setSelectedMember] = useState(null); const [memberAccess, setMemberAccess] = useState([]); const [dialogOpen, setDialogOpen] = useState(false); const [searchQuery, setSearchQuery] = useState(''); const [filterRole, setFilterRole] = useState('all'); const [deleteDialogOpen, setDeleteDialogOpen] = useState(false); const [memberToDelete, setMemberToDelete] = useState(null); const [isDeleting, setIsDeleting] = useState(false); useEffect(() => { if (!authLoading) { if (!user) navigate("/auth"); else if (!isAdmin) navigate("/dashboard"); else fetchMembers(); } }, [user, isAdmin, authLoading]); const fetchMembers = async () => { const [profilesRes, rolesRes] = await Promise.all([ supabase.from("profiles").select("*").order("created_at", { ascending: false }), supabase.from("user_roles").select("user_id").eq("role", "admin"), ]); const admins = new Set((rolesRes.data || []).map((r) => r.user_id)); setAdminIds(admins); if (profilesRes.data) { setMembers(profilesRes.data.map((p) => ({ ...p, isAdmin: admins.has(p.id) }))); } setLoading(false); }; // Filter members based on search and role const filteredMembers = members.filter((member) => { const matchesSearch = member.name?.toLowerCase().includes(searchQuery.toLowerCase()) || member.email?.toLowerCase().includes(searchQuery.toLowerCase()); const matchesRole = filterRole === 'all' || (filterRole === 'admin' && adminIds.has(member.id)) || (filterRole === 'member' && !adminIds.has(member.id)); return matchesSearch && matchesRole; }); const clearFilters = () => { setSearchQuery(''); setFilterRole('all'); }; const viewMemberDetails = async (member: Member) => { setSelectedMember(member); const { data } = await supabase.from("user_access").select("*, product:products(title)").eq("user_id", member.id); setMemberAccess((data as unknown as UserAccess[]) || []); setDialogOpen(true); }; const toggleAdminRole = async (memberId: string, currentlyAdmin: boolean) => { if (currentlyAdmin) { const { error } = await supabase.from("user_roles").delete().eq("user_id", memberId).eq("role", "admin"); if (error) toast({ title: "Error", description: "Gagal menghapus role admin", variant: "destructive" }); else { toast({ title: "Berhasil", description: "Role admin dihapus" }); fetchMembers(); } } else { const { error } = await supabase.from("user_roles").insert({ user_id: memberId, role: "admin" }); if (error) toast({ title: "Error", description: error.message, variant: "destructive" }); else { toast({ title: "Berhasil", description: "Role admin ditambahkan" }); fetchMembers(); } } }; const confirmDeleteMember = (member: Member) => { if (member.id === user?.id) { toast({ title: "Error", description: "Tidak bisa menghapus akun sendiri", variant: "destructive" }); return; } setMemberToDelete(member); setDeleteDialogOpen(true); }; const deleteMember = async () => { if (!memberToDelete) return; setIsDeleting(true); try { const userId = memberToDelete.id; // Step 1: Delete auth_otps await supabase.from("auth_otps").delete().eq("user_id", userId); // Step 2: Delete order_items (first to avoid FK issues) const { data: orders } = await supabase.from("orders").select("id").eq("user_id", userId); if (orders && orders.length > 0) { const orderIds = orders.map(o => o.id); await supabase.from("order_items").delete().in("order_id", orderIds); } // Step 3: Delete orders await supabase.from("orders").delete().eq("user_id", userId); // Step 4: Delete user_access await supabase.from("user_access").delete().eq("user_id", userId); // Step 5: Delete video_progress await supabase.from("video_progress").delete().eq("user_id", userId); // Step 6: Delete collaboration withdrawals + wallet records await supabase.from("withdrawals").delete().eq("user_id", userId); await supabase.from("wallet_transactions").delete().eq("user_id", userId); await supabase.from("collaborator_wallets").delete().eq("user_id", userId); // Step 7: Delete consulting_slots await supabase.from("consulting_slots").delete().eq("user_id", userId); // Step 8: Delete calendar_events await supabase.from("calendar_events").delete().eq("user_id", userId); // Step 9: Delete user_roles await supabase.from("user_roles").delete().eq("user_id", userId); // Step 10: Delete profile await supabase.from("profiles").delete().eq("id", userId); // Step 11: Delete from auth.users using edge function const { error: deleteError } = await supabase.functions.invoke('delete-user', { body: { user_id: userId } }); if (deleteError) { console.error('Error deleting from auth.users:', deleteError); throw new Error(`Gagal menghapus user dari auth: ${deleteError.message}`); } toast({ title: "Berhasil", description: `Member ${memberToDelete.email || memberToDelete.name} berhasil dihapus beserta semua data terkait` }); setDeleteDialogOpen(false); setMemberToDelete(null); fetchMembers(); } catch (error: unknown) { const message = error instanceof Error ? error.message : "Gagal menghapus member"; console.error('Delete member error:', error); toast({ title: "Error", description: message, variant: "destructive" }); } finally { setIsDeleting(false); } }; if (authLoading || loading) { return (
); } return (

Manajemen Member

Kelola semua pengguna

{/* Search & Filter */}
setSearchQuery(e.target.value)} className="pl-10 border-2" /> {searchQuery && ( )}
Role: {(searchQuery || filterRole !== 'all') && ( )}

Menampilkan {filteredMembers.length} dari {members.length} member

{filteredMembers.length === 0 ? (

{searchQuery || filterRole !== 'all' ? 'Tidak ada member yang cocok dengan filter' : 'Belum ada member'}

) : ( <> {/* Desktop Table */}
Email Nama Role Bergabung Aksi {filteredMembers.map((member) => ( {member.email || "-"} {member.name || "-"} {adminIds.has(member.id) ? ( Admin ) : ( Member )} {formatDateTime(member.created_at)} ))}
{/* Mobile Card Layout */}
{filteredMembers.map((member) => (

{member.name || "Tanpa Nama"}

{member.email || "-"}

{adminIds.has(member.id) ? ( Admin ) : ( Member )}
Bergabung: {formatDateTime(member.created_at)}
))}
)} Detail Member {selectedMember && (

Email: {selectedMember.email}

Nama: {selectedMember.name || "-"}

ID: {selectedMember.id}

Akses Produk:

{memberAccess.length === 0 ? (

Tidak ada akses

) : (
{memberAccess.map((access) => (
{access.product?.title} {formatDateTime(access.granted_at)}
))}
)}
)}
Hapus Member?

Anda akan menghapus member {memberToDelete?.email || memberToDelete?.name}.

Tindakan ini akan menghapus SEMUA data terkait member ini:

  • Order dan item order
  • Akses produk
  • Progress video
  • Jadwal konsultasi
  • Event kalender
  • Role admin (jika ada)
  • Profil user
  • Akun autentikasi

Tindakan ini TIDAK BISA dibatalkan.

Batal {isDeleting ? ( <> Menghapus... ) : ( <> Ya, Hapus Member )}
); }