mirror of
https://github.com/Unleash/unleash.git
synced 2025-07-26 13:48:33 +02:00
feat: move admin menu into nav sidebar (#9774)
This commit is contained in:
parent
86cfb2f651
commit
ab594f5c29
@ -1,11 +1,11 @@
|
||||
import type { INavigationMenuItem } from 'interfaces/route';
|
||||
|
||||
export const adminGroups: Record<string, string> = {
|
||||
users: 'User administration',
|
||||
users: 'User config',
|
||||
access: 'Access control',
|
||||
sso: 'Single sign-on',
|
||||
network: 'Network',
|
||||
instance: 'Instance configuration',
|
||||
instance: 'Instance config',
|
||||
};
|
||||
|
||||
export const adminRoutes: INavigationMenuItem[] = [
|
||||
@ -87,7 +87,7 @@ export const adminRoutes: INavigationMenuItem[] = [
|
||||
|
||||
// Single sign-on/login
|
||||
{
|
||||
path: '/admin/auth',
|
||||
path: '/admin/auth/oidc',
|
||||
title: 'Open ID Connect',
|
||||
menu: { adminSettings: true, mode: ['enterprise'] },
|
||||
group: 'sso',
|
||||
|
@ -9,7 +9,7 @@ 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 { Route, Routes, useLocation } from 'react-router-dom';
|
||||
import { Navigate, Route, Routes, useLocation } from 'react-router-dom';
|
||||
import { usePageTitle } from 'hooks/usePageTitle';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
|
||||
@ -20,7 +20,7 @@ export const AuthSettings = () => {
|
||||
const tabs = [
|
||||
{
|
||||
label: 'Single sign-on: OpenID Connect',
|
||||
path: '/admin/auth',
|
||||
path: '/admin/auth/oidc',
|
||||
},
|
||||
{
|
||||
label: 'Single sign-on: SAML 2.0',
|
||||
@ -55,7 +55,12 @@ export const AuthSettings = () => {
|
||||
<PermissionGuard permissions={[ADMIN, UPDATE_AUTH_CONFIGURATION]}>
|
||||
<PageContent header={activeTab}>
|
||||
<Routes>
|
||||
<Route path='/' element={<OidcAuth />} />
|
||||
<Route
|
||||
path='/'
|
||||
index
|
||||
element={<Navigate to='/admin/auth/oidc' />}
|
||||
/>
|
||||
<Route path='/oidc' index element={<OidcAuth />} />
|
||||
<Route path='/saml' element={<SamlAuth />} />
|
||||
<Route path='/password' element={<PasswordAuth />} />
|
||||
{googleAuthEnabled && (
|
||||
|
@ -10,7 +10,7 @@ import {
|
||||
AccordionDetails,
|
||||
} from '@mui/material';
|
||||
import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
|
||||
import type { FC, ReactNode } from 'react';
|
||||
import { useState, type FC, type ReactNode } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import type { Theme } from '@mui/material/styles/createTheme';
|
||||
|
||||
@ -18,15 +18,29 @@ const listItemButtonStyle = (theme: Theme) => ({
|
||||
borderRadius: theme.spacing(0.5),
|
||||
borderLeft: `${theme.spacing(0.5)} solid transparent`,
|
||||
m: 0,
|
||||
paddingTop: theme.spacing(1),
|
||||
paddingBottom: theme.spacing(1),
|
||||
'&.Mui-selected': {
|
||||
borderLeft: `${theme.spacing(0.5)} solid ${theme.palette.primary.main}`,
|
||||
backgroundColor: '#607B81',
|
||||
color: theme.palette.common.white,
|
||||
'& p': {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
color: 'inherit',
|
||||
fontWeight: theme.typography.fontWeightLight,
|
||||
'& p': {
|
||||
fontWeight: theme.typography.fontWeightLight,
|
||||
},
|
||||
},
|
||||
},
|
||||
minHeight: '0px',
|
||||
'.MuiAccordionSummary-content': { margin: 0 },
|
||||
'&>.MuiAccordionSummary-content.MuiAccordionSummary-content': {
|
||||
margin: '0',
|
||||
alignItems: 'center',
|
||||
padding: theme.spacing(0.5, 0),
|
||||
padding: theme.spacing(0.1, 0),
|
||||
},
|
||||
});
|
||||
|
||||
@ -35,17 +49,32 @@ const subListItemButtonStyle = (theme: Theme) => ({
|
||||
borderRadius: theme.spacing(0.5),
|
||||
borderLeft: `${theme.spacing(0.5)} solid transparent`,
|
||||
m: 0,
|
||||
paddingTop: theme.spacing(0.75),
|
||||
paddingBottom: theme.spacing(0.75),
|
||||
'&.Mui-selected': {
|
||||
borderLeft: `${theme.spacing(0.5)} solid ${theme.palette.primary.main}`,
|
||||
backgroundColor: '#607B81',
|
||||
color: theme.palette.common.white,
|
||||
'& p': {
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
},
|
||||
'&:hover': {
|
||||
backgroundColor: theme.palette.action.hover,
|
||||
color: 'inherit',
|
||||
fontWeight: theme.typography.fontWeightLight,
|
||||
'& p': {
|
||||
fontWeight: theme.typography.fontWeightLight,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const CappedText = styled(Typography)({
|
||||
const CappedText = styled(Typography)(({ theme }) => ({
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
width: '100%',
|
||||
});
|
||||
paddingTop: theme.spacing(0.25),
|
||||
}));
|
||||
|
||||
const StyledListItemIcon = styled(ListItemIcon)(({ theme }) => ({
|
||||
minWidth: theme.spacing(4),
|
||||
@ -58,6 +87,7 @@ const StyledListItemText = styled(ListItemText)(({ theme }) => ({
|
||||
|
||||
const StyledAccordion = styled(Accordion)(({ theme }) => ({
|
||||
paddingTop: theme.spacing(0),
|
||||
backgroundColor: 'inherit',
|
||||
}));
|
||||
|
||||
const StyledAccordionSummary = styled(AccordionSummary)(({ theme }) => ({
|
||||
@ -84,10 +114,16 @@ export const MenuGroup = ({
|
||||
isActiveMenu,
|
||||
staticExpanded,
|
||||
}: IMenuGroupProps) => {
|
||||
const [isExpanded, setIsExpanded] = staticExpanded
|
||||
? [true, () => undefined]
|
||||
: useState<boolean>(isActiveMenu);
|
||||
return (
|
||||
<StyledAccordion
|
||||
disableGutters={true}
|
||||
expanded={staticExpanded}
|
||||
expanded={isExpanded}
|
||||
onChange={(element, expanded) => {
|
||||
setIsExpanded(expanded);
|
||||
}}
|
||||
sx={{
|
||||
boxShadow: 'none',
|
||||
'&:before': {
|
||||
|
@ -35,9 +35,9 @@ export const IconRenderer: FC<{ path: string; active: boolean }> = ({
|
||||
|
||||
return (
|
||||
<IconComponent
|
||||
sx={{
|
||||
color: active ? 'primary.main' : 'inherit',
|
||||
}}
|
||||
sx={(theme) => ({
|
||||
color: active ? theme.palette.common.white : 'inherit',
|
||||
})}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
@ -1,4 +1,12 @@
|
||||
import { Button, styled, Typography, List } from '@mui/material';
|
||||
import {
|
||||
styled,
|
||||
Typography,
|
||||
List,
|
||||
ListItem,
|
||||
ListItemButton,
|
||||
ListItemText,
|
||||
type Theme,
|
||||
} from '@mui/material';
|
||||
import { OtherLinksList } from '../NavigationSidebar/NavigationList';
|
||||
import ArrowBackIcon from '@mui/icons-material/ArrowBack';
|
||||
import StopRoundedIcon from '@mui/icons-material/StopRounded';
|
||||
@ -6,11 +14,11 @@ import { AdminListItem, AdminSubListItem, MenuGroup } from './AdminListItem';
|
||||
import { IconRenderer } from './AdminMenuIcons';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { useInstanceStatus } from 'hooks/api/getters/useInstanceStatus/useInstanceStatus';
|
||||
import { useLocation, useNavigate } from 'react-router-dom';
|
||||
import { Link, useLocation } from 'react-router-dom';
|
||||
import { filterByConfig } from 'component/common/util';
|
||||
import { filterAdminRoutes } from 'component/admin/filterAdminRoutes';
|
||||
import { adminGroups, adminRoutes } from 'component/admin/adminRoutes';
|
||||
import type { ReactNode } from 'react';
|
||||
import { useEffect, useState, type ReactNode } from 'react';
|
||||
import type { INavigationMenuItem } from 'interfaces/route';
|
||||
import { useShowBadge } from 'component/layout/components/EnterprisePlanBadge/useShowBadge';
|
||||
import { EnterprisePlanBadge } from 'component/layout/components/EnterprisePlanBadge/EnterprisePlanBadge';
|
||||
@ -34,31 +42,72 @@ const SettingsHeader = styled(Typography)(({ theme }) => ({
|
||||
fontWeight: theme.fontWeight.bold,
|
||||
}));
|
||||
|
||||
const StyledButton = styled(Button)(({ theme }) => ({
|
||||
paddingLeft: theme.spacing(0),
|
||||
marginBottom: theme.spacing(3),
|
||||
const CappedText = styled(Typography)(({ theme }) => ({
|
||||
whiteSpace: 'nowrap',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
width: '100%',
|
||||
paddingTop: theme.spacing(0.25),
|
||||
marginLeft: theme.spacing(0.75),
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
}));
|
||||
|
||||
const StyledListItemText = styled(ListItemText)(({ theme }) => ({
|
||||
margin: 0,
|
||||
}));
|
||||
|
||||
const StyledDiv = styled('div')(({ theme }) => ({
|
||||
padding: theme.spacing(2, 2.5, 0, 2.5),
|
||||
|
||||
'&.MuiButton-root': {
|
||||
padding: theme.spacing(0),
|
||||
},
|
||||
}));
|
||||
|
||||
const StyledStopRoundedIcon = styled(StopRoundedIcon)(({ theme }) => ({
|
||||
color: theme.palette.primary.main,
|
||||
color: '#607B81',
|
||||
}));
|
||||
|
||||
export const DashboardLink = () => {
|
||||
const navigate = useNavigate();
|
||||
const ActiveStyledStopRoundedIcon = styled(StopRoundedIcon)(({ theme }) => ({
|
||||
color: theme.palette.common.white,
|
||||
}));
|
||||
|
||||
const listItemButtonStyle = (theme: Theme) => ({
|
||||
borderRadius: theme.spacing(0.5),
|
||||
borderLeft: `${theme.spacing(0.5)} solid transparent`,
|
||||
color: theme.palette.primary.main,
|
||||
fontWeight: theme.typography.fontWeightBold,
|
||||
m: 0,
|
||||
paddingTop: theme.spacing(1),
|
||||
paddingBottom: theme.spacing(1),
|
||||
minHeight: '0px',
|
||||
'.MuiAccordionSummary-content': { margin: 0 },
|
||||
'&>.MuiAccordionSummary-content.MuiAccordionSummary-content': {
|
||||
margin: '0',
|
||||
alignItems: 'center',
|
||||
padding: theme.spacing(0.1, 0),
|
||||
},
|
||||
});
|
||||
|
||||
export const DashboardLink = ({ onClick }: { onClick: () => void }) => {
|
||||
return (
|
||||
<StyledButton
|
||||
onClick={() => {
|
||||
navigate(`/personal`);
|
||||
}}
|
||||
rel='noreferrer'
|
||||
startIcon={<ArrowBackIcon />}
|
||||
>
|
||||
Back to Unleash
|
||||
</StyledButton>
|
||||
<List>
|
||||
<ListItem disablePadding>
|
||||
<ListItemButton
|
||||
dense={true}
|
||||
component={Link}
|
||||
to='/personal'
|
||||
sx={listItemButtonStyle}
|
||||
selected={false}
|
||||
onClick={onClick}
|
||||
>
|
||||
<ArrowBackIcon />
|
||||
<StyledListItemText>
|
||||
<CappedText>Back to Unleash</CappedText>
|
||||
</StyledListItemText>
|
||||
</ListItemButton>
|
||||
</ListItem>
|
||||
</List>
|
||||
);
|
||||
};
|
||||
|
||||
@ -66,8 +115,9 @@ export const AdminMobileNavigation = ({ onClick }: { onClick: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<StyledDiv>
|
||||
<AdminNavigationHeader />
|
||||
<SettingsHeader>Admin settings</SettingsHeader>
|
||||
</StyledDiv>
|
||||
<DashboardLink onClick={onClick} />
|
||||
|
||||
<AdminNavigationItems staticExpanded={true} onClick={onClick} />
|
||||
|
||||
@ -79,29 +129,18 @@ export const AdminMobileNavigation = ({ onClick }: { onClick: () => void }) => {
|
||||
export const AdminMenuNavigation = ({ onClick }: { onClick: () => void }) => {
|
||||
return (
|
||||
<>
|
||||
<AdminNavigationHeader />
|
||||
<DashboardLink onClick={onClick} />
|
||||
<AdminNavigationItems onClick={onClick} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const AdminNavigationHeader = () => {
|
||||
return (
|
||||
<>
|
||||
<SettingsHeader>Admin settings</SettingsHeader>
|
||||
<DashboardLink />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const AdminNavigationItems = ({
|
||||
onClick,
|
||||
staticExpanded,
|
||||
}: { onClick: () => void; staticExpanded?: true | undefined }) => {
|
||||
const { uiConfig, isPro, isEnterprise } = useUiConfig();
|
||||
const { isBilling } = useInstanceStatus();
|
||||
const isActiveItem = (item?: string) =>
|
||||
item !== undefined && location.pathname === item;
|
||||
const location = useLocation();
|
||||
const showBadge = useShowBadge();
|
||||
|
||||
@ -115,6 +154,28 @@ export const AdminNavigationItems = ({
|
||||
}),
|
||||
);
|
||||
|
||||
const findActiveItem = () => {
|
||||
const activeItem = routes.find(
|
||||
(route) => route.path === location.pathname,
|
||||
);
|
||||
if (!activeItem) {
|
||||
return routes.find(
|
||||
(route) =>
|
||||
route.path !== '/admin' &&
|
||||
location.pathname.startsWith(route.path),
|
||||
)?.path;
|
||||
}
|
||||
return activeItem.path;
|
||||
};
|
||||
const [activeItem, setActiveItem] = useState<string | undefined>(
|
||||
findActiveItem(),
|
||||
);
|
||||
const isActiveItem = (item?: string) =>
|
||||
item !== undefined && activeItem !== undefined && item === activeItem;
|
||||
useEffect(() => {
|
||||
setActiveItem(findActiveItem());
|
||||
}, [location, location.pathname]);
|
||||
|
||||
const menuStructure = routes.reduce(
|
||||
(acc: Record<string, IMenuItem>, route) => {
|
||||
if (route.group && adminGroups[route.group]) {
|
||||
@ -159,7 +220,7 @@ export const AdminNavigationItems = ({
|
||||
<IconRenderer path={item.href} active={false} />
|
||||
}
|
||||
activeIcon={
|
||||
<IconRenderer path={item.href} active={true} />
|
||||
<IconRenderer path={item.href} active={false} />
|
||||
}
|
||||
isActiveMenu={Boolean(isActiveMenu)}
|
||||
key={item.text}
|
||||
@ -178,7 +239,11 @@ export const AdminNavigationItems = ({
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<StyledStopRoundedIcon />
|
||||
{isActiveItem(subItem.href) ? (
|
||||
<ActiveStyledStopRoundedIcon />
|
||||
) : (
|
||||
<StyledStopRoundedIcon />
|
||||
)}
|
||||
</AdminSubListItem>
|
||||
))}
|
||||
</MenuGroup>
|
||||
@ -197,7 +262,10 @@ export const AdminNavigationItems = ({
|
||||
) : null
|
||||
}
|
||||
>
|
||||
<IconRenderer path={item.href} active={false} />
|
||||
<IconRenderer
|
||||
path={item.href}
|
||||
active={isActiveItem(item.href)}
|
||||
/>
|
||||
</AdminListItem>
|
||||
);
|
||||
})}
|
||||
|
@ -18,9 +18,6 @@ import { NavigationSidebar } from './NavigationSidebar/NavigationSidebar';
|
||||
import { EventTimelineProvider } from 'component/events/EventTimeline/EventTimelineProvider';
|
||||
import { NewInUnleash } from './NavigationSidebar/NewInUnleash/NewInUnleash';
|
||||
|
||||
import { WrapIfAdminSubpage } from './AdminMenu/AdminMenu';
|
||||
import { useNewAdminMenu } from '../../../hooks/useNewAdminMenu';
|
||||
|
||||
interface IMainLayoutProps {
|
||||
children: ReactNode;
|
||||
}
|
||||
@ -34,6 +31,34 @@ const MainLayoutContainer = styled(Grid)(() => ({
|
||||
position: 'relative',
|
||||
}));
|
||||
|
||||
const MainLayoutContent = styled(Grid)(({ theme }) => ({
|
||||
minWidth: 0, // this is a fix for overflowing flex
|
||||
maxWidth: `1512px`,
|
||||
margin: '0 auto',
|
||||
paddingLeft: theme.spacing(2),
|
||||
paddingRight: theme.spacing(2),
|
||||
[theme.breakpoints.up(1856)]: {
|
||||
width: '100%',
|
||||
},
|
||||
[theme.breakpoints.down(1856)]: {
|
||||
marginLeft: theme.spacing(7),
|
||||
marginRight: theme.spacing(7),
|
||||
},
|
||||
[theme.breakpoints.down('lg')]: {
|
||||
maxWidth: `1250px`,
|
||||
paddingLeft: theme.spacing(1),
|
||||
paddingRight: theme.spacing(1),
|
||||
},
|
||||
[theme.breakpoints.down(1024)]: {
|
||||
marginLeft: 0,
|
||||
marginRight: 0,
|
||||
},
|
||||
[theme.breakpoints.down('sm')]: {
|
||||
minWidth: '100%',
|
||||
},
|
||||
minHeight: '94vh',
|
||||
}));
|
||||
|
||||
const MainLayoutContentWrapper = styled('div')(({ theme }) => ({
|
||||
margin: theme.spacing(0, 'auto'),
|
||||
flexGrow: 1,
|
||||
@ -65,7 +90,6 @@ const MainLayoutContentContainer = styled('main')(({ theme }) => ({
|
||||
|
||||
export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
({ children }, ref) => {
|
||||
const showOnlyAdminMenu = useNewAdminMenu();
|
||||
const { uiConfig } = useUiConfig();
|
||||
const projectId = useOptionalPathParam('projectId');
|
||||
const { isChangeRequestConfiguredInAnyEnv } = useChangeRequestsEnabled(
|
||||
@ -74,9 +98,6 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
|
||||
const showRegularNavigationSideBar =
|
||||
!isSmallScreen && !showOnlyAdminMenu;
|
||||
|
||||
return (
|
||||
<EventTimelineProvider>
|
||||
<SkipNavLink />
|
||||
@ -97,7 +118,7 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
})}
|
||||
>
|
||||
<ConditionallyRender
|
||||
condition={showRegularNavigationSideBar}
|
||||
condition={!isSmallScreen}
|
||||
show={
|
||||
<NavigationSidebar
|
||||
NewInUnleash={NewInUnleash}
|
||||
@ -115,14 +136,14 @@ export const MainLayout = forwardRef<HTMLDivElement, IMainLayoutProps>(
|
||||
>
|
||||
<Header />
|
||||
|
||||
<WrapIfAdminSubpage>
|
||||
<MainLayoutContent>
|
||||
<SkipNavTarget />
|
||||
<MainLayoutContentContainer ref={ref}>
|
||||
<BreadcrumbNav />
|
||||
<Proclamation toast={uiConfig.toast} />
|
||||
{children}
|
||||
</MainLayoutContentContainer>
|
||||
</WrapIfAdminSubpage>
|
||||
</MainLayoutContent>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
|
@ -60,6 +60,7 @@ const icons: Record<
|
||||
'/admin/roles/project-roles': RoleIcon,
|
||||
'/admin/api': ApiAccessIcon,
|
||||
'/admin/auth': SingleSignOnIcon,
|
||||
'/admin/auth/oidc': SingleSignOnIcon,
|
||||
'/admin/auth/saml': SingleSignOnIcon,
|
||||
'/admin/auth/scim': SingleSignOnIcon,
|
||||
'/admin/auth/password': SingleSignOnIcon,
|
||||
|
@ -22,6 +22,8 @@ import { ProjectIcon } from 'component/common/ProjectIcon/ProjectIcon';
|
||||
import useProjectOverview from 'hooks/api/getters/useProjectOverview/useProjectOverview';
|
||||
import { useShowBadge } from 'component/layout/components/EnterprisePlanBadge/useShowBadge';
|
||||
import { EnterprisePlanBadge } from 'component/layout/components/EnterprisePlanBadge/EnterprisePlanBadge';
|
||||
import { useNewAdminMenu } from 'hooks/useNewAdminMenu';
|
||||
import { AdminMenuNavigation } from '../AdminMenu/AdminNavigationItems';
|
||||
|
||||
export const SecondaryNavigationList: FC<{
|
||||
routes: INavigationMenuItem[];
|
||||
@ -235,10 +237,18 @@ export const AdminSettingsNavigation: FC<{
|
||||
activeItem,
|
||||
mode,
|
||||
}) => {
|
||||
const newAdminUIEnabled = useUiFlag('adminNavUI');
|
||||
const { showOnlyAdminMenu, newAdminUIEnabled } = useNewAdminMenu();
|
||||
if (showOnlyAdminMenu) {
|
||||
return <AdminMenuNavigation onClick={() => onClick('/admin')} />;
|
||||
}
|
||||
|
||||
const setFullModeOnClick = (activeItem: string) => {
|
||||
onSetFullMode();
|
||||
onClick(activeItem);
|
||||
};
|
||||
|
||||
if (newAdminUIEnabled) {
|
||||
return <AdminSettingsLink mode={mode} onClick={onClick} />;
|
||||
return <AdminSettingsLink mode={mode} onClick={setFullModeOnClick} />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
@ -31,6 +31,7 @@ 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<{
|
||||
onClick: () => void;
|
||||
@ -62,9 +63,12 @@ export const MobileNavigationSidebar: FC<{
|
||||
);
|
||||
};
|
||||
|
||||
export const StretchContainer = styled(Box)<{ mode: string }>(
|
||||
({ theme, mode }) => ({
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
export const StretchContainer = styled(Box)<{ mode: string; admin: boolean }>(
|
||||
({ theme, mode, admin }) => ({
|
||||
backgroundColor: admin
|
||||
? theme.palette.background.application
|
||||
: theme.palette.background.paper,
|
||||
borderRight: admin ? `2px solid ${theme.palette.divider}` : 'none',
|
||||
padding: theme.spacing(2),
|
||||
alignSelf: 'stretch',
|
||||
display: 'flex',
|
||||
@ -98,12 +102,14 @@ const StyledUnleashLogoOnlyWhite = styled(LogoOnlyWhite)(({ theme }) => ({
|
||||
}));
|
||||
|
||||
// This component is needed when the sticky item could overlap with nav items. You can replicate it on a short screen.
|
||||
const StickyContainer = styled(Box)(({ theme }) => ({
|
||||
const StickyContainer = styled(Box)<{ admin: boolean }>(({ theme, admin }) => ({
|
||||
position: 'sticky',
|
||||
paddingBottom: theme.spacing(1.5),
|
||||
paddingTop: theme.spacing(1),
|
||||
bottom: theme.spacing(0),
|
||||
backgroundColor: theme.palette.background.paper,
|
||||
backgroundColor: admin
|
||||
? theme.palette.background.application
|
||||
: theme.palette.background.paper,
|
||||
borderTop: `1px solid ${theme.palette.divider}`,
|
||||
}));
|
||||
|
||||
@ -112,6 +118,7 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
|
||||
}) => {
|
||||
const { routes } = useRoutes();
|
||||
const celebrateUnleashFrontend = useFlag('celebrateUnleashFrontend');
|
||||
const { showOnlyAdminMenu } = useNewAdminMenu();
|
||||
|
||||
const [mode, setMode] = useNavigationMode();
|
||||
const [expanded, changeExpanded] = useExpanded<'configure' | 'admin'>();
|
||||
@ -131,7 +138,7 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
|
||||
}, [initialPathname]);
|
||||
|
||||
return (
|
||||
<StretchContainer mode={mode}>
|
||||
<StretchContainer mode={mode} admin={showOnlyAdminMenu}>
|
||||
<ConditionallyRender
|
||||
condition={mode === 'full'}
|
||||
show={
|
||||
@ -168,72 +175,94 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({
|
||||
}
|
||||
/>
|
||||
|
||||
<PrimaryNavigationList
|
||||
mode={mode}
|
||||
onClick={setActiveItem}
|
||||
activeItem={activeItem}
|
||||
<ConditionallyRender
|
||||
condition={!showOnlyAdminMenu}
|
||||
show={
|
||||
<>
|
||||
<PrimaryNavigationList
|
||||
mode={mode}
|
||||
onClick={setActiveItem}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
<SecondaryNavigation
|
||||
expanded={expanded.includes('configure')}
|
||||
onExpandChange={(expand) => {
|
||||
changeExpanded('configure', expand);
|
||||
}}
|
||||
mode={mode}
|
||||
title='Configure'
|
||||
>
|
||||
<SecondaryNavigationList
|
||||
routes={routes.mainNavRoutes}
|
||||
mode={mode}
|
||||
onClick={setActiveItem}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
</SecondaryNavigation>
|
||||
|
||||
<AdminSettingsNavigation
|
||||
onClick={setActiveItem}
|
||||
mode={mode}
|
||||
onSetFullMode={() => setMode('full')}
|
||||
activeItem={activeItem}
|
||||
onExpandChange={(expand) => {
|
||||
changeExpanded('admin', expand);
|
||||
}}
|
||||
expanded={expanded.includes('admin')}
|
||||
routes={routes.adminRoutes}
|
||||
/>
|
||||
|
||||
{showRecentProject && (
|
||||
<RecentProjectsNavigation
|
||||
mode={mode}
|
||||
projectId={lastViewedProject}
|
||||
onClick={() => setActiveItem('/projects')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showRecentFlags && (
|
||||
<RecentFlagsNavigation
|
||||
mode={mode}
|
||||
flags={lastViewedFlags}
|
||||
onClick={() => setActiveItem('/projects')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* this will push the show/hide to the bottom on short nav list */}
|
||||
<Box sx={{ flex: 1 }} />
|
||||
|
||||
<StickyContainer admin={showOnlyAdminMenu}>
|
||||
{NewInUnleash ? (
|
||||
<NewInUnleash
|
||||
mode={mode}
|
||||
onMiniModeClick={() => setMode('full')}
|
||||
/>
|
||||
) : null}
|
||||
<ShowHide
|
||||
mode={mode}
|
||||
onChange={() => {
|
||||
setMode(mode === 'full' ? 'mini' : 'full');
|
||||
}}
|
||||
/>
|
||||
</StickyContainer>
|
||||
</>
|
||||
}
|
||||
elseShow={
|
||||
<>
|
||||
<AdminSettingsNavigation
|
||||
onClick={setActiveItem}
|
||||
mode={mode}
|
||||
onSetFullMode={() => setMode('full')}
|
||||
activeItem={activeItem}
|
||||
onExpandChange={(expand) => {
|
||||
changeExpanded('admin', expand);
|
||||
}}
|
||||
expanded={expanded.includes('admin')}
|
||||
routes={routes.adminRoutes}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
<SecondaryNavigation
|
||||
expanded={expanded.includes('configure')}
|
||||
onExpandChange={(expand) => {
|
||||
changeExpanded('configure', expand);
|
||||
}}
|
||||
mode={mode}
|
||||
title='Configure'
|
||||
>
|
||||
<SecondaryNavigationList
|
||||
routes={routes.mainNavRoutes}
|
||||
mode={mode}
|
||||
onClick={setActiveItem}
|
||||
activeItem={activeItem}
|
||||
/>
|
||||
</SecondaryNavigation>
|
||||
|
||||
<AdminSettingsNavigation
|
||||
onClick={setActiveItem}
|
||||
mode={mode}
|
||||
onSetFullMode={() => setMode('full')}
|
||||
activeItem={activeItem}
|
||||
onExpandChange={(expand) => {
|
||||
changeExpanded('admin', expand);
|
||||
}}
|
||||
expanded={expanded.includes('admin')}
|
||||
routes={routes.adminRoutes}
|
||||
/>
|
||||
|
||||
{showRecentProject && (
|
||||
<RecentProjectsNavigation
|
||||
mode={mode}
|
||||
projectId={lastViewedProject}
|
||||
onClick={() => setActiveItem('/projects')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{showRecentFlags && (
|
||||
<RecentFlagsNavigation
|
||||
mode={mode}
|
||||
flags={lastViewedFlags}
|
||||
onClick={() => setActiveItem('/projects')}
|
||||
/>
|
||||
)}
|
||||
|
||||
{/* this will push the show/hide to the bottom on short nav list */}
|
||||
<Box sx={{ flex: 1 }} />
|
||||
|
||||
<StickyContainer>
|
||||
{NewInUnleash ? (
|
||||
<NewInUnleash
|
||||
mode={mode}
|
||||
onMiniModeClick={() => setMode('full')}
|
||||
/>
|
||||
) : null}
|
||||
<ShowHide
|
||||
mode={mode}
|
||||
onChange={() => {
|
||||
setMode(mode === 'full' ? 'mini' : 'full');
|
||||
}}
|
||||
/>
|
||||
</StickyContainer>
|
||||
</StretchContainer>
|
||||
);
|
||||
};
|
||||
|
@ -33,7 +33,7 @@ export const DrawerMenu: VFC<IDrawerMenuProps> = ({
|
||||
open = false,
|
||||
toggleDrawer,
|
||||
}) => {
|
||||
const showOnlyAdminMenu = useNewAdminMenu();
|
||||
const { showOnlyAdminMenu } = useNewAdminMenu();
|
||||
const onClick = () => {
|
||||
toggleDrawer();
|
||||
};
|
||||
|
@ -1,7 +1,6 @@
|
||||
import { useState } from 'react';
|
||||
import useMediaQuery from '@mui/material/useMediaQuery';
|
||||
import { type Theme, useTheme } from '@mui/material/styles';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useTheme } from '@mui/material/styles';
|
||||
import {
|
||||
AppBar,
|
||||
Box,
|
||||
@ -14,12 +13,9 @@ import MenuIcon from '@mui/icons-material/Menu';
|
||||
import UserProfile from 'component/user/UserProfile';
|
||||
import { ConditionallyRender } from 'component/common/ConditionallyRender/ConditionallyRender';
|
||||
import MenuBookIcon from '@mui/icons-material/MenuBook';
|
||||
import { ReactComponent as UnleashLogo } from 'assets/img/logoDarkWithText.svg';
|
||||
import { ReactComponent as UnleashLogoWhite } from 'assets/img/logoWithWhiteText.svg';
|
||||
|
||||
import { DrawerMenu } from './DrawerMenu/DrawerMenu';
|
||||
import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig';
|
||||
import { focusable } from 'themes/themeStyles';
|
||||
import DarkModeOutlined from '@mui/icons-material/DarkModeOutlined';
|
||||
import LightModeOutlined from '@mui/icons-material/LightModeOutlined';
|
||||
import { useThemeMode } from 'hooks/useThemeMode';
|
||||
@ -27,10 +23,6 @@ import { Notifications } from 'component/common/Notifications/Notifications';
|
||||
import InviteLinkButton from './InviteLink/InviteLinkButton/InviteLinkButton';
|
||||
import { useUiFlag } from 'hooks/useUiFlag';
|
||||
import { CommandBar } from 'component/commandBar/CommandBar';
|
||||
import { ThemeMode } from 'component/common/ThemeMode/ThemeMode';
|
||||
import { ReactComponent as CelebratoryUnleashLogo } from 'assets/img/unleashHoliday.svg';
|
||||
import { ReactComponent as CelebratoryUnleashLogoWhite } from 'assets/img/unleashHolidayDark.svg';
|
||||
import { useNewAdminMenu } from 'hooks/useNewAdminMenu';
|
||||
|
||||
const HeaderComponent = styled(AppBar)(({ theme }) => ({
|
||||
backgroundColor: theme.palette.background.application,
|
||||
@ -71,16 +63,6 @@ const StyledNav = styled('nav')({
|
||||
flexGrow: 1,
|
||||
});
|
||||
|
||||
const StyledCelebratoryLogo = styled(CelebratoryUnleashLogo)({
|
||||
height: '50px',
|
||||
});
|
||||
|
||||
const StyledUnleashLogoWhite = styled(UnleashLogoWhite)({ height: '50px' });
|
||||
|
||||
const StyledUnleashLogo = styled(UnleashLogo)({ height: '50px' });
|
||||
|
||||
const StyledLink = styled(Link)(({ theme }) => focusable(theme));
|
||||
|
||||
const StyledIconButton = styled(IconButton)<{
|
||||
component?: 'a' | 'button';
|
||||
href?: string;
|
||||
@ -100,16 +82,10 @@ const Header = () => {
|
||||
const theme = useTheme();
|
||||
|
||||
const disableNotifications = useUiFlag('disableNotifications');
|
||||
const { uiConfig, isOss } = useUiConfig();
|
||||
const { isOss } = useUiConfig();
|
||||
const smallScreen = useMediaQuery(theme.breakpoints.down('lg'));
|
||||
const [openDrawer, setOpenDrawer] = useState(false);
|
||||
const toggleDrawer = () => setOpenDrawer((prev) => !prev);
|
||||
const celebratoryUnleash = useUiFlag('celebrateUnleash');
|
||||
const headerLogo = (theme: Theme) => ({
|
||||
height: '50px',
|
||||
marginLeft: theme.spacing(1.5),
|
||||
});
|
||||
const adminMenu = useNewAdminMenu();
|
||||
|
||||
if (smallScreen) {
|
||||
return (
|
||||
@ -141,40 +117,6 @@ const Header = () => {
|
||||
<HeaderComponent position='static'>
|
||||
<ContainerComponent>
|
||||
<StyledNav>
|
||||
<ConditionallyRender
|
||||
condition={adminMenu}
|
||||
show={
|
||||
<StyledLink
|
||||
to='/personal'
|
||||
sx={headerLogo}
|
||||
aria-label='Home'
|
||||
>
|
||||
<ThemeMode
|
||||
darkmode={
|
||||
<ConditionallyRender
|
||||
condition={celebratoryUnleash}
|
||||
show={
|
||||
<CelebratoryUnleashLogoWhite />
|
||||
}
|
||||
elseShow={
|
||||
<StyledUnleashLogoWhite aria-label='Unleash logo' />
|
||||
}
|
||||
/>
|
||||
}
|
||||
lightmode={
|
||||
<ConditionallyRender
|
||||
condition={celebratoryUnleash}
|
||||
show={<StyledCelebratoryLogo />}
|
||||
elseShow={
|
||||
<StyledUnleashLogo aria-label='Unleash logo' />
|
||||
}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</StyledLink>
|
||||
}
|
||||
/>
|
||||
|
||||
<StyledUserContainer>
|
||||
<CommandBar />
|
||||
<Divider
|
||||
|
@ -4,9 +4,11 @@ import { useLocation } from 'react-router';
|
||||
export const useNewAdminMenu = () => {
|
||||
const newAdminUIEnabled = useUiFlag('adminNavUI');
|
||||
const location = useLocation();
|
||||
return (
|
||||
newAdminUIEnabled &&
|
||||
(location.pathname.indexOf('/admin') === 0 ||
|
||||
location.pathname.indexOf('/history') === 0)
|
||||
);
|
||||
return {
|
||||
showOnlyAdminMenu:
|
||||
newAdminUIEnabled &&
|
||||
(location.pathname.indexOf('/admin') === 0 ||
|
||||
location.pathname.indexOf('/history') === 0),
|
||||
newAdminUIEnabled,
|
||||
};
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user