1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-04-01 01:18:10 +02:00

Refactor/lazy load (#2842)

Currently our bundle size is clocking in at: 1,798.28 kB │ gzip: 558.42 kB
After the changes: 1,299.32 kB │ gzip: 403.26 kB
This commit is contained in:
Fredrik Strand Oseberg 2023-01-12 11:34:45 +01:00 committed by GitHub
parent ce815e5f29
commit 0dcf28a0f0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
31 changed files with 279 additions and 533 deletions

View File

@ -66,3 +66,7 @@ Whenever there are changes to the backend API, the client should be regenerated:
This script assumes that you have a running instance of the enterprise backend at `http://localhost:4242`.
The new OpenAPI client will be generated from the runtime schema of this instance.
The target URL can be changed by setting the `UNLEASH_OPENAPI_URL` env var.
## Analyzing bundle size
`npx vite-bundle-visualizer` in the root of the frontend directory

View File

@ -14,7 +14,7 @@
"start": "vite",
"start:prod": "vite build && vite preview",
"start:sandbox": "UNLEASH_API=https://sandbox.getunleash.io/ospro yarn run start",
"start:enterprise": "UNLEASH_API=https://unleash4.herokuapp.com yarn run start",
"start:enterprise": "UNLEASH_API=https://unleash.herokuapp.com yarn run start",
"start:demo": "UNLEASH_BASE_PATH=/demo/ yarn start",
"test": "tsc && vitest run",
"test:snapshot": "yarn test -u",

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 113 KiB

View File

@ -0,0 +1,52 @@
import { Routes, Route } from 'react-router-dom';
import { ApiPage } from './api';
import { CreateApiToken } from './apiToken/CreateApiToken/CreateApiToken';
import { AuthSettings } from './auth/AuthSettings';
import { Billing } from './billing/Billing';
import FlaggedBillingRedirect from './billing/FlaggedBillingRedirect/FlaggedBillingRedirect';
import { CorsAdmin } from './cors';
import { CreateGroup } from './groups/CreateGroup/CreateGroup';
import { EditGroup } from './groups/EditGroup/EditGroup';
import { Group } from './groups/Group/Group';
import { GroupsAdmin } from './groups/GroupsAdmin';
import { InstanceAdmin } from './instance-admin/InstanceAdmin';
import { MaintenanceAdmin } from './maintenance';
import AdminMenu from './menu/AdminMenu';
import { Network } from './network/Network';
import CreateProjectRole from './projectRoles/CreateProjectRole/CreateProjectRole';
import EditProjectRole from './projectRoles/EditProjectRole/EditProjectRole';
import ProjectRoles from './projectRoles/ProjectRoles/ProjectRoles';
import { ServiceAccounts } from './serviceAccounts/ServiceAccounts';
import CreateUser from './users/CreateUser/CreateUser';
import EditUser from './users/EditUser/EditUser';
import { InviteLink } from './users/InviteLink/InviteLink';
import UsersAdmin from './users/UsersAdmin';
export const Admin = () => (
<>
<AdminMenu />
<Routes>
<Route path="users" element={<UsersAdmin />} />
<Route path="create-project-role" element={<CreateProjectRole />} />
<Route path="roles/:id/edit" element={<EditProjectRole />} />
<Route path="api" element={<ApiPage />} />
<Route path="api/create-token" element={<CreateApiToken />} />
<Route path="users/:id/edit" element={<EditUser />} />
<Route path="service-accounts" element={<ServiceAccounts />} />
<Route path="create-user" element={<CreateUser />} />
<Route path="invite-link" element={<InviteLink />} />
<Route path="groups" element={<GroupsAdmin />} />
<Route path="groups/create-group" element={<CreateGroup />} />
<Route path="groups/:groupId/edit" element={<EditGroup />} />
<Route path="groups/:groupId" element={<Group />} />
<Route path="roles" element={<ProjectRoles />} />
<Route path="instance" element={<InstanceAdmin />} />
<Route path="network/*" element={<Network />} />
<Route path="maintenance" element={<MaintenanceAdmin />} />
<Route path="cors" element={<CorsAdmin />} />
<Route path="auth" element={<AuthSettings />} />
<Route path="admin-invoices" element={<FlaggedBillingRedirect />} />
<Route path="billing" element={<Billing />} />
</Routes>
</>
);

View File

@ -0,0 +1,3 @@
import { Navigate } from 'react-router-dom';
export const AdminRedirect = () => <Navigate to="/admin/users" replace />;

View File

@ -0,0 +1,3 @@
import { lazy } from 'react';
export const LazyAdmin = lazy(() => import('./LazyAdminExport'));

View File

@ -0,0 +1,7 @@
// Lazy loading only supports default export. We have an ADR
// that says we should only use named exports, because
// it makes it harder to diverge from the name of the component when
// importing it in other files.
import { Admin } from './Admin';
export default Admin;

View File

@ -1,18 +1,8 @@
import { useLocation } from 'react-router-dom';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ApiTokenPage } from 'component/admin/apiToken/ApiTokenPage/ApiTokenPage';
import AdminMenu from '../menu/AdminMenu';
const ApiPage = () => {
const { pathname } = useLocation();
const showAdminMenu = pathname.includes('/admin/');
export const ApiPage = () => {
return (
<div>
<ConditionallyRender
condition={showAdminMenu}
show={<AdminMenu />}
/>
<ApiTokenPage />
</div>
);

View File

@ -1,5 +1,4 @@
import React from 'react';
import AdminMenu from '../menu/AdminMenu';
import { Alert } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
@ -34,7 +33,6 @@ export const AuthSettings = () => {
return (
<div>
<AdminMenu />
<PageContent header="Single Sign-On">
<ConditionallyRender
condition={authenticationType === 'enterprise'}

View File

@ -1,4 +1,3 @@
import AdminMenu from '../menu/AdminMenu';
import { PageContent } from 'component/common/PageContent/PageContent';
import { useContext, useEffect } from 'react';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
@ -32,7 +31,6 @@ export const Billing = () => {
return (
<div>
<AdminMenu />
<PageContent header="Billing" isLoading={loading}>
<ConditionallyRender
condition={isBilling}

View File

@ -1,6 +1,5 @@
import { useLocation } from 'react-router-dom';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import AdminMenu from '../menu/AdminMenu';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AccessContext from 'contexts/AccessContext';
@ -13,16 +12,10 @@ import { CorsForm } from 'component/admin/cors/CorsForm';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
export const CorsAdmin = () => {
const { pathname } = useLocation();
const showAdminMenu = pathname.includes('/admin/');
const { hasAccess } = useContext(AccessContext);
return (
<div>
<ConditionallyRender
condition={showAdminMenu}
show={<AdminMenu />}
/>
<ConditionallyRender
condition={hasAccess(ADMIN)}
show={<CorsPage />}

View File

@ -1,10 +1,8 @@
import { GroupsList } from './GroupsList/GroupsList';
import AdminMenu from '../menu/AdminMenu';
export const GroupsAdmin = () => {
return (
<div>
<AdminMenu />
<GroupsList />
</div>
);

View File

@ -1,12 +0,0 @@
import React from 'react';
import PropTypes from 'prop-types';
import { Navigate } from 'react-router-dom';
const render = () => <Navigate to="/admin/users" replace />;
render.propTypes = {
match: PropTypes.object.isRequired,
history: PropTypes.object.isRequired,
};
export default render;

View File

@ -1,10 +1,8 @@
import AdminMenu from '../menu/AdminMenu';
import { InstanceStats } from './InstanceStats/InstanceStats';
export const InstanceAdmin = () => {
return (
<div>
<AdminMenu />
<InstanceStats />
</div>
);

View File

@ -1,6 +1,4 @@
import { useLocation } from 'react-router-dom';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import AdminMenu from '../menu/AdminMenu';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AccessContext from 'contexts/AccessContext';
@ -13,16 +11,10 @@ import { MaintenanceTooltip } from './MaintenanceTooltip';
import { MaintenanceToggle } from './MaintenanceToggle';
export const MaintenanceAdmin = () => {
const { pathname } = useLocation();
const showAdminMenu = pathname.includes('/admin/');
const { hasAccess } = useContext(AccessContext);
return (
<div>
<ConditionallyRender
condition={showAdminMenu}
show={<AdminMenu />}
/>
<ConditionallyRender
condition={hasAccess(ADMIN)}
show={<MaintenancePage />}

View File

@ -1,5 +1,5 @@
import { lazy } from 'react';
import AdminMenu from '../menu/AdminMenu';
import { styled, Tab, Tabs } from '@mui/material';
import { Route, Routes, useLocation } from 'react-router-dom';
import { CenteredNavLink } from '../menu/CenteredNavLink';
@ -30,7 +30,6 @@ export const Network = () => {
return (
<div>
<AdminMenu />
<StyledPageContent
headerClass="page-header"
header={

View File

@ -2,7 +2,6 @@ import { useContext } from 'react';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import AdminMenu from 'component/admin/menu/AdminMenu';
import ProjectRoleList from './ProjectRoleList/ProjectRoleList';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
@ -11,7 +10,6 @@ const ProjectRoles = () => {
return (
<div>
<AdminMenu />
<ConditionallyRender
condition={hasAccess(ADMIN)}
show={<ProjectRoleList />}

View File

@ -1,5 +1,4 @@
import { useContext } from 'react';
import AdminMenu from '../menu/AdminMenu';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
@ -11,7 +10,6 @@ export const ServiceAccounts = () => {
return (
<div>
<AdminMenu />
<ConditionallyRender
condition={hasAccess(ADMIN)}
show={<ServiceAccountsTable />}

View File

@ -1,18 +1,16 @@
import { useContext } from 'react';
import UsersList from './UsersList/UsersList';
import AdminMenu from '../menu/AdminMenu';
import AccessContext from 'contexts/AccessContext';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { AdminAlert } from 'component/common/AdminAlert/AdminAlert';
import { InviteLinkBar } from './InviteLinkBar/InviteLinkBar';
const UsersAdmin = () => {
export const UsersAdmin = () => {
const { hasAccess } = useContext(AccessContext);
return (
<div>
<AdminMenu />
<InviteLinkBar />
<ConditionallyRender
condition={hasAccess(ADMIN)}

View File

@ -1,23 +1,24 @@
import { weightTypes } from '../feature/FeatureView/FeatureVariants/FeatureVariantsList/AddFeatureVariant/enums';
import { IUiConfig } from 'interfaces/uiConfig';
import { IRoute } from 'interfaces/route';
import { INavigationMenuItem } from 'interfaces/route';
import { IFeatureVariant } from 'interfaces/featureToggle';
import { format, isValid } from 'date-fns';
export const filterByConfig = (config: IUiConfig) => (r: IRoute) => {
if (r.flag) {
// Check if the route's `flag` is enabled in IUiConfig.flags.
const flags = config.flags as unknown as Record<string, boolean>;
return Boolean(flags[r.flag]);
}
export const filterByConfig =
(config: IUiConfig) => (r: INavigationMenuItem) => {
if (r.flag) {
// Check if the route's `flag` is enabled in IUiConfig.flags.
const flags = config.flags as unknown as Record<string, boolean>;
return Boolean(flags[r.flag]);
}
if (r.configFlag) {
// Check if the route's `configFlag` is enabled in IUiConfig.
return Boolean(config[r.configFlag]);
}
if (r.configFlag) {
// Check if the route's `configFlag` is enabled in IUiConfig.
return Boolean(config[r.configFlag]);
}
return true;
};
return true;
};
export const scrollToTop = () => {
window.scrollTo(0, 0);

View File

@ -0,0 +1,7 @@
// Lazy loading only supports default export. We have an ADR
// that says we should only use named exports, because
// it makes it harder to diverge from the name of the component when
// importing it in other files.
import { FeatureView } from './FeatureView';
export default FeatureView;

View File

@ -0,0 +1,3 @@
import { lazy } from 'react';
export const LazyFeatureView = lazy(() => import('./FeatureViewLazyExport'));

View File

@ -9,7 +9,7 @@ import NavigationLink from '../NavigationLink/NavigationLink';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import { basePath } from 'utils/formatPath';
import { IFlags } from 'interfaces/uiConfig';
import { IRoute } from 'interfaces/route';
import { INavigationMenuItem } from 'interfaces/route';
import styles from './DrawerMenu.module.scss'; // FIXME: useStyle - theme
interface IDrawerMenuProps {
@ -25,9 +25,9 @@ interface IDrawerMenuProps {
}>;
flags?: IFlags;
routes: {
mainNavRoutes: IRoute[];
mobileRoutes: IRoute[];
adminRoutes: IRoute[];
mainNavRoutes: INavigationMenuItem[];
mobileRoutes: INavigationMenuItem[];
adminRoutes: INavigationMenuItem[];
};
}

View File

@ -26,12 +26,16 @@ import { flexRow, focusable } from 'themes/themeStyles';
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { IPermission } from 'interfaces/user';
import { NavigationMenu } from './NavigationMenu/NavigationMenu';
import { getRoutes } from 'component/menu/routes';
import {
getRoutes,
adminMenuRoutes,
getCondensedRoutes,
} from 'component/menu/routes';
import { KeyboardArrowDown } from '@mui/icons-material';
import { filterByConfig } from 'component/common/util';
import { useAuthPermissions } from 'hooks/api/getters/useAuth/useAuthPermissions';
import { useId } from 'hooks/useId';
import { IRoute } from 'interfaces/route';
import { INavigationMenuItem } from 'interfaces/route';
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
import { useThemeMode } from 'hooks/useThemeMode';
@ -134,14 +138,24 @@ const Header: VFC = () => {
const routes = getRoutes();
const filterByEnterprise = (route: IRoute): boolean => {
const filterByEnterprise = (route: INavigationMenuItem): boolean => {
return !route.menu.isEnterprise || !isOss();
};
const filteredMainRoutes = {
mainNavRoutes: routes.mainNavRoutes.filter(filterByConfig(uiConfig)),
mobileRoutes: routes.mobileRoutes.filter(filterByConfig(uiConfig)),
adminRoutes: routes.adminRoutes
mainNavRoutes: getCondensedRoutes(routes.mainNavRoutes)
.concat([
{
path: '/admin/api',
title: 'API access',
menu: {},
},
])
.filter(filterByConfig(uiConfig)),
mobileRoutes: getCondensedRoutes(routes.mobileRoutes).filter(
filterByConfig(uiConfig)
),
adminRoutes: adminMenuRoutes
.filter(filterByConfig(uiConfig))
.filter(filterByEnterprise)
.map(route => ({

View File

@ -11,7 +11,14 @@ exports[`returns all baseRoutes 1`] = `
"type": "protected",
},
{
"component": [Function],
"component": {
"$$typeof": Symbol(react.lazy),
"_init": [Function],
"_payload": {
"_result": [Function],
"_status": -1,
},
},
"enterprise": true,
"menu": {},
"parent": "/projects",
@ -53,7 +60,14 @@ exports[`returns all baseRoutes 1`] = `
"type": "protected",
},
{
"component": [Function],
"component": {
"$$typeof": Symbol(react.lazy),
"_init": [Function],
"_payload": {
"_result": [Function],
"_status": -1,
},
},
"menu": {},
"parent": "/projects",
"path": "/projects/:projectId/features/:featureId/*",
@ -77,7 +91,14 @@ exports[`returns all baseRoutes 1`] = `
"type": "protected",
},
{
"component": [Function],
"component": {
"$$typeof": Symbol(react.lazy),
"_init": [Function],
"_payload": {
"_result": [Function],
"_status": -1,
},
},
"flag": "P",
"menu": {},
"parent": "/projects",
@ -333,199 +354,6 @@ exports[`returns all baseRoutes 1`] = `
"title": "Archived toggles",
"type": "protected",
},
{
"component": [Function],
"menu": {},
"parent": "/admin",
"path": "/admin/api/create-token",
"title": "API access",
"type": "protected",
},
{
"component": [Function],
"flag": "RE",
"menu": {},
"path": "/admin/create-project-role",
"title": "Create",
"type": "protected",
},
{
"component": [Function],
"flag": "RE",
"menu": {},
"path": "/admin/roles/:id/edit",
"title": "Edit",
"type": "protected",
},
{
"component": [Function],
"menu": {
"advanced": true,
"mobile": true,
},
"parent": "/admin",
"path": "/admin/api",
"title": "API access",
"type": "protected",
},
{
"component": [Function],
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/users",
"title": "Users",
"type": "protected",
},
{
"component": [Function],
"flag": "serviceAccounts",
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/service-accounts",
"title": "Service accounts",
"type": "protected",
},
{
"component": [Function],
"menu": {},
"parent": "/admin",
"path": "/admin/create-user",
"title": "Users",
"type": "protected",
},
{
"component": [Function],
"menu": {},
"parent": "/admin",
"path": "/admin/invite-link",
"title": "Invite link",
"type": "protected",
},
{
"component": [Function],
"flag": "UG",
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/groups",
"title": "Groups",
"type": "protected",
},
{
"component": [Function],
"flag": "UG",
"menu": {},
"parent": "/admin",
"path": "/admin/groups/:groupId",
"title": ":groupId",
"type": "protected",
},
{
"component": [Function],
"flag": "UG",
"menu": {},
"parent": "/admin/groups",
"path": "/admin/groups/create-group",
"title": "Create group",
"type": "protected",
},
{
"component": [Function],
"flag": "UG",
"menu": {},
"parent": "/admin/groups",
"path": "/admin/groups/:groupId/edit",
"title": "Edit group",
"type": "protected",
},
{
"component": [Function],
"flag": "RE",
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/roles",
"title": "Project roles",
"type": "protected",
},
{
"component": [Function],
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/auth",
"title": "Single sign-on",
"type": "protected",
},
{
"component": [Function],
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/instance",
"title": "Instance stats",
"type": "protected",
},
{
"component": [Function],
"flag": "networkView",
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/network/*",
"title": "Network",
"type": "protected",
},
{
"component": [Function],
"flag": "maintenance",
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/maintenance",
"title": "Maintenance",
"type": "protected",
},
{
"component": [Function],
"flag": "embedProxyFrontend",
"menu": {
"adminSettings": true,
},
"parent": "/admin",
"path": "/admin/cors",
"title": "CORS origins",
"type": "protected",
},
{
"component": [Function],
"menu": {},
"parent": "/admin",
"path": "/admin/billing",
"title": "Billing",
"type": "protected",
},
{
"component": [Function],
"menu": {
"adminSettings": true,
"isEnterprise": true,
},
"parent": "/admin",
"path": "/admin-invoices",
"title": "Billing & invoices",
"type": "protected",
},
{
"component": [Function],
"hidden": false,
@ -534,6 +362,21 @@ exports[`returns all baseRoutes 1`] = `
"title": "Admin",
"type": "protected",
},
{
"component": {
"$$typeof": Symbol(react.lazy),
"_init": [Function],
"_payload": {
"_result": [Function],
"_status": -1,
},
},
"hidden": false,
"menu": {},
"path": "/admin/*",
"title": "Admin",
"type": "protected",
},
{
"component": [Function],
"menu": {},

View File

@ -3,33 +3,19 @@ import { StrategyView } from 'component/strategies/StrategyView/StrategyView';
import { StrategiesList } from 'component/strategies/StrategiesList/StrategiesList';
import { TagTypeList } from 'component/tags/TagTypeList/TagTypeList';
import { AddonList } from 'component/addons/AddonList/AddonList';
import Admin from 'component/admin';
import AdminApi from 'component/admin/api';
import AdminUsers from 'component/admin/users/UsersAdmin';
import { GroupsAdmin } from 'component/admin/groups/GroupsAdmin';
import { AuthSettings } from 'component/admin/auth/AuthSettings';
import Login from 'component/user/Login/Login';
import { EEA, P, RE, SE, UG } from 'component/common/flags';
import { NewUser } from 'component/user/NewUser/NewUser';
import ResetPassword from 'component/user/ResetPassword/ResetPassword';
import ForgottenPassword from 'component/user/ForgottenPassword/ForgottenPassword';
import { ProjectListNew } from 'component/project/ProjectList/ProjectList';
import Project from 'component/project/Project/Project';
import RedirectArchive from 'component/archive/RedirectArchive';
import { FeatureView } from 'component/feature/FeatureView/FeatureView';
import ProjectRoles from 'component/admin/projectRoles/ProjectRoles/ProjectRoles';
import CreateProjectRole from 'component/admin/projectRoles/CreateProjectRole/CreateProjectRole';
import EditProjectRole from 'component/admin/projectRoles/EditProjectRole/EditProjectRole';
import CreateUser from 'component/admin/users/CreateUser/CreateUser';
import EditUser from 'component/admin/users/EditUser/EditUser';
import { CreateApiToken } from 'component/admin/apiToken/CreateApiToken/CreateApiToken';
import CreateEnvironment from 'component/environments/CreateEnvironment/CreateEnvironment';
import EditEnvironment from 'component/environments/EditEnvironment/EditEnvironment';
import { EditContext } from 'component/context/EditContext/EditContext';
import EditTagType from 'component/tags/EditTagType/EditTagType';
import CreateTagType from 'component/tags/CreateTagType/CreateTagType';
import EditProject from 'component/project/Project/EditProject/EditProject';
import CreateProject from 'component/project/Project/CreateProject/CreateProject';
import CreateFeature from 'component/feature/CreateFeature/CreateFeature';
import EditFeature from 'component/feature/EditFeature/EditFeature';
import { ApplicationEdit } from 'component/application/ApplicationEdit/ApplicationEdit';
@ -46,23 +32,17 @@ import { SplashPage } from 'component/splash/SplashPage/SplashPage';
import { CreateUnleashContextPage } from 'component/context/CreateUnleashContext/CreateUnleashContextPage';
import { CreateSegment } from 'component/segments/CreateSegment/CreateSegment';
import { EditSegment } from 'component/segments/EditSegment/EditSegment';
import { IRoute } from 'interfaces/route';
import { INavigationMenuItem, IRoute } from 'interfaces/route';
import { EnvironmentTable } from 'component/environments/EnvironmentTable/EnvironmentTable';
import { SegmentTable } from 'component/segments/SegmentTable';
import FlaggedBillingRedirect from 'component/admin/billing/FlaggedBillingRedirect/FlaggedBillingRedirect';
import { FeaturesArchiveTable } from '../archive/FeaturesArchiveTable';
import { Billing } from 'component/admin/billing/Billing';
import { Group } from 'component/admin/groups/Group/Group';
import { CreateGroup } from 'component/admin/groups/CreateGroup/CreateGroup';
import { EditGroup } from 'component/admin/groups/EditGroup/EditGroup';
import { LazyPlayground } from 'component/playground/Playground/LazyPlayground';
import { CorsAdmin } from 'component/admin/cors';
import { InviteLink } from 'component/admin/users/InviteLink/InviteLink';
import { Profile } from 'component/user/Profile/Profile';
import { InstanceAdmin } from '../admin/instance-admin/InstanceAdmin';
import { Network } from 'component/admin/network/Network';
import { MaintenanceAdmin } from '../admin/maintenance';
import { ServiceAccounts } from 'component/admin/serviceAccounts/ServiceAccounts';
import { LazyCreateProject } from 'component/project/Project/CreateProject/LazyCreateProject';
import { LazyFeatureView } from 'component/feature/FeatureView/LazyFeatureView';
import { LazyAdmin } from 'component/admin/LazyAdmin';
import { LazyProject } from 'component/project/Project/LazyProject';
import { AdminRedirect } from 'component/admin/AdminRedirect';
export const routes: IRoute[] = [
// Splash
@ -80,7 +60,7 @@ export const routes: IRoute[] = [
path: '/projects/create',
parent: '/projects',
title: 'Create',
component: CreateProject,
component: LazyCreateProject,
type: 'protected',
enterprise: true,
menu: {},
@ -122,7 +102,7 @@ export const routes: IRoute[] = [
path: '/projects/:projectId/features/:featureId/*',
parent: '/projects',
title: 'FeatureView',
component: FeatureView,
component: LazyFeatureView,
type: 'protected',
menu: {},
},
@ -146,7 +126,7 @@ export const routes: IRoute[] = [
path: '/projects/:projectId/*',
parent: '/projects',
title: ':projectId',
component: Project,
component: LazyProject,
flag: P,
type: 'protected',
menu: {},
@ -385,187 +365,19 @@ export const routes: IRoute[] = [
},
// Admin
{
path: '/admin/api/create-token',
parent: '/admin',
title: 'API access',
component: CreateApiToken,
type: 'protected',
menu: {},
},
{
path: '/admin/create-project-role',
title: 'Create',
component: CreateProjectRole,
type: 'protected',
menu: {},
flag: RE,
},
{
path: '/admin/roles/:id/edit',
title: 'Edit',
component: EditProjectRole,
type: 'protected',
menu: {},
flag: RE,
},
{
path: '/admin/users/:id/edit',
title: 'Edit',
component: EditUser,
type: 'protected',
menu: {},
hidden: true,
},
{
path: '/admin/api',
parent: '/admin',
title: 'API access',
component: AdminApi,
type: 'protected',
menu: { mobile: true, advanced: true },
},
{
path: '/admin/users',
parent: '/admin',
title: 'Users',
component: AdminUsers,
type: 'protected',
menu: { adminSettings: true },
},
{
path: '/admin/service-accounts',
parent: '/admin',
title: 'Service accounts',
component: ServiceAccounts,
type: 'protected',
menu: { adminSettings: true },
flag: 'serviceAccounts',
},
{
path: '/admin/create-user',
parent: '/admin',
title: 'Users',
component: CreateUser,
type: 'protected',
menu: {},
},
{
path: '/admin/invite-link',
parent: '/admin',
title: 'Invite link',
component: InviteLink,
type: 'protected',
menu: {},
},
{
path: '/admin/groups',
parent: '/admin',
title: 'Groups',
component: GroupsAdmin,
type: 'protected',
menu: { adminSettings: true },
flag: UG,
},
{
path: '/admin/groups/:groupId',
parent: '/admin',
title: ':groupId',
component: Group,
type: 'protected',
menu: {},
flag: UG,
},
{
path: '/admin/groups/create-group',
parent: '/admin/groups',
title: 'Create group',
component: CreateGroup,
type: 'protected',
menu: {},
flag: UG,
},
{
path: '/admin/groups/:groupId/edit',
parent: '/admin/groups',
title: 'Edit group',
component: EditGroup,
type: 'protected',
menu: {},
flag: UG,
},
{
path: '/admin/roles',
parent: '/admin',
title: 'Project roles',
component: ProjectRoles,
type: 'protected',
flag: RE,
menu: { adminSettings: true },
},
{
path: '/admin/auth',
parent: '/admin',
title: 'Single sign-on',
component: AuthSettings,
type: 'protected',
menu: { adminSettings: true },
},
{
path: '/admin/instance',
parent: '/admin',
title: 'Instance stats',
component: InstanceAdmin,
type: 'protected',
menu: { adminSettings: true },
},
{
path: '/admin/network/*',
parent: '/admin',
title: 'Network',
component: Network,
type: 'protected',
menu: { adminSettings: true },
flag: 'networkView',
},
{
path: '/admin/maintenance',
parent: '/admin',
title: 'Maintenance',
component: MaintenanceAdmin,
type: 'protected',
menu: { adminSettings: true },
flag: 'maintenance',
},
{
path: '/admin/cors',
parent: '/admin',
title: 'CORS origins',
component: CorsAdmin,
type: 'protected',
flag: 'embedProxyFrontend',
menu: { adminSettings: true },
},
{
path: '/admin/billing',
parent: '/admin',
title: 'Billing',
component: Billing,
type: 'protected',
menu: {},
},
{
path: '/admin-invoices',
parent: '/admin',
title: 'Billing & invoices',
component: FlaggedBillingRedirect,
type: 'protected',
menu: { adminSettings: true, isEnterprise: true },
},
{
path: '/admin',
title: 'Admin',
component: Admin,
component: AdminRedirect,
hidden: false,
type: 'protected',
menu: {},
},
{
path: '/admin/*',
title: 'Admin',
component: LazyAdmin,
hidden: false,
type: 'protected',
menu: {},
@ -620,6 +432,70 @@ export const routes: IRoute[] = [
},
];
export const adminMenuRoutes: INavigationMenuItem[] = [
{
path: '/history',
title: 'Event log',
menu: { adminSettings: true },
},
{
path: '/admin/users',
title: 'Users',
menu: { adminSettings: true },
},
{
path: '/admin/groups',
title: 'Groups',
menu: { adminSettings: true },
flag: UG,
},
{
path: '/admin/roles',
title: 'Project roles',
flag: RE,
menu: { adminSettings: true },
},
{
path: '/admin/auth',
title: 'Single sign-on',
menu: { adminSettings: true },
},
{
path: '/admin/instance',
title: 'Instance stats',
menu: { adminSettings: true },
},
{
path: '/admin/service-accounts',
title: 'Service accounts',
menu: { adminSettings: true },
flag: 'serviceAccounts',
},
{
path: '/admin/network/*',
title: 'Network',
menu: { adminSettings: true },
flag: 'networkView',
},
{
path: '/admin/maintenance',
title: 'Maintenance',
menu: { adminSettings: true },
flag: 'maintenance',
},
{
path: '/admin/cors',
title: 'CORS origins',
flag: 'embedProxyFrontend',
menu: { adminSettings: true },
},
{
path: '/admin/admin-invoices',
title: 'Billing & invoices',
menu: { adminSettings: true, isEnterprise: true },
},
];
export const getRoute = (path: string) =>
routes.find(route => route.path === path);
@ -640,4 +516,17 @@ const computeRoutes = () => {
};
};
export const getCondensedRoutes = (routes: IRoute[]): INavigationMenuItem[] => {
return routes.map(route => {
const condensedRoute = {
path: route.path,
flag: route.flag,
title: route.title,
menu: route.menu,
configFlag: route.configFlag,
};
return condensedRoute;
});
};
export const getRoutes = computeRoutes();

View File

@ -0,0 +1,3 @@
import { lazy } from 'react';
export const LazyCreateProject = lazy(() => import('./CreateProject'));

View File

@ -0,0 +1,3 @@
import { lazy } from 'react';
export const LazyProject = lazy(() => import('./LazyProjectExport'));

View File

@ -0,0 +1,7 @@
// Lazy loading only supports default export. We have an ADR
// that says we should only use named exports, because
// it makes it harder to diverge from the name of the component when
// importing it in other files.
import { Project } from './Project';
export default Project;

View File

@ -41,7 +41,7 @@ import { ProjectChangeRequests } from '../../changeRequest/ProjectChangeRequests
import { ProjectSettings } from './ProjectSettings/ProjectSettings';
import { useFavoriteProjectsApi } from 'hooks/api/actions/useFavoriteProjectsApi/useFavoriteProjectsApi';
const Project = () => {
export const Project = () => {
const projectId = useRequiredPathParam('projectId');
const params = useQueryParams();
const { project, loading, refetch } = useProject(projectId);
@ -250,5 +250,3 @@ const Project = () => {
</div>
);
};
export default Project;

View File

@ -16,6 +16,14 @@ export interface IRoute {
isStandalone?: boolean;
}
export interface INavigationMenuItem {
path: string;
title: string;
menu: IRouteMenu;
flag?: keyof IFlags;
configFlag?: keyof IUiConfig;
}
interface IRouteMenu {
mobile?: boolean;
advanced?: boolean;