170 lines
4.9 KiB
TypeScript
170 lines
4.9 KiB
TypeScript
import { useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useFormContext, Controller } from "react-hook-form";
|
|
import { FormGroup, Button, Checkbox } from "@patternfly/react-core";
|
|
import { MinusCircleIcon } from "@patternfly/react-icons";
|
|
import {
|
|
TableComposable,
|
|
Thead,
|
|
Tr,
|
|
Th,
|
|
Tbody,
|
|
Td,
|
|
} from "@patternfly/react-table";
|
|
|
|
import { Row, ServiceRole } from "../../../components/role-mapping/RoleMapping";
|
|
import type { RequiredIdValue } from "./ClientScope";
|
|
import { HelpItem } from "../../../components/help-enabler/HelpItem";
|
|
import { useAdminClient, useFetch } from "../../../context/auth/AdminClient";
|
|
import { AddRoleMappingModal } from "../../../components/role-mapping/AddRoleMappingModal";
|
|
|
|
export const Role = () => {
|
|
const { t } = useTranslation("clients");
|
|
const {
|
|
control,
|
|
getValues,
|
|
setValue,
|
|
formState: { errors },
|
|
} = useFormContext<{
|
|
roles?: RequiredIdValue[];
|
|
}>();
|
|
const values = getValues("roles");
|
|
|
|
const [open, setOpen] = useState(false);
|
|
const [selectedRoles, setSelectedRoles] = useState<Row[]>([]);
|
|
|
|
const { adminClient } = useAdminClient();
|
|
|
|
useFetch(
|
|
async () => {
|
|
if (values && values.length > 0) {
|
|
const roles = await Promise.all(
|
|
values.map((r) => adminClient.roles.findOneById({ id: r.id }))
|
|
);
|
|
return Promise.all(
|
|
roles
|
|
.filter((r) => r?.clientRole)
|
|
.map(async (role) => ({
|
|
role: role!,
|
|
client: await adminClient.clients.findOne({
|
|
id: role?.containerId!,
|
|
}),
|
|
}))
|
|
);
|
|
}
|
|
return Promise.resolve([]);
|
|
},
|
|
setSelectedRoles,
|
|
[]
|
|
);
|
|
|
|
return (
|
|
<FormGroup
|
|
label={t("roles")}
|
|
labelIcon={
|
|
<HelpItem
|
|
helpText="clients-help:policyRoles"
|
|
fieldLabelId="clients:roles"
|
|
/>
|
|
}
|
|
fieldId="roles"
|
|
helperTextInvalid={t("requiredRoles")}
|
|
validated={errors.roles ? "error" : "default"}
|
|
isRequired
|
|
>
|
|
<Controller
|
|
name="roles"
|
|
control={control}
|
|
defaultValue={[]}
|
|
rules={{
|
|
validate: (value: RequiredIdValue[]) =>
|
|
value.filter((c) => c.id).length > 0,
|
|
}}
|
|
render={({ onChange, value }) => (
|
|
<>
|
|
{open && (
|
|
<AddRoleMappingModal
|
|
id="role"
|
|
type="roles"
|
|
onAssign={(rows) => {
|
|
onChange([
|
|
...value,
|
|
...rows.map((row) => ({ id: row.role.id })),
|
|
]);
|
|
setSelectedRoles([...selectedRoles, ...rows]);
|
|
setOpen(false);
|
|
}}
|
|
onClose={() => {
|
|
setOpen(false);
|
|
}}
|
|
isLDAPmapper
|
|
/>
|
|
)}
|
|
<Button
|
|
data-testid="select-role-button"
|
|
variant="secondary"
|
|
onClick={() => {
|
|
setOpen(true);
|
|
}}
|
|
>
|
|
{t("addRoles")}
|
|
</Button>
|
|
</>
|
|
)}
|
|
/>
|
|
{selectedRoles.length > 0 && (
|
|
<TableComposable variant="compact">
|
|
<Thead>
|
|
<Tr>
|
|
<Th>{t("roles")}</Th>
|
|
<Th>{t("required")}</Th>
|
|
<Th />
|
|
</Tr>
|
|
</Thead>
|
|
<Tbody>
|
|
{selectedRoles.map((row, index) => (
|
|
<Tr key={row.role.id}>
|
|
<Td>
|
|
<ServiceRole role={row.role} client={row.client} />
|
|
</Td>
|
|
<Td>
|
|
<Controller
|
|
name={`roles[${index}].required`}
|
|
defaultValue={false}
|
|
control={control}
|
|
render={({ onChange, value }) => (
|
|
<Checkbox
|
|
id="required"
|
|
data-testid="standard"
|
|
name="required"
|
|
isChecked={value}
|
|
onChange={onChange}
|
|
/>
|
|
)}
|
|
/>
|
|
</Td>
|
|
<Td>
|
|
<Button
|
|
variant="link"
|
|
className="keycloak__client-authorization__policy-row-remove"
|
|
icon={<MinusCircleIcon />}
|
|
onClick={() => {
|
|
setValue("roles", [
|
|
...(values || []).filter((s) => s.id !== row.role.id),
|
|
]);
|
|
setSelectedRoles([
|
|
...selectedRoles.filter(
|
|
(s) => s.role.id !== row.role.id
|
|
),
|
|
]);
|
|
}}
|
|
/>
|
|
</Td>
|
|
</Tr>
|
|
))}
|
|
</Tbody>
|
|
</TableComposable>
|
|
)}
|
|
</FormGroup>
|
|
);
|
|
};
|