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>
107 lines
7.1 KiB
TypeScript
107 lines
7.1 KiB
TypeScript
import { Link } from 'react-router-dom'
|
|
import { BuildingOffice2Icon } 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 }> = {
|
|
en: { pain: 'Your day today', features: 'Where netOrk helps' },
|
|
de: { pain: 'Euer Alltag heute', features: 'Wo netOrk hilft' },
|
|
}
|
|
|
|
const PAIN_POINTS: Record<'en' | 'de', Item[]> = {
|
|
en: [
|
|
{ title: 'Client sites you can\'t reach directly', body: 'Firewalled or disconnected networks mean juggling a VPN per client just to check a device.' },
|
|
{ title: 'No unified view across clients', body: 'Each client\'s network lives in its own silo — there\'s no single dashboard across every site you manage.' },
|
|
{ title: 'Proving what you did, and when', body: 'A client asks what changed after an incident, and there\'s no clean record to hand over — just memory and Slack messages.' },
|
|
{ title: 'Client data in someone else\'s cloud', body: 'SaaS network-management tools mean client infrastructure data sits on a vendor\'s servers you don\'t control.' },
|
|
],
|
|
de: [
|
|
{ title: 'Kundenstandorte, die ihr nicht direkt erreicht', body: 'Firewalled oder getrennte Netzwerke bedeuten VPN-Jonglieren pro Kunde, nur um ein Gerät zu prüfen.' },
|
|
{ title: 'Keine einheitliche Sicht über alle Kunden', body: 'Jedes Kundennetzwerk lebt in seiner eigenen Silo — kein einziges Dashboard über alle betreuten Standorte.' },
|
|
{ title: 'Belegen, was ihr wann gemacht habt', body: 'Ein Kunde fragt nach einem Vorfall, was sich geändert hat — und es gibt kein sauberes Protokoll, nur Erinnerung und Slack-Nachrichten.' },
|
|
{ title: 'Kundendaten in fremder Cloud', body: 'SaaS-Netzwerk-Tools bedeuten, dass die Infrastrukturdaten eurer Kunden auf Servern eines Anbieters liegen, den ihr nicht kontrolliert.' },
|
|
],
|
|
}
|
|
|
|
const FEATURES: Record<'en' | 'de', Item[]> = {
|
|
en: [
|
|
{ title: 'Satellite Deployments', body: 'A lightweight Docker agent deployed at a client site netOrk can\'t reach directly — polls devices locally and syncs results back to Central over HTTPS. Deployed in one flow via VM Provisioning.' },
|
|
{ title: 'Central auto-routes around unreachable sites', body: 'Once a satellite is online and heartbeating, Central automatically skips direct polling for that site — no manual toggling per client.' },
|
|
{ title: 'Actions work the same everywhere', body: 'Scheduled/on-demand reboots, the SNMP auto-fix flow, and discovery jobs all route through the same command channel, direct or behind a satellite. SNMP health-metric polling and WebSSH still need direct reach today.' },
|
|
{ title: 'A real audit trail to hand a client', body: 'Every orchestration action is logged — who, what, when — filterable and exportable to CSV or PDF. Evidence, not a verbal assurance.' },
|
|
{ title: 'Roles scoped per technician', body: 'Custom roles control exactly what each technician can do, from read-only visibility to full config access, per engagement.' },
|
|
{ title: 'Self-hosted, no per-seat SaaS', body: 'Docker Compose deployment, no telemetry, no cloud dependency — runs on your infrastructure or a client\'s, not a vendor\'s.' },
|
|
],
|
|
de: [
|
|
{ title: 'Satellite-Deployments', body: 'Ein leichtgewichtiger Docker-Agent an einem Kundenstandort, den netOrk nicht direkt erreicht — pollt Geräte lokal und synct Ergebnisse per HTTPS zurück an Central. In einem Ablauf per VM-Provisioning deployt.' },
|
|
{ title: 'Central umgeht nicht erreichbare Standorte automatisch', body: 'Sobald ein Satellite online ist und heartbeatet, überspringt Central automatisch das direkte Polling für diesen Standort — kein manuelles Umschalten pro Kunde.' },
|
|
{ title: 'Aktionen funktionieren überall gleich', body: 'Geplante/On-Demand-Reboots, der SNMP-Auto-Fix und Discovery-Jobs laufen über denselben Command-Kanal, egal ob direkt oder hinter einem Satellite. SNMP-Health-Metrik-Polling und WebSSH brauchen heute noch direkten Zugriff.' },
|
|
{ title: 'Ein echter Audit-Trail für den Kunden', body: 'Jede Orchestrierungsaktion wird protokolliert — wer, was, wann — filterbar und exportierbar als CSV oder PDF. Beleg statt mündlicher Zusicherung.' },
|
|
{ title: 'Rollen pro Techniker', body: 'Benutzerdefinierte Rollen legen genau fest, was jeder Techniker darf — von reinem Lesezugriff bis vollem Konfigurationszugriff, je nach Einsatz.' },
|
|
{ title: 'Self-hosted, kein Pro-Seat-SaaS', body: 'Docker-Compose-Deployment, keine Telemetrie, keine Cloud-Abhängigkeit — läuft auf eurer Infrastruktur oder der eines Kunden, nicht bei einem Anbieter.' },
|
|
],
|
|
}
|
|
|
|
export default function ForMsp() {
|
|
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">
|
|
<BuildingOffice2Icon className="h-5 w-5 text-sky-400" />
|
|
</div>
|
|
<h1 className="text-4xl md:text-5xl font-bold text-slate-100 mb-4">{t.forMsp.heading}</h1>
|
|
<p className="text-base text-slate-400 leading-relaxed max-w-2xl">{linkify(t.forMsp.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="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.forMsp.cta.heading}</h2>
|
|
<p className="text-sm text-slate-400 leading-relaxed mb-6 max-w-md mx-auto">{t.forMsp.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.forMsp.cta.button}
|
|
</Link>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|