import React, { useMemo, useState, KeyboardEvent } from "react"; import { useTranslation } from "react-i18next"; import { AlertVariant, Button, ButtonVariant, Dropdown, DropdownItem, DropdownToggle, InputGroup, PageSection, TextInput, Toolbar, ToolbarGroup, ToolbarItem, } from "@patternfly/react-core"; import { SearchIcon } from "@patternfly/react-icons"; import type { KeyMetadataRepresentation } from "@keycloak/keycloak-admin-client/lib/defs/keyMetadataRepresentation"; import type ComponentRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentRepresentation"; import type ComponentTypeRepresentation from "@keycloak/keycloak-admin-client/lib/defs/componentTypeRepresentation"; import type { ProviderType } from "../routes/KeyProvider"; import { useServerInfo } from "../../context/server-info/ServerInfoProvider"; import { useAdminClient } from "../../context/auth/AdminClient"; import { useAlerts } from "../../components/alert/Alerts"; import { useConfirmDialog } from "../../components/confirm-dialog/ConfirmDialog"; import { useRealm } from "../../context/realm-context/RealmContext"; import { Link, useRouteMatch } from "react-router-dom"; import { KEY_PROVIDER_TYPE } from "../../util"; import { DraggableTable } from "../../authentication/components/DraggableTable"; import { KeyProviderModal } from "./key-providers/KeyProviderModal"; import useToggle from "../../utils/useToggle"; import "../realm-settings-section.css"; type ComponentData = KeyMetadataRepresentation & { id?: string; providerDescription?: string; name?: string; toggleHidden?: boolean; config?: any; parentId?: string; }; type KeysProvidersTabProps = { realmComponents: ComponentRepresentation[]; refresh: () => void; }; export const KeysProvidersTab = ({ realmComponents, refresh, }: KeysProvidersTabProps) => { const { t } = useTranslation("realm-settings"); const { addAlert, addError } = useAlerts(); const adminClient = useAdminClient(); const { realm } = useRealm(); const { url } = useRouteMatch(); const [searchVal, setSearchVal] = useState(""); const [filteredComponents, setFilteredComponents] = useState( [] ); const [isCreateModalOpen, handleModalToggle] = useToggle(); const serverInfo = useServerInfo(); const keyProviderComponentTypes = serverInfo.componentTypes?.[KEY_PROVIDER_TYPE] ?? []; const providerTypes = keyProviderComponentTypes.map((item) => item.id); const [providerDropdownOpen, setProviderDropdownOpen] = useState(false); const [defaultConsoleDisplayName, setDefaultConsoleDisplayName] = useState(); const [selectedComponent, setSelectedComponent] = useState(); const components = useMemo( () => realmComponents.map((component) => { const provider = keyProviderComponentTypes.find( (componentType: ComponentTypeRepresentation) => component.providerId === componentType.id ); return { ...component, providerDescription: provider?.helpText, }; }), [realmComponents] ); const [toggleDeleteDialog, DeleteConfirm] = useConfirmDialog({ titleKey: "realm-settings:deleteProviderTitle", messageKey: t("deleteProviderConfirm", { provider: selectedComponent?.name, }), continueButtonLabel: "common:delete", continueButtonVariant: ButtonVariant.danger, onConfirm: async () => { try { await adminClient.components.del({ id: selectedComponent!.id!, realm: realm, }); refresh(); addAlert(t("deleteProviderSuccess"), AlertVariant.success); } catch (error) { addError("realm-settings:deleteProviderError", error); } }, }); const onSearch = () => { if (searchVal !== "") { setSearchVal(searchVal); const filteredComponents = components.filter( (component) => component.name?.includes(searchVal) || component.providerId?.includes(searchVal) ); setFilteredComponents(filteredComponents); } else { setSearchVal(""); setFilteredComponents(components); } }; const handleKeyDown = (e: KeyboardEvent) => { if (e.key === "Enter") { onSearch(); } }; const handleInputChange = (value: string) => { setSearchVal(value); }; return ( <> {isCreateModalOpen && defaultConsoleDisplayName && ( { handleModalToggle(); refresh(); }} /> )} setProviderDropdownOpen(val)} isPrimary > {t("addProvider")} } dropdownItems={[ providerTypes.map((item) => ( { handleModalToggle(); setProviderDropdownOpen(false); setDefaultConsoleDisplayName(item as ProviderType); }} data-testid={`option-${item}`} key={item} > {item} )), ]} /> { const updateAll = components.map((component: ComponentData) => { const componentToSave = { ...component }; delete componentToSave.providerDescription; return adminClient.components.update( { id: component.id! }, { ...componentToSave, config: { priority: [ ( itemOrder.length - itemOrder.indexOf(component.id!) + 100 ).toString(), ], }, } ); }); try { await Promise.all(updateAll); refresh(); addAlert(t("saveProviderListSuccess"), AlertVariant.success); } catch (error) { addError("realm-settings:saveProviderError", error); } }} columns={[ { name: "name", displayKey: "realm-settings:name", cellRenderer: (component) => ( {component.name} ), }, { name: "providerId", displayKey: "realm-settings:provider", }, { name: "providerDescription", displayKey: "realm-settings:providerDescription", }, ]} actions={[ { title: t("common:delete"), onClick: (_key, _idx, component) => { setSelectedComponent(component as ComponentRepresentation); toggleDeleteDialog(); }, }, ]} /> ); };