From 0fbd19638082a44840b1ce0cb616dc9e0d3c160b Mon Sep 17 00:00:00 2001 From: agagancarczyk Date: Wed, 25 Aug 2021 15:19:26 +0100 Subject: [PATCH] Events search fixes (#1056) * events: uncommented test for searching user events * events: added refresh btn and tidied up css * events: increased search form width * events: removed unwanted Divider * events: fixed grey line issue when no events logged, fixed test * events: renamed css classes to make them reusable for admin search * events: added admin events search ui * events: fixed refresh btn on user events tab Co-authored-by: Agnieszka Gancarczyk --- .../admin_console/manage/events/EventsPage.ts | 15 +- src/events/AdminEvents.tsx | 196 ++++++++- src/events/EventsSection.tsx | 381 +++++++++--------- src/events/{events-section.css => events.css} | 27 +- src/events/messages.ts | 7 +- 5 files changed, 405 insertions(+), 221 deletions(-) rename src/events/{events-section.css => events.css} (50%) diff --git a/cypress/support/pages/admin_console/manage/events/EventsPage.ts b/cypress/support/pages/admin_console/manage/events/EventsPage.ts index 431bbbdb16..65aa0c1bf4 100644 --- a/cypress/support/pages/admin_console/manage/events/EventsPage.ts +++ b/cypress/support/pages/admin_console/manage/events/EventsPage.ts @@ -60,15 +60,14 @@ export default class EventsPage { cy.get("table").should("not.have.text", "LOGIN_ERROR"); cy.get("table").should("not.have.text", "LOGOUT"); - // cy.get('[id^=remove_pf]').click(); - // cy.get("table").contains("LOGIN"); + cy.get("[id^=remove_pf]").click(); - // cy.get(this.searchEventDrpDwnBtn).click(); - // cy.getId(this.userIdInputFld).type("11111"); - // cy.get(this.eventsPageTitle).contains("No events logged"); - // cy.getId(this.searchEventsBtn).click(); - // cy.get('[id^=remove_group]').click(); - // cy.get("table").contains("LOGIN"); + cy.get(this.searchEventDrpDwnBtn).click(); + cy.getId(this.userIdInputFld).type("11111"); + cy.getId(this.searchEventsBtn).click(); + cy.get(this.eventsPageTitle).contains("No events logged"); + cy.get("[id^=remove_group]").click(); + cy.get("table").contains("LOGIN"); } shouldDoNoResultsSearch() { diff --git a/src/events/AdminEvents.tsx b/src/events/AdminEvents.tsx index 3e482eeb76..a285ab1e81 100644 --- a/src/events/AdminEvents.tsx +++ b/src/events/AdminEvents.tsx @@ -1,8 +1,17 @@ import { + ActionGroup, Button, + Dropdown, + DropdownToggle, + Flex, + FlexItem, + Form, + FormGroup, Modal, ModalVariant, - ToolbarItem, + Select, + SelectVariant, + TextInput, Tooltip, } from "@patternfly/react-core"; import { @@ -15,18 +24,44 @@ import { import type AdminEventRepresentation from "keycloak-admin/lib/defs/adminEventRepresentation"; import moment from "moment"; import React, { FunctionComponent, useState } from "react"; +import { useForm } from "react-hook-form"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { ListEmptyState } from "../components/list-empty-state/ListEmptyState"; import { KeycloakDataTable } from "../components/table-toolbar/KeycloakDataTable"; import { useAdminClient } from "../context/auth/AdminClient"; import { useRealm } from "../context/realm-context/RealmContext"; +import "./events.css"; type DisplayDialogProps = { titleKey: string; onClose: () => void; }; +type AdminEventSearchForm = { + operationType: string[]; + resourceType: string[]; + resourcePath: string; + dateFrom: string; + dateTo: string; + client: string; + user: string; + realm: string[]; + ipAddress: string; +}; + +const defaultValues: AdminEventSearchForm = { + operationType: [], + resourceType: [], + resourcePath: "", + dateFrom: "", + dateTo: "", + client: "", + user: "", + realm: [], + ipAddress: "", +}; + const DisplayDialog: FunctionComponent = ({ titleKey, onClose, @@ -72,12 +107,23 @@ export const AdminEvents = () => { const { realm } = useRealm(); const [key, setKey] = useState(0); + const [searchDropdownOpen, setSearchDropdownOpen] = useState(false); + const [selectOpen, setSelectOpen] = useState(false); const refresh = () => setKey(new Date().getTime()); const [authEvent, setAuthEvent] = useState(); const [representationEvent, setRepresentationEvent] = useState(); + const { + register, + formState: { isDirty }, + } = useForm({ + shouldUnregister: false, + mode: "onChange", + defaultValues, + }); + const loader = async (first?: number, max?: number, search?: string) => { const params = { first: first!, @@ -111,6 +157,145 @@ export const AdminEvents = () => { ); + const adminEventSearchFormDisplay = () => { + return ( + <> + + + setSearchDropdownOpen(isOpen)} + className="keycloak__events_search_selector_dropdown__toggle" + > + {t("searchForAdminEvent")} + + } + isOpen={searchDropdownOpen} + > +
+ + + + + + + + + + + + + + + + + + + + + + + + +
+
+ +
+
+ + ); + }; + return ( <> {authEvent && ( @@ -139,14 +324,7 @@ export const AdminEvents = () => { loader={loader} isPaginated ariaLabelKey="events:adminEvents" - searchPlaceholderKey="events:searchForEvent" - toolbarItem={ - <> - - - - - } + toolbarItem={adminEventSearchFormDisplay()} actions={[ { title: t("auth"), diff --git a/src/events/EventsSection.tsx b/src/events/EventsSection.tsx index 7484091b36..ce0eb698fb 100644 --- a/src/events/EventsSection.tsx +++ b/src/events/EventsSection.tsx @@ -7,7 +7,6 @@ import { DescriptionListDescription, DescriptionListGroup, DescriptionListTerm, - Divider, Dropdown, DropdownToggle, Flex, @@ -43,7 +42,7 @@ import { useRealm } from "../context/realm-context/RealmContext"; import { toRealmSettings } from "../realm-settings/routes/RealmSettings"; import { toUser } from "../user/routes/User"; import { AdminEvents } from "./AdminEvents"; -import "./events-section.css"; +import "./events.css"; type UserEventSearchForm = { client: string; @@ -170,6 +169,10 @@ export const EventsSection = () => { setKey(key + 1); } + function refresh() { + commitFilters(); + } + const UserDetailLink = (event: EventRepresentation) => ( { ); - return ( - <> - - If you want to configure user events, Admin events or Event - listeners, please enter - - {t("eventConfig")} - - page realm settings to configure. - - } - divider={false} - /> - - - {t("userEvents")}} - > - - - setSearchDropdownOpen(isOpen)} - className="keycloak__user_events_search_selector_dropdown__toggle" - > - {t("searchForEvent")} - - } - isOpen={searchDropdownOpen} + const userEventSearchFormDisplay = () => { + return ( + <> + + + setSearchDropdownOpen(isOpen)} + className="keycloak__events_search_selector_dropdown__toggle" > -
- - - - - void; - value: EventType[]; - }) => ( - setSelectOpen(isOpen)} + selections={value} + onSelect={(_, selectedValue) => { + const option = selectedValue.toString() as EventType; + const changedValue = value.includes(option) + ? value.filter((item) => item !== option) + : [...value, option]; - onChange(changedValue); - }} - onClear={(event) => { - event.stopPropagation(); - onChange([]); - }} - isOpen={selectOpen} - aria-labelledby={"eventType"} - chipGroupComponent={ - - {value.map((chip) => ( - { - event.stopPropagation(); - - onChange( - value.filter((val) => val !== chip) - ); - }} - > - {chip} - - ))} - - } - > - {events?.enabledEventTypes?.map((option) => ( - + onChange(changedValue); + }} + onClear={(event) => { + event.stopPropagation(); + onChange([]); + }} + isOpen={selectOpen} + aria-labelledby={"eventType"} + chipGroupComponent={ + + {value.map((chip) => ( + { + event.stopPropagation(); + onChange(value.filter((val) => val !== chip)); + }} + > + {chip} + ))} - - )} - /> - - - - - - - - - - - - - -
-
-
-
+ {events?.enabledEventTypes?.map((option) => ( + + ))} + + )} + /> + + + + + + + + + + + + + + +
+ +
+ {Object.entries(activeFilters).length > 0 && (
{Object.entries(activeFilters).map((filter) => { @@ -396,6 +383,34 @@ export const EventsSection = () => { })}
)} +
+
+ + ); + }; + + return ( + <> + + If you want to configure user events, Admin events or Event + listeners, please enter + + {t("eventConfig")} + + page realm settings to configure. + + } + divider={false} + /> + + + {t("userEvents")}} + >
{ ]} isPaginated ariaLabelKey="events:title" + toolbarItem={userEventSearchFormDisplay()} columns={[ { name: "time", @@ -438,7 +454,6 @@ export const EventsSection = () => { ]} emptyState={
-