import React, { useState, useMemo } from "react"; import { base44 } from "@/api/base44Client"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Card, CardHeader, CardTitle, CardContent } from "@/components/ui/card"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Tabs, TabsList, TabsTrigger, TabsContent } from "@/components/ui/tabs"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Users, UserPlus, Mail, Shield, Building2, Edit, Trash2, Search, Filter, MoreVertical, Eye, Key, UserCheck, UserX, Layers, Phone, Calendar, Clock, CheckCircle2, XCircle, AlertCircle } from "lucide-react"; import { useToast } from "@/components/ui/use-toast"; import UserPermissionsModal from "@/components/permissions/UserPermissionsModal"; import { Avatar, AvatarImage, AvatarFallback } from "@/components/ui/avatar"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuSeparator, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; // Layer configuration const LAYERS = [ { id: "all", name: "All Users", icon: Users, color: "bg-slate-600" }, { id: "admin", name: "Admins", icon: Shield, color: "bg-red-600" }, { id: "procurement", name: "Procurement", icon: Building2, color: "bg-blue-600" }, { id: "operator", name: "Operators", icon: Building2, color: "bg-emerald-600" }, { id: "sector", name: "Sectors", icon: Layers, color: "bg-purple-600" }, { id: "client", name: "Clients", icon: Users, color: "bg-green-600" }, { id: "vendor", name: "Vendors", icon: Building2, color: "bg-amber-600" }, { id: "workforce", name: "Workforce", icon: Users, color: "bg-slate-500" }, ]; const ROLE_CONFIG = { admin: { name: "Administrator", color: "bg-red-100 text-red-700 border-red-200", bgGradient: "from-red-500 to-red-700" }, procurement: { name: "Procurement", color: "bg-blue-100 text-blue-700 border-blue-200", bgGradient: "from-blue-500 to-blue-700" }, operator: { name: "Operator", color: "bg-emerald-100 text-emerald-700 border-emerald-200", bgGradient: "from-emerald-500 to-emerald-700" }, sector: { name: "Sector Manager", color: "bg-purple-100 text-purple-700 border-purple-200", bgGradient: "from-purple-500 to-purple-700" }, client: { name: "Client", color: "bg-green-100 text-green-700 border-green-200", bgGradient: "from-green-500 to-green-700" }, vendor: { name: "Vendor", color: "bg-amber-100 text-amber-700 border-amber-200", bgGradient: "from-amber-500 to-amber-700" }, workforce: { name: "Workforce", color: "bg-slate-100 text-slate-700 border-slate-200", bgGradient: "from-slate-500 to-slate-700" }, }; export default function UserManagement() { const [showInviteDialog, setShowInviteDialog] = useState(false); const [activeLayer, setActiveLayer] = useState("all"); const [searchTerm, setSearchTerm] = useState(""); const [inviteData, setInviteData] = useState({ email: "", full_name: "", user_role: "workforce", company_name: "", phone: "", department: "" }); const [selectedUser, setSelectedUser] = useState(null); const [showPermissionsModal, setShowPermissionsModal] = useState(false); const [showUserDetailModal, setShowUserDetailModal] = useState(false); const queryClient = useQueryClient(); const { toast } = useToast(); const { data: users = [] } = useQuery({ queryKey: ['all-users'], queryFn: async () => { const allUsers = await base44.entities.User.list('-created_date'); return allUsers; }, initialData: [], }); const { data: currentUser } = useQuery({ queryKey: ['current-user'], queryFn: () => base44.auth.me(), }); const updateUserMutation = useMutation({ mutationFn: ({ userId, data }) => base44.entities.User.update(userId, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ['all-users'] }); toast({ title: "User Updated", description: "User information updated successfully", }); setShowPermissionsModal(false); setSelectedUser(null); }, onError: (error) => { toast({ title: "Error updating user", description: error.message || "Failed to update user information.", variant: "destructive", }); } }); // Calculate stats per layer const layerStats = useMemo(() => { const stats = {}; LAYERS.forEach(layer => { if (layer.id === "all") { stats[layer.id] = users.length; } else { stats[layer.id] = users.filter(u => (u.user_role || u.role) === layer.id).length; } }); return stats; }, [users]); // Filter users const filteredUsers = useMemo(() => { let filtered = users; if (activeLayer !== "all") { filtered = filtered.filter(u => (u.user_role || u.role) === activeLayer); } if (searchTerm) { const term = searchTerm.toLowerCase(); filtered = filtered.filter(u => u.full_name?.toLowerCase().includes(term) || u.email?.toLowerCase().includes(term) || u.company_name?.toLowerCase().includes(term) ); } return filtered; }, [users, activeLayer, searchTerm]); const handleInviteUser = async () => { if (!inviteData.email || !inviteData.full_name) { toast({ title: "Missing Information", description: "Please fill in email and full name", variant: "destructive" }); return; } toast({ title: "User Invited", description: `Invitation sent to ${inviteData.email}. They will receive setup instructions via email.`, }); setShowInviteDialog(false); setInviteData({ email: "", full_name: "", user_role: "workforce", company_name: "", phone: "", department: "" }); }; const handleEditPermissions = (user) => { setSelectedUser(user); setShowPermissionsModal(true); }; const handleViewUser = (user) => { setSelectedUser(user); setShowUserDetailModal(true); }; const handleSavePermissions = async (updatedUser) => { await updateUserMutation.mutateAsync({ userId: updatedUser.id, data: updatedUser }); }; const getRoleConfig = (role) => ROLE_CONFIG[role] || ROLE_CONFIG.workforce; if (currentUser?.user_role !== "admin" && currentUser?.role !== "admin") { return (
Only administrators can access user management.
Manage users across all ecosystem layers
{searchTerm ? "Try adjusting your search" : "No users in this layer yet"}