Skip to content

Commit fee17d5

Browse files
Merge pull request #584 from zhuje/ou888-perses-edit-panel-pr
OU-888: Perses Dashboards Integrate panel editing
2 parents 93319c8 + eba0226 commit fee17d5

File tree

7 files changed

+520
-140
lines changed

7 files changed

+520
-140
lines changed

web/src/components/dashboards/perses/PersesWrapper.tsx

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import {
2727
useInitialRefreshInterval,
2828
useInitialTimeRange,
2929
usePluginBuiltinVariableDefinitions,
30+
ValidationProvider,
3031
} from '@perses-dev/plugin-system';
3132
import React, { useMemo } from 'react';
3233
import { usePatternFlyTheme } from '../../hooks/usePatternflyTheme';
@@ -35,8 +36,9 @@ import { PERSES_PROXY_BASE_PATH, useFetchPersesDashboard } from './perses-client
3536
import { CachedDatasourceAPI } from './perses/datasource-api';
3637
import {
3738
chart_color_blue_100,
38-
chart_color_blue_200,
3939
chart_color_blue_300,
40+
chart_color_blue_400,
41+
chart_color_blue_500,
4042
t_color_gray_95,
4143
t_color_white,
4244
} from '@patternfly/react-tokens';
@@ -47,10 +49,11 @@ import { LoadingBox } from '../../../components/console/console-shared/src/compo
4749
import { pluginLoader } from './persesPluginsLoader';
4850

4951
// Override eChart defaults with PatternFly colors.
50-
const patternflyBlue300 = '#2b9af3';
51-
const patternflyBlue400 = '#0066cc';
52-
const patternflyBlue500 = '#004080';
53-
const patternflyBlue600 = '#002952';
52+
const patternflyBlue100 = chart_color_blue_100.value;
53+
const patternflyBlue300 = chart_color_blue_300.value;
54+
const patternflyBlue400 = chart_color_blue_400.value;
55+
const patternflyBlue500 = chart_color_blue_500.value;
56+
const patternflyBlue600 = chart_color_blue_100.value;
5457
const defaultPaletteColors = [patternflyBlue400, patternflyBlue500, patternflyBlue600];
5558

5659
const chartColorScale = getThemeColors(ChartThemeColor.multiUnordered).chart.colorScale;
@@ -92,11 +95,12 @@ const mapPatterflyThemeToMUI = (theme: 'light' | 'dark'): ThemeOptions => {
9295
},
9396
},
9497
palette: {
98+
mode: isDark ? 'dark' : 'light', // Help CodeMirror detect theme mode
9599
primary: {
96100
light: chart_color_blue_100.value,
97-
main: chart_color_blue_200.value,
98-
dark: chart_color_blue_300.value,
99-
contrastText: primaryTextColor,
101+
main: patternflyBlue300,
102+
dark: patternflyBlue500,
103+
contrastText: t_color_white.value,
100104
},
101105
secondary: {
102106
main: primaryTextColor,
@@ -133,13 +137,6 @@ const mapPatterflyThemeToMUI = (theme: 'light' | 'dark'): ThemeOptions => {
133137
},
134138
},
135139
},
136-
MuiSvgIcon: {
137-
styleOverrides: {
138-
root: {
139-
color: theme === 'dark' ? t_color_white.value : t_color_gray_95.value,
140-
},
141-
},
142-
},
143140
MuiCard: {
144141
styleOverrides: {
145142
root: {
@@ -154,9 +151,9 @@ const mapPatterflyThemeToMUI = (theme: 'light' | 'dark'): ThemeOptions => {
154151
'&.MuiCardHeader-root': {
155152
borderBottom: 'none',
156153
paddingBlockEnd: 'var(--pf-t--global--spacer--md)',
157-
paddingBlockStart: 'var(--pf-t--global--spacer--lg)',
158-
paddingLeft: 'var(--pf-t--global--spacer--lg)',
159-
paddingRight: 'var(--pf-t--global--spacer--lg)',
154+
paddingBlockStart: 'var(--pf-t--global--spacer--md)',
155+
paddingLeft: 'var(--pf-t--global--spacer--md)',
156+
paddingRight: 'var(--pf-t--global--spacer--md)',
160157
},
161158
},
162159
},
@@ -167,9 +164,9 @@ const mapPatterflyThemeToMUI = (theme: 'light' | 'dark'): ThemeOptions => {
167164
'&.MuiCardContent-root': {
168165
borderTop: 'none',
169166
'&:last-child': {
170-
paddingBottom: 'var(--pf-t--global--spacer--lg)',
171-
paddingLeft: 'var(--pf-t--global--spacer--lg)',
172-
paddingRight: 'var(--pf-t--global--spacer--lg)',
167+
paddingBottom: 'var(--pf-t--global--spacer--md)',
168+
paddingLeft: 'var(--pf-t--global--spacer--sm)',
169+
paddingRight: 'var(--pf-t--global--spacer--md)',
173170
},
174171
},
175172
},
@@ -201,6 +198,52 @@ const mapPatterflyThemeToMUI = (theme: 'light' | 'dark'): ThemeOptions => {
201198
},
202199
},
203200
},
201+
MuiButton: {
202+
styleOverrides: {
203+
root: {
204+
'&.MuiButton-colorPrimary': {
205+
borderRadius: 'var(--pf-t--global--border--radius--pill)',
206+
borderColor: 'var(--pf-t--global--border--color--default)',
207+
color: isDark ? patternflyBlue100 : patternflyBlue300,
208+
},
209+
// Buttons with colored backgrounds should have white text
210+
'&.MuiButton-contained.MuiButton-colorPrimary': {
211+
color: t_color_white.value,
212+
},
213+
},
214+
},
215+
},
216+
MuiFormLabel: {
217+
styleOverrides: {
218+
root: {
219+
// Align placeholder text in Editing Panel
220+
'&.MuiFormLabel-root.MuiInputLabel-root.MuiInputLabel-formControl.MuiInputLabel-animated.MuiInputLabel-sizeMedium.MuiInputLabel-outlined.MuiFormLabel-colorPrimary[data-shrink="false"]':
221+
{
222+
top: '-7px',
223+
},
224+
},
225+
},
226+
},
227+
MuiTab: {
228+
styleOverrides: {
229+
root: {
230+
// Selected tab color
231+
'&.MuiButtonBase-root.MuiTab-root.Mui-selected': {
232+
color: isDark ? patternflyBlue100 : patternflyBlue300,
233+
},
234+
},
235+
},
236+
},
237+
MuiTabs: {
238+
styleOverrides: {
239+
indicator: {
240+
// Tab indicator should match color of selected MuiTab
241+
'&.MuiTabs-indicator': {
242+
backgroundColor: isDark ? patternflyBlue100 : patternflyBlue300,
243+
},
244+
},
245+
},
246+
},
204247
},
205248
};
206249
};
@@ -331,7 +374,7 @@ function InnerWrapper({ children, project, dashboardName }) {
331374
dashboardResource: clearedDashboardResource,
332375
}}
333376
>
334-
{children}
377+
<ValidationProvider>{children}</ValidationProvider>
335378
</DashboardProvider>
336379
) : (
337380
<>{children}</>

web/src/components/dashboards/perses/dashboard-page.tsx

Lines changed: 103 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
11
import { Overview } from '@openshift-console/dynamic-plugin-sdk';
2-
import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
3-
import type { FC } from 'react';
2+
import {
3+
QueryClient,
4+
QueryClientProvider,
5+
useMutation,
6+
UseMutationResult,
7+
useQueryClient,
8+
} from '@tanstack/react-query';
9+
import { useCallback, type FC } from 'react';
410
import { QueryParamProvider } from 'use-query-params';
511
import { ReactRouter5Adapter } from 'use-query-params/adapters/react-router-5';
612
import { LoadingInline } from '../../console/console-shared/src/components/loading/LoadingInline';
@@ -9,8 +15,24 @@ import { DashboardSkeleton } from './dashboard-skeleton';
915
import { DashboardEmptyState } from './emptystates/DashboardEmptyState';
1016
import { ProjectEmptyState } from './emptystates/ProjectEmptyState';
1117
import { useDashboardsData } from './hooks/useDashboardsData';
12-
import PersesBoard from './perses-dashboards';
1318
import { ProjectBar } from './project/ProjectBar';
19+
import {
20+
DashboardResource,
21+
EphemeralDashboardResource,
22+
getResourceExtendedDisplayName,
23+
} from '@perses-dev/core';
24+
import { getCSRFToken } from '@openshift-console/dynamic-plugin-sdk/lib/utils/fetch/console-fetch-utils';
25+
import { useSnackbar } from '@perses-dev/components';
26+
import buildURL from './perses/url-builder';
27+
import { OCPDashboardApp } from './dashoard-app';
28+
import { t } from 'i18next';
29+
30+
const resource = 'dashboards';
31+
const HTTPMethodPUT = 'PUT';
32+
const HTTPHeader: Record<string, string> = {
33+
'Content-Type': 'application/json',
34+
Accept: 'application/json',
35+
};
1436

1537
const queryClient = new QueryClient({
1638
defaultOptions: {
@@ -21,6 +43,47 @@ const queryClient = new QueryClient({
2143
},
2244
});
2345

46+
async function updateDashboard(entity: DashboardResource): Promise<DashboardResource> {
47+
const url = buildURL({
48+
resource: resource,
49+
project: entity.metadata.project,
50+
name: entity.metadata.name,
51+
});
52+
53+
const response = await fetch(url, {
54+
method: HTTPMethodPUT,
55+
headers: {
56+
...HTTPHeader,
57+
'X-CSRFToken': getCSRFToken(),
58+
},
59+
body: JSON.stringify(entity),
60+
});
61+
62+
if (!response.ok) {
63+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
64+
}
65+
66+
return response.json();
67+
}
68+
69+
function useUpdateDashboardMutation(): UseMutationResult<
70+
DashboardResource,
71+
Error,
72+
DashboardResource
73+
> {
74+
const queryClient = useQueryClient();
75+
76+
return useMutation<DashboardResource, Error, DashboardResource>({
77+
mutationKey: [resource],
78+
mutationFn: (dashboard) => {
79+
return updateDashboard(dashboard);
80+
},
81+
onSuccess: () => {
82+
return queryClient.invalidateQueries({ queryKey: [resource] });
83+
},
84+
});
85+
}
86+
2487
const MonitoringDashboardsPage_: FC = () => {
2588
const {
2689
changeBoard,
@@ -31,6 +94,32 @@ const MonitoringDashboardsPage_: FC = () => {
3194
dashboardName,
3295
} = useDashboardsData();
3396

97+
const updateDashboardMutation = useUpdateDashboardMutation();
98+
const { successSnackbar, exceptionSnackbar } = useSnackbar();
99+
100+
const handleDashboardSave = useCallback(
101+
(data: DashboardResource | EphemeralDashboardResource) => {
102+
if (data.kind !== 'Dashboard') {
103+
throw new Error('Invalid kind');
104+
}
105+
return updateDashboardMutation.mutateAsync(data, {
106+
onSuccess: (updatedDashboard: DashboardResource) => {
107+
successSnackbar(
108+
`Dashboard ${getResourceExtendedDisplayName(
109+
updatedDashboard,
110+
)} has been successfully updated`,
111+
);
112+
return updatedDashboard;
113+
},
114+
onError: (err) => {
115+
exceptionSnackbar(err);
116+
throw err;
117+
},
118+
});
119+
},
120+
[exceptionSnackbar, successSnackbar, updateDashboardMutation],
121+
);
122+
34123
if (combinedInitialLoad) {
35124
return <LoadingInline />;
36125
}
@@ -54,7 +143,17 @@ const MonitoringDashboardsPage_: FC = () => {
54143
activeProject={activeProject}
55144
>
56145
<Overview>
57-
<PersesBoard />
146+
<OCPDashboardApp
147+
dashboardResource={activeProjectDashboardsMetadata[0].persesDashboard}
148+
isReadonly={false}
149+
isVariableEnabled={true}
150+
isDatasourceEnabled={true}
151+
onSave={handleDashboardSave}
152+
emptyDashboardProps={{
153+
title: t('Empty Dashboard'),
154+
description: t('To get started add something to your dashboard'),
155+
}}
156+
/>
58157
</Overview>
59158
</DashboardSkeleton>
60159
)}

0 commit comments

Comments
 (0)