form layout no logic

This commit is contained in:
mfrances 2021-03-23 17:15:46 -04:00
parent 95c22fd862
commit f863f850d1
3 changed files with 329 additions and 135 deletions

View file

@ -29,6 +29,7 @@ import { RoleMappingForm } from "./client-scopes/add/RoleMappingForm";
import { RealmRoleTabs } from "./realm-roles/RealmRoleTabs"; import { RealmRoleTabs } from "./realm-roles/RealmRoleTabs";
import { SearchGroups } from "./groups/SearchGroups"; import { SearchGroups } from "./groups/SearchGroups";
import { CreateInitialAccessToken } from "./clients/initial-access/CreateInitialAccessToken"; import { CreateInitialAccessToken } from "./clients/initial-access/CreateInitialAccessToken";
import { LdapMappingDetails } from "./user-federation/ldap/mappers/LdapMappingDetails";
export type RouteDef = BreadcrumbsRoute & { export type RouteDef = BreadcrumbsRoute & {
access: AccessType; access: AccessType;
@ -224,6 +225,12 @@ export const routes: RoutesFn = (t: TFunction) => [
breadcrumb: t("common:settings"), breadcrumb: t("common:settings"),
access: "view-realm", access: "view-realm",
}, },
{
path: "/:realm/user-federation/ldap/:id/:tab/:mapperId",
component: LdapMappingDetails,
breadcrumb: t("client-scopes:mappingDetails"),
access: "view-clients",
},
{ {
path: "/:realm/user-federation/ldap/new", path: "/:realm/user-federation/ldap/new",
component: UserFederationLdapSettings, component: UserFederationLdapSettings,

View file

@ -1,15 +1,25 @@
import React, { useState, useEffect } from "react"; import React, { useState, useEffect } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { AlertVariant } from "@patternfly/react-core";
import { import {
Table, AlertVariant,
TableBody, Button,
TableHeader, ToolbarItem,
TableVariant, // Dropdown,
} from "@patternfly/react-table"; // DropdownItem,
import { useErrorHandler } from "react-error-boundary"; // DropdownToggle,
} from "@patternfly/react-core";
import { CaretDownIcon } from "@patternfly/react-icons";
import { TableToolbar } from "../../../components/table-toolbar/TableToolbar"; // import {
// Table,
// TableBody,
// TableHeader,
// TableVariant,
// } from "@patternfly/react-table";
import { useErrorHandler } from "react-error-boundary";
import { KeycloakDataTable } from "../../../components/table-toolbar/KeycloakDataTable";
// import { TableToolbar } from "../../../components/table-toolbar/TableToolbar";
import { ListEmptyState } from "../../../components/list-empty-state/ListEmptyState"; import { ListEmptyState } from "../../../components/list-empty-state/ListEmptyState";
import { useAlerts } from "../../../components/alert/Alerts"; import { useAlerts } from "../../../components/alert/Alerts";
import { import {
@ -17,7 +27,7 @@ import {
asyncStateFetch, asyncStateFetch,
} from "../../../context/auth/AdminClient"; } from "../../../context/auth/AdminClient";
import { useParams } from "react-router-dom"; import { useParams, Link } from "react-router-dom";
interface ComponentMapperRepresentation { interface ComponentMapperRepresentation {
config?: Record<string, any>; config?: Record<string, any>;
@ -28,10 +38,10 @@ interface ComponentMapperRepresentation {
parentID?: string; parentID?: string;
} }
type Row = { // type Row = ComponentMapperRepresentation {
name: JSX.Element; // name: JSX.Element;
type: string; // type: string;
}; // };
export const LdapMapperList = () => { export const LdapMapperList = () => {
const [mappers, setMappers] = useState<ComponentMapperRepresentation[]>(); const [mappers, setMappers] = useState<ComponentMapperRepresentation[]>();
@ -39,11 +49,14 @@ export const LdapMapperList = () => {
const { t } = useTranslation("client-scopes"); const { t } = useTranslation("client-scopes");
const adminClient = useAdminClient(); const adminClient = useAdminClient();
const { addAlert } = useAlerts(); const { addAlert } = useAlerts();
// const [mapperAction, setMapperAction] = useState(false);
const handleError = useErrorHandler(); const handleError = useErrorHandler();
const [key, setKey] = useState(0); const [key, setKey] = useState(0);
const { id } = useParams<{ id: string }>(); const { id } = useParams<{ id: string }>();
useEffect(() => { useEffect(() => {
return asyncStateFetch( return asyncStateFetch(
() => { () => {
@ -73,36 +86,205 @@ export const LdapMapperList = () => {
); );
} }
const loader = async () =>
Promise.resolve(
(mappers || []).map((mapper) => {
// const mapperType = mappers.filter(
// (type) => type.id === mapper.protocolMapper
// )[0];
return {
...mapper,
name: mapper.name,
type: mapper.providerId,
} as ComponentMapperRepresentation;
})
// .sort((a, b) => a.priority - b.priority)
);
const url = "mappers";
const MapperLink = (mapper: ComponentMapperRepresentation) => (
<>
<Link to={`${url}/${mapper.id}`}>{mapper.name}</Link>
</>
);
return ( return (
<TableToolbar <>
inputGroupName="clientsScopeToolbarTextInput" <KeycloakDataTable
inputGroupPlaceholder={t("mappersSearchFor")} key={key}
> loader={loader}
<Table ariaLabelKey="client-scopes:clientScopeList"
variant={TableVariant.compact} searchPlaceholderKey="client-scopes:mappersSearchFor"
cells={[ toolbarItem={
t("common:name"), <ToolbarItem>
t("common:type"), <Button
]} data-testid="createMapperBtn"
rows={mappers.map((cell) => { variant="primary"
return { // onClick={handleModalToggle}
cells: Object.values([cell.name, cell.providerId]), onClick={ () => addAlert(t("Add functionality not implemented yet!"), AlertVariant.success)}
}; >
})} {/* TODO: Add proper strings */}
aria-label={t("clientScopeList")} {t("Add mapper")}
</Button>
</ToolbarItem>
}
// <Dropdown
// onSelect={() => setMapperAction(false)}
// toggle={
// <DropdownToggle
// isPrimary
// id="mapperAction"
// onToggle={() => setMapperAction(!mapperAction)}
// toggleIndicator={CaretDownIcon}
// >
// {t("addMapper")}
// </DropdownToggle>
// }
// isOpen={mapperAction}
// dropdownItems={[
// <DropdownItem
// key="predefined"
// onClick={() =>
// addAlert(t("mappingCreatedSuccess"), AlertVariant.success)
// }
// >
// {t("fromPredefinedMapper")}
// </DropdownItem>,
// ]}
// />
// toolbarItem={
// <Dropdown
// onSelect={() => setMapperAction(false)}
// toggle={
// <DropdownToggle
// isPrimary
// id="mapperAction"
// onToggle={() => setMapperAction(!mapperAction)}
// toggleIndicator={CaretDownIcon}
// >
// {t("addMapper")}
// </DropdownToggle>
// }
// isOpen={mapperAction}
// dropdownItems={[
// <DropdownItem
// key="predefined"
// onClick={() => toggleAddMapperDialog(true)}
// >
// {t("fromPredefinedMapper")}
// </DropdownItem>,
// <DropdownItem
// key="byConfiguration"
// onClick={() => toggleAddMapperDialog(false)}
// >
// {t("byConfiguration")}
// </DropdownItem>,
// ]}
// />
// }
// actions={[
// {
// title: t("common:delete"),
// onRowClick: async (mapper) => {
// try {
// await adminClient.clientScopes.delProtocolMapper({
// id: clientScope.id!,
// mapperId: mapper.id!,
// });
// addAlert(t("mappingDeletedSuccess"), AlertVariant.success);
// refresh();
// } catch (error) {
// addAlert(
// t("mappingDeletedError", { error }),
// AlertVariant.danger
// );
// }
// return true;
// },
// },
// ]}
actions={[ actions={[
{ {
title: t("common:delete"), title: t("common:delete"),
onClick: () => { onRowClick: async (mapper) => {
addAlert(t("mappingDeletedSuccess"), AlertVariant.success); try {
// await adminClient.clientScopes.delProtocolMapper({
// id: mapper.id!,
// mapperId: mapper.id!,
// });
// addAlert(t("mappingDeletedSuccess"), AlertVariant.success);
addAlert(
"Delete functionality not implemented yet!",
AlertVariant.success
);
// refresh();
} catch (error) {
addAlert(
t("mappingDeletedError", { error }),
AlertVariant.danger
);
}
return true;
}, },
}, },
]} ]}
> columns={[
<TableHeader /> {
<TableBody /> name: "name",
</Table> cellRenderer: MapperLink,
</TableToolbar> },
{
name: "type",
},
]}
emptyState={
<ListEmptyState
message={t("emptyMappers")}
instructions={t("emptyMappersInstructions")}
primaryActionText={t("emptyPrimaryAction")}
// onPrimaryAction={() => toggleAddMapperDialog(true)}
// secondaryActions={[
// {
// text: t("emptySecondaryAction"),
// onClick: () => toggleAddMapperDialog(false),
// type: ButtonVariant.secondary,
// },
// ]}
/>
}
/>
</>
// <TableToolbar
// inputGroupName="clientsScopeToolbarTextInput"
// inputGroupPlaceholder={t("mappersSearchFor")}
// >
// <Table
// variant={TableVariant.compact}
// cells={[
// t("common:name"),
// t("common:type"),
// ]}
// rows={mappers.map((cell) => {
// return {
// cells: Object.values([cell.name, cell.providerId]),
// };
// })}
// aria-label={t("clientScopeList")}
// actions={[
// {
// title: t("common:delete"),
// onClick: () => {
// addAlert(t("mappingDeletedSuccess"), AlertVariant.success);
// },
// },
// ]}
// >
// <TableHeader />
// <TableBody />
// </Table>
// </TableToolbar>
); );
}; };

View file

@ -19,6 +19,7 @@ import {
Switch, Switch,
TextInput, TextInput,
ValidatedOptions, ValidatedOptions,
Text
} from "@patternfly/react-core"; } from "@patternfly/react-core";
import { ConfigPropertyRepresentation } from "keycloak-admin/lib/defs/configPropertyRepresentation"; import { ConfigPropertyRepresentation } from "keycloak-admin/lib/defs/configPropertyRepresentation";
import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation"; import ProtocolMapperRepresentation from "keycloak-admin/lib/defs/protocolMapperRepresentation";
@ -42,112 +43,115 @@ type Params = {
}; };
export const LdapMappingDetails = () => { export const LdapMappingDetails = () => {
const { t } = useTranslation("client-scopes"); // const { t } = useTranslation("client-scopes");
const adminClient = useAdminClient(); // const adminClient = useAdminClient();
const handleError = useErrorHandler(); // const handleError = useErrorHandler();
const { addAlert } = useAlerts(); // const { addAlert } = useAlerts();
const { id, mapperId } = useParams<Params>(); // const { id, mapperId } = useParams<Params>();
const { register, errors, setValue, control, handleSubmit } = useForm(); // const { register, errors, setValue, control, handleSubmit } = useForm();
const [mapping, setMapping] = useState<ProtocolMapperRepresentation>(); // const [mapping, setMapping] = useState<ProtocolMapperRepresentation>();
const [typeOpen, setTypeOpen] = useState(false); // const [typeOpen, setTypeOpen] = useState(false);
const [configProperties, setConfigProperties] = useState< // const [configProperties, setConfigProperties] = useState<
ConfigPropertyRepresentation[] // ConfigPropertyRepresentation[]
>(); // >();
const history = useHistory(); // const history = useHistory();
const serverInfo = useServerInfo(); // const serverInfo = useServerInfo();
const { url } = useRouteMatch(); // const { url } = useRouteMatch();
const isGuid = /^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$/; // const isGuid = /^[{]?[0-9a-fA-F]{8}-([0-9a-fA-F]{4}-){3}[0-9a-fA-F]{12}[}]?$/;
useEffect(() => { // useEffect(() => {
return asyncStateFetch( // return asyncStateFetch(
async () => { // async () => {
if (mapperId.match(isGuid)) { // if (mapperId.match(isGuid)) {
const data = await adminClient.clientScopes.findProtocolMapper({ // const data = await adminClient.clientScopes.findProtocolMapper({
id, // id,
mapperId, // mapperId,
}); // });
if (data) { // if (data) {
Object.entries(data).map((entry) => { // Object.entries(data).map((entry) => {
convertToFormValues(entry[1], "config", setValue); // convertToFormValues(entry[1], "config", setValue);
}); // });
} // }
const mapperTypes = serverInfo.protocolMapperTypes![data!.protocol!]; // const mapperTypes = serverInfo.protocolMapperTypes![data!.protocol!];
const properties = mapperTypes.find( // const properties = mapperTypes.find(
(type) => type.id === data!.protocolMapper // (type) => type.id === data!.protocolMapper
)?.properties!; // )?.properties!;
return { // return {
configProperties: properties, // configProperties: properties,
mapping: data, // mapping: data,
}; // };
} else { // } else {
const scope = await adminClient.clientScopes.findOne({ id }); // const scope = await adminClient.clientScopes.findOne({ id });
const protocolMappers = serverInfo.protocolMapperTypes![ // const protocolMappers = serverInfo.protocolMapperTypes![
scope.protocol! // scope.protocol!
]; // ];
const mapping = protocolMappers.find( // const mapping = protocolMappers.find(
(mapper) => mapper.id === mapperId // (mapper) => mapper.id === mapperId
)!; // )!;
return { // return {
mapping: { // mapping: {
name: mapping.name, // name: mapping.name,
protocol: scope.protocol, // protocol: scope.protocol,
protocolMapper: mapperId, // protocolMapper: mapperId,
}, // },
}; // };
} // }
}, // },
(result) => { // (result) => {
setConfigProperties(result.configProperties); // setConfigProperties(result.configProperties);
setMapping(result.mapping); // setMapping(result.mapping);
}, // },
handleError // handleError
); // );
}, []); // }, []);
const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ // const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({
titleKey: "client-scopes:deleteMappingTitle", // titleKey: "client-scopes:deleteMappingTitle",
messageKey: "client-scopes:deleteMappingConfirm", // messageKey: "client-scopes:deleteMappingConfirm",
continueButtonLabel: "common:delete", // continueButtonLabel: "common:delete",
continueButtonVariant: ButtonVariant.danger, // continueButtonVariant: ButtonVariant.danger,
onConfirm: async () => { // onConfirm: async () => {
try { // try {
await adminClient.clientScopes.delClientScopeMappings( // await adminClient.clientScopes.delClientScopeMappings(
{ client: id, id: mapperId }, // { client: id, id: mapperId },
[] // []
); // );
addAlert(t("mappingDeletedSuccess"), AlertVariant.success); // addAlert(t("mappingDeletedSuccess"), AlertVariant.success);
history.push(`${url}/${id}`); // history.push(`${url}/${id}`);
} catch (error) { // } catch (error) {
addAlert(t("mappingDeletedError", { error }), AlertVariant.danger); // addAlert(t("mappingDeletedError", { error }), AlertVariant.danger);
} // }
}, // },
}); // });
const save = async (formMapping: ProtocolMapperRepresentation) => { // const save = async (formMapping: ProtocolMapperRepresentation) => {
const config = convertFormValuesToObject(formMapping.config); // const config = convertFormValuesToObject(formMapping.config);
const map = { ...mapping, ...formMapping, config }; // const map = { ...mapping, ...formMapping, config };
const key = mapperId.match(isGuid) ? "Updated" : "Created"; // const key = mapperId.match(isGuid) ? "Updated" : "Created";
try { // try {
if (mapperId.match(isGuid)) { // if (mapperId.match(isGuid)) {
await adminClient.clientScopes.updateProtocolMapper( // await adminClient.clientScopes.updateProtocolMapper(
{ id, mapperId }, // { id, mapperId },
map // map
); // );
} else { // } else {
await adminClient.clientScopes.addProtocolMapper({ id }, map); // await adminClient.clientScopes.addProtocolMapper({ id }, map);
} // }
addAlert(t(`mapping${key}Success`), AlertVariant.success); // addAlert(t(`mapping${key}Success`), AlertVariant.success);
} catch (error) { // } catch (error) {
addAlert(t(`mapping${key}Error`, { error }), AlertVariant.danger); // addAlert(t(`mapping${key}Error`, { error }), AlertVariant.danger);
} // }
}; // };
return ( return (
<> <>
<DeleteConfirm /> <Text>
Coming soon!
</Text>
{/* <DeleteConfirm />
<ViewHeader <ViewHeader
titleKey={mapping ? mapping.name! : t("addMapper")} titleKey={mapping ? mapping.name! : t("addMapper")}
subKey={mapperId.match(isGuid) ? mapperId : ""} subKey={mapperId.match(isGuid) ? mapperId : ""}
@ -371,6 +375,7 @@ export const LdapMappingDetails = () => {
</ActionGroup> </ActionGroup>
</FormAccess> </FormAccess>
</PageSection> </PageSection>
</> */}
</>
); );
}; };