Adding Mapping list and detail screen (#180)
* added client scope details screen * initial version of the mapping list * added tabs * changed route create * merge * added detail mapper page * fixed merge errors * fix merge error * dynamic title * added access types to routs
This commit is contained in:
parent
a9dc031fff
commit
4195e0fbf3
9 changed files with 573 additions and 200 deletions
|
@ -56,11 +56,7 @@ export const ClientScopesSection = () => {
|
|||
inputGroupPlaceholder={t("searchFor")}
|
||||
inputGroupOnChange={filterData}
|
||||
toolbarItem={
|
||||
<Button
|
||||
onClick={() =>
|
||||
history.push("/client-scopes/add-client-scopes/")
|
||||
}
|
||||
>
|
||||
<Button onClick={() => history.push("/client-scopes/new")}>
|
||||
{t("createClientScope")}
|
||||
</Button>
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ import React, { useContext, useState } from "react";
|
|||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
AlertVariant,
|
||||
ButtonVariant,
|
||||
Dropdown,
|
||||
DropdownItem,
|
||||
DropdownToggle,
|
||||
|
@ -24,13 +25,14 @@ import { ListEmptyState } from "../../components/list-empty-state/ListEmptyState
|
|||
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import { Link } from "react-router-dom";
|
||||
|
||||
type MapperListProps = {
|
||||
clientScope: ClientScopeRepresentation;
|
||||
};
|
||||
|
||||
type Row = {
|
||||
name: string;
|
||||
name: JSX.Element;
|
||||
category: string;
|
||||
type: string;
|
||||
priority: number;
|
||||
|
@ -58,6 +60,13 @@ export const MapperList = ({ clientScope }: MapperListProps) => {
|
|||
instructions={t("emptyMappersInstructions")}
|
||||
primaryActionText={t("emptyPrimaryAction")}
|
||||
onPrimaryAction={() => {}}
|
||||
secondaryActions={[
|
||||
{
|
||||
text: t("emptySecondaryAction"),
|
||||
onClick: () => {},
|
||||
type: ButtonVariant.secondary,
|
||||
},
|
||||
]}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
@ -70,7 +79,13 @@ export const MapperList = ({ clientScope }: MapperListProps) => {
|
|||
return {
|
||||
mapper,
|
||||
cells: {
|
||||
name: mapper.name,
|
||||
name: (
|
||||
<>
|
||||
<Link to={`/client-scopes/${clientScope.id}/${mapper.id}`}>
|
||||
{mapper.name}
|
||||
</Link>
|
||||
</>
|
||||
),
|
||||
category: mapperType.category,
|
||||
type: mapperType.name,
|
||||
priority: mapperType.priority,
|
||||
|
@ -82,7 +97,7 @@ export const MapperList = ({ clientScope }: MapperListProps) => {
|
|||
const filterData = (search: string) => {
|
||||
setFilteredData(
|
||||
data.filter((column) =>
|
||||
column.cells.name.toLowerCase().includes(search.toLowerCase())
|
||||
column.mapper.name!.toLowerCase().includes(search.toLowerCase())
|
||||
)
|
||||
);
|
||||
};
|
||||
|
|
293
src/client-scopes/details/MappingDetails.tsx
Normal file
293
src/client-scopes/details/MappingDetails.tsx
Normal file
|
@ -0,0 +1,293 @@
|
|||
import React, { useContext, useEffect, useState } from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import {
|
||||
ActionGroup,
|
||||
AlertVariant,
|
||||
Button,
|
||||
ButtonVariant,
|
||||
Checkbox,
|
||||
DropdownItem,
|
||||
Flex,
|
||||
FlexItem,
|
||||
Form,
|
||||
FormGroup,
|
||||
PageSection,
|
||||
Select,
|
||||
SelectOption,
|
||||
SelectVariant,
|
||||
Switch,
|
||||
TextInput,
|
||||
} from "@patternfly/react-core";
|
||||
|
||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
|
||||
import { RealmContext } from "../../context/realm-context/RealmContext";
|
||||
import { ProtocolMapperRepresentation } from "../models/client-scope";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog";
|
||||
import { useAlerts } from "../../components/alert/Alerts";
|
||||
import { HelpItem } from "../../components/help-enabler/HelpItem";
|
||||
import { useServerInfo } from "../../context/server-info/ServerInfoProvider";
|
||||
import { ConfigPropertyRepresentation } from "../../context/server-info/server-info";
|
||||
|
||||
export const MappingDetails = () => {
|
||||
const { t } = useTranslation("client-scopes");
|
||||
const httpClient = useContext(HttpClientContext)!;
|
||||
const { realm } = useContext(RealmContext);
|
||||
const { addAlert } = useAlerts();
|
||||
|
||||
const { scopeId, id } = useParams<{ scopeId: string; id: string }>();
|
||||
const { register, setValue, control, handleSubmit } = useForm();
|
||||
const [mapping, setMapping] = useState<ProtocolMapperRepresentation>();
|
||||
const [typeOpen, setTypeOpen] = useState(false);
|
||||
const [configProperties, setConfigProperties] = useState<
|
||||
ConfigPropertyRepresentation[]
|
||||
>();
|
||||
|
||||
const serverInfo = useServerInfo();
|
||||
const url = `/admin/realms/${realm}/client-scopes/${scopeId}/protocol-mappers/models/${id}`;
|
||||
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
const response = await httpClient.doGet<ProtocolMapperRepresentation>(
|
||||
url
|
||||
);
|
||||
if (response.data) {
|
||||
Object.entries(response.data).map((entry) => {
|
||||
if (entry[0] === "config") {
|
||||
Object.keys(entry[1]).map((key) => {
|
||||
const newKey = key.replace(/\./g, "_");
|
||||
setValue("config." + newKey, entry[1][key]);
|
||||
});
|
||||
}
|
||||
setValue(entry[0], entry[1]);
|
||||
});
|
||||
}
|
||||
setMapping(response.data);
|
||||
const mapperTypes =
|
||||
serverInfo.protocolMapperTypes[response.data!.protocol!];
|
||||
const properties = mapperTypes.find(
|
||||
(type) => type.id === mapping?.protocolMapper
|
||||
)?.properties;
|
||||
setConfigProperties(properties);
|
||||
})();
|
||||
}, []);
|
||||
|
||||
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
|
||||
titleKey: "client-scopes:deleteMappingTitle",
|
||||
messageKey: "client-scopes:deleteMappingConfirm",
|
||||
continueButtonLabel: "common:delete",
|
||||
continueButtonVariant: ButtonVariant.danger,
|
||||
onConfirm: () => {
|
||||
try {
|
||||
httpClient.doDelete(url);
|
||||
addAlert(t("mappingDeletedSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addAlert(t("mappingDeletedError", { error }), AlertVariant.danger);
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const save = async (formMapping: ProtocolMapperRepresentation) => {
|
||||
const keyValues = Object.keys(formMapping.config!).map((key) => {
|
||||
const newKey = key.replace(/_/g, ".");
|
||||
return { [newKey]: formMapping.config![key] };
|
||||
});
|
||||
|
||||
const map = { ...mapping, config: Object.assign({}, ...keyValues) };
|
||||
try {
|
||||
await httpClient.doPut(url, map);
|
||||
addAlert(t("mappingUpdatedSuccess"), AlertVariant.success);
|
||||
} catch (error) {
|
||||
addAlert(t("mappingUpdatedError", { error }), AlertVariant.danger);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<DeleteConfirm />
|
||||
<ViewHeader
|
||||
titleKey={mapping ? mapping.name! : ""}
|
||||
subKey={id}
|
||||
badge={mapping?.protocol}
|
||||
dropdownItems={[
|
||||
<DropdownItem
|
||||
key="delete"
|
||||
value="delete"
|
||||
onClick={toggleDeleteDialog}
|
||||
>
|
||||
{t("common:delete")}
|
||||
</DropdownItem>,
|
||||
]}
|
||||
/>
|
||||
<PageSection variant="light">
|
||||
<Form isHorizontal onSubmit={handleSubmit(save)}>
|
||||
<FormGroup
|
||||
label={t("realmRolePrefix")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="client-scopes-help:prefix"
|
||||
forLabel={t("realmRolePrefix")}
|
||||
forID="prefix"
|
||||
/>
|
||||
}
|
||||
fieldId="prefix"
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
id="prefix"
|
||||
name="config.usermodel_realmRoleMapping_rolePrefix"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("multiValued")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="client-scopes-help:multiValued"
|
||||
forLabel={t("multiValued")}
|
||||
forID="multiValued"
|
||||
/>
|
||||
}
|
||||
fieldId="multiValued"
|
||||
>
|
||||
<Controller
|
||||
name="config.multivalued"
|
||||
control={control}
|
||||
defaultValue="false"
|
||||
render={({ onChange, value }) => (
|
||||
<Switch
|
||||
id="multiValued"
|
||||
label={t("common:on")}
|
||||
labelOff={t("common:off")}
|
||||
isChecked={value === "true"}
|
||||
onChange={(value) => onChange("" + value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("tokenClaimName")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="client-scopes-help:tokenClaimName"
|
||||
forLabel={t("tokenClaimName")}
|
||||
forID="claimName"
|
||||
/>
|
||||
}
|
||||
fieldId="claimName"
|
||||
>
|
||||
<TextInput
|
||||
ref={register()}
|
||||
type="text"
|
||||
id="claimName"
|
||||
name="config.claim_name"
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
label={t("claimJsonType")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText="client-scopes-help:claimJsonType"
|
||||
forLabel={t("claimJsonType")}
|
||||
forID="claimJsonType"
|
||||
/>
|
||||
}
|
||||
fieldId="claimJsonType"
|
||||
>
|
||||
<Controller
|
||||
name="config.jsonType_label"
|
||||
defaultValue=""
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Select
|
||||
toggleId="claimJsonType"
|
||||
onToggle={() => setTypeOpen(!typeOpen)}
|
||||
onSelect={(_, value) => {
|
||||
onChange(value as string);
|
||||
setTypeOpen(false);
|
||||
}}
|
||||
selections={value}
|
||||
variant={SelectVariant.single}
|
||||
aria-label={t("claimJsonType")}
|
||||
isOpen={typeOpen}
|
||||
>
|
||||
{configProperties &&
|
||||
configProperties
|
||||
.find((property) => property.name === "jsonType.label")
|
||||
?.options.map((option) => (
|
||||
<SelectOption
|
||||
selected={option === value}
|
||||
key={option}
|
||||
value={option}
|
||||
/>
|
||||
))}
|
||||
</Select>
|
||||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
<FormGroup
|
||||
hasNoPaddingTop
|
||||
label={t("addClaimTo")}
|
||||
fieldId="addClaimTo"
|
||||
>
|
||||
<Flex>
|
||||
<FlexItem>
|
||||
<Controller
|
||||
name="config.id_token_claim"
|
||||
defaultValue={false}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Checkbox
|
||||
label={t("idToken")}
|
||||
id="idToken"
|
||||
isChecked={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FlexItem>
|
||||
<FlexItem>
|
||||
<Controller
|
||||
name="config.access_token_claim"
|
||||
defaultValue={false}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Checkbox
|
||||
label={t("accessToken")}
|
||||
id="accessToken"
|
||||
isChecked={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FlexItem>
|
||||
<FlexItem>
|
||||
<Controller
|
||||
name="config.userinfo_token_claim"
|
||||
defaultValue={false}
|
||||
control={control}
|
||||
render={({ onChange, value }) => (
|
||||
<Checkbox
|
||||
label={t("userInfo")}
|
||||
id="userInfo"
|
||||
isChecked={value}
|
||||
onChange={onChange}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</FlexItem>
|
||||
</Flex>
|
||||
</FormGroup>
|
||||
<ActionGroup>
|
||||
<Button variant="primary" type="submit">
|
||||
{t("common:save")}
|
||||
</Button>
|
||||
<Button variant="link">{t("common:cancel")}</Button>
|
||||
</ActionGroup>
|
||||
</Form>
|
||||
</PageSection>
|
||||
</>
|
||||
);
|
||||
};
|
|
@ -11,6 +11,9 @@ import {
|
|||
SelectOption,
|
||||
SelectVariant,
|
||||
Switch,
|
||||
Tab,
|
||||
Tabs,
|
||||
TabTitleText,
|
||||
TextInput,
|
||||
} from "@patternfly/react-core";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
@ -24,14 +27,16 @@ import { useAlerts } from "../../components/alert/Alerts";
|
|||
import { useLoginProviders } from "../../context/server-info/ServerInfoProvider";
|
||||
import { ViewHeader } from "../../components/view-header/ViewHeader";
|
||||
import { convertFormValuesToObject, convertToFormValues } from "../../util";
|
||||
import { MapperList } from "../details/MapperList";
|
||||
|
||||
export const ClientScopeForm = () => {
|
||||
const { t } = useTranslation("client-scopes");
|
||||
const helpText = useTranslation("client-scopes-help").t;
|
||||
const { register, control, handleSubmit, errors, setValue } = useForm<
|
||||
ClientScopeRepresentation
|
||||
>();
|
||||
const history = useHistory();
|
||||
const [clientScope, setClientScope] = useState<ClientScopeRepresentation>();
|
||||
const [activeTab, setActiveTab] = useState(0);
|
||||
|
||||
const httpClient = useContext(HttpClientContext)!;
|
||||
const { realm } = useContext(RealmContext);
|
||||
|
@ -55,6 +60,8 @@ export const ClientScopeForm = () => {
|
|||
setValue(entry[0], entry[1]);
|
||||
});
|
||||
}
|
||||
|
||||
setClientScope(response.data);
|
||||
}
|
||||
})();
|
||||
}, []);
|
||||
|
@ -83,17 +90,29 @@ export const ClientScopeForm = () => {
|
|||
return (
|
||||
<>
|
||||
<ViewHeader
|
||||
titleKey="client-scopes:createClientScope"
|
||||
titleKey={
|
||||
clientScope ? clientScope.name! : "client-scopes:createClientScope"
|
||||
}
|
||||
subKey="client-scopes:clientScopeExplain"
|
||||
badge={clientScope ? clientScope.protocol : undefined}
|
||||
/>
|
||||
|
||||
<PageSection variant="light">
|
||||
<Tabs
|
||||
activeKey={activeTab}
|
||||
onSelect={(_, key) => setActiveTab(key as number)}
|
||||
isBox
|
||||
>
|
||||
<Tab
|
||||
eventKey={0}
|
||||
title={<TabTitleText>{t("settings")}</TabTitleText>}
|
||||
>
|
||||
<Form isHorizontal onSubmit={handleSubmit(save)}>
|
||||
<FormGroup
|
||||
label={t("name")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("name")}
|
||||
helpText="client-scopes-help:name"
|
||||
forLabel={t("name")}
|
||||
forID="kc-name"
|
||||
/>
|
||||
|
@ -114,7 +133,7 @@ export const ClientScopeForm = () => {
|
|||
label={t("description")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("description")}
|
||||
helpText="client-scopes-help:description"
|
||||
forLabel={t("description")}
|
||||
forID="kc-description"
|
||||
/>
|
||||
|
@ -128,11 +147,12 @@ export const ClientScopeForm = () => {
|
|||
name="description"
|
||||
/>
|
||||
</FormGroup>
|
||||
{!id && (
|
||||
<FormGroup
|
||||
label={t("protocol")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("protocol")}
|
||||
helpText="client-scopes-help:protocol"
|
||||
forLabel="protocol"
|
||||
forID="kc-protocol"
|
||||
/>
|
||||
|
@ -169,12 +189,13 @@ export const ClientScopeForm = () => {
|
|||
)}
|
||||
/>
|
||||
</FormGroup>
|
||||
)}
|
||||
<FormGroup
|
||||
hasNoPaddingTop
|
||||
label={t("displayOnConsentScreen")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("displayOnConsentScreen")}
|
||||
helpText="client-scopes-help:displayOnConsentScreen"
|
||||
forLabel={t("displayOnConsentScreen")}
|
||||
forID="kc-display.on.consent.screen"
|
||||
/>
|
||||
|
@ -184,14 +205,14 @@ export const ClientScopeForm = () => {
|
|||
<Controller
|
||||
name="attributes.display_on_consent_screen"
|
||||
control={control}
|
||||
defaultValue={false}
|
||||
defaultValue="false"
|
||||
render={({ onChange, value }) => (
|
||||
<Switch
|
||||
id="kc-display.on.consent.screen"
|
||||
label={t("common:on")}
|
||||
labelOff={t("common:off")}
|
||||
isChecked={value}
|
||||
onChange={onChange}
|
||||
isChecked={value === "true"}
|
||||
onChange={(value) => onChange("" + value)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
@ -200,7 +221,7 @@ export const ClientScopeForm = () => {
|
|||
label={t("consentScreenText")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("consentScreenText")}
|
||||
helpText="client-scopes-help:consentScreenText"
|
||||
forLabel={t("consentScreenText")}
|
||||
forID="kc-consent-screen-text"
|
||||
/>
|
||||
|
@ -219,7 +240,7 @@ export const ClientScopeForm = () => {
|
|||
label={t("includeInTokenScope")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("includeInTokenScope")}
|
||||
helpText="client-scopes-help:includeInTokenScope"
|
||||
forLabel={t("includeInTokenScope")}
|
||||
forID="includeInTokenScope"
|
||||
/>
|
||||
|
@ -245,7 +266,7 @@ export const ClientScopeForm = () => {
|
|||
label={t("guiOrder")}
|
||||
labelIcon={
|
||||
<HelpItem
|
||||
helpText={helpText("guiOrder")}
|
||||
helpText="client-scopes-help:guiOrder"
|
||||
forLabel={t("guiOrder")}
|
||||
forID="kc-gui-order"
|
||||
/>
|
||||
|
@ -271,6 +292,11 @@ export const ClientScopeForm = () => {
|
|||
</Button>
|
||||
</ActionGroup>
|
||||
</Form>
|
||||
</Tab>
|
||||
<Tab eventKey={1} title={<TabTitleText>{t("mappers")}</TabTitleText>}>
|
||||
{clientScope && <MapperList clientScope={clientScope} />}
|
||||
</Tab>
|
||||
</Tabs>
|
||||
</PageSection>
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -6,6 +6,10 @@
|
|||
"displayOnConsentScreen": "If on, and this client scope is added to some client with consent required, the text specified by 'Consent Screen Text' will be displayed on consent screen. If off, this client scope will not be displayed on the consent screen",
|
||||
"consentScreenText": "Text that will be shown on the consent screen when this client scope is added to some client with consent required. Defaults to name of client scope if it is not filled",
|
||||
"includeInTokenScope": "If on, the name of this client scope will be added to the access token property 'scope' as well as to the Token Introspection Endpoint response. If off, this client scope will be omitted from the token and from the Token Introspection Endpoint response.",
|
||||
"guiOrder": "Specify order of the provider in GUI (such as in Consent page) as integer"
|
||||
"guiOrder": "Specify order of the provider in GUI (such as in Consent page) as integer",
|
||||
"prefix": "A prefix for each Realm Role (optional).",
|
||||
"multiValued": "Indicates if attribute supports multiple values. If true, the list of all values of this attribute will be set as claim. If false, just first value will be set as claim",
|
||||
"tokenClaimName": "Name of the claim to insert into the token. This can be a fully qualified name like 'address.street'. In this case, a nested json object will be created. To prevent nesting and use dot literally, escape the dot with backslash (\\.).",
|
||||
"claimJsonType": "JSON type that should be used to populate the json claim in the token. long, int, boolean, String and JSON are valid values."
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,6 +12,31 @@
|
|||
"priority": "Priority",
|
||||
"protocol": "Protocol",
|
||||
"includeInTokenScope": "Include in token scope",
|
||||
"settings": "Settings",
|
||||
"mappers": "Mappers",
|
||||
"mappersSearchFor": "Search for mapper",
|
||||
"addMapper": "Add mapper",
|
||||
"fromPredefinedMapper": "From predefined mappers",
|
||||
"byConfiguration": "By configuration",
|
||||
"emptyMappers": "No mappers",
|
||||
"emptyMappersInstructions": "If you want to add mappers, please click the button below to add some predefined mappers or to configure a new mapper.",
|
||||
"emptyPrimaryAction": "Add predefined mapper",
|
||||
"emptySecondaryAction": "Configure a new mapper",
|
||||
"mappingDetails": "Mapper details",
|
||||
"deleteMappingTitle": "Delete mapping?",
|
||||
"deleteMappingConfirm": "Are you sure you want to delete this mapping?",
|
||||
"mappingDeletedSuccess": "Mapping successfully deleted",
|
||||
"mappingDeletedError": "Could not delete mapping: '{{error}}'",
|
||||
"mappingUpdatedSuccess": "Mapping successfully updated",
|
||||
"mappingUpdatedError": "Could not update mapping: '{{error}}'",
|
||||
"realmRolePrefix": "Realm role prefix",
|
||||
"multiValued": "Multivalued",
|
||||
"tokenClaimName": "Token claim name",
|
||||
"claimJsonType": "Claim JSON type",
|
||||
"addClaimTo": "Add claim to",
|
||||
"idToken": "ID token",
|
||||
"accessToken": "Access token",
|
||||
"userInfo": "User info",
|
||||
"createSuccess": "Client scope created",
|
||||
"createError": "Could not create client scope: '{{error}}'",
|
||||
"updateSuccess": "Client scope updated",
|
||||
|
|
|
@ -16,7 +16,7 @@ export const HelpItem = ({ helpText, forLabel, forID }: HelpItemProps) => {
|
|||
return (
|
||||
<>
|
||||
{enabled && (
|
||||
<Popover bodyContent={helpText}>
|
||||
<Popover bodyContent={t(helpText)}>
|
||||
<button
|
||||
aria-label={t(`helpLabel`, { label: forLabel })}
|
||||
onClick={(e) => e.preventDefault()}
|
||||
|
|
|
@ -18,7 +18,6 @@ import {
|
|||
} from "@patternfly/react-core";
|
||||
import { HelpContext } from "../help-enabler/HelpHeader";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { PageBreadCrumbs } from "../bread-crumb/PageBreadCrumbs";
|
||||
import { ExternalLink } from "../external-link/ExternalLink";
|
||||
import { isRowExpanded } from "@patternfly/react-table";
|
||||
|
||||
|
@ -68,10 +67,10 @@ export const ViewHeader = ({
|
|||
</Level>
|
||||
</LevelItem>
|
||||
<LevelItem></LevelItem>
|
||||
{dropdownItems && (
|
||||
<LevelItem>
|
||||
<Toolbar>
|
||||
<ToolbarContent>
|
||||
{onToggle && (
|
||||
<ToolbarItem>
|
||||
<Switch
|
||||
id={`${titleKey}-switch`}
|
||||
|
@ -86,6 +85,8 @@ export const ViewHeader = ({
|
|||
}}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
)}
|
||||
{dropdownItems && (
|
||||
<ToolbarItem>
|
||||
<Dropdown
|
||||
position={DropdownPosition.right}
|
||||
|
@ -98,10 +99,10 @@ export const ViewHeader = ({
|
|||
dropdownItems={dropdownItems}
|
||||
/>
|
||||
</ToolbarItem>
|
||||
)}
|
||||
</ToolbarContent>
|
||||
</Toolbar>
|
||||
</LevelItem>
|
||||
)}
|
||||
</Level>
|
||||
{enabled && (
|
||||
<TextContent>
|
||||
|
|
|
@ -17,6 +17,7 @@ import { NewRealmForm } from "./realm/add/NewRealmForm";
|
|||
import { SessionsSection } from "./sessions/SessionsSection";
|
||||
import { UserFederationSection } from "./user-federation/UserFederationSection";
|
||||
import { UsersSection } from "./user/UsersSection";
|
||||
import { MappingDetails } from "./client-scopes/details/MappingDetails";
|
||||
|
||||
import { AccessType } from "./context/whoami/who-am-i-model";
|
||||
|
||||
|
@ -67,7 +68,7 @@ export const routes: RoutesFn = (t: TFunction) => [
|
|||
access: "view-clients",
|
||||
},
|
||||
{
|
||||
path: "/client-scopes/add-client-scopes",
|
||||
path: "/client-scopes/new",
|
||||
component: ClientScopeForm,
|
||||
breadcrumb: t("client-scopes:createClientScope"),
|
||||
access: "manage-clients",
|
||||
|
@ -78,6 +79,18 @@ export const routes: RoutesFn = (t: TFunction) => [
|
|||
breadcrumb: t("client-scopes:clientScopeDetails"),
|
||||
access: "view-clients",
|
||||
},
|
||||
{
|
||||
path: "/client-scopes/:scopeId/:id",
|
||||
component: MappingDetails,
|
||||
breadcrumb: t("client-scopes:mappingDetails"),
|
||||
access: "view-clients",
|
||||
},
|
||||
{
|
||||
path: "/client-scopes/:id",
|
||||
component: ClientScopeForm,
|
||||
breadcrumb: t("client-scopes:clientScopeDetails"),
|
||||
access: "view-clients",
|
||||
},
|
||||
{
|
||||
path: "/roles",
|
||||
component: RealmRolesSection,
|
||||
|
|
Loading…
Reference in a new issue