refactor(nav): reorganize sidebar with dynamic sections at top

- move runs and sessions to top of navigation (most dynamic)
- add visual separator between dynamic and configuration sections
- reorder remaining items: scenarios, snippets, credentials, environments
- implement separator component in nav rendering logic
This commit is contained in:
2026-04-14 23:15:49 +03:00
parent 6c43b53590
commit 622924ed53
2 changed files with 32 additions and 18 deletions
+6
View File
@@ -95,6 +95,12 @@
background: var(--color-secondary-hover);
}
.navSeparator {
height: var(--border-width);
background: var(--color-border);
margin: var(--space-2) var(--space-2);
}
.main {
flex: 1;
overflow-y: auto;
+26 -18
View File
@@ -90,13 +90,17 @@ const SnippetDetailPage = lazy(() =>
import('./pages/snippet/SnippetDetailPage').then((m) => ({ default: m.SnippetDetailPage })),
);
const NAV: { path: string; labelKey: string; Icon: LucideIcon }[] = [
{ path: '/environments', labelKey: 'nav.environments', Icon: Globe },
{ path: '/credentials', labelKey: 'nav.credentials', Icon: KeyRound },
{ path: '/snippets', labelKey: 'nav.snippets', Icon: Braces },
{ path: '/sessions', labelKey: 'nav.sessions', Icon: Monitor },
{ path: '/scenarios', labelKey: 'nav.scenarios', Icon: ClipboardList },
const NAV: (
| { path: string; labelKey: string; Icon: LucideIcon }
| { separator: true }
)[] = [
{ path: '/runs', labelKey: 'nav.runs', Icon: Activity },
{ path: '/sessions', labelKey: 'nav.sessions', Icon: Monitor },
{ separator: true },
{ path: '/scenarios', labelKey: 'nav.scenarios', Icon: ClipboardList },
{ path: '/snippets', labelKey: 'nav.snippets', Icon: Braces },
{ path: '/credentials', labelKey: 'nav.credentials', Icon: KeyRound },
{ path: '/environments', labelKey: 'nav.environments', Icon: Globe },
];
export default function App() {
@@ -114,18 +118,22 @@ export default function App() {
>
<div className={styles.sideContent}>
<nav className={styles.nav}>
{NAV.map(({ path, labelKey, Icon }) => (
<NavLink
key={path}
to={path}
className={({ isActive }) =>
[styles.navItem, isActive ? styles.navItemActive : ''].filter(Boolean).join(' ')
}
>
<Icon size={16} className={styles.navIcon} aria-hidden="true" />
{t(labelKey)}
</NavLink>
))}
{NAV.map((item, index) =>
'separator' in item ? (
<div key={`separator-${index}`} className={styles.navSeparator} />
) : (
<NavLink
key={item.path}
to={item.path}
className={({ isActive }) =>
[styles.navItem, isActive ? styles.navItemActive : ''].filter(Boolean).join(' ')
}
>
<item.Icon size={16} className={styles.navIcon} aria-hidden="true" />
{t(item.labelKey)}
</NavLink>
),
)}
</nav>
<div className={styles.sideFooter}>