mirror of
https://github.com/Unleash/unleash.git
synced 2025-05-17 01:17:29 +02:00
feat: command bar admin menu improvements (#9689)
This commit is contained in:
parent
6432262be5
commit
b9a7c0cda6
@ -1,16 +1,19 @@
|
|||||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||||
import { adminRoutes } from './oldAdminRoutes';
|
import { adminRoutes as oldAdminRoutes } from './oldAdminRoutes';
|
||||||
|
import { adminRoutes } from './adminRoutes';
|
||||||
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
||||||
import { filterAdminRoutes } from './filterAdminRoutes';
|
import { filterAdminRoutes } from './filterAdminRoutes';
|
||||||
import { filterByConfig, mapRouteLink } from 'component/common/util';
|
import { filterByConfig, mapRouteLink } from 'component/common/util';
|
||||||
|
import { useUiFlag } from 'hooks/useUiFlag';
|
||||||
|
|
||||||
export const useAdminRoutes = () => {
|
export const useAdminRoutes = () => {
|
||||||
|
const newAdminUIEnabled = useUiFlag('adminNavUI');
|
||||||
const { uiConfig, isPro, isEnterprise } = useUiConfig();
|
const { uiConfig, isPro, isEnterprise } = useUiConfig();
|
||||||
const { isBilling } = useInstanceStatus();
|
const { isBilling } = useInstanceStatus();
|
||||||
const routes = [...adminRoutes];
|
const routes = newAdminUIEnabled ? [...adminRoutes] : [...oldAdminRoutes];
|
||||||
|
|
||||||
if (uiConfig.flags.UNLEASH_CLOUD) {
|
if (uiConfig.flags.UNLEASH_CLOUD) {
|
||||||
const adminBillingMenuItem = adminRoutes.findIndex(
|
const adminBillingMenuItem = routes.findIndex(
|
||||||
(route) => route.title === 'Billing & invoices',
|
(route) => route.title === 'Billing & invoices',
|
||||||
);
|
);
|
||||||
routes[adminBillingMenuItem] = {
|
routes[adminBillingMenuItem] = {
|
||||||
|
22
frontend/src/component/commandBar/ButtonItemIcon.tsx
Normal file
22
frontend/src/component/commandBar/ButtonItemIcon.tsx
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import { IconRenderer } from 'component/layout/MainLayout/NavigationSidebar/IconRenderer';
|
||||||
|
import InsightsIcon from '@mui/icons-material/Insights';
|
||||||
|
import PlaygroundIcon from '@mui/icons-material/AutoFixNormal';
|
||||||
|
import { ProjectIcon } from 'component/common/ProjectIcon/ProjectIcon';
|
||||||
|
|
||||||
|
export const ButtonItemIcon = ({
|
||||||
|
path,
|
||||||
|
}: {
|
||||||
|
path: string;
|
||||||
|
}) => {
|
||||||
|
if (path === '/projects') {
|
||||||
|
return <ProjectIcon />;
|
||||||
|
}
|
||||||
|
if (path === '/playground') {
|
||||||
|
return <PlaygroundIcon />;
|
||||||
|
}
|
||||||
|
if (path === '/insights') {
|
||||||
|
return <InsightsIcon />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return <IconRenderer path={path} />;
|
||||||
|
};
|
@ -18,7 +18,6 @@ import {
|
|||||||
type CommandResultGroupItem,
|
type CommandResultGroupItem,
|
||||||
} from './RecentlyVisited/CommandResultGroup';
|
} from './RecentlyVisited/CommandResultGroup';
|
||||||
import { CommandPageSuggestions } from './CommandPageSuggestions';
|
import { CommandPageSuggestions } from './CommandPageSuggestions';
|
||||||
import { useRoutes } from 'component/layout/MainLayout/NavigationSidebar/useRoutes';
|
|
||||||
import { useAsyncDebounce } from 'react-table';
|
import { useAsyncDebounce } from 'react-table';
|
||||||
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
import useProjects from 'hooks/api/getters/useProjects/useProjects';
|
||||||
import {
|
import {
|
||||||
@ -31,6 +30,7 @@ import { CommandSearchPages } from './CommandSearchPages';
|
|||||||
import { CommandBarFeedback } from './CommandBarFeedback';
|
import { CommandBarFeedback } from './CommandBarFeedback';
|
||||||
import { RecentlyVisitedRecorder } from './RecentlyVisitedRecorder';
|
import { RecentlyVisitedRecorder } from './RecentlyVisitedRecorder';
|
||||||
import { ScreenReaderOnly } from 'component/common/ScreenReaderOnly/ScreenReaderOnly';
|
import { ScreenReaderOnly } from 'component/common/ScreenReaderOnly/ScreenReaderOnly';
|
||||||
|
import { useCommandBarRoutes } from './useCommandBarRoutes';
|
||||||
|
|
||||||
export const CommandResultsPaper = styled(Paper)(({ theme }) => ({
|
export const CommandResultsPaper = styled(Paper)(({ theme }) => ({
|
||||||
position: 'absolute',
|
position: 'absolute',
|
||||||
@ -101,12 +101,6 @@ const StyledClose = styled(Close)(({ theme }) => ({
|
|||||||
fontSize: theme.typography.body1.fontSize,
|
fontSize: theme.typography.body1.fontSize,
|
||||||
}));
|
}));
|
||||||
|
|
||||||
interface IPageRouteInfo {
|
|
||||||
path: string;
|
|
||||||
route: string;
|
|
||||||
title: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export const CommandBar = () => {
|
export const CommandBar = () => {
|
||||||
const { trackEvent } = usePlausibleTracker();
|
const { trackEvent } = usePlausibleTracker();
|
||||||
const searchInputRef = useRef<HTMLInputElement>(null);
|
const searchInputRef = useRef<HTMLInputElement>(null);
|
||||||
@ -124,19 +118,7 @@ export const CommandBar = () => {
|
|||||||
useState<CommandQueryCounter>({ query: '', count: 0 });
|
useState<CommandQueryCounter>({ query: '', count: 0 });
|
||||||
const [hasNoResults, setHasNoResults] = useState(false);
|
const [hasNoResults, setHasNoResults] = useState(false);
|
||||||
const [value, setValue] = useState<string>('');
|
const [value, setValue] = useState<string>('');
|
||||||
const { routes } = useRoutes();
|
const { allRoutes } = useCommandBarRoutes();
|
||||||
const allRoutes: Record<string, IPageRouteInfo> = {};
|
|
||||||
for (const route of [
|
|
||||||
...routes.mainNavRoutes,
|
|
||||||
...routes.adminRoutes,
|
|
||||||
...routes.primaryRoutes,
|
|
||||||
]) {
|
|
||||||
allRoutes[route.path] = {
|
|
||||||
path: route.path,
|
|
||||||
route: route.route,
|
|
||||||
title: route.title,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const hideSuggestions = () => {
|
const hideSuggestions = () => {
|
||||||
setShowSuggestions(false);
|
setShowSuggestions(false);
|
||||||
@ -159,7 +141,7 @@ export const CommandBar = () => {
|
|||||||
setSearchedProjects(mappedProjects);
|
setSearchedProjects(mappedProjects);
|
||||||
|
|
||||||
const filteredPages = Object.values(allRoutes).filter((route) =>
|
const filteredPages = Object.values(allRoutes).filter((route) =>
|
||||||
route.title.toLowerCase().includes(query.toLowerCase()),
|
route.searchText.toLowerCase().includes(query.toLowerCase()),
|
||||||
);
|
);
|
||||||
const mappedPages = filteredPages.map((page) => ({
|
const mappedPages = filteredPages.map((page) => ({
|
||||||
name: page.title,
|
name: page.title,
|
||||||
|
@ -9,7 +9,7 @@ import {
|
|||||||
type CommandResultGroupItem,
|
type CommandResultGroupItem,
|
||||||
} from './RecentlyVisited/CommandResultGroup';
|
} from './RecentlyVisited/CommandResultGroup';
|
||||||
import { ListItemButton } from '@mui/material';
|
import { ListItemButton } from '@mui/material';
|
||||||
import { IconRenderer } from 'component/layout/MainLayout/NavigationSidebar/IconRenderer';
|
import { ButtonItemIcon } from './ButtonItemIcon';
|
||||||
|
|
||||||
export const CommandSearchPages = ({
|
export const CommandSearchPages = ({
|
||||||
items,
|
items,
|
||||||
@ -50,7 +50,7 @@ export const CommandSearchPages = ({
|
|||||||
sx={listItemButtonStyle}
|
sx={listItemButtonStyle}
|
||||||
>
|
>
|
||||||
<StyledListItemIcon>
|
<StyledListItemIcon>
|
||||||
<IconRenderer path={item.link} />
|
<ButtonItemIcon path={item.link} />
|
||||||
</StyledListItemIcon>
|
</StyledListItemIcon>
|
||||||
<StyledListItemText>
|
<StyledListItemText>
|
||||||
<StyledButtonTypography color='textPrimary'>
|
<StyledButtonTypography color='textPrimary'>
|
||||||
|
@ -10,14 +10,12 @@ import {
|
|||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
import type { Theme } from '@mui/material/styles/createTheme';
|
import type { Theme } from '@mui/material/styles/createTheme';
|
||||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||||
import { IconRenderer } from 'component/layout/MainLayout/NavigationSidebar/IconRenderer';
|
|
||||||
import InsightsIcon from '@mui/icons-material/Insights';
|
|
||||||
import PlaygroundIcon from '@mui/icons-material/AutoFixNormal';
|
|
||||||
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
import { TooltipResolver } from 'component/common/TooltipResolver/TooltipResolver';
|
||||||
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
import { usePlausibleTracker } from 'hooks/usePlausibleTracker';
|
||||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||||
import { Children } from 'react';
|
import { Children } from 'react';
|
||||||
import { ProjectIcon } from 'component/common/ProjectIcon/ProjectIcon';
|
import { ProjectIcon } from 'component/common/ProjectIcon/ProjectIcon';
|
||||||
|
import { ButtonItemIcon } from '../ButtonItemIcon';
|
||||||
|
|
||||||
export const listItemButtonStyle = (theme: Theme) => ({
|
export const listItemButtonStyle = (theme: Theme) => ({
|
||||||
border: `1px solid transparent`,
|
border: `1px solid transparent`,
|
||||||
@ -48,24 +46,6 @@ export interface CommandResultGroupItem {
|
|||||||
description?: string | null;
|
description?: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ButtonItemIcon = ({
|
|
||||||
path,
|
|
||||||
}: {
|
|
||||||
path: string;
|
|
||||||
}) => {
|
|
||||||
if (path === '/projects') {
|
|
||||||
return <ProjectIcon />;
|
|
||||||
}
|
|
||||||
if (path === '/playground') {
|
|
||||||
return <PlaygroundIcon />;
|
|
||||||
}
|
|
||||||
if (path === '/insights') {
|
|
||||||
return <InsightsIcon />;
|
|
||||||
}
|
|
||||||
|
|
||||||
return <IconRenderer path={path} />;
|
|
||||||
};
|
|
||||||
|
|
||||||
export const RecentlyVisitedPathButton = ({
|
export const RecentlyVisitedPathButton = ({
|
||||||
keyName,
|
keyName,
|
||||||
path,
|
path,
|
||||||
|
55
frontend/src/component/commandBar/useCommandBarRoutes.ts
Normal file
55
frontend/src/component/commandBar/useCommandBarRoutes.ts
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
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';
|
||||||
|
|
||||||
|
interface IPageRouteInfo {
|
||||||
|
path: string;
|
||||||
|
route: string;
|
||||||
|
title: string;
|
||||||
|
searchText: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCommandBarRoutes = () => {
|
||||||
|
const newAdminUIEnabled = useUiFlag('adminNavUI');
|
||||||
|
const { routes } = useRoutes();
|
||||||
|
const getSearchText = (route: INavigationMenuItem, title: string) => {
|
||||||
|
if (route.group && adminGroups[route.group]) {
|
||||||
|
return `${title} ${route.path} ${route.group} ${adminGroups[route.group]}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return `${title} ${route.path}`;
|
||||||
|
};
|
||||||
|
|
||||||
|
const getRouteTitle = (route: INavigationMenuItem) => {
|
||||||
|
if (route.path === '/admin') {
|
||||||
|
return 'Admin settings';
|
||||||
|
}
|
||||||
|
|
||||||
|
return route.title;
|
||||||
|
};
|
||||||
|
return useMemo(() => {
|
||||||
|
const allRoutes: Record<string, IPageRouteInfo> = {};
|
||||||
|
for (const route of [
|
||||||
|
...routes.mainNavRoutes,
|
||||||
|
...routes.adminRoutes,
|
||||||
|
...routes.primaryRoutes,
|
||||||
|
]) {
|
||||||
|
const title = getRouteTitle(route);
|
||||||
|
allRoutes[route.path] = {
|
||||||
|
path: route.path,
|
||||||
|
route: route.route,
|
||||||
|
title: title,
|
||||||
|
searchText: newAdminUIEnabled
|
||||||
|
? getSearchText(route, title)
|
||||||
|
: title,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
allRoutes,
|
||||||
|
newAdminUIEnabled,
|
||||||
|
};
|
||||||
|
}, [routes]);
|
||||||
|
};
|
@ -13,7 +13,9 @@ import UsersIcon from '@mui/icons-material/GroupOutlined';
|
|||||||
import ServiceAccountIcon from '@mui/icons-material/Computer';
|
import ServiceAccountIcon from '@mui/icons-material/Computer';
|
||||||
import GroupsIcon from '@mui/icons-material/GroupsOutlined';
|
import GroupsIcon from '@mui/icons-material/GroupsOutlined';
|
||||||
import RoleIcon from '@mui/icons-material/AdminPanelSettingsOutlined';
|
import RoleIcon from '@mui/icons-material/AdminPanelSettingsOutlined';
|
||||||
|
import SettingsIcon from '@mui/icons-material/Settings';
|
||||||
import SearchIcon from '@mui/icons-material/Search';
|
import SearchIcon from '@mui/icons-material/Search';
|
||||||
|
import InsightsIcon from '@mui/icons-material/Insights';
|
||||||
import ApiAccessIcon from '@mui/icons-material/KeyOutlined';
|
import ApiAccessIcon from '@mui/icons-material/KeyOutlined';
|
||||||
import SingleSignOnIcon from '@mui/icons-material/AssignmentOutlined';
|
import SingleSignOnIcon from '@mui/icons-material/AssignmentOutlined';
|
||||||
import NetworkIcon from '@mui/icons-material/HubOutlined';
|
import NetworkIcon from '@mui/icons-material/HubOutlined';
|
||||||
@ -39,6 +41,7 @@ const icons: Record<
|
|||||||
typeof SvgIcon | FC<ComponentProps<typeof SvgIcon>>
|
typeof SvgIcon | FC<ComponentProps<typeof SvgIcon>>
|
||||||
> = {
|
> = {
|
||||||
'/search': SearchIcon,
|
'/search': SearchIcon,
|
||||||
|
'/insights': InsightsIcon,
|
||||||
'/applications': ApplicationsIcon,
|
'/applications': ApplicationsIcon,
|
||||||
'/context': ContextFieldsIcon,
|
'/context': ContextFieldsIcon,
|
||||||
'/feature-toggle-type': FlagTypesIcon,
|
'/feature-toggle-type': FlagTypesIcon,
|
||||||
@ -47,13 +50,24 @@ const icons: Record<
|
|||||||
'/strategies': CustomStrategiesIcon,
|
'/strategies': CustomStrategiesIcon,
|
||||||
'/tag-types': TagTypesIcon,
|
'/tag-types': TagTypesIcon,
|
||||||
'/environments': EnvironmentsIcon,
|
'/environments': EnvironmentsIcon,
|
||||||
|
'/admin': SettingsIcon,
|
||||||
'/admin/users': UsersIcon,
|
'/admin/users': UsersIcon,
|
||||||
'/admin/service-accounts': ServiceAccountIcon,
|
'/admin/service-accounts': ServiceAccountIcon,
|
||||||
'/admin/groups': GroupsIcon,
|
'/admin/groups': GroupsIcon,
|
||||||
'/admin/roles': RoleIcon,
|
'/admin/roles': RoleIcon,
|
||||||
|
'/admin/roles/project-roles': RoleIcon,
|
||||||
'/admin/api': ApiAccessIcon,
|
'/admin/api': ApiAccessIcon,
|
||||||
'/admin/auth': SingleSignOnIcon,
|
'/admin/auth': SingleSignOnIcon,
|
||||||
|
'/admin/auth/saml': SingleSignOnIcon,
|
||||||
|
'/admin/auth/scim': SingleSignOnIcon,
|
||||||
|
'/admin/auth/password': SingleSignOnIcon,
|
||||||
|
'/admin/auth/google': SingleSignOnIcon,
|
||||||
'/admin/network': NetworkIcon,
|
'/admin/network': NetworkIcon,
|
||||||
|
'/admin/network/traffic': NetworkIcon,
|
||||||
|
'/admin/network/data-usage': NetworkIcon,
|
||||||
|
'/admin/network/frontend-data-usage': NetworkIcon,
|
||||||
|
'/admin/network/connected-edges': NetworkIcon,
|
||||||
|
'/admin/network/backend-connections': NetworkIcon,
|
||||||
'/admin/maintenance': MaintenanceIcon,
|
'/admin/maintenance': MaintenanceIcon,
|
||||||
'/admin/banners': BannersIcon,
|
'/admin/banners': BannersIcon,
|
||||||
'/admin/instance': InstanceStatsIcon,
|
'/admin/instance': InstanceStatsIcon,
|
||||||
|
Loading…
Reference in New Issue
Block a user