Skip to content

Commit 177fe68

Browse files
committed
feat(RHINENG-20424): add incident id filter to the incidents page
1 parent 1cac1ac commit 177fe68

File tree

5 files changed

+150
-41
lines changed

5 files changed

+150
-41
lines changed

web/src/components/Incidents/IncidentsPage.tsx

Lines changed: 74 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ import {
3030
} from './processIncidents';
3131
import {
3232
filterIncident,
33+
getIncidentIdOptions,
3334
onDeleteGroupIncidentFilterChip,
3435
onDeleteIncidentFilterChip,
3536
onIncidentFiltersSelect,
@@ -58,7 +59,7 @@ import IncidentsChart from './IncidentsChart/IncidentsChart';
5859
import AlertsChart from './AlertsChart/AlertsChart';
5960
import { usePatternFlyTheme } from '../hooks/usePatternflyTheme';
6061
import { MonitoringState } from 'src/reducers/observe';
61-
import { Incident } from './model';
62+
import { Incident, IncidentsPageFiltersExpandedState } from './model';
6263
import { useAlertsPoller } from '../hooks/useAlertsPoller';
6364
import { Rule } from '@openshift-console/dynamic-plugin-sdk';
6465
import IncidentFilterToolbarItem, { severityOptions, stateOptions } from './ToolbarItemFilter';
@@ -84,24 +85,30 @@ const IncidentsPage = () => {
8485
>([]);
8586
const [hideCharts, setHideCharts] = useState(false);
8687

87-
const [daysFilterIsExpanded, setDaysFilterIsExpanded] = useState(false);
88-
const [filterTypeIsExpanded, setFilterTypeIsExpanded] = useState(false);
89-
const [severityFilterExpanded, setSeverityFilterExpanded] = useState(false);
90-
const [stateFilterExpanded, setStateFilterExpanded] = useState(false);
88+
const [filtersExpanded, setFiltersExpanded] = useState<IncidentsPageFiltersExpandedState>({
89+
severity: false,
90+
state: false,
91+
groupId: false,
92+
});
9193

92-
const onFilterTypeToggle = (ev) => {
93-
ev.stopPropagation();
94-
setFilterTypeIsExpanded(!filterTypeIsExpanded);
95-
};
96-
const onSeverityFilterToggle = (ev) => {
97-
ev.stopPropagation();
98-
setSeverityFilterExpanded(!severityFilterExpanded);
99-
};
100-
const onStateFilterToggle = (ev) => {
94+
const [filterTypeExpanded, setFilterTypeExpanded] = useState({
95+
filterType: false,
96+
});
97+
98+
const onFilterToggle = (
99+
ev: React.MouseEvent,
100+
filterName: keyof IncidentsPageFiltersExpandedState | 'filterType',
101+
setter,
102+
) => {
101103
ev.stopPropagation();
102-
setStateFilterExpanded(!stateFilterExpanded);
104+
setter((prevFilters) => ({
105+
...prevFilters,
106+
[filterName]: !prevFilters[filterName],
107+
}));
103108
};
104109

110+
const [daysFilterIsExpanded, setDaysFilterIsExpanded] = useState(false);
111+
105112
const onToggleClick = (ev) => {
106113
ev.stopPropagation();
107114
setDaysFilterIsExpanded(!daysFilterIsExpanded);
@@ -146,6 +153,7 @@ const IncidentsPage = () => {
146153
days: urlParams.days ? urlParams.days : ['7 days'],
147154
severity: urlParams.severity ? urlParams.severity : [],
148155
state: urlParams.state ? urlParams.state : [],
156+
groupId: urlParams.groupId ? urlParams.groupId : [],
149157
},
150158
}),
151159
);
@@ -313,6 +321,8 @@ const IncidentsPage = () => {
313321
setDaysFilterIsExpanded(false);
314322
};
315323

324+
const incidentIdFilterOptions = incidents ? getIncidentIdOptions(incidents) : [];
325+
316326
return (
317327
<>
318328
<Helmet>
@@ -345,19 +355,22 @@ const IncidentsPage = () => {
345355
<ToolbarItem>
346356
<Select
347357
aria-label="Filter type selection"
348-
isOpen={filterTypeIsExpanded}
358+
isOpen={filterTypeExpanded.filterType}
349359
role="menu"
350360
selected={incidentPageFilterTypeSelected}
351-
onOpenChange={(isOpen) => setFilterTypeIsExpanded(isOpen)}
352-
onSelect={(event, selection) =>
353-
dispatch(setIncidentPageFilterType({ incidentPageFilterType: selection }))
361+
onOpenChange={(isOpen) =>
362+
setFiltersExpanded((prev) => ({ ...prev, filterType: isOpen }))
354363
}
364+
onSelect={(event, selection) => {
365+
dispatch(setIncidentPageFilterType({ incidentPageFilterType: selection }));
366+
setFilterTypeExpanded((prev) => ({ ...prev, filterType: false }));
367+
}}
355368
shouldFocusToggleOnSelect
356369
toggle={(toggleRef) => (
357370
<MenuToggle
358371
ref={toggleRef}
359-
onClick={onFilterTypeToggle}
360-
isExpanded={filterTypeIsExpanded}
372+
onClick={(ev) => onFilterToggle(ev, 'filterType', setFilterTypeExpanded)}
373+
isExpanded={filterTypeExpanded.filterType}
361374
icon={<FilterIcon />}
362375
>
363376
{incidentPageFilterTypeSelected}
@@ -376,6 +389,12 @@ const IncidentsPage = () => {
376389
>
377390
State
378391
</SelectOption>
392+
<SelectOption
393+
value="Incident ID"
394+
isSelected={incidentPageFilterTypeSelected?.includes('Incident ID')}
395+
>
396+
Incident ID
397+
</SelectOption>
379398
</Select>
380399
</ToolbarItem>
381400
<ToolbarItem>
@@ -386,10 +405,14 @@ const IncidentsPage = () => {
386405
incidentsActiveFilters={incidentsActiveFilters}
387406
onDeleteIncidentFilterChip={onDeleteIncidentFilterChip}
388407
onDeleteGroupIncidentFilterChip={onDeleteGroupIncidentFilterChip}
389-
incidentFilterIsExpanded={severityFilterExpanded}
408+
incidentFilterIsExpanded={filtersExpanded.severity}
390409
onIncidentFiltersSelect={onIncidentFiltersSelect}
391-
setIncidentIsExpanded={setSeverityFilterExpanded}
392-
onIncidentFilterToggle={onSeverityFilterToggle}
410+
setIncidentIsExpanded={(isExpanded) =>
411+
setFiltersExpanded((prev) => ({ ...prev, severity: isExpanded }))
412+
}
413+
onIncidentFilterToggle={(ev) =>
414+
onFilterToggle(ev, 'severity', setFiltersExpanded)
415+
}
393416
dispatch={dispatch}
394417
showToolbarItem={incidentPageFilterTypeSelected?.includes('Severity')}
395418
/>
@@ -402,14 +425,38 @@ const IncidentsPage = () => {
402425
incidentsActiveFilters={incidentsActiveFilters}
403426
onDeleteIncidentFilterChip={onDeleteIncidentFilterChip}
404427
onDeleteGroupIncidentFilterChip={onDeleteGroupIncidentFilterChip}
405-
incidentFilterIsExpanded={stateFilterExpanded}
428+
incidentFilterIsExpanded={filtersExpanded.state}
406429
onIncidentFiltersSelect={onIncidentFiltersSelect}
407-
setIncidentIsExpanded={setStateFilterExpanded}
408-
onIncidentFilterToggle={onStateFilterToggle}
430+
setIncidentIsExpanded={(isExpanded) =>
431+
setFiltersExpanded((prev) => ({ ...prev, state: isExpanded }))
432+
}
433+
onIncidentFilterToggle={(ev) =>
434+
onFilterToggle(ev, 'state', setFiltersExpanded)
435+
}
409436
dispatch={dispatch}
410437
showToolbarItem={incidentPageFilterTypeSelected?.includes('State')}
411438
/>
412439
</ToolbarItem>
440+
<ToolbarItem>
441+
<IncidentFilterToolbarItem
442+
categoryName="Incident ID"
443+
toggleLabel="Incident ID filters"
444+
options={incidentIdFilterOptions}
445+
incidentsActiveFilters={incidentsActiveFilters}
446+
onDeleteIncidentFilterChip={onDeleteIncidentFilterChip}
447+
onDeleteGroupIncidentFilterChip={onDeleteGroupIncidentFilterChip}
448+
incidentFilterIsExpanded={filtersExpanded.groupId}
449+
onIncidentFiltersSelect={onIncidentFiltersSelect}
450+
setIncidentIsExpanded={(isExpanded) =>
451+
setFiltersExpanded((prev) => ({ ...prev, incidentId: isExpanded }))
452+
}
453+
onIncidentFilterToggle={(ev) =>
454+
onFilterToggle(ev, 'groupId', setFiltersExpanded)
455+
}
456+
dispatch={dispatch}
457+
showToolbarItem={incidentPageFilterTypeSelected?.includes('Incident ID')}
458+
/>
459+
</ToolbarItem>
413460
</ToolbarGroup>
414461
<ToolbarItem align={{ default: 'alignEnd' }}>
415462
<Stack>

web/src/components/Incidents/ToolbarItemFilter.tsx

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,18 +9,20 @@ import {
99
Badge,
1010
} from '@patternfly/react-core';
1111
import FilterIcon from '@patternfly/react-icons/dist/js/icons/filter-icon';
12-
import { isIncidentFilter } from './utils'; // Assuming this utility function exists
12+
import { getFilterKey } from './utils'; // Assuming this utility function exists
1313

1414
interface IncidentFilterToolbarItemProps {
1515
categoryName: string;
1616
toggleLabel: string;
1717
options: {
1818
value: string;
19-
description: string;
19+
description?: string;
2020
}[];
2121
incidentsActiveFilters: {
2222
severity: string[];
2323
state: string[];
24+
days: string[];
25+
groupId: string[];
2426
};
2527
onDeleteIncidentFilterChip: (
2628
category: string,
@@ -61,9 +63,9 @@ const IncidentFilterToolbarItem: React.FC<IncidentFilterToolbarItemProps> = ({
6163
<ToolbarItem>
6264
<ToolbarFilter
6365
showToolbarItem={showToolbarItem}
64-
labels={incidentsActiveFilters[categoryName.toLowerCase()]}
66+
labels={incidentsActiveFilters[getFilterKey(categoryName)]}
6567
deleteLabel={(category, chip) => {
66-
if (isIncidentFilter(chip) && typeof category === 'string') {
68+
if (typeof category === 'string' && typeof chip === 'string') {
6769
onDeleteIncidentFilterChip(category, chip, incidentsActiveFilters, dispatch);
6870
}
6971
}}
@@ -77,9 +79,9 @@ const IncidentFilterToolbarItem: React.FC<IncidentFilterToolbarItemProps> = ({
7779
role="menu"
7880
aria-label="Filters"
7981
isOpen={incidentFilterIsExpanded}
80-
selected={incidentsActiveFilters[categoryName.toLowerCase()]}
82+
selected={incidentsActiveFilters[getFilterKey(categoryName)]}
8183
onSelect={(event, selection) => {
82-
if (isIncidentFilter(selection)) {
84+
if (typeof selection === 'string') {
8385
onIncidentFiltersSelect(
8486
event,
8587
selection,
@@ -97,9 +99,9 @@ const IncidentFilterToolbarItem: React.FC<IncidentFilterToolbarItemProps> = ({
9799
isExpanded={incidentFilterIsExpanded}
98100
icon={<FilterIcon />}
99101
badge={
100-
Object.entries(incidentsActiveFilters[categoryName.toLowerCase()]).length > 0 ? (
102+
Object.entries(incidentsActiveFilters[getFilterKey(categoryName)]).length > 0 ? (
101103
<Badge isRead>
102-
{Object.entries(incidentsActiveFilters[categoryName.toLowerCase()]).length}
104+
{Object.entries(incidentsActiveFilters[getFilterKey(categoryName)]).length}
103105
</Badge>
104106
) : undefined
105107
}
@@ -114,10 +116,10 @@ const IncidentFilterToolbarItem: React.FC<IncidentFilterToolbarItemProps> = ({
114116
<SelectOption
115117
key={option.value}
116118
value={option.value}
117-
isSelected={incidentsActiveFilters[categoryName.toLowerCase()].includes(
119+
isSelected={(incidentsActiveFilters[getFilterKey(categoryName)] ?? []).includes(
118120
option.value,
119121
)}
120-
description={option.description}
122+
description={option?.description}
121123
hasCheckbox
122124
>
123125
{option.value}

web/src/components/Incidents/model.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,3 +62,9 @@ export type IncidentFiltersCombined = {
6262
severity?: Array<string>;
6363
state?: Array<string>;
6464
};
65+
66+
export type IncidentsPageFiltersExpandedState = {
67+
severity: boolean;
68+
state: boolean;
69+
groupId: boolean;
70+
};

web/src/components/Incidents/utils.ts

Lines changed: 56 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -352,7 +352,7 @@ export function filterIncident(filters: IncidentFiltersCombined, incidents: Arra
352352

353353
export const onDeleteIncidentFilterChip = (
354354
type: string,
355-
id: IncidentFilters | undefined,
355+
id: IncidentFilters | string,
356356
filters: IncidentFiltersCombined,
357357
setFilters,
358358
) => {
@@ -363,6 +363,7 @@ export const onDeleteIncidentFilterChip = (
363363
severity: filters.severity,
364364
days: filters.days,
365365
state: filters.state.filter((fil) => fil !== id),
366+
groupId: filters.groupId,
366367
},
367368
}),
368369
);
@@ -374,6 +375,19 @@ export const onDeleteIncidentFilterChip = (
374375
severity: filters.severity.filter((fil) => fil !== id),
375376
days: filters.days,
376377
state: filters.state,
378+
groupId: filters.groupId,
379+
},
380+
}),
381+
);
382+
}
383+
if (type === 'Incident ID') {
384+
setFilters(
385+
setIncidentsActiveFilters({
386+
incidentsActiveFilters: {
387+
severity: filters.severity,
388+
days: filters.days,
389+
state: filters.state,
390+
groupId: filters.groupId.filter((fil) => fil !== id),
377391
},
378392
}),
379393
);
@@ -392,6 +406,7 @@ export const onDeleteGroupIncidentFilterChip = (
392406
severity: filters.severity,
393407
days: filters.days,
394408
state: [],
409+
groupId: filters.groupId,
395410
},
396411
}),
397412
);
@@ -402,6 +417,18 @@ export const onDeleteGroupIncidentFilterChip = (
402417
severity: [],
403418
days: filters.days,
404419
state: filters.state,
420+
groupId: filters.groupId,
421+
},
422+
}),
423+
);
424+
} else if (category === 'Incident ID') {
425+
setFilters(
426+
setIncidentsActiveFilters({
427+
incidentsActiveFilters: {
428+
severity: filters.severity,
429+
days: filters.days,
430+
state: filters.state,
431+
groupId: [],
405432
},
406433
}),
407434
);
@@ -412,6 +439,7 @@ export const onDeleteGroupIncidentFilterChip = (
412439
severity: [],
413440
days: filters.days,
414441
state: [],
442+
groupId: [],
415443
},
416444
}),
417445
);
@@ -472,17 +500,22 @@ export const onIncidentFiltersSelect = (
472500

473501
const onSelect = (event, selection, dispatch, incidentsActiveFilters, filterCategoryType) => {
474502
const checked = event.target.checked;
503+
let effectiveFilterType = filterCategoryType;
504+
505+
if (effectiveFilterType === 'incident id') {
506+
effectiveFilterType = 'groupId';
507+
}
475508

476509
dispatch(() => {
477-
const targetArray = incidentsActiveFilters[filterCategoryType] || [];
510+
const targetArray = incidentsActiveFilters[effectiveFilterType] || [];
478511
const newFilters = { ...incidentsActiveFilters };
479512

480513
if (checked) {
481514
if (!targetArray.includes(selection)) {
482-
newFilters[filterCategoryType] = [...targetArray, selection];
515+
newFilters[effectiveFilterType] = [...targetArray, selection];
483516
}
484517
} else {
485-
newFilters[filterCategoryType] = targetArray.filter((value) => value !== selection);
518+
newFilters[effectiveFilterType] = targetArray.filter((value) => value !== selection);
486519
}
487520

488521
dispatch(
@@ -508,3 +541,22 @@ export const parseUrlParams = (search) => {
508541

509542
return result;
510543
};
544+
545+
export const getIncidentIdOptions = (incidents: Array<Incident>) => {
546+
const uniqueIds = new Set<string>();
547+
incidents.forEach((incident) => {
548+
if (incident.group_id) {
549+
uniqueIds.add(incident.group_id);
550+
}
551+
});
552+
return Array.from(uniqueIds).map((id) => ({
553+
value: id,
554+
}));
555+
};
556+
557+
export const getFilterKey = (categoryName: string): string => {
558+
if (categoryName === 'Incident ID') {
559+
return 'groupId';
560+
}
561+
return categoryName.toLowerCase();
562+
};

web/src/reducers/observe.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,13 @@ export default (state: ObserveState, action: ObserveAction): ObserveState => {
9494
days: ['7 days'],
9595
severity: ['Critical', 'Warning'],
9696
state: ['Firing'],
97+
groupId: [],
9798
},
9899
incidentsActiveFilters: {
99100
days: [],
100101
severity: [],
101102
state: [],
103+
groupId: [],
102104
},
103105
incidentPageFilterType: 'Severity',
104106
groupId: '',

0 commit comments

Comments
 (0)