import React, { useState, useMemo, useEffect } from 'react'; import { useNavigate } from 'react-router-dom'; import useToastStore from '../../store/toastStore'; import { Users, Search, ShieldCheck, Zap, Activity, MoreVertical, Eye, Trash2, UserX, UserCheck, AlertTriangle, Clock, Mail, Hash, X, User as UserIcon, Loader2 } from 'lucide-react'; import { supabase } from "../../lib/supabaseClient"; import useAuthStore from "../../store/authStore"; import StatCard from '../components/StatCard'; import { Card } from "../../components/ui/card"; import { Select } from "../../components/ui/select"; const AdminUsers = () => { // eslint-disable-next-line no-unused-vars const navigate = useNavigate(); const { user: currentUser, profile: currentProfile } = useAuthStore(); const { showToast } = useToastStore(); // Data State const [users, setUsers] = useState([]); const [loading, setLoading] = useState(true); const [_error, setError] = useState(null); const [isProcessing, setIsProcessing] = useState(null); // ID of user being updated const [searchQuery, setSearchQuery] = useState(''); const [showDeleteModal, setShowDeleteModal] = useState(false); const [userToAction, setUserToAction] = useState(null); const [activeTab, setActiveTab] = useState('active'); // 'active' | 'pending' const [pendingRequests, setPendingRequests] = useState([]); // Modals state const [showProfileModal, setShowProfileModal] = useState(false); const [activeProfile, setActiveProfile] = useState(null); const fetchUsers = async () => { setLoading(true); setError(null); try { // 0. EXTREME SELF-HEALING: Sync profile and auto-repair broken accounts let activeCompanyId = currentProfile?.company_id; let activeCompanyName = currentProfile?.company; if (currentUser) { let { data: freshProfile } = await supabase .from('profiles') .select('*') .eq('id', currentUser.id) .single(); // If profile is completely missing from public.profiles, create it now! if (!freshProfile) { console.warn("⚠️ Local profile missing from DB. Initiating Emergency Repair for:", currentUser.email); const { data: repaired, error: repairErr } = await supabase .from('profiles') .insert([{ id: currentUser.id, email: currentUser.email, full_name: currentUser.user_metadata?.full_name || 'Admin', role: currentUser.user_metadata?.role || 'admin', status: 'active', company: currentUser.user_metadata?.company || 'RITESH PVT LTD' }]) .select() .single(); if (!repairErr) freshProfile = repaired; else console.error("Identity repair failed:", repairErr); } if (freshProfile) { // Try to resolve company_id if it's missing but we have a name if (!freshProfile.company_id && freshProfile.company) { const { data: comp } = await supabase .from('companies') .select('id') .eq('name', freshProfile.company) .single(); if (comp) { await supabase .from('profiles') .update({ company_id: comp.id }) .eq('id', currentUser.id); freshProfile.company_id = comp.id; } } activeCompanyId = freshProfile.company_id; activeCompanyName = freshProfile.company; useAuthStore.setState({ profile: freshProfile }); } } console.log("Admin session synchronized. Company ID:", activeCompanyId, "Name:", activeCompanyName); // 1. Fetch ACTIVE users let query = supabase.from('profiles').select('*').eq('status', 'active'); if (activeCompanyId) { query = query.eq('company_id', activeCompanyId); } else if (activeCompanyName) { query = query.eq('company', activeCompanyName); } else { setUsers([]); setLoading(false); return; } const { data, error: sbError } = await query.order('created_at', { ascending: false }); if (sbError) throw sbError; setUsers(data || []); // 2. Fetch PENDING entities (Profile-based status lookup below handles all pending users) let allPending = []; // Path B: Direct profile lookup (Ensure Anjali is visible even if request row failed) let profileQuery = supabase.from('profiles') .select('*') .eq('status', 'pending_approval'); // Inclusion Logic: Find any user that matches our ID OR our Name if (activeCompanyId && activeCompanyName) { profileQuery = profileQuery.or(`company_id.eq.${activeCompanyId},company.eq."${activeCompanyName}"`); } else if (activeCompanyId) { profileQuery = profileQuery.eq('company_id', activeCompanyId); } else if (activeCompanyName) { profileQuery = profileQuery.eq('company', activeCompanyName); } const { data: profData } = await profileQuery; if (profData) { profData.forEach(p => { const alreadyPresent = allPending.some(r => r.user_id === p.id); if (!alreadyPresent) { allPending.push({ id: `p-${p.id}`, user_id: p.id, company_id: p.company_id || activeCompanyId, // Auto-associate with admin's company status: 'pending', created_at: p.created_at, user: p, isManual: true }); } }); } setPendingRequests(allPending); } catch (err) { console.error("Admin user sync error:", err); setError(err.message); } finally { setLoading(false); } }; useEffect(() => { fetchUsers(); }, []); const handleUpdateRole = async (userId, newRole) => { setIsProcessing(userId); try { const { error: upError } = await supabase .from('profiles') .update({ role: newRole }) .eq('id', userId); if (upError) throw upError; setUsers(prev => prev.map(u => u.id === userId ? { ...u, role: newRole } : u)); if (activeProfile?.id === userId) setActiveProfile(prev => ({ ...prev, role: newRole })); showToast(`Protocol: Security clearance updated to ${newRole}.`, "success"); } catch (err) { showToast("Update failed: " + err.message, "error"); } finally { setIsProcessing(null); } }; const handleDeleteUser = async () => { if (!userToAction) return; setIsProcessing(userToAction.id); try { // Note: This only deletes the profile record. // Deleting the Auth user requires Admin API (service role). const { error: delError } = await supabase .from('profiles') .delete() .eq('id', userToAction.id); if (delError) throw delError; setUsers(prev => prev.filter(u => u.id !== userToAction.id)); setShowDeleteModal(false); setUserToAction(null); if (activeProfile?.id === userToAction.id) setShowProfileModal(false); showToast("User record purged from active directory.", "success"); } catch (err) { showToast("Deletion failed: " + err.message, "error"); } finally { setIsProcessing(null); } }; const handleApproveUser = async (request) => { setIsProcessing(request.id); try { // 1. Resolve Company ID with high redundancy let targetCompanyId = currentProfile?.company_id || request.company_id; // Deep fetch if still missing — ensures we aren't blocked by stale local state if (!targetCompanyId && currentUser) { console.log("⚠️ Target Company ID missing in state, reaching out to database..."); const { data: freshProfile, error: _profileFetchErr } = await supabase .from('profiles') .select('company_id, company') .eq('id', currentUser.id) .single(); if (freshProfile?.company_id) { targetCompanyId = freshProfile.company_id; // Update store in background for future actions useAuthStore.setState({ profile: { ...currentProfile, ...freshProfile } }); } else if (freshProfile?.company) { // Final fallback: Find company by name const { data: compData } = await supabase .from('companies') .select('id') .eq('name', freshProfile.company) .single(); if (compData) targetCompanyId = compData.id; } } console.log("🚀 Initiating approval for:", request.user_id, "Company ID:", targetCompanyId); if (!targetCompanyId) { console.error("❌ Critical: Could not resolve Company Association."); throw new Error("Security Protocol Error: Your administrator account is not linked to a registered company. Please contact support."); } // 2. Perform the update const { data: updateResult, error: profileErr } = await supabase .from('profiles') .update({ status: 'active', company_id: targetCompanyId }) .eq('id', request.user_id) .select(); // Requesting data back to confirm it worked if (profileErr) { console.error("Supabase Profile Update Error:", profileErr); throw profileErr; } console.log("✅ Profile updated successfully:", updateResult); // 3. Log the request audit (Non-blocking) if (!request.isManual) { const { error: requestErr } = await supabase .from('user_requests') .update({ status: 'approved', reviewed_by: currentUser.id, reviewed_at: new Date().toISOString() }) .eq('id', request.id); if (requestErr) console.warn("Audit log update failed:", requestErr.message); } // 4. Send notification (Non-blocking) supabase.functions.invoke('send-user-approval-email', { body: { userId: request.user_id, email: request.user?.email, name: request.user?.full_name, company: request.user?.company || currentProfile?.company } }); // Re-fetch UI await fetchUsers(); showToast(`Protocol: Identity verified. ${request.user?.full_name} is now active.`, "success"); } catch (err) { console.error("Critical Approval Failure:", err); showToast("Approval failed: " + err.message, "error"); } finally { setIsProcessing(null); } }; const handleRejectUser = async (request) => { if (!window.confirm(`Are you sure you want to reject ${request.user?.full_name || 'this user'}?`)) return; setIsProcessing(request.id); try { // 1. Update Profile const { error: profileErr } = await supabase .from('profiles') .update({ status: 'rejected' }) .eq('id', request.user_id); if (profileErr) throw profileErr; // 2. Mark Request as Rejected (Only if real record) if (!request.isManual) { const { error: requestErr } = await supabase .from('user_requests') .update({ status: 'rejected', reviewed_by: currentUser.id, reviewed_at: new Date().toISOString() }) .eq('id', request.id); if (requestErr) console.warn("Request log rejection failed:", requestErr.message); } // Remove from local state setPendingRequests(prev => prev.filter(r => r.id !== request.id)); showToast(`Access request rejected.`, "warning"); } catch (err) { showToast("Rejection failed: " + err.message, "error"); } finally { setIsProcessing(null); } }; // 1. Filter Logic const filteredUsers = useMemo(() => { return users.filter(user => (user.full_name || '').toLowerCase().includes((searchQuery || '').toLowerCase()) || (user.email || '').toLowerCase().includes((searchQuery || '').toLowerCase()) || (user.role || '').toLowerCase().includes((searchQuery || '').toLowerCase()) ); }, [users, searchQuery]); const stats = useMemo(() => ({ total: users.length, admins: users.filter(u => u.role === 'admin').length, pending: pendingRequests.length }), [users, pendingRequests]); // 2. Lifecycle Action Handlers const confirmDelete = (user) => { setUserToAction(user); setShowDeleteModal(true); }; const openProfile = (user) => { setActiveProfile(user); setShowProfileModal(true); }; if (loading) return (

Loading directory...

); return (
{/* Header Section */}

Access Control

User Directory.

Manage your team members and their roles here.

{/* Metrics Dashboard */}
0 ? "amber" : "slate"} />
{/* Tabs */}
{/* Terminal Interface */}
{activeTab === 'active' ? ( <>
setSearchQuery(e.target.value)} className="w-full bg-white border border-slate-200 rounded-2xl pl-12 pr-4 py-3 text-sm font-bold focus:outline-none focus:ring-4 focus:ring-indigo-600/5 focus:border-indigo-600 transition-all text-slate-700" />
{filteredUsers.map((user) => ( ))}
User ID User Role Joined On Actions
{user.id.slice(0, 8)}...
{user.profile_picture ? ( {user.full_name ) : (
{user.full_name?.charAt(0) || '?'}
)}
{user.full_name || 'Unnamed User'}
{user.email}
{new Date(user.created_at).toLocaleDateString()}
{filteredUsers.length === 0 && (

No users found

Adjust search query to find who you're looking for.

)} ) : ( <> {/* Pending Requests Table */}
{pendingRequests.map((request) => ( ))}
Request ID User Requested On Actions
{request.id.slice(0, 8)}...
{request.user?.profile_picture ? ( {request.user.full_name ) : (
{request.user?.full_name?.charAt(0) || '?'}
)}
{request.user?.full_name || 'Unnamed User'}
{request.user?.email}
{new Date(request.created_at).toLocaleDateString()}
{pendingRequests.length === 0 && (

Queue is clear

No pending user requests found for your organization.

)} )}
{/* Profile Detail Modal */} {showProfileModal && activeProfile && (

{activeProfile.full_name}

{activeProfile.email}

{/* User Info */}

Entity Metadata

System Hash / UUID {activeProfile.id}
Assigned Role {activeProfile.role}
Joined Terminal {new Date(activeProfile.created_at).toLocaleString()}
Status Authorized
{/* Profile Actions */}
)} {/* Delete Confirmation Modal */} {showDeleteModal && (

Purge User?

You are about to permanently delete @{userToAction?.full_name}. This action remove their profile record from the system.

)}
); }; export default AdminUsers;