Skip to content

Commit

Permalink
refactor: [M3-8623] - Introduce TanStack Router (#10997)
Browse files Browse the repository at this point in the history
* Initial commit: proof of concept

* Route Updates and types

* NBs & Domains

* testing + utils

* better util types

* testing existing routes (wip)

* more testing before more routes

* save progress

* save progress

* save progress

* type improvements

* make basic account routing work

* Revert "make basic account routing work"

This reverts commit 28511f5.

* event routes

* firewalls

* databases

* update maincontent v2

* betas

* VPCs

* Cloud pulse and cleanup

* Rename MainContentV2 to Router

* update vpc routes to use reccomended lazy routing

* support and volumes lazy imports

* post rebase yarn

* lazy imports/exports stackscripts, volumes, profile

* lazy imports/exports placement groups

* lazy imports/exports obj + nb

* lazy imports/exports managed

* lazy imports/exports longview and linodes

* lazy imports/exports images and k8

* lazy imports/exports events & firewalls

* lazy imports/exports remaining

* some cleanup

* More cleanup

* more account routes

* missing domains and linodes routes

* missing longview routes

* missing routes

* wrap up missing routes

* light cleanup

* rename stylesheet

* Added changeset: Introduce TanStack Router

* feedback @bnussman-akamai

* feedback @jaalah-akamai

* post rebase fix

---------

Co-authored-by: Banks Nussman <banks@nussman.us>
  • Loading branch information
abailly-akamai and bnussman authored Oct 4, 2024
1 parent 2aeea52 commit ccb4e0b
Show file tree
Hide file tree
Showing 88 changed files with 2,611 additions and 69 deletions.
5 changes: 5 additions & 0 deletions packages/manager/.changeset/pr-10997-added-1727970898327.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@linode/manager": Added
---

Introduce TanStack Router ([#10997](https://github.com/linode/manager/pull/10997))
1 change: 1 addition & 0 deletions packages/manager/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@sentry/react": "^7.57.0",
"@tanstack/react-query": "5.51.24",
"@tanstack/react-query-devtools": "5.51.24",
"@tanstack/react-router": "^1.58.3",
"@xterm/xterm": "^5.5.0",
"algoliasearch": "^4.14.3",
"axios": "~1.7.4",
Expand Down
11 changes: 9 additions & 2 deletions packages/manager/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,14 @@ import {
import withFeatureFlagProvider from 'src/containers/withFeatureFlagProvider.container';
import TheApplicationIsOnFire from 'src/features/TheApplicationIsOnFire';

import { GoTo } from './GoTo';
import { MainContent } from './MainContent';
import { SplashScreen } from './components/SplashScreen';
import { GoTo } from './GoTo';
import { useAdobeAnalytics } from './hooks/useAdobeAnalytics';
import { useInitialRequests } from './hooks/useInitialRequests';
import { useNewRelic } from './hooks/useNewRelic';
import { MainContent } from './MainContent';
import { useEventsPoller } from './queries/events/events';
// import { Router } from './Router';
import { useSetupFeatureFlags } from './useSetupFeatureFlags';

// Ensure component's display name is 'App'
Expand Down Expand Up @@ -47,6 +48,12 @@ const BaseApp = withDocumentTitleProvider(
<GoTo />
<DocumentTitleSegment segment="Akamai Cloud Manager" />
<MainContent />
{/*
* This will be our new entry point
* Leaving this commented out so reviewers can test the app with the new routing at any point by replacing
* MainContent with <Router />.
<Router />
*/}
<GlobalListeners />
</ErrorBoundary>
);
Expand Down
85 changes: 85 additions & 0 deletions packages/manager/src/Root.styles.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
import { makeStyles } from 'tss-react/mui';

import { SIDEBAR_WIDTH } from 'src/components/PrimaryNav/SideMenu';

import type { Theme } from '@mui/material/styles';

export const useStyles = makeStyles()((theme: Theme) => ({
activationWrapper: {
padding: theme.spacing(4),
[theme.breakpoints.up('xl')]: {
margin: '0 auto',
width: '50%',
},
},
appFrame: {
backgroundColor: theme.bg.app,
display: 'flex',
flexDirection: 'column',
minHeight: '100vh',
position: 'relative',
zIndex: 1,
},
bgStyling: {
backgroundColor: theme.bg.main,
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
minHeight: '100vh',
},
cmrWrapper: {
maxWidth: `${theme.breakpoints.values.lg}px !important`,
padding: `${theme.spacing(3)} 0`,
paddingTop: 12,
[theme.breakpoints.between('md', 'xl')]: {
paddingLeft: theme.spacing(2),
paddingRight: theme.spacing(2),
},
[theme.breakpoints.down('md')]: {
paddingLeft: 0,
paddingRight: 0,
paddingTop: theme.spacing(2),
},
transition: theme.transitions.create('opacity'),
},
content: {
flex: 1,
[theme.breakpoints.up('md')]: {
marginLeft: SIDEBAR_WIDTH,
},
transition: 'margin-left .1s linear',
},
fullWidthContent: {
marginLeft: 0,
[theme.breakpoints.up('md')]: {
marginLeft: 52,
},
},
grid: {
marginLeft: 0,
marginRight: 0,
[theme.breakpoints.up('lg')]: {
height: '100%',
},
width: '100%',
},
logo: {
'& > g': {
fill: theme.color.black,
},
},
switchWrapper: {
'& > .MuiGrid-container': {
maxWidth: theme.breakpoints.values.lg,
width: '100%',
},
'&.mlMain': {
[theme.breakpoints.up('lg')]: {
maxWidth: '78.8%',
},
},
flex: 1,
maxWidth: '100%',
position: 'relative',
},
}));
130 changes: 130 additions & 0 deletions packages/manager/src/Root.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,130 @@
import Grid from '@mui/material/Unstable_Grid2';
import { Outlet } from '@tanstack/react-router';
import React from 'react';

import Logo from 'src/assets/logo/akamai-logo.svg';
import { Box } from 'src/components/Box';
import { MainContentBanner } from 'src/components/MainContentBanner';
import { MaintenanceScreen } from 'src/components/MaintenanceScreen';
import { SideMenu } from 'src/components/PrimaryNav/SideMenu';
import { SuspenseLoader } from 'src/components/SuspenseLoader';
import { useDialogContext } from 'src/context/useDialogContext';
import { Footer } from 'src/features/Footer';
import { GlobalNotifications } from 'src/features/GlobalNotifications/GlobalNotifications';
import {
notificationCenterContext,
useNotificationContext,
} from 'src/features/NotificationCenter/NotificationCenterContext';
import { TopMenu } from 'src/features/TopMenu/TopMenu';
import {
useMutatePreferences,
usePreferences,
} from 'src/queries/profile/preferences';

import { ENABLE_MAINTENANCE_MODE } from './constants';
import { complianceUpdateContext } from './context/complianceUpdateContext';
import { sessionExpirationContext } from './context/sessionExpirationContext';
import { switchAccountSessionContext } from './context/switchAccountSessionContext';
import { useGlobalErrors } from './hooks/useGlobalErrors';
import { useProfile } from './queries/profile/profile';
import { useStyles } from './Root.styles';

export const Root = () => {
const { classes, cx } = useStyles();
const { data: preferences } = usePreferences();
const { mutateAsync: updatePreferences } = useMutatePreferences();

const globalErrors = useGlobalErrors();

const NotificationProvider = notificationCenterContext.Provider;
const contextValue = useNotificationContext();

const ComplianceUpdateProvider = complianceUpdateContext.Provider;
const complianceUpdateContextValue = useDialogContext();

const SwitchAccountSessionProvider = switchAccountSessionContext.Provider;
const switchAccountSessionContextValue = useDialogContext({
isOpen: false,
});

const SessionExpirationProvider = sessionExpirationContext.Provider;
const sessionExpirationContextValue = useDialogContext({
isOpen: false,
});

const [menuIsOpen, toggleMenu] = React.useState<boolean>(false);

const { data: profile } = useProfile();
const username = profile?.username || '';

const desktopMenuIsOpen = preferences?.desktop_sidebar_open ?? false;

const desktopMenuToggle = () => {
updatePreferences({
desktop_sidebar_open: !preferences?.desktop_sidebar_open,
});
};

if (globalErrors.account_unactivated) {
return (
<div className={classes.bgStyling}>
<div className={classes.activationWrapper}>
<Box style={{ display: 'flex', justifyContent: 'center' }}>
<Logo className={classes.logo} width={215} />
</Box>
<Outlet />
</div>
</div>
);
}

if (globalErrors.api_maintenance_mode || ENABLE_MAINTENANCE_MODE) {
return <MaintenanceScreen />;
}

return (
<div className={classes.appFrame} data-testid="root">
<SessionExpirationProvider value={sessionExpirationContextValue}>
<SwitchAccountSessionProvider value={switchAccountSessionContextValue}>
<ComplianceUpdateProvider value={complianceUpdateContextValue}>
<NotificationProvider value={contextValue}>
<SideMenu
closeMenu={() => toggleMenu(false)}
collapse={desktopMenuIsOpen || false}
open={menuIsOpen}
/>
<div
className={cx(classes.content, {
[classes.fullWidthContent]: desktopMenuIsOpen,
})}
>
<MainContentBanner />
<TopMenu
desktopMenuToggle={desktopMenuToggle}
isSideMenuOpen={!desktopMenuIsOpen}
openSideMenu={() => toggleMenu(true)}
username={username}
/>
<main
className={classes.cmrWrapper}
id="main-content"
role="main"
>
<Grid className={classes.grid} container spacing={0}>
<Grid className={cx(classes.switchWrapper, 'p0')}>
<GlobalNotifications />
<React.Suspense fallback={<SuspenseLoader />}>
<Outlet />
</React.Suspense>
</Grid>
</Grid>
</main>
</div>
</NotificationProvider>
<Footer desktopMenuIsOpen={desktopMenuIsOpen} />
</ComplianceUpdateProvider>
</SwitchAccountSessionProvider>
</SessionExpirationProvider>
</div>
);
};
34 changes: 34 additions & 0 deletions packages/manager/src/Router.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { RouterProvider } from '@tanstack/react-router';
import * as React from 'react';

import { useFlags } from 'src/hooks/useFlags';
import { useGlobalErrors } from 'src/hooks/useGlobalErrors';

import { useIsACLPEnabled } from './features/CloudPulse/Utils/utils';
import { useIsDatabasesEnabled } from './features/Databases/utilities';
import { useIsPlacementGroupsEnabled } from './features/PlacementGroups/utils';
import { useAccountSettings } from './queries/account/settings';
import { router } from './routes';

export const Router = () => {
const { data: accountSettings } = useAccountSettings();
const { isDatabasesEnabled } = useIsDatabasesEnabled();
const { isPlacementGroupsEnabled } = useIsPlacementGroupsEnabled();
const { isACLPEnabled } = useIsACLPEnabled();
const globalErrors = useGlobalErrors();
const flags = useFlags();

// Update the router's context
router.update({
context: {
accountSettings,
globalErrors,
isACLPEnabled,
isDatabasesEnabled,
isPlacementGroupsEnabled,
selfServeBetas: flags.selfServeBetas,
},
});

return <RouterProvider router={router} />;
};
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import Warning from '@mui/icons-material/CheckCircle';
import { createLazyRoute } from '@tanstack/react-router';
import * as React from 'react';
import { useHistory } from 'react-router-dom';

Expand Down Expand Up @@ -69,4 +70,10 @@ const AccountActivationLanding = () => {
);
};

export const accountActivationLandingLazyRoute = createLazyRoute(
'/account-activation'
)({
component: AccountActivationLanding,
});

export default React.memo(AccountActivationLanding);
12 changes: 8 additions & 4 deletions packages/manager/src/features/Account/AccountLanding.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,9 @@
import { createLazyRoute } from '@tanstack/react-router';
import * as React from 'react';
import { matchPath, useHistory, useLocation } from 'react-router-dom';

import { DocumentTitleSegment } from 'src/components/DocumentTitle';
import {
LandingHeader,
LandingHeaderProps,
} from 'src/components/LandingHeader';
import { LandingHeader } from 'src/components/LandingHeader';
import { SuspenseLoader } from 'src/components/SuspenseLoader';
import { SafeTabPanel } from 'src/components/Tabs/SafeTabPanel';
import { TabLinkList } from 'src/components/Tabs/TabLinkList';
Expand All @@ -23,6 +21,8 @@ import AccountLogins from './AccountLogins';
import { SwitchAccountButton } from './SwitchAccountButton';
import { SwitchAccountDrawer } from './SwitchAccountDrawer';

import type { LandingHeaderProps } from 'src/components/LandingHeader';

const Billing = React.lazy(() =>
import('src/features/Billing/BillingDetail').then((module) => ({
default: module.BillingDetail,
Expand Down Expand Up @@ -217,4 +217,8 @@ const AccountLanding = () => {
);
};

export const accountLandingLazyRoute = createLazyRoute('/account')({
component: AccountLanding,
});

export default AccountLanding;
5 changes: 3 additions & 2 deletions packages/manager/src/features/Betas/BetasLanding.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { Stack } from 'src/components/Stack';
import * as React from 'react';

import { LandingHeader } from 'src/components/LandingHeader/LandingHeader';
import { ProductInformationBanner } from 'src/components/ProductInformationBanner/ProductInformationBanner';
import { Stack } from 'src/components/Stack';
import { BetaDetailsList } from 'src/features/Betas/BetaDetailsList';
import { useAccountBetasQuery } from 'src/queries/account/betas';
import { useBetasQuery } from 'src/queries/betas';
import { categorizeBetasByStatus } from 'src/utilities/betaUtils';
import { AccountBeta, Beta } from '@linode/api-v4';

import type { AccountBeta, Beta } from '@linode/api-v4';

const BetasLanding = () => {
const {
Expand Down
Loading

0 comments on commit ccb4e0b

Please sign in to comment.