diff --git a/ui/actions/roles/index.ts b/ui/actions/roles/index.ts
new file mode 100644
index 0000000000..70310e7da4
--- /dev/null
+++ b/ui/actions/roles/index.ts
@@ -0,0 +1 @@
+export * from "./roles";
diff --git a/ui/actions/roles/roles.ts b/ui/actions/roles/roles.ts
new file mode 100644
index 0000000000..39aac321eb
--- /dev/null
+++ b/ui/actions/roles/roles.ts
@@ -0,0 +1,174 @@
+"use server";
+
+import { revalidatePath } from "next/cache";
+import { redirect } from "next/navigation";
+
+import { auth } from "@/auth.config";
+import { parseStringify } from "@/lib";
+
+export const getRoles = async ({
+ page = 1,
+ query = "",
+ sort = "",
+ filters = {},
+}) => {
+ const session = await auth();
+
+ if (isNaN(Number(page)) || page < 1) redirect("/roles");
+
+ const keyServer = process.env.API_BASE_URL;
+ const url = new URL(`${keyServer}/roles`);
+
+ if (page) url.searchParams.append("page[number]", page.toString());
+ if (query) url.searchParams.append("filter[search]", query);
+ if (sort) url.searchParams.append("sort", sort);
+
+ // Handle multiple filters
+ Object.entries(filters).forEach(([key, value]) => {
+ if (key !== "filter[search]") {
+ url.searchParams.append(key, String(value));
+ }
+ });
+
+ try {
+ const invitations = await fetch(url.toString(), {
+ headers: {
+ Accept: "application/vnd.api+json",
+ Authorization: `Bearer ${session?.accessToken}`,
+ },
+ });
+ const data = await invitations.json();
+ const parsedData = parseStringify(data);
+ revalidatePath("/roles");
+ return parsedData;
+ } catch (error) {
+ // eslint-disable-next-line no-console
+ console.error("Error fetching roles:", error);
+ return undefined;
+ }
+};
+
+// export const sendInvite = async (formData: FormData) => {
+// const session = await auth();
+// const keyServer = process.env.API_BASE_URL;
+
+// const email = formData.get("email");
+// const url = new URL(`${keyServer}/tenants/invitations`);
+
+// const body = JSON.stringify({
+// data: {
+// type: "invitations",
+// attributes: {
+// email,
+// },
+// relationships: {},
+// },
+// });
+
+// try {
+// const response = await fetch(url.toString(), {
+// method: "POST",
+// headers: {
+// "Content-Type": "application/vnd.api+json",
+// Accept: "application/vnd.api+json",
+// Authorization: `Bearer ${session?.accessToken}`,
+// },
+// body,
+// });
+// const data = await response.json();
+
+// return parseStringify(data);
+// } catch (error) {
+// return {
+// error: getErrorMessage(error),
+// };
+// }
+// };
+
+// export const updateInvite = async (formData: FormData) => {
+// const session = await auth();
+// const keyServer = process.env.API_BASE_URL;
+
+// const invitationId = formData.get("invitationId");
+// const invitationEmail = formData.get("invitationEmail");
+// const expiresAt = formData.get("expires_at");
+
+// const url = new URL(`${keyServer}/tenants/invitations/${invitationId}`);
+
+// try {
+// const response = await fetch(url.toString(), {
+// method: "PATCH",
+// headers: {
+// "Content-Type": "application/vnd.api+json",
+// Accept: "application/vnd.api+json",
+// Authorization: `Bearer ${session?.accessToken}`,
+// },
+// body: JSON.stringify({
+// data: {
+// type: "invitations",
+// id: invitationId,
+// attributes: {
+// email: invitationEmail,
+// ...(expiresAt && { expires_at: expiresAt }),
+// },
+// },
+// }),
+// });
+// const data = await response.json();
+// revalidatePath("/invitations");
+// return parseStringify(data);
+// } catch (error) {
+// // eslint-disable-next-line no-console
+// console.error("Error updating invitation:", error);
+// return {
+// error: getErrorMessage(error),
+// };
+// }
+// };
+
+// export const getInvitationInfoById = async (invitationId: string) => {
+// const session = await auth();
+// const keyServer = process.env.API_BASE_URL;
+// const url = new URL(`${keyServer}/tenants/invitations/${invitationId}`);
+
+// try {
+// const response = await fetch(url.toString(), {
+// method: "GET",
+// headers: {
+// Accept: "application/vnd.api+json",
+// Authorization: `Bearer ${session?.accessToken}`,
+// },
+// });
+
+// const data = await response.json();
+// return parseStringify(data);
+// } catch (error) {
+// return {
+// error: getErrorMessage(error),
+// };
+// }
+// };
+
+// export const revokeInvite = async (formData: FormData) => {
+// const session = await auth();
+// const keyServer = process.env.API_BASE_URL;
+
+// const invitationId = formData.get("invitationId");
+// const url = new URL(`${keyServer}/tenants/invitations/${invitationId}`);
+// try {
+// const response = await fetch(url.toString(), {
+// method: "DELETE",
+// headers: {
+// Authorization: `Bearer ${session?.accessToken}`,
+// },
+// });
+// const data = await response.json();
+// await wait(1000);
+// revalidatePath("/invitations");
+// return parseStringify(data);
+// } catch (error) {
+// return {
+// error: getErrorMessage(error),
+// };
+// }
+// };
diff --git a/ui/app/(prowler)/roles/page.tsx b/ui/app/(prowler)/roles/page.tsx
new file mode 100644
index 0000000000..07b77bf7e6
--- /dev/null
+++ b/ui/app/(prowler)/roles/page.tsx
@@ -0,0 +1,64 @@
+import { Spacer } from "@nextui-org/react";
+import { Suspense } from "react";
+
+import { getRoles } from "@/actions/roles";
+import { FilterControls } from "@/components/filters";
+import { filterInvitations } from "@/components/filters/data-filters";
+import { AddRoleButton } from "@/components/roles";
+import { ColumnsRoles } from "@/components/roles/table";
+import { SkeletonTableRoles } from "@/components/roles/table";
+import { Header } from "@/components/ui";
+import { DataTable, DataTableFilterCustom } from "@/components/ui/table";
+import { SearchParamsProps } from "@/types";
+
+export default async function Roles({
+ searchParams,
+}: {
+ searchParams: SearchParamsProps;
+}) {
+ const searchParamsKey = JSON.stringify(searchParams || {});
+
+ return (
+ <>
+
+ {data.name[0].toUpperCase() + data.name.slice(1).toLowerCase()} +
+ ); + }, + }, + { + accessorKey: "users", + header: ({ column }) => ( ++ {count === 0 + ? "No Users" + : `${count} ${count === 1 ? "User" : "Users"}`} +
+ ); + }, + }, + { + accessorKey: "invitations", + header: ({ column }) => ( +{relationships.invitations.meta.count}
+ ); + }, + }, + { + accessorKey: "updated_at", + header: ({ column }) => ( +