diff --git a/src/PageNav.tsx b/src/PageNav.tsx index c8c2e99d18..4ad7558c67 100644 --- a/src/PageNav.tsx +++ b/src/PageNav.tsx @@ -69,7 +69,7 @@ export const PageNav: React.FunctionComponent = () => { {makeNavItem("clients", "clients")} {makeNavItem("clientScopes", "client-scopes")} - {makeNavItem("realmRoles", "realm-roles")} + {makeNavItem("realmRoles", "roles")} {makeNavItem("users", "users")} {makeNavItem("groups", "groups")} {makeNavItem("sessions", "sessions")} diff --git a/src/realm-roles/RealmRolesSection.tsx b/src/realm-roles/RealmRolesSection.tsx index 46b2a40d75..c7729a5217 100644 --- a/src/realm-roles/RealmRolesSection.tsx +++ b/src/realm-roles/RealmRolesSection.tsx @@ -1,46 +1,69 @@ -import React, { useContext } from "react"; +import React, { useContext, useEffect, useState } from "react"; import { useHistory } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Button, PageSection } from "@patternfly/react-core"; +import { Bullseye, Button, PageSection, Spinner } from "@patternfly/react-core"; -import { DataLoader } from "../components/data-loader/DataLoader"; -import { TableToolbar } from "../components/table-toolbar/TableToolbar"; import { HttpClientContext } from "../context/http-service/HttpClientContext"; import { RoleRepresentation } from "../model/role-model"; import { RolesList } from "./RoleList"; import { RealmContext } from "../context/realm-context/RealmContext"; import { ViewHeader } from "../components/view-header/ViewHeader"; +import { PaginatingTableToolbar } from "../components/table-toolbar/PaginatingTableToolbar"; export const RealmRolesSection = () => { + const [max, setMax] = useState(10); + const [first, setFirst] = useState(0); const { t } = useTranslation("roles"); const history = useHistory(); const httpClient = useContext(HttpClientContext)!; + const [roles, setRoles] = useState(); const { realm } = useContext(RealmContext); const loader = async () => { + const params: { [name: string]: string | number } = { first, max }; + const result = await httpClient.doGet( - `/admin/realms/${realm}/roles` + `/admin/realms/${realm}/roles`, + { params: params } ); - return result.data; + setRoles(result.data); }; + useEffect(() => { + loader(); + }, [first, max]); + return ( <> - - - - - } - > - - {(roles) => } - - + + {!roles && ( + + + + )} + {roles && ( + { + setFirst(first); + setMax(max); + }} + toolbarItem={ + <> + + + } + > + + + )} ); diff --git a/src/realm-roles/RoleList.tsx b/src/realm-roles/RoleList.tsx index 7e9b233c4a..2fc4e2b1a7 100644 --- a/src/realm-roles/RoleList.tsx +++ b/src/realm-roles/RoleList.tsx @@ -1,4 +1,4 @@ -import React from "react"; +import React, { useContext, useState } from "react"; import { useTranslation } from "react-i18next"; import { @@ -12,9 +12,15 @@ import { import { ExternalLink } from "../components/external-link/ExternalLink"; import { RoleRepresentation } from "../model/role-model"; +import { AlertVariant, ButtonVariant } from "@patternfly/react-core"; +import { HttpClientContext } from "../context/http-service/HttpClientContext"; +import { useAlerts } from "../components/alert/Alerts"; +import { RealmContext } from "../context/realm-context/RealmContext"; +import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; type RolesListProps = { roles?: RoleRepresentation[]; + refresh: () => void; }; const columns: (keyof RoleRepresentation)[] = [ @@ -23,8 +29,12 @@ const columns: (keyof RoleRepresentation)[] = [ "description", ]; -export const RolesList = ({ roles }: RolesListProps) => { +export const RolesList = ({ roles, refresh }: RolesListProps) => { const { t } = useTranslation("roles"); + const httpClient = useContext(HttpClientContext)!; + const { realm } = useContext(RealmContext); + const { addAlert } = useAlerts(); + const [selectedRowId, setSelectedRowId] = useState(-1); const emptyFormatter = (): IFormatter => (data?: IFormatterValueType) => { return data ? data : "—"; @@ -43,37 +53,66 @@ export const RolesList = ({ roles }: RolesListProps) => { ? boolVal.charAt(0).toUpperCase() + boolVal.slice(1) : undefined) as string; }; - - const data = roles!.map((c) => { - return { cells: columns.map((col) => c[col]) }; + const data = roles!.map((column) => { + return { cells: columns.map((col) => column[col]), role: column }; }); + + let selectedRoleName; + if (selectedRowId === data.length) { + selectedRoleName = data[selectedRowId - 1].role.name; + } else if (selectedRowId != -1) { + selectedRoleName = data[selectedRowId].role.name; + } + + const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ + titleKey: "roles:roleDeleteConfirm", + messageKey: t("roles:roleDeleteConfirmDialog", { selectedRoleName }), + continueButtonLabel: "common:delete", + continueButtonVariant: ButtonVariant.danger, + onConfirm: async () => { + try { + await httpClient.doDelete( + `/admin/realms/${realm}/roles/${data[selectedRowId].role.name}` + ); + refresh(); + addAlert(t("roleDeletedSuccess"), AlertVariant.success); + } catch (error) { + addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger); + } + }, + }); + return ( - - - -
+ <> + + { + setSelectedRowId(rowId); + toggleDeleteDialog(); + }, + }, + ]} + aria-label="Roles list" + > + + +
+ ); }; diff --git a/src/realm-roles/add/NewRoleForm.tsx b/src/realm-roles/add/NewRoleForm.tsx index 9d91b7694e..8e7f391718 100644 --- a/src/realm-roles/add/NewRoleForm.tsx +++ b/src/realm-roles/add/NewRoleForm.tsx @@ -18,7 +18,7 @@ import { import { RoleRepresentation } from "../../model/role-model"; import { HttpClientContext } from "../../context/http-service/HttpClientContext"; import { useAlerts } from "../../components/alert/Alerts"; -import { Controller, useForm } from "react-hook-form"; +import { Controller, useForm, FieldErrors } from "react-hook-form"; import { RealmContext } from "../../context/realm-context/RealmContext"; export const NewRoleForm = () => { @@ -40,6 +40,8 @@ export const NewRoleForm = () => { } }; + console.log(errors); + return ( <> @@ -56,16 +58,18 @@ export const NewRoleForm = () => { type="text" id="kc-role-name" name="name" - ref={register()} + ref={register({ required: true })} /> { render={({ onChange, value }) => (