/** * Settings page — role and member management (Discord-like UI) * Two tabs: "Roles" and "Members" * Left sidebar + right panel layout */ "use client"; import { useState, useEffect, useCallback } from "react"; import { useRouter } from "next/navigation"; import { Shield, Users, Plus, X, Check, Trash2, ChevronDown, ChevronRight, Edit2, AlertTriangle, ArrowLeft, } from "lucide-react"; import { Button } from "@/components/ui/button"; import { Badge } from "@/components/ui/badge"; import { fetchCustomRoles, createCustomRole, updateCustomRole, deleteCustomRole, getUserPermissions, fetchEmployeesWithRoles, assignRoleToEmployee, removeRoleFromEmployee, removeEmployee, } from "@/lib/supabase/rbac-actions"; import { PERMISSIONS_LIST, PERMISSION_CATEGORIES } from "@/lib/permissions"; import type { Permission } from "@/lib/permissions"; import PageSkeleton from "@/app/(app)/pages/PageSkeleton"; const ROLE_COLORS = [ "#1D9E75", "#6366F1", "#F59E0B", "#EF4444", "#EC4899", "#8B5CF6", "#06B6D4", "#10B981", "#F97316", "#6B7280", ]; export default function SettingsPage() { const router = useRouter(); const [tab, setTab] = useState<"roles" | "members">("roles"); const [loading, setLoading] = useState(true); const [canManage, setCanManage] = useState(false); const [roles, setRoles] = useState([]); const [employees, setEmployees] = useState([]); // Selected items const [selectedRoleId, setSelectedRoleId] = useState(null); const [selectedEmployeeId, setSelectedEmployeeId] = useState(null); // Role editing const [editingRole, setEditingRole] = useState(null); const [roleName, setRoleName] = useState(""); const [roleColor, setRoleColor] = useState(ROLE_COLORS[0]); const [roleHierarchy, setRoleHierarchy] = useState(3); const [rolePerms, setRolePerms] = useState>(new Set()); const [canManageLower, setCanManageLower] = useState(false); const [savingRole, setSavingRole] = useState(false); const [showDeleteRole, setShowDeleteRole] = useState(false); // Member editing const [assigningRole, setAssigningRole] = useState(null); useEffect(() => { let cancelled = false; Promise.all([ getUserPermissions().catch(() => ({ orgRole: "employee" })), fetchCustomRoles().catch(() => []), fetchEmployeesWithRoles().catch(() => []), ]) .then(([perms, r, e]) => { if (!cancelled) { setCanManage(perms.orgRole === "owner" || perms.orgRole === "admin"); setRoles(r); setEmployees(e); } }) .catch(() => {}) .finally(() => { if (!cancelled) setLoading(false); }); return () => { cancelled = true; }; }, []); // Open role editor const openRoleEditor = useCallback((role: any | null) => { setSelectedRoleId(role?.id || null); setEditingRole(role); setRoleName(role?.name || ""); setRoleColor(role?.color || ROLE_COLORS[0]); setRoleHierarchy(role?.hierarchy_level || 3); setCanManageLower(role?.can_manage_lower || false); const perms = new Set((role?.role_permissions || []).map((p: any) => p.permission) as Permission[]); setRolePerms(perms); setShowDeleteRole(false); }, []); // Open member panel const openMemberPanel = useCallback((emp: any) => { setSelectedEmployeeId(emp.id); setAssigningRole(null); }, []); // Save role const handleSaveRole = async () => { if (!roleName.trim()) return; setSavingRole(true); try { if (editingRole) { await updateCustomRole(editingRole.id, roleName, roleHierarchy, Array.from(rolePerms), roleColor, canManageLower); } else { await createCustomRole(roleName, roleHierarchy, Array.from(rolePerms), roleColor, canManageLower); } const updated = await fetchCustomRoles(); setRoles(updated); setSelectedRoleId(null); setEditingRole(null); } catch (e: any) { alert(e.message || "Failed to save role"); } finally { setSavingRole(false); } }; // Delete role const handleDeleteRole = async () => { if (!editingRole) return; try { await deleteCustomRole(editingRole.id); const updated = await fetchCustomRoles(); setRoles(updated); setSelectedRoleId(null); setEditingRole(null); } catch (e: any) { alert(e.message || "Failed to delete role"); } setShowDeleteRole(false); }; // Assign role to employee const handleAssignRole = async (employeeId: string, roleId: string) => { await assignRoleToEmployee(employeeId, roleId); const emps = await fetchEmployeesWithRoles(); setEmployees(emps); setAssigningRole(null); }; // Remove role from employee const handleRemoveRole = async (employeeId: string, roleId: string) => { await removeRoleFromEmployee(employeeId, roleId); const emps = await fetchEmployeesWithRoles(); setEmployees(emps); }; // Remove employee const handleRemoveEmployee = async (employeeId: string) => { if (!confirm("Remove this employee? This cannot be undone.")) return; await removeEmployee(employeeId); const emps = await fetchEmployeesWithRoles(); setEmployees(emps); setSelectedEmployeeId(null); }; if (loading) return ; if (!canManage) { return (

You don't have permission to manage settings.

); } const selectedRole = roles.find((r) => r.id === selectedRoleId) || null; const selectedEmployee = employees.find((e) => e.id === selectedEmployeeId) || null; return (
{/* Left Sidebar */}
{/* Tabs */}
{/* Tab Content */}
{tab === "roles" && (
Roles
{/* System roles */}
System Roles
{[ { name: "Owner", color: "#EF4444", level: 10, system: true }, { name: "Admin", color: "#F59E0B", level: 9, system: true }, { name: "Employee", color: "#6B7280", level: 1, system: true }, ].map((r) => (
{r.name} Level {r.level}
))} {/* Custom roles */} {roles.length > 0 && ( <>
Custom Roles
{roles .sort((a, b) => (b.hierarchy_level || 0) - (a.hierarchy_level || 0)) .map((role) => ( ))} )}
)} {tab === "members" && (
Members ({employees.length})
{employees.map((emp) => ( ))}
)}
{/* Right Panel */}
{tab === "roles" && selectedRole && (

{editingRole ? "Edit Role" : "Create Role"}

{editingRole && !showDeleteRole && ( )} {showDeleteRole ? (
) : ( )}
{/* Name */}
setRoleName(e.target.value)} placeholder="e.g. Finance Lead" className="mt-2 w-full border border-gray-200 rounded-lg px-3 py-2.5 text-sm focus:outline-none focus:ring-2 focus:ring-teal-300 focus:border-teal-400" />
{/* Color */}
{ROLE_COLORS.map((c) => (
{/* Hierarchy Level */}
setRoleHierarchy(parseInt(e.target.value))} className="w-full mt-2 accent-teal-600" />

Higher roles can manage lower roles in the hierarchy.

{/* Can Manage Lower Roles */}
setCanManageLower(e.target.checked)} className="rounded border-gray-300 text-teal-600 focus:ring-teal-500" />

Can manage roles below this level

Users with this role can assign/remove roles with a lower hierarchy level.

{/* Permissions */}
{PERMISSION_CATEGORIES.map((cat) => (

{cat}

{PERMISSIONS_LIST.filter((p) => p.category === cat).map((perm) => ( ))}
))}
{/* Save */}
)} {tab === "roles" && !selectedRole && (

Select a role to edit, or click + to create one.

)} {tab === "members" && selectedEmployee && (
{selectedEmployee.first_name[0]}{selectedEmployee.last_name[0]}

{selectedEmployee.first_name} {selectedEmployee.last_name}

{selectedEmployee.job_title || "No title"}

{/* Details */}
{[ { label: "Email", value: selectedEmployee.email }, { label: "Phone", value: selectedEmployee.phone || "—" }, { label: "Department", value: selectedEmployee.department_name || "—" }, { label: "Org Role", value: selectedEmployee.org_role || "Employee" }, { label: "Status", value: selectedEmployee.status }, ].map(({ label, value }) => (
{label} {value}
))}
{/* Roles */}
{assigningRole === selectedEmployee.id && (
{roles.length === 0 ? (

No custom roles created.

) : ( roles.map((role: any) => { const isAssigned = (selectedEmployee.employee_roles || []).some((er: any) => er.role_id === role.id); return (
{role.name}
{isAssigned ? ( Assigned ) : ( )}
); }) )}
)} {(selectedEmployee.employee_roles || []).length === 0 ? (

No custom roles assigned.

) : (
{(selectedEmployee.employee_roles as any[]).map((er: any) => (
{er.role?.name} Level {er.role?.hierarchy_level}
))}
)}
{/* Remove */}
)} {tab === "members" && !selectedEmployee && (

Select a member to view details.

)}
); }