1
0
mirror of https://github.com/Unleash/unleash.git synced 2025-07-17 13:46:47 +02:00

chore: clean up adminNavUI flag (#9907)

This commit is contained in:
David Leek 2025-05-06 14:59:07 +02:00 committed by GitHub
parent b9f1d8414c
commit 681079bd08
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 27 additions and 512 deletions

View File

@ -2,7 +2,6 @@ import { Routes, Route } from 'react-router-dom';
import { ApiTokenPage } from './apiToken/ApiTokenPage/ApiTokenPage';
import { CreateApiToken } from './apiToken/CreateApiToken/CreateApiToken';
import { AuthSettings } from './auth/AuthSettings';
import { OldAuthSettings } from './auth/OldAuthSettings';
import { Billing } from './billing/Billing';
import FlaggedBillingRedirect from './billing/FlaggedBillingRedirect/FlaggedBillingRedirect';
import { CorsAdmin } from './cors';
@ -17,23 +16,15 @@ import CreateUser from './users/CreateUser/CreateUser';
import { InviteLink } from './users/InviteLink/InviteLink';
import UsersAdmin from './users/UsersAdmin';
import NotFound from 'component/common/NotFound/NotFound';
import { AdminIndex } from './AdminIndex';
import { Banners } from './banners/Banners';
import { License } from './license/License';
import { useUiFlag } from 'hooks/useUiFlag';
import { AdminHome } from './AdminHome';
export const Admin = () => {
const newAdminUIEnabled = useUiFlag('adminNavUI');
return (
<>
<Routes>
{newAdminUIEnabled ? (
<Route index element={<AdminHome />} />
) : (
<Route index element={<AdminIndex />} />
)}
<Route index element={<AdminHome />} />
<Route path='users/*' element={<UsersAdmin />} />
<Route path='api' element={<ApiTokenPage />} />
<Route path='api/create-token' element={<CreateApiToken />} />
@ -48,11 +39,7 @@ export const Admin = () => {
<Route path='banners' element={<Banners />} />
<Route path='license' element={<License />} />
<Route path='cors' element={<CorsAdmin />} />
{newAdminUIEnabled ? (
<Route path='auth/*' element={<AuthSettings />} />
) : (
<Route path='auth' element={<OldAuthSettings />} />
)}
<Route path='auth/*' element={<AuthSettings />} />
<Route
path='admin-invoices'
element={<FlaggedBillingRedirect />}

View File

@ -1,60 +0,0 @@
import { PageContent } from 'component/common/PageContent/PageContent';
import { PageHeader } from 'component/common/PageHeader/PageHeader';
import type { VFC } from 'react';
import { adminGroups } from './oldAdminRoutes';
import type { INavigationMenuItem } from 'interfaces/route';
import { Box, Link, Typography } from '@mui/material';
import { Link as RouterLink } from 'react-router-dom';
import { useAdminRoutes } from './useAdminRoutes';
export const AdminIndex: VFC = () => {
const adminRoutes = useAdminRoutes();
const routeGroups = adminRoutes.reduce(
(acc, route) => {
const group = route.group || 'other';
const index = acc.findIndex((item) => item.name === group);
if (index === -1) {
acc.push({
name: group,
description: adminGroups[group] || 'Other',
items: [route],
});
return acc;
}
acc[index].items.push(route);
return acc;
},
[] as Array<{
name: string;
description: string;
items: INavigationMenuItem[];
}>,
);
return (
<PageContent header={<PageHeader title='Manage Unleash' />}>
{routeGroups.map((group) => (
<Box
key={group.name}
sx={(theme) => ({ marginBottom: theme.spacing(2) })}
>
<Typography variant='h2'>{group.description}</Typography>
<ul>
{group.items.map((route) => (
<li key={route.path}>
<Link component={RouterLink} to={route.path}>
{route.title}
</Link>
</li>
))}
</ul>
</Box>
))}
</PageContent>
);
};

View File

@ -1,96 +0,0 @@
import { Tab, Tabs } from '@mui/material';
import { PageContent } from 'component/common/PageContent/PageContent';
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { OidcAuth } from './OidcAuth/OidcAuth';
import { SamlAuth } from './SamlAuth/SamlAuth';
import { ScimSettings } from './ScimSettings/ScimSettings';
import { PasswordAuth } from './PasswordAuth/PasswordAuth';
import { GoogleAuth } from './GoogleAuth/GoogleAuth';
import { PermissionGuard } from 'component/common/PermissionGuard/PermissionGuard';
import { ADMIN, UPDATE_AUTH_CONFIGURATION } from '@server/types/permissions';
import { PremiumFeature } from 'component/common/PremiumFeature/PremiumFeature';
import { useState } from 'react';
import { TabPanel } from 'component/common/TabNav/TabPanel/TabPanel';
import { usePageTitle } from 'hooks/usePageTitle';
export const OldAuthSettings = () => {
const { uiConfig, isEnterprise } = useUiConfig();
const tabs = [
{
label: 'OpenID Connect',
component: <OidcAuth />,
},
{
label: 'SAML 2.0',
component: <SamlAuth />,
},
{
label: 'Password',
component: <PasswordAuth />,
},
{
label: 'Google',
component: <GoogleAuth />,
},
{
label: 'SCIM',
component: <ScimSettings />,
},
].filter(
(item) => uiConfig.flags?.googleAuthEnabled || item.label !== 'Google',
);
const [activeTab, setActiveTab] = useState(0);
usePageTitle(`Single sign-on: ${tabs[activeTab].label}`);
if (!isEnterprise()) {
return <PremiumFeature feature='sso' page />;
}
return (
<div>
<PermissionGuard permissions={[ADMIN, UPDATE_AUTH_CONFIGURATION]}>
<PageContent
withTabs
header={
<Tabs
value={activeTab}
onChange={(_, tabId) => {
setActiveTab(tabId);
}}
indicatorColor='primary'
textColor='primary'
>
{tabs.map((tab, index) => (
<Tab
key={`${tab.label}_${index}`}
label={tab.label}
id={`tab-${index}`}
aria-controls={`tabpanel-${index}`}
sx={{
minWidth: {
lg: 160,
},
}}
/>
))}
</Tabs>
}
>
<div>
{tabs.map((tab, index) => (
<TabPanel
key={index}
value={activeTab}
index={index}
>
{tab.component}
</TabPanel>
))}
</div>
</PageContent>
</PermissionGuard>
</div>
);
};

View File

@ -1,122 +0,0 @@
import type { INavigationMenuItem } from 'interfaces/route';
export const adminGroups: Record<string, string> = {
users: 'User administration',
access: 'Access control',
instance: 'Instance configuration',
log: 'Logs',
other: 'Other',
};
export const adminRoutes: INavigationMenuItem[] = [
{
path: '/admin/users',
title: 'Users',
menu: { adminSettings: true },
group: 'users',
},
{
path: '/admin/service-accounts',
title: 'Service accounts',
menu: {
adminSettings: true,
mode: ['enterprise'],
},
group: 'users',
},
{
path: '/admin/groups',
title: 'Groups',
menu: {
adminSettings: true,
mode: ['enterprise'],
},
group: 'users',
},
{
path: '/admin/roles/*',
title: 'Roles',
menu: {
adminSettings: true,
mode: ['enterprise'],
},
group: 'users',
},
{
path: '/admin/api',
title: 'API access',
menu: { adminSettings: true },
group: 'access',
},
{
path: '/admin/cors',
title: 'CORS origins',
flag: 'embedProxyFrontend',
menu: { adminSettings: true },
group: 'access',
},
{
path: '/admin/auth',
title: 'Single sign-on',
menu: { adminSettings: true, mode: ['enterprise'] },
group: 'access',
},
{
path: '/admin/network/*',
title: 'Network',
menu: { adminSettings: true, mode: ['pro', 'enterprise'] },
group: 'instance',
},
{
path: '/admin/maintenance',
title: 'Maintenance',
menu: { adminSettings: true },
group: 'instance',
},
{
path: '/admin/banners',
title: 'Banners',
menu: { adminSettings: true, mode: ['enterprise'] },
group: 'instance',
},
{
path: '/admin/instance',
title: 'Instance stats',
menu: { adminSettings: true },
group: 'instance',
},
{
path: '/admin/license',
title: 'License',
menu: { adminSettings: true, mode: ['enterprise'] },
flag: 'enableLicense',
group: 'instance',
},
{
path: '/admin/instance-privacy',
title: 'Instance privacy',
menu: { adminSettings: true },
group: 'instance',
},
{
path: '/admin/admin-invoices',
title: 'Billing & invoices',
menu: { adminSettings: true, billing: true },
group: 'instance',
},
{
path: '/admin/logins',
title: 'Login history',
menu: {
adminSettings: true,
mode: ['enterprise'],
},
group: 'log',
},
{
path: '/history',
title: 'Event log',
menu: { adminSettings: true },
group: 'log',
},
];

View File

@ -1,16 +1,13 @@
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
import { adminRoutes as oldAdminRoutes } from './oldAdminRoutes';
import { adminRoutes } from './adminRoutes';
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
import { filterRoutesByPlanData } from './filterRoutesByPlanData';
import { filterByConfig, mapRouteLink } from 'component/common/util';
import { useUiFlag } from 'hooks/useUiFlag';
export const useAdminRoutes = () => {
const newAdminUIEnabled = useUiFlag('adminNavUI');
const { uiConfig, isPro, isEnterprise } = useUiConfig();
const { isBilling } = useInstanceStatus();
const routes = newAdminUIEnabled ? [...adminRoutes] : [...oldAdminRoutes];
const routes = [...adminRoutes];
if (uiConfig.flags.UNLEASH_CLOUD) {
const adminBillingMenuItem = routes.findIndex(

View File

@ -1,6 +1,5 @@
import { adminGroups } from 'component/admin/adminRoutes';
import { useRoutes } from 'component/layout/MainLayout/NavigationSidebar/useRoutes';
import { useUiFlag } from 'hooks/useUiFlag';
import type { INavigationMenuItem } from 'interfaces/route';
import { useMemo } from 'react';
@ -12,7 +11,6 @@ interface IPageRouteInfo {
}
export const useCommandBarRoutes = () => {
const newAdminUIEnabled = useUiFlag('adminNavUI');
const { routes } = useRoutes();
const getSearchText = (route: INavigationMenuItem, title: string) => {
if (route.group && adminGroups[route.group]) {
@ -41,15 +39,12 @@ export const useCommandBarRoutes = () => {
path: route.path,
route: route.route,
title: title,
searchText: newAdminUIEnabled
? getSearchText(route, title)
: title,
searchText: getSearchText(route, title),
};
}
return {
allRoutes,
newAdminUIEnabled,
};
}, [routes]);
};

View File

@ -2,7 +2,6 @@ import type React from 'react';
import type { FC } from 'react';
import type { INavigationMenuItem } from 'interfaces/route';
import type { NavigationMode } from './NavigationMode';
import { ShowAdmin } from './ShowHide';
import {
ExternalFullListItem,
FullListItem,
@ -237,7 +236,7 @@ export const AdminSettingsNavigation: FC<{
activeItem,
mode,
}) => {
const { showOnlyAdminMenu, newAdminUIEnabled } = useNewAdminMenu();
const { showOnlyAdminMenu } = useNewAdminMenu();
if (showOnlyAdminMenu) {
return <AdminMenuNavigation onClick={() => onClick('/admin')} />;
}
@ -247,40 +246,7 @@ export const AdminSettingsNavigation: FC<{
onClick(activeItem);
};
if (newAdminUIEnabled) {
return <AdminSettingsLink mode={mode} onClick={setFullModeOnClick} />;
}
return (
<>
{mode === 'full' && (
<SecondaryNavigation
expanded={expanded}
onExpandChange={(expand) => {
onExpandChange(expand);
}}
mode={mode}
title='Admin'
>
<SecondaryNavigationList
routes={routes}
mode={mode}
onClick={onClick}
activeItem={activeItem}
/>
</SecondaryNavigation>
)}
{mode === 'mini' && (
<ShowAdmin
onChange={() => {
onExpandChange(true);
onSetFullMode();
}}
/>
)}
</>
);
return <AdminSettingsLink mode={mode} onClick={setFullModeOnClick} />;
};
export const AdminSettingsLink: FC<{

View File

@ -23,7 +23,6 @@ test('switch full mode and mini mode', () => {
expect(screen.queryByText('Projects')).toBeInTheDocument();
expect(screen.queryByText('Applications')).toBeInTheDocument();
expect(screen.queryByText('Users')).toBeInTheDocument();
const hide = screen.getByText('Hide (⌘ + B)');
@ -31,14 +30,12 @@ test('switch full mode and mini mode', () => {
expect(screen.queryByText('Projects')).not.toBeInTheDocument();
expect(screen.queryByText('Applications')).not.toBeInTheDocument();
expect(screen.queryByText('Users')).not.toBeInTheDocument();
const expand = screen.getByTestId('expand-navigation');
fireEvent.click(expand);
expect(screen.queryByText('Projects')).toBeInTheDocument();
expect(screen.queryByText('Applications')).toBeInTheDocument();
expect(screen.queryByText('Users')).toBeInTheDocument();
});
test('persist navigation mode and expansion selection in storage', async () => {
@ -48,9 +45,6 @@ test('persist navigation mode and expansion selection in storage', async () => {
const configure = screen.getByText('Configure');
configure.click(); // expand
configure.click(); // hide
const admin = screen.getByText('Admin');
admin.click();
const hide = screen.getByText('Hide (⌘ + B)');
hide.click();
@ -63,7 +57,7 @@ test('persist navigation mode and expansion selection in storage', async () => {
'navigation-expanded:v1',
{},
);
expect(expanded).toEqual(['admin']);
expect(expanded).toEqual(['configure']);
});
});
@ -122,13 +116,6 @@ describe('order of items in navigation', () => {
await waitFor(() =>
expect(configureButton.getAttribute('aria-expanded')).toBe('true'),
);
const adminButton = await screen.findByRole('button', {
name: /admin/i,
});
adminButton.click();
await waitFor(() =>
expect(adminButton.getAttribute('aria-expanded')).toBe('true'),
);
const links = await screen.findAllByRole('link');
return links.map((el: HTMLElement) => ({

View File

@ -30,7 +30,6 @@ import { ReactComponent as LogoOnlyWhite } from 'assets/img/logo.svg';
import { ReactComponent as LogoOnly } from 'assets/img/logoDark.svg';
import { Link } from 'react-router-dom';
import { useFlag } from '@unleash/proxy-client-react';
import { useUiFlag } from 'hooks/useUiFlag';
import { useNewAdminMenu } from 'hooks/useNewAdminMenu';
export const MobileNavigationSidebar: FC<{
@ -38,7 +37,6 @@ export const MobileNavigationSidebar: FC<{
NewInUnleash?: typeof NewInUnleash;
}> = ({ onClick, NewInUnleash }) => {
const { routes } = useRoutes();
const newAdminUIEnabled = useUiFlag('adminNavUI');
return (
<>
@ -49,15 +47,7 @@ export const MobileNavigationSidebar: FC<{
mode='full'
onClick={onClick}
/>
{newAdminUIEnabled ? (
<AdminSettingsLink mode={'full'} onClick={onClick} />
) : (
<SecondaryNavigationList
routes={routes.adminRoutes}
mode='full'
onClick={onClick}
/>
)}
<AdminSettingsLink mode={'full'} onClick={onClick} />
<OtherLinksList />
</>
);
@ -248,19 +238,17 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
</>
}
elseShow={
<>
<AdminSettingsNavigation
onClick={setActiveItem}
mode={mode}
onSetFullMode={() => setMode('full')}
activeItem={activeItem}
onExpandChange={(expand) => {
changeExpanded('admin', expand);
}}
expanded={expanded.includes('admin')}
routes={routes.adminRoutes}
/>
</>
<AdminSettingsNavigation
onClick={setActiveItem}
mode={mode}
onSetFullMode={() => setMode('full')}
activeItem={activeItem}
onExpandChange={(expand) => {
changeExpanded('admin', expand);
}}
expanded={expanded.includes('admin')}
routes={routes.adminRoutes}
/>
}
/>
</StretchContainer>

View File

@ -59,56 +59,8 @@ exports[`order of items in navigation > menu for enterprise plan 1`] = `
"text": "Segments",
},
{
"icon": "GroupOutlinedIcon",
"text": "Users",
},
{
"icon": "ComputerIcon",
"text": "Service accounts",
},
{
"icon": "GroupsOutlinedIcon",
"text": "Groups",
},
{
"icon": "AdminPanelSettingsOutlinedIcon",
"text": "Roles",
},
{
"icon": "KeyOutlinedIcon",
"text": "API access",
},
{
"icon": "AssignmentOutlinedIcon",
"text": "Single sign-on",
},
{
"icon": "HubOutlinedIcon",
"text": "Network",
},
{
"icon": "BuildOutlinedIcon",
"text": "Maintenance",
},
{
"icon": "ViewCarouselIcon",
"text": "Banners",
},
{
"icon": "QueryStatsOutlinedIcon",
"text": "Instance stats",
},
{
"icon": "ShieldOutlinedIcon",
"text": "Instance privacy",
},
{
"icon": "HistoryOutlinedIcon",
"text": "Login history",
},
{
"icon": "EventNoteOutlinedIcon",
"text": "Event log",
"icon": "SettingsIcon",
"text": "Admin settings",
},
]
`;
@ -164,28 +116,8 @@ exports[`order of items in navigation > menu for open-source 1`] = `
"text": "Segments",
},
{
"icon": "GroupOutlinedIcon",
"text": "Users",
},
{
"icon": "KeyOutlinedIcon",
"text": "API access",
},
{
"icon": "BuildOutlinedIcon",
"text": "Maintenance",
},
{
"icon": "QueryStatsOutlinedIcon",
"text": "Instance stats",
},
{
"icon": "ShieldOutlinedIcon",
"text": "Instance privacy",
},
{
"icon": "EventNoteOutlinedIcon",
"text": "Event log",
"icon": "SettingsIcon",
"text": "Admin settings",
},
]
`;
@ -249,56 +181,8 @@ exports[`order of items in navigation > menu for pro plan 1`] = `
"text": "Segments",
},
{
"icon": "GroupOutlinedIcon",
"text": "Users",
},
{
"icon": "ComputerIcon",
"text": "Service accounts",
},
{
"icon": "GroupsOutlinedIcon",
"text": "Groups",
},
{
"icon": "AdminPanelSettingsOutlinedIcon",
"text": "Roles",
},
{
"icon": "KeyOutlinedIcon",
"text": "API access",
},
{
"icon": "AssignmentOutlinedIcon",
"text": "Single sign-on",
},
{
"icon": "HubOutlinedIcon",
"text": "Network",
},
{
"icon": "BuildOutlinedIcon",
"text": "Maintenance",
},
{
"icon": "ViewCarouselIcon",
"text": "Banners",
},
{
"icon": "QueryStatsOutlinedIcon",
"text": "Instance stats",
},
{
"icon": "ShieldOutlinedIcon",
"text": "Instance privacy",
},
{
"icon": "HistoryOutlinedIcon",
"text": "Login history",
},
{
"icon": "EventNoteOutlinedIcon",
"text": "Event log",
"icon": "SettingsIcon",
"text": "Admin settings",
},
]
`;

View File

@ -1,14 +1,10 @@
import { useUiFlag } from './useUiFlag';
import { useLocation } from 'react-router';
export const useNewAdminMenu = () => {
const newAdminUIEnabled = useUiFlag('adminNavUI');
const location = useLocation();
return {
showOnlyAdminMenu:
newAdminUIEnabled &&
(location.pathname.indexOf('/admin') === 0 ||
location.pathname.indexOf('/history') === 0),
newAdminUIEnabled,
location.pathname.indexOf('/admin') === 0 ||
location.pathname.indexOf('/history') === 0,
};
};

View File

@ -89,7 +89,6 @@ export type UiFlags = {
showUserDeviceCount?: boolean;
consumptionModel?: boolean;
edgeObservability?: boolean;
adminNavUI?: boolean;
tagTypeColor?: boolean;
addEditStrategy?: boolean;
newStrategyDropdown?: boolean;

View File

@ -60,7 +60,6 @@ export type IFlagKey =
| 'consumptionModel'
| 'teamsIntegrationChangeRequests'
| 'edgeObservability'
| 'adminNavUI'
| 'tagTypeColor'
| 'addEditStrategy'
| 'newStrategyDropdown'
@ -290,10 +289,6 @@ const flags: IFlags = {
process.env.EXPERIMENTAL_EDGE_OBSERVABILITY,
false,
),
adminNavUI: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_ADMIN_NAV_UI,
false,
),
tagTypeColor: parseEnvVarBoolean(
process.env.UNLEASH_EXPERIMENTAL_TAG_TYPE_COLOR,
false,

View File

@ -54,7 +54,6 @@ process.nextTick(async () => {
uniqueSdkTracking: true,
filterExistingFlagNames: true,
teamsIntegrationChangeRequests: true,
adminNavUI: true,
tagTypeColor: true,
newStrategyDropdown: true,
addEditStrategy: true,