diff --git a/src/app/admin/roles/page.jsx b/src/app/admin/roles/page.jsx index c4ddcee..55afff7 100644 --- a/src/app/admin/roles/page.jsx +++ b/src/app/admin/roles/page.jsx @@ -1,807 +1,261 @@ -// "use client"; -// import { useState, useEffect } from "react"; - -// export default function RolesPermissions() { -// const [activeTab, setActiveTab] = useState("roles"); -// const [roles, setRoles] = useState([]); -// const [users, setUsers] = useState([]); -// const [modules, setModules] = useState({}); -// const [loading, setLoading] = useState(true); -// const [showRoleModal, setShowRoleModal] = useState(false); -// const [editingRole, setEditingRole] = useState(null); -// const [roleName, setRoleName] = useState(""); -// const [roleStatus, setRoleStatus] = useState("Active"); -// const [selectedRole, setSelectedRole] = useState(null); - -// // ΓöÇΓöÇΓöÇ Fetch roles & permission modules ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const fetchRoles = async () => { -// try { -// const res = await fetch("/api/all-roles"); -// const data = await res.json(); -// setRoles(data.roles || []); -// setModules(data.modules || {}); -// } catch (err) { -// console.error("Error fetching roles:", err); -// } -// }; - -// // ΓöÇΓöÇΓöÇ Fetch all users (students + instructors) ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const fetchUsers = async () => { -// try { -// const [studentRes, instructorRes] = await Promise.all([ -// fetch("/api/register"), -// fetch("/api/instructor"), -// ]); - -// const students = await studentRes.json(); -// const instructors = await instructorRes.json(); - -// const studentList = (Array.isArray(students) ? students : []).map((s) => ({ -// ...s, -// currentRole: s.role || "student", -// })); -// const instructorList = (Array.isArray(instructors) ? instructors : []).map((i) => ({ -// ...i, -// currentRole: i.role || "instructor", -// })); - -// setUsers([...studentList, ...instructorList]); -// } catch (err) { -// console.error("Error fetching users:", err); -// } -// }; - -// useEffect(() => { -// const loadData = async () => { -// setLoading(true); -// await Promise.all([fetchRoles(), fetchUsers()]); -// setLoading(false); -// }; -// loadData(); -// }, []); - -// // ΓöÇΓöÇΓöÇ Role CRUD ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const handleCreateRole = async () => { -// if (!roleName.trim()) return alert("Role name is required"); - -// try { -// const method = editingRole ? "PATCH" : "POST"; -// const body = editingRole -// ? { id: editingRole._id, name: roleName, status: roleStatus } -// : { name: roleName, status: roleStatus }; - -// const res = await fetch("/api/roles", { -// method, -// headers: { "Content-Type": "application/json" }, -// body: JSON.stringify(body), -// }); - -// const data = await res.json(); -// if (!res.ok) return alert(data.error || "Failed to save role"); - -// setShowRoleModal(false); -// setEditingRole(null); -// setRoleName(""); -// setRoleStatus("Active"); -// await fetchRoles(); -// } catch (err) { -// alert("Server error saving role"); -// } -// }; - -// const handleDeleteRole = async (id) => { -// if (!confirm("Are you sure you want to delete this role?")) return; -// try { -// const res = await fetch(`/api/roles?id=${id}`, { method: "DELETE" }); -// if (res.ok) await fetchRoles(); -// else alert("Failed to delete role"); -// } catch (err) { -// alert("Server error deleting role"); -// } -// }; - -// const openEditRole = (role) => { -// setEditingRole(role); -// setRoleName(role.name); -// setRoleStatus(role.status); -// setShowRoleModal(true); -// }; - -// // ΓöÇΓöÇΓöÇ Permission toggling ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const handlePermissionToggle = async (roleId, module, action, currentValue) => { -// try { -// const res = await fetch("/api/permissions", { -// method: "PATCH", -// headers: { "Content-Type": "application/json" }, -// body: JSON.stringify({ -// roleId, -// module, -// action, -// value: !currentValue, -// }), -// }); - -// const data = await res.json(); -// if (!res.ok) return alert(data.error || "Failed to update permission"); - -// // Update local state -// setRoles((prev) => -// prev.map((r) => (r._id === roleId ? { ...r, permissions: data.role.permissions } : r)) -// ); - -// // Update selected role if viewing it -// if (selectedRole && selectedRole._id === roleId) { -// setSelectedRole({ ...selectedRole, permissions: data.role.permissions }); -// } -// } catch (err) { -// alert("Server error updating permission"); -// } -// }; - -// // ΓöÇΓöÇΓöÇ User role change ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const handleRoleChange = async (userId, newRole) => { -// try { -// const res = await fetch(`/api/admin/users/${userId}`, { -// method: "PUT", -// headers: { "Content-Type": "application/json" }, -// body: JSON.stringify({ role: newRole }), -// }); - -// const data = await res.json(); -// if (res.ok) { -// setUsers((prev) => -// prev.map((u) => (u._id === userId ? { ...u, currentRole: newRole } : u)) -// ); -// } else { -// alert(data.message || "Failed to update role"); -// } -// } catch (err) { -// alert("Server error updating role"); -// } -// }; - -// const handleDeleteUser = async (userId) => { -// if (!confirm("Are you sure you want to delete this user?")) return; -// try { -// const res = await fetch(`/api/admin/users/${userId}`, { method: "DELETE" }); -// if (res.ok) { -// setUsers((prev) => prev.filter((u) => u._id !== userId)); -// } else { -// const data = await res.json(); -// alert(data.message || "Failed to delete user"); -// } -// } catch (err) { -// alert("Server error deleting user"); -// } -// }; - -// // ΓöÇΓöÇΓöÇ Role color mapping ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const getRoleColor = (roleName) => { -// const n = (roleName || "").toUpperCase(); -// if (n === "ADMIN") return { bg: "#dc3545", light: "#fce4ec", text: "#c62828" }; -// if (n === "INSTRUCTOR") return { bg: "#0d6efd", light: "#e3f2fd", text: "#1565c0" }; -// if (n === "STUDENT") return { bg: "#198754", light: "#e8f5e9", text: "#2e7d32" }; -// return { bg: "#6c757d", light: "#f5f5f5", text: "#616161" }; -// }; - -// // ΓöÇΓöÇΓöÇ Styles ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ -// const styles = { -// container: { padding: "30px" }, -// heading: { fontSize: "28px", fontWeight: "700", marginBottom: "10px" }, -// subHeading: { fontSize: "14px", color: "#888", marginBottom: "25px" }, -// tabs: { display: "flex", gap: "0", marginBottom: "25px", borderBottom: "2px solid #eee" }, -// tab: (active) => ({ -// padding: "12px 24px", -// cursor: "pointer", -// fontWeight: active ? "600" : "400", -// color: active ? "#5b4fbd" : "#666", -// borderBottom: active ? "3px solid #5b4fbd" : "3px solid transparent", -// background: "none", -// border: "none", -// fontSize: "15px", -// transition: "all 0.2s", -// }), -// card: { -// background: "#fff", -// borderRadius: "12px", -// padding: "25px", -// border: "1px solid #eee", -// boxShadow: "0 2px 8px rgba(0,0,0,0.04)", -// }, -// btn: (color) => ({ -// padding: "8px 18px", -// borderRadius: "8px", -// border: "none", -// color: "#fff", -// backgroundColor: color, -// cursor: "pointer", -// fontSize: "13px", -// fontWeight: "500", -// transition: "opacity 0.2s", -// }), -// btnSm: (color) => ({ -// padding: "5px 12px", -// borderRadius: "6px", -// border: "none", -// color: "#fff", -// backgroundColor: color, -// cursor: "pointer", -// fontSize: "12px", -// fontWeight: "500", -// }), -// badge: (color) => ({ -// backgroundColor: color, -// color: "#fff", -// padding: "4px 12px", -// borderRadius: "6px", -// fontSize: "13px", -// fontWeight: "500", -// }), -// toggle: (active, color = "#5b4fbd") => ({ -// width: "42px", -// height: "22px", -// borderRadius: "12px", -// backgroundColor: active ? color : "#ddd", -// position: "relative", -// cursor: "pointer", -// transition: "background-color 0.3s", -// border: "none", -// padding: "0", -// }), -// toggleDot: (active) => ({ -// width: "18px", -// height: "18px", -// borderRadius: "50%", -// backgroundColor: "#fff", -// position: "absolute", -// top: "2px", -// left: active ? "22px" : "2px", -// transition: "left 0.3s", -// boxShadow: "0 1px 3px rgba(0,0,0,0.2)", -// }), -// modal: { -// position: "fixed", -// top: 0, -// left: 0, -// right: 0, -// bottom: 0, -// backgroundColor: "rgba(0,0,0,0.5)", -// display: "flex", -// alignItems: "center", -// justifyContent: "center", -// zIndex: 1000, -// }, -// modalContent: { -// background: "#fff", -// borderRadius: "16px", -// padding: "30px", -// width: "450px", -// maxWidth: "90%", -// boxShadow: "0 10px 40px rgba(0,0,0,0.15)", -// }, -// input: { -// width: "100%", -// padding: "10px 14px", -// borderRadius: "8px", -// border: "1px solid #ddd", -// fontSize: "14px", -// outline: "none", -// marginBottom: "15px", -// }, -// select: { -// width: "100%", -// padding: "10px 14px", -// borderRadius: "8px", -// border: "1px solid #ddd", -// fontSize: "14px", -// outline: "none", -// marginBottom: "15px", -// backgroundColor: "#fff", -// }, -// }; - -// if (loading) { -// return ( -//
-//

