diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/IconRenderer.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/IconRenderer.tsx index 27a59e9371..bec601a2af 100644 --- a/frontend/src/component/layout/MainLayout/NavigationSidebar/IconRenderer.tsx +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/IconRenderer.tsx @@ -35,6 +35,7 @@ import { ProjectIcon } from 'component/common/ProjectIcon/ProjectIcon'; import PlaygroundIcon from '@mui/icons-material/AutoFixNormal'; import FlagOutlinedIcon from '@mui/icons-material/FlagOutlined'; import RocketLaunchIcon from '@mui/icons-material/RocketLaunchOutlined'; +import BuildIcon from '@mui/icons-material/Build'; // TODO: move to routes const icons: Record< @@ -86,6 +87,7 @@ const icons: Record< '/custom-metrics': RocketLaunchIcon, GitHub: GitHubIcon, Documentation: LibraryBooksIcon, + Configure: BuildIcon, }; export const IconRenderer: FC<{ path: string }> = ({ path }) => { diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/MobileNavigationSidebar.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/MobileNavigationSidebar.tsx new file mode 100644 index 0000000000..7e1fcbc159 --- /dev/null +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/MobileNavigationSidebar.tsx @@ -0,0 +1,30 @@ +import type { FC } from 'react'; +import { + PrimaryNavigationList, + AdminSettingsLink, + OtherLinksList, +} from './NavigationList.tsx'; +import type { NewInUnleash } from './NewInUnleash/NewInUnleash.tsx'; +import { SecondaryNavigationList } from './SecondaryNavigationList.tsx'; +import { useRoutes } from './useRoutes.ts'; + +export const MobileNavigationSidebar: FC<{ + onClick: () => void; + NewInUnleash?: typeof NewInUnleash; +}> = ({ onClick, NewInUnleash }) => { + const { routes } = useRoutes(); + + return ( + <> + {NewInUnleash ? : null} + + + + + + ); +}; diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationList.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationList.tsx index 19cf17d78b..6dacf4358a 100644 --- a/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationList.tsx +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationList.tsx @@ -1,4 +1,3 @@ -import type React from 'react'; import type { FC } from 'react'; import type { INavigationMenuItem } from 'interfaces/route'; import type { NavigationMode } from './NavigationMode.tsx'; @@ -11,48 +10,12 @@ import { import { Box, List, Typography } from '@mui/material'; import { IconRenderer } from './IconRenderer.tsx'; import useUiConfig from 'hooks/api/getters/useUiConfig/useUiConfig'; -import Accordion from '@mui/material/Accordion'; -import AccordionDetails from '@mui/material/AccordionDetails'; -import AccordionSummary from '@mui/material/AccordionSummary'; -import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; import FlagIcon from '@mui/icons-material/OutlinedFlag'; 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.tsx'; - -export const SecondaryNavigationList: FC<{ - routes: INavigationMenuItem[]; - mode: NavigationMode; - onClick: (activeItem: string) => void; - activeItem?: string; -}> = ({ routes, mode, onClick, activeItem }) => { - const showBadge = useShowBadge(); - const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem; - - return ( - - {routes.map((route) => ( - onClick(route.path)} - href={route.path} - text={route.title} - selected={activeItem === route.path} - badge={ - showBadge(route?.menu?.mode) ? ( - - ) : null - } - > - - - ))} - - ); -}; +import { useUiFlag } from 'hooks/useUiFlag.ts'; export const OtherLinksList = () => { const { uiConfig } = useUiConfig(); @@ -126,6 +89,7 @@ export const PrimaryNavigationList: FC<{ }> = ({ mode, onClick, activeItem }) => { const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem; const { isOss } = useUiConfig(); + const sideMenuCleanup = useUiFlag('sideMenuCleanup'); return ( @@ -165,7 +129,7 @@ export const PrimaryNavigationList: FC<{ {!isOss() ? ( onClick('/insights')} selected={activeItem === '/insights'} > @@ -176,47 +140,6 @@ export const PrimaryNavigationList: FC<{ ); }; -const AccordionHeader: FC<{ children?: React.ReactNode }> = ({ children }) => { - return ( - } - aria-controls='configure-content' - id='configure-header' - > - - {children} - - - ); -}; - -export const SecondaryNavigation: FC<{ - expanded: boolean; - onExpandChange: (expanded: boolean) => void; - mode: NavigationMode; - title: string; - children?: React.ReactNode; -}> = ({ mode, expanded, onExpandChange, title, children }) => { - return ( - { - onExpandChange(expand); - }} - > - {mode === 'full' && {title}} - {children} - - ); -}; - export const AdminSettingsNavigation: FC<{ onClick: (activeItem: string) => void; onSetFullMode: () => void; diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationSidebar.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationSidebar.tsx index 0094a1dbc5..6d25fababb 100644 --- a/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationSidebar.tsx +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/NavigationSidebar.tsx @@ -5,15 +5,13 @@ import { ShowHide } from './ShowHide.tsx'; import { useRoutes } from './useRoutes.ts'; import { useExpanded } from './useExpanded.ts'; import { - OtherLinksList, PrimaryNavigationList, RecentFlagsNavigation, RecentProjectsNavigation, - SecondaryNavigation, - SecondaryNavigationList, AdminSettingsNavigation, - AdminSettingsLink, } from './NavigationList.tsx'; +import { SecondaryNavigationList } from './SecondaryNavigationList.tsx'; +import { SecondaryNavigation } from './SecondaryNavigation.tsx'; import { FullListItem, MiniListItem } from './ListItems.tsx'; import { useInitialPathname } from './useInitialPathname.ts'; import { useLastViewedProject } from 'hooks/useLastViewedProject'; @@ -31,27 +29,7 @@ import { ReactComponent as LogoOnly } from 'assets/img/logoDark.svg'; import { Link } from 'react-router-dom'; import { useFlag } from '@unleash/proxy-client-react'; import { useNewAdminMenu } from 'hooks/useNewAdminMenu'; - -export const MobileNavigationSidebar: FC<{ - onClick: () => void; - NewInUnleash?: typeof NewInUnleash; -}> = ({ onClick, NewInUnleash }) => { - const { routes } = useRoutes(); - - return ( - <> - {NewInUnleash ? : null} - - - - - - ); -}; +import { useUiFlag } from 'hooks/useUiFlag.ts'; export const StretchContainer = styled(Box, { shouldForwardProp: (propName) => @@ -118,12 +96,15 @@ export const NavigationSidebar: FC<{ NewInUnleash?: typeof NewInUnleash }> = ({ const initialPathname = useInitialPathname(); const [activeItem, setActiveItem] = useState(initialPathname); + const sideMenuCleanup = useUiFlag('sideMenuCleanup'); const { lastViewed: lastViewedProject } = useLastViewedProject(); - const showRecentProject = mode === 'full' && lastViewedProject; + const showRecentProject = + !sideMenuCleanup && mode === 'full' && lastViewedProject; const { lastViewed: lastViewedFlags } = useLastViewedFlags(); - const showRecentFlags = mode === 'full' && lastViewedFlags.length > 0; + const showRecentFlags = + !sideMenuCleanup && mode === 'full' && lastViewedFlags.length > 0; const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem; useEffect(() => { diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/SecondaryNavigation.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/SecondaryNavigation.tsx new file mode 100644 index 0000000000..968229e570 --- /dev/null +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/SecondaryNavigation.tsx @@ -0,0 +1,49 @@ +import type React from 'react'; +import type { FC } from 'react'; +import type { NavigationMode } from './NavigationMode.tsx'; +import { Typography } from '@mui/material'; +import Accordion from '@mui/material/Accordion'; +import AccordionDetails from '@mui/material/AccordionDetails'; +import AccordionSummary from '@mui/material/AccordionSummary'; +import ExpandMoreIcon from '@mui/icons-material/ExpandMore'; + +const AccordionHeader: FC<{ children?: React.ReactNode }> = ({ children }) => { + return ( + } + aria-controls='configure-content' + id='configure-header' + > + + {children} + + + ); +}; + +export const SecondaryNavigation: FC<{ + expanded: boolean; + onExpandChange: (expanded: boolean) => void; + mode: NavigationMode; + title: string; + children?: React.ReactNode; +}> = ({ mode, expanded, onExpandChange, title, children }) => { + return ( + { + onExpandChange(expand); + }} + > + {mode === 'full' && {title}} + {children} + + ); +}; diff --git a/frontend/src/component/layout/MainLayout/NavigationSidebar/SecondaryNavigationList.tsx b/frontend/src/component/layout/MainLayout/NavigationSidebar/SecondaryNavigationList.tsx new file mode 100644 index 0000000000..ac2a433adf --- /dev/null +++ b/frontend/src/component/layout/MainLayout/NavigationSidebar/SecondaryNavigationList.tsx @@ -0,0 +1,46 @@ +import type { FC } from 'react'; +import type { INavigationMenuItem } from 'interfaces/route'; +import type { NavigationMode } from './NavigationMode.tsx'; +import { FullListItem, MiniListItem } from './ListItems.tsx'; +import { List } from '@mui/material'; +import { IconRenderer } from './IconRenderer.tsx'; +import { useUiFlag } from 'hooks/useUiFlag.ts'; +import StopRoundedIcon from '@mui/icons-material/StopRounded'; +import { useShowBadge } from 'component/layout/components/EnterprisePlanBadge/useShowBadge'; +import { EnterprisePlanBadge } from 'component/layout/components/EnterprisePlanBadge/EnterprisePlanBadge'; + +export const SecondaryNavigationList: FC<{ + routes: INavigationMenuItem[]; + mode: NavigationMode; + onClick: (activeItem: string) => void; + activeItem?: string; +}> = ({ routes, mode, onClick, activeItem }) => { + const showBadge = useShowBadge(); + const DynamicListItem = mode === 'mini' ? MiniListItem : FullListItem; + const sideMenuCleanup = useUiFlag('sideMenuCleanup'); + + return ( + + {routes.map((route) => ( + onClick(route.path)} + href={route.path} + text={route.title} + selected={activeItem === route.path} + badge={ + showBadge(route?.menu?.mode) ? ( + + ) : null + } + > + {sideMenuCleanup ? ( + + ) : ( + + )} + + ))} + + ); +}; diff --git a/frontend/src/component/menu/Header/DrawerMenu/DrawerMenu.tsx b/frontend/src/component/menu/Header/DrawerMenu/DrawerMenu.tsx index 90407aa760..cfcb6a474f 100644 --- a/frontend/src/component/menu/Header/DrawerMenu/DrawerMenu.tsx +++ b/frontend/src/component/menu/Header/DrawerMenu/DrawerMenu.tsx @@ -6,7 +6,7 @@ import { ReactComponent as UnleashLogoWhite } from 'assets/img/logoWithWhiteText import styles from './DrawerMenu.module.scss'; // FIXME: useStyle - theme import theme from 'themes/theme'; import { ThemeMode } from 'component/common/ThemeMode/ThemeMode'; -import { MobileNavigationSidebar } from 'component/layout/MainLayout/NavigationSidebar/NavigationSidebar'; +import { MobileNavigationSidebar } from 'component/layout/MainLayout/NavigationSidebar/MobileNavigationSidebar'; import { NewInUnleash } from 'component/layout/MainLayout/NavigationSidebar/NewInUnleash/NewInUnleash'; import { AdminMobileNavigation } from 'component/layout/MainLayout/AdminMenu/AdminNavigationItems'; import { useNewAdminMenu } from 'hooks/useNewAdminMenu'; diff --git a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap index eeccfd38c5..50e27cca8d 100644 --- a/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap +++ b/frontend/src/component/menu/__tests__/__snapshots__/routes.test.tsx.snap @@ -131,10 +131,22 @@ exports[`returns all baseRoutes 1`] = ` "menu": { "primary": true, }, + "notFlag": "sideMenuCleanup", "path": "/insights", "title": "Insights", "type": "protected", }, + { + "component": [Function], + "enterprise": true, + "flag": "sideMenuCleanup", + "menu": { + "primary": true, + }, + "path": "/insights", + "title": "Analytics", + "type": "protected", + }, { "component": [Function], "menu": {}, diff --git a/frontend/src/component/menu/routes.ts b/frontend/src/component/menu/routes.ts index a1a8f9b51e..01af7b4d43 100644 --- a/frontend/src/component/menu/routes.ts +++ b/frontend/src/component/menu/routes.ts @@ -157,6 +157,16 @@ export const routes: IRoute[] = [ type: 'protected', menu: { primary: true }, enterprise: true, + notFlag: 'sideMenuCleanup', + }, + { + path: '/insights', + title: 'Analytics', + component: Insights, + type: 'protected', + menu: { primary: true }, + enterprise: true, + flag: 'sideMenuCleanup', }, // Applications diff --git a/frontend/src/interfaces/uiConfig.ts b/frontend/src/interfaces/uiConfig.ts index d1df42082d..9c3db9bd18 100644 --- a/frontend/src/interfaces/uiConfig.ts +++ b/frontend/src/interfaces/uiConfig.ts @@ -93,6 +93,7 @@ export type UiFlags = { projectLinkTemplates?: boolean; customMetrics?: boolean; lifecycleMetrics?: boolean; + sideMenuCleanup?: boolean; }; export interface IVersionInfo { diff --git a/src/lib/types/experimental.ts b/src/lib/types/experimental.ts index 948f809a2e..98006fb575 100644 --- a/src/lib/types/experimental.ts +++ b/src/lib/types/experimental.ts @@ -66,7 +66,8 @@ export type IFlagKey = | 'lastSeenBulkQuery' | 'newGettingStartedEmail' | 'lifecycleMetrics' - | 'customMetrics'; + | 'customMetrics' + | 'sideMenuCleanup'; export type IFlags = Partial<{ [key in IFlagKey]: boolean | Variant }>; @@ -312,6 +313,10 @@ const flags: IFlags = { process.env.UNLEASH_EXPERIMENTAL_LIFECYCLE_METRICS, false, ), + sideMenuCleanup: parseEnvVarBoolean( + process.env.UNLEASH_EXPERIMENTAL_SIDE_MENU_CLEANUP, + false, + ), }; export const defaultExperimentalOptions: IExperimentalOptions = { diff --git a/src/server-dev.ts b/src/server-dev.ts index 039178e7d8..1d04fd9200 100644 --- a/src/server-dev.ts +++ b/src/server-dev.ts @@ -59,6 +59,7 @@ process.nextTick(async () => { reportUnknownFlags: true, customMetrics: true, lifecycleMetrics: true, + sideMenuCleanup: true, }, }, authentication: {