import React, { useEffect, useMemo, useState } from "react"; import { ActionGroup, AlertVariant, Button, ButtonVariant, DataList, DataListCell, DataListItem, DataListItemCells, DataListItemRow, Divider, DropdownItem, Flex, FlexItem, FormGroup, PageSection, Text, TextArea, TextInput, TextVariants, ValidatedOptions, } from "@patternfly/react-core"; import { useTranslation } from "react-i18next"; import { useForm } from "react-hook-form"; import { FormAccess } from "../components/form-access/FormAccess"; import { ViewHeader } from "../components/view-header/ViewHeader"; import { Link, useHistory, useParams } from "react-router-dom"; import { useAlerts } from "../components/alert/Alerts"; import { useAdminClient, useFetch } from "../context/auth/AdminClient"; import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation"; import { HelpItem } from "../components/help-enabler/HelpItem"; import { PlusCircleIcon, TrashIcon } from "@patternfly/react-icons"; import "./RealmSettingsSection.css"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { toAddExecutor } from "./routes/AddExecutor"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { ClientProfileParams, toClientProfile } from "./routes/ClientProfile"; import { toExecutor } from "./routes/Executor"; import { toClientPolicies } from "./routes/ClientPolicies"; type ClientProfileForm = Required; const defaultValues: ClientProfileForm = { name: "", description: "", executors: [], }; export default function ClientProfileForm() { const { t } = useTranslation("realm-settings"); const history = useHistory(); const { handleSubmit, setValue, register, errors, formState: { isDirty }, } = useForm({ defaultValues, mode: "onChange", }); const { addAlert, addError } = useAlerts(); const adminClient = useAdminClient(); const [globalProfiles, setGlobalProfiles] = useState< ClientProfileRepresentation[] >([]); const [profiles, setProfiles] = useState([]); const { realm, profileName } = useParams(); const serverInfo = useServerInfo(); const executorTypes = useMemo( () => serverInfo.componentTypes?.[ "org.keycloak.services.clientpolicy.executor.ClientPolicyExecutorProvider" ], [] ); const [executorToDelete, setExecutorToDelete] = useState<{ idx: number; name: string }>(); const editMode = profileName ? true : false; const [key, setKey] = useState(0); const reload = () => setKey(new Date().getTime()); useFetch( () => adminClient.clientPolicies.listProfiles({ includeGlobalProfiles: true }), (profiles) => { setGlobalProfiles(profiles.globalProfiles ?? []); setProfiles(profiles.profiles ?? []); }, [key] ); const save = async (form: ClientProfileForm) => { const updatedProfiles = editMode ? patchProfiles(form) : addProfile(form); try { await adminClient.clientPolicies.createProfiles({ profiles: updatedProfiles, globalProfiles: globalProfiles, }); addAlert( editMode ? t("realm-settings:updateClientProfileSuccess") : t("realm-settings:createClientProfileSuccess"), AlertVariant.success ); history.push(toClientProfile({ realm, profileName: form.name })); } catch (error) { addError( editMode ? "realm-settings:updateClientProfileError" : "realm-settings:createClientProfileError", error ); } }; const patchProfiles = (data: ClientProfileRepresentation) => profiles.map((profile) => { if (profile.name !== profileName) { return profile; } return { ...profile, name: data.name, description: data.description, }; }); const addProfile = (data: ClientProfileRepresentation) => profiles.concat({ ...data, executors: [], }); const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: executorToDelete?.name! ? t("deleteExecutorProfileConfirmTitle") : t("deleteClientProfileConfirmTitle"), messageKey: executorToDelete?.name! ? t("deleteExecutorProfileConfirm", { executorName: executorToDelete.name!, }) : t("deleteClientProfileConfirm", { profileName, }), continueButtonLabel: t("delete"), continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { if (executorToDelete?.name!) { profileExecutors.splice(executorToDelete.idx!, 1); try { await adminClient.clientPolicies.createProfiles({ profiles: profiles, globalProfiles, }); addAlert(t("deleteExecutorSuccess"), AlertVariant.success); history.push(toClientProfile({ realm, profileName })); } catch (error) { addError(t("deleteExecutorError"), error); } } else { const updatedProfiles = profiles.filter( (profile) => profile.name !== profileName ); try { await adminClient.clientPolicies.createProfiles({ profiles: updatedProfiles, globalProfiles, }); addAlert(t("deleteClientSuccess"), AlertVariant.success); history.push(toClientPolicies({ realm })); } catch (error) { addError(t("deleteClientError"), error); } } }, }); const profile = profiles.find((profile) => profile.name === profileName); const profileExecutors = profile?.executors || []; const globalProfile = globalProfiles.find( (globalProfile) => globalProfile.name === profileName ); const globalProfileExecutors = globalProfile?.executors || []; useEffect(() => { setValue("name", globalProfile?.name ?? profile?.name); setValue("description", globalProfile?.description ?? profile?.description); }, [profiles]); return ( <> {t("deleteClientProfile")} , ] : undefined } />