diff --git a/frontend/src/component/admin/Admin.tsx b/frontend/src/component/admin/Admin.tsx
index a0d556c1df..f8dfb87bb5 100644
--- a/frontend/src/component/admin/Admin.tsx
+++ b/frontend/src/component/admin/Admin.tsx
@@ -12,7 +12,7 @@ import { GroupsAdmin } from './groups/GroupsAdmin';
import { InstanceAdmin } from './instance-admin/InstanceAdmin';
import { InstancePrivacy } from './instance-privacy/InstancePrivacy';
import { MaintenanceAdmin } from './maintenance';
-import AdminMenu from './menu/AdminMenu';
+import { AdminTabsMenu } from './menu/AdminTabsMenu';
import { Network } from './network/Network';
import { Roles } from './roles/Roles';
import { ServiceAccounts } from './serviceAccounts/ServiceAccounts';
@@ -20,34 +20,67 @@ import CreateUser from './users/CreateUser/CreateUser';
import EditUser from './users/EditUser/EditUser';
import { InviteLink } from './users/InviteLink/InviteLink';
import UsersAdmin from './users/UsersAdmin';
+import { EnterpriseFeatureUpgradePage } from 'component/common/EnterpriseFeatureUpgradePage/EnterpriseFeatureUpgradePage';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
-export const Admin = () => (
- <>
-
-
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- }
- />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
- } />
-
- >
-);
+export const Admin = () => {
+ const { isEnterprise } = useUiConfig();
+
+ return (
+ <>
+
+
+ } />
+ } />
+ } />
+ } />
+
+ ) : (
+
+ )
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ } />
+
+ ) : (
+
+ )
+ }
+ />
+ } />
+ } />
+ } />
+ } />
+ } />
+ }
+ />
+ } />
+ } />
+
+ >
+ );
+};
diff --git a/frontend/src/component/admin/menu/AdminMenu.tsx b/frontend/src/component/admin/menu/AdminMenu.tsx
deleted file mode 100644
index 7b79869025..0000000000
--- a/frontend/src/component/admin/menu/AdminMenu.tsx
+++ /dev/null
@@ -1,146 +0,0 @@
-import { useLocation } from 'react-router-dom';
-import { Paper, styled, Tab, Tabs } from '@mui/material';
-import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
-import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
-import { CenteredNavLink } from './CenteredNavLink';
-
-const StyledPaper = styled(Paper)(({ theme }) => ({
- marginBottom: '1rem',
- borderRadius: '12.5px',
- boxShadow: 'none',
- padding: '0 2rem',
-}));
-
-function AdminMenu() {
- const { uiConfig, isEnterprise } = useUiConfig();
- const { pathname } = useLocation();
- const { isBilling } = useInstanceStatus();
- const { flags, networkViewEnabled } = uiConfig;
-
- const activeTab = pathname.split('/')[2];
-
- return (
-
-
-
- Users
-
- }
- />
- {isEnterprise() && (
-
- Service accounts
-
- }
- />
- )}
- {flags.UG && (
-
- Groups
-
- }
- />
- )}
- {isEnterprise() && (
-
- Roles
-
- }
- />
- )}
-
- API access
-
- }
- />
- {uiConfig.flags.embedProxyFrontend && (
-
- CORS origins
-
- }
- />
- )}
-
- Single sign-on
-
- }
- />
-
- Instance stats
-
- }
- />
- {networkViewEnabled && (
-
- Network
-
- }
- />
- )}
-
-
- Maintenance
-
- }
- />
-
-
- Instance privacy
-
- }
- />
-
- {isBilling && (
-
- Billing
-
- }
- />
- )}
-
-
- );
-}
-
-export default AdminMenu;
diff --git a/frontend/src/component/admin/menu/AdminTabsMenu.tsx b/frontend/src/component/admin/menu/AdminTabsMenu.tsx
new file mode 100644
index 0000000000..47663bf3f9
--- /dev/null
+++ b/frontend/src/component/admin/menu/AdminTabsMenu.tsx
@@ -0,0 +1,141 @@
+import { useLocation } from 'react-router-dom';
+import { Box, Paper, styled, Tab, Tabs } from '@mui/material';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
+import { CenteredNavLink } from './CenteredNavLink';
+import { VFC } from 'react';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
+
+const StyledPaper = styled(Paper)(({ theme }) => ({
+ marginBottom: '1rem',
+ borderRadius: `${theme.shape.borderRadiusLarge}px`,
+ boxShadow: 'none',
+ padding: theme.spacing(0, 2),
+}));
+
+const StyledBadgeContainer = styled('div')(({ theme }) => ({
+ marginLeft: theme.spacing(1),
+ display: 'flex',
+ alignItems: 'center',
+}));
+
+export const AdminTabsMenu: VFC = () => {
+ const { uiConfig, isEnterprise, isPro } = useUiConfig();
+ const { pathname } = useLocation();
+ const { isBilling } = useInstanceStatus();
+ const { flags, networkViewEnabled } = uiConfig;
+
+ const activeTab = pathname.split('/')[2];
+
+ const showEnterpriseFeaturesInPro =
+ uiConfig?.flags?.frontendNavigationUpdate;
+
+ const tabs = [
+ {
+ value: 'users',
+ label: 'Users',
+ link: '/admin/users',
+ },
+ {
+ value: 'service-accounts',
+ label: 'Service accounts',
+ link: '/admin/service-accounts',
+ condition:
+ isEnterprise() || (isPro() && showEnterpriseFeaturesInPro),
+ showEnterpriseBadge: true,
+ },
+ {
+ value: 'groups',
+ label: 'Groups',
+ link: '/admin/groups',
+ condition: flags.UG,
+ },
+ {
+ value: 'roles',
+ label: 'Roles',
+ link: '/admin/roles',
+ condition:
+ isEnterprise() || (isPro() && showEnterpriseFeaturesInPro),
+ showEnterpriseBadge: true,
+ },
+ {
+ value: 'api',
+ label: 'API access',
+ link: '/admin/api',
+ },
+ {
+ value: 'cors',
+ label: 'CORS origins',
+ link: '/admin/cors',
+ condition: uiConfig.flags.embedProxyFrontend,
+ },
+ {
+ value: 'auth',
+ label: 'Single sign-on',
+ link: '/admin/auth',
+ },
+ {
+ value: 'instance',
+ label: 'Instance stats',
+ link: '/admin/instance',
+ },
+ {
+ value: 'network',
+ label: 'Network',
+ link: '/admin/network',
+ condition: networkViewEnabled,
+ },
+ {
+ value: 'maintenance',
+ label: 'Maintenance',
+ link: '/admin/maintenance',
+ },
+ {
+ value: 'instance-privacy',
+ label: 'Instance privacy',
+ link: '/admin/instance-privacy',
+ },
+ {
+ value: 'billing',
+ label: 'Billing',
+ link: '/admin/billing',
+ condition: isBilling,
+ },
+ ];
+
+ return (
+
+
+ {tabs
+ .filter(tab => tab.condition || tab.condition === undefined)
+ .map(tab => (
+
+ {tab.label}
+
+
+
+ }
+ />
+
+ }
+ />
+ ))}
+
+
+ );
+};
diff --git a/frontend/src/component/common/EnterpriseBadge/EnterpriseBadge.tsx b/frontend/src/component/common/EnterpriseBadge/EnterpriseBadge.tsx
new file mode 100644
index 0000000000..13dd5d0285
--- /dev/null
+++ b/frontend/src/component/common/EnterpriseBadge/EnterpriseBadge.tsx
@@ -0,0 +1,15 @@
+import { VFC } from 'react';
+import { ReactComponent as ProPlanIcon } from 'assets/icons/pro-enterprise-feature-badge.svg';
+import { ReactComponent as ProPlanIconLight } from 'assets/icons/pro-enterprise-feature-badge-light.svg';
+import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
+
+type EnterpriseBadgeProps = {
+ size?: number;
+};
+
+export const EnterpriseBadge: VFC = ({ size = 16 }) => (
+ }
+ lightmode={}
+ />
+);
diff --git a/frontend/src/component/common/EnterpriseFeatureUpgradePage/EnterpriseFeatureUpgradePage.tsx b/frontend/src/component/common/EnterpriseFeatureUpgradePage/EnterpriseFeatureUpgradePage.tsx
new file mode 100644
index 0000000000..75aa26b91d
--- /dev/null
+++ b/frontend/src/component/common/EnterpriseFeatureUpgradePage/EnterpriseFeatureUpgradePage.tsx
@@ -0,0 +1,64 @@
+import { VFC } from 'react';
+import { Box, Button, Typography, styled } from '@mui/material';
+import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
+import { PageContent } from '../PageContent/PageContent';
+import { PageHeader } from '../PageHeader/PageHeader';
+
+type EnterpriseFeatureUpgradePageProps = {
+ title: string;
+ link: string;
+};
+
+const StyledContainer = styled(Box)(({ theme }) => ({
+ background: theme.palette.background.elevation2,
+ padding: theme.spacing(8, 2),
+ borderRadius: `${theme.shape.borderRadiusMedium}px`,
+ display: 'flex',
+ flexDirection: 'column',
+ alignItems: 'center',
+ textAlign: 'center',
+}));
+
+const StyledHeader = styled(Typography)(({ theme }) => ({
+ marginBottom: theme.spacing(3),
+ display: 'flex',
+ alignItems: 'center',
+}));
+
+const StyledBadgeContainer = styled('div')(({ theme }) => ({
+ paddingRight: theme.spacing(1),
+ display: 'flex',
+ alignItems: 'center',
+}));
+
+export const EnterpriseFeatureUpgradePage: VFC<
+ EnterpriseFeatureUpgradePageProps
+> = ({ title, link }) => (
+ }>
+
+
+
+
+
+ Enterprise feature
+
+
+ {title} is a feature available for the
+ Enterprise plan.
+
+
+ You need to upgrade your plan if you want to use it.
+
+
+
+
+);
diff --git a/frontend/src/component/loginHistory/LoginHistory.tsx b/frontend/src/component/loginHistory/LoginHistory.tsx
index 439f575f8a..f1ce9f1a78 100644
--- a/frontend/src/component/loginHistory/LoginHistory.tsx
+++ b/frontend/src/component/loginHistory/LoginHistory.tsx
@@ -1,11 +1,26 @@
import { ADMIN } from 'component/providers/AccessProvider/permissions';
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { LoginHistoryTable } from './LoginHistoryTable/LoginHistoryTable';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { EnterpriseFeatureUpgradePage } from 'component/common/EnterpriseFeatureUpgradePage/EnterpriseFeatureUpgradePage';
-export const LoginHistory = () => (
-
-);
+export const LoginHistory = () => {
+ const { isEnterprise } = useUiConfig();
+
+ if (!isEnterprise()) {
+ return (
+
+ );
+ }
+
+ return (
+
+ );
+};
diff --git a/frontend/src/component/menu/Header/Header.tsx b/frontend/src/component/menu/Header/Header.tsx
index 967edd1569..a8f1f338c6 100644
--- a/frontend/src/component/menu/Header/Header.tsx
+++ b/frontend/src/component/menu/Header/Header.tsx
@@ -132,12 +132,23 @@ const Header: VFC = () => {
const routes = getRoutes();
const filterByMode = (route: INavigationMenuItem): boolean => {
- const { mode } = route.menu;
- return (
- !mode ||
- (mode.includes('pro') && isPro()) ||
- (mode.includes('enterprise') && isEnterprise())
- );
+ const { mode, showEnterpriseBadge } = route.menu;
+
+ if (!mode) return true;
+
+ if (isPro()) {
+ return (
+ mode.includes('pro') ||
+ (mode.includes('enterprise') && showEnterpriseBadge) ||
+ false
+ );
+ }
+
+ if (isEnterprise()) {
+ return mode.includes('enterprise');
+ }
+
+ return false;
};
const filteredMainRoutes = {
diff --git a/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx b/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx
index d2dc3cee48..8ad66515b3 100644
--- a/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx
+++ b/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx
@@ -2,11 +2,12 @@ import { Divider } from '@mui/material';
import { Menu, MenuItem, styled } from '@mui/material';
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
-import { Fragment } from 'react';
+import { INavigationMenuItem } from 'interfaces/route';
import { Link } from 'react-router-dom';
+import { EnterpriseBadge } from '../../../common/EnterpriseBadge/EnterpriseBadge';
interface INavigationMenuProps {
- options: any[];
+ options: INavigationMenuItem[];
id: string;
anchorEl: any;
handleClose: () => void;
@@ -35,6 +36,12 @@ const StyledSpan = styled('span')(({ theme }) => ({
borderRadius: '2px',
}));
+const StyledBadgeContainer = styled('div')(({ theme }) => ({
+ marginLeft: 'auto',
+ paddingLeft: theme.spacing(2),
+ display: 'flex',
+}));
+
export const NavigationMenu = ({
options,
id,
@@ -43,7 +50,7 @@ export const NavigationMenu = ({
style,
}: INavigationMenuProps) => {
const { uiConfig } = useUiConfig();
- const showDividers = uiConfig?.flags?.frontendNavigationUpdate;
+ const showUpdatedMenu = uiConfig?.flags?.frontendNavigationUpdate;
return (
);
};
diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts
index 1ca59d362b..96095573f0 100644
--- a/frontend/src/component/menu/routes.ts
+++ b/frontend/src/component/menu/routes.ts
@@ -462,20 +462,32 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
{
path: '/admin/service-accounts',
title: 'Service accounts',
- menu: { adminSettings: true, mode: ['enterprise'] },
+ menu: {
+ adminSettings: true,
+ mode: ['enterprise'],
+ showEnterpriseBadge: true,
+ },
group: 'users',
},
{
path: '/admin/groups',
title: 'Groups',
- menu: { adminSettings: true, mode: ['enterprise'] },
+ menu: {
+ adminSettings: true,
+ mode: ['enterprise'],
+ showEnterpriseBadge: true,
+ },
flag: UG,
group: 'users',
},
{
path: '/admin/roles/*',
title: 'Roles',
- menu: { adminSettings: true, mode: ['enterprise'] },
+ menu: {
+ adminSettings: true,
+ mode: ['enterprise'],
+ showEnterpriseBadge: true,
+ },
group: 'users',
},
{
@@ -532,7 +544,11 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
{
path: '/admin/logins',
title: 'Login history',
- menu: { adminSettings: true, mode: ['enterprise'] },
+ menu: {
+ adminSettings: true,
+ mode: ['enterprise'],
+ showEnterpriseBadge: true,
+ },
group: 'log',
},
{
diff --git a/frontend/src/interfaces/route.ts b/frontend/src/interfaces/route.ts
index a5dde225b5..772a2096b5 100644
--- a/frontend/src/interfaces/route.ts
+++ b/frontend/src/interfaces/route.ts
@@ -30,4 +30,5 @@ interface IRouteMenu {
advanced?: boolean;
adminSettings?: boolean;
mode?: Array<'pro' | 'enterprise'>;
+ showEnterpriseBadge?: boolean;
}