diff --git a/client/app/globals.css b/client/app/globals.css
index a30a014..800182b 100644
--- a/client/app/globals.css
+++ b/client/app/globals.css
@@ -98,3 +98,7 @@
background: #0F172A; /* Color of the scrollbar handle on hover */
border: 2px solid white; /* Color of the scrollbar handle on hover */
}
+
+nextjs-portal {
+ display: none;
+}
\ No newline at end of file
diff --git a/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx b/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx
index bab6f88..87137ee 100644
--- a/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx
+++ b/client/components/dashboard-componenets/cards/EmptyFillContainer.tsx
@@ -1,6 +1,6 @@
-export default function EmptyFillContainer({children}: {children: React.ReactNode}) {
+export default function EmptyFillContainer({children, className = ""}: {children: React.ReactNode, className?: string}) {
return (
-
{children}
+ {children}
);
}
\ No newline at end of file
diff --git a/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx b/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx
index 8950880..7190efb 100644
--- a/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx
+++ b/client/components/dashboard-componenets/mainContent/systemAdminContents/Roles.tsx
@@ -1,7 +1,9 @@
import { Button } from "@/components/ui/button";
import { Cog, UserRoundCog } from "lucide-react";
import EmptyFillContainer from "../../cards/EmptyFillContainer";
-import { RoleCreateModal } from "@/components/modals/userControls/RoleModal";
+import { RoleCreateModal } from "@/components/modals/userControls/RoleModal";
+import PermissonList from "@/components/lists/PermissonList";
+import { RoleDeleteModal } from "@/components/modals/userControls/RoleDeleteModal";
export default function AdminRolesManagementPanel() {
return (
@@ -11,32 +13,30 @@ export default function AdminRolesManagementPanel() {
-
+
+
+
+
-
-
-
- Roles List
-
-
- Permisson List
-
-
- Role Distribution chart
-
-
- Role Requests
-
-
+
+
+
+ PERMISSON LIST BY ROLE
+
+
+
);
diff --git a/client/components/lists/PermissonList.tsx b/client/components/lists/PermissonList.tsx
new file mode 100644
index 0000000..b318c89
--- /dev/null
+++ b/client/components/lists/PermissonList.tsx
@@ -0,0 +1,197 @@
+"usr client";
+import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group";
+import { Label } from "@/components/ui/label";
+import EmptyFillContainer from "../dashboard-componenets/cards/EmptyFillContainer";
+import { use, useEffect, useState } from "react";
+import useGetAllRole from "@/hooks/user_data/useGetAllRole";
+import { fetchAllPermissons } from "@/hooks/user_data/getAllPermissonList";
+import { set } from "react-hook-form";
+import {
+ AlertDialog,
+ AlertDialogAction,
+ AlertDialogCancel,
+ AlertDialogContent,
+ AlertDialogDescription,
+ AlertDialogFooter,
+ AlertDialogHeader,
+ AlertDialogTitle,
+ AlertDialogTrigger,
+} from "../ui/alert-dialog";
+import { Button } from "../ui/button";
+import { updatePermisson } from "@/hooks/user_data/updatePermisson";
+import { Pointer, SquareMousePointer } from "lucide-react";
+
+type RolesWithPermisson = {
+ id: string;
+ name: string;
+ permissions: [
+ {
+ name: string;
+ description: string;
+ }
+ ];
+};
+
+type Permisson = {
+ name: string;
+ description: string;
+};
+
+type EditPermisson = {
+ role: string;
+ permission: string;
+ action: string;
+};
+
+function PermissonList() {
+ const { fetchAllRoles, rolesWithPermissions } = useGetAllRole();
+ const [roles, setRoles] = useState
([]);
+ const [permissions, setPermissions] = useState([]);
+ const [msg, setMsg] = useState();
+
+ async function getPermissons() {
+ setPermissions(await fetchAllPermissons());
+ }
+
+ useEffect(() => {
+ fetchAllRoles();
+ getPermissons();
+ }, []);
+
+ useEffect(() => {
+ setRoles(rolesWithPermissions);
+ //setSelectedRole(rolesWithPermissions[0]?.name);
+ }, [rolesWithPermissions]);
+
+ const [selectedRole, setSelectedRole] = useState();
+
+ return (
+
+
setSelectedRole(e)}
+ >
+ {roles.map((role) => (
+
+
+
+
+ ))}
+
+
+ {selectedRole ? (
+
+
+ {roles
+ .filter((role) => role.name === selectedRole)
+ .map((role) => (
+
+
+
+ {permissions.map((permission) => (
+
{
+ const str = role.permissions.find(
+ (perm) => perm.name === permission.name
+ )
+ ? "REMOVE"
+ : "ADD";
+ await setMsg({
+ role: role.name,
+ permission: permission.name,
+ action: str,
+ });
+ }}
+ className={
+ "flex flex-col gap-1 my-2 px-6 py-4 bg-green-100 text-card-foreground shadow-md rounded-lg hover:cursor-pointer hover:scale-110 hover:text-white" +
+ (role.permissions.find(
+ (perm) => perm.name === permission.name
+ )
+ ? " bg-green-500"
+ : " bg-red-500")
+ }
+ >
+
+ {permission.name}
+
+
{permission.description}
+
+ ))}
+
+
+
+
+
+
+ CONFIRM THAT YOU WANT TO{" "}
+ {msg?.action === "ADD" ? (
+
+
+ ADD "{msg?.permission}"
+ {" "}
+ PERMISSION TO{" "}
+
+ ) : (
+
+
+ REMOVE "{msg?.permission}"
+ {" "}
+ PERMISSION FROM{" "}
+
+ )}
+
{msg?.role} ?
+
+
+
+
+
+ ACTION : {msg?.action} PERMISSON
+
+
+ ROLE : {msg?.role}
+
+
+ PERMISSION : {msg?.permission}
+
+
+
+
+
+ Cancel
+ {
+ msg && alert(await updatePermisson(msg));
+ await fetchAllRoles();
+ setMsg(undefined);
+ }}
+ >
+ Confirm
+
+
+
+
+ ))}
+
+
+ ) : (
+
Select a role from above to view permissions
+ )}
+
+ );
+}
+export default PermissonList;
diff --git a/client/components/modals/userControls/RoleDeleteModal.tsx b/client/components/modals/userControls/RoleDeleteModal.tsx
new file mode 100644
index 0000000..3b17fc8
--- /dev/null
+++ b/client/components/modals/userControls/RoleDeleteModal.tsx
@@ -0,0 +1,72 @@
+import { Button } from "@/components/ui/button";
+import {
+ Dialog,
+ DialogContent,
+ DialogDescription,
+ DialogFooter,
+ DialogHeader,
+ DialogTitle,
+ DialogTrigger,
+ DialogClose
+} from "@/components/ui/dialog";
+
+import { Input } from "@/components/ui/input";
+import { Label } from "@/components/ui/label";
+import addNewRole from "@/hooks/user_data/addNewRole";
+import deleteRole from "@/hooks/user_data/deleteRole";
+import React , {useState} from "react";
+
+interface DialogWrapperProps {
+ children: React.ReactNode;
+}
+
+export const RoleDeleteModal: React.FC = ({
+ children,
+}) => {
+ const [roleName, setRoleName] = useState("");
+
+ const handleSaveChanges = async () => {
+ await deleteRole(roleName);
+ window.location.reload();
+ };
+
+
+ return (
+
+ );
+};
diff --git a/client/components/modals/userControls/RoleModal.tsx b/client/components/modals/userControls/RoleModal.tsx
index fc9f7d0..cad0dd3 100644
--- a/client/components/modals/userControls/RoleModal.tsx
+++ b/client/components/modals/userControls/RoleModal.tsx
@@ -12,6 +12,7 @@ import {
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
+import addNewRole from "@/hooks/user_data/addNewRole";
import React , {useState} from "react";
interface DialogWrapperProps {
@@ -21,13 +22,11 @@ interface DialogWrapperProps {
export const RoleCreateModal: React.FC = ({
children,
}) => {
- const [description, setDescription] = useState("");
const [roleName, setRoleName] = useState("");
- const handleSaveChanges = () => {
- console.log("Vehicle Number:", description);
- console.log("Vehicle Number:", roleName);
-
+ const handleSaveChanges = async () => {
+ await addNewRole(roleName);
+ window.location.reload();
};
@@ -56,19 +55,7 @@ export const RoleCreateModal: React.FC = ({
value={roleName}
onChange={(e) => setRoleName(e.target.value)}
/>
-
-
-
- setDescription(e.target.value)}
- />
-
+
diff --git a/client/components/ui/radio-group.tsx b/client/components/ui/radio-group.tsx
new file mode 100644
index 0000000..cf01673
--- /dev/null
+++ b/client/components/ui/radio-group.tsx
@@ -0,0 +1,44 @@
+"use client"
+
+import * as React from "react"
+import { CheckIcon } from "@radix-ui/react-icons"
+import * as RadioGroupPrimitive from "@radix-ui/react-radio-group"
+
+import { cn } from "@/lib/utils"
+
+const RadioGroup = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+ )
+})
+RadioGroup.displayName = RadioGroupPrimitive.Root.displayName
+
+const RadioGroupItem = React.forwardRef<
+ React.ElementRef,
+ React.ComponentPropsWithoutRef
+>(({ className, ...props }, ref) => {
+ return (
+
+
+
+
+
+ )
+})
+RadioGroupItem.displayName = RadioGroupPrimitive.Item.displayName
+
+export { RadioGroup, RadioGroupItem }
diff --git a/client/data/apiRoutes.ts b/client/data/apiRoutes.ts
index 6b80a02..9af7026 100644
--- a/client/data/apiRoutes.ts
+++ b/client/data/apiRoutes.ts
@@ -27,10 +27,12 @@ export const apiRoutes = {
edit: `${baseUrl}/landfills/`,
},
rbac: {
- create: `${baseUrl}/rbac/create`,
+ create: `${baseUrl}/rbac/roles`,
getByRole: `${baseUrl}/rbac/roles/get/`,
- delete: `${baseUrl}/rbac/`,
- edit: `${baseUrl}/rbac/`,
+ delete: `${baseUrl}/rbac/roles/delete/`,
+ edit: `${baseUrl}/rbac/roles/`,
+ getAllRolesWithPermisson: `${baseUrl}/rbac/all`,
+ fetchAllPermissons: `${baseUrl}/rbac/permissions`,
},
vehicle: {
create: `${baseUrl}/vehicles/create`,
diff --git a/client/hooks/user_data/addNewRole.ts b/client/hooks/user_data/addNewRole.ts
new file mode 100644
index 0000000..bcab66b
--- /dev/null
+++ b/client/hooks/user_data/addNewRole.ts
@@ -0,0 +1,27 @@
+"use client";
+import { UserData } from "@/components/graphs/Data";
+import { apiRoutes } from "@/data/apiRoutes";
+import { jwtToken } from "@/data/cookieNames";
+import { admin, landfillManager, stsManager, unassigned } from "@/data/roles";
+import { getCookie } from "@/lib/cookieFunctions";
+import axios from "axios";
+import { useState, useEffect, use } from "react";
+
+
+export default async function addNewRole(roleName: string) {
+
+ if(!roleName) return "Enter a valid role name.";
+ try {
+ const res = await axios.post(apiRoutes.rbac.create, { roleName },{
+ headers: {
+ Authorization: `Bearer ${await getCookie(jwtToken)}`,
+ },
+ });
+
+ if(res) return "Role added successfully";
+ } catch (error: any) {
+ alert("Error creating role... Are you authorized?");
+ console.log(error.message);
+ }
+
+}
diff --git a/client/hooks/user_data/deleteRole.ts b/client/hooks/user_data/deleteRole.ts
new file mode 100644
index 0000000..4619a8c
--- /dev/null
+++ b/client/hooks/user_data/deleteRole.ts
@@ -0,0 +1,27 @@
+"use client";
+import { UserData } from "@/components/graphs/Data";
+import { apiRoutes } from "@/data/apiRoutes";
+import { jwtToken } from "@/data/cookieNames";
+import { admin, landfillManager, stsManager, unassigned } from "@/data/roles";
+import { getCookie } from "@/lib/cookieFunctions";
+import axios from "axios";
+import { useState, useEffect, use } from "react";
+
+
+export default async function deleteRole(roleName: string) {
+
+ if(!roleName) return "Enter a valid role name.";
+ try {
+ const res = await axios.delete(apiRoutes.rbac.delete + roleName ,{
+ headers: {
+ Authorization: `Bearer ${await getCookie(jwtToken)}`,
+ },
+ });
+
+ if(res) return "Role added successfully";
+ } catch (error: any) {
+ alert("Error creating role... Are you authorized?");
+ console.log(error.message);
+ }
+
+}
diff --git a/client/hooks/user_data/getAllPermissonList.ts b/client/hooks/user_data/getAllPermissonList.ts
new file mode 100644
index 0000000..9bf5a50
--- /dev/null
+++ b/client/hooks/user_data/getAllPermissonList.ts
@@ -0,0 +1,26 @@
+import { apiRoutes } from "@/data/apiRoutes";
+import { jwtToken } from "@/data/cookieNames";
+import { getCookie } from "@/lib/cookieFunctions";
+import axios from "axios";
+
+export async function fetchAllPermissons() {
+ try {
+ const token = getCookie(jwtToken);
+ const response = await axios.get(apiRoutes.rbac.fetchAllPermissons, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
+
+ //console.log(response.data);
+ return response.data.map((permisson: any) => {
+ return {
+ name: permisson.name,
+ description: permisson.description,
+ };
+ });
+ } catch (error: any) {
+ alert("Error fetching permissons... Are you authorized?");
+ console.log(error.message);
+ }
+}
diff --git a/client/hooks/user_data/updatePermisson.tsx b/client/hooks/user_data/updatePermisson.tsx
new file mode 100644
index 0000000..d155dcf
--- /dev/null
+++ b/client/hooks/user_data/updatePermisson.tsx
@@ -0,0 +1,46 @@
+import { apiRoutes } from "@/data/apiRoutes";
+import { jwtToken } from "@/data/cookieNames";
+import { getCookie } from "@/lib/cookieFunctions";
+import axios from "axios";
+
+type EditPermisson = {
+ role: string;
+ permission: string;
+ action: string;
+};
+
+export async function updatePermisson(payload: EditPermisson) {
+ try {
+ const token = getCookie(jwtToken);
+ let response;
+ payload.action === "ADD"
+ ? (response = await axios.post(
+ apiRoutes.rbac.edit +
+ payload.role +
+ "/permissions/" +
+ payload.permission,
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ ))
+ : (response = await axios.delete(
+ apiRoutes.rbac.delete +
+ payload.role +
+ "/permissions/" +
+ payload.permission,
+ {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ }
+ ));
+
+ //console.log(response.data);
+ return "Permisson updated successfully!";
+ } catch (error: any) {
+ alert("Error updating permissons... Are you authorized?");
+ console.log(error.message);
+ }
+}
diff --git a/client/hooks/user_data/useGetAllRole.tsx b/client/hooks/user_data/useGetAllRole.tsx
index 933d946..c6431f7 100644
--- a/client/hooks/user_data/useGetAllRole.tsx
+++ b/client/hooks/user_data/useGetAllRole.tsx
@@ -5,20 +5,42 @@ import { admin, landfillManager, stsManager, unassigned } from "@/data/roles";
import { getCookie } from "@/lib/cookieFunctions";
import axios from "axios";
import { useState, useEffect, use } from "react";
+import { set } from "react-hook-form";
-type User = {
+type RolesWithPermisson = {
id: string;
- username: string;
- email: string;
- role: string;
+ name: string;
+ permissions: [{
+ name: string;
+ description: string;
+}]
};
export default function useGetAllRole() {
const [roles, setRoles] = useState([]);
+ const [rolesWithPermissions, setRolesWithPermissions] = useState([]);
async function fetchAllRoles() {
try {
- await setRoles([unassigned, admin, landfillManager, stsManager]);
+ const token = getCookie(jwtToken);
+ const response = await axios.get(apiRoutes.rbac.getAllRolesWithPermisson, {
+ headers: {
+ Authorization: `Bearer ${token}`,
+ },
+ });
+ setRolesWithPermissions(response.data.map((role: RolesWithPermisson) => {
+ return {
+ id: role.id,
+ name: role.name,
+ permissions: role.permissions.map((permission: any) => {
+ return {
+ name: permission.name,
+ description: permission.description,
+ };
+ },
+ )};
+ }));
+ await setRoles([unassigned, admin, landfillManager, stsManager]);
console.log(roles);
} catch (error: any) {
alert("Error fetching roles... Are you authorized?");
@@ -30,5 +52,5 @@ export default function useGetAllRole() {
fetchAllRoles();
}, []);
- return {fetchAllRoles, roles};
+ return {fetchAllRoles, roles, rolesWithPermissions};
}
diff --git a/client/package-lock.json b/client/package-lock.json
index 16d7b9b..cedf5da 100644
--- a/client/package-lock.json
+++ b/client/package-lock.json
@@ -19,6 +19,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
@@ -2513,6 +2514,38 @@
}
}
},
+ "node_modules/@radix-ui/react-radio-group": {
+ "version": "1.1.3",
+ "resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.1.3.tgz",
+ "integrity": "sha512-x+yELayyefNeKeTx4fjK6j99Fs6c4qKm3aY38G3swQVTN6xMpsrbigC0uHs2L//g8q4qR7qOcww8430jJmi2ag==",
+ "dependencies": {
+ "@babel/runtime": "^7.13.10",
+ "@radix-ui/primitive": "1.0.1",
+ "@radix-ui/react-compose-refs": "1.0.1",
+ "@radix-ui/react-context": "1.0.1",
+ "@radix-ui/react-direction": "1.0.1",
+ "@radix-ui/react-presence": "1.0.1",
+ "@radix-ui/react-primitive": "1.0.3",
+ "@radix-ui/react-roving-focus": "1.0.4",
+ "@radix-ui/react-use-controllable-state": "1.0.1",
+ "@radix-ui/react-use-previous": "1.0.1",
+ "@radix-ui/react-use-size": "1.0.1"
+ },
+ "peerDependencies": {
+ "@types/react": "*",
+ "@types/react-dom": "*",
+ "react": "^16.8 || ^17.0 || ^18.0",
+ "react-dom": "^16.8 || ^17.0 || ^18.0"
+ },
+ "peerDependenciesMeta": {
+ "@types/react": {
+ "optional": true
+ },
+ "@types/react-dom": {
+ "optional": true
+ }
+ }
+ },
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz",
diff --git a/client/package.json b/client/package.json
index 8725d7f..5a99c45 100644
--- a/client/package.json
+++ b/client/package.json
@@ -20,6 +20,7 @@
"@radix-ui/react-dropdown-menu": "^2.0.6",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
+ "@radix-ui/react-radio-group": "^1.1.3",
"@radix-ui/react-select": "^2.0.0",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-toast": "^1.1.5",
diff --git a/server/src/controllers/rbac.ts b/server/src/controllers/rbac.ts
index 53acc4e..a5d3612 100644
--- a/server/src/controllers/rbac.ts
+++ b/server/src/controllers/rbac.ts
@@ -7,11 +7,15 @@ const prisma = new PrismaClient();
const getAllRoles = errorWrapper(
async (req: Request, res: Response) => {
- const roles = await prisma.role.findMany({});
+ const roles = await prisma.role.findMany({
+ include: {
+ User: true,
+ },
+ });
- const roleNames = roles.map((role) => role.name);
+ // const roleNames = roles.map((role) => role.name);
- res.json(roleNames);
+ res.json(roles);
},
{ statusCode: 500, message: "Couldn't fetch roles" }
);
@@ -59,7 +63,7 @@ const getPermissions = errorWrapper(
async (req: Request, res: Response) => {
const permissions = await prisma.permission.findMany({});
- const permissionNames = permissions.map((permission) => permission.name);
+ const permissionNames = permissions;
res.json(permissionNames);
},