keycloak-scim/src/client-scopes/add/RoleMappingForm.tsx
Erik Jan de Wit aea12169f4
initial version of the role mapping create form (#177)
* initial version of the role mapping create form

[marvel](https://marvelapp.com/prototype/f0e8fih/screen/72042619)

* fixed format

* changed to typeahead

* format

* group by

* review fixes

* added group and divider

* change name to realm name
2020-10-27 07:25:54 -04:00

297 lines
9.2 KiB
TypeScript

import React, { useContext, useEffect, useState } from "react";
import { useHistory } from "react-router-dom";
import { useTranslation } from "react-i18next";
import { Controller, useForm } from "react-hook-form";
import {
Form,
FormGroup,
PageSection,
Select,
SelectVariant,
TextInput,
SelectOption,
ActionGroup,
Button,
SelectGroup,
Split,
SplitItem,
Divider,
} from "@patternfly/react-core";
import { useAlerts } from "../../components/alert/Alerts";
import { RealmContext } from "../../context/realm-context/RealmContext";
import { HttpClientContext } from "../../context/http-service/HttpClientContext";
import { ViewHeader } from "../../components/view-header/ViewHeader";
import { HelpItem } from "../../components/help-enabler/HelpItem";
import {
ClientRepresentation,
RoleRepresentation,
} from "../../realm/models/Realm";
import { ProtocolMapperRepresentation } from "../models/client-scope";
export type RoleMappingFormProps = {
clientScopeId: string;
};
export const RoleMappingForm = ({ clientScopeId }: RoleMappingFormProps) => {
const httpClient = useContext(HttpClientContext)!;
const { realm } = useContext(RealmContext);
const history = useHistory();
const { addAlert } = useAlerts();
const { t } = useTranslation("client-scopes");
const { register, handleSubmit, control, errors } = useForm();
const [roleOpen, setRoleOpen] = useState(false);
const [roles, setRoles] = useState<RoleRepresentation[]>([]);
const [clientsOpen, setClientsOpen] = useState(false);
const [clients, setClients] = useState<ClientRepresentation[]>([]);
const [selectedClient, setSelectedClient] = useState<ClientRepresentation>();
const [clientRoles, setClientRoles] = useState<RoleRepresentation[]>();
useEffect(() => {
(async () => {
const response = await httpClient.doGet<RoleRepresentation[]>(
`/admin/realms/${realm}/roles`
);
setRoles(response.data!);
const clientResponse = await httpClient.doGet<ClientRepresentation[]>(
`/admin/realms/${realm}/clients`
);
const clients = clientResponse.data!;
clients.map(
(client) =>
(client.toString = function () {
return this.name;
})
);
setClients(clients);
})();
});
useEffect(() => {
(async () => {
const client = selectedClient as ClientRepresentation;
if (client && client.name !== "realmRoles") {
const response = await httpClient.doGet<RoleRepresentation[]>(
`/admin/realms/master/clients/${client.id}/roles`
);
setClientRoles(response.data!);
}
})();
}, [selectedClient]);
const save = async (mapping: ProtocolMapperRepresentation) => {
try {
await httpClient.doPost(
`/admin/realms/${realm}/client-scopes/${clientScopeId}/protocol-mappers/models`,
mapping
);
addAlert(t("mapperCreateSuccess"));
} catch (error) {
addAlert(t("mapperCreateError", error));
}
};
const createSelectGroup = (clients: ClientRepresentation[]) => {
return [
<SelectGroup key="role" label={t("roleGroup")}>
<SelectOption
key="realmRoles"
value={
{
name: "realmRoles",
toString: () => t("realmRoles"),
} as ClientRepresentation
}
>
{realm}
</SelectOption>
</SelectGroup>,
<Divider key="divider" />,
<SelectGroup key="group" label={t("clientGroup")}>
{clients.map((client) => (
<SelectOption key={client.id} value={client}>
{client.name}
</SelectOption>
))}
</SelectGroup>,
];
};
const roleSelectOptions = () => {
const createItem = (role: RoleRepresentation) => (
<SelectOption key={role.id} value={role}>
{role.name}
</SelectOption>
);
if (clientRoles) {
return clientRoles.map((role) => createItem(role));
} else {
return roles.map((role) => createItem(role));
}
};
return (
<>
<ViewHeader
titleKey="client-scopes:addMapper"
subKey="client-scopes:addMapperExplain"
/>
<PageSection variant="light">
<Form isHorizontal onSubmit={handleSubmit(save)}>
<FormGroup
label={t("protocolMapper")}
labelIcon={
<HelpItem
helpText="client-scopes-help:protocolMapper"
forLabel={t("protocolMapper")}
forID="protocolMapper"
/>
}
fieldId="protocolMapper"
>
<TextInput
ref={register()}
type="text"
id="protocolMapper"
name="protocolMapper"
isReadOnly
/>
</FormGroup>
<FormGroup
label={t("name")}
labelIcon={
<HelpItem
helpText="client-scopes-help:mapperName"
forLabel={t("name")}
forID="name"
/>
}
fieldId="name"
isRequired
validated={errors.name ? "error" : "default"}
helperTextInvalid={t("common:required")}
>
<TextInput
ref={register({ required: true })}
type="text"
id="name"
name="name"
/>
</FormGroup>
<FormGroup
label={t("role")}
labelIcon={
<HelpItem
helpText="client-scopes-help:role"
forLabel={t("role")}
forID="role"
/>
}
validated={errors["config.role"] ? "error" : "default"}
helperTextInvalid={t("common:required")}
fieldId="role"
>
<Split hasGutter>
<SplitItem>
<Select
toggleId="role"
onToggle={() => setClientsOpen(!clientsOpen)}
isOpen={clientsOpen}
variant={SelectVariant.typeahead}
typeAheadAriaLabel={t("selectASourceOfRoles")}
placeholderText={t("selectASourceOfRoles")}
isGrouped
onFilter={(evt) => {
const textInput = evt.target.value;
if (textInput === "") {
return createSelectGroup(clients);
} else {
return createSelectGroup(
clients.filter((client) =>
client.name
.toLowerCase()
.includes(textInput.toLowerCase())
)
);
}
}}
selections={selectedClient}
onClear={() => setSelectedClient(undefined)}
onSelect={(_, value) => {
if (value) {
setSelectedClient(value as ClientRepresentation);
setClientsOpen(false);
}
}}
>
{createSelectGroup(clients)}
</Select>
</SplitItem>
<SplitItem>
<Controller
name="config.role"
defaultValue=""
control={control}
rules={{ required: true }}
render={({ onChange, value }) => (
<Select
onToggle={() => setRoleOpen(!roleOpen)}
isOpen={roleOpen}
variant={SelectVariant.typeahead}
placeholderText={
selectedClient && selectedClient.name !== "realmRoles"
? t("clientRoles")
: t("selectARole")
}
isDisabled={!selectedClient}
typeAheadAriaLabel={t("selectARole")}
selections={value.name}
onSelect={(_, value) => {
onChange(value);
setRoleOpen(false);
}}
onClear={() => onChange("")}
>
{roleSelectOptions()}
</Select>
)}
/>
</SplitItem>
</Split>
</FormGroup>
<FormGroup
label={t("newRoleName")}
labelIcon={
<HelpItem
helpText="client-scopes-help:newRoleName"
forLabel={t("newRoleName")}
forID="newRoleName"
/>
}
fieldId="newRoleName"
>
<TextInput
ref={register()}
type="text"
id="newRoleName"
name="config.new_role_name"
/>
</FormGroup>
<ActionGroup>
<Button variant="primary" type="submit">
{t("common:save")}
</Button>
<Button variant="link" onClick={() => history.push("..")}>
{t("common:cancel")}
</Button>
</ActionGroup>
</Form>
</PageSection>
</>
);
};