Skip to content

Commit 1d38cf7

Browse files
Alerting: Add empty state to triage page WIP (#113390)
* add empty state to triage page WIP * tidy up and refactor * generate translations * resolve PR comments * generate translations * resolve PR comment part 2
1 parent 891ed6c commit 1d38cf7

File tree

3 files changed

+69
-31
lines changed

3 files changed

+69
-31
lines changed

public/app/features/alerting/unified/triage/Workbench.tsx

Lines changed: 56 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,9 @@ import { useState } from 'react';
44
import { useMeasure } from 'react-use';
55

66
import { GrafanaTheme2 } from '@grafana/data';
7+
import { Trans } from '@grafana/i18n';
78
import { SceneQueryRunner } from '@grafana/scenes';
8-
import { ScrollContainer, useSplitter, useStyles2 } from '@grafana/ui';
9+
import { Box, EmptyState, ScrollContainer, useSplitter, useStyles2 } from '@grafana/ui';
910
import { DEFAULT_PER_PAGE_PAGINATION } from 'app/core/constants';
1011

1112
import LoadMoreHelper from '../rule-list/LoadMoreHelper';
@@ -26,6 +27,7 @@ type WorkbenchProps = {
2627
groupBy?: string[];
2728
filterBy?: Filter[];
2829
queryRunner: SceneQueryRunner;
30+
hasActiveFilters?: boolean;
2931
};
3032

3133
const initialSize = 1 / 3;
@@ -116,14 +118,18 @@ function renderWorkbenchRow(
116118
│ │ │ │
117119
└─────────────────────────┘ └───────────────────────────────────┘
118120
*/
119-
export function Workbench({ domain, data, queryRunner, groupBy }: WorkbenchProps) {
121+
export function Workbench({ domain, data, queryRunner, groupBy, hasActiveFilters = false }: WorkbenchProps) {
120122
const styles = useStyles2(getStyles);
121123

122124
const isLoading = !queryRunner.isDataReadyToDisplay();
123125
const [pageIndex, setPageIndex] = useState<number>(1);
124126

125127
// Calculate once: show folder metadata only if not grouping by grafana_folder
126128
const enableFolderMeta = !groupBy?.includes('grafana_folder');
129+
130+
// Determine UI state
131+
const showEmptyState = !isLoading && data.length === 0;
132+
const showData = !isLoading && data.length > 0;
127133
// splitter for template and payload editor
128134
const splitter = useSplitter({
129135
direction: 'row',
@@ -149,42 +155,62 @@ export function Workbench({ domain, data, queryRunner, groupBy }: WorkbenchProps
149155
<div {...splitter.primaryProps}>
150156
<div ref={leftColumnRef} className={cx(styles.flexFull, styles.minColumnWidth)} />
151157
</div>
152-
<div {...splitter.splitterProps} />
158+
{!showEmptyState && <div {...splitter.splitterProps} />}
153159
<div {...splitter.secondaryProps}>
154160
<div ref={rightColumnRef} className={cx(styles.flexFull, styles.minColumnWidth)} />
155161
</div>
156162
</div>
157163
{/* content goes here */}
158164
<div data-testid="groups-container" className={cx(splitter.containerProps.className, styles.groupsContainer)}>
159-
<div className={cx(styles.groupItemWrapper(leftColumnWidth), styles.summaryContainer)}>
160-
<SummaryStatsReact />
161-
<SummaryChartReact />
162-
</div>
163-
{/* Render actual data */}
164-
<div className={styles.virtualizedContainer}>
165-
<WorkbenchProvider
166-
leftColumnWidth={leftColumnWidth}
167-
rightColumnWidth={rightColumnWidth}
168-
domain={domain}
169-
queryRunner={queryRunner}
170-
>
171-
<ScrollContainer height="100%" width="100%" scrollbarWidth="none" showScrollIndicators>
172-
{isLoading ? (
173-
<>
174-
<GenericRowSkeleton key="skeleton-1" width={leftColumnWidth} depth={0} />
175-
<GenericRowSkeleton key="skeleton-2" width={leftColumnWidth} depth={0} />
176-
<GenericRowSkeleton key="skeleton-3" width={leftColumnWidth} depth={0} />
177-
</>
165+
{showEmptyState ? (
166+
<Box display="flex" alignItems="center" justifyContent="center" width="100%" height="100%" minHeight="400px">
167+
<EmptyState
168+
variant="not-found"
169+
message={hasActiveFilters ? 'No matching instances found' : 'No firing or pending instances'}
170+
>
171+
{hasActiveFilters ? (
172+
<Trans i18nKey="alerting.triage.no-matching-instances-with-filters">
173+
No alert instances match your current set of filters for the selected time range.
174+
</Trans>
178175
) : (
179-
dataSlice.map((row, index) => {
180-
const rowKey = generateRowKey(row, index);
181-
return renderWorkbenchRow(row, leftColumnWidth, domain, rowKey, enableFolderMeta);
182-
})
176+
<Trans i18nKey="alerting.triage.no-firing-or-pending-instances">
177+
You have no alert instances in a firing or pending state for the selected time range.
178+
</Trans>
183179
)}
184-
{hasMore && <LoadMoreHelper handleLoad={() => setPageIndex((prevIndex) => prevIndex + 1)} />}
185-
</ScrollContainer>
186-
</WorkbenchProvider>
187-
</div>
180+
</EmptyState>
181+
</Box>
182+
) : (
183+
<>
184+
<div className={cx(styles.groupItemWrapper(leftColumnWidth), styles.summaryContainer)}>
185+
<SummaryStatsReact />
186+
<SummaryChartReact />
187+
</div>
188+
<div className={styles.virtualizedContainer}>
189+
<WorkbenchProvider
190+
leftColumnWidth={leftColumnWidth}
191+
rightColumnWidth={rightColumnWidth}
192+
domain={domain}
193+
queryRunner={queryRunner}
194+
>
195+
<ScrollContainer height="100%" width="100%" scrollbarWidth="none" showScrollIndicators={showData}>
196+
{isLoading && (
197+
<>
198+
<GenericRowSkeleton key="skeleton-1" width={leftColumnWidth} depth={0} />
199+
<GenericRowSkeleton key="skeleton-2" width={leftColumnWidth} depth={0} />
200+
<GenericRowSkeleton key="skeleton-3" width={leftColumnWidth} depth={0} />
201+
</>
202+
)}
203+
{showData &&
204+
dataSlice.map((row, index) => {
205+
const rowKey = generateRowKey(row, index);
206+
return renderWorkbenchRow(row, leftColumnWidth, domain, rowKey, enableFolderMeta);
207+
})}
208+
{hasMore && <LoadMoreHelper handleLoad={() => setPageIndex((prevIndex) => prevIndex + 1)} />}
209+
</ScrollContainer>
210+
</WorkbenchProvider>
211+
</div>
212+
</>
213+
)}
188214
</div>
189215
</div>
190216
);

public/app/features/alerting/unified/triage/scene/Workbench.tsx

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,17 @@ export function WorkbenchRenderer() {
3434
const { data } = runner.useState();
3535
const rows = data ? convertToWorkbenchRows(data, groupByKeys) : [];
3636

37-
return <Workbench data={rows} domain={domain} queryRunner={runner} groupBy={groupByKeys} />;
37+
const hasFiltersApplied = queryFilter.length > 0;
38+
39+
return (
40+
<Workbench
41+
data={rows}
42+
domain={domain}
43+
queryRunner={runner}
44+
groupBy={groupByKeys}
45+
hasActiveFilters={hasFiltersApplied}
46+
/>
47+
);
3848
}
3949

4050
type DataPoint = Record<ArrayValues<typeof DEFAULT_FIELDS>, string> & Record<string, string | undefined>;

public/locales/en-US/grafana.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2948,8 +2948,10 @@
29482948
"instance-details-drawer": {
29492949
"instance-details": "Instance details"
29502950
},
2951+
"no-firing-or-pending-instances": "You have no alert instances in a firing or pending state for the selected time range.",
29512952
"no-instances-found": "No alert instances found for rule: {{ruleUID}}",
29522953
"no-labels": "No labels",
2954+
"no-matching-instances-with-filters": "No alert instances match your current set of filters for the selected time range.",
29532955
"open-in-sidebar": "Open in sidebar",
29542956
"open-rule-details": "Open rule details",
29552957
"rule-details": {

0 commit comments

Comments
 (0)