Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor: [M3-8623] - Introduce TanStack Router #10997

Merged
merged 45 commits into from
Oct 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
8bd0774
Initial commit: proof of concept
abailly-akamai Sep 24, 2024
8b61d5b
Route Updates and types
abailly-akamai Sep 24, 2024
373537c
NBs & Domains
abailly-akamai Sep 24, 2024
e79a3ad
testing + utils
abailly-akamai Sep 25, 2024
65b366c
better util types
abailly-akamai Sep 25, 2024
6bb0d5e
testing existing routes (wip)
abailly-akamai Sep 25, 2024
2930229
more testing before more routes
abailly-akamai Sep 26, 2024
a8820e7
save progress
abailly-akamai Sep 26, 2024
99655ee
save progress
abailly-akamai Sep 26, 2024
696df7c
save progress
abailly-akamai Sep 26, 2024
257177a
type improvements
abailly-akamai Sep 26, 2024
dbf91d9
make basic account routing work
bnussman Sep 26, 2024
6219592
Revert "make basic account routing work"
bnussman Sep 26, 2024
f4826fa
event routes
abailly-akamai Sep 27, 2024
e44e781
firewalls
abailly-akamai Sep 27, 2024
3228ae1
databases
abailly-akamai Sep 27, 2024
cc7fae1
update maincontent v2
abailly-akamai Sep 27, 2024
7846dc0
betas
abailly-akamai Sep 27, 2024
861fe76
VPCs
abailly-akamai Sep 27, 2024
0fb58fd
Cloud pulse and cleanup
abailly-akamai Sep 27, 2024
2167131
Rename MainContentV2 to Router
abailly-akamai Sep 27, 2024
cd8f378
update vpc routes to use reccomended lazy routing
bnussman Sep 27, 2024
4120de5
support and volumes lazy imports
abailly-akamai Sep 27, 2024
5093af7
post rebase yarn
abailly-akamai Sep 28, 2024
5fcb678
lazy imports/exports stackscripts, volumes, profile
abailly-akamai Sep 28, 2024
c0adce6
lazy imports/exports placement groups
abailly-akamai Sep 30, 2024
8604c52
lazy imports/exports obj + nb
abailly-akamai Sep 30, 2024
f7e2089
lazy imports/exports managed
abailly-akamai Sep 30, 2024
41b77a9
lazy imports/exports longview and linodes
abailly-akamai Sep 30, 2024
eff58c7
lazy imports/exports images and k8
abailly-akamai Sep 30, 2024
f90191e
lazy imports/exports events & firewalls
abailly-akamai Sep 30, 2024
72cb808
lazy imports/exports remaining
abailly-akamai Sep 30, 2024
4e3405e
some cleanup
abailly-akamai Sep 30, 2024
4f32ff5
More cleanup
abailly-akamai Oct 1, 2024
bb7191e
more account routes
abailly-akamai Oct 1, 2024
c83738a
missing domains and linodes routes
abailly-akamai Oct 2, 2024
4ecfed9
missing longview routes
abailly-akamai Oct 2, 2024
43b9a8c
missing routes
abailly-akamai Oct 2, 2024
1fbcf22
wrap up missing routes
abailly-akamai Oct 2, 2024
b808165
light cleanup
abailly-akamai Oct 3, 2024
a9cc322
rename stylesheet
abailly-akamai Oct 3, 2024
6cd3d82
Added changeset: Introduce TanStack Router
abailly-akamai Oct 3, 2024
526fedd
feedback @bnussman-akamai
abailly-akamai Oct 3, 2024
8f5f1a1
feedback @jaalah-akamai
abailly-akamai Oct 4, 2024
19971cf
post rebase fix
abailly-akamai Oct 4, 2024
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
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',
},
}));
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All existing styles from <MainContent /> extracted to their own file - this also isn't affecting the current experience

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>
);
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I decoupled the root from the so the saparation of concern between routing and composition is more clear. This is not affecting the current experience (unless switching things in <App /> manually)

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} />;
};
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our new entrypoint to load all routes in Cloud Manager (not active in this PR!)

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,
});
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All createLazyRoute exports you will see in this PR aim to facilitate lazy loading smaller bundles: https://tanstack.com/router/latest/docs/framework/react/api/router/createLazyRouteFunction#createlazyroute-function

Those get imported in the /routes/* files via .lazy()


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