Skip to content

Commit 51f0da3

Browse files
authored
Node filter for global activities and UI improvements (#5896)
* set current branch in filter * set current branch in filter + update event type filter * add event type * fix param * update ui * add kind filter form for filter * add new group event properties * ui + lint * temporary fix for global events * udpate labels * update ui for column display * fix combobox list * lint
1 parent 2775c1d commit 51f0da3

File tree

12 files changed

+431
-135
lines changed

12 files changed

+431
-135
lines changed

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

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,16 @@ const EVENTS_QUERY = gql`
9090
... on BranchRebasedEvent {
9191
payload
9292
}
93+
... on GroupEvent {
94+
ancestors {
95+
id
96+
kind
97+
}
98+
members {
99+
id
100+
kind
101+
}
102+
}
93103
}
94104
}
95105
}

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

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { EventNodeInterface, NodeMutatedEvent } from "@/shared/api/graphql/generated/graphql";
1+
import {
2+
EventNodeInterface,
3+
GroupEvent,
4+
NodeMutatedEvent,
5+
} from "@/shared/api/graphql/generated/graphql";
26
import { DateDisplay } from "@/shared/components/display/date-display";
37

48
import { ACCOUNT_OBJECT } from "@/config/constants";
@@ -33,7 +37,7 @@ export type NodeEventType = NodeMutatedEvent & {
3337
__typename: typeof NODE_MUTATED_EVENT;
3438
};
3539

36-
export type EventType = BranchEventType | NodeEventType;
40+
export type EventType = BranchEventType | NodeEventType | GroupEvent;
3741

3842
export const EventDetails = ({
3943
id,
@@ -42,6 +46,8 @@ export const EventDetails = ({
4246
account_id,
4347
primary_node,
4448
related_nodes,
49+
ancestors,
50+
members,
4551
...props
4652
}: EventType) => {
4753
return (
@@ -105,6 +111,48 @@ export const EventDetails = ({
105111
}
106112
/>
107113
)}
114+
{!!ancestors?.length && (
115+
<PropertyRow
116+
title="Related Nodes"
117+
value={
118+
<div className="flex flex-col items-end gap-1">
119+
{ancestors.map((node) => {
120+
return (
121+
<Link
122+
key={node.id}
123+
to={constructPath(`/${node.kind}/${node.id}`, [
124+
{ name: QSP.BRANCH, value: props.branch },
125+
])}
126+
>
127+
<NodeLabel id={node?.id} />
128+
</Link>
129+
);
130+
})}
131+
</div>
132+
}
133+
/>
134+
)}
135+
{!!members?.length && (
136+
<PropertyRow
137+
title="Related Nodes"
138+
value={
139+
<div className="flex flex-col items-end gap-1">
140+
{members.map((node) => {
141+
return (
142+
<Link
143+
key={node.id}
144+
to={constructPath(`/${node.kind}/${node.id}`, [
145+
{ name: QSP.BRANCH, value: props.branch },
146+
])}
147+
>
148+
<NodeLabel id={node?.id} />
149+
</Link>
150+
);
151+
})}
152+
</div>
153+
}
154+
/>
155+
)}
108156
{"attributes" in props && (
109157
<PropertyRow title="Changes" value={<EventAttributes attributes={props.attributes} />} />
110158
)}
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { KindComboboxList } from "@/entities/nodes/object/ui/filters/kind-combobox-list";
2+
import { schemaKindLabelState } from "@/entities/schema/stores/schemaKindLabel.atom";
3+
import { Combobox, ComboboxContent } from "@/shared/components/ui/combobox";
4+
import { PopoverTrigger } from "@/shared/components/ui/popover";
5+
import { inputStyle } from "@/shared/components/ui/style";
6+
import { classNames } from "@/shared/utils/common";
7+
import { Icon } from "@iconify-icon/react";
8+
import { useAtomValue } from "jotai";
9+
10+
export function FilterKindSelect({
11+
value,
12+
onChange,
13+
}: { value: string | null; onChange: (value: string) => void }) {
14+
const schemaKindLabel = useAtomValue(schemaKindLabelState);
15+
16+
return (
17+
<Combobox defaultOpen>
18+
Kind:
19+
<PopoverTrigger asChild>
20+
<div
21+
className={classNames(
22+
inputStyle,
23+
"has-[>:last-child:focus]:outline-none has-[>:last-child:focus]:ring-2 has-[>:last-child:focus]:ring-custom-blue-600/25 has-[>:last-child:focus]:border-custom-blue-600",
24+
"cursor-pointer min-w-[132px] max-w-[300px]"
25+
)}
26+
>
27+
<div className="flex-grow flex flex-wrap gap-2">{value && schemaKindLabel[value]}</div>
28+
29+
<button type="button" className="text-gray-600 outline-none w-3.5 h-3.5">
30+
<Icon icon="mdi:unfold-more-horizontal" />
31+
</button>
32+
</div>
33+
</PopoverTrigger>
34+
<ComboboxContent fitTriggerWidth={false}>
35+
<KindComboboxList
36+
onSelect={(kind) => {
37+
onChange(kind);
38+
}}
39+
/>
40+
</ComboboxContent>
41+
</Combobox>
42+
);
43+
}

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

Lines changed: 24 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,44 +13,43 @@ export const Event = ({ __typename, ...props }: EventType) => {
1313
return (
1414
<div
1515
className={classNames(
16-
"grid grid-cols-3 relative gap-8 rounded-md shadow-sm transition-all border",
17-
"bg-gray-50"
16+
"grid grid-cols-8 relative gap-2 p-2",
17+
"rounded-md shadow-sm transition-all border bg-gray-50"
1818
)}
1919
>
20-
<div className="col-span-2 p-2.5">
20+
<div className="flex items-center text-xs font-medium text-gray-500 whitespace-nowrap">
21+
<Tooltip enabled content={props.occurred_at}>
22+
<span>{format(new Date(props.occurred_at), "MMM dd, HH:mm:ss")}</span>
23+
</Tooltip>
24+
</div>
25+
26+
<div className="col-span-5 flex item-center gap-4 overflow-hidden">
2127
{"attributes" in props && <NodeEvent {...props} />}
2228

2329
{BRANCH_EVENTS.includes(__typename) && <BranchEvent {...props} />}
2430

2531
{STANDARD_EVENTS.includes(__typename) && <StandardEvent {...props} />}
2632
</div>
2733

28-
<div className="grid grid-cols-3 col-span-1 items-center text-right p-2.5 relative">
29-
<div className="text-xs font-medium text-gray-500 flex items-center gap-1">
30-
{props.has_children && (
31-
<Tooltip enabled content="Contains sub activities">
32-
<Icon
33-
icon={"mdi:subtasks"}
34-
className="absolute -left-8 rounded-full text-custom-blue-500 bg-custom-blue-500/10 p-1.5"
35-
/>
36-
</Tooltip>
37-
)}
38-
39-
<Icon icon={"mdi:source-branch"} />
40-
{props.branch}
41-
</div>
42-
43-
<div className="flex text-xs font-medium text-gray-500 whitespace-nowrap">
44-
{props.occurred_at && (
45-
<Tooltip enabled content={props.occurred_at}>
46-
<span>{format(new Date(props.occurred_at), "MMM dd - HH:mm:ss")}</span>
47-
</Tooltip>
48-
)}
49-
</div>
34+
<div className="text-xs font-medium text-gray-500 flex items-center gap-1">
35+
<Icon icon={"mdi:source-branch"} />
5036

37+
{props.branch}
38+
</div>
39+
40+
<div className="relative">
5141
<Link to={`/activities/${props.id}`} className="text-xs text-gray-500">
5242
View more
5343
</Link>
44+
45+
{props.has_children && (
46+
<Tooltip enabled content="Contains sub activities">
47+
<Icon
48+
icon={"mdi:subtasks"}
49+
className="absolute right-2 rounded-full text-custom-blue-500 bg-custom-blue-500/10 p-1.5"
50+
/>
51+
</Tooltip>
52+
)}
5453
</div>
5554
</div>
5655
);

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

Lines changed: 46 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ import { branchesState } from "@/entities/branches/stores";
22
import { ScrollArea } from "@/shared/components/ui/scroll-area";
33
import { useAtomValue } from "jotai";
44
import { TagGroup, TagList } from "react-aria-components";
5-
import { FILTERS } from "../utils/constants";
5+
import { EVENT_TYPE_CHOICES } from "../utils/constants";
66
import { GlobalBranchFilter } from "./global-branch-filter";
77
import { GlobalFilter } from "./global-filter";
8+
import { GlobalKindFilter } from "./global-kind-filter";
89

910
export const GlobalEventsFilters = () => {
1011
const branches = useAtomValue(branchesState);
@@ -27,9 +28,50 @@ export const GlobalEventsFilters = () => {
2728
}}
2829
/>
2930

30-
{FILTERS.map((filter) => {
31-
return <GlobalFilter key={filter.name} {...filter} />;
32-
})}
31+
<GlobalFilter
32+
name="eventType"
33+
label="Event Type"
34+
fieldSchema={{
35+
kind: "Dropdown",
36+
choices: EVENT_TYPE_CHOICES,
37+
}}
38+
/>
39+
40+
<GlobalFilter
41+
name="hasChildren"
42+
label="Has Children"
43+
fieldSchema={{
44+
kind: "Boolean",
45+
}}
46+
/>
47+
48+
<GlobalKindFilter name="primaryNodeIds" label="Primary Node" />
49+
50+
<GlobalKindFilter name="relatedNodeIds" label="Related Node" />
51+
52+
<GlobalFilter
53+
name="accountIds"
54+
label="Account"
55+
fieldSchema={{
56+
peer: "CoreAccount",
57+
}}
58+
/>
59+
60+
<GlobalFilter
61+
name="since"
62+
label="Start Date"
63+
fieldSchema={{
64+
kind: "DateTime",
65+
}}
66+
/>
67+
68+
<GlobalFilter
69+
name="until"
70+
label="End Date"
71+
fieldSchema={{
72+
kind: "DateTime",
73+
}}
74+
/>
3375
</TagList>
3476
</TagGroup>
3577
</ScrollArea>

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

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ export const GlobalEvents = () => {
3434
},
3535
});
3636

37-
const flatData = React.useMemo(() => data?.pages?.flat() ?? [], [data]);
37+
const flatData = React.useMemo(() => data?.pages?.flat()?.filter(Boolean) ?? [], [data]);
3838

3939
if (error) {
4040
return <ErrorScreen message={error.message} />;
@@ -43,9 +43,18 @@ export const GlobalEvents = () => {
4343
return (
4444
<Content.Card className="relative">
4545
<Content.CardTitle title="Activities" isReloadLoading={isLoading} reload={() => refetch()} />
46-
<div className="flex items-center gap-2 sticky top-0 bg-white z-10 p-2">
47-
<GlobalEventsFilters />
48-
{filters.length > 0 && <FilterResetButton />}
46+
<div className="flex flex-col gap-2 sticky top-0 bg-white z-10 p-2">
47+
<div className="flex items-center gap-2">
48+
<GlobalEventsFilters />
49+
{filters.length > 0 && <FilterResetButton />}
50+
</div>
51+
52+
<div className="grid grid-cols-8 gap-2 text-xs text-gray-500 font-semibold px-2">
53+
<span>Date</span>
54+
<span className="col-span-5">Event</span>
55+
<span>Branch</span>
56+
<span>Action</span>
57+
</div>
4958
</div>
5059

5160
<div className="flex flex-col flex-grow gap-2 p-2 bg-white z-30">

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

Lines changed: 17 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -34,21 +34,23 @@ export function GlobalFilterForm({ name, fieldSchema, onSuccess }: GlobalFilterF
3434
};
3535

3636
return (
37-
<Form
38-
className="space-y-0 flex items-center gap-2"
39-
onSubmit={(formData) => {
40-
handleSubmit(formData);
41-
}}
42-
>
43-
<FormField
44-
name="filter"
45-
defaultValue={currentFilter?.value}
46-
render={({ field }) => {
47-
return <DynamicFilterInput {...field} fieldSchema={fieldSchema} />;
37+
<div className="flex items-center gap-4 min-w-64">
38+
<Form
39+
className="space-y-0 flex items-center gap-2"
40+
onSubmit={(formData) => {
41+
handleSubmit(formData);
4842
}}
49-
/>
50-
51-
<FormSubmit>Apply</FormSubmit>
52-
</Form>
43+
>
44+
<FormField
45+
name="filter"
46+
defaultValue={currentFilter?.value}
47+
render={({ field }) => {
48+
return <DynamicFilterInput {...field} fieldSchema={fieldSchema} />;
49+
}}
50+
/>
51+
52+
<FormSubmit>Apply</FormSubmit>
53+
</Form>
54+
</div>
5355
);
5456
}

0 commit comments

Comments
 (0)