Alerts: rules over sensors, watched fleet-wide from the hub; ntfy and webhook channels; firing count on the rail. Docker: containers by stack with live use, start/stop/restart, following logs, inspect, images/volumes/networks. Skins: HUD, Nord, Gruvbox, Catppuccin (+Latte), Dracula, Tokyo Night, Solarized (+Light). Accents: per-machine colour. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
27 lines
1.0 KiB
TypeScript
27 lines
1.0 KiB
TypeScript
import { ReactNode } from 'react'
|
|
|
|
// Every group is a page: clicking it opens it. `soon` groups are inert placeholders
|
|
// for the roadmap.
|
|
export interface Group { id: string; label: string; icon: ReactNode; soon?: boolean }
|
|
|
|
interface Props { groups: Group[]; view: string; header?: ReactNode; badges?: Record<string, number>; onView: (id: string) => void }
|
|
|
|
export default function Rail({ groups, view, header, badges, onView }: Props) {
|
|
return (
|
|
<nav className="rail">
|
|
{header}
|
|
{groups.map((g) => (
|
|
<div key={g.id} className={'group' + (g.soon ? ' soon' : '')}>
|
|
<button className={'group-head' + (view === g.id ? ' active' : '')} disabled={g.soon}
|
|
onClick={() => { if (!g.soon) onView(g.id) }}>
|
|
<span className="gicon">{g.icon}</span>
|
|
<span>{g.label}</span>
|
|
{g.soon && <span className="badge">soon</span>}
|
|
{!!badges?.[g.id] && <span className="rail-badge" title={`${badges[g.id]} firing`}>{badges[g.id]}</span>}
|
|
</button>
|
|
</div>
|
|
))}
|
|
</nav>
|
|
)
|
|
}
|