Files
website/src/pages/Drivers.tsx
T
Christian ManivongandClaude Sonnet 4.6 d48c7143aa
CI / TypeScript — type-check (push) Successful in 8s
CI / Publish — build & push image (push) Successful in 8s
CI / Deploy — pull & restart on host (push) Successful in 2s
feat: bilingual website (DE/EN) with language switcher
Add LangContext with localStorage persistence (default: DE).
DE/EN toggle in Nav (top-right, before CTA button).

All pages translated:
- Home: all 8 sections incl. NIS2 block
- Features: all 13 sections with full German bullet points
- Drivers: headings and descriptions
- Getting Started (coming soon)
- Roadmap: bilingual data arrays inline (Planned + Under consideration)
- NIS2: full Art. 21 mapping, evidence blocks, coming-soon list

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-28 17:00:22 +02:00

125 lines
6.5 KiB
TypeScript

import { useLang } from '../context/LangContext'
type Cap = '✓' | '—'
interface Driver {
name: string
deviceType: string
facts: Cap
interfaces: Cap
lldp: Cap
vlans: Cap
ssids: Cap
health: Cap
docker: Cap
reboot: Cap
configPush: Cap
status: 'stable' | 'beta'
}
const DRIVERS: Driver[] = [
{ name: 'fritzbox', deviceType: 'AVM Fritz!Box routers', facts: '✓', interfaces: '✓', lldp: '—', vlans: '—', ssids: '✓', health: '—', docker: '—', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'procurve', deviceType: 'HP ProCurve / Aruba switches', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '✓', docker: '—', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'linux', deviceType: 'Generic Linux servers', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '—', ssids: '—', health: '✓', docker: '✓', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'netgear', deviceType: 'Netgear switches', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '—', docker: '—', reboot: '—', configPush: '—', status: 'beta' },
{ name: 'openmediavault', deviceType: 'OpenMediaVault NAS', facts: '✓', interfaces: '✓', lldp: '—', vlans: '—', ssids: '—', health: '✓', docker: '✓', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'openwrt', deviceType: 'OpenWRT access points', facts: '✓', interfaces: '✓', lldp: '—', vlans: '✓', ssids: '✓', health: '✓', docker: '—', reboot: '✓', configPush: '✓', status: 'stable' },
{ name: 'opnsense', deviceType: 'OPNsense firewalls', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '✓', docker: '—', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'proxmox', deviceType: 'Proxmox VE hypervisors', facts: '✓', interfaces: '✓', lldp: '—', vlans: '—', ssids: '—', health: '✓', docker: '✓', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'sonos', deviceType: 'Sonos speakers', facts: '✓', interfaces: '—', lldp: '—', vlans: '—', ssids: '—', health: '—', docker: '—', reboot: '—', configPush: '—', status: 'beta' },
{ name: 'tplink_jetstream', deviceType: 'TP-Link Jetstream switches', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '—', docker: '—', reboot: '✓', configPush: '—', status: 'stable' },
{ name: 'zyxel', deviceType: 'Zyxel switches', facts: '✓', interfaces: '✓', lldp: '✓', vlans: '✓', ssids: '—', health: '—', docker: '—', reboot: '—', configPush: '—', status: 'beta' },
]
const NAPALM_BUILTIN = ['Arista EOS', 'Cisco IOS', 'Cisco IOS-XE', 'Cisco NX-OS', 'Juniper JunOS']
const CAPS: { key: keyof Driver; label: string }[] = [
{ key: 'facts', label: 'Facts' },
{ key: 'interfaces', label: 'Ifaces' },
{ key: 'lldp', label: 'LLDP' },
{ key: 'vlans', label: 'VLANs' },
{ key: 'ssids', label: 'SSIDs' },
{ key: 'health', label: 'Health' },
{ key: 'docker', label: 'Docker' },
{ key: 'reboot', label: 'Reboot' },
{ key: 'configPush', label: 'Config push' },
]
function StatusBadge({ status }: { status: 'stable' | 'beta' }) {
return (
<span className={`text-xs font-medium px-2 py-0.5 rounded-full ${
status === 'stable'
? 'bg-green-500/20 text-green-400'
: 'bg-yellow-500/20 text-yellow-400'
}`}>
{status}
</span>
)
}
export default function Drivers() {
const { t } = useLang()
return (
<div className="py-16 md:py-24">
<div className="max-w-7xl mx-auto px-6">
<div className="mb-12">
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.drivers.heading}</h1>
<p className="text-base text-slate-400 leading-relaxed max-w-xl">{t.drivers.sub}</p>
</div>
<div className="mb-16 overflow-x-auto">
<h2 className="text-xl font-semibold text-slate-200 mb-4">{t.drivers.customHeading}</h2>
<div className="rounded-xl border border-slate-800 overflow-hidden">
<table className="w-full text-sm">
<thead>
<tr className="border-b border-slate-800 bg-slate-900">
<th className="px-4 py-3 text-left font-medium text-slate-500">Driver</th>
<th className="px-4 py-3 text-left font-medium text-slate-500">Device type</th>
{CAPS.map((c) => (
<th key={c.key} className="px-3 py-3 text-center font-medium text-slate-500 whitespace-nowrap">
{c.label}
</th>
))}
<th className="px-4 py-3 text-left font-medium text-slate-500">Status</th>
</tr>
</thead>
<tbody>
{DRIVERS.map((d) => (
<tr key={d.name} className="border-b border-slate-800 last:border-0 hover:bg-slate-800/40 transition-colors">
<td className="px-4 py-3 font-mono text-sky-400 whitespace-nowrap">{d.name}</td>
<td className="px-4 py-3 text-slate-300 whitespace-nowrap">{d.deviceType}</td>
{CAPS.map((c) => (
<td key={c.key} className="px-3 py-3 text-center">
<span className={d[c.key] === '✓' ? 'text-green-400' : 'text-slate-700'}>
{d[c.key]}
</span>
</td>
))}
<td className="px-4 py-3"><StatusBadge status={d.status} /></td>
</tr>
))}
</tbody>
</table>
</div>
</div>
<div>
<h2 className="text-xl font-semibold text-slate-200 mb-4">{t.drivers.napalmHeading}</h2>
<p className="text-sm text-slate-400 mb-6">{t.drivers.napalmSub}</p>
<div className="flex flex-wrap gap-3">
{NAPALM_BUILTIN.map((name) => (
<span
key={name}
className="inline-flex items-center gap-1.5 px-3 py-1 rounded-full border border-slate-700 bg-slate-900 text-xs font-medium text-slate-300"
>
{name}
</span>
))}
</div>
</div>
</div>
</div>
)
}