diff --git a/frontend/src/components/Sidebar/Sidebar.stories.tsx b/frontend/src/components/Sidebar/Sidebar.stories.tsx index 434b86a0df..3e0a4a9abd 100644 --- a/frontend/src/components/Sidebar/Sidebar.stories.tsx +++ b/frontend/src/components/Sidebar/Sidebar.stories.tsx @@ -55,22 +55,22 @@ const Template: StoryFn = args => { ); }; -export const InClusterSidebarOpen = Template.bind({}); -InClusterSidebarOpen.args = { - isSidebarOpen: true, - selected: { - item: 'cluster', - sidebar: DefaultSidebars.IN_CLUSTER, - }, -}; -export const InClusterSidebarClosed = Template.bind({}); -InClusterSidebarClosed.args = { - isSidebarOpen: false, - selected: { - item: 'cluster', - sidebar: DefaultSidebars.IN_CLUSTER, - }, -}; +// export const InClusterSidebarOpen = Template.bind({}); +// InClusterSidebarOpen.args = { +// isSidebarOpen: true, +// selected: { +// item: 'cluster', +// sidebar: DefaultSidebars.IN_CLUSTER, +// }, +// }; +// export const InClusterSidebarClosed = Template.bind({}); +// InClusterSidebarClosed.args = { +// isSidebarOpen: false, +// selected: { +// item: 'cluster', +// sidebar: DefaultSidebars.IN_CLUSTER, +// }, +// }; export const NoSidebar = Template.bind({}); NoSidebar.args = { selected: { @@ -78,15 +78,15 @@ NoSidebar.args = { sidebar: null, }, }; -export const SelectedItemWithSidebarOmitted = Template.bind({}); -SelectedItemWithSidebarOmitted.args = { - selected: { - item: 'workloads', - // This is what happens internally when plugins only set a selected name, not a selected sidebar. - // i.e. it will use the in-cluster sidebar by default. - sidebar: '', - }, -}; +// export const SelectedItemWithSidebarOmitted = Template.bind({}); +// SelectedItemWithSidebarOmitted.args = { +// selected: { +// item: 'workloads', +// // This is what happens internally when plugins only set a selected name, not a selected sidebar. +// // i.e. it will use the in-cluster sidebar by default. +// sidebar: '', +// }, +// }; export const HomeSidebarOpen = Template.bind({}); HomeSidebarOpen.args = { selected: { diff --git a/frontend/src/components/Sidebar/VersionButton.tsx b/frontend/src/components/Sidebar/VersionButton.tsx index 87344073f9..fb8e2ffc36 100644 --- a/frontend/src/components/Sidebar/VersionButton.tsx +++ b/frontend/src/components/Sidebar/VersionButton.tsx @@ -6,14 +6,15 @@ import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; import DialogTitle from '@mui/material/DialogTitle'; import { styled, useTheme } from '@mui/system'; -import { useQuery } from '@tanstack/react-query'; import { useSnackbar } from 'notistack'; import React from 'react'; import { useTranslation } from 'react-i18next'; import semver from 'semver'; import { getVersion, useCluster } from '../../lib/k8s'; import { StringDict } from '../../lib/k8s/cluster'; +import { getClusterGroup } from '../../lib/util'; import { useTypedSelector } from '../../redux/reducers/reducers'; +import { Tabs } from '../common'; import { NameValueTable } from '../common/SimpleTable'; const versionSnackbarHideTimeout = 5000; // ms @@ -28,12 +29,23 @@ const VersionIcon = styled(Icon)({ export default function VersionButton() { const isSidebarOpen = useTypedSelector(state => state.sidebar.isSidebarOpen); const { enqueueSnackbar } = useSnackbar(); + const [clusterVersions, setClusterVersions] = React.useState<{ + [key: string]: StringDict | null; + }>({}); const cluster = useCluster(); const theme = useTheme(); const [open, setOpen] = React.useState(false); const { t } = useTranslation('glossary'); + const clusters = React.useMemo(() => { + return getClusterGroup(); + }, [cluster]); - function getVersionRows() { + function getVersionRows(clusterName: string) { + if (!Object.values(clusterVersions).length) { + return []; + } + + const clusterVersion = clusterVersions[clusterName]; if (!clusterVersion) { return []; } @@ -62,49 +74,134 @@ export default function VersionButton() { ]; } - const { data: clusterVersion } = useQuery({ - placeholderData: null as any, - queryKey: ['version', cluster ?? ''], - queryFn: () => { - return getVersion() - .then((results: StringDict) => { - let versionChange = 0; - if (clusterVersion && results && results.gitVersion) { - versionChange = semver.compare(results.gitVersion, clusterVersion.gitVersion); - - let msg = ''; - if (versionChange > 0) { - msg = t('translation|Cluster version upgraded to {{ gitVersion }}', { - gitVersion: results.gitVersion, - }); - } else if (versionChange < 0) { - msg = t('translation|Cluster version downgraded to {{ gitVersion }}', { - gitVersion: results.gitVersion, - }); + React.useEffect( + () => { + let stillAlive = true; + function fetchVersion() { + Promise.allSettled(clusters.map(cluster => getVersion(cluster || ''))) + .then(results => { + if (!stillAlive) { + return; } + const newVersions: typeof clusterVersions = {}; + for (const result of results) { + const { status } = result; + + if (status === 'rejected') { + console.error( + 'Getting the version for a cluster:', + (result as PromiseRejectedResult).reason + ); + continue; + } + if (cluster === null) { + continue; + } + + const clusterVersion = result.value; - if (msg) { - enqueueSnackbar(msg, { - key: 'version', - preventDuplicate: true, - autoHideDuration: versionSnackbarHideTimeout, - variant: 'info', - }); + newVersions[cluster] = clusterVersion; + let versionChange = 0; + + if (clusterVersion && clusterVersion && clusterVersion.gitVersion) { + versionChange = semver.compare( + clusterVersion.gitVersion, + clusterVersion.gitVersion + ); + + let msg = ''; + if (versionChange > 0) { + msg = t('translation|Cluster version upgraded to {{ gitVersion }}', { + gitVersion: clusterVersion.gitVersion, + }); + } else if (versionChange < 0) { + msg = t('translation|Cluster version downgraded to {{ gitVersion }}', { + gitVersion: clusterVersion.gitVersion, + }); + } + + if (msg) { + enqueueSnackbar(msg, { + key: 'version', + preventDuplicate: true, + autoHideDuration: versionSnackbarHideTimeout, + variant: 'info', + }); + } + } } - } - return results; - }) - .catch((error: Error) => console.error('Getting the cluster version:', error)); + setClusterVersions(newVersions); + }) + .catch((error: Error) => console.error('Getting the cluster version:', error)); + + for (const cluster of []) { + getVersion(cluster) + .then() + .catch((error: Error) => console.error('Getting the cluster version:', error)); + } + } + + if (Object.keys(clusterVersions).length === 0) { + fetchVersion(); + } + + const intervalHandler = setInterval(() => { + fetchVersion(); + }, versionFetchInterval); + + return function cleanup() { + stillAlive = false; + clearInterval(intervalHandler); + }; }, - refetchInterval: versionFetchInterval, - }); + // eslint-disable-next-line + [clusterVersions] + ); + + // Use the location to make sure the version is changed, as it depends on the cluster + // (defined in the URL ATM). + // @todo: Update this if the active cluster management is changed. + React.useEffect(() => { + setClusterVersions(versions => { + if (!cluster || !versions[cluster]) { + return {}; + } + return versions; + }); + }, [cluster]); function handleClose() { setOpen(false); } - return !clusterVersion ? null : ( + const clusterVersionText = React.useMemo(() => { + let versionText: string[] = []; + // We only up to two versions (if they are different). If more + // than 2 different versions exist, then we show the 1st one + ... . + for (const versionInfo of Object.values(clusterVersions)) { + if (versionText.length > 2) { + break; + } + + if (versionText.length === 0 && !!versionInfo?.gitVersion) { + versionText.push(versionInfo.gitVersion); + } else if (versionText[0] === (versionInfo?.gitVersion || '')) { + // If it's the same version, we just check the next cluster's. + continue; + } else if (!!versionInfo?.gitVersion) { + versionText.push(versionInfo.gitVersion); + } + } + + if (versionText.length > 2) { + versionText = [versionText[0], '...']; + } + + return versionText.join('+'); + }, [clusterVersions]); + + return Object.keys(clusterVersions).length === 0 ? null : ( - {clusterVersion.gitVersion} + {clusterVersionText} {t('Kubernetes Version')} - + {Object.keys(clusterVersions).length === 1 ? ( + + ) : ( + ({ + label: clusterName, + component: ( + + + + ), + }))} + /> + )} -
-
-
- -
-
-
-
- -
-
-
- - - - - - - - -