feat(legal): Claims-Korrektur, zentrale Legal-Config & öffentliche Rechtsseiten (v1.7.0)

Teil 1 des Compliance-Updates (nach vollständiger Repo-Analyse):

- src/config/legal.ts: zentrale, typisierte Betreiber-/Rechtskonfiguration mit sichtbaren
  Platzhaltern, ENV-Overrides (LEGAL_*), Build-Warnung bei offenen Pflichtangaben, echte
  Subprozessorenliste (aus dem Code abgeleitet)
- Claims-Korrektur (20 Aussagen): 24/7, 100% Swiss Hosted, DSG/DSGVO-konform, 100% der Spende,
  Kein Unternehmen, "bleibt kostenlos", "sicher via Stripe", "keine Weitergabe/keine
  Übermittlung ins Ausland", "verschlüsselt"→"gehasht" — in page.tsx, spenden, impressum,
  datenschutz (Neuaufbau), settings-tab
- Neue Legal-Seiten (druckbar, responsiv, versioniert, als Entwurf gekennzeichnet):
  /nutzungsbedingungen, /verantwortungsvolle-nutzung, /sicherheit, /unterstuetzen,
  /organisationen/datenschutzvereinbarung + wiederverwendbare LegalPage-Shell
- Datenschutz komplett neu aufgebaut: Subprozessoren-Tabelle (#dienstleister), Auslandbekanntgabe,
  gehashte Passwörter, Backups/Löschung, Zahlung (Stripe), Karten/Geodienste, Rechte/EDÖB
- Footer um alle Pflicht-Rechtslinks erweitert; sitemap.ts ergänzt
- docs/legal/website-claims-audit.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Pepe Ziberi
2026-07-23 11:57:39 +02:00
parent 6aa70e1504
commit 872b8927cc
15 changed files with 1069 additions and 207 deletions

View File

@@ -0,0 +1,91 @@
'use client'
import Link from 'next/link'
import { ArrowLeft, Printer } from 'lucide-react'
import { legal } from '@/config/legal'
/** Pflicht-Links, die im Footer jeder öffentlichen (Rechts-)Seite erscheinen. */
export const LEGAL_LINKS: { href: string; label: string }[] = [
{ href: '/impressum', label: 'Impressum' },
{ href: '/datenschutz', label: 'Datenschutz' },
{ href: '/nutzungsbedingungen', label: 'Nutzungsbedingungen' },
{ href: '/verantwortungsvolle-nutzung', label: 'Verantwortungsvolle Nutzung' },
{ href: '/sicherheit', label: 'Sicherheit' },
{ href: '/organisationen/datenschutzvereinbarung', label: 'Datenschutzvereinbarung (Org.)' },
{ href: '/unterstuetzen', label: 'Unterstützen' },
]
export function LegalFooterLinks() {
return (
<nav aria-label="Rechtliche Seiten" className="mt-10 border-t border-border pt-6 print:hidden">
<ul className="flex flex-wrap gap-x-4 gap-y-2 text-sm text-muted-foreground">
{LEGAL_LINKS.map(l => (
<li key={l.href}>
<Link href={l.href} className="hover:text-foreground underline underline-offset-2">{l.label}</Link>
</li>
))}
</ul>
</nav>
)
}
interface LegalPageProps {
title: string
/** Versionsangabe, z.B. legal.documents.terms.version */
version?: string
/** Veröffentlichungsdatum ISO */
publishedAt?: string
/** Als sorgfältiger Entwurf kennzeichnen (juristisch noch nicht geprüft) */
draft?: boolean
/** Kurzer Einleitungssatz unter dem Titel */
intro?: string
children: React.ReactNode
}
/**
* Einheitliche, responsive, druckbare Hülle für alle öffentlichen Rechtsseiten.
* Enthält Zurück-Link, Drucken-Button, Version/Datum, Entwurfshinweis und die Pflicht-Footer-Links.
*/
export function LegalPage({ title, version, publishedAt, draft = true, intro, children }: LegalPageProps) {
return (
<div className="min-h-screen bg-background px-4 py-10 print:py-0 print:bg-white">
<div className="max-w-3xl mx-auto">
<div className="flex items-center justify-between mb-6 print:hidden">
<Link href="/" className="text-sm text-muted-foreground hover:text-foreground inline-flex items-center gap-1">
<ArrowLeft className="w-3.5 h-3.5" /> Zur Startseite
</Link>
<button
onClick={() => window.print()}
className="text-sm text-muted-foreground hover:text-foreground inline-flex items-center gap-1"
>
<Printer className="w-3.5 h-3.5" /> Drucken
</button>
</div>
<article className="bg-card rounded-xl border border-border shadow-sm p-6 md:p-10 print:border-0 print:shadow-none print:p-0">
<h1 className="text-3xl font-bold text-foreground">{title}</h1>
<div className="mt-2 flex flex-wrap items-center gap-x-3 gap-y-1 text-xs text-muted-foreground">
{version && <span>Version {version}</span>}
{publishedAt && <span>· Stand: {publishedAt}</span>}
<span>· {legal.projectName} ({legal.siteDomain})</span>
</div>
{intro && <p className="mt-4 text-sm text-muted-foreground leading-relaxed">{intro}</p>}
{draft && (
<div className="mt-4 rounded-lg border border-amber-300 bg-amber-50 dark:bg-amber-950/30 dark:border-amber-800 px-4 py-3 text-sm text-amber-800 dark:text-amber-300">
<strong>Sorgfältiger Entwurf.</strong> {legal.draftNotice}
</div>
)}
<div className="legal-prose mt-6 space-y-6 text-sm leading-relaxed text-muted-foreground [&_h2]:text-lg [&_h2]:font-semibold [&_h2]:text-foreground [&_h2]:mb-2 [&_h2]:mt-2 [&_h3]:font-semibold [&_h3]:text-foreground [&_strong]:text-foreground [&_a]:text-red-600 [&_a]:underline [&_ul]:list-disc [&_ul]:pl-5 [&_ul]:space-y-1 [&_ol]:list-decimal [&_ol]:pl-5 [&_ol]:space-y-1">
{children}
</div>
<LegalFooterLinks />
</article>
</div>
</div>
)
}