/* eslint-disable */ // @ts-nocheck import type EventRepresentation from "@keycloak/keycloak-admin-client/lib/defs/eventRepresentation"; import type EventType from "@keycloak/keycloak-admin-client/lib/defs/eventTypes"; import { KeycloakDataTable, KeycloakSelect, ListEmptyState, SelectVariant, TextControl, useFetch, } from "../../shared/keycloak-ui-shared"; import { ActionGroup, Button, Chip, ChipGroup, DatePicker, DescriptionList, DescriptionListDescription, DescriptionListGroup, DescriptionListTerm, Flex, FlexItem, Form, FormGroup, Icon, SelectOption, Tooltip, } from "../../shared/@patternfly/react-core"; import { CheckCircleIcon, WarningTriangleIcon } from "../../shared/@patternfly/react-icons"; import { cellWidth } from "../../shared/@patternfly/react-table"; import { pickBy } from "lodash-es"; import { useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { useAdminClient } from "../admin-client"; import { EventsBanners } from "../Banners"; import DropdownPanel from "../components/dropdown-panel/DropdownPanel"; import { useRealm } from "../context/realm-context/RealmContext"; import { toUser } from "../user/routes/User"; import useFormatDate, { FORMAT_DATE_AND_TIME } from "../utils/useFormatDate"; import useLocaleSort from "../utils/useLocaleSort"; import "./events.css"; type UserEventSearchForm = { client: string; dateFrom: string; dateTo: string; user: string; type: EventType[]; ipAddress: string; }; const StatusRow = (event: EventRepresentation) => !event.error ? ( {event.type} ) : ( {event.type} ); const DetailCell = (event: EventRepresentation) => ( {event.details && Object.entries(event.details).map(([key, value]) => ( {key} {value} ))} {event.error && ( error {event.error} )} ); const UserDetailLink = (event: EventRepresentation) => { const { t } = useTranslation(); const { realm } = useRealm(); return ( <> {event.userId && ( {event.userId} )} {!event.userId && t("noUserDetails")} ); }; type UserEventsProps = { user?: string; client?: string; }; export const UserEvents = ({ user, client }: UserEventsProps) => { const { adminClient } = useAdminClient(); const { t } = useTranslation(); const localeSort = useLocaleSort(); const { realm } = useRealm(); const formatDate = useFormatDate(); const [key, setKey] = useState(0); const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); const [selectOpen, setSelectOpen] = useState(false); const [events, setEvents] = useState(); const [userEventsEnabled, setUserEventsEnabled] = useState(); const [activeFilters, setActiveFilters] = useState< Partial >({}); const defaultValues: UserEventSearchForm = { client: client ? client : "", dateFrom: "", dateTo: "", user: user ? user : "", type: [], ipAddress: "", }; const filterLabels: Record = { client: t("client"), dateFrom: t("dateFrom"), dateTo: t("dateTo"), user: t("userId"), type: t("eventType"), ipAddress: t("ipAddress"), }; const form = useForm({ mode: "onChange", defaultValues, }); const { getValues, reset, formState: { isDirty }, control, handleSubmit, } = form; useFetch( () => adminClient.realms.getConfigEvents({ realm }), (events) => { setUserEventsEnabled(events.eventsEnabled!); setEvents(localeSort(events.enabledEventTypes || [], (e) => e)); }, [], ); function loader(first?: number, max?: number) { return adminClient.realms.findEvents({ client, user, // 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 as any), realm, first, max, }); } function onSubmit() { setSearchDropdownOpen(false); commitFilters(); } function resetSearch() { reset(); commitFilters(); } function removeFilter(key: keyof UserEventSearchForm) { const formValues: UserEventSearchForm = { ...getValues() }; delete formValues[key]; reset({ ...defaultValues, ...formValues }); commitFilters(); } function removeFilterValue( key: keyof UserEventSearchForm, valueToRemove: EventType, ) { 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: Partial = pickBy( getValues(), (value) => value !== "" || (Array.isArray(value) && value.length > 0), ); if (user) { delete newFilters.user; } if (client) { delete newFilters.client; } setActiveFilters(newFilters); setKey(key + 1); } const userEventSearchFormDisplay = () => { return (
{!user && ( )} ( setSelectOpen(isOpen)} selections={field.value} onSelect={(selectedValue) => { const option = selectedValue.toString() as EventType; const changedValue = field.value.includes(option) ? field.value.filter( (item: string) => item !== option, ) : [...field.value, option]; field.onChange(changedValue); }} onClear={() => { field.onChange([]); }} isOpen={selectOpen} aria-labelledby={"eventType"} chipGroupComponent={ {field.value.map((chip: string) => ( { event.stopPropagation(); field.onChange( field.value.filter( (val: string) => val !== chip, ), ); }} > {t(`eventTypes.${chip}.name`)} ))} } > {events?.map((option) => ( {t(`eventTypes.${option}.name`)} ))} )} /> {!client && ( )} ( field.onChange(value)} inputProps={{ id: "kc-dateFrom" }} /> )} /> ( field.onChange(value)} inputProps={{ id: "kc-dateTo" }} /> )} />
{Object.entries(activeFilters).length > 0 && (
{Object.entries(activeFilters).map((filter) => { const [key, value] = filter as [ keyof UserEventSearchForm, string | EventType[], ]; if ( (key === "user" && !!user) || (key === "client" && !!client) ) { return null; } return ( removeFilter(key)} isClosable > {typeof value === "string" ? ( {value} ) : ( value.map((entry) => ( removeFilterValue(key, entry)} > {t(`eventTypes.${entry}.name`)} )) )} ); })}
)}
); }; return ( <> {!userEventsEnabled && } event.details !== undefined, cellRenderer: DetailCell, }, ]} isPaginated ariaLabelKey="titleEvents" toolbarItem={userEventSearchFormDisplay()} columns={[ { name: "time", displayKey: "time", cellRenderer: (row) => formatDate(new Date(row.time!), FORMAT_DATE_AND_TIME), }, ...(!user ? [ { name: "userId", cellRenderer: UserDetailLink, }, ] : []), { name: "type", displayKey: "eventType", cellRenderer: StatusRow, }, { name: "ipAddress", displayKey: "ipAddress", transforms: [cellWidth(10)], }, ...(!client ? [ { name: "clientId", displayKey: "client", }, ] : []), ]} emptyState={ setKey(key + 1)} /> } isSearching={Object.keys(activeFilters).length > 0} /> ); };