diff --git a/frontend/src/container/AppLayout/index.tsx b/frontend/src/container/AppLayout/index.tsx index 7574e59143..6546271c7f 100644 --- a/frontend/src/container/AppLayout/index.tsx +++ b/frontend/src/container/AppLayout/index.tsx @@ -25,7 +25,7 @@ import { } from 'types/actions/app'; import AppReducer from 'types/reducer/app'; -import { ChildrenContainer, Layout } from './styles'; +import { ChildrenContainer, Layout, LayoutContent } from './styles'; import { getRouteKey } from './utils'; function AppLayout(props: AppLayoutProps): JSX.Element { @@ -240,12 +240,12 @@ function AppLayout(props: AppLayoutProps): JSX.Element { {isToDisplayLayout &&
} {isToDisplayLayout && } - + {isToDisplayLayout && } {children} - + ); diff --git a/frontend/src/container/AppLayout/styles.ts b/frontend/src/container/AppLayout/styles.ts index 1f48b54033..d3a030bf1f 100644 --- a/frontend/src/container/AppLayout/styles.ts +++ b/frontend/src/container/AppLayout/styles.ts @@ -7,9 +7,14 @@ export const Layout = styled(LayoutComponent)` position: relative; min-height: calc(100vh - 4rem); overflow: hidden; + height: 100%; } `; +export const LayoutContent = styled(LayoutComponent.Content)` + overflow-y: auto; +`; + export const ChildrenContainer = styled.div` margin: 0 1rem; display: flex; diff --git a/frontend/src/container/SideNav/index.tsx b/frontend/src/container/SideNav/SideNav.tsx similarity index 59% rename from frontend/src/container/SideNav/index.tsx rename to frontend/src/container/SideNav/SideNav.tsx index 0a4942f115..4271b47690 100644 --- a/frontend/src/container/SideNav/index.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -1,33 +1,30 @@ import { CheckCircleTwoTone, WarningOutlined } from '@ant-design/icons'; -import { Menu, MenuProps } from 'antd'; +import { MenuProps } from 'antd'; import getLocalStorageKey from 'api/browser/localstorage/get'; import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; import ROUTES from 'constants/routes'; import history from 'lib/history'; -import { - ReactNode, - useCallback, - useLayoutEffect, - useMemo, - useState, -} from 'react'; +import { useCallback, useLayoutEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; import { useLocation } from 'react-router-dom'; -import { SideBarCollapse } from 'store/actions/app'; +import { sideBarCollapse } from 'store/actions/app'; import { AppState } from 'store/reducers'; import AppReducer from 'types/reducer/app'; import { routeConfig, styles } from './config'; import { getQueryString } from './helper'; -import menus from './menuItems'; +import menuItems from './menuItems'; +import { MenuItem, SecondaryMenuItemKey } from './sideNav.types'; +import { getActiveMenuKeyFromPath } from './sideNav.utils'; import Slack from './Slack'; import { + MenuLabelContainer, RedDot, Sider, - SlackButton, - SlackMenuItemContainer, - VersionContainer, + StyledPrimaryMenu, + StyledSecondaryMenu, + StyledText, } from './styles'; function SideNav(): JSX.Element { @@ -49,7 +46,7 @@ function SideNav(): JSX.Element { }, []); useLayoutEffect(() => { - dispatch(SideBarCollapse(collapsed)); + dispatch(sideBarCollapse(collapsed)); }, [collapsed, dispatch]); const onClickHandler = useCallback( @@ -80,90 +77,56 @@ function SideNav(): JSX.Element { const isNotCurrentVersion = currentVersion !== latestVersion; - const sidebar: SidebarItem[] = [ + const secondaryMenuItems: MenuItem[] = [ { - onClick: onClickSlackHandler, - icon: , - text: Support, - key: 'slack', - }, - { - onClick: onClickVersionHandler, - key: 'version', + key: SecondaryMenuItemKey.Version, icon: isNotCurrentVersion ? ( ) : ( ), - text: ( - - {!isCurrentVersionError ? ( - {currentVersion} - ) : ( - {t('n_a')} - )} + label: ( + + + {!isCurrentVersionError ? currentVersion : t('n_a')} + {isNotCurrentVersion && } - + ), + onClick: onClickVersionHandler, + }, + { + key: SecondaryMenuItemKey.Slack, + icon: , + label: Support, + onClick: onClickSlackHandler, }, ]; - const currentMenu = useMemo(() => { - const routeKeys = Object.keys(ROUTES) as (keyof typeof ROUTES)[]; - const currentRouteKey = routeKeys.find((key) => { - const route = ROUTES[key]; - return pathname === route; - }); - - if (!currentRouteKey) return null; - - return ROUTES[currentRouteKey]; - }, [pathname]); - - const sidebarItems = (props: SidebarItem, index: number): SidebarItem => ({ - key: `${index}`, - icon: props.icon, - onClick: props.onClick, - label: props.text, - }); + const activeMenuKey = useMemo(() => getActiveMenuKeyFromPath(pathname), [ + pathname, + ]); return ( - - {sidebar.map((props, index) => ( - - - - ))} + ); } -interface SidebarItem { - onClick: VoidFunction; - icon?: ReactNode; - text?: ReactNode; - key: string; - label?: ReactNode; -} - export default SideNav; diff --git a/frontend/src/container/SideNav/Slack.tsx b/frontend/src/container/SideNav/Slack.tsx index 118ac924c1..ed9992cdf3 100644 --- a/frontend/src/container/SideNav/Slack.tsx +++ b/frontend/src/container/SideNav/Slack.tsx @@ -1,6 +1,6 @@ interface ISlackProps { - width?: number; - height?: number; + width?: number | string; + height?: number | string; } function Slack({ width, height }: ISlackProps): JSX.Element { return ( @@ -47,8 +47,8 @@ function Slack({ width, height }: ISlackProps): JSX.Element { ); } Slack.defaultProps = { - width: 28, - height: 28, + width: '1em', + height: '1em', }; export default Slack; diff --git a/frontend/src/container/SideNav/index.ts b/frontend/src/container/SideNav/index.ts new file mode 100644 index 0000000000..919d1a693f --- /dev/null +++ b/frontend/src/container/SideNav/index.ts @@ -0,0 +1,3 @@ +import SideNav from './SideNav'; + +export default SideNav; diff --git a/frontend/src/container/SideNav/menuItems.tsx b/frontend/src/container/SideNav/menuItems.tsx index b7348e3d50..3591923cc4 100644 --- a/frontend/src/container/SideNav/menuItems.tsx +++ b/frontend/src/container/SideNav/menuItems.tsx @@ -10,28 +10,11 @@ import { MenuOutlined, SettingOutlined, } from '@ant-design/icons'; -import { MenuProps, Space, Typography } from 'antd'; import ROUTES from 'constants/routes'; -import { Tags } from './styles'; +import { SidebarMenu } from './sideNav.types'; -type MenuItem = Required['items'][number]; - -export const createLabelWithTags = ( - label: string, - tags: string[], -): JSX.Element => ( - -
{label}
- {tags.map((tag) => ( - - {tag} - - ))} -
-); - -const menus: SidebarMenu[] = [ +const menuItems: SidebarMenu[] = [ { key: ROUTES.APPLICATION, label: 'Services', @@ -111,8 +94,11 @@ const menus: SidebarMenu[] = [ }, ]; -type SidebarMenu = MenuItem & { - tags?: string[]; +/** Mapping of some newly added routes and their corresponding active sidebar menu key */ +export const NEW_ROUTES_MENU_ITEM_KEY_MAP = { + [ROUTES.TRACES_EXPLORER]: ROUTES.TRACE, + [ROUTES.TRACE_EXPLORER]: ROUTES.TRACE, + [ROUTES.LOGS_EXPLORER]: ROUTES.LOGS, }; -export default menus; +export default menuItems; diff --git a/frontend/src/container/SideNav/sideNav.types.ts b/frontend/src/container/SideNav/sideNav.types.ts new file mode 100644 index 0000000000..d67862e51c --- /dev/null +++ b/frontend/src/container/SideNav/sideNav.types.ts @@ -0,0 +1,21 @@ +import { MenuProps } from 'antd'; +import { ReactNode } from 'react'; + +export type MenuItem = Required['items'][number]; + +export type SidebarMenu = MenuItem & { + tags?: string[]; +}; + +export interface SidebarItem { + onClick: VoidFunction; + icon?: ReactNode; + text?: ReactNode; + key: string; + label?: ReactNode; +} + +export enum SecondaryMenuItemKey { + Slack = 'slack', + Version = 'version', +} diff --git a/frontend/src/container/SideNav/sideNav.utils.ts b/frontend/src/container/SideNav/sideNav.utils.ts new file mode 100644 index 0000000000..3c455ddab2 --- /dev/null +++ b/frontend/src/container/SideNav/sideNav.utils.ts @@ -0,0 +1,11 @@ +import { NEW_ROUTES_MENU_ITEM_KEY_MAP } from './menuItems'; + +export const getActiveMenuKeyFromPath = (pathname: string): string => { + const basePath = pathname?.split('/')?.[1]; // Get the base path, Eg; /dashboard/dc5beb63-589c-46a3-ad4c-1b9ca248ee33 -> dashboard + + if (!basePath) return ''; + + const baseRoute = `/${basePath}`; + + return NEW_ROUTES_MENU_ITEM_KEY_MAP[baseRoute] || baseRoute; +}; diff --git a/frontend/src/container/SideNav/styles.ts b/frontend/src/container/SideNav/styles.ts index 9cdca6065c..35dcc2ed38 100644 --- a/frontend/src/container/SideNav/styles.ts +++ b/frontend/src/container/SideNav/styles.ts @@ -1,87 +1,68 @@ -import { Layout, Tag, Typography } from 'antd'; -import { StyledCSS } from 'container/GantChart/Trace/styles'; -import styled, { css } from 'styled-components'; +import { Layout, Menu, Typography } from 'antd'; +import styled from 'styled-components'; const { Sider: SiderComponent } = Layout; -interface LogoProps { - collapsed: boolean; - index: number; -} - export const Sider = styled(SiderComponent)` &&& { background: #1f1f1f; + .ant-layout-sider-children { + display: flex; + flex-direction: column; + } + .ant-layout-sider-trigger { background: #1f1f1f; } } `; -export const SlackButton = styled(Typography)` - &&& { - margin-left: 1rem; - color: white; - } +export const StyledPrimaryMenu = styled(Menu)` + flex: 1; + overflow-y: auto; `; -export const SlackMenuItemContainer = styled.div` - position: fixed; - bottom: ${({ index }): string => `${index * 48 + (index + 16)}px`}; - background: #262626; - width: ${({ collapsed }): string => (!collapsed ? '200px' : '80px')}; - transition: inherit; - background: #1f1f1f; - +export const StyledSecondaryMenu = styled(Menu)` &&& { - li { - ${({ collapsed }): StyledCSS => - collapsed && - css` - padding-left: 24px; - padding-top: 6px; - `} + :not(.ant-menu-inline-collapsed) > .ant-menu-item { + padding-inline: 48px; + + display: flex; + align-items: center; + justify-content: center; } - svg { - margin-left: ${({ collapsed }): string => (collapsed ? '0' : '24px')}; - width: 28px; - height: 28px; - - ${({ collapsed }): StyledCSS => - collapsed && - css` - height: 100%; - margin: 0 auto; - `} - } .ant-menu-title-content { - margin: 0; + margin-inline-start: 10px; + width: 100%; + } + + &.ant-menu-inline-collapsed .ant-menu-title-content { + opacity: 0; } } `; export const RedDot = styled.div` - width: 12px; - height: 12px; + width: 10px; + height: 10px; background: #d32029; border-radius: 50%; margin-left: 0.5rem; - margin-top: 0.5rem; `; -export const VersionContainer = styled.div` - &&& { - display: flex; - } +export const MenuLabelContainer = styled.div` + display: inline-flex; + align-items: center; + justify-content: center; + + width: 100%; `; -export const Tags = styled(Tag)` - &&& { - position: absolute; - top: 0; - border-radius: 0.5rem; - } +export const StyledText = styled(Typography.Text)` + width: 100%; + + color: white; `; diff --git a/frontend/src/container/TopNav/styles.ts b/frontend/src/container/TopNav/styles.ts index feda027d24..ef3cb15c37 100644 --- a/frontend/src/container/TopNav/styles.ts +++ b/frontend/src/container/TopNav/styles.ts @@ -4,6 +4,5 @@ import styled from 'styled-components'; export const Container = styled(Row)` &&& { margin-top: 2rem; - min-height: 8vh; } `; diff --git a/frontend/src/globalStyles.ts b/frontend/src/globalStyles.ts new file mode 100644 index 0000000000..86dc8258ed --- /dev/null +++ b/frontend/src/globalStyles.ts @@ -0,0 +1,18 @@ +import { createGlobalStyle } from 'styled-components'; + +const GlobalStyles = createGlobalStyle` +#root, +html, +body { + height: 100%; + overflow: hidden; +} + +body { + padding: 0; + margin: 0; + box-sizing: border-box; +} +`; + +export default GlobalStyles; diff --git a/frontend/src/index.html.ejs b/frontend/src/index.html.ejs index 6d7c037e1f..0e198e2cf3 100644 --- a/frontend/src/index.html.ejs +++ b/frontend/src/index.html.ejs @@ -57,7 +57,7 @@ rel="stylesheet" /> - +
diff --git a/frontend/src/index.tsx b/frontend/src/index.tsx index 049c0a4c0e..fd2d4c7b0e 100644 --- a/frontend/src/index.tsx +++ b/frontend/src/index.tsx @@ -2,6 +2,7 @@ import './wdyr'; import './ReactI18'; import AppRoutes from 'AppRoutes'; +import GlobalStyles from 'globalStyles'; import { ThemeProvider } from 'hooks/useDarkMode'; import { createRoot } from 'react-dom/client'; import { HelmetProvider } from 'react-helmet-async'; @@ -33,6 +34,7 @@ if (container) { + {process.env.NODE_ENV === 'development' && ( diff --git a/frontend/src/store/actions/app/sideBarCollapse.ts b/frontend/src/store/actions/app/sideBarCollapse.ts index dc2fde6108..c99d79b6fb 100644 --- a/frontend/src/store/actions/app/sideBarCollapse.ts +++ b/frontend/src/store/actions/app/sideBarCollapse.ts @@ -3,7 +3,7 @@ import { IS_SIDEBAR_COLLAPSED } from 'constants/app'; import { Dispatch } from 'redux'; import AppActions from 'types/actions'; -export const SideBarCollapse = ( +export const sideBarCollapse = ( collapseState: boolean, ): ((dispatch: Dispatch) => void) => { setLocalStorageKey(IS_SIDEBAR_COLLAPSED, `${collapseState}`);