Split out role form for realm and client in seperate components (#4141)

This commit is contained in:
Jon Koops 2023-01-09 13:57:40 +01:00 committed by GitHub
parent 84da38789d
commit b4fef326de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 287 additions and 223 deletions

View file

@ -243,13 +243,6 @@ describe("Realm roles test", () => {
createRealmRolePage.checkDescription(updateDescription); createRealmRolePage.checkDescription(updateDescription);
}); });
it("should revert realm role", () => {
listingPage.itemExist(editRoleName).goToItemDetails(editRoleName);
createRealmRolePage.checkDescription(updateDescription);
createRealmRolePage.updateDescription("going to revert").cancel();
createRealmRolePage.checkDescription(updateDescription);
});
const keyValue = new KeyValueInput("attributes"); const keyValue = new KeyValueInput("attributes");
it("should add attribute", () => { it("should add attribute", () => {
listingPage.itemExist(editRoleName).goToItemDetails(editRoleName); listingPage.itemExist(editRoleName).goToItemDetails(editRoleName);

View file

@ -93,9 +93,8 @@ export default class ProviderPage {
private mappersTab = "ldap-mappers-tab"; private mappersTab = "ldap-mappers-tab";
private rolesTab = "rolesTab"; private rolesTab = "rolesTab";
private createRoleBtn = "no-roles-for-this-client-empty-action"; private createRoleBtn = "no-roles-for-this-client-empty-action";
private realmRolesSaveBtn = "realm-roles-save-button"; private roleSaveBtn = "save";
private roleNameField = "#kc-name"; private roleNameField = "#kc-name";
private clientIdSelect = "#client\\.id-select-typeahead";
private groupName = "aa-uf-mappers-group"; private groupName = "aa-uf-mappers-group";
private clientName = "aa-uf-mappers-client"; private clientName = "aa-uf-mappers-client";
@ -313,7 +312,7 @@ export default class ProviderPage {
cy.wait(1000); cy.wait(1000);
cy.get(this.roleNameField).clear().type(roleName); cy.get(this.roleNameField).clear().type(roleName);
cy.wait(1000); cy.wait(1000);
cy.findByTestId(this.realmRolesSaveBtn).click(); cy.findByTestId(this.roleSaveBtn).click();
cy.wait(1000); cy.wait(1000);
} }

View file

@ -1,8 +1,8 @@
class CreateRealmRolePage { class CreateRealmRolePage {
private realmRoleNameInput = "#kc-name"; private realmRoleNameInput = "#kc-name";
private realmRoleNameError = "#kc-name-helper"; private realmRoleNameError = "#kc-name-helper";
private realmRoleDescriptionInput = "#kc-role-description"; private realmRoleDescriptionInput = "#kc-description";
private saveBtn = "realm-roles-save-button"; private saveBtn = "save";
private cancelBtn = "cancel"; private cancelBtn = "cancel";
//#region General Settings //#region General Settings

View file

@ -0,0 +1,70 @@
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { AlertVariant } from "@patternfly/react-core";
import { SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom-v5-compat";
import { useAlerts } from "../../components/alert/Alerts";
import { AttributeForm } from "../../components/key-value-form/AttributeForm";
import { RoleForm } from "../../components/role-form/RoleForm";
import { useAdminClient } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { toClientRole } from "../../realm-roles/routes/ClientRole";
import { toClient } from "../routes/Client";
import { NewRoleParams } from "../routes/NewRole";
export default function CreateClientRole() {
const { t } = useTranslation("roles");
const form = useForm<AttributeForm>({ mode: "onChange" });
const navigate = useNavigate();
const { clientId } = useParams<NewRoleParams>();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { addAlert, addError } = useAlerts();
const onSubmit: SubmitHandler<AttributeForm> = async (formValues) => {
const role: RoleRepresentation = {
...formValues,
name: formValues.name?.trim(),
attributes: {},
};
try {
await adminClient.clients.createRole({
id: clientId,
...role,
});
const createdRole = await adminClient.clients.findRole({
id: clientId!,
roleName: role.name!,
});
addAlert(t("roleCreated"), AlertVariant.success);
navigate(
toClientRole({
realm,
clientId: clientId!,
id: createdRole.id!,
tab: "details",
})
);
} catch (error) {
addError("roles:roleCreateError", error);
}
};
return (
<RoleForm
form={form}
onSubmit={onSubmit}
cancelLink={toClient({
realm,
clientId: clientId!,
tab: "roles",
})}
role="manage-clients"
editMode={false}
/>
);
}

View file

@ -12,6 +12,7 @@ import {
ResourceDetailsRoute, ResourceDetailsRoute,
ResourceDetailsWithResourceIdRoute, ResourceDetailsWithResourceIdRoute,
} from "./routes/Resource"; } from "./routes/Resource";
import { NewRoleRoute } from "./routes/NewRole";
import { NewScopeRoute } from "./routes/NewScope"; import { NewScopeRoute } from "./routes/NewScope";
import { import {
ScopeDetailsRoute, ScopeDetailsRoute,
@ -44,6 +45,7 @@ const routes: RouteDef[] = [
NewResourceRoute, NewResourceRoute,
ResourceDetailsRoute, ResourceDetailsRoute,
ResourceDetailsWithResourceIdRoute, ResourceDetailsWithResourceIdRoute,
NewRoleRoute,
NewScopeRoute, NewScopeRoute,
ScopeDetailsRoute, ScopeDetailsRoute,
ScopeDetailsWithScopeIdRoute, ScopeDetailsWithScopeIdRoute,

View file

@ -0,0 +1,17 @@
import { lazy } from "react";
import type { Path } from "react-router-dom-v5-compat";
import { generatePath } from "react-router-dom-v5-compat";
import type { RouteDef } from "../../route-config";
export type NewRoleParams = { realm: string; clientId: string };
export const NewRoleRoute: RouteDef = {
path: "/:realm/clients/:clientId/roles/new",
component: lazy(() => import("../roles/CreateClientRole")),
breadcrumb: (t) => t("roles:createRole"),
access: "manage-clients",
};
export const toCreateRole = (params: NewRoleParams): Partial<Path> => ({
pathname: generatePath(NewRoleRoute.path, params),
});

View file

@ -5,32 +5,32 @@ import {
PageSection, PageSection,
ValidatedOptions, ValidatedOptions,
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import type { SubmitHandler, UseFormMethods } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import type { UseFormMethods } from "react-hook-form"; import { Link, To } from "react-router-dom-v5-compat";
import { ViewHeader } from "../components/view-header/ViewHeader";
import { FormAccess } from "../components/form-access/FormAccess";
import type { AttributeForm } from "../components/key-value-form/AttributeForm";
import { KeycloakTextInput } from "../components/keycloak-text-input/KeycloakTextInput";
import { KeycloakTextArea } from "../components/keycloak-text-area/KeycloakTextArea";
import { useRealm } from "../context/realm-context/RealmContext";
import { useNavigate } from "react-router-dom-v5-compat";
export type RealmRoleFormProps = { import { FormAccess } from "../form-access/FormAccess";
import type { AttributeForm } from "../key-value-form/AttributeForm";
import { KeycloakTextArea } from "../keycloak-text-area/KeycloakTextArea";
import { KeycloakTextInput } from "../keycloak-text-input/KeycloakTextInput";
import { ViewHeader } from "../view-header/ViewHeader";
export type RoleFormProps = {
form: UseFormMethods<AttributeForm>; form: UseFormMethods<AttributeForm>;
save: () => void; onSubmit: SubmitHandler<AttributeForm>;
cancelLink: To;
role: "manage-realm" | "manage-clients";
editMode: boolean; editMode: boolean;
reset: () => void;
}; };
export const RealmRoleForm = ({ export const RoleForm = ({
form: { handleSubmit, errors, register, getValues }, form: { handleSubmit, errors, register, getValues },
save, onSubmit,
cancelLink,
role,
editMode, editMode,
reset, }: RoleFormProps) => {
}: RealmRoleFormProps) => {
const { t } = useTranslation("roles"); const { t } = useTranslation("roles");
const navigate = useNavigate();
const { realm: realmName } = useRealm();
return ( return (
<> <>
@ -38,26 +38,27 @@ export const RealmRoleForm = ({
<PageSection variant="light"> <PageSection variant="light">
<FormAccess <FormAccess
isHorizontal isHorizontal
onSubmit={handleSubmit(save)} onSubmit={handleSubmit(onSubmit)}
role="manage-realm" role={role}
className="pf-u-mt-lg" className="pf-u-mt-lg"
> >
<FormGroup <FormGroup
label={t("roleName")} label={t("roleName")}
fieldId="kc-name" fieldId="kc-name"
isRequired validated={
validated={errors.name ? "error" : "default"} errors.name ? ValidatedOptions.error : ValidatedOptions.default
}
helperTextInvalid={t("common:required")} helperTextInvalid={t("common:required")}
isRequired={!editMode}
> >
<KeycloakTextInput <KeycloakTextInput
id="kc-name"
name="name"
ref={register({ ref={register({
required: !editMode, required: !editMode,
validate: (value: string) => validate: (value: string) =>
!!value.trim() || t("common:required").toString(), !!value.trim() || t("common:required").toString(),
})} })}
type="text"
id="kc-name"
name="name"
isReadOnly={editMode} isReadOnly={editMode}
/> />
</FormGroup> </FormGroup>
@ -72,40 +73,32 @@ export const RealmRoleForm = ({
helperTextInvalid={errors.description?.message} helperTextInvalid={errors.description?.message}
> >
<KeycloakTextArea <KeycloakTextArea
id="kc-description"
name="description" name="description"
aria-label="description"
isDisabled={getValues().name?.includes("default-roles")}
ref={register({ ref={register({
maxLength: { maxLength: {
value: 255, value: 255,
message: t("common:maxLength", { length: 255 }), message: t("common:maxLength", { length: 255 }),
}, },
})} })}
type="text"
validated={ validated={
errors.description errors.description
? ValidatedOptions.error ? ValidatedOptions.error
: ValidatedOptions.default : ValidatedOptions.default
} }
id="kc-role-description" isDisabled={getValues().name?.includes("default-roles")}
/> />
</FormGroup> </FormGroup>
<ActionGroup> <ActionGroup>
<Button <Button data-testid="save" type="submit" variant="primary">
variant="primary"
onClick={save}
data-testid="realm-roles-save-button"
>
{t("common:save")} {t("common:save")}
</Button> </Button>
<Button <Button
data-testid="cancel" data-testid="cancel"
onClick={() =>
editMode ? reset() : navigate(`/${realmName}/roles`)
}
variant="link" variant="link"
component={(props) => <Link {...props} to={cancelLink} />}
> >
{editMode ? t("common:revert") : t("common:cancel")} {t("common:cancel")}
</Button> </Button>
</ActionGroup> </ActionGroup>
</FormAccess> </FormAccess>

View file

@ -0,0 +1,57 @@
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { AlertVariant } from "@patternfly/react-core";
import { SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom-v5-compat";
import { useAlerts } from "../components/alert/Alerts";
import { AttributeForm } from "../components/key-value-form/AttributeForm";
import { RoleForm } from "../components/role-form/RoleForm";
import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { toRealmRole } from "./routes/RealmRole";
import { toRealmRoles } from "./routes/RealmRoles";
export default function CreateRealmRole() {
const { t } = useTranslation("roles");
const form = useForm<AttributeForm>({ mode: "onChange" });
const navigate = useNavigate();
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { addAlert, addError } = useAlerts();
const onSubmit: SubmitHandler<AttributeForm> = async (formValues) => {
const role: RoleRepresentation = {
...formValues,
name: formValues.name?.trim(),
attributes: {},
};
try {
await adminClient.roles.create(role);
const createdRole = await adminClient.roles.findOneByName({
name: formValues.name!,
});
if (!createdRole) {
throw new Error(t("common:notFound"));
}
addAlert(t("roleCreated"), AlertVariant.success);
navigate(toRealmRole({ realm, id: createdRole.id!, tab: "details" }));
} catch (error) {
addError("roles:roleCreateError", error);
}
};
return (
<RoleForm
form={form}
onSubmit={onSubmit}
cancelLink={toRealmRoles({ realm })}
role="manage-realm"
editMode={false}
/>
);
}

View file

@ -10,11 +10,12 @@ import {
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { omit } from "lodash-es"; import { omit } from "lodash-es";
import { useState } from "react"; import { useState } from "react";
import { useForm } from "react-hook-form"; import { SubmitHandler, useForm } from "react-hook-form";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useRouteMatch } from "react-router-dom"; import { useRouteMatch } from "react-router-dom";
import { useNavigate } from "react-router-dom-v5-compat"; import { useNavigate } from "react-router-dom-v5-compat";
import { toClient } from "../clients/routes/Client";
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 { import {
@ -28,6 +29,7 @@ import {
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs"; import { KeycloakTabs } from "../components/keycloak-tabs/KeycloakTabs";
import { PermissionsTab } from "../components/permission-tab/PermissionTab"; import { PermissionsTab } from "../components/permission-tab/PermissionTab";
import { RoleForm } from "../components/role-form/RoleForm";
import { AddRoleMappingModal } from "../components/role-mapping/AddRoleMappingModal"; import { AddRoleMappingModal } from "../components/role-mapping/AddRoleMappingModal";
import { RoleMapping } from "../components/role-mapping/RoleMapping"; import { RoleMapping } from "../components/role-mapping/RoleMapping";
import { ViewHeader } from "../components/view-header/ViewHeader"; import { ViewHeader } from "../components/view-header/ViewHeader";
@ -35,13 +37,13 @@ import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext"; import { useRealm } from "../context/realm-context/RealmContext";
import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useParams } from "../utils/useParams"; import { useParams } from "../utils/useParams";
import { RealmRoleForm } from "./RealmRoleForm";
import { import {
ClientRoleParams, ClientRoleParams,
ClientRoleRoute, ClientRoleRoute,
toClientRole, toClientRole,
} from "./routes/ClientRole"; } from "./routes/ClientRole";
import { toRealmRole } from "./routes/RealmRole"; import { toRealmRole } from "./routes/RealmRole";
import { toRealmRoles } from "./routes/RealmRoles";
import { UsersInRoleTab } from "./UsersInRoleTab"; import { UsersInRoleTab } from "./UsersInRoleTab";
export default function RealmRoleTabs() { export default function RealmRoleTabs() {
@ -49,7 +51,7 @@ export default function RealmRoleTabs() {
const form = useForm<AttributeForm>({ const form = useForm<AttributeForm>({
mode: "onChange", mode: "onChange",
}); });
const { setValue, getValues, trigger, reset } = form; const { setValue, reset } = form;
const navigate = useNavigate(); const navigate = useNavigate();
const { adminClient } = useAdminClient(); const { adminClient } = useAdminClient();
@ -84,52 +86,47 @@ export default function RealmRoleTabs() {
useFetch( useFetch(
async () => { async () => {
const realm = await adminClient.realms.findOne({ realm: realmName }); const [realm, role] = await Promise.all([
if (!id) { adminClient.realms.findOne({ realm: realmName }),
return { realm }; adminClient.roles.findOneById({ id }),
} ]);
const role = await adminClient.roles.findOneById({ id });
return { realm, role }; return { realm, role };
}, },
({ realm, role }) => { ({ realm, role }) => {
if (!realm || (!role && id)) { if (!realm || !role) {
throw new Error(t("common:notFound")); throw new Error(t("common:notFound"));
} }
setRealm(realm);
if (role) {
const convertedRole = convert(role); const convertedRole = convert(role);
setRealm(realm);
setRole(convertedRole); setRole(convertedRole);
Object.entries(convertedRole).map((entry) => { Object.entries(convertedRole).map((entry) => {
setValue(entry[0], entry[1]); setValue(entry[0], entry[1]);
}); });
}
}, },
[key, url] [key, url]
); );
const save = async () => { const onSubmit: SubmitHandler<AttributeForm> = async (formValues) => {
try { try {
const values = getValues();
if ( if (
values.attributes && formValues.attributes &&
values.attributes[values.attributes.length - 1]?.key === "" formValues.attributes[formValues.attributes.length - 1]?.key === ""
) { ) {
setValue( setValue(
"attributes", "attributes",
values.attributes.slice(0, values.attributes.length - 1) formValues.attributes.slice(0, formValues.attributes.length - 1)
); );
} }
if (!(await trigger())) {
return; const { attributes, ...rest } = formValues;
}
const { attributes, ...rest } = values;
let roleRepresentation: RoleRepresentation = rest; let roleRepresentation: RoleRepresentation = rest;
roleRepresentation.name = roleRepresentation.name?.trim(); roleRepresentation.name = roleRepresentation.name?.trim();
if (id) {
if (attributes) { if (attributes) {
roleRepresentation.attributes = keyValueToArray(attributes); roleRepresentation.attributes = keyValueToArray(attributes);
} }
@ -141,47 +138,15 @@ export default function RealmRoleTabs() {
await adminClient.roles.updateById({ id }, roleRepresentation); await adminClient.roles.updateById({ id }, roleRepresentation);
} else { } else {
await adminClient.clients.updateRole( await adminClient.clients.updateRole(
{ id: clientId, roleName: values.name! }, { id: clientId, roleName: formValues.name! },
roleRepresentation roleRepresentation
); );
} }
setRole(convert(roleRepresentation)); setRole(convert(roleRepresentation));
} else { addAlert(t("roleSaveSuccess"), AlertVariant.success);
let createdRole;
if (!clientId) {
await adminClient.roles.create(roleRepresentation);
createdRole = await adminClient.roles.findOneByName({
name: values.name!,
});
} else {
await adminClient.clients.createRole({
id: clientId,
name: values.name,
});
if (values.description) {
await adminClient.clients.updateRole(
{ id: clientId, roleName: values.name! },
roleRepresentation
);
}
createdRole = await adminClient.clients.findRole({
id: clientId,
roleName: values.name!,
});
}
if (!createdRole) {
throw new Error(t("common:notFound"));
}
setRole(convert(createdRole));
navigate(
url.substr(0, url.lastIndexOf("/") + 1) + createdRole.id + "/details"
);
}
addAlert(t(id ? "roleSaveSuccess" : "roleCreated"), AlertVariant.success);
} catch (error) { } catch (error) {
addError(`roles:${id ? "roleSave" : "roleCreate"}Error`, error); addError("roles:roleSaveError", error);
} }
}; };
@ -199,7 +164,7 @@ export default function RealmRoleTabs() {
} else { } else {
await adminClient.clients.delRole({ await adminClient.clients.delRole({
id: clientId, id: clientId,
roleName: role!.name as string, roleName: role!.name!,
}); });
} }
addAlert(t("roleDeletedSuccess"), AlertVariant.success); addAlert(t("roleDeletedSuccess"), AlertVariant.success);
@ -328,19 +293,9 @@ export default function RealmRoleTabs() {
const isDefaultRole = (name: string) => realm?.defaultRole!.name === name; const isDefaultRole = (name: string) => realm?.defaultRole!.name === name;
if (!realm) { if (!realm || !role) {
return <KeycloakSpinner />; return <KeycloakSpinner />;
} }
if (!role) {
return (
<RealmRoleForm
reset={() => reset(role)}
form={form}
save={save}
editMode={false}
/>
);
}
return ( return (
<> <>
@ -356,7 +311,7 @@ export default function RealmRoleTabs() {
/> />
)} )}
<ViewHeader <ViewHeader
titleKey={role.name || t("createRole")} titleKey={role.name!}
badges={[ badges={[
{ {
id: "composite-role-badge", id: "composite-role-badge",
@ -364,22 +319,25 @@ export default function RealmRoleTabs() {
readonly: true, readonly: true,
}, },
]} ]}
subKey={id ? "" : "roles:roleCreateExplain"}
actionsDropdownId="roles-actions-dropdown" actionsDropdownId="roles-actions-dropdown"
dropdownItems={dropdownItems} dropdownItems={dropdownItems}
divider={!id} divider={false}
/> />
<PageSection variant="light" className="pf-u-p-0"> <PageSection variant="light" className="pf-u-p-0">
{id && (
<KeycloakTabs isBox mountOnEnter> <KeycloakTabs isBox mountOnEnter>
<Tab <Tab
eventKey="details" eventKey="details"
title={<TabTitleText>{t("common:details")}</TabTitleText>} title={<TabTitleText>{t("common:details")}</TabTitleText>}
> >
<RealmRoleForm <RoleForm
reset={() => reset(role)}
form={form} form={form}
save={save} onSubmit={onSubmit}
role={clientRoleRouteMatch ? "manage-clients" : "manage-realm"}
cancelLink={
clientRoleRouteMatch
? toClient({ realm: realmName, clientId, tab: "roles" })
: toRealmRoles({ realm: realmName })
}
editMode={true} editMode={true}
/> />
</Tab> </Tab>
@ -406,7 +364,7 @@ export default function RealmRoleTabs() {
> >
<AttributesForm <AttributesForm
form={form} form={form}
save={save} save={onSubmit}
reset={() => reset(role)} reset={() => reset(role)}
/> />
</Tab> </Tab>
@ -430,7 +388,6 @@ export default function RealmRoleTabs() {
</Tab> </Tab>
)} )}
</KeycloakTabs> </KeycloakTabs>
)}
</PageSection> </PageSection>
</> </>
); );

View file

@ -116,7 +116,7 @@ export const RolesList = ({
}, },
}); });
const goToCreate = () => navigate(`${url}/add-role`); const goToCreate = () => navigate(`${url}/new`);
if (!realm) { if (!realm) {
return <KeycloakSpinner />; return <KeycloakSpinner />;

View file

@ -1,12 +1,10 @@
import type { RouteDef } from "../route-config"; import type { RouteDef } from "../route-config";
import { AddRoleRoute } from "./routes/AddRole"; import { AddRoleRoute } from "./routes/AddRole";
import { AddRoleToClientRoute } from "./routes/AddRoleToClient";
import { ClientRoleRoute, ClientRoleRouteWithTab } from "./routes/ClientRole"; import { ClientRoleRoute, ClientRoleRouteWithTab } from "./routes/ClientRole";
import { RealmRoleRoute, RealmRoleRouteWithTab } from "./routes/RealmRole"; import { RealmRoleRoute, RealmRoleRouteWithTab } from "./routes/RealmRole";
import { RealmRolesRoute } from "./routes/RealmRoles"; import { RealmRolesRoute } from "./routes/RealmRoles";
const routes: RouteDef[] = [ const routes: RouteDef[] = [
AddRoleToClientRoute,
ClientRoleRoute, ClientRoleRoute,
ClientRoleRouteWithTab, ClientRoleRouteWithTab,
RealmRolesRoute, RealmRolesRoute,

View file

@ -6,8 +6,8 @@ import type { RouteDef } from "../../route-config";
export type AddRoleParams = { realm: string }; export type AddRoleParams = { realm: string };
export const AddRoleRoute: RouteDef = { export const AddRoleRoute: RouteDef = {
path: "/:realm/roles/add-role", path: "/:realm/roles/new",
component: lazy(() => import("../RealmRoleTabs")), component: lazy(() => import("../CreateRealmRole")),
breadcrumb: (t) => t("roles:createRole"), breadcrumb: (t) => t("roles:createRole"),
access: "manage-realm", access: "manage-realm",
}; };

View file

@ -1,22 +0,0 @@
import { lazy } from "react";
import type { Path } from "react-router-dom-v5-compat";
import { generatePath } from "react-router-dom-v5-compat";
import type { RouteDef } from "../../route-config";
export type AddRoleToClientParams = {
realm: string;
clientId: string;
};
export const AddRoleToClientRoute: RouteDef = {
path: "/:realm/clients/:clientId/roles/add-role",
component: lazy(() => import("../RealmRoleTabs")),
breadcrumb: (t) => t("roles:createRole"),
access: "manage-realm",
};
export const toAddRoleToClient = (
params: AddRoleToClientParams
): Partial<Path> => ({
pathname: generatePath(AddRoleToClientRoute.path, params),
});