Refactor roles list to React Router v6 (#4187)

This commit is contained in:
Jon Koops 2023-01-12 16:51:08 +01:00 committed by GitHub
parent 2b9744f6bf
commit d11ed1ccef
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
12 changed files with 103 additions and 118 deletions

View file

@ -31,6 +31,7 @@ import { DownloadDialog } from "../components/download-dialog/DownloadDialog";
import type { KeyValueType } from "../components/key-value-form/key-value-convert";
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
import { PermissionsTab } from "../components/permission-tab/PermissionTab";
import { RolesList } from "../components/roles-list/RolesList";
import {
RoutableTabs,
useRoutableTab,
@ -43,7 +44,6 @@ import { useAccess } from "../context/access/Access";
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { RolesList } from "../realm-roles/RolesList";
import {
convertAttributeNameToForm,
convertFormValuesToObject,
@ -70,8 +70,10 @@ import {
toAuthorizationTab,
} from "./routes/AuthenticationTab";
import { ClientParams, ClientTab, toClient } from "./routes/Client";
import { toClientRole } from "./routes/ClientRole";
import { toClients } from "./routes/Clients";
import { ClientScopesTab, toClientScopesTab } from "./routes/ClientScopeTab";
import { toCreateRole } from "./routes/NewRole";
import { ClientScopes } from "./scopes/ClientScopes";
import { EvaluateScopes } from "./scopes/EvaluateScopes";
import { ServiceAccount } from "./service-account/ServiceAccount";
@ -474,6 +476,15 @@ export default function ClientDetails() {
loader={loader}
paginated={false}
messageBundle="clients"
toCreate={toCreateRole({ realm, clientId: client.id! })}
toDetail={(roleId) =>
toClientRole({
realm,
clientId: client.id!,
id: roleId,
tab: "details",
})
}
isReadOnly={!(hasManageClients || client.access?.configure)}
/>
</Tab>

View file

@ -9,8 +9,8 @@ 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 { toClientRole } from "../routes/ClientRole";
import { NewRoleParams } from "../routes/NewRole";
export default function CreateClientRole() {

View file

@ -1,34 +1,35 @@
import type { RouteDef } from "../route-config";
import { AddClientRoute } from "./routes/AddClient";
import { ClientRoute } from "./routes/Client";
import { ClientsRoute, ClientsRouteWithTab } from "./routes/Clients";
import { CreateInitialAccessTokenRoute } from "./routes/CreateInitialAccessToken";
import { ImportClientRoute } from "./routes/ImportClient";
import { MapperRoute } from "./routes/Mapper";
import { ClientScopesRoute } from "./routes/ClientScopeTab";
import { AuthorizationRoute } from "./routes/AuthenticationTab";
import { NewResourceRoute } from "./routes/NewResource";
import {
ResourceDetailsRoute,
ResourceDetailsWithResourceIdRoute,
} from "./routes/Resource";
import { NewRoleRoute } from "./routes/NewRole";
import { NewScopeRoute } from "./routes/NewScope";
import {
ScopeDetailsRoute,
ScopeDetailsWithScopeIdRoute,
} from "./routes/Scope";
import { NewPolicyRoute } from "./routes/NewPolicy";
import { PolicyDetailsRoute } from "./routes/PolicyDetails";
import {
NewPermissionRoute,
NewPermissionWithSelectedIdRoute,
} from "./routes/NewPermission";
import { PermissionDetailsRoute } from "./routes/PermissionDetails";
import { ClientRoute } from "./routes/Client";
import { ClientRoleRoute } from "./routes/ClientRole";
import { ClientsRoute, ClientsRouteWithTab } from "./routes/Clients";
import { ClientScopesRoute } from "./routes/ClientScopeTab";
import { CreateInitialAccessTokenRoute } from "./routes/CreateInitialAccessToken";
import {
DedicatedScopeDetailsRoute,
DedicatedScopeDetailsWithTabRoute,
} from "./routes/DedicatedScopeDetails";
import { ImportClientRoute } from "./routes/ImportClient";
import { MapperRoute } from "./routes/Mapper";
import {
NewPermissionRoute,
NewPermissionWithSelectedIdRoute,
} from "./routes/NewPermission";
import { NewPolicyRoute } from "./routes/NewPolicy";
import { NewResourceRoute } from "./routes/NewResource";
import { NewRoleRoute } from "./routes/NewRole";
import { NewScopeRoute } from "./routes/NewScope";
import { PermissionDetailsRoute } from "./routes/PermissionDetails";
import { PolicyDetailsRoute } from "./routes/PolicyDetails";
import {
ResourceDetailsRoute,
ResourceDetailsWithResourceIdRoute,
} from "./routes/Resource";
import {
ScopeDetailsRoute,
ScopeDetailsWithScopeIdRoute,
} from "./routes/Scope";
const routes: RouteDef[] = [
AddClientRoute,
@ -41,6 +42,7 @@ const routes: RouteDef[] = [
DedicatedScopeDetailsRoute,
DedicatedScopeDetailsWithTabRoute,
ClientScopesRoute,
ClientRoleRoute,
AuthorizationRoute,
NewResourceRoute,
ResourceDetailsRoute,

View file

@ -13,25 +13,16 @@ export type ClientRoleParams = {
realm: string;
clientId: string;
id: string;
tab?: ClientRoleTab;
tab: ClientRoleTab;
};
export const ClientRoleRoute: RouteDef = {
path: "/:realm/clients/:clientId/roles/:id",
component: lazy(() => import("../RealmRoleTabs")),
path: "/:realm/clients/:clientId/roles/:id/:tab",
component: lazy(() => import("../../realm-roles/RealmRoleTabs")),
breadcrumb: (t) => t("roles:roleDetails"),
access: "view-realm",
};
export const ClientRoleRouteWithTab: RouteDef = {
...ClientRoleRoute,
path: "/:realm/clients/:clientId/roles/:id/:tab",
};
export const toClientRole = (params: ClientRoleParams): Partial<Path> => {
const path = params.tab ? ClientRoleRouteWithTab.path : ClientRoleRoute.path;
return {
pathname: generatePath(path, params),
};
};
export const toClientRole = (params: ClientRoleParams): Partial<Path> => ({
pathname: generatePath(ClientRoleRoute.path, params),
});

View file

@ -1,32 +1,30 @@
import { FunctionComponent, useState } from "react";
import { useRouteMatch } from "react-router-dom";
import { Link, useNavigate } from "react-router-dom-v5-compat";
import { useTranslation } from "react-i18next";
import { AlertVariant, Button, ButtonVariant } from "@patternfly/react-core";
import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
import { useAlerts } from "../components/alert/Alerts";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import { emptyFormatter, upperCaseFormatter } from "../util";
import { useRealm } from "../context/realm-context/RealmContext";
import type RealmRepresentation from "@keycloak/keycloak-admin-client/lib/defs/realmRepresentation";
import { HelpItem } from "../components/help-enabler/HelpItem";
import { ClientParams, ClientRoute } from "../clients/routes/Client";
import { toClientRole } from "./routes/ClientRole";
import { toRealmRole } from "./routes/RealmRole";
import { toRealmSettings } from "../realm-settings/routes/RealmSettings";
import type RoleRepresentation from "@keycloak/keycloak-admin-client/lib/defs/roleRepresentation";
import { AlertVariant, Button, ButtonVariant } from "@patternfly/react-core";
import { useState } from "react";
import { useTranslation } from "react-i18next";
import { Link, To, useNavigate } from "react-router-dom-v5-compat";
import "./RealmRolesSection.css";
import { useAdminClient, useFetch } from "../../context/auth/AdminClient";
import { useRealm } from "../../context/realm-context/RealmContext";
import { toRealmSettings } from "../../realm-settings/routes/RealmSettings";
import { emptyFormatter, upperCaseFormatter } from "../../util";
import { useAlerts } from "../alert/Alerts";
import { useConfirmDialog } from "../confirm-dialog/ConfirmDialog";
import { HelpItem } from "../help-enabler/HelpItem";
import { KeycloakSpinner } from "../keycloak-spinner/KeycloakSpinner";
import { ListEmptyState } from "../list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../table-toolbar/KeycloakDataTable";
import "./RolesList.css";
type RolesListProps = {
paginated?: boolean;
parentRoleId?: string;
messageBundle?: string;
isReadOnly: boolean;
toCreate: To;
toDetail: (roleId: string) => To;
loader?: (
first?: number,
max?: number,
@ -34,32 +32,19 @@ type RolesListProps = {
) => Promise<RoleRepresentation[]>;
};
type RoleLinkProps = {
role: RoleRepresentation;
};
const RoleLink: FunctionComponent<RoleLinkProps> = ({ children, role }) => {
const { realm } = useRealm();
const clientRouteMatch = useRouteMatch<ClientParams>(ClientRoute.path);
const to = clientRouteMatch
? toClientRole({ ...clientRouteMatch.params, id: role.id!, tab: "details" })
: toRealmRole({ realm, id: role.id!, tab: "details" });
return <Link to={to}>{children}</Link>;
};
export const RolesList = ({
loader,
paginated = true,
parentRoleId,
messageBundle = "roles",
toCreate,
toDetail,
isReadOnly,
}: RolesListProps) => {
const { t } = useTranslation(messageBundle);
const navigate = useNavigate();
const { adminClient } = useAdminClient();
const { addAlert, addError } = useAlerts();
const { url } = useRouteMatch();
const { realm: realmName } = useRealm();
const [realm, setRealm] = useState<RealmRepresentation>();
@ -75,7 +60,7 @@ export const RolesList = ({
const RoleDetailLink = (role: RoleRepresentation) =>
role.name !== realm?.defaultRole?.name ? (
<RoleLink role={role}>{role.name}</RoleLink>
<Link to={toDetail(role.id!)}>{role.name}</Link>
) : (
<>
<Link
@ -116,8 +101,6 @@ export const RolesList = ({
},
});
const goToCreate = () => navigate(`${url}/new`);
if (!realm) {
return <KeycloakSpinner />;
}
@ -133,7 +116,10 @@ export const RolesList = ({
isPaginated={paginated}
toolbarItem={
!isReadOnly && (
<Button data-testid="create-role" onClick={goToCreate}>
<Button
data-testid="create-role"
component={(props) => <Link {...props} to={toCreate} />}
>
{t("createRole")}
</Button>
)
@ -179,7 +165,7 @@ export const RolesList = ({
message={t("noRoles")}
instructions={isReadOnly ? "" : t("noRolesInstructions")}
primaryActionText={isReadOnly ? "" : t("createRole")}
onPrimaryAction={goToCreate}
onPrimaryAction={() => navigate(toCreate)}
/>
}
/>

View file

@ -92,7 +92,7 @@ const createLink = (realm: string, event: AdminEventRepresentation) => {
}
if (event.resourcePath?.startsWith("roles-by-id")) {
return toRealmRole({ realm, id });
return toRealmRole({ realm, id, tab: "details" });
}
return "";

View file

@ -16,6 +16,11 @@ import { useRouteMatch } from "react-router-dom";
import { useNavigate } from "react-router-dom-v5-compat";
import { toClient } from "../clients/routes/Client";
import {
ClientRoleParams,
ClientRoleRoute,
toClientRole,
} from "../clients/routes/ClientRole";
import { useAlerts } from "../components/alert/Alerts";
import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog";
import {
@ -37,11 +42,6 @@ import { useAdminClient, useFetch } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { useServerInfo } from "../context/server-info/ServerInfoProvider";
import { useParams } from "../utils/useParams";
import {
ClientRoleParams,
ClientRoleRoute,
toClientRole,
} from "./routes/ClientRole";
import { toRealmRole } from "./routes/RealmRole";
import { toRealmRoles } from "./routes/RealmRoles";
import { UsersInRoleTab } from "./UsersInRoleTab";

View file

@ -1,13 +1,17 @@
import { PageSection } from "@patternfly/react-core";
import { RolesList } from "../components/roles-list/RolesList";
import { ViewHeader } from "../components/view-header/ViewHeader";
import { useAdminClient } from "../context/auth/AdminClient";
import { RolesList } from "./RolesList";
import helpUrls from "../help-urls";
import { useAccess } from "../context/access/Access";
import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import helpUrls from "../help-urls";
import { toAddRole } from "./routes/AddRole";
import { toRealmRole } from "./routes/RealmRole";
export default function RealmRolesSection() {
const { adminClient } = useAdminClient();
const { realm } = useRealm();
const { hasAccess } = useAccess();
const isManager = hasAccess("manage-realm");
@ -34,7 +38,14 @@ export default function RealmRolesSection() {
helpUrl={helpUrls.realmRolesUrl}
/>
<PageSection variant="light" padding={{ default: "noPadding" }}>
<RolesList loader={loader} isReadOnly={!isManager} />
<RolesList
loader={loader}
toCreate={toAddRole({ realm })}
toDetail={(roleId) =>
toRealmRole({ realm, id: roleId, tab: "details" })
}
isReadOnly={!isManager}
/>
</PageSection>
</>
);

View file

@ -3,6 +3,7 @@ import { QuestionCircleIcon } from "@patternfly/react-icons";
import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom-v5-compat";
import type { ClientRoleParams } from "../clients/routes/ClientRole";
import { useHelp } from "../components/help-enabler/HelpHeader";
import { ListEmptyState } from "../components/list-empty-state/ListEmptyState";
import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable";
@ -10,7 +11,6 @@ import { useAdminClient } from "../context/auth/AdminClient";
import { useRealm } from "../context/realm-context/RealmContext";
import { emptyFormatter, upperCaseFormatter } from "../util";
import { useParams } from "../utils/useParams";
import type { ClientRoleParams } from "./routes/ClientRole";
export const UsersInRoleTab = () => {
const navigate = useNavigate();

View file

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

View file

@ -1,6 +1,7 @@
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 RealmRoleTab =
@ -12,25 +13,16 @@ export type RealmRoleTab =
export type RealmRoleParams = {
realm: string;
id: string;
tab?: RealmRoleTab;
tab: RealmRoleTab;
};
export const RealmRoleRoute: RouteDef = {
path: "/:realm/roles/:id",
path: "/:realm/roles/:id/:tab",
component: lazy(() => import("../RealmRoleTabs")),
breadcrumb: (t) => t("roles:roleDetails"),
access: ["view-realm", "view-users"],
};
export const RealmRoleRouteWithTab: RouteDef = {
...RealmRoleRoute,
path: "/:realm/roles/:id/:tab",
};
export const toRealmRole = (params: RealmRoleParams): Partial<Path> => {
const path = params.tab ? RealmRoleRouteWithTab.path : RealmRoleRoute.path;
return {
pathname: generatePath(path, params),
};
};
export const toRealmRole = (params: RealmRoleParams): Partial<Path> => ({
pathname: generatePath(RealmRoleRoute.path, params),
});