UNPKG

@keycloakify/keycloak-admin-ui

Version:
176 lines 15 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { KeycloakSelect, SelectVariant, TextControl, } from "../ui-shared"; import { CodeEditor, Language } from "@patternfly/react-code-editor"; import { ActionGroup, Button, Chip, ChipGroup, DatePicker, Flex, FlexItem, Form, FormGroup, Modal, ModalVariant, SelectOption, } from "@patternfly/react-core"; import { Table, TableVariant, Tbody, Td, Th, Thead, Tr, cellWidth, } from "@patternfly/react-table"; import { pickBy } from "lodash-es"; import { useMemo, useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { useAdminClient } from "../admin-client"; import DropdownPanel from "../components/dropdown-panel/DropdownPanel"; import { ListEmptyState } from "../ui-shared"; import { KeycloakDataTable } from "../ui-shared"; import { useRealm } from "../context/realm-context/RealmContext"; import { useServerInfo } from "../context/server-info/ServerInfoProvider"; import { prettyPrintJSON } from "../util"; import useFormatDate, { FORMAT_DATE_AND_TIME } from "../utils/useFormatDate"; import { CellResourceLinkRenderer } from "../events/ResourceLinks"; import "./events.css"; const defaultValues = { resourceTypes: [], operationTypes: [], resourcePath: "", dateFrom: "", dateTo: "", authClient: "", authUser: "", authRealm: "", authIpAddress: "", }; const DisplayDialog = ({ titleKey, onClose, children, }) => { const { t } = useTranslation(); return (_jsx(Modal, { variant: ModalVariant.medium, title: t(titleKey), isOpen: true, onClose: onClose, children: children })); }; export const AdminEvents = () => { var _a, _b, _c, _d, _e, _f; const { adminClient } = useAdminClient(); const { t } = useTranslation(); const { realm } = useRealm(); const serverInfo = useServerInfo(); const formatDate = useFormatDate(); const resourceTypes = (_a = serverInfo.enums) === null || _a === void 0 ? void 0 : _a["resourceType"]; const operationTypes = (_b = serverInfo.enums) === null || _b === void 0 ? void 0 : _b["operationType"]; const [key, setKey] = useState(0); const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); const [selectResourceTypesOpen, setSelectResourceTypesOpen] = useState(false); const [selectOperationTypesOpen, setSelectOperationTypesOpen] = useState(false); const [activeFilters, setActiveFilters] = useState({}); const [authEvent, setAuthEvent] = useState(); const [representationEvent, setRepresentationEvent] = useState(); const filterLabels = { resourceTypes: t("resourceTypes"), operationTypes: t("operationTypes"), resourcePath: t("resourcePath"), dateFrom: t("dateFrom"), dateTo: t("dateTo"), authClient: t("client"), authUser: t("userId"), authRealm: t("realm"), authIpAddress: t("ipAddress"), }; const form = useForm({ mode: "onChange", defaultValues, }); const { getValues, reset, formState: { isDirty }, control, } = form; function loader(first, max) { return adminClient.realms.findAdminEvents({ // The admin client wants 'dateFrom' and 'dateTo' to be Date objects, however it cannot actually handle them so we need to cast to any. ...activeFilters, realm, first, max, }); } function submitSearch() { setSearchDropdownOpen(false); commitFilters(); } function resetSearch() { reset(); commitFilters(); } function removeFilter(key) { const formValues = { ...getValues() }; delete formValues[key]; reset({ ...defaultValues, ...formValues }); commitFilters(); } function removeFilterValue(key, valueToRemove) { const formValues = getValues(); const fieldValue = formValues[key]; const newFieldValue = Array.isArray(fieldValue) ? fieldValue.filter((val) => val !== valueToRemove) : fieldValue; reset({ ...formValues, [key]: newFieldValue }); commitFilters(); } function commitFilters() { const newFilters = pickBy(getValues(), (value) => value !== "" || (Array.isArray(value) && value.length > 0)); setActiveFilters(newFilters); setKey(key + 1); } const code = useMemo(() => (representationEvent === null || representationEvent === void 0 ? void 0 : representationEvent.representation) ? prettyPrintJSON(JSON.parse(representationEvent.representation)) : "", [representationEvent === null || representationEvent === void 0 ? void 0 : representationEvent.representation]); return (_jsxs(_Fragment, { children: [authEvent && (_jsx(DisplayDialog, { titleKey: "auth", onClose: () => setAuthEvent(undefined), children: _jsxs(Table, { "aria-label": "authData", "data-testid": "auth-dialog", variant: TableVariant.compact, children: [_jsx(Thead, { children: _jsxs(Tr, { children: [_jsx(Th, { children: t("attribute") }), _jsx(Th, { children: t("value") })] }) }), _jsxs(Tbody, { children: [_jsxs(Tr, { children: [_jsx(Td, { children: t("realm") }), _jsx(Td, { children: (_c = authEvent.authDetails) === null || _c === void 0 ? void 0 : _c.realmId })] }), _jsxs(Tr, { children: [_jsx(Td, { children: t("client") }), _jsx(Td, { children: (_d = authEvent.authDetails) === null || _d === void 0 ? void 0 : _d.clientId })] }), _jsxs(Tr, { children: [_jsx(Td, { children: t("user") }), _jsx(Td, { children: (_e = authEvent.authDetails) === null || _e === void 0 ? void 0 : _e.userId })] }), _jsxs(Tr, { children: [_jsx(Td, { children: t("ipAddress") }), _jsx(Td, { children: (_f = authEvent.authDetails) === null || _f === void 0 ? void 0 : _f.ipAddress })] })] })] }) })), representationEvent && (_jsx(DisplayDialog, { titleKey: "representation", "data-testid": "representation-dialog", onClose: () => setRepresentationEvent(undefined), children: _jsx(CodeEditor, { isLineNumbersVisible: true, isReadOnly: true, code: code, language: Language.json, height: "8rem" }) })), _jsx(KeycloakDataTable, { loader: loader, isPaginated: true, ariaLabelKey: "adminEvents", toolbarItem: _jsx(FormProvider, { ...form, children: _jsxs(Flex, { direction: { default: "column" }, spaceItems: { default: "spaceItemsNone" }, children: [_jsx(FlexItem, { children: _jsx(DropdownPanel, { buttonText: t("searchForAdminEvent"), setSearchDropdownOpen: setSearchDropdownOpen, searchDropdownOpen: searchDropdownOpen, marginRight: "2.5rem", width: "15vw", children: _jsxs(Form, { isHorizontal: true, className: "keycloak__events_search__form", "data-testid": "searchForm", children: [_jsx(FormGroup, { label: t("resourceTypes"), fieldId: "kc-resourceTypes", className: "keycloak__events_search__form_label", children: _jsx(Controller, { name: "resourceTypes", control: control, render: ({ field }) => (_jsx(KeycloakSelect, { className: "keycloak__events_search__type_select", "data-testid": "resource-types-searchField", chipGroupProps: { numChips: 1, expandedText: t("hide"), collapsedText: t("showRemaining"), }, variant: SelectVariant.typeaheadMulti, typeAheadAriaLabel: "Select", onToggle: (isOpen) => setSelectResourceTypesOpen(isOpen), selections: field.value, onSelect: (selectedValue) => { const option = selectedValue.toString(); const changedValue = field.value.includes(option) ? field.value.filter((item) => item !== option) : [...field.value, option]; field.onChange(changedValue); }, onClear: () => { field.onChange([]); }, isOpen: selectResourceTypesOpen, "aria-labelledby": "resourceTypes", chipGroupComponent: _jsx(ChipGroup, { children: field.value.map((chip) => (_jsx(Chip, { onClick: (resource) => { resource.stopPropagation(); field.onChange(field.value.filter((val) => val !== chip)); }, children: chip }, chip))) }), children: resourceTypes === null || resourceTypes === void 0 ? void 0 : resourceTypes.map((option) => (_jsx(SelectOption, { value: option, children: option }, option))) })) }) }), _jsx(FormGroup, { label: t("operationTypes"), fieldId: "kc-operationTypes", className: "keycloak__events_search__form_label", children: _jsx(Controller, { name: "operationTypes", control: control, render: ({ field }) => (_jsx(KeycloakSelect, { className: "keycloak__events_search__type_select", "data-testid": "operation-types-searchField", chipGroupProps: { numChips: 1, expandedText: t("hide"), collapsedText: t("showRemaining"), }, variant: SelectVariant.typeaheadMulti, typeAheadAriaLabel: "Select", onToggle: (isOpen) => setSelectOperationTypesOpen(isOpen), selections: field.value, onSelect: (selectedValue) => { const option = selectedValue.toString(); const changedValue = field.value.includes(option) ? field.value.filter((item) => item !== option) : [...field.value, option]; field.onChange(changedValue); }, onClear: () => { field.onChange([]); }, isOpen: selectOperationTypesOpen, "aria-labelledby": "operationTypes", chipGroupComponent: _jsx(ChipGroup, { children: field.value.map((chip) => (_jsx(Chip, { onClick: (operation) => { operation.stopPropagation(); field.onChange(field.value.filter((val) => val !== chip)); }, children: chip }, chip))) }), children: operationTypes === null || operationTypes === void 0 ? void 0 : operationTypes.map((option) => (_jsx(SelectOption, { value: option, children: option }, option.toString()))) })) }) }), _jsx(TextControl, { name: "resourcePath", label: t("resourcePath") }), _jsx(TextControl, { name: "authRealm", label: t("realm") }), _jsx(TextControl, { name: "authClient", label: t("client") }), _jsx(TextControl, { name: "authUser", label: t("user") }), _jsx(TextControl, { name: "authIpAddress", label: t("ipAddress") }), _jsx(FormGroup, { label: t("dateFrom"), fieldId: "kc-dateFrom", className: "keycloak__events_search__form_label", children: _jsx(Controller, { name: "dateFrom", control: control, render: ({ field }) => (_jsx(DatePicker, { className: "pf-v5-u-w-100", value: field.value, onChange: (_, value) => field.onChange(value), inputProps: { id: "kc-dateFrom" } })) }) }), _jsx(FormGroup, { label: t("dateTo"), fieldId: "kc-dateTo", className: "keycloak__events_search__form_label", children: _jsx(Controller, { name: "dateTo", control: control, render: ({ field }) => (_jsx(DatePicker, { className: "pf-v5-u-w-100", value: field.value, onChange: (_, value) => field.onChange(value), inputProps: { id: "kc-dateTo" } })) }) }), _jsxs(ActionGroup, { children: [_jsx(Button, { variant: "primary", onClick: submitSearch, "data-testid": "search-events-btn", isDisabled: !isDirty, children: t("searchAdminEventsBtn") }), _jsx(Button, { variant: "secondary", onClick: resetSearch, isDisabled: !isDirty, children: t("resetBtn") })] })] }) }) }), _jsx(FlexItem, { children: Object.entries(activeFilters).length > 0 && (_jsx("div", { className: "keycloak__searchChips pf-v5-u-ml-md", children: Object.entries(activeFilters).map((filter) => { const [key, value] = filter; return (_jsx(ChipGroup, { className: "pf-v5-u-mt-md pf-v5-u-mr-md", categoryName: filterLabels[key], isClosable: true, onClick: () => removeFilter(key), children: typeof value === "string" ? (_jsx(Chip, { isReadOnly: true, children: value })) : (value.map((entry) => (_jsx(Chip, { onClick: () => removeFilterValue(key, entry), children: entry }, entry)))) }, key)); }) })) })] }) }), actions: [ { title: t("auth"), onRowClick: (event) => setAuthEvent(event), }, { title: t("representation"), onRowClick: (event) => setRepresentationEvent(event), }, ], columns: [ { name: "time", displayKey: "time", cellRenderer: (row) => formatDate(new Date(row.time), FORMAT_DATE_AND_TIME), }, { name: "resourcePath", displayKey: "resourcePath", cellRenderer: CellResourceLinkRenderer, }, { name: "resourceType", displayKey: "resourceType", }, { name: "operationType", displayKey: "operationType", transforms: [cellWidth(10)], }, { name: "", displayKey: "user", cellRenderer: (event) => { var _a; return ((_a = event.authDetails) === null || _a === void 0 ? void 0 : _a.userId) || ""; }, }, ], emptyState: _jsx(ListEmptyState, { message: t("emptyAdminEvents"), instructions: t("emptyAdminEventsInstructions") }), isSearching: Object.keys(activeFilters).length > 0 }, key)] })); }; //# sourceMappingURL=AdminEvents.js.map