(null);
const { uiConfig, isOss, isPro, isEnterprise } = useUiConfig();
+ const { isBilling } = useInstanceStatus();
const smallScreen = useMediaQuery(theme.breakpoints.down('md'));
const [openDrawer, setOpenDrawer] = useState(false);
const showApiAccessInConfigure = !uiConfig?.flags?.frontendNavigationUpdate;
+ const showEnterpriseOptionsInPro = Boolean(
+ uiConfig?.flags?.frontendNavigationUpdate
+ );
const toggleDrawer = () => setOpenDrawer(prev => !prev);
const onAdminClose = () => setAdminRef(null);
@@ -131,26 +137,6 @@ const Header: VFC = () => {
const routes = getRoutes();
- const filterByMode = (route: INavigationMenuItem): boolean => {
- 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 = {
mainNavRoutes: getCondensedRoutes(routes.mainNavRoutes)
.concat(
@@ -182,7 +168,17 @@ const Header: VFC = () => {
.map(mapRouteLink),
adminRoutes: adminMenuRoutes
.filter(filterByConfig(uiConfig))
- .filter(filterByMode)
+ .filter(route =>
+ filterAdminRoutes(
+ route?.menu,
+ {
+ enterprise: isEnterprise(),
+ pro: isPro(),
+ billing: isBilling,
+ },
+ showEnterpriseOptionsInPro
+ )
+ )
.map(mapRouteLink),
};
diff --git a/frontend/src/component/menu/Header/NavigationLink/NavigationLink.tsx b/frontend/src/component/menu/Header/NavigationLink/NavigationLink.tsx
index eaef608871..349275f928 100644
--- a/frontend/src/component/menu/Header/NavigationLink/NavigationLink.tsx
+++ b/frontend/src/component/menu/Header/NavigationLink/NavigationLink.tsx
@@ -1,9 +1,14 @@
import { ListItem, Link, styled } from '@mui/material';
+import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
+import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
+import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { INavigationMenuItem } from 'interfaces/route';
import { Link as RouterLink } from 'react-router-dom';
interface INavigationLinkProps {
path: string;
text: string;
handleClose: () => void;
+ mode?: INavigationMenuItem['menu']['mode'];
}
const StyledListItem = styled(ListItem)({
@@ -37,7 +42,25 @@ const StyledSpan = styled('span')(({ theme }) => ({
borderRadius: '2px',
}));
-const NavigationLink = ({ path, text, handleClose }: INavigationLinkProps) => {
+const StyledBadgeContainer = styled('div')(({ theme }) => ({
+ marginLeft: 'auto',
+ paddingLeft: theme.spacing(2),
+ display: 'flex',
+}));
+
+const NavigationLink = ({
+ path,
+ text,
+ handleClose,
+ ...props
+}: INavigationLinkProps) => {
+ const { uiConfig, isPro } = useUiConfig();
+ const showEnterpriseBadgeToPro = Boolean(
+ uiConfig?.flags?.frontendNavigationUpdate &&
+ isPro() &&
+ props.mode?.includes('enterprise')
+ );
+
return (
{
@@ -52,6 +75,15 @@ const NavigationLink = ({ path, text, handleClose }: INavigationLinkProps) => {
>
{text}
+
+
+
+
+ }
+ />
);
diff --git a/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx b/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx
index 8ad66515b3..0fe73ba8cc 100644
--- a/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx
+++ b/frontend/src/component/menu/Header/NavigationMenu/NavigationMenu.tsx
@@ -5,6 +5,7 @@ import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { INavigationMenuItem } from 'interfaces/route';
import { Link } from 'react-router-dom';
import { EnterpriseBadge } from '../../../common/EnterpriseBadge/EnterpriseBadge';
+import { useCallback } from 'react';
interface INavigationMenuProps {
options: INavigationMenuItem[];
@@ -49,9 +50,25 @@ export const NavigationMenu = ({
anchorEl,
style,
}: INavigationMenuProps) => {
- const { uiConfig } = useUiConfig();
+ const { uiConfig, isPro } = useUiConfig();
const showUpdatedMenu = uiConfig?.flags?.frontendNavigationUpdate;
+ const showBadge = useCallback(
+ (mode?: INavigationMenuItem['menu']['mode']) => {
+ if (
+ isPro() &&
+ !mode?.includes('pro') &&
+ mode?.includes('enterprise') &&
+ showUpdatedMenu
+ ) {
+ return true;
+ }
+
+ return false;
+ },
+ [isPro, showUpdatedMenu]
+ );
+
return (
diff --git a/frontend/src/component/menu/Header/filterAdminRoutes.test.ts b/frontend/src/component/menu/Header/filterAdminRoutes.test.ts
new file mode 100644
index 0000000000..d74e5df1bd
--- /dev/null
+++ b/frontend/src/component/menu/Header/filterAdminRoutes.test.ts
@@ -0,0 +1,103 @@
+import { filterAdminRoutes } from './filterAdminRoutes';
+
+describe('filterAdminRoutes - open souce routes', () => {
+ test('open source - should show menu item if mode paid plan mode is not defined', () => {
+ expect(
+ filterAdminRoutes(
+ {},
+ {
+ pro: false,
+ enterprise: false,
+ billing: false,
+ }
+ )
+ ).toBe(true);
+ });
+
+ test('open source - should not show menu item from paid plans', () => {
+ const state = {
+ pro: false,
+ enterprise: false,
+ billing: false,
+ };
+
+ expect(filterAdminRoutes({ mode: ['pro'] }, state)).toBe(false);
+ expect(filterAdminRoutes({ mode: ['enterprise'] }, state)).toBe(false);
+ expect(filterAdminRoutes({ mode: ['pro', 'enterprise'] }, state)).toBe(
+ false
+ );
+ expect(filterAdminRoutes({ billing: true }, state)).toBe(false);
+ });
+
+ test('pro - should show menu item for pro customers', () => {
+ const state = {
+ pro: true,
+ enterprise: false,
+ billing: false,
+ };
+
+ expect(filterAdminRoutes({ mode: ['pro'] }, state)).toBe(true);
+ expect(filterAdminRoutes({ mode: ['pro', 'enterprise'] }, state)).toBe(
+ true
+ );
+ // This is to show enterprise badge in pro mode
+ expect(filterAdminRoutes({ mode: ['enterprise'] }, state)).toBe(true);
+ });
+
+ test('enterprise - should show menu item if mode enterprise is defined or mode is undefined', () => {
+ const state = {
+ pro: false,
+ enterprise: true,
+ billing: false,
+ };
+
+ expect(filterAdminRoutes({ mode: ['enterprise'] }, state)).toBe(true);
+ expect(filterAdminRoutes({ mode: ['pro', 'enterprise'] }, state)).toBe(
+ true
+ );
+ expect(filterAdminRoutes({ mode: ['pro'] }, state)).toBe(false);
+ });
+
+ test('billing - should show menu item if billing is defined', () => {
+ expect(
+ filterAdminRoutes(
+ { mode: ['pro'], billing: true },
+ {
+ pro: true,
+ enterprise: false,
+ billing: true,
+ }
+ )
+ ).toBe(true);
+ expect(
+ filterAdminRoutes(
+ { mode: ['enterprise'], billing: true },
+ {
+ pro: false,
+ enterprise: true,
+ billing: true,
+ }
+ )
+ ).toBe(true);
+ expect(
+ filterAdminRoutes(
+ { mode: ['pro', 'enterprise'], billing: true },
+ {
+ pro: true,
+ enterprise: false,
+ billing: true,
+ }
+ )
+ ).toBe(true);
+ expect(
+ filterAdminRoutes(
+ { mode: ['pro'], billing: true },
+ {
+ pro: false,
+ enterprise: false,
+ billing: true,
+ }
+ )
+ ).toBe(false);
+ });
+});
diff --git a/frontend/src/component/menu/Header/filterAdminRoutes.ts b/frontend/src/component/menu/Header/filterAdminRoutes.ts
new file mode 100644
index 0000000000..0caaf29150
--- /dev/null
+++ b/frontend/src/component/menu/Header/filterAdminRoutes.ts
@@ -0,0 +1,34 @@
+import { INavigationMenuItem } from 'interfaces/route';
+
+export const filterAdminRoutes = (
+ menu: INavigationMenuItem['menu'],
+ {
+ pro,
+ enterprise,
+ billing,
+ }: { pro?: boolean; enterprise?: boolean; billing?: boolean },
+ showEnterpriseOptionsInPro = true
+): boolean => {
+ const mode = menu?.mode;
+ if (menu?.billing && !billing) return false;
+
+ if (!mode || mode.length === 0) {
+ return true;
+ }
+
+ if (pro) {
+ if (mode.includes('pro')) {
+ return true;
+ }
+
+ if (showEnterpriseOptionsInPro && mode.includes('enterprise')) {
+ return true;
+ }
+ }
+
+ if (enterprise && mode.includes('enterprise')) {
+ return true;
+ }
+
+ return false;
+};
diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts
index 96095573f0..7fc5c341a3 100644
--- a/frontend/src/component/menu/routes.ts
+++ b/frontend/src/component/menu/routes.ts
@@ -465,7 +465,6 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
menu: {
adminSettings: true,
mode: ['enterprise'],
- showEnterpriseBadge: true,
},
group: 'users',
},
@@ -475,7 +474,6 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
menu: {
adminSettings: true,
mode: ['enterprise'],
- showEnterpriseBadge: true,
},
flag: UG,
group: 'users',
@@ -486,7 +484,6 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
menu: {
adminSettings: true,
mode: ['enterprise'],
- showEnterpriseBadge: true,
},
group: 'users',
},
@@ -538,7 +535,7 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
{
path: '/admin/admin-invoices',
title: 'Billing & invoices',
- menu: { adminSettings: true, mode: ['pro'] },
+ menu: { adminSettings: true, mode: ['pro'], billing: true },
group: 'instance',
},
{
@@ -547,7 +544,6 @@ export const adminMenuRoutes: INavigationMenuItem[] = [
menu: {
adminSettings: true,
mode: ['enterprise'],
- showEnterpriseBadge: true,
},
group: 'log',
},
diff --git a/frontend/src/component/project/Project/Project.tsx b/frontend/src/component/project/Project/Project.tsx
index f2b3704992..5b5e050696 100644
--- a/frontend/src/component/project/Project/Project.tsx
+++ b/frontend/src/component/project/Project/Project.tsx
@@ -14,7 +14,7 @@ import {
StyledTabContainer,
StyledTopRow,
} from './Project.styles';
-import { Paper, Tabs, Typography } from '@mui/material';
+import { Box, Paper, Tabs, Typography } from '@mui/material';
import { Delete, Edit, FileUpload } from '@mui/icons-material';
import useToast from 'hooks/useToast';
import useQueryParams from 'hooks/useQueryParams';
@@ -40,6 +40,7 @@ import { ProjectSettings } from './ProjectSettings/ProjectSettings';
import { useFavoriteProjectsApi } from 'hooks/api/actions/useFavoriteProjectsApi/useFavoriteProjectsApi';
import { ImportModal } from './Import/ImportModal';
import { IMPORT_BUTTON } from 'utils/testIds';
+import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
const NAVIGATE_TO_EDIT_PROJECT = 'NAVIGATE_TO_EDIT_PROJECT';
@@ -52,13 +53,15 @@ export const Project = () => {
const [modalOpen, setModalOpen] = useState(false);
const navigate = useNavigate();
const { pathname } = useLocation();
- const { isOss, uiConfig } = useUiConfig();
+ const { isOss, uiConfig, isPro } = useUiConfig();
const basePath = `/projects/${projectId}`;
const projectName = project?.name || projectId;
const { favorite, unfavorite } = useFavoriteProjectsApi();
const [showDelDialog, setShowDelDialog] = useState(false);
+ const updatedNavigation = uiConfig?.flags?.frontendNavigationUpdate;
+
const tabs = [
{
title: 'Overview',
@@ -79,18 +82,34 @@ export const Project = () => {
title: 'Change requests',
path: `${basePath}/change-requests`,
name: 'change-request',
+ isEnterprise: true,
},
- {
- title: 'Project settings',
- path: `${basePath}/settings`,
- name: 'settings',
- },
- {
- title: 'Event log',
- path: `${basePath}/logs`,
- name: 'logs',
- },
- ];
+ ...(updatedNavigation
+ ? [
+ {
+ title: 'Event log',
+ path: `${basePath}/logs`,
+ name: 'logs',
+ },
+ {
+ title: 'Project settings',
+ path: `${basePath}/settings`,
+ name: 'settings',
+ },
+ ]
+ : [
+ {
+ title: 'Project settings',
+ path: `${basePath}/settings`,
+ name: 'settings',
+ },
+ {
+ title: 'Event log',
+ path: `${basePath}/logs`,
+ name: 'logs',
+ },
+ ]),
+ ].filter(tab => !updatedNavigation || !(isOss() && tab.isEnterprise));
const activeTab = [...tabs]
.reverse()
@@ -130,6 +149,17 @@ export const Project = () => {
refetch();
};
+ const enterpriseIcon = (
+ ({
+ marginLeft: theme.spacing(1),
+ display: 'flex',
+ })}
+ >
+
+
+ );
+
return (
@@ -227,6 +257,16 @@ export const Project = () => {
value={tab.path}
onClick={() => navigate(tab.path)}
data-testid={`TAB_${tab.title}`}
+ iconPosition={
+ tab.isEnterprise ? 'end' : undefined
+ }
+ icon={
+ tab.isEnterprise &&
+ isPro() &&
+ updatedNavigation
+ ? enterpriseIcon
+ : undefined
+ }
/>
))}
diff --git a/frontend/src/component/project/Project/ProjectSettings/ProjectSettings.tsx b/frontend/src/component/project/Project/ProjectSettings/ProjectSettings.tsx
index a484abb09a..f30ba76097 100644
--- a/frontend/src/component/project/Project/ProjectSettings/ProjectSettings.tsx
+++ b/frontend/src/component/project/Project/ProjectSettings/ProjectSettings.tsx
@@ -14,12 +14,16 @@ import { ProjectSegments } from './ProjectSegments/ProjectSegments';
import { ProjectDefaultStrategySettings } from './ProjectDefaultStrategySettings/ProjectDefaultStrategySettings';
import { Settings } from './Settings/Settings';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
+import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
+import { Box } from '@mui/material';
export const ProjectSettings = () => {
const location = useLocation();
- const { uiConfig } = useUiConfig();
+ const { uiConfig, isPro, isEnterprise } = useUiConfig();
const navigate = useNavigate();
+ const updatedNavigation = uiConfig.flags?.frontendNavigationUpdate;
+
const tabs: ITab[] = [
...(uiConfig.flags.newProjectLayout
? [
@@ -33,18 +37,28 @@ export const ProjectSettings = () => {
id: 'environments',
label: 'Environments',
},
- {
- id: 'access',
- label: 'Access',
- },
- {
- id: 'segments',
- label: 'Segments',
- },
- {
- id: 'change-requests',
- label: 'Change request configuration',
- },
+ ...(!updatedNavigation || isPro() || isEnterprise()
+ ? [
+ {
+ id: 'access',
+ label: 'Access',
+ },
+ {
+ id: 'segments',
+ label: 'Segments',
+ },
+ {
+ id: 'change-requests',
+ label: 'Change request configuration',
+ icon:
+ isPro() && updatedNavigation ? (
+
+
+
+ ) : undefined,
+ },
+ ]
+ : []),
{
id: 'api-access',
label: 'API access',
diff --git a/frontend/src/interfaces/route.ts b/frontend/src/interfaces/route.ts
index 772a2096b5..8755f81553 100644
--- a/frontend/src/interfaces/route.ts
+++ b/frontend/src/interfaces/route.ts
@@ -30,5 +30,5 @@ interface IRouteMenu {
advanced?: boolean;
adminSettings?: boolean;
mode?: Array<'pro' | 'enterprise'>;
- showEnterpriseBadge?: boolean;
+ billing?: boolean;
}