Loading...

-//
-// ); -// } - -// return ( -//
-//

Roles & Permissions

-//

Manage roles, assign permissions, and control user access

- -// {/* Tabs */} -//
-// -// -// -//
- -// {/* ΓöÇΓöÇΓöÇ TAB 1: Manage Roles ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -// {activeTab === "roles" && ( -//
-//
-//

All Roles

-// -//
- -// {roles.length === 0 ? ( -//

No roles found. Create your first role.

-// ) : ( -//
-// -// -// -// -// -// -// -// -// -// -// -// -// {roles.map((role, index) => { -// const permCount = role.permissions -// ? Object.values(role.permissions).reduce((sum, mod) => { -// if (typeof mod === 'object') { -// return sum + Object.values(mod).filter(v => v === true).length; -// } -// return sum; -// }, 0) -// : 0; -// const rc = getRoleColor(role.name); - -// return ( -// -// -// -// -// -// -// -// -// ); -// })} -// -//
#Role NameStatusPermissions SetCreatedActions
{index + 1} -// -// {role.name} -// -// -// -// {role.status} -// -// -// {permCount} active -// {new Date(role.createdAt).toLocaleDateString()} -//
-// -// -// -//
-//
-//
-// )} -//
-// )} - -// {/* ΓöÇΓöÇΓöÇ TAB 2: Permissions ΓÇö All Roles Side by Side ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -// {activeTab === "permissions" && ( -//
-// {/* ΓöÇΓöÇ Role filter bar ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -//
-// -// View: -// -// -// {roles.map((r) => { -// const rc = getRoleColor(r.name); -// const isActive = selectedRole?._id === r._id; -// return ( -// -// ); -// })} -//
- -// {/* ΓöÇΓöÇ Permissions comparison table ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -// {(() => { -// const displayRoles = selectedRole -// ? [selectedRole] -// : roles; - -// if (displayRoles.length === 0) { -// return ( -//
-//

