mirror of
https://github.com/elicpeter/nyx.git
synced 2026-06-09 19:45:13 +02:00
176 lines
4.3 KiB
TypeScript
176 lines
4.3 KiB
TypeScript
import { NavLink } from 'react-router-dom';
|
|
import {
|
|
OverviewIcon,
|
|
FindingsIcon,
|
|
ScansIcon,
|
|
RulesIcon,
|
|
TriageIcon,
|
|
ConfigIcon,
|
|
ExplorerIcon,
|
|
DebugIcon,
|
|
FolderIcon,
|
|
TagIcon,
|
|
} from '../icons/Icons';
|
|
import type { FC } from 'react';
|
|
import type { IconProps } from '../icons/Icons';
|
|
import { useHealth } from '../../api/queries/health';
|
|
import { useOverview } from '../../api/queries/overview';
|
|
import { useSSE } from '../../contexts/SSEContext';
|
|
|
|
interface NavItem {
|
|
id: string;
|
|
label: string;
|
|
path: string;
|
|
Icon: FC<IconProps>;
|
|
group: 'primary' | 'secondary' | 'footer';
|
|
}
|
|
|
|
const NAV_SECTIONS: NavItem[] = [
|
|
{
|
|
id: 'overview',
|
|
label: 'Overview',
|
|
path: '/',
|
|
Icon: OverviewIcon,
|
|
group: 'primary',
|
|
},
|
|
{
|
|
id: 'findings',
|
|
label: 'Findings',
|
|
path: '/findings',
|
|
Icon: FindingsIcon,
|
|
group: 'primary',
|
|
},
|
|
{
|
|
id: 'scans',
|
|
label: 'Scans',
|
|
path: '/scans',
|
|
Icon: ScansIcon,
|
|
group: 'primary',
|
|
},
|
|
{
|
|
id: 'rules',
|
|
label: 'Rules',
|
|
path: '/rules',
|
|
Icon: RulesIcon,
|
|
group: 'primary',
|
|
},
|
|
{
|
|
id: 'triage',
|
|
label: 'Triage',
|
|
path: '/triage',
|
|
Icon: TriageIcon,
|
|
group: 'primary',
|
|
},
|
|
{
|
|
id: 'explorer',
|
|
label: 'Explorer',
|
|
path: '/explorer',
|
|
Icon: ExplorerIcon,
|
|
group: 'secondary',
|
|
},
|
|
{
|
|
id: 'debug',
|
|
label: 'Debug',
|
|
path: '/debug',
|
|
Icon: DebugIcon,
|
|
group: 'secondary',
|
|
},
|
|
{
|
|
id: 'config',
|
|
label: 'Config',
|
|
path: '/config',
|
|
Icon: ConfigIcon,
|
|
group: 'footer',
|
|
},
|
|
];
|
|
|
|
function navLinkClass({ isActive }: { isActive: boolean }) {
|
|
return `nav-link${isActive ? ' active' : ''}`;
|
|
}
|
|
|
|
export function Sidebar() {
|
|
const { data: health } = useHealth();
|
|
const { data: overview } = useOverview();
|
|
const { isScanRunning } = useSSE();
|
|
|
|
const primary = NAV_SECTIONS.filter((n) => n.group === 'primary');
|
|
const secondary = NAV_SECTIONS.filter((n) => n.group === 'secondary');
|
|
const footer = NAV_SECTIONS.filter((n) => n.group === 'footer');
|
|
const findingsCount =
|
|
overview && overview.state !== 'empty' ? overview.total_findings : null;
|
|
|
|
return (
|
|
<aside className="sidebar">
|
|
<div className="sidebar-header">
|
|
<img src="/logo.png" alt="Nyx" className="sidebar-logo-img" />
|
|
</div>
|
|
|
|
<ul className="nav-list">
|
|
{primary.map((item) => (
|
|
<li key={item.id}>
|
|
<NavLink
|
|
to={item.path}
|
|
end={item.path === '/'}
|
|
className={navLinkClass}
|
|
>
|
|
<span className="nav-icon">
|
|
<item.Icon />
|
|
</span>
|
|
<span className="nav-label">{item.label}</span>
|
|
{item.id === 'findings' && findingsCount != null && (
|
|
<span className="nav-badge">{findingsCount}</span>
|
|
)}
|
|
</NavLink>
|
|
</li>
|
|
))}
|
|
|
|
<li className="nav-section-header">Tools</li>
|
|
|
|
{secondary.map((item) => (
|
|
<li key={item.id}>
|
|
<NavLink to={item.path} className={navLinkClass}>
|
|
<span className="nav-icon">
|
|
<item.Icon />
|
|
</span>
|
|
<span className="nav-label">{item.label}</span>
|
|
</NavLink>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
|
|
<div className="sidebar-footer">
|
|
<ul className="nav-list" style={{ flex: 'none' }}>
|
|
{footer.map((item) => (
|
|
<li key={item.id}>
|
|
<NavLink to={item.path} className={navLinkClass}>
|
|
<span className="nav-icon">
|
|
<item.Icon />
|
|
</span>
|
|
<span className="nav-label">{item.label}</span>
|
|
</NavLink>
|
|
</li>
|
|
))}
|
|
</ul>
|
|
</div>
|
|
|
|
<div className="sidebar-meta">
|
|
{health?.scan_root && (
|
|
<div className="sidebar-meta-item" title={health.scan_root}>
|
|
<FolderIcon />
|
|
<span>{health.scan_root}</span>
|
|
</div>
|
|
)}
|
|
{health?.version && (
|
|
<div className="sidebar-meta-item">
|
|
<TagIcon />
|
|
<span>v{health.version}</span>
|
|
</div>
|
|
)}
|
|
<div className={`scan-indicator${isScanRunning ? ' visible' : ''}`}>
|
|
<span className="status-dot running" />
|
|
Scanning...
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|