Skip to content

Commit 366199d

Browse files
authored
Add global events filter (#5788)
* add event filter * update main view * remove search * update filters query and components * handle boolean filter * handle dropdown and array * add all filters * handle overflow and fix filter list * enable filters in query * fix filter as array * fix brancehs choices * add event types * update query * fix query * fix group events * fix group event for node events * improve display label hook and update link to all activities * add enabled props * rename * update text * update filter modal position * update query fn * update filter style * update test * update test * fix test
1 parent a6dffc7 commit 366199d

16 files changed

+488
-126
lines changed
Lines changed: 62 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,48 @@
11
import graphqlClient from "@/shared/api/graphql/graphqlClientApollo";
22
import { gql } from "@apollo/client";
3+
import { EventType } from "../ui/event";
4+
import { INFRAHUB_EVENT } from "../utils/constants";
5+
6+
export type GlobalEventsFilters = {
7+
hasChildren?: boolean;
8+
eventType?: Array<string>;
9+
primaryNodeIds?: Array<string>;
10+
relatedNodeIds?: Array<string>;
11+
parentIds?: Array<string>;
12+
accountIds?: Array<string>;
13+
since?: Date;
14+
until?: Date;
15+
offset?: number;
16+
limit?: number;
17+
};
318

419
const EVENTS_QUERY = gql`
5-
query GET_ACTIVITIES($ids: [String!], $offset: Int, $limit: Int) {
6-
InfrahubEvent(related_node__ids: $ids, offset: $offset, limit: $limit) {
20+
query GET_ACTIVITIES(
21+
$hasChildren: Boolean
22+
$branches: [String!]
23+
$eventType: [String!]
24+
$primaryNodeIds: [String!]
25+
$relatedNodeIds: [String!]
26+
$parentIds: [String!]
27+
$accountIds: [String!]
28+
$since: DateTime
29+
$until: DateTime
30+
$offset: Int
31+
$limit: Int
32+
) {
33+
InfrahubEvent(
34+
has_children: $hasChildren
35+
branches: $branches
36+
event_type: $eventType
37+
primary_node__ids: $primaryNodeIds
38+
related_node__ids: $relatedNodeIds
39+
parent__ids: $parentIds
40+
account__ids: $accountIds
41+
since: $since
42+
until: $until
43+
offset: $offset
44+
limit: $limit
45+
) {
746
count
847
edges {
948
node {
@@ -17,6 +56,11 @@ const EVENTS_QUERY = gql`
1756
id
1857
kind
1958
}
59+
related_nodes {
60+
id
61+
kind
62+
}
63+
has_children
2064
__typename
2165
... on NodeMutatedEvent {
2266
attributes {
@@ -34,32 +78,30 @@ const EVENTS_QUERY = gql`
3478
}
3579
`;
3680

37-
export function getEventsFromApi({
38-
ids,
39-
offset,
40-
limit,
41-
search,
81+
export async function getEventsFromApi({
4282
branchName,
4383
atDate,
44-
}: {
45-
ids?: Array<string | undefined>;
46-
offset?: number;
47-
limit?: number;
48-
search?: string;
49-
branchName: string;
50-
atDate: Date | null;
51-
}) {
52-
return graphqlClient.query({
84+
...filters
85+
}: GlobalEventsFilters & { branchName?: string; atDate?: Date | null }) {
86+
const { data } = await graphqlClient.query({
5387
query: EVENTS_QUERY,
5488
variables: {
55-
ids,
56-
offset,
57-
limit,
58-
search,
89+
...filters,
5990
},
6091
context: {
6192
branch: branchName,
6293
date: atDate,
6394
},
6495
});
96+
97+
const activities: EventType[] = data?.[INFRAHUB_EVENT]?.edges?.map((edge) => {
98+
return edge.node;
99+
});
100+
101+
const count = data?.data?.[INFRAHUB_EVENT]?.count;
102+
103+
return {
104+
activities,
105+
count,
106+
};
65107
}

frontend/app/src/entities/events/api/get-events.query.ts

Lines changed: 6 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -2,51 +2,24 @@ import { getCurrentBranchName } from "@/entities/branches/domain/get-current-bra
22
import { store } from "@/shared/stores";
33
import { datetimeAtom } from "@/shared/stores/time.atom";
44
import { queryOptions, useQuery } from "@tanstack/react-query";
5-
import { EventType } from "../ui/event";
6-
import { INFRAHUB_EVENT } from "../utils/constants";
7-
import { getEventsFromApi } from "./get-events-from-api";
5+
import { GlobalEventsFilters, getEventsFromApi } from "./get-events-from-api";
86

9-
export function getEventsQueryOptions({
10-
ids,
11-
offset,
12-
limit,
13-
search,
14-
}: { ids?: Array<string | undefined>; offset?: number; limit?: number; search?: string }) {
7+
export function getEventsQueryOptions(filters: GlobalEventsFilters) {
158
const currentBranchName = getCurrentBranchName();
169
const timeMachineDate = store.get(datetimeAtom);
1710

1811
return queryOptions({
19-
queryKey: ["events", ids, offset, limit, search],
12+
queryKey: ["events", filters],
2013
queryFn: () => {
2114
return getEventsFromApi({
22-
ids,
23-
offset,
24-
limit,
25-
search,
15+
...filters,
2616
branchName: currentBranchName,
2717
atDate: timeMachineDate,
2818
});
2919
},
3020
});
3121
}
3222

33-
export const useEvents = ({
34-
ids = [],
35-
offset,
36-
limit,
37-
search,
38-
}: { ids?: Array<string | undefined>; offset?: number; limit?: number; search?: string }) => {
39-
const { data } = useQuery(getEventsQueryOptions({ ids, offset, limit, search }));
40-
41-
const activities: EventType[] = data?.data?.[INFRAHUB_EVENT]?.edges?.map((edge) => {
42-
return edge.node;
43-
});
44-
45-
const count = data?.data?.[INFRAHUB_EVENT]?.count;
46-
47-
return {
48-
...useQuery(getEventsQueryOptions({ ids, offset, limit, search })),
49-
data: activities,
50-
count,
51-
};
23+
export const useEvents = (filters: GlobalEventsFilters) => {
24+
return useQuery(getEventsQueryOptions(filters));
5225
};

frontend/app/src/entities/events/ui/event.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { EventNodeInterface, NodeMutatedEvent } from "@/shared/api/graphql/generated/graphql";
22
import { DateDisplay } from "@/shared/components/display/date-display";
33

4-
import { DisplayLabel } from "@/entities/nodes/object/ui/display-label";
4+
import { NodeLabel } from "@/entities/nodes/object/ui/display-label";
55
import { PropertyRow } from "@/entities/schema/ui/styled";
66
import { CopyToClipboard } from "@/shared/components/buttons/copy-to-clipboard";
77
import { Popover, PopoverContent, PopoverTrigger } from "@/shared/components/ui/popover";
@@ -11,9 +11,11 @@ import {
1111
BRANCH_DELETED_EVENT,
1212
BRANCH_EVENTS,
1313
BRANCH_REBASEDED_EVENT,
14+
GROUP_EVENTS,
1415
NODE_MUTATED_EVENT,
1516
} from "../utils/constants";
1617
import { BranchEvent } from "./branch-event";
18+
import { GroupEvent } from "./group-event";
1719
import { EventAttributes, NodeEvent } from "./node-event";
1820

1921
export type BranchEventType = EventNodeInterface & {
@@ -42,7 +44,7 @@ export const EventDetails = ({ id, event, occurred_at, account_id, ...props }: E
4244
/>
4345
<PropertyRow title="Event" value={event} />
4446
<PropertyRow title="Occured at" value={<DateDisplay date={occurred_at} />} />
45-
{account_id && <PropertyRow title="Account" value={<DisplayLabel id={account_id} />} />}
47+
{account_id && <PropertyRow title="Account" value={<NodeLabel id={account_id} />} />}
4648
{"attributes" in props && (
4749
<PropertyRow title="Changes" value={<EventAttributes attributes={props.attributes} />} />
4850
)}
@@ -63,6 +65,8 @@ export const Event = ({ __typename, ...props }: EventType) => {
6365

6466
{BRANCH_EVENTS.includes(__typename) && <BranchEvent {...props} />}
6567

68+
{GROUP_EVENTS.includes(__typename) && <GroupEvent {...props} />}
69+
6670
<div className="flex justify-between">
6771
<div className="text-xs font-medium text-gray-500 dark:text-neutral-400">
6872
<DateDisplay date={props.occurred_at} />
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import { branchesState } from "@/entities/branches/stores";
2+
import { ScrollArea } from "@/shared/components/ui/scroll-area";
3+
import { useAtomValue } from "jotai";
4+
import { TagGroup, TagList } from "react-aria-components";
5+
import { GlobalFilter } from "./global-filter";
6+
7+
export const GlobalEventsFilters = () => {
8+
const branches = useAtomValue(branchesState);
9+
10+
const FILTERS = [
11+
{
12+
name: "hasChildren",
13+
label: "Has Children",
14+
fieldSchema: {
15+
kind: "Boolean",
16+
},
17+
},
18+
{
19+
name: "branches",
20+
label: "Branch",
21+
fieldSchema: {
22+
kind: "Dropdown",
23+
choices: branches.map((branch) => {
24+
return {
25+
label: branch.name,
26+
name: branch.name,
27+
};
28+
}),
29+
},
30+
},
31+
{
32+
name: "eventType",
33+
label: "Event Type",
34+
fieldSchema: {
35+
kind: "Dropdown",
36+
choices: [
37+
{
38+
label: "Node updated",
39+
name: "NodeMutatedEvent",
40+
},
41+
{
42+
label: "Branch created",
43+
name: "BranchCreatedEvent",
44+
},
45+
{
46+
label: "Branch updated",
47+
name: "BranchUpdatedEvent",
48+
},
49+
{
50+
label: "Branch rebased",
51+
name: "BranchRebasedEvent",
52+
},
53+
{
54+
label: "Standard event",
55+
name: "StandardEvent",
56+
},
57+
],
58+
},
59+
},
60+
{
61+
name: "primaryNodeIds",
62+
label: "Primary Node",
63+
fieldSchema: {
64+
peer: "CoreNode",
65+
},
66+
},
67+
{
68+
name: "relatedNodeIds",
69+
label: "Related Node",
70+
fieldSchema: {
71+
peer: "CoreNode",
72+
},
73+
},
74+
{
75+
name: "accountIds",
76+
label: "Account",
77+
fieldSchema: {
78+
peer: "CoreAccount",
79+
},
80+
},
81+
{
82+
name: "since",
83+
label: "Start Date",
84+
fieldSchema: {
85+
kind: "DateTime",
86+
},
87+
},
88+
{
89+
name: "until",
90+
label: "End Date",
91+
fieldSchema: {
92+
kind: "DateTime",
93+
},
94+
},
95+
];
96+
97+
return (
98+
<ScrollArea scrollX>
99+
<TagGroup className="flex" selectionMode="single">
100+
<TagList className="flex items-center gap-2 py-3">
101+
{FILTERS.map((filter) => {
102+
return <GlobalFilter key={filter.name} {...filter} />;
103+
})}
104+
</TagList>
105+
</TagGroup>
106+
</ScrollArea>
107+
);
108+
};

frontend/app/src/entities/events/ui/global-events.tsx

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,33 @@
11
import { FilterResetButton } from "@/entities/nodes/object/ui/filters/filter-reset-button";
2-
import { FilterSearchInput } from "@/entities/nodes/object/ui/filters/filter-search-input";
32
import ErrorFallback from "@/shared/components/errors/error-fallback";
43
import NoDataFound from "@/shared/components/errors/no-data-found";
54
import Content from "@/shared/components/layout/content";
65
import { Pagination } from "@/shared/components/ui/pagination";
76
import { Spinner } from "@/shared/components/ui/spinner";
7+
import useFilters from "@/shared/hooks/useFilters";
88
import usePagination from "@/shared/hooks/usePagination";
9-
import { useSearch } from "@/shared/hooks/useSearch";
109
import { useEvents } from "../api/get-events.query";
1110
import { Event } from "./global-event";
11+
import { GlobalEventsFilters } from "./global-events-filters";
1212

1313
export const GlobalEvents = () => {
1414
const [pagination] = usePagination();
15-
const [search] = useSearch();
16-
const { isLoading, data, count, error, refetch } = useEvents({ ...pagination, search });
15+
const [filters] = useFilters();
16+
17+
const queryFilters = filters.reduce((acc, filter) => {
18+
if (Array.isArray(filter.value)) {
19+
return {
20+
...acc,
21+
[filter.name.split("__")[0]]: filter.value.map((value) => {
22+
return value.id;
23+
}),
24+
};
25+
}
26+
27+
return { ...acc, [filter.name.split("__")[0]]: filter.value };
28+
}, {});
29+
30+
const { isLoading, data, error, refetch } = useEvents({ ...pagination, ...queryFilters });
1731

1832
if (error) {
1933
return <ErrorFallback error={error} />;
@@ -23,21 +37,20 @@ export const GlobalEvents = () => {
2337
<Content.Card>
2438
<Content.CardTitle
2539
title="Activities"
26-
badgeContent={count}
40+
badgeContent={data?.count}
2741
isReloadLoading={isLoading}
2842
reload={() => refetch()}
2943
/>
3044
<div className="flex flex-col flex-grow gap-2 p-2">
31-
<div className="flex items-center">
32-
<FilterSearchInput placeholder="Search an activity" />
33-
34-
{search.length > 0 && <FilterResetButton />}
45+
<div className="flex items-center gap-2">
46+
<GlobalEventsFilters />
47+
{filters.length > 0 && <FilterResetButton />}
3548
</div>
3649

3750
<div className="flex flex-col gap-2">
38-
{!isLoading && !data?.length && <NoDataFound message="No activity found." />}
51+
{!isLoading && !data?.activities?.length && <NoDataFound message="No activity found." />}
3952

40-
{data?.map((activity) => (
53+
{data?.activities?.map((activity) => (
4154
<Event key={activity.id} {...activity} />
4255
))}
4356
</div>
@@ -48,7 +61,7 @@ export const GlobalEvents = () => {
4861
</div>
4962
)}
5063

51-
<Pagination count={count} />
64+
<Pagination count={data?.count} />
5265
</div>
5366
</Content.Card>
5467
);

0 commit comments

Comments
 (0)