import type ClientProfileRepresentation from "@keycloak/keycloak-admin-client/lib/defs/clientProfileRepresentation"; import { CodeEditor, Language } from "@patternfly/react-code-editor"; import { ActionGroup, AlertVariant, Button, ButtonVariant, Divider, Flex, FlexItem, FormGroup, Label, PageSection, Radio, Title, ToolbarItem, } from "@patternfly/react-core"; import { omit } from "lodash-es"; import { useState } from "react"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { adminClient } from "../admin-client"; import { useAlerts } from "../components/alert/Alerts"; import { useConfirmDialog } from "../components/confirm-dialog/ConfirmDialog"; import { KeycloakSpinner } from "../components/keycloak-spinner/KeycloakSpinner"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { Action, KeycloakDataTable, } from "../components/table-toolbar/KeycloakDataTable"; import { useRealm } from "../context/realm-context/RealmContext"; import { prettyPrintJSON } from "../util"; import { useFetch } from "../utils/useFetch"; import { toAddClientProfile } from "./routes/AddClientProfile"; import { toClientProfile } from "./routes/ClientProfile"; import "./realm-settings-section.css"; type ClientProfile = ClientProfileRepresentation & { global: boolean; }; export default function ProfilesTab() { const { t } = useTranslation(); const { realm } = useRealm(); const { addAlert, addError } = useAlerts(); const [tableProfiles, setTableProfiles] = useState(); const [globalProfiles, setGlobalProfiles] = useState(); const [selectedProfile, setSelectedProfile] = useState(); const [show, setShow] = useState(false); const [code, setCode] = useState(); const [key, setKey] = useState(0); useFetch( () => adminClient.clientPolicies.listProfiles({ includeGlobalProfiles: true, }), (allProfiles) => { setGlobalProfiles(allProfiles.globalProfiles); const globalProfiles = allProfiles.globalProfiles?.map( (globalProfiles) => ({ ...globalProfiles, global: true, }), ); const profiles = allProfiles.profiles?.map((profiles) => ({ ...profiles, global: false, })); const allClientProfiles = globalProfiles?.concat(profiles ?? []); setTableProfiles(allClientProfiles || []); setCode(JSON.stringify(allClientProfiles, null, 2)); }, [key], ); const loader = async () => tableProfiles ?? []; const normalizeProfile = ( profile: ClientProfile, ): ClientProfileRepresentation => omit(profile, "global"); const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: t("deleteClientProfileConfirmTitle"), messageKey: t("deleteClientProfileConfirm", { profileName: selectedProfile?.name, }), continueButtonLabel: t("delete"), continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { const updatedProfiles = tableProfiles ?.filter( (profile) => profile.name !== selectedProfile?.name && !profile.global, ) .map((profile) => normalizeProfile(profile), ); try { await adminClient.clientPolicies.createProfiles({ profiles: updatedProfiles, globalProfiles, }); addAlert(t("deleteClientSuccess"), AlertVariant.success); setKey(key + 1); } catch (error) { addError(t("deleteClientError"), error); } }, }); const cellFormatter = (row: ClientProfile) => ( {row.name} {row.global && } ); if (!tableProfiles) { return ; } const save = async () => { if (!code) { return; } try { const obj: ClientProfile[] = JSON.parse(code); const changedProfiles = obj .filter((profile) => !profile.global) .map((profile) => normalizeProfile(profile)); const changedGlobalProfiles = obj .filter((profile) => profile.global) .map((profile) => normalizeProfile(profile)); try { await adminClient.clientPolicies.createProfiles({ profiles: changedProfiles, globalProfiles: changedGlobalProfiles, }); addAlert(t("updateClientProfilesSuccess"), AlertVariant.success); setKey(key + 1); } catch (error) { addError("updateClientProfilesError", error); } } catch (error) { console.warn("Invalid json, ignoring value using {}"); } }; return ( <> {t("profilesConfigType")} setShow(false)} label={t("profilesConfigTypes.formView")} id="formView-profilesView" className="kc-form-radio-btn pf-u-mr-sm pf-u-ml-sm" data-testid="formView-profilesView" /> setShow(true)} label={t("profilesConfigTypes.jsonEditor")} id="jsonEditor-profilesView" className="kc-editor-radio-btn" data-testid="jsonEditor-profilesView" /> {!show ? ( } isRowDisabled={(value) => value.global} actions={[ { title: t("delete"), onRowClick: (profile) => { setSelectedProfile(profile); toggleDeleteDialog(); }, } as Action, ]} columns={[ { name: "name", displayKey: t("name"), cellRenderer: cellFormatter, }, { name: "description", displayKey: t("clientProfileDescription"), }, ]} emptyState={ } /> ) : (
{ setCode(value ?? ""); }} />
)} ); }