-// No roles found. Create roles in the "Manage Roles" tab first. -//

-//
-// ); -// } - -// return ( -//
-//
-//

-// {selectedRole -// ? <>Permissions for: {selectedRole.name} -// : "Permissions Comparison ΓÇö All Roles" -// } -//

-// {!selectedRole && ( -//

-// Compare and manage permissions for Admin and Instructor side by side -//

-// )} -//
- -//
-// -// -// {/* Row 1: Module header + role name groups */} -// -// -// {displayRoles.map((role) => { -// const rc = getRoleColor(role.name); -// return ( -// -// ); -// })} -// -// {/* Row 2: Action sub-headers per role */} -// -// {displayRoles.map((role) => { -// const rc = getRoleColor(role.name); -// return ["View", "Create", "Edit", "Delete"].map((action, i) => ( -// -// )); -// })} -// -// -// -// {Object.entries(modules).map(([moduleName, config]) => ( -// -// -// {displayRoles.map((role) => { -// const rc = getRoleColor(role.name); -// return ["view", "create", "edit", "delete"].map((action, i) => { -// const hasAction = config.actions.includes(action); -// const isEnabled = role.permissions?.[moduleName]?.[action] === true; - -// return ( -// -// ); -// }); -// })} -// -// ))} -// -//
-// Module -// -// -// -// {role.name} -// -//
-// {action} -//
-// {config.label} -// -// {hasAction ? ( -// -// ) : ( -// ΓÇö -// )} -//
-//
- -// {/* ΓöÇΓöÇ Legend ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -//
-// {displayRoles.map((role) => { -// const rc = getRoleColor(role.name); -// const permCount = role.permissions -// ? Object.values(role.permissions).reduce((sum, mod) => { -// if (typeof mod === 'object') { -// return sum + Object.values(mod).filter(v => v === true).length; -// } -// return sum; -// }, 0) -// : 0; -// const totalPossible = Object.values(modules).reduce( -// (sum, config) => sum + config.actions.length, 0 -// ); - -// return ( -//
-// -// -// {role.name} -// -// -// {permCount}/{totalPossible} permissions -// -//
-// ); -// })} -//
-//
-// ); -// })()} -//
-// )} - -// {/* ΓöÇΓöÇΓöÇ TAB 3: User Roles ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -// {activeTab === "users" && ( -//
-//

User Role Assignments

- -// {users.length === 0 ? ( -//

No users found.

-// ) : ( -//
-// -// -// -// -// -// -// -// -// -// -// -// -// {users.map((user, index) => { -// const rc = getRoleColor(user.currentRole); -// return ( -// -// -// -// -// -// -// -// -// ); -// })} -// -//
#NameEmailCurrent RoleChange RoleAction
{index + 1} -// {user.firstName} {user.lastName} -// {user.email} -// -// {user.currentRole} -// -// -// -// -// -//
-//
-// )} -//
-// )} - -// {/* ΓöÇΓöÇΓöÇ Role Modal ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} -// {showRoleModal && ( -//
setShowRoleModal(false)}> -//
e.stopPropagation()}> -//

-// {editingRole ? "Edit Role" : "Create New Role"} -//

- -// -// setRoleName(e.target.value)} -// style={styles.input} -// /> - -// -// - -//
-// -// -//
-//
-//
-// )} -//
-// ); -// } \ No newline at end of file +"use client"; +import { useState, useEffect } from "react"; +import { usePermissions } from "@/lib/PermissionContext"; +import { Trash2, Edit3, ShieldCheck, UserCog, Filter, Plus, Search, MoreHorizontal } from "lucide-react"; + +export default function RolesPermissions() { + const { hasPermission, loading: permLoading } = usePermissions(); + const canView = hasPermission('roles', 'view'); + + const [activeTab, setActiveTab] = useState("users"); + const [roles, setRoles] = useState([]); + const [users, setUsers] = useState([]); + const [modules, setModules] = useState({}); + const [loading, setLoading] = useState(true); + const [showRoleModal, setShowRoleModal] = useState(false); + const [editingRole, setEditingRole] = useState(null); + const [roleName, setRoleName] = useState(""); + const [roleStatus, setRoleStatus] = useState("Active"); + const [selectedRole, setSelectedRole] = useState(null); + + // ΓöÇΓöÇΓöÇ Fetch roles & permission modules ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ + const fetchRoles = async () => { + try { + const res = await fetch("/api/all-roles"); + const data = await res.json(); + setRoles(data.roles || []); + setModules(data.modules || {}); + } catch (err) { + console.error("Error fetching roles:", err); + } + }; + + // ΓöÇΓöÇΓöÇ Fetch all users (students + instructors) ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ + const fetchUsers = async () => { + try { + const [studentRes, instructorRes] = await Promise.all([ + fetch("/api/register"), + fetch("/api/instructor"), + ]); + + const students = await studentRes.json(); + const instructors = await instructorRes.json(); + + const studentList = (Array.isArray(students) ? students : []).map((s) => ({ + ...s, + currentRole: s.role || "student", + })); + const instructorList = (Array.isArray(instructors) ? instructors : []).map((i) => ({ + ...i, + currentRole: i.role || "instructor", + })); + + setUsers([...studentList, ...instructorList]); + } catch (err) { + console.error("Error fetching users:", err); + } + }; + + useEffect(() => { + const loadData = async () => { + setLoading(true); + await Promise.all([fetchRoles(), fetchUsers()]); + setLoading(false); + }; + loadData(); + }, []); + + // ΓöÇΓöÇΓöÇ Role CRUD ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ + const handleCreateRole = async () => { + if (!roleName.trim()) return alert("Role name is required"); + try { + const method = editingRole ? "PATCH" : "POST"; + const body = editingRole + ? { id: editingRole._id, name: roleName, status: roleStatus } + : { name: roleName, status: roleStatus }; + + const res = await fetch("/api/roles", { + method, + headers: { "Content-Type": "application/json" }, + body: JSON.stringify(body), + }); + + if (res.ok) { + setShowRoleModal(false); + setEditingRole(null); + setRoleName(""); + await fetchRoles(); + } + } catch (err) { alert("Error saving role"); } + }; + + const handleDeleteUser = async (userId) => { + if (!confirm("Delete this user?")) return; + try { + const res = await fetch(`/api/admin/users/${userId}`, { method: "DELETE" }); + if (res.ok) setUsers((prev) => prev.filter((u) => u._id !== userId)); + } catch (err) { console.error(err); } + }; + + // ΓöÇΓöÇΓöÇ Permission Toggle ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ + const handlePermissionToggle = async (roleId, module, action, currentValue) => { + try { + const res = await fetch("/api/permissions", { + method: "PATCH", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ roleId, module, action, value: !currentValue }), + }); + const data = await res.json(); + if (res.ok) { + setRoles((prev) => prev.map((r) => (r._id === roleId ? { ...r, permissions: data.role.permissions } : r))); + if (selectedRole?._id === roleId) setSelectedRole({ ...selectedRole, permissions: data.role.permissions }); + } + } catch (err) { console.error(err); } + }; + + const handleRoleChange = async (userId, newRole) => { + try { + const res = await fetch(`/api/admin/users/${userId}`, { + method: "PUT", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ role: newRole }), + }); + if (res.ok) setUsers((prev) => prev.map((u) => u._id === userId ? { ...u, currentRole: newRole } : u)); + } catch (err) { console.error(err); } + }; + + // ΓöÇΓöÇΓöÇ Modern Styles ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ + const styles = { + container: { padding: "40px", backgroundColor: "#fbfcfe", minHeight: "100vh", fontFamily: "'Inter', sans-serif" }, + heading: { fontSize: "24px", fontWeight: "700", color: "#111", marginBottom: "30px" }, + tabs: { display: "flex", gap: "25px", marginBottom: "30px", borderBottom: "1px solid #eee" }, + tab: (active) => ({ + padding: "12px 4px", cursor: "pointer", fontWeight: active ? "600" : "500", + color: active ? "#007bff" : "#888", borderBottom: active ? "2px solid #007bff" : "2px solid transparent", + background: "none", border: "none", fontSize: "14px", transition: "0.2s" + }), + card: { background: "#fff", borderRadius: "12px", border: "1px solid #f0f0f0", boxShadow: "0 4px 20px rgba(0,0,0,0.02)", overflow: "hidden" }, + table: { width: "100%", borderCollapse: "collapse" }, + th: { textAlign: "left", padding: "16px 24px", fontSize: "12px", color: "#999", textTransform: "uppercase", letterSpacing: "1px", borderBottom: "1px solid #f8f8f8", background: "#fafafa" }, + td: { padding: "16px 24px", borderBottom: "1px solid #f8f8f8", fontSize: "14px", color: "#444" }, + badge: (role) => { + const colors = { + ADMIN: { bg: "#eaf3ff", text: "#007bff" }, + EDITOR: { bg: "#e6f9f0", text: "#00b341" }, + INSTRUCTOR: { bg: "#fff4e5", text: "#ff9800" }, + STUDENT: { bg: "#e8f5e9", text: "#2e7d32" } + }; + const pair = colors[role.toUpperCase()] || { bg: "#f3f4f6", text: "#6b7280" }; + return { backgroundColor: pair.bg, color: pair.text, padding: "4px 12px", borderRadius: "6px", fontSize: "12px", fontWeight: "700" }; + }, + toggleBase: (active) => ({ width: "36px", height: "18px", borderRadius: "10px", background: active ? "#007bff" : "#e0e0e0", position: "relative", cursor: "pointer", border: "none", transition: "0.3s" }), + toggleDot: (active) => ({ width: "12px", height: "12px", background: "#fff", borderRadius: "50%", position: "absolute", top: "3px", left: active ? "21px" : "3px", transition: "0.3s" }), + actionBtn: { background: "none", border: "none", cursor: "pointer", color: "#aaa" } + }; + + if (permLoading || loading) return
Loading Access Management...
; + + return ( +
+
+

Ms Edutech Website Access Management UI

+ +
+ + + + + + {/* ΓöÇΓöÇΓöÇ TAB 2: Permissions Matrix (Grid Toggle Style) ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} + {activeTab === "permissions" && ( +
+
+ {roles.map(r => ( + + ))} +
+ {selectedRole && ( + + + + + + + + + + + + {Object.entries(modules).map(([key, config]) => ( + + + {["view", "create", "edit", "delete"].map(act => ( + + ))} + + ))} + +
Module NameViewCreateEditDelete
{config.label} + {config.actions.includes(act) ? ( + + ) : ΓÇö} +
+ )} +
+ )} + + {/* ΓöÇΓöÇΓöÇ TAB 3: Roles Master (CRUD) ΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇΓöÇ */} + {activeTab === "roles" && ( +
+
+

Defined Master Roles

+ +
+ + + + + + + + + + {roles.map(r => ( + + + + + + ))} + +
Role NameStatusActions
{r.name}ΓùÅ {r.status} +
+ { setEditingRole(r); setRoleName(r.name); setShowRoleModal(true); }} /> + +
+
+
+ )} + + {/* ΓöÇΓöÇ Role Modal ΓöÇΓöÇ */} + {showRoleModal && ( +
+
+

{editingRole ? "Update Role" : "Create New Role"}

+ setRoleName(e.target.value)} placeholder="Role Name" style={{ width: '100%', padding: '12px', marginBottom: '20px', borderRadius: '8px', border: '1px solid #ddd' }} /> +
+ + +
+
+
+ )} +
+ ); +} \ No newline at end of file