mirror of
https://github.com/Unleash/unleash.git
synced 2025-01-25 00:07:47 +01:00
feat: plan specific navigation (#7126)
This commit is contained in:
parent
a4c48a16a4
commit
b783e89c88
@ -1,5 +1,5 @@
|
||||
import { forwardRef, type ReactNode } from 'react';
|
||||
import { Box, Grid, styled } from '@mui/material';
|
||||
import { Box, Grid, styled, useMediaQuery, useTheme } from '@mui/material';
|
||||
import Header from 'component/menu/Header/Header';
|
||||
import Footer from 'component/menu/Footer/Footer';
|
||||
import Proclamation from 'component/common/Proclamation/Proclamation';
|
||||
@ -105,6 +105,8 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
|
||||
const StyledMainLayoutContent = SpaciousMainLayoutContent;
|
||||
const sidebarNavigationEnabled = useUiFlag('navigationSidebar');
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('md'));
|
||||
|
||||
return (
|
||||
<>
|
||||
@ -120,9 +122,16 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
)}
|
||||
show={<DraftBanner project={projectId || ''} />}
|
||||
/>
|
||||
<Box sx={{ display: 'flex', mt: '2px' }}>
|
||||
<Box
|
||||
sx={(theme) => ({
|
||||
display: 'flex',
|
||||
mt: theme.spacing(0.25),
|
||||
})}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={sidebarNavigationEnabled}
|
||||
condition={
|
||||
sidebarNavigationEnabled && !isSmallScreen
|
||||
}
|
||||
show={<NavigationSidebar />}
|
||||
/>
|
||||
<StyledMainLayoutContent
|
||||
|
@ -6,6 +6,7 @@ import {
|
||||
ListItemIcon,
|
||||
ListItemText,
|
||||
styled,
|
||||
Tooltip,
|
||||
Typography,
|
||||
} from '@mui/material';
|
||||
import { Link } from 'react-router-dom';
|
||||
@ -37,8 +38,19 @@ import LicenseIcon from '@mui/icons-material/ReceiptLongOutlined';
|
||||
import InstancePrivacyIcon from '@mui/icons-material/ShieldOutlined';
|
||||
import LoginHistoryIcon from '@mui/icons-material/HistoryOutlined';
|
||||
import EventLogIcon from '@mui/icons-material/EventNoteOutlined';
|
||||
import FlagTypesIcon from '@mui/icons-material/OutlinedFlag';
|
||||
import EmptyIcon from '@mui/icons-material/CheckBoxOutlineBlankOutlined';
|
||||
import CorsIcon from '@mui/icons-material/StorageOutlined';
|
||||
import BillingIcon from '@mui/icons-material/CreditCardOutlined';
|
||||
import { ReactComponent as ProjectIcon } from 'assets/icons/projectIconSmall.svg';
|
||||
import type { FC } from 'react';
|
||||
import { type FC, type ReactNode, useCallback } from 'react';
|
||||
import { getCondensedRoutes, getRoutes } from '../../../menu/routes';
|
||||
import { useAdminRoutes } from '../../../admin/useAdminRoutes';
|
||||
import { filterByConfig, mapRouteLink } from 'component/common/util';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import type SvgIcon from '@mui/material/SvgIcon/SvgIcon';
|
||||
import { EnterpriseBadge } from 'component/common/EnterpriseBadge/EnterpriseBadge';
|
||||
import type { INavigationMenuItem } from 'interfaces/route';
|
||||
|
||||
export const StyledProjectIcon = styled(ProjectIcon)(({ theme }) => ({
|
||||
fill: theme.palette.neutral.main,
|
||||
@ -49,9 +61,23 @@ export const StyledProjectIcon = styled(ProjectIcon)(({ theme }) => ({
|
||||
fontSize: theme.spacing(3),
|
||||
}));
|
||||
|
||||
const StyledListItem: FC<{ href: string; text: string }> = ({
|
||||
const StyledBadgeContainer = styled('div')(({ theme }) => ({
|
||||
paddingLeft: theme.spacing(2),
|
||||
display: 'flex',
|
||||
}));
|
||||
|
||||
const EnterprisePlanBadge = () => (
|
||||
<Tooltip title='This is an Enterprise feature'>
|
||||
<StyledBadgeContainer>
|
||||
<EnterpriseBadge />
|
||||
</StyledBadgeContainer>
|
||||
</Tooltip>
|
||||
);
|
||||
|
||||
const StyledListItem: FC<{ href: string; text: string; badge?: ReactNode }> = ({
|
||||
href,
|
||||
text,
|
||||
badge,
|
||||
children,
|
||||
}) => {
|
||||
return (
|
||||
@ -61,6 +87,7 @@ const StyledListItem: FC<{ href: string; text: string }> = ({
|
||||
{children}
|
||||
</ListItemIcon>
|
||||
<ListItemText primary={text} />
|
||||
{badge}
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
);
|
||||
@ -73,7 +100,75 @@ export const StyledBox = styled(Box)(({ theme }) => ({
|
||||
minHeight: '95vh',
|
||||
}));
|
||||
|
||||
const icons: Record<string, typeof SvgIcon> = {
|
||||
'/applications': ApplicationsIcon,
|
||||
'/context': ContextFieldsIcon,
|
||||
'/feature-toggle-type': FlagTypesIcon,
|
||||
'/integrations': IntegrationsIcon,
|
||||
'/segments': SegmentsIcon,
|
||||
'/strategies': CustomStrategiesIcon,
|
||||
'/tag-types': TagTypesIcon,
|
||||
'/environments': EnvironmentsIcon,
|
||||
'/admin/users': UsersIcon,
|
||||
'/admin/service-accounts': ServiceAccountIcon,
|
||||
'/admin/groups': GroupsIcon,
|
||||
'/admin/roles': RoleIcon,
|
||||
'/admin/api': ApiAccessIcon,
|
||||
'/admin/auth': SingleSignOnIcon,
|
||||
'/admin/network': NetworkIcon,
|
||||
'/admin/maintenance': MaintenanceIcon,
|
||||
'/admin/banners': BannersIcon,
|
||||
'/admin/instance': InstanceStatsIcon,
|
||||
'/admin/license': LicenseIcon,
|
||||
'/admin/instance-privacy': InstancePrivacyIcon,
|
||||
'/admin/logins': LoginHistoryIcon,
|
||||
'/admin/cors': CorsIcon,
|
||||
'/admin/billing': BillingIcon,
|
||||
'/history': EventLogIcon,
|
||||
};
|
||||
|
||||
const findIconByPath = (path: string) => {
|
||||
return icons[path] || EmptyIcon;
|
||||
};
|
||||
|
||||
const IconRenderer: FC<{ path: string }> = ({ path }) => {
|
||||
const IconComponent = findIconByPath(path); // Fallback to 'default' if the type is not found
|
||||
|
||||
return <IconComponent />;
|
||||
};
|
||||
|
||||
const useRoutes = () => {
|
||||
const { uiConfig, isPro } = useUiConfig();
|
||||
const routes = getRoutes();
|
||||
const adminRoutes = useAdminRoutes();
|
||||
|
||||
const filteredMainRoutes = {
|
||||
mainNavRoutes: getCondensedRoutes(routes.mainNavRoutes)
|
||||
.filter(filterByConfig(uiConfig))
|
||||
.map(mapRouteLink),
|
||||
mobileRoutes: getCondensedRoutes(routes.mobileRoutes)
|
||||
.filter(filterByConfig(uiConfig))
|
||||
.map(mapRouteLink),
|
||||
adminRoutes,
|
||||
};
|
||||
|
||||
const showBadge = useCallback(
|
||||
(mode?: INavigationMenuItem['menu']['mode']) => {
|
||||
return !!(
|
||||
isPro() &&
|
||||
!mode?.includes('pro') &&
|
||||
mode?.includes('enterprise')
|
||||
);
|
||||
},
|
||||
[isPro],
|
||||
);
|
||||
|
||||
return { routes: filteredMainRoutes, showBadge };
|
||||
};
|
||||
|
||||
export const NavigationSidebar = () => {
|
||||
const { routes, showBadge } = useRoutes();
|
||||
|
||||
return (
|
||||
<StyledBox>
|
||||
<List>
|
||||
@ -102,39 +197,14 @@ export const NavigationSidebar = () => {
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ p: 0 }}>
|
||||
<List>
|
||||
<StyledListItem
|
||||
href='/integrations'
|
||||
text='Integrations'
|
||||
>
|
||||
<IntegrationsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/environments'
|
||||
text='Environments'
|
||||
>
|
||||
<EnvironmentsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/context' text='Context fields'>
|
||||
<ContextFieldsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/segments' text='Segments'>
|
||||
<SegmentsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/tag-types' text='Tag types'>
|
||||
<TagTypesIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/applications'
|
||||
text='Applications'
|
||||
>
|
||||
<ApplicationsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/strategies'
|
||||
text='Custom strategies'
|
||||
>
|
||||
<CustomStrategiesIcon />
|
||||
</StyledListItem>
|
||||
{routes.mainNavRoutes.map((route) => (
|
||||
<StyledListItem
|
||||
href={route.path}
|
||||
text={route.title}
|
||||
>
|
||||
<IconRenderer path={route.path} />
|
||||
</StyledListItem>
|
||||
))}
|
||||
</List>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
@ -150,66 +220,19 @@ export const NavigationSidebar = () => {
|
||||
</AccordionSummary>
|
||||
<AccordionDetails sx={{ p: 0 }}>
|
||||
<List>
|
||||
<StyledListItem href='/admin/users' text='Users'>
|
||||
<UsersIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/admin/service-accounts'
|
||||
text='Service accounts'
|
||||
>
|
||||
<ServiceAccountIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/admin/groups' text='Groups'>
|
||||
<GroupsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/admin/roles' text='Roles'>
|
||||
<RoleIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/admin/api' text='API Access'>
|
||||
<ApiAccessIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/admin/auth'
|
||||
text='Single sign-on'
|
||||
>
|
||||
<SingleSignOnIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/admin/network' text='Network'>
|
||||
<NetworkIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/admin/maintenance'
|
||||
text='Maintenance'
|
||||
>
|
||||
<MaintenanceIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/admin/banners' text='Banners'>
|
||||
<BannersIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/admin/instance'
|
||||
text='Instance stats'
|
||||
>
|
||||
<InstanceStatsIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/admin/license' text='License'>
|
||||
<LicenseIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/admin/instance-privacy'
|
||||
text='Instance privacy'
|
||||
>
|
||||
<InstancePrivacyIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem
|
||||
href='/admin/logins'
|
||||
text='Login history'
|
||||
>
|
||||
<LoginHistoryIcon />
|
||||
</StyledListItem>
|
||||
<StyledListItem href='/history' text='Event log'>
|
||||
<EventLogIcon />
|
||||
</StyledListItem>
|
||||
{routes.adminRoutes.map((route) => (
|
||||
<StyledListItem
|
||||
href={route.path}
|
||||
text={route.title}
|
||||
badge={
|
||||
showBadge(route?.menu?.mode) ? (
|
||||
<EnterprisePlanBadge />
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<IconRenderer path={route.path} />
|
||||
</StyledListItem>
|
||||
))}
|
||||
</List>
|
||||
</AccordionDetails>
|
||||
</Accordion>
|
||||
|
Loading…
Reference in New Issue
Block a user