Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,16 @@

import React, { Suspense } from 'react';

import { Switch as AntDSwitch, Layout } from 'antd';
import { Switch as AntDSwitch, Layout, message } from 'antd';
import NavBar from './components/navBar/navBar';
import NavBarV2 from '@/v2/components/navBar/navBar';
import Breadcrumbs from './components/breadcrumbs/breadcrumbs';
import BreadcrumbsV2 from '@/v2/components/breadcrumbs/breadcrumbs';
import { HashRouter as Router, Switch, Route, Redirect } from 'react-router-dom';
import { routes } from '@/routes';
import { routesV2 } from '@/v2/routes-v2';
import { breadcrumbNameMap as breadcrumbNameMapV1 } from '@/constants/breadcrumbs.constants';
import { breadcrumbNameMap as breadcrumbNameMapV2 } from '@/v2/constants/breadcrumbs.constants';
import { MakeRouteWithSubRoutes } from '@/makeRouteWithSubRoutes';
import classNames from 'classnames';

Expand All @@ -38,6 +40,18 @@ const {
Header, Content, Footer
} = Layout;

const FALLBACK_PATH = '/Overview';
const TOAST_DURATION_SECONDS = 4;
type BreadcrumbNameMap = typeof breadcrumbNameMapV1;

// Strict membership check that ignores parameterized/catch-all entries
// (the v1 routes table ends with `/:NotFound`, which would otherwise match anything).
const pathExistsIn = (path: string, table: ReadonlyArray<{ path: string }>): boolean =>
table.some((r) => !r.path.includes(':') && r.path === path);

const getViewName = (path: string, preferredMap: BreadcrumbNameMap): string =>
preferredMap[path] ?? path;

interface IAppState {
collapsed: boolean;
enableOldUI: boolean;
Expand All @@ -57,6 +71,39 @@ class App extends React.Component<Record<string, object>, IAppState> {
this.setState({ collapsed });
};

handleUIToggle = (enableOldUI: boolean) => {
const currentPath = window.location.hash.slice(1).split('?')[0] || FALLBACK_PATH;
const targetTable = enableOldUI ? routes : routesV2;
const sourceTable = enableOldUI ? routesV2 : routes;
const shouldRedirect = !pathExistsIn(currentPath, targetTable);
let redirectMessage: string | undefined;

if (shouldRedirect) {
window.location.hash = FALLBACK_PATH;
// Only explain the redirect when the user came from a real page in the source UI;
// a typo'd path otherwise produces a misleading "only available in..." message.
if (pathExistsIn(currentPath, sourceTable)) {
const sourceMap = enableOldUI ? breadcrumbNameMapV2 : breadcrumbNameMapV1;
const targetMap = enableOldUI ? breadcrumbNameMapV1 : breadcrumbNameMapV2;
const friendly = getViewName(currentPath, sourceMap);
const fallbackViewName = getViewName(FALLBACK_PATH, targetMap);
const sourceUiName = enableOldUI ? 'New UI' : 'Old UI';
redirectMessage =
`The '${friendly}' view is only available in the ${sourceUiName}. We've returned you to the ${fallbackViewName} dashboard.`;
}
}

this.setState({ enableOldUI }, () => {
// This is to persist the state of the UI between refreshes.
// While using session storage to store state is an anti-pattern, provided the size of the data stored in this case
// and the plan to deprecate UI v1 (old UI) in the future - this is the simplest approach/fix for persisting state.
sessionStorage.setItem('enableOldUI', JSON.stringify(enableOldUI));
if (redirectMessage) {
message.info(redirectMessage, TOAST_DURATION_SECONDS);
}
});
};

render() {
const { collapsed, enableOldUI } = this.state;
const layoutClass = classNames('content-layout', { 'sidebar-collapsed': collapsed });
Expand All @@ -80,24 +127,15 @@ class App extends React.Component<Record<string, object>, IAppState> {
unCheckedChildren={<div style={{ paddingRight: '2px' }}>Old UI</div>}
checkedChildren={<div style={{ paddingLeft: '2px' }}>New UI</div>}
checked={this.state.enableOldUI}
onChange={(checked: boolean) => {
this.setState({
enableOldUI: checked
}, () => {
// This is to persist the state of the UI between refreshes.
// While using session storage to store state is an anti-pattern, provided the size of the data stored in this case
// and the plan to deprecate UI v1 (old UI) in the future - this is the simplest approach/fix for persisting state.
sessionStorage.setItem('enableOldUI', JSON.stringify(checked));
});
}} />
onChange={this.handleUIToggle} />
</span>
</div>
</Header>
<Content style={(enableOldUI) ? { margin: '0 16px 0', overflow: 'initial' } : {}}>
<Suspense fallback={<Loader />}>
<Switch>
<Route exact path='/'>
<Redirect to='/Overview' />
<Redirect to={FALLBACK_PATH} />
</Route>
{(enableOldUI)
? routes.map(
Expand All @@ -121,4 +159,4 @@ class App extends React.Component<Record<string, object>, IAppState> {
}
}

export default App;
export default App;
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,6 @@ export const breadcrumbNameMap: BreadcrumbNameMap = {
'/Insights': 'Insights',
'/NamespaceUsage': 'Namespace Usage',
'/Heatmap': 'Heatmap',
'/Om': 'OM DB Insights'
'/Om': 'OM DB Insights',
'/Capacity': 'Cluster Capacity'
};