diff --git a/frontend/src/container/AppLayout/AppLayout.styles.scss b/frontend/src/container/AppLayout/AppLayout.styles.scss index a34929618b..220268d010 100644 --- a/frontend/src/container/AppLayout/AppLayout.styles.scss +++ b/frontend/src/container/AppLayout/AppLayout.styles.scss @@ -48,7 +48,7 @@ } .app-content { - width: calc(100% - 64px); // width of the sidebar + width: calc(100% - 54px); // width of the sidebar z-index: 0; margin: 0 auto; diff --git a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/entityEvents.styles.scss b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/entityEvents.styles.scss index 0feef33b0f..60fe9c3efa 100644 --- a/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/entityEvents.styles.scss +++ b/frontend/src/container/InfraMonitoringK8s/EntityDetailsUtils/EntityEvents/entityEvents.styles.scss @@ -168,7 +168,7 @@ .ant-pagination { position: fixed; bottom: 0; - width: calc(100% - 64px); + width: calc(100% - 54px); background: rgb(18, 19, 23); padding: 16px; margin: 0; diff --git a/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.styles.scss b/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.styles.scss index 4587023d9e..4cd55323d5 100644 --- a/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.styles.scss +++ b/frontend/src/container/InfraMonitoringK8s/InfraMonitoringK8s.styles.scss @@ -442,7 +442,7 @@ .ant-pagination { position: fixed; bottom: 0; - width: calc(100% - 64px); + width: calc(100% - 54px); background: var(--bg-ink-500); padding: 16px; margin: 0; diff --git a/frontend/src/container/ListOfDashboard/DashboardTemplates/DashboardTemplatesModal.styles.scss b/frontend/src/container/ListOfDashboard/DashboardTemplates/DashboardTemplatesModal.styles.scss index 51fb567364..4907374684 100644 --- a/frontend/src/container/ListOfDashboard/DashboardTemplates/DashboardTemplatesModal.styles.scss +++ b/frontend/src/container/ListOfDashboard/DashboardTemplates/DashboardTemplatesModal.styles.scss @@ -58,7 +58,7 @@ overflow-y: auto; box-sizing: border-box; - height: calc(100% - 64px); + height: calc(100% - 54px); &::-webkit-scrollbar { height: 1rem; diff --git a/frontend/src/container/MetricsExplorer/Summary/Summary.styles.scss b/frontend/src/container/MetricsExplorer/Summary/Summary.styles.scss index f135ebf3e1..ffefe5bf20 100644 --- a/frontend/src/container/MetricsExplorer/Summary/Summary.styles.scss +++ b/frontend/src/container/MetricsExplorer/Summary/Summary.styles.scss @@ -194,7 +194,7 @@ .ant-pagination { position: fixed; bottom: 0; - width: calc(100% - 64px); + width: calc(100% - 54px); background: var(--bg-ink-500); padding: 16px; margin: 0; diff --git a/frontend/src/container/SideNav/NavItem/NavItem.styles.scss b/frontend/src/container/SideNav/NavItem/NavItem.styles.scss index 7f49a28acb..e7db8770c5 100644 --- a/frontend/src/container/SideNav/NavItem/NavItem.styles.scss +++ b/frontend/src/container/SideNav/NavItem/NavItem.styles.scss @@ -13,6 +13,12 @@ .nav-item-active-marker { background: #4e74f8; } + + .nav-item-data { + .nav-item-label { + color: var(--bg-vanilla-100, #fff); + } + } } &.disabled { @@ -27,14 +33,14 @@ .nav-item-data { color: white; - background: var(--Slate-500, #161922); + background: var(--bg-slate-500, #161922); } } &.active { .nav-item-data { color: white; - background: var(--Slate-500, #161922); + background: var(--bg-slate-500, #161922); // color: #3f5ecc; } } @@ -50,9 +56,9 @@ .nav-item-data { flex-grow: 1; - max-width: calc(100% - 24px); + max-width: calc(100% - 20px); display: flex; - margin: 0px 8px; + margin: 0px 0px 0px 6px; padding: 2px 8px; flex-direction: row; align-items: center; @@ -68,7 +74,7 @@ background: transparent; - transition: 0.2s all linear; + transition: 0.08s all ease; border-radius: 3px; @@ -100,7 +106,7 @@ &:hover { .nav-item-label { - color: var(--Vanilla-100, #fff); + color: var(--bg-vanilla-100, #fff); } .nav-item-pin-icon { @@ -120,6 +126,12 @@ .nav-item-active-marker { background: #4e74f8; } + + .nav-item-data { + .nav-item-label { + color: var(--bg-slate-500); + } + } } &:hover { diff --git a/frontend/src/container/SideNav/NavItem/NavItem.tsx b/frontend/src/container/SideNav/NavItem/NavItem.tsx index 207e49901e..825e69def6 100644 --- a/frontend/src/container/SideNav/NavItem/NavItem.tsx +++ b/frontend/src/container/SideNav/NavItem/NavItem.tsx @@ -1,11 +1,12 @@ /* eslint-disable jsx-a11y/no-static-element-interactions */ /* eslint-disable jsx-a11y/click-events-have-key-events */ -import { Tag } from 'antd'; +import { Tag, Tooltip } from 'antd'; import cx from 'classnames'; import { Pin, PinOff } from 'lucide-react'; import { SidebarItem } from '../sideNav.types'; +import './NavItem.styles.scss'; import './NavItem.styles.scss'; export default function NavItem({ @@ -74,21 +75,25 @@ export default function NavItem({ )} {onTogglePin && !isPinned && ( - + + + )} {onTogglePin && isPinned && ( - + + + )} diff --git a/frontend/src/container/SideNav/SideNav.styles.scss b/frontend/src/container/SideNav/SideNav.styles.scss index fdbe6193c1..1b443e287b 100644 --- a/frontend/src/container/SideNav/SideNav.styles.scss +++ b/frontend/src/container/SideNav/SideNav.styles.scss @@ -1,5 +1,5 @@ .sidenav-container { - width: 64px; + width: 54px; height: 100%; position: relative; z-index: 1; @@ -10,47 +10,60 @@ } .sideNav { - flex: 0 0 64px; + flex: 0 0 54px; height: 100%; - max-width: 64px; - min-width: 64px; - width: 64px; - border-right: 1px solid var(--Slate-500, #161922); - background: var(--Ink-500, #0b0c0e); + max-width: 54px; + min-width: 54px; + width: 54px; + border-right: 1px solid var(--bg-slate-500, #161922); + background: var(--bg-ink-500, #0b0c0e); padding-bottom: 48px; - transition: all 0.2s, background 0s, border 0s; + transition: all 0.08s ease, background 0s, border 0s; .brand-container { - padding: 8px 18px; + padding: 8px 15px; max-width: 100%; + background: transparent; } .brand-company-meta { display: flex; - gap: 8px; + align-items: center; + gap: 6px; + flex-shrink: 0; + width: 100%; + justify-content: center; } .brand { display: flex; align-items: center; + justify-content: center; max-width: 100%; overflow: hidden; gap: 32px; height: 32px; + width: 100%; .brand-logo { display: flex; align-items: center; - justify-content: space-between; + justify-content: center; gap: 8px; + flex-shrink: 0; + width: 20px; + height: 16px; + position: relative; cursor: pointer; img { height: 16px; + width: auto; + display: block; } .brand-logo-name { @@ -66,6 +79,10 @@ .brand-title-section { display: none; + flex-shrink: 0; + align-items: center; + gap: 0; + position: relative; .license-type { display: flex; @@ -76,7 +93,7 @@ color: var(--bg-vanilla-100); border-radius: 4px 0px 0px 4px; - background: var(--Slate-400, #1d212d); + background: var(--bg-slate-400, #1d212d); text-align: center; font-family: Inter; @@ -98,11 +115,11 @@ gap: 6px; border-radius: 0px 4px 4px 0px; - background: var(--Slate-300, #242834); + background: var(--bg-slate-300, #242834); } .version { - color: var(--Vanilla-400, #c0c1c3); + color: var(--bg-vanilla-400, #c0c1c3); text-align: center; font-variant-numeric: lining-nums tabular-nums slashed-zero; font-feature-settings: 'salt' on; @@ -156,24 +173,48 @@ .get-started-nav-items { display: flex; - margin: 4px 13px 12px 10px; + margin: 4px 10px 12px 8px; .get-started-btn { display: flex; align-items: center; justify-content: center; padding: 8px; - margin-left: 2px; gap: 8px; width: 100%; height: 32px; border-radius: 3px; - border: 1px solid var(--Slate-400, #1d212d); - background: var(--Slate-500, #161922); + border: 1px solid var(--bg-slate-400, #1d212d); + background: var(--bg-slate-500, #161922); box-shadow: none !important; + + color: var(--bg-vanilla-400, #c0c1c3); + + svg { + color: var(--bg-vanilla-400, #c0c1c3); + } + + .nav-item-label { + color: var(--bg-vanilla-400, #c0c1c3); + } + + &:hover:not(:disabled) { + background: var(--bg-slate-400, #1d212d); + border-color: var(--bg-slate-400, #1d212d); + + color: var(--bg-vanilla-100, #fff); + + svg { + color: var(--bg-vanilla-100, #fff); + } + + .nav-item-label { + color: var(--bg-vanilla-100, #fff); + } + } } } @@ -192,6 +233,10 @@ width: 100%; } + .nav-item { + margin-bottom: 6px; + } + .nav-top-section { display: flex; flex-direction: column; @@ -227,7 +272,7 @@ } .nav-section-title { - color: var(--Slate-50, #62687c); + color: var(--bg-slate-50, #62687c); font-family: Inter; font-size: 11px; font-style: normal; @@ -241,7 +286,7 @@ align-items: center; gap: 8px; - padding: 0 20px; + padding: 0 17px; .nav-section-title-text { display: none; @@ -250,11 +295,17 @@ .nav-section-title-icon { display: flex; align-items: center; + transition: opacity 0.08s ease, transform 0.08s ease; &.reorder { display: none; cursor: pointer; margin-left: auto; + transition: color 0.2s; + + &:hover { + color: var(--bg-vanilla-100, #fff); + } } } @@ -268,7 +319,7 @@ } .nav-section-subtitle { - color: var(--Vanilla-400, #c0c1c3); + color: var(--bg-vanilla-400, #c0c1c3); font-family: Inter; font-size: 11px; font-style: normal; @@ -276,20 +327,20 @@ line-height: 14px; /* 150% */ letter-spacing: 0.4px; - padding: 0 20px; + padding: 6px 20px; opacity: 0.6; display: none; - transition: all 0.3s, background 0s, border 0s; - transition-delay: 0.1s; + transition: all 0.08s ease, background 0s, border 0s; + transition-delay: 0.03s; } .nav-items-section { margin-top: 8px; display: flex; flex-direction: column; - gap: 4px; + transition: all 0.08s ease; } } @@ -302,7 +353,7 @@ .nav-items-section { opacity: 0; transform: translateY(-10px); - transition: all 0.4s ease; + transition: all 0.1s ease; overflow: hidden; height: 0; } @@ -312,11 +363,34 @@ .nav-items-section { opacity: 1; transform: translateY(0); - transition: all 0.4s ease; + transition: all 0.1s ease; overflow: hidden; height: auto; } } + + &.sidebar-collapsed { + .nav-title-section { + display: none; + } + + .nav-items-section { + margin-top: 0; + opacity: 1; + transform: translateY(0); + transition: all 0.08s ease; + height: auto; + overflow: visible; + } + } + } + + .shortcut-nav-items { + &.sidebar-collapsed { + .nav-items-section { + margin-top: 0; + } + } } .scroll-for-more-container { @@ -326,7 +400,7 @@ width: 100%; bottom: 12px; bottom: 8px; - margin-left: 50px; + margin-left: 43px; .scroll-for-more { display: flex; @@ -370,8 +444,6 @@ overflow-y: auto; overflow-x: hidden; - padding-top: 12px; - .secondary-nav-items { display: flex; flex-direction: column; @@ -381,10 +453,10 @@ overflow-x: hidden; padding: 8px 0; max-width: 100%; - width: 64px; + width: 54px; // width: 100%; // temp - transition: all 0.2s, background 0s, border 0s; + transition: all 0.08s ease, background 0s, border 0s; background: linear-gradient(180deg, rgba(11, 12, 14, 0) 0%, #0b0c0e 27.11%); @@ -413,7 +485,7 @@ &.scroll-available { .nav-bottom-section { - border-top: 1px solid var(--Slate-500, #161922); + border-top: 1px solid var(--bg-slate-500, #161922); } } } @@ -424,24 +496,53 @@ } &.collapsed { - flex: 0 0 64px; - max-width: 64px; - min-width: 64px; - width: 64px; + flex: 0 0 54px; + max-width: 54px; + min-width: 54px; + width: 54px; .nav-wrapper { .nav-top-section { .shortcut-nav-items { - .nav-section-title, + .nav-section-title { + display: none; + } + .nav-section-subtitle { display: none; } + + .nav-items-section { + display: flex; + margin-top: 0; + } + + .nav-title-section { + margin-top: 0; + margin-bottom: 0; + gap: 0; + } + } + + .more-nav-items { + .nav-section-title { + display: none; + } + + .nav-items-section { + display: flex; + margin-top: 0; + } + + .nav-title-section { + display: none; + } } } .nav-bottom-section { .secondary-nav-items { - width: 64px; + width: 54px; } } } @@ -466,7 +567,7 @@ border-radius: 12px; background: var(--Robin-500, #4e74f8); - color: var(--Vanilla-100, #fff); + color: var(--bg-vanilla-100, #fff); font-variant-numeric: lining-nums tabular-nums slashed-zero; font-feature-settings: 'case' on, 'cpsp' on, 'dlig' on, 'salt' on; font-family: Inter; @@ -479,7 +580,7 @@ } .sidenav-beta-tag { - color: var(--Vanilla-100, #fff); + color: var(--bg-vanilla-100, #fff); font-variant-numeric: lining-nums tabular-nums slashed-zero; font-feature-settings: 'case' on, 'cpsp' on, 'dlig' on, 'salt' on; font-family: Inter; @@ -494,7 +595,47 @@ background: var(--bg-slate-300); } - &:hover { + &:not(.pinned) { + .nav-item { + .nav-item-data { + justify-content: center; + } + } + + .shortcut-nav-items, + .more-nav-items { + .nav-section-title { + padding: 0 17px; + + .nav-section-title-icon { + display: none; + } + } + } + + &.dropdown-open { + .nav-item { + .nav-item-data { + flex-grow: 1; + justify-content: flex-start; + } + } + + .shortcut-nav-items, + .more-nav-items { + .nav-section-title { + padding: 0 17px; + + .nav-section-title-icon { + display: flex; + } + } + } + } + } + + &:not(.pinned):hover, + &.dropdown-open { flex: 0 0 240px; max-width: 240px; min-width: 240px; @@ -505,8 +646,17 @@ z-index: 10; background: #0b0c0e; + .brand-container { + padding: 8px 15px; + } + .brand { - justify-content: space-between; + justify-content: flex-start; + + .brand-company-meta { + justify-content: flex-start; + width: 100%; + } .brand-title-section { display: flex; @@ -533,6 +683,11 @@ .nav-section-title-icon { &.reorder { display: flex; + transition: color 0.2s; + + &:hover { + color: var(--bg-vanilla-100, #fff); + } } } } @@ -574,7 +729,7 @@ flex-direction: row; gap: 3px; border-radius: 20px; - background: var(--Slate-400, #1d212d); + background: var(--bg-slate-400, #1d212d); /* Drop Shadow */ box-shadow: 0px 103px 12px 0px rgba(0, 0, 0, 0.01), @@ -590,7 +745,7 @@ width: 140px; .scroll-for-more-label { - color: var(--Vanilla-400, #c0c1c3); + color: var(--bg-vanilla-400, #c0c1c3); font-family: Inter; font-size: 12px; font-style: normal; @@ -631,6 +786,13 @@ align-items: flex-start; } } + + .nav-item { + .nav-item-data { + flex-grow: 1; + justify-content: flex-start; + } + } } .get-started-nav-items { @@ -664,8 +826,17 @@ z-index: 10; background: #0b0c0e; + .brand-container { + padding: 8px 15px; + } + .brand { - justify-content: space-between; + justify-content: flex-start; + + .brand-company-meta { + justify-content: flex-start; + width: 100%; + } .brand-title-section { display: flex; @@ -692,6 +863,11 @@ .nav-section-title-icon { &.reorder { display: flex; + transition: color 0.2s; + + &:hover { + color: var(--bg-vanilla-100, #fff); + } } } } @@ -733,7 +909,7 @@ flex-direction: row; gap: 3px; border-radius: 20px; - background: var(--Slate-400, #1d212d); + background: var(--bg-slate-400, #1d212d); /* Drop Shadow */ box-shadow: 0px 103px 12px 0px rgba(0, 0, 0, 0.01), @@ -751,7 +927,7 @@ .scroll-for-more-label { display: block; - color: var(--Vanilla-400, #c0c1c3); + color: var(--bg-vanilla-400, #c0c1c3); font-family: Inter; font-size: 12px; font-style: normal; @@ -856,7 +1032,7 @@ .ant-dropdown-menu-item { .ant-dropdown-menu-title-content { - color: var(--Vanilla-400, #c0c1c3); + color: var(--bg-vanilla-400, #c0c1c3); font-family: Inter; font-size: 12px; font-style: normal; @@ -864,6 +1040,12 @@ line-height: normal; letter-spacing: 0.14px; } + + &:hover:not(.ant-dropdown-menu-item-disabled) { + .ant-dropdown-menu-title-content { + color: var(--bg-vanilla-100, #fff); + } + } } } } @@ -875,7 +1057,7 @@ gap: 8px; .user-settings-dropdown-label-text { - color: var(--Slate-50, #62687c); + color: var(--bg-slate-50, #62687c); font-family: Inter; font-size: 10px; font-family: Inter; @@ -887,7 +1069,7 @@ } .user-settings-dropdown-label-email { - color: var(--Vanilla-400, #c0c1c3); + color: var(--bg-vanilla-400, #c0c1c3); font-family: Inter; font-size: 12px; font-style: normal; @@ -897,12 +1079,16 @@ } .ant-dropdown-menu-item-divider { - background-color: var(--Slate-500, #161922) !important; + background-color: var(--bg-slate-500, #161922) !important; } .ant-dropdown-menu-item-disabled { opacity: 0.7; } + + .ant-dropdown-menu { + width: 100% !important; + } } .settings-dropdown, @@ -912,6 +1098,27 @@ } } +.secondary-nav-items { + .nav-item { + position: relative; + + .nav-item-active-marker { + position: absolute; + left: -5px; + top: 50%; + transform: translateY(-50%); + margin: 0; + width: 8px; + height: 24px; + z-index: 1; + } + } + + .nav-item-data { + margin-left: 8px !important; + } +} + .reorder-shortcut-nav-items-modal { width: 384px !important; @@ -1028,7 +1235,6 @@ display: flex; align-items: center; border-radius: 2px; - border-radius: 2px; background: var(--Robin-500, #4e74f8) !important; color: var(--bg-vanilla-100) !important; font-family: Inter; @@ -1038,10 +1244,10 @@ line-height: 24px; &.secondary-btn { - background-color: var(--Slate-500, #161922) !important; + background-color: var(--bg-slate-500, #161922) !important; border: 1px solid var(--bg-slate-500) !important; - color: var(--Vanilla-400, #c0c1c3) !important; + color: var(--bg-vanilla-400, #c0c1c3) !important; /* button/ small */ font-family: Inter; @@ -1064,6 +1270,10 @@ } } +.help-support-dropdown li.ant-dropdown-menu-item-divider { + background-color: var(--bg-slate-500, #161922) !important; +} + .lightMode { .sideNav { background: var(--bg-vanilla-100); @@ -1095,8 +1305,32 @@ .get-started-nav-items { .get-started-btn { border: 1px solid var(--bg-vanilla-300); - background: var(--bg-vanilla-100); - color: var(--bg-ink-400); + background: var(--bg-vanilla-200); + color: var(--bg-slate-50, #62687c); + + svg { + color: var(--bg-slate-50, #62687c); + } + + .nav-item-label { + color: var(--bg-ink-400, #62687c); + } + + // Hover state (light mode) + &:hover:not(:disabled) { + background: var(--bg-vanilla-300); + border-color: var(--bg-vanilla-300); + + color: var(--bg-slate-500, #161922); + + svg { + color: var(--bg-slate-500, #161922); + } + + .nav-item-label { + color: var(--bg-slate-500, #161922); + } + } } } @@ -1108,7 +1342,25 @@ } } + .brand-container { + background: transparent; + } + .nav-wrapper { + .nav-top-section { + .shortcut-nav-items { + .nav-section-title { + .nav-section-title-icon { + &.reorder { + &:hover { + color: var(--bg-slate-400, #1d212d); + } + } + } + } + } + } + .secondary-nav-items { border-top: 1px solid var(--bg-vanilla-300); @@ -1123,8 +1375,43 @@ } } - &:hover { + &.pinned { + .nav-wrapper { + .nav-top-section { + .shortcut-nav-items { + .nav-section-title { + .nav-section-title-icon { + &.reorder { + &:hover { + color: var(--bg-slate-400, #1d212d); + } + } + } + } + } + } + } + } + + &:not(.pinned):hover, + &.dropdown-open { background: var(--bg-vanilla-100); + + .nav-wrapper { + .nav-top-section { + .shortcut-nav-items { + .nav-section-title { + .nav-section-title-icon { + &.reorder { + &:hover { + color: var(--bg-slate-400, #1d212d); + } + } + } + } + } + } + } } } @@ -1134,6 +1421,12 @@ .ant-dropdown-menu-title-content { color: var(--bg-ink-400); } + + &:hover:not(.ant-dropdown-menu-item-disabled) { + .ant-dropdown-menu-title-content { + color: var(--bg-ink-500); + } + } } } } @@ -1210,6 +1503,10 @@ color: var(--bg-ink-400); } } + + .help-support-dropdown li.ant-dropdown-menu-item-divider { + background-color: var(--bg-vanilla-300) !important; + } } .version-tooltip-overlay { @@ -1222,7 +1519,7 @@ border-radius: 2px; border: 1px solid var(--bg-slate-500); - color: var(--Vanilla-100, #fff); + color: var(--bg-vanilla-100, #fff); font-family: Inter; font-size: 11px; font-style: normal; @@ -1237,7 +1534,7 @@ gap: 4px; .version-update-notification-tooltip-title { - color: var(--Vanilla-100, #fff); + color: var(--bg-vanilla-100, #fff); font-family: Inter; font-size: 11px; font-style: normal; @@ -1247,7 +1544,7 @@ } .version-update-notification-tooltip-content { - color: var(--Vanilla-100, #fff); + color: var(--bg-vanilla-100, #fff); font-family: Inter; font-size: 10px; font-style: normal; diff --git a/frontend/src/container/SideNav/SideNav.tsx b/frontend/src/container/SideNav/SideNav.tsx index 54679ad903..311b508ed8 100644 --- a/frontend/src/container/SideNav/SideNav.tsx +++ b/frontend/src/container/SideNav/SideNav.tsx @@ -157,18 +157,27 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { DefaultHelpSupportDropdownMenuItems, ); - const [pinnedMenuItems, setPinnedMenuItems] = useState([]); - const [tempPinnedMenuItems, setTempPinnedMenuItems] = useState( [], ); - const [secondaryMenuItems, setSecondaryMenuItems] = useState( - [], - ); - const [hasScroll, setHasScroll] = useState(false); const navTopSectionRef = useRef(null); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + const [isHovered, setIsHovered] = useState(false); + const [pinnedMenuItems, setPinnedMenuItems] = useState([]); + const [secondaryMenuItems, setSecondaryMenuItems] = useState( + [], + ); + + const handleMouseEnter = useCallback(() => { + setIsHovered(true); + }, []); + + const handleMouseLeave = useCallback(() => { + setIsHovered(false); + }, []); const checkScroll = useCallback((): void => { if (navTopSectionRef.current) { @@ -217,63 +226,68 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { const isAdmin = user.role === USER_ROLES.ADMIN; const isEditor = user.role === USER_ROLES.EDITOR; - useEffect(() => { - const navShortcuts = (userPreferences?.find( + // Compute initial pinned items and secondary menu items synchronously to avoid flash + const computedPinnedMenuItems = useMemo(() => { + const navShortcutsPreference = userPreferences?.find( (preference) => preference.name === USER_PREFERENCES.NAV_SHORTCUTS, - )?.value as unknown) as string[]; + ); + const navShortcuts = (navShortcutsPreference?.value as unknown) as + | string[] + | undefined; - const shouldShowIntegrations = - (isCloudUser || isEnterpriseSelfHostedUser) && (isAdmin || isEditor); + // If userPreferences not loaded yet, return empty to avoid showing defaults before preferences load + if (userPreferences === null) { + return []; + } - if (navShortcuts && isArray(navShortcuts) && navShortcuts.length > 0) { - // nav shortcuts is array of strings - const pinnedItems = navShortcuts + // If preference exists with non-empty array, use stored shortcuts + if (isArray(navShortcuts) && navShortcuts.length > 0) { + return navShortcuts .map((shortcut) => defaultMoreMenuItems.find((item) => item.itemKey === shortcut), ) .filter((item): item is SidebarItem => item !== undefined); - - // Set pinned items in the order they were stored - setPinnedMenuItems(pinnedItems); - - setSecondaryMenuItems( - defaultMoreMenuItems.map((item) => ({ - ...item, - isPinned: pinnedItems.some((pinned) => pinned.itemKey === item.itemKey), - isEnabled: - item.key === ROUTES.INTEGRATIONS - ? shouldShowIntegrations - : item.isEnabled, - })), - ); - } else { - // Set default pinned items - const defaultPinnedItems = defaultMoreMenuItems.filter( - (item) => item.isPinned, - ); - setPinnedMenuItems(defaultPinnedItems); - - setSecondaryMenuItems( - defaultMoreMenuItems.map((item) => ({ - ...item, - isPinned: defaultPinnedItems.some( - (pinned) => pinned.itemKey === item.itemKey, - ), - isEnabled: - item.key === ROUTES.INTEGRATIONS - ? shouldShowIntegrations - : item.isEnabled, - })), - ); } + + // No preference, or empty array → use defaults + return defaultMoreMenuItems.filter((item) => item.isPinned); + }, [userPreferences]); + + const computedSecondaryMenuItems = useMemo(() => { + const shouldShowIntegrationsValue = + (isCloudUser || isEnterpriseSelfHostedUser) && (isAdmin || isEditor); + + return defaultMoreMenuItems.map((item) => ({ + ...item, + isPinned: computedPinnedMenuItems.some( + (pinned) => pinned.itemKey === item.itemKey, + ), + isEnabled: + item.key === ROUTES.INTEGRATIONS + ? shouldShowIntegrationsValue + : item.isEnabled, + })); }, [ - userPreferences, + computedPinnedMenuItems, isCloudUser, isEnterpriseSelfHostedUser, isAdmin, isEditor, ]); + // Track if we've done the initial sync (to avoid overwriting user actions during session) + const hasInitializedRef = useRef(false); + + // Sync state only on initial load when userPreferences first becomes available + useEffect(() => { + // Only sync once: when userPreferences loads for the first time + if (!hasInitializedRef.current && userPreferences !== null) { + setPinnedMenuItems(computedPinnedMenuItems); + setSecondaryMenuItems(computedSecondaryMenuItems); + hasInitializedRef.current = true; + } + }, [computedPinnedMenuItems, computedSecondaryMenuItems, userPreferences]); + const isOnboardingV3Enabled = featureFlags?.find( (flag) => flag.name === FeatureKeys.ONBOARDING_V3, )?.active; @@ -327,6 +341,17 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { .map((item) => item.itemKey) .filter(Boolean) as string[]; + // Update context immediately (optimistically) so computed values reflect the change + updateUserPreferenceInContext({ + name: USER_PREFERENCES.NAV_SHORTCUTS, + description: USER_PREFERENCES.NAV_SHORTCUTS, + valueType: 'array', + defaultValue: false, + allowedValues: [], + allowedScopes: ['user'], + value: navShortcuts, + }); + updateUserPreferenceMutation( { name: USER_PREFERENCES.NAV_SHORTCUTS, @@ -335,6 +360,7 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { { onSuccess: (response) => { if (response.data) { + // Update context again on success to ensure consistency updateUserPreferenceInContext({ name: USER_PREFERENCES.NAV_SHORTCUTS, description: USER_PREFERENCES.NAV_SHORTCUTS, @@ -368,13 +394,13 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { if (isCurrentlyPinned) { return prevItems.filter((i) => i.key !== item.key); } - return [item, ...prevItems]; + return [...prevItems, item]; }); // Get the updated pinned menu items for preference update const updatedPinnedItems = pinnedMenuItems.some((i) => i.key === item.key) ? pinnedMenuItems.filter((i) => i.key !== item.key) - : [item, ...pinnedMenuItems]; + : [...pinnedMenuItems, item]; // Update user preference with the ordered list of item keys updateNavShortcutsPreference(updatedPinnedItems); @@ -455,6 +481,10 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { pathname, ]); + const isSettingsPage = useMemo(() => pathname.startsWith(ROUTES.SETTINGS), [ + pathname, + ]); + const userSettingsDropdownMenuItems: MenuProps['items'] = useMemo( () => [ @@ -594,7 +624,7 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { }, { type: 'group', - label: "WHAT's NEW", + label: "WHAT'S NEW", }, ...dropdownItems, { @@ -750,6 +780,15 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { [secondaryMenuItems], ); + // Get active "More" items that should be visible in collapsed state + const activeMoreMenuItems = useMemo( + () => moreMenuItems.filter((item) => activeMenuKey === item.key), + [moreMenuItems, activeMenuKey], + ); + + // Check if sidebar is collapsed (not pinned, not hovered, and no dropdown open) + const isCollapsed = !isPinned && !isHovered && !isDropdownOpen; + const renderNavItems = ( items: SidebarItem[], allowPin?: boolean, @@ -901,7 +940,15 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { return (
-
+
@@ -999,35 +1046,43 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { {renderNavItems(primaryMenuItems)}
-
-
-
-
- -
+ {(pinnedMenuItems.length > 0 || !isCollapsed) && ( +
+ {!isCollapsed && ( +
+
+
+ +
-
SHORTCUTS
+
SHORTCUTS
- {pinnedMenuItems.length > 1 && ( -
{ - logEvent('Sidebar V2: Manage shortcuts clicked', {}); - setIsReorderShortcutNavItemsModalOpen(true); - }} - > - + {pinnedMenuItems.length > 1 && ( + +
{ + logEvent('Sidebar V2: Manage shortcuts clicked', {}); + setIsReorderShortcutNavItemsModalOpen(true); + }} + > + +
+
+ )}
- )} -
- {pinnedMenuItems.length === 0 && ( -
- You have not added any shortcuts yet. + {pinnedMenuItems.length === 0 && ( +
+ You have not added any shortcuts yet. +
+ )}
)} - {pinnedMenuItems.length > 0 && ( + {(pinnedMenuItems.length > 0 || isCollapsed) && (
{renderNavItems( pinnedMenuItems.filter((item) => item.isEnabled), @@ -1036,46 +1091,60 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element {
)}
-
+ )} {moreMenuItems.length > 0 && (
-
-
{ - logEvent('Sidebar V2: More menu clicked', { - action: isMoreMenuCollapsed ? 'expand' : 'collapse', - }); - setIsMoreMenuCollapsed(!isMoreMenuCollapsed); - }} - > -
- -
+ {!isCollapsed && ( +
+
{ + // Only allow toggling when sidebar is open (pinned, hovered, or dropdown open) + if (isCollapsed) { + return; + } + const newCollapsedState = !isMoreMenuCollapsed; + logEvent('Sidebar V2: More menu clicked', { + action: isMoreMenuCollapsed ? 'expand' : 'collapse', + }); + setIsMoreMenuCollapsed(newCollapsedState); + }} + > +
+ +
-
MORE
+
MORE
-
- {isMoreMenuCollapsed ? ( - - ) : ( - - )} +
+ {isMoreMenuCollapsed ? ( + + ) : ( + + )} +
-
+ )}
- {renderNavItems( - moreMenuItems.filter((item) => item.isEnabled), - true, - )} + {/* Show all items when expanded, only active items when collapsed */} + {isCollapsed + ? renderNavItems( + activeMoreMenuItems.filter((item) => item.isEnabled), + true, + ) + : renderNavItems( + moreMenuItems.filter((item) => item.isEnabled), + true, + )}
)} @@ -1102,6 +1171,7 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { placement="topLeft" overlayClassName="nav-dropdown-overlay help-support-dropdown" trigger={['click']} + onOpenChange={(open): void => setIsDropdownOpen(open)} >
@@ -1122,8 +1192,10 @@ function SideNav({ isPinned }: { isPinned: boolean }): JSX.Element { placement="topLeft" overlayClassName="nav-dropdown-overlay settings-dropdown" trigger={['click']} + onOpenChange={(open): void => setIsDropdownOpen(open)} > -
+
+
{userSettingsMenuItem.icon}
diff --git a/frontend/src/pages/Settings/Settings.styles.scss b/frontend/src/pages/Settings/Settings.styles.scss index 936ff20c1c..1f6c145017 100644 --- a/frontend/src/pages/Settings/Settings.styles.scss +++ b/frontend/src/pages/Settings/Settings.styles.scss @@ -33,6 +33,19 @@ height: calc(100vh - 48px); border-right: 1px solid var(--Slate-500, #161922); background: var(--Ink-500, #0b0c0e); + margin-top: 4px; + + .nav-item { + .nav-item-data { + margin: 0px 8px 0px 4px; + } + + &.active { + .nav-item-data .nav-item-label { + color: var(--bg-vanilla-100, #fff); + } + } + } } .settings-page-content { @@ -81,6 +94,14 @@ .settings-page-sidenav { border-right: 1px solid var(--bg-vanilla-300); background: var(--bg-vanilla-100); + + .nav-item { + &.active { + .nav-item-data .nav-item-label { + color: var(--bg-ink-500); + } + } + } } .settings-page-content { diff --git a/frontend/src/pages/Settings/Settings.tsx b/frontend/src/pages/Settings/Settings.tsx index f87d83354f..af109f3278 100644 --- a/frontend/src/pages/Settings/Settings.tsx +++ b/frontend/src/pages/Settings/Settings.tsx @@ -13,7 +13,7 @@ import { SidebarItem } from 'container/SideNav/sideNav.types'; import useComponentPermission from 'hooks/useComponentPermission'; import { useGetTenantLicense } from 'hooks/useGetTenantLicense'; import history from 'lib/history'; -import { Wrench } from 'lucide-react'; +import { Cog } from 'lucide-react'; import { useAppContext } from 'providers/App/App'; import { USER_ROLES } from 'types/roles'; @@ -236,7 +236,7 @@ function SettingsPage(): JSX.Element { className="settings-page-header-title" data-testid="settings-page-title" > - + Settings