Realm roles: associated roles tab (#358)
* WIP modal * modal WIP * add modal * place modal in separate file * format * wip implementation * getCompositeRoles with Jeff * add associated roles tab WIP * addComposites function WIP * fix post call * additional roles fetch * big rebase * WIP refresh * resolve conflicts with Erik latest -> fixes role creation * cypress tests, bump react-hook-form to remove console warnings * delete add * refresh with Jeff, update cypress tests, select additional roles tab on add * make dropdownId optional * format * add additionalRolesModal to associated roles tab * add toolbar items * add toolbaritems to associated role tab, matches mock * rebase * add descriptions to alert * add badge * fix badge logic * fix URL when associate roles are deleted, format * update cypress test * format * add associated roles refresh, PR feedback from Erik * add associated roles refresh, PR feedback from Erik * lint
This commit is contained in:
parent
86cf4f8501
commit
a5f08a9202
9 changed files with 211 additions and 58 deletions
|
@ -10,6 +10,7 @@ export type AlertType = {
|
||||||
key: number;
|
key: number;
|
||||||
message: string;
|
message: string;
|
||||||
variant: AlertVariant;
|
variant: AlertVariant;
|
||||||
|
description?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type AlertPanelProps = {
|
type AlertPanelProps = {
|
||||||
|
@ -20,11 +21,11 @@ type AlertPanelProps = {
|
||||||
export function AlertPanel({ alerts, onCloseAlert }: AlertPanelProps) {
|
export function AlertPanel({ alerts, onCloseAlert }: AlertPanelProps) {
|
||||||
return (
|
return (
|
||||||
<AlertGroup isToast>
|
<AlertGroup isToast>
|
||||||
{alerts.map(({ key, variant, message }) => (
|
{alerts.map(({ key, variant, message, description }) => (
|
||||||
|
<>
|
||||||
<Alert
|
<Alert
|
||||||
key={key}
|
key={key}
|
||||||
isLiveRegion
|
isLiveRegion
|
||||||
timeout={true}
|
|
||||||
variant={AlertVariant[variant]}
|
variant={AlertVariant[variant]}
|
||||||
variantLabel=""
|
variantLabel=""
|
||||||
title={message}
|
title={message}
|
||||||
|
@ -34,7 +35,10 @@ export function AlertPanel({ alerts, onCloseAlert }: AlertPanelProps) {
|
||||||
onClose={() => onCloseAlert(key)}
|
onClose={() => onCloseAlert(key)}
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
/>
|
>
|
||||||
|
{description && <p>{description}</p>}
|
||||||
|
</Alert>
|
||||||
|
</>
|
||||||
))}
|
))}
|
||||||
</AlertGroup>
|
</AlertGroup>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,7 +3,11 @@ import { AlertType, AlertPanel } from "./AlertPanel";
|
||||||
import { AlertVariant } from "@patternfly/react-core";
|
import { AlertVariant } from "@patternfly/react-core";
|
||||||
|
|
||||||
type AlertProps = {
|
type AlertProps = {
|
||||||
addAlert: (message: string, variant?: AlertVariant) => void;
|
addAlert: (
|
||||||
|
message: string,
|
||||||
|
variant?: AlertVariant,
|
||||||
|
description?: string
|
||||||
|
) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AlertContext = createContext<AlertProps>({
|
export const AlertContext = createContext<AlertProps>({
|
||||||
|
@ -28,9 +32,10 @@ export const AlertProvider = ({ children }: { children: ReactNode }) => {
|
||||||
|
|
||||||
const addAlert = (
|
const addAlert = (
|
||||||
message: string,
|
message: string,
|
||||||
variant: AlertVariant = AlertVariant.default
|
variant: AlertVariant = AlertVariant.default,
|
||||||
|
description?: string
|
||||||
) => {
|
) => {
|
||||||
setAlerts([...alerts, { key: createId(), message, variant }]);
|
setAlerts([...alerts, { key: createId(), message, variant, description }]);
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
|
@ -25,6 +25,8 @@ import {
|
||||||
export type ViewHeaderProps = {
|
export type ViewHeaderProps = {
|
||||||
titleKey: string;
|
titleKey: string;
|
||||||
badge?: string;
|
badge?: string;
|
||||||
|
badgeId?: string;
|
||||||
|
badgeIsRead?: boolean;
|
||||||
subKey: string;
|
subKey: string;
|
||||||
actionsDropdownId?: string;
|
actionsDropdownId?: string;
|
||||||
subKeyLinkProps?: FormattedLinkProps;
|
subKeyLinkProps?: FormattedLinkProps;
|
||||||
|
@ -39,6 +41,8 @@ export const ViewHeader = ({
|
||||||
actionsDropdownId,
|
actionsDropdownId,
|
||||||
titleKey,
|
titleKey,
|
||||||
badge,
|
badge,
|
||||||
|
badgeId,
|
||||||
|
badgeIsRead,
|
||||||
subKey,
|
subKey,
|
||||||
subKeyLinkProps,
|
subKeyLinkProps,
|
||||||
dropdownItems,
|
dropdownItems,
|
||||||
|
@ -73,7 +77,9 @@ export const ViewHeader = ({
|
||||||
</LevelItem>
|
</LevelItem>
|
||||||
{badge && (
|
{badge && (
|
||||||
<LevelItem>
|
<LevelItem>
|
||||||
<Badge>{badge}</Badge>
|
<Badge id={badgeId} isRead={badgeIsRead}>
|
||||||
|
{badge}
|
||||||
|
</Badge>
|
||||||
</LevelItem>
|
</LevelItem>
|
||||||
)}
|
)}
|
||||||
</Level>
|
</Level>
|
||||||
|
|
|
@ -1,28 +1,36 @@
|
||||||
import React, { useState } from "react";
|
import React, { useState } from "react";
|
||||||
import { Link, useHistory, useRouteMatch } from "react-router-dom";
|
import { useHistory, useParams, useRouteMatch } from "react-router-dom";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import {
|
import {
|
||||||
AlertVariant,
|
AlertVariant,
|
||||||
Button,
|
Button,
|
||||||
ButtonVariant,
|
ButtonVariant,
|
||||||
|
Checkbox,
|
||||||
PageSection,
|
PageSection,
|
||||||
} from "@patternfly/react-core";
|
} from "@patternfly/react-core";
|
||||||
import { IFormatter, IFormatterValueType } from "@patternfly/react-table";
|
|
||||||
|
|
||||||
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
import RoleRepresentation from "keycloak-admin/lib/defs/roleRepresentation";
|
||||||
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
|
||||||
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
|
||||||
import { formattedLinkTableCell } from "../components/external-link/FormattedLink";
|
import { formattedLinkTableCell } from "../components/external-link/FormattedLink";
|
||||||
import { useAlerts } from "../components/alert/Alerts";
|
import { useAlerts } from "../components/alert/Alerts";
|
||||||
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
|
||||||
import { emptyFormatter, toUpperCase } from "../util";
|
import { boolFormatter, emptyFormatter } from "../util";
|
||||||
|
import { AssociatedRolesModal } from "./AssociatedRolesModal";
|
||||||
|
import { useAdminClient } from "../context/auth/AdminClient";
|
||||||
|
import { RoleFormType } from "./RealmRoleTabs";
|
||||||
|
|
||||||
type AssociatedRolesTabProps = {
|
type AssociatedRolesTabProps = {
|
||||||
additionalRoles: RoleRepresentation[];
|
additionalRoles: RoleRepresentation[];
|
||||||
|
addComposites: (newReps: RoleRepresentation[]) => void;
|
||||||
|
parentRole: RoleFormType;
|
||||||
|
onRemove: (newReps: RoleRepresentation[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const AssociatedRolesTab = ({
|
export const AssociatedRolesTab = ({
|
||||||
additionalRoles,
|
additionalRoles,
|
||||||
|
addComposites,
|
||||||
|
parentRole,
|
||||||
|
onRemove,
|
||||||
}: AssociatedRolesTabProps) => {
|
}: AssociatedRolesTabProps) => {
|
||||||
const { t } = useTranslation("roles");
|
const { t } = useTranslation("roles");
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
|
@ -30,7 +38,11 @@ export const AssociatedRolesTab = ({
|
||||||
const { url } = useRouteMatch();
|
const { url } = useRouteMatch();
|
||||||
const tableRefresher = React.useRef<() => void>();
|
const tableRefresher = React.useRef<() => void>();
|
||||||
|
|
||||||
const [selectedRole, setSelectedRole] = useState<RoleRepresentation>();
|
const [selectedRows, setSelectedRows] = useState<RoleRepresentation[]>([]);
|
||||||
|
const [open, setOpen] = useState(false);
|
||||||
|
|
||||||
|
const adminClient = useAdminClient();
|
||||||
|
const { id } = useParams<{ id: string; clientId: string }>();
|
||||||
|
|
||||||
const loader = async () => {
|
const loader = async () => {
|
||||||
return Promise.resolve(additionalRoles);
|
return Promise.resolve(additionalRoles);
|
||||||
|
@ -40,33 +52,47 @@ export const AssociatedRolesTab = ({
|
||||||
tableRefresher.current && tableRefresher.current();
|
tableRefresher.current && tableRefresher.current();
|
||||||
}, [additionalRoles]);
|
}, [additionalRoles]);
|
||||||
|
|
||||||
const RoleDetailLink = (role: RoleRepresentation) => (
|
const RoleName = (role: RoleRepresentation) => <>{role.name}</>;
|
||||||
<>
|
|
||||||
<Link key={role.id} to={`${url}/${role.id}`}>
|
|
||||||
{role.name}
|
|
||||||
</Link>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
|
|
||||||
const boolFormatter = (): IFormatter => (data?: IFormatterValueType) => {
|
const toggleModal = () => setOpen(!open);
|
||||||
const boolVal = data?.toString();
|
|
||||||
|
|
||||||
return (boolVal ? toUpperCase(boolVal) : undefined) as string;
|
|
||||||
};
|
|
||||||
|
|
||||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||||
titleKey: "roles:roleRemoveAssociatedRoleConfirm",
|
titleKey: "roles:roleRemoveAssociatedRoleConfirm",
|
||||||
messageKey: t("roles:roleRemoveAssociatedText", {
|
messageKey: t("roles:roleRemoveAssociatedText"),
|
||||||
selectedRoleName: selectedRole ? selectedRole!.name : "",
|
continueButtonLabel: "common:delete",
|
||||||
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
|
onConfirm: async () => {
|
||||||
|
try {
|
||||||
|
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
|
||||||
|
setSelectedRows([]);
|
||||||
|
|
||||||
|
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
||||||
|
} catch (error) {
|
||||||
|
addAlert(t("roleDeleteError", { error }), AlertVariant.danger);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const [
|
||||||
|
toggleDeleteAssociatedRolesDialog,
|
||||||
|
DeleteAssociatedRolesConfirm,
|
||||||
|
] = useConfirmDialog({
|
||||||
|
titleKey: t("roles:removeAssociatedRoles") + "?",
|
||||||
|
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
|
||||||
|
name: parentRole?.name || t("createRole"),
|
||||||
}),
|
}),
|
||||||
continueButtonLabel: "common:delete",
|
continueButtonLabel: "common:delete",
|
||||||
continueButtonVariant: ButtonVariant.danger,
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
try {
|
try {
|
||||||
// await adminClient.roles.delCompositeRoles({ id: compID }, compies);
|
if (selectedRows.length === additionalRoles.length) {
|
||||||
|
onRemove(selectedRows);
|
||||||
setSelectedRole(undefined);
|
const loc = url.replace(/\/AssociatedRoles/g, "/details");
|
||||||
addAlert(t("roleDeletedSuccess"), AlertVariant.success);
|
history.push(loc);
|
||||||
|
}
|
||||||
|
onRemove(selectedRows);
|
||||||
|
await adminClient.roles.delCompositeRoles({ id }, selectedRows);
|
||||||
|
addAlert(t("associatedRolesRemoved"), AlertVariant.success);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
||||||
}
|
}
|
||||||
|
@ -82,23 +108,54 @@ export const AssociatedRolesTab = ({
|
||||||
<>
|
<>
|
||||||
<PageSection variant="light">
|
<PageSection variant="light">
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
|
<DeleteAssociatedRolesConfirm />
|
||||||
|
<AssociatedRolesModal
|
||||||
|
onConfirm={addComposites}
|
||||||
|
existingCompositeRoles={additionalRoles}
|
||||||
|
open={open}
|
||||||
|
toggleDialog={() => setOpen(!open)}
|
||||||
|
/>
|
||||||
<KeycloakDataTable
|
<KeycloakDataTable
|
||||||
key={selectedRole ? selectedRole.id : "roleList"}
|
|
||||||
loader={loader}
|
loader={loader}
|
||||||
ariaLabelKey="roles:roleList"
|
ariaLabelKey="roles:roleList"
|
||||||
searchPlaceholderKey="roles:searchFor"
|
searchPlaceholderKey="roles:searchFor"
|
||||||
|
canSelectAll
|
||||||
|
onSelect={(rows) => {
|
||||||
|
setSelectedRows([...rows]);
|
||||||
|
}}
|
||||||
isPaginated
|
isPaginated
|
||||||
setRefresher={setRefresher}
|
setRefresher={setRefresher}
|
||||||
toolbarItem={
|
toolbarItem={
|
||||||
<>
|
<>
|
||||||
<Button onClick={goToCreate}>{t("createRole")}</Button>
|
<Checkbox
|
||||||
|
label="Hide inherited roles"
|
||||||
|
key="associated-roles-check"
|
||||||
|
id="kc-hide-inherited-roles-checkbox"
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="kc-add-role-button"
|
||||||
|
key="add-role-button"
|
||||||
|
onClick={() => toggleModal()}
|
||||||
|
>
|
||||||
|
{t("addRole")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
variant="link"
|
||||||
|
isDisabled={selectedRows.length == 0}
|
||||||
|
key="remove-role-button"
|
||||||
|
onClick={() => {
|
||||||
|
toggleDeleteAssociatedRolesDialog();
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{t("removeRoles")}
|
||||||
|
</Button>
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
actions={[
|
actions={[
|
||||||
{
|
{
|
||||||
title: t("common:remove"),
|
title: t("common:remove"),
|
||||||
onRowClick: (role) => {
|
onRowClick: (role) => {
|
||||||
setSelectedRole(role);
|
setSelectedRows([role]);
|
||||||
toggleDeleteDialog();
|
toggleDeleteDialog();
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
@ -107,12 +164,12 @@ export const AssociatedRolesTab = ({
|
||||||
{
|
{
|
||||||
name: "name",
|
name: "name",
|
||||||
displayKey: "roles:roleName",
|
displayKey: "roles:roleName",
|
||||||
cellRenderer: RoleDetailLink,
|
cellRenderer: RoleName,
|
||||||
cellFormatters: [formattedLinkTableCell(), emptyFormatter()],
|
cellFormatters: [formattedLinkTableCell(), emptyFormatter()],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "composite",
|
name: "inherited from",
|
||||||
displayKey: "roles:composite",
|
displayKey: "roles:inheritedFrom",
|
||||||
cellFormatters: [boolFormatter(), emptyFormatter()],
|
cellFormatters: [boolFormatter(), emptyFormatter()],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
@ -213,11 +213,39 @@ export const RealmRoleTabs = () => {
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const [
|
||||||
|
toggleDeleteAllAssociatedRolesDialog,
|
||||||
|
DeleteAllAssociatedRolesConfirm,
|
||||||
|
] = useConfirmDialog({
|
||||||
|
titleKey: t("roles:removeAllAssociatedRoles") + "?",
|
||||||
|
messageKey: t("roles:removeAllAssociatedRolesConfirmDialog", {
|
||||||
|
name: role?.name || t("createRole"),
|
||||||
|
}),
|
||||||
|
continueButtonLabel: "common:delete",
|
||||||
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
|
onConfirm: async () => {
|
||||||
|
try {
|
||||||
|
await adminClient.roles.delCompositeRoles({ id }, additionalRoles);
|
||||||
|
addAlert(
|
||||||
|
t("compositeRoleOff"),
|
||||||
|
AlertVariant.success,
|
||||||
|
t("compositesRemovedAlertDescription")
|
||||||
|
);
|
||||||
|
const loc = url.replace(/\/AssociatedRoles/g, "/details");
|
||||||
|
history.push(loc);
|
||||||
|
refresh();
|
||||||
|
} catch (error) {
|
||||||
|
addAlert(`${t("roleDeleteError")} ${error}`, AlertVariant.danger);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const toggleModal = () => setOpen(!open);
|
const toggleModal = () => setOpen(!open);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<DeleteConfirm />
|
<DeleteConfirm />
|
||||||
|
<DeleteAllAssociatedRolesConfirm />
|
||||||
<AssociatedRolesModal
|
<AssociatedRolesModal
|
||||||
onConfirm={addComposites}
|
onConfirm={addComposites}
|
||||||
existingCompositeRoles={additionalRoles}
|
existingCompositeRoles={additionalRoles}
|
||||||
|
@ -226,10 +254,30 @@ export const RealmRoleTabs = () => {
|
||||||
/>
|
/>
|
||||||
<ViewHeader
|
<ViewHeader
|
||||||
titleKey={role?.name || t("createRole")}
|
titleKey={role?.name || t("createRole")}
|
||||||
|
badge={additionalRoles.length > 0 ? t("composite") : ""}
|
||||||
|
badgeId="composite-role-badge"
|
||||||
|
badgeIsRead={true}
|
||||||
subKey={id ? "" : "roles:roleCreateExplain"}
|
subKey={id ? "" : "roles:roleCreateExplain"}
|
||||||
actionsDropdownId="roles-actions-dropdown"
|
actionsDropdownId="roles-actions-dropdown"
|
||||||
dropdownItems={
|
dropdownItems={
|
||||||
id
|
url.includes("AssociatedRoles")
|
||||||
|
? [
|
||||||
|
<DropdownItem
|
||||||
|
key="delete-all-associated"
|
||||||
|
component="button"
|
||||||
|
onClick={() => toggleDeleteAllAssociatedRolesDialog()}
|
||||||
|
>
|
||||||
|
{t("roles:removeAllAssociatedRoles")}
|
||||||
|
</DropdownItem>,
|
||||||
|
<DropdownItem
|
||||||
|
key="delete-role"
|
||||||
|
component="button"
|
||||||
|
onClick={() => toggleDeleteDialog()}
|
||||||
|
>
|
||||||
|
{t("deleteRole")}
|
||||||
|
</DropdownItem>,
|
||||||
|
]
|
||||||
|
: id
|
||||||
? [
|
? [
|
||||||
<DropdownItem
|
<DropdownItem
|
||||||
key="delete-role"
|
key="delete-role"
|
||||||
|
@ -269,7 +317,12 @@ export const RealmRoleTabs = () => {
|
||||||
eventKey="AssociatedRoles"
|
eventKey="AssociatedRoles"
|
||||||
title={<TabTitleText>{t("associatedRolesText")}</TabTitleText>}
|
title={<TabTitleText>{t("associatedRolesText")}</TabTitleText>}
|
||||||
>
|
>
|
||||||
<AssociatedRolesTab additionalRoles={additionalRoles} />
|
<AssociatedRolesTab
|
||||||
|
additionalRoles={additionalRoles}
|
||||||
|
addComposites={addComposites}
|
||||||
|
parentRole={role!}
|
||||||
|
onRemove={() => refresh()}
|
||||||
|
/>
|
||||||
</Tab>
|
</Tab>
|
||||||
) : null}
|
) : null}
|
||||||
<Tab
|
<Tab
|
||||||
|
|
|
@ -15,6 +15,10 @@
|
||||||
color: var(--pf-c-button--m-plain--Color);
|
color: var(--pf-c-button--m-plain--Color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.kc-add-role-button {
|
||||||
|
margin-left: var(--pf-global--spacer--lg);
|
||||||
|
}
|
||||||
|
|
||||||
.kc-role-attributes__action-group {
|
.kc-role-attributes__action-group {
|
||||||
/* subtract the padding at the bottom of the table from the action group margin */
|
/* subtract the padding at the bottom of the table from the action group margin */
|
||||||
--pf-c-form__group--m-action--MarginTop: calc(
|
--pf-c-form__group--m-action--MarginTop: calc(
|
||||||
|
|
|
@ -19,9 +19,14 @@ type RolesListProps = {
|
||||||
search?: string
|
search?: string
|
||||||
) => Promise<RoleRepresentation[]>;
|
) => Promise<RoleRepresentation[]>;
|
||||||
paginated?: boolean;
|
paginated?: boolean;
|
||||||
|
parentRoleId?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const RolesList = ({ loader, paginated = true }: RolesListProps) => {
|
export const RolesList = ({
|
||||||
|
loader,
|
||||||
|
paginated = true,
|
||||||
|
parentRoleId,
|
||||||
|
}: RolesListProps) => {
|
||||||
const { t } = useTranslation("roles");
|
const { t } = useTranslation("roles");
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const adminClient = useAdminClient();
|
const adminClient = useAdminClient();
|
||||||
|
@ -47,9 +52,15 @@ export const RolesList = ({ loader, paginated = true }: RolesListProps) => {
|
||||||
continueButtonVariant: ButtonVariant.danger,
|
continueButtonVariant: ButtonVariant.danger,
|
||||||
onConfirm: async () => {
|
onConfirm: async () => {
|
||||||
try {
|
try {
|
||||||
|
if (!parentRoleId) {
|
||||||
await adminClient.roles.delById({
|
await adminClient.roles.delById({
|
||||||
id: selectedRole!.id!,
|
id: selectedRole!.id!,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
await adminClient.roles.delCompositeRoles({ id: parentRoleId }, [
|
||||||
|
selectedRole!,
|
||||||
|
]);
|
||||||
|
}
|
||||||
setSelectedRole(undefined);
|
setSelectedRole(undefined);
|
||||||
addAlert(t("roleDeletedSuccess"), AlertVariant.success);
|
addAlert(t("roleDeletedSuccess"), AlertVariant.success);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
"addAssociatedRolesSuccess": "Associated roles have been added",
|
"addAssociatedRolesSuccess": "Associated roles have been added",
|
||||||
"associatedRolesModalTitle": "Add roles to {{name}}",
|
"associatedRolesModalTitle": "Add roles to {{name}}",
|
||||||
"title": "Realm roles",
|
"title": "Realm roles",
|
||||||
|
"addRole": "Add role",
|
||||||
"createRole": "Create role",
|
"createRole": "Create role",
|
||||||
"importRole": "Import role",
|
"importRole": "Import role",
|
||||||
"roleID": "Role ID",
|
"roleID": "Role ID",
|
||||||
|
@ -19,6 +20,7 @@
|
||||||
"composite": "Composite",
|
"composite": "Composite",
|
||||||
"deleteRole": "Delete this role",
|
"deleteRole": "Delete this role",
|
||||||
"details": "Details",
|
"details": "Details",
|
||||||
|
"inheritedFrom": "Inherited from",
|
||||||
"roleList": "Role list",
|
"roleList": "Role list",
|
||||||
"searchFor": "Search role by name",
|
"searchFor": "Search role by name",
|
||||||
"generalSettings": "General Settings",
|
"generalSettings": "General Settings",
|
||||||
|
@ -30,14 +32,21 @@
|
||||||
"roleDeleteConfirm": "Delete role?",
|
"roleDeleteConfirm": "Delete role?",
|
||||||
"roleDeleteConfirmDialog": "This action will permanently delete the role {{selectedRoleName}} and cannot be undone.",
|
"roleDeleteConfirmDialog": "This action will permanently delete the role {{selectedRoleName}} and cannot be undone.",
|
||||||
"roleDeletedSuccess": "The role has been deleted",
|
"roleDeletedSuccess": "The role has been deleted",
|
||||||
"roleDeleteError": "Could not delete role:",
|
"roleDeleteError": "Could not delete role: {{error}}",
|
||||||
"roleSaveSuccess": "The role has been saved",
|
"roleSaveSuccess": "The role has been saved",
|
||||||
"roleSaveError": "Could not save role: {{error}}",
|
"roleSaveError": "Could not save role: {{error}}",
|
||||||
"noRolesInThisRealm": "No roles in this realm",
|
"noRolesInThisRealm": "No roles in this realm",
|
||||||
"noRolesInThisRealmInstructions": "You haven't created any roles in this realm. Create a role to get started.",
|
"noRolesInThisRealmInstructions": "You haven't created any roles in this realm. Create a role to get started.",
|
||||||
"roleAuthentication": "Role authentication",
|
"roleAuthentication": "Role authentication",
|
||||||
|
"removeAllAssociatedRoles": "Remove all associated roles",
|
||||||
|
"removeAssociatedRoles": "Remove associated roles",
|
||||||
|
"removeRoles": "Remove roles",
|
||||||
|
"removeAllAssociatedRolesConfirmDialog": "This action will remove the associated roles of {{name}}. Users who have permission to {{name}} will no longer have access to these roles.",
|
||||||
"roleRemoveAssociatedRoleConfirm": "Remove associated role?",
|
"roleRemoveAssociatedRoleConfirm": "Remove associated role?",
|
||||||
"roleRemoveAssociatedText": "This action will remove {{role}} from {{roleName}. All the associated roles of {{role}} will also be removed."
|
"roleRemoveAssociatedText": "This action will remove {{role}} from {{roleName}. All the associated roles of {{role}} will also be removed.",
|
||||||
|
"compositeRoleOff": "Composite role turned off",
|
||||||
|
"associatedRolesRemoved": "Associated roles have been removed",
|
||||||
|
"compositesRemovedAlertDescription": "All the associated roles have been removed"
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -60,7 +60,7 @@ describe("Realm roles test", function () {
|
||||||
listingPage.itemExist(itemId, false);
|
listingPage.itemExist(itemId, false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Associated roles modal test", function () {
|
it("Associated roles test", function () {
|
||||||
itemId += "_" + (Math.random() + 1).toString(36).substring(7);
|
itemId += "_" + (Math.random() + 1).toString(36).substring(7);
|
||||||
|
|
||||||
// Create
|
// Create
|
||||||
|
@ -79,6 +79,10 @@ describe("Realm roles test", function () {
|
||||||
cy.get('[type="checkbox"]').eq(1).check();
|
cy.get('[type="checkbox"]').eq(1).check();
|
||||||
|
|
||||||
cy.get("#add-associated-roles-button").contains("Add").click();
|
cy.get("#add-associated-roles-button").contains("Add").click();
|
||||||
|
|
||||||
|
cy.url().should("include", "/AssociatedRoles");
|
||||||
|
|
||||||
|
cy.get("#composite-role-badge").should("contain.text", "Composite");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue