feat: add persona landing pages for IT department, IT support, and MSPs
Adds three "who it's for" pages reachable via a new nav dropdown, each built around a distinct day-to-day workflow rather than a reworded homepage pitch: - /for/it-department — the core admin/engineer audience: drift-fix, Ansible automation, VM provisioning, dashboards - /for/it-support — day-to-day operators: status at a glance, one-click Ack, Wake-on-LAN, scheduled reboots, filterable audit log - /for/msp — managed service providers: Satellite deployments for unreachable client sites, audit trail as client-facing evidence, self-hosted with no per-seat SaaS The MSP page deliberately avoids claiming per-site/per-customer RBAC — verified against netork/models/role.py that permissions are global role-based sets, not site-scoped, and phrased the copy accordingly. Nav.tsx's Docs dropdown logic is extracted into a reusable NavDropdown component (open state, outside-click, route-change-close) instead of being duplicated for the new "Für wen" dropdown, which also links to the existing /nis2 page rather than rebuilding that persona. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 5
parent
d57734767d
commit
2d85aa2e47
@@ -0,0 +1,120 @@
|
||||
import { Link } from 'react-router-dom'
|
||||
import { ServerStackIcon } from '@heroicons/react/24/outline'
|
||||
import { useLang } from '../context/LangContext'
|
||||
import { linkify } from '../lib/glossary'
|
||||
|
||||
type Item = { title: string; body: string }
|
||||
|
||||
const SECTION_LABELS: Record<'en' | 'de', { pain: string; features: string; drivers: string }> = {
|
||||
en: { pain: 'Your day today', features: 'Where netOrk helps', drivers: 'Runs on what you already have' },
|
||||
de: { pain: 'Euer Alltag heute', features: 'Wo netOrk hilft', drivers: 'Läuft auf dem, was ihr schon habt' },
|
||||
}
|
||||
|
||||
const PAIN_POINTS: Record<'en' | 'de', Item[]> = {
|
||||
en: [
|
||||
{ title: 'A different admin UI per vendor', body: 'OPNsense here, HP ProCurve there, a separate tab for OpenWRT and Proxmox. Every task starts with figuring out which UI to open.' },
|
||||
{ title: 'No single view of what\'s running where', body: 'Vendor, model, firmware, and status per device live in nobody\'s head — or a spreadsheet that\'s already out of date.' },
|
||||
{ title: 'Config changes with no audit trail', body: 'Someone tweaks a firewall rule directly on the device. Nobody documents it. It breaks something three weeks later.' },
|
||||
{ title: 'Manual SSH just to check a VLAN', body: 'Verifying interface status or VLAN membership means logging into the device directly, every time.' },
|
||||
],
|
||||
de: [
|
||||
{ title: 'Für jeden Hersteller eine andere Oberfläche', body: 'OPNsense hier, HP ProCurve dort, ein eigener Tab für OpenWRT und Proxmox. Jede Aufgabe beginnt damit, herauszufinden, welche UI überhaupt zuständig ist.' },
|
||||
{ title: 'Keine einzige Ansicht, was wo läuft', body: 'Hersteller, Modell, Firmware und Status pro Gerät stehen in niemandes Kopf — oder in einer Tabelle, die längst veraltet ist.' },
|
||||
{ title: 'Konfigurationsänderungen ohne Audit-Trail', body: 'Jemand ändert eine Firewall-Regel direkt am Gerät. Niemand dokumentiert es. Drei Wochen später bricht etwas.' },
|
||||
{ title: 'Manuelles SSH nur um ein VLAN zu prüfen', body: 'Um Interface-Status oder VLAN-Zugehörigkeit zu verifizieren, muss man sich jedes Mal direkt am Gerät einloggen.' },
|
||||
],
|
||||
}
|
||||
|
||||
const FEATURES: Record<'en' | 'de', Item[]> = {
|
||||
en: [
|
||||
{ title: 'Device Management', body: 'CRUD for every device with credential profiles and SSH key management. Vendor, model, and OS auto-populate from NAPALM get_facts() — no manual data entry.' },
|
||||
{ title: 'Config drift detection & one-click fix', body: 'Every poll compares device state against the desired state in netOrk. Drifted devices get a warning; fixing it streams live SSH output straight to the browser.' },
|
||||
{ title: 'Git-backed config history', body: 'Every poll snapshots the config into a local Git repository — full history, side-by-side diffs between any two points in time, and one-click restore for OPNsense.' },
|
||||
{ title: 'Ansible automation', body: '11 built-in roles (base, docker, adguard, wireguard, fail2ban, and more) with automatic dependency resolution. Write your own roles too, with full run history per device.' },
|
||||
{ title: 'VM Provisioning', body: 'Cloud-Init VMs on Proxmox straight from the hypervisor\'s VMs tab — pick an image, a VLAN, and an IP, and netOrk handles the DHCP reservation and Device linking.' },
|
||||
{ title: 'Dashboards', body: 'Configurable, shareable dashboards built from 13 widgets on a WYSIWYG grid — replace the fixed layout with the view your team actually needs.' },
|
||||
],
|
||||
de: [
|
||||
{ title: 'Geräteverwaltung', body: 'CRUD für jedes Gerät mit Credential-Profilen und SSH-Schlüsselverwaltung. Hersteller, Modell und OS werden automatisch über NAPALM get_facts() befüllt — keine manuelle Eingabe.' },
|
||||
{ title: 'Konfigurationsdrift-Erkennung & Ein-Klick-Fix', body: 'Bei jedem Poll wird der Gerätezustand mit dem Sollzustand in netOrk verglichen. Abweichende Geräte erhalten eine Warnung; der Fix streamt Live-SSH-Output direkt in den Browser.' },
|
||||
{ title: 'Git-basierte Konfigurationshistorie', body: 'Bei jedem Poll wird die Konfiguration in ein lokales Git-Repository gesnapshottet — vollständige Historie, Side-by-Side-Diffs zwischen beliebigen Zeitpunkten und Ein-Klick-Restore für OPNsense.' },
|
||||
{ title: 'Ansible-Automatisierung', body: '11 eingebaute Rollen (base, docker, adguard, wireguard, fail2ban und mehr) mit automatischer Abhängigkeitsauflösung. Eigene Rollen schreiben inklusive vollständiger Lauf-Historie pro Gerät.' },
|
||||
{ title: 'VM-Provisioning', body: 'Cloud-Init-VMs auf Proxmox direkt aus dem VMs-Tab des Hypervisors — Image, VLAN und IP auswählen, netOrk übernimmt DHCP-Reservierung und Geräteverknüpfung.' },
|
||||
{ title: 'Dashboards', body: 'Konfigurierbare, teilbare Dashboards aus 13 Widgets auf einem WYSIWYG-Grid — statt festem Layout die Ansicht, die euer Team wirklich braucht.' },
|
||||
],
|
||||
}
|
||||
|
||||
export default function ForItDepartment() {
|
||||
const { lang, t } = useLang()
|
||||
const labels = SECTION_LABELS[lang]
|
||||
const painPoints = PAIN_POINTS[lang]
|
||||
const features = FEATURES[lang]
|
||||
|
||||
return (
|
||||
<div className="py-16 md:py-24">
|
||||
<div className="max-w-4xl mx-auto px-6">
|
||||
|
||||
<div className="mb-16">
|
||||
<div className="mb-4 flex h-10 w-10 items-center justify-center rounded-lg bg-sky-600/10">
|
||||
<ServerStackIcon className="h-5 w-5 text-sky-400" />
|
||||
</div>
|
||||
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.forItDepartment.heading}</h1>
|
||||
<p className="text-base text-slate-400 leading-relaxed max-w-2xl">{linkify(t.forItDepartment.sub)}</p>
|
||||
</div>
|
||||
|
||||
<div className="mb-20">
|
||||
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
|
||||
{labels.pain}
|
||||
</h2>
|
||||
<div className="grid sm:grid-cols-2 gap-6">
|
||||
{painPoints.map((p) => (
|
||||
<div key={p.title} className="rounded-xl border border-slate-800 bg-slate-900 p-5">
|
||||
<p className="text-sm font-semibold text-slate-200 mb-1.5">{p.title}</p>
|
||||
<p className="text-sm text-slate-500 leading-relaxed">{linkify(p.body)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-20">
|
||||
<h2 className="text-xl font-semibold text-slate-200 mb-6 pb-2 border-b border-slate-800">
|
||||
{labels.features}
|
||||
</h2>
|
||||
<div className="grid sm:grid-cols-2 gap-6">
|
||||
{features.map((f) => (
|
||||
<div key={f.title} className="rounded-xl border border-slate-800 bg-slate-900 p-5">
|
||||
<p className="text-sm font-semibold text-slate-200 mb-1.5">{f.title}</p>
|
||||
<p className="text-sm text-slate-500 leading-relaxed">{linkify(f.body)}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="mb-20">
|
||||
<h2 className="text-xl font-semibold text-slate-200 mb-4 pb-2 border-b border-slate-800">
|
||||
{labels.drivers}
|
||||
</h2>
|
||||
<p className="text-sm text-slate-400 leading-relaxed mb-4">
|
||||
{lang === 'en'
|
||||
? 'Custom NAPALM drivers for 11 device types, plus every built-in NAPALM driver.'
|
||||
: 'Eigene NAPALM-Treiber für 11 Gerätetypen, plus alle integrierten NAPALM-Treiber.'}
|
||||
</p>
|
||||
<Link to="/drivers" className="text-sky-400 hover:text-sky-300 transition-colors text-sm font-medium">
|
||||
{lang === 'en' ? 'Full driver reference →' : 'Vollständige Treiberreferenz →'}
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="rounded-xl border border-slate-800 bg-slate-900 p-8 text-center">
|
||||
<h2 className="text-xl font-semibold text-slate-100 mb-3">{t.forItDepartment.cta.heading}</h2>
|
||||
<p className="text-sm text-slate-400 leading-relaxed mb-6 max-w-md mx-auto">{t.forItDepartment.cta.body}</p>
|
||||
<Link
|
||||
to="/docs/getting-started"
|
||||
className="inline-flex items-center gap-2 px-5 py-2.5 rounded-lg bg-sky-600 hover:bg-sky-500 text-white text-sm font-medium transition-colors"
|
||||
>
|
||||
{t.forItDepartment.cta.button}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user