Skip to content

Commit e65a5aa

Browse files
authored
[Catalog insights] Add insights components with dummy data (dagster-io#29341)
## Summary & Motivation Dummy components with fake data. ## How I Tested These Changes ![screencapture-localhost-3001-test-staging-all-assets-assets-2025-04-16-23_40_15](https://github.com/user-attachments/assets/08f0d31d-2119-47eb-b5b0-ec40303605d1)
1 parent 9e0fd62 commit e65a5aa

11 files changed

+479
-8
lines changed

js_modules/dagster-ui/packages/ui-core/src/assets/AssetCatalogTableV2.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,12 @@ import {useBlockTraceUntilTrue} from '../performance/TraceContext';
2828
import {SyntaxError} from '../selection/CustomErrorListener';
2929
import {IndeterminateLoadingBar} from '../ui/IndeterminateLoadingBar';
3030
import {numberFormatter} from '../ui/formatters';
31+
import {AssetCatalogInsights} from './insights/AssetCatalogInsights';
32+
3133
dayjs.extend(relativeTime);
3234
dayjs.extend(updateLocale);
3335

34-
export const AssetsCatalogTableV2 = React.memo(
36+
export const AssetCatalogTableV2 = React.memo(
3537
({isFullScreen, toggleFullScreen}: {isFullScreen: boolean; toggleFullScreen: () => void}) => {
3638
const {assets, loading: assetsLoading, error} = useAllAssets();
3739
useBlockTraceUntilTrue('useAllAssets', !!assets?.length && !assetsLoading);
@@ -124,7 +126,7 @@ export const AssetsCatalogTableV2 = React.memo(
124126
/>
125127
);
126128
case 'insights':
127-
return <div>Insights</div>;
129+
return <AssetCatalogInsights selection={assetSelection} />;
128130
default:
129131
return <Table assets={filtered} groupedByStatus={groupedByStatus} loading={loading} />;
130132
}

js_modules/dagster-ui/packages/ui-core/src/assets/AssetRecentUpdatesTrend.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,14 +15,14 @@ import styled from 'styled-components';
1515
import {AssetHealthSummary} from './AssetHealthSummary';
1616
import {useAllAssets} from './AssetsCatalogTable';
1717
import {useRecentAssetEvents} from './useRecentAssetEvents';
18+
import {Timestamp} from '../app/time/Timestamp';
1819
import {AssetHealthFragment} from '../asset-data/types/AssetHealthDataProvider.types';
1920
import {tokenForAssetKey} from '../asset-graph/Utils';
2021
import {
2122
AssetFailedToMaterializeFragment,
2223
AssetObservationFragment,
2324
AssetSuccessfulMaterializationFragment,
2425
} from './types/useRecentAssetEvents.types';
25-
import {Timestamp} from '../app/time/Timestamp';
2626

2727
export const AssetRecentUpdatesTrend = React.memo(({asset}: {asset: AssetHealthFragment}) => {
2828
const {assetsByAssetKey} = useAllAssets();

js_modules/dagster-ui/packages/ui-core/src/assets/AssetsCatalog.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {useRouteMatch} from 'react-router-dom';
44
import {useRecoilState, useSetRecoilState} from 'recoil';
55
import {ViewBreadcrumb} from 'shared/assets/ViewBreadcrumb.oss';
66

7-
import {AssetsCatalogTableV2} from './AssetCatalogTableV2';
7+
import {AssetCatalogTableV2} from './AssetCatalogTableV2';
88
import {isFullScreenAtom} from '../app/AppTopNav/AppTopNavContext';
99
import {currentPageAtom} from '../app/analytics';
1010

@@ -33,7 +33,7 @@ export const AssetsCatalog = React.memo(() => {
3333
>
3434
<ViewBreadcrumb full />
3535
</Box>
36-
<AssetsCatalogTableV2
36+
<AssetCatalogTableV2
3737
isFullScreen={isFullScreen}
3838
toggleFullScreen={() => setIsFullScreen(!isFullScreen)}
3939
/>

js_modules/dagster-ui/packages/ui-core/src/assets/__tests__/AssetCatalogTablev2.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ import {
2222
buildAssetNode,
2323
} from '../../graphql/types';
2424
import {buildQueryMock, getMockResultFn} from '../../testing/mocking';
25-
import {AssetsCatalogTableV2} from '../AssetCatalogTableV2';
25+
import {AssetCatalogTableV2} from '../AssetCatalogTableV2';
2626
import {AssetCatalogV2VirtualizedTable} from '../AssetCatalogV2VirtualizedTable';
2727
import {ASSET_CATALOG_TABLE_QUERY} from '../AssetsCatalogTable';
2828
import {
@@ -156,7 +156,7 @@ describe('AssetCatalogTableV2', () => {
156156
]}
157157
>
158158
<AssetLiveDataProvider>
159-
<AssetsCatalogTableV2 isFullScreen={false} toggleFullScreen={() => {}} />
159+
<AssetCatalogTableV2 isFullScreen={false} toggleFullScreen={() => {}} />
160160
</AssetLiveDataProvider>
161161
</MockedProvider>
162162
</MemoryRouter>
@@ -202,7 +202,7 @@ describe('AssetCatalogTableV2', () => {
202202
]}
203203
>
204204
<AssetLiveDataProvider>
205-
<AssetsCatalogTableV2 isFullScreen={false} toggleFullScreen={() => {}} />
205+
<AssetCatalogTableV2 isFullScreen={false} toggleFullScreen={() => {}} />
206206
</AssetLiveDataProvider>
207207
</MockedProvider>
208208
</MemoryRouter>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import {BodyLarge, BodySmall, Box, Popover} from '@dagster-io/ui-components';
2+
import dayjs from 'dayjs';
3+
import React from 'react';
4+
5+
import styles from './AssetCatalogInsights.module.css';
6+
import {numberFormatter} from '../../ui/formatters';
7+
8+
const hours = Array.from({length: 24}, (_, i) => i);
9+
10+
export const ActivityChart = React.memo(({header, color}: {header: string; color: string}) => {
11+
return (
12+
<div className={styles.ActivityChartContainer}>
13+
<Box flex={{direction: 'row', alignItems: 'center'}} padding={{bottom: 12}}>
14+
<BodyLarge>{header}</BodyLarge>
15+
</Box>
16+
<div className={styles.ActivityChart}>
17+
<ActivityChartRow
18+
date="2024-04-16"
19+
data={hours.map((hour) => {
20+
if (hour > 16) {
21+
return null;
22+
}
23+
const value = Math.random() * 100;
24+
return value > 50 ? value : 0;
25+
})}
26+
max={100}
27+
color={color}
28+
/>
29+
<ActivityChartRow
30+
date="2024-04-15"
31+
data={hours.map(() => Math.random() * 100)}
32+
max={100}
33+
color={color}
34+
/>
35+
<ActivityChartRow
36+
date="2024-04-14"
37+
data={hours.map(() => Math.random() * 100)}
38+
max={100}
39+
color={color}
40+
/>
41+
<ActivityChartRow
42+
date="2024-04-13"
43+
data={hours.map(() => Math.random() * 100)}
44+
max={100}
45+
color={color}
46+
/>
47+
<ActivityChartRow
48+
date="2024-04-12"
49+
data={hours.map(() => Math.random() * 100)}
50+
max={100}
51+
color={color}
52+
/>
53+
<ActivityChartRow
54+
date="2024-04-11"
55+
data={hours.map(() => Math.random() * 100)}
56+
max={100}
57+
color={color}
58+
/>
59+
<div className={styles.ActivityChartRow}>
60+
<div />
61+
<div className={styles.ActivityChartBottomLegend}>
62+
<BodySmall>12AM</BodySmall>
63+
<BodySmall>6AM</BodySmall>
64+
<BodySmall>12PM</BodySmall>
65+
<BodySmall>6PM</BodySmall>
66+
<BodySmall>12AM</BodySmall>
67+
</div>
68+
</div>
69+
</div>
70+
</div>
71+
);
72+
});
73+
74+
const ActivityChartRow = React.memo(
75+
({
76+
date,
77+
data,
78+
max,
79+
color,
80+
}: {
81+
date: string;
82+
data: Array<number | null>;
83+
max: number;
84+
color: string;
85+
}) => {
86+
return (
87+
<div className={styles.ActivityChartRow}>
88+
<BodySmall>{dayjs(date).format('MMM D')}</BodySmall>
89+
<div
90+
style={{
91+
display: 'grid',
92+
gap: 2,
93+
gridTemplateColumns: 'repeat(24, 1fr)',
94+
gridTemplateRows: '16px',
95+
}}
96+
>
97+
{data.map((value, index) => {
98+
if (value === null) {
99+
return <div key={index} />;
100+
}
101+
if (value === 0) {
102+
return (
103+
<div key={index} className={styles.TileContainer}>
104+
<div className={styles.Tile} />
105+
</div>
106+
);
107+
}
108+
return (
109+
<Popover
110+
key={index}
111+
targetTagName="div"
112+
interactionKind="hover"
113+
content={
114+
<div className={styles.Tooltip}>
115+
<BodySmall>{numberFormatter.format(value)}</BodySmall>
116+
</div>
117+
}
118+
>
119+
<div className={styles.TileContainer}>
120+
<div className={styles.Tile} />
121+
<div
122+
className={styles.Tile}
123+
style={{
124+
backgroundColor: color,
125+
opacity: value / max,
126+
}}
127+
/>
128+
</div>
129+
</Popover>
130+
);
131+
})}
132+
</div>
133+
</div>
134+
);
135+
},
136+
);
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
.TileContainer {
2+
position: relative;
3+
width: 100%;
4+
height: 100%;
5+
}
6+
7+
.Tile {
8+
position: absolute;
9+
width: 100%;
10+
height: 100%;
11+
border-radius: 2px;
12+
background-color: var(--color-background-light);
13+
}
14+
15+
.Tooltip {
16+
padding: 4px 8px;
17+
border-radius: 4px;
18+
}
19+
20+
.ActivityChartContainer {
21+
padding: 16px 24px;
22+
border-radius: 12px;
23+
border: 1px solid var(--color-keyline-default);
24+
}
25+
26+
.ActivityChartRow {
27+
display: grid;
28+
grid-template-columns: 40px 1fr;
29+
gap: 12px;
30+
}
31+
32+
.ActivityChartBottomLegend {
33+
display: flex;
34+
justify-content: space-between;
35+
align-items: center;
36+
align-self: stretch;
37+
gap: 6px;
38+
}
39+
40+
.MainLineCharts {
41+
display: grid;
42+
grid-template-columns: repeat(2, minmax(0, 1fr));
43+
gap: 12px;
44+
}
45+
46+
.LineCharts {
47+
display: grid;
48+
grid-template-columns: repeat(4, minmax(0, 1fr));
49+
gap: 12px;
50+
padding-bottom: 12px;
51+
}
52+
53+
.ActivityChart {
54+
display: grid;
55+
grid-template-columns: 1fr;
56+
gap: 2px;
57+
color: var(--color-text-light);
58+
}
59+
60+
.TopAssetsChartsSection {
61+
display: grid;
62+
grid-template-columns: repeat(4, minmax(0, 1fr));
63+
gap: 12px;
64+
}
65+
66+
.ActivityCharts {
67+
display: grid;
68+
grid-template-columns: repeat(3, minmax(0, 1fr));
69+
gap: 12px;
70+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
import {Box, Colors} from '@dagster-io/ui-components';
2+
import React, {useMemo} from 'react';
3+
4+
import {ActivityChart} from './AssetCatalogActivityChart';
5+
import styles from './AssetCatalogInsights.module.css';
6+
import {AssetCatalogInsightsLineChart} from './AssetCatalogInsightsLineChart';
7+
import {AssetCatalogTopAssetsChart} from './AssetCatalogTopAssetsChart';
8+
import {useRGBColorsForTheme} from '../../app/useRGBColorsForTheme';
9+
10+
export const AssetCatalogInsights = React.memo((_: {selection: string}) => {
11+
const rgbColors = useRGBColorsForTheme();
12+
const colors = useMemo(() => {
13+
return {
14+
FAILURE: rgbColors[Colors.accentRed()]!,
15+
SUCCESS: rgbColors[Colors.accentGreen()]!,
16+
NEUTRAL: rgbColors[Colors.accentBlue()]!,
17+
WARNING: rgbColors[Colors.accentYellow()]!,
18+
};
19+
}, [rgbColors]);
20+
21+
return (
22+
<Box padding={24}>
23+
<Box flex={{direction: 'column', gap: 12}}>
24+
<div className={styles.MainLineCharts}>
25+
<AssetCatalogInsightsLineChart color={colors.NEUTRAL} />
26+
<AssetCatalogInsightsLineChart color={colors.FAILURE} />
27+
</div>
28+
<div className={styles.LineCharts}>
29+
<AssetCatalogInsightsLineChart color={colors.NEUTRAL} />
30+
<AssetCatalogInsightsLineChart color={colors.NEUTRAL} />
31+
<AssetCatalogInsightsLineChart color={colors.FAILURE} />
32+
<AssetCatalogInsightsLineChart color={colors.FAILURE} />
33+
</div>
34+
</Box>
35+
<div className={styles.ActivityCharts}>
36+
<ActivityChart header="Materializations by hour" color={colors.SUCCESS} />
37+
<ActivityChart header="Avg execution time by hour" color={colors.NEUTRAL} />
38+
<ActivityChart header="Retries by hour" color={colors.NEUTRAL} />
39+
<ActivityChart header="Materialization failures by hour" color={colors.FAILURE} />
40+
<ActivityChart header="Freshness policy violations by hour" color={colors.FAILURE} />
41+
<ActivityChart header="Check failures by hour" color={colors.FAILURE} />
42+
</div>
43+
<div className={styles.TopAssetsChartsSection}>
44+
<AssetCatalogTopAssetsChart header="Top assets by materialization count" />
45+
<AssetCatalogTopAssetsChart header="Top assets by failure count" />
46+
<AssetCatalogTopAssetsChart header="Top assets by avg. execution time" />
47+
<AssetCatalogTopAssetsChart header="Top assets by freshness failure count" />
48+
<AssetCatalogTopAssetsChart header="Top assets by check failure count" />
49+
</div>
50+
</Box>
51+
);
52+
});

0 commit comments

Comments
 (0)