Skip to content

Commit 50e7e77

Browse files
feature: add namespace dropdown to dashboards page
1 parent afc4510 commit 50e7e77

7 files changed

Lines changed: 160 additions & 62 deletions

File tree

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

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Overview } from '@openshift-console/dynamic-plugin-sdk';
1+
import { NamespaceBar, Overview } from '@openshift-console/dynamic-plugin-sdk';
22
import type { FC } from 'react';
33
import { useTranslation } from 'react-i18next';
44
import { useParams } from 'react-router-dom-v5-compat';
@@ -12,45 +12,46 @@ import ErrorAlert from './error';
1212
import { DashboardSkeletonLegacy } from './dashboard-skeleton-legacy';
1313
import { useLegacyDashboards } from './useLegacyDashboards';
1414
import { MonitoringProvider } from '../../../contexts/MonitoringContext';
15+
import { useOpenshiftProject } from './useOpenshiftProject';
1516

1617
type LegacyDashboardsPageProps = {
1718
urlBoard: string;
18-
namespace?: string;
1919
};
2020

21-
const LegacyDashboardsPage_: FC<LegacyDashboardsPageProps> = ({
22-
urlBoard,
23-
namespace, // only used in developer perspective
24-
}) => {
21+
const LegacyDashboardsPage_: FC<LegacyDashboardsPageProps> = ({ urlBoard }) => {
22+
const { project, setProject } = useOpenshiftProject();
2523
const {
2624
legacyDashboardsError,
2725
legacyRows,
2826
legacyDashboardsLoading,
2927
legacyDashboardsMetadata,
3028
changeLegacyDashboard,
3129
legacyDashboard,
32-
} = useLegacyDashboards(namespace, urlBoard);
30+
} = useLegacyDashboards(project, urlBoard);
3331
const { perspective } = usePerspective();
3432
const { t } = useTranslation(process.env.I18N_NAMESPACE);
3533

3634
return (
37-
<DashboardSkeletonLegacy
38-
boardItems={legacyDashboardsMetadata}
39-
changeBoard={changeLegacyDashboard}
40-
dashboardName={legacyDashboard}
41-
>
42-
<Overview>
43-
{legacyDashboardsLoading ? (
44-
<LoadingInline />
45-
) : legacyDashboardsError ? (
46-
<ErrorAlert
47-
error={{ message: legacyDashboardsError, name: t('Error Loading Dashboards') }}
48-
/>
49-
) : (
50-
<LegacyDashboard rows={legacyRows} perspective={perspective} />
51-
)}
52-
</Overview>
53-
</DashboardSkeletonLegacy>
35+
<>
36+
<NamespaceBar onNamespaceChange={(namespace) => setProject(namespace)} />
37+
<DashboardSkeletonLegacy
38+
boardItems={legacyDashboardsMetadata}
39+
changeBoard={changeLegacyDashboard}
40+
dashboardName={legacyDashboard}
41+
>
42+
<Overview>
43+
{legacyDashboardsLoading ? (
44+
<LoadingInline />
45+
) : legacyDashboardsError ? (
46+
<ErrorAlert
47+
error={{ message: legacyDashboardsError, name: t('Error Loading Dashboards') }}
48+
/>
49+
) : (
50+
<LegacyDashboard rows={legacyRows} perspective={perspective} />
51+
)}
52+
</Overview>
53+
</DashboardSkeletonLegacy>
54+
</>
5455
);
5556
};
5657

@@ -62,7 +63,7 @@ export const MpCmoLegacyDashboardsPage: FC = () => {
6263
return (
6364
<MonitoringProvider monitoringContext={{ plugin: 'monitoring-plugin', prometheus: 'cmo' }}>
6465
<QueryParamProvider adapter={ReactRouter5Adapter}>
65-
<LegacyDashboardsPageWithFallback urlBoard={params?.dashboardName} namespace={params?.ns} />
66+
<LegacyDashboardsPageWithFallback urlBoard={params?.dashboardName} />
6667
</QueryParamProvider>
6768
</MonitoringProvider>
6869
);

web/src/components/dashboards/legacy/legacy-dashboard.tsx

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ import {
3939
} from '../../hooks/usePerspective';
4040
import KebabDropdown from '../../kebab-dropdown';
4141
import { MonitoringState } from '../../../store/store';
42-
import { evaluateVariableTemplate } from './legacy-variable-dropdowns';
42+
import { evaluateVariableTemplate, Variable } from './legacy-variable-dropdowns';
4343
import { Panel, Row } from './types';
4444
import { QueryParams } from '../../query-params';
4545
import { CustomDataSource } from '@openshift-console/dynamic-plugin-sdk-internal/lib/extensions/dashboard-data-source';
@@ -104,7 +104,6 @@ const Card: FC<CardProps> = memo(({ panel, perspective }) => {
104104
const { t } = useTranslation(process.env.I18N_NAMESPACE);
105105
const { plugin } = useMonitoring();
106106

107-
const [namespace] = useActiveNamespace();
108107
const pollInterval = useSelector(
109108
(state: MonitoringState) => getObserveState(plugin, state).dashboards.pollInterval,
110109
);
@@ -115,6 +114,9 @@ const Card: FC<CardProps> = memo(({ panel, perspective }) => {
115114
(state: MonitoringState) => getObserveState(plugin, state).dashboards.variables,
116115
);
117116

117+
// Directly use the namespace variable to prevent desync
118+
const namespace = variables?.['namespace'] as Variable;
119+
118120
const ref = useRef();
119121
const [, wasEverVisible] = useIsVisible(ref);
120122

@@ -281,7 +283,9 @@ const Card: FC<CardProps> = memo(({ panel, perspective }) => {
281283
if (!rawQueries.length) {
282284
return null;
283285
}
284-
const queries = rawQueries.map((expr) => evaluateVariableTemplate(expr, variables, timespan));
286+
const queries = rawQueries.map((expr) =>
287+
evaluateVariableTemplate(expr, variables, timespan, namespace?.value ?? ''),
288+
);
285289
const isLoading =
286290
(_.some(queries, _.isUndefined) && dataSourceInfoLoading) || customDataSource === undefined;
287291

@@ -353,7 +357,7 @@ const Card: FC<CardProps> = memo(({ panel, perspective }) => {
353357
panel={panel}
354358
pollInterval={pollInterval}
355359
query={queries[0]}
356-
namespace={namespace}
360+
namespace={namespace?.value ?? ''}
357361
customDataSource={customDataSource}
358362
/>
359363
)}
@@ -362,7 +366,7 @@ const Card: FC<CardProps> = memo(({ panel, perspective }) => {
362366
panel={panel}
363367
pollInterval={pollInterval}
364368
queries={queries}
365-
namespace={namespace}
369+
namespace={namespace?.value ?? ''}
366370
customDataSource={customDataSource}
367371
/>
368372
)}

web/src/components/dashboards/legacy/legacy-variable-dropdowns.tsx

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ import { useTranslation } from 'react-i18next';
2323
import { useDispatch, useSelector } from 'react-redux';
2424

2525
import { SingleTypeaheadDropdown } from '../../console/utils/single-typeahead-dropdown';
26-
import { getPrometheusBasePath, buildPrometheusUrl } from '../../utils';
26+
import { getPrometheusBasePath, buildPrometheusUrl, ALL_NAMESPACES_KEY } from '../../utils';
2727
import { getQueryArgument, setQueryArgument } from '../../console/utils/router';
2828
import { useSafeFetch } from '../../console/utils/safe-fetch-hook';
2929

@@ -48,6 +48,7 @@ export const evaluateVariableTemplate = (
4848
template: string,
4949
variables: any,
5050
timespan: number,
51+
namespace: string,
5152
): string => {
5253
if (_.isEmpty(template)) {
5354
return undefined;
@@ -81,8 +82,11 @@ export const evaluateVariableTemplate = (
8182
result = undefined;
8283
return false;
8384
}
84-
const replacement =
85+
let replacement =
8586
v.value === MONITORING_DASHBOARDS_VARIABLE_ALL_OPTION_KEY ? '.+' : v.value || '';
87+
if (v.name === 'namespace' && namespace !== ALL_NAMESPACES_KEY) {
88+
replacement = namespace;
89+
}
8690
result = result.replace(re, replacement);
8791
}
8892
});
@@ -103,9 +107,10 @@ const LegacyDashboardsVariableOption = ({ value, isSelected, ...rest }) =>
103107
</SelectOption>
104108
);
105109

106-
const LegacyDashboardsVariableDropdown: FC<VariableDropdownProps> = ({ id, name, namespace }) => {
110+
const LegacyDashboardsVariableDropdown: FC<VariableDropdownProps> = ({ id, name }) => {
107111
const { t } = useTranslation(process.env.I18N_NAMESPACE);
108112
const { plugin } = useMonitoring();
113+
const [namespace] = useActiveNamespace();
109114

110115
const timespan = useSelector(
111116
(state: MonitoringState) => getObserveState(plugin, state).dashboards.timespan,
@@ -115,11 +120,12 @@ const LegacyDashboardsVariableDropdown: FC<VariableDropdownProps> = ({ id, name,
115120
(state: MonitoringState) => getObserveState(plugin, state).dashboards.variables,
116121
);
117122
const variable = variables?.[name] as Variable;
123+
118124
const options = useDeepMemo(() => {
119125
return variable?.options;
120126
}, [variable?.options]);
121127

122-
const query = evaluateVariableTemplate(variable?.query, variables, timespan);
128+
const query = evaluateVariableTemplate(variable?.query, variables, timespan, namespace);
123129

124130
const dispatch = useDispatch();
125131

@@ -309,7 +315,6 @@ const LegacyDashboardsVariableDropdown: FC<VariableDropdownProps> = ({ id, name,
309315

310316
// Expects to be inside of a Patternfly Split Component
311317
export const LegacyDashboardsAllVariableDropdowns: FC = () => {
312-
const [namespace] = useActiveNamespace();
313318
const { plugin } = useMonitoring();
314319

315320
const variables = useSelector(
@@ -323,7 +328,7 @@ export const LegacyDashboardsAllVariableDropdowns: FC = () => {
323328
return (
324329
<Split hasGutter isWrappable>
325330
{Object.keys(variables).map((name: string) => (
326-
<LegacyDashboardsVariableDropdown id={name} key={name} name={name} namespace={namespace} />
331+
<LegacyDashboardsVariableDropdown id={name} key={name} name={name} />
327332
))}
328333
</Split>
329334
);
@@ -342,5 +347,4 @@ export type Variable = {
342347
type VariableDropdownProps = {
343348
id: string;
344349
name: string;
345-
namespace?: string;
346350
};

web/src/components/dashboards/legacy/useLegacyDashboards.ts

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ import {
2121
import { CombinedDashboardMetadata } from '../perses/hooks/useDashboardsData';
2222
import { useNavigate } from 'react-router-dom-v5-compat';
2323
import { QueryParams } from '../../query-params';
24-
import { NumberParam, StringParam, useQueryParam } from 'use-query-params';
24+
import { NumberParam, useQueryParam } from 'use-query-params';
25+
import { ALL_NAMESPACES_KEY } from '../../utils';
2526

2627
export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
2728
const { t } = useTranslation('plugin__monitoring-plugin');
@@ -31,26 +32,19 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
3132
const safeFetch = useCallback(useSafeFetch(), []);
3233
const [legacyDashboards, setLegacyDashboards] = useState<Board[]>([]);
3334
const [legacyDashboardsError, setLegacyDashboardsError] = useState<string>();
34-
const [dashboardParam] = useQueryParam(QueryParams.Dashboard, StringParam);
3535
const [refreshInterval] = useQueryParam(QueryParams.RefreshInterval, NumberParam);
3636
const [legacyDashboardsLoading, , , setLegacyDashboardsLoaded] = useBoolean(true);
37-
const [initialLoad, , , setInitialLoaded] = useBoolean(true);
37+
const [initialLoad, , setInitialUnloaded, setInitialLoaded] = useBoolean(true);
3838
const dispatch = useDispatch();
3939
const navigate = useNavigate();
40-
const legacyDashboard = useMemo(() => {
41-
if (perspective === 'dev') {
42-
return dashboardParam;
43-
}
44-
return urlBoard;
45-
}, [perspective, dashboardParam, urlBoard]);
4640

4741
useEffect(() => {
4842
safeFetch<any>('/api/console/monitoring-dashboard-config')
4943
.then((response) => {
5044
setLegacyDashboardsLoaded();
5145
setLegacyDashboardsError(undefined);
5246
let items = response.items;
53-
if (namespace) {
47+
if (namespace !== ALL_NAMESPACES_KEY) {
5448
items = _.filter(
5549
items,
5650
(item) => item.metadata?.labels['console.openshift.io/odc-dashboard'] === 'true',
@@ -83,7 +77,7 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
8377
}, [namespace, safeFetch, setLegacyDashboardsLoaded, t]);
8478

8579
const legacyRows = useMemo(() => {
86-
const data = _.find(legacyDashboards, { name: legacyDashboard })?.data;
80+
const data = _.find(legacyDashboards, { name: urlBoard })?.data;
8781

8882
return data?.rows?.length
8983
? data.rows
@@ -101,17 +95,17 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
10195
}
10296
return acc;
10397
}, []) ?? [];
104-
}, [legacyDashboard, legacyDashboards]);
98+
}, [urlBoard, legacyDashboards]);
10599

106100
useEffect(() => {
107101
// Dashboard query argument is only set in dev perspective, so skip for admin
108102
if (perspective !== 'dev') {
109103
return;
110104
}
111-
const allVariables = getAllVariables(legacyDashboards, legacyDashboard, namespace);
105+
const allVariables = getAllVariables(legacyDashboards, urlBoard, namespace);
112106
dispatch(dashboardsPatchAllVariables(allVariables));
113107
// eslint-disable-next-line react-hooks/exhaustive-deps
114-
}, [namespace, legacyDashboard]);
108+
}, [namespace, urlBoard]);
115109

116110
// Homogenize data needed for dashboards dropdown between legacy and perses dashboards
117111
// to enable both to use the same component
@@ -143,7 +137,7 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
143137
let url = getLegacyDashboardsUrl(perspective, newBoard, namespace);
144138
url = `${url}${perspective === 'dev' ? '&' : '?'}${params.toString()}`;
145139

146-
if (newBoard !== legacyDashboard || initialLoad) {
140+
if (newBoard !== urlBoard || initialLoad) {
147141
if (params.get(QueryParams.Dashboard) !== newBoard) {
148142
navigate(url, { replace: true });
149143
}
@@ -165,7 +159,7 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
165159
},
166160
[
167161
perspective,
168-
legacyDashboard,
162+
urlBoard,
169163
dispatch,
170164
navigate,
171165
namespace,
@@ -177,15 +171,24 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
177171

178172
useEffect(() => {
179173
if (
180-
(!legacyDashboard ||
181-
!legacyDashboards.some((legacyBoard) => legacyBoard.name === legacyDashboard) ||
174+
(!urlBoard ||
175+
!legacyDashboards.some((legacyBoard) => legacyBoard.name === urlBoard) ||
182176
initialLoad) &&
183177
!_.isEmpty(legacyDashboards)
184178
) {
185-
changeLegacyDashboard(legacyDashboard || legacyDashboards?.[0]?.name);
179+
changeLegacyDashboard(urlBoard || legacyDashboards?.[0]?.name);
186180
setInitialLoaded();
187181
}
188-
}, [legacyDashboards, changeLegacyDashboard, initialLoad, setInitialLoaded, legacyDashboard]);
182+
}, [legacyDashboards, changeLegacyDashboard, initialLoad, setInitialLoaded, urlBoard]);
183+
184+
useEffect(() => {
185+
// Basically perform a full reload when changing a namespace to force the variables and the
186+
// dashboard to reset. This is needed for when we transition between ALL_NS and a normal
187+
// namespace, but is performed quickly and should help insure consistency when transitioning
188+
// between any namespaces
189+
setInitialUnloaded();
190+
/* eslint-disable react-hooks/exhaustive-deps */
191+
}, [namespace]);
189192

190193
// Clear variables on unmount
191194
useEffect(() => {
@@ -201,7 +204,7 @@ export const useLegacyDashboards = (namespace: string, urlBoard: string) => {
201204
legacyRows,
202205
legacyDashboardsMetadata,
203206
changeLegacyDashboard,
204-
legacyDashboard,
207+
legacyDashboard: urlBoard,
205208
};
206209
};
207210

@@ -227,11 +230,14 @@ const getAllVariables = (boards: Board[], newBoardName: string, namespace: strin
227230
allVariables[v.name] = {
228231
datasource: v.datasource,
229232
includeAll: !!v.includeAll,
230-
isHidden: namespace && v.name === 'namespace' ? true : v.hide !== 0,
231-
isLoading: namespace ? v.type === 'query' && !namespace : v.type === 'query',
233+
isHidden: v.name === 'namespace' && namespace !== ALL_NAMESPACES_KEY ? true : v.hide !== 0,
234+
isLoading: v.name === 'namespace' ? false : v.type === 'query',
232235
options: _.map(v.options, 'value'),
233236
query: v.type === 'query' ? v.query : undefined,
234-
value: namespace && v.name === 'namespace' ? namespace : value || v.options?.[0]?.value,
237+
value:
238+
v.name === 'namespace' && namespace !== ALL_NAMESPACES_KEY
239+
? namespace
240+
: value || v.options?.[0]?.value,
235241
};
236242
}
237243
});

0 commit comments

Comments
 (0)