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,46 @@
# Website Claims Audit — Lageplan
Stand: 2026-07-23 · Version 1.0 (sorgfältiger Entwurf, ersetzt keine juristische Prüfung)
Prüfung aller werblichen / absoluten Aussagen im Repository und deren Korrektur zu sachlichen,
nachweisbaren Formulierungen. Fundstellen beziehen sich auf den Stand vor der Korrektur.
| # | Bisherige Aussage | Fundstelle (vorher) | Risiko / Widerspruch | Neue Formulierung | Geänderte Datei |
|---|---|---|---|---|---|
| 1 | „100% · Swiss Hosted · Server in der Schweiz" | `src/app/page.tsx:241` | Serverstandort nicht für alle Subprozessoren (Stripe, SMTP, globale Kartendienste) belegt; absolutes 100% | „CH · Swiss Made · In der Schweiz entwickelt" | `src/app/page.tsx` |
| 2 | „24/7 · Verfügbar" | `src/app/page.tsx:243` | Verfügbarkeitszusage ohne SLA | „Web · Überall nutzbar · Browser, Tablet, Smartphone" | `src/app/page.tsx` |
| 3 | „Die Applikation ist DSG- und DSGVO-konform." (FAQ + JSON-LD) | `src/app/page.tsx:87,458` | Unbelegte pauschale Konformitätszusage | „Die eingesetzten Dienstleister und Speicherorte sind in der Datenschutzerklärung transparent aufgeführt. Lageplan orientiert sich am Schweizer Datenschutzgesetz (DSG)." | `src/app/page.tsx` |
| 4 | „Sicher & Schweiz … DSG/DSGVO-konform." (Feature-Karte) | `src/app/page.tsx:361` | Absolute Sicherheits-/Konformitätszusage | „Sicherheit & Datenschutz … Angemessene technische und organisatorische Schutzmassnahmen — Details in der Datenschutzerklärung." | `src/app/page.tsx` |
| 5 | „Lückenlose Protokollierung aller Aktionen" | `src/app/page.tsx:360` | „lückenlos" = absolute Zusage | „Protokollierung von Aktionen mit Zeitstempel für bessere Nachvollziehbarkeit." | `src/app/page.tsx` |
| 6 | „Nichts. Lageplan ist kostenlos …" (FAQ + JSON-LD) | `src/app/page.tsx:55,442` | Impliziert dauerhafte Kostenlosigkeit | „… grundsätzlich kostenlos … Ein Anspruch darauf, dass alle Funktionen dauerhaft kostenlos bleiben, besteht nicht." | `src/app/page.tsx` |
| 7 | „100% deiner Spende fliesst direkt …" | `src/app/page.tsx:588`, `src/app/spenden/page.tsx:252` | Ignoriert Stripe-Gebühren; absolutes 100% | „Beiträge werden nach Abzug allfälliger Zahlungsgebühren für Hosting, Infrastruktur und Weiterentwicklung eingesetzt." | `src/app/page.tsx`, `src/app/spenden/page.tsx` |
| 8 | „Kein Unternehmen, keine Investoren …" | `src/app/page.tsx:589`, `src/app/spenden/page.tsx:253` | Betreiberform ist rechtlich noch festzulegen; potenziell irreführend | „Ein Projekt aus der Feuerwehr-Praxis — entwickelt in der Freizeit." | `src/app/page.tsx`, `src/app/spenden/page.tsx` |
| 9 | „Lageplan ist und bleibt kostenlos." | `src/app/page.tsx:551`, `src/app/spenden/page.tsx:239` | „bleibt" = dauerhafte Zusage | „Lageplan wird grundsätzlich kostenlos angeboten." | `src/app/page.tsx`, `src/app/spenden/page.tsx` |
| 10 | „Zahlung sicher via Stripe" | `src/app/page.tsx:574`, `src/app/spenden/page.tsx:226` | „sicher" = absolute Zusage | „Zahlung über Stripe" | `src/app/page.tsx`, `src/app/spenden/page.tsx` |
| 11 | „Server-Hosting in der Schweiz (monatliche Kosten)" | `src/app/spenden/page.tsx:99` | Serverstandort nicht abschliessend belegt | „Server-Hosting und Infrastruktur (monatliche Kosten)" | `src/app/spenden/page.tsx` |
| 12 | „Jeder Franken fliesst direkt in Serverkosten …" | `src/app/spenden/page.tsx:244` | Ignoriert Gebühren | „Beiträge helfen, Hosting, Infrastruktur und Weiterentwicklung mitzufinanzieren." | `src/app/spenden/page.tsx` |
| 13 | „weder an Dritte verkauft noch weitergegeben" | `src/app/impressum/page.tsx:108` | Absolute „keine Weitergabe"; widerspricht Einsatz von Dienstleistern | „… nicht an Dritte verkauft. Für den technischen Betrieb können sorgfältig ausgewählte … Dienstleister eingesetzt werden. Einzelheiten enthält die Datenschutzerklärung." | `src/app/impressum/page.tsx` |
| 14 | „Einsatzdaten … werden verschlüsselt gespeichert" | `src/app/impressum/page.tsx:116` | „verschlüsselt gespeichert" technisch nicht belegt (Ruhedaten) | Zugriffs-/Mandantenkonzept + „Übertragung erfolgt über HTTPS/TLS"; Passwörter „gehasht (bcrypt)" | `src/app/impressum/page.tsx` |
| 15 | „Passwort (verschlüsselt gespeichert)" | `src/app/datenschutz/page.tsx:47` | Falsch: Passwörter werden gehasht, nicht verschlüsselt | „Passwort wird mit einem geeigneten Einwegverfahren (bcrypt) gehasht" | `src/app/datenschutz/page.tsx` (Neuaufbau) |
| 16 | „Alle Daten … auf Servern in der Schweiz … keine Übermittlung ins Ausland" | `src/app/datenschutz/page.tsx:105-106` | Widerspricht Stripe/SMTP/Kartendiensten; absolute Auslandsaussage | Ziffer 6 „Bekanntgabe ins Ausland": Auslandbearbeitung je nach Dienstleister möglich | `src/app/datenschutz/page.tsx` (Neuaufbau) |
| 17 | „Personenbezogene Daten werden nicht an Dritte weitergegeben" | `src/app/datenschutz/page.tsx:91,164` | Absolute „keine Weitergabe" | Dienstleisterliste (Subprozessoren) + „nicht zu Werbezwecken verkauft" | `src/app/datenschutz/page.tsx` (Neuaufbau) |
| 18 | „Gemäss DSG und DSGVO haben Sie folgende Rechte" | `src/app/datenschutz/page.tsx:144` | Pauschale DSGVO-Anwendbarkeit unbelegt | „Im Rahmen des anwendbaren Rechts …" + Beschwerde bei EDÖB | `src/app/datenschutz/page.tsx` (Neuaufbau) |
| 19 | „Bei der Löschung werden sämtliche Daten unwiderruflich entfernt" | `src/app/datenschutz/page.tsx:128` | Ignoriert Backups | „Produktivdaten werden nach Abschluss des Löschvorgangs entfernt. Daten in Sicherungskopien können bis zum Ablauf des Backup-Zyklus fortbestehen." | `src/app/datenschutz/page.tsx` (Neuaufbau) |
| 20 | „Passwörter werden verschlüsselt in der Datenbank gespeichert." | `src/components/admin/settings-tab.tsx:242` | Falsch: gehasht, nicht verschlüsselt | „… mit einem Einwegverfahren (bcrypt) gehasht …" | `src/components/admin/settings-tab.tsx` |
## Nicht beanstandet (Kontext geprüft)
- Zahlreiche `100%` in `rapport-pdf.tsx`, `measure-panel.tsx`, `map-view.tsx`, E-Mail-Templates → CSS-Breitenangaben, keine Werbeaussage.
- „sicher" in `README.md`, `docs/*`, `jwt-secret.ts`, `modules.ts` → technische Kommentare, nicht öffentlich.
- „SSL-Zertifikate" (Substantiv) → keine „zertifiziert"-Behauptung.
## Nicht gefunden (im gesamten Repo)
`garantiert`, `fehlerfrei`, `gemeinnützig`, `steuerlich`, `rechtssicher`, `zertifiziert`,
„immer verfügbar", „kostenlos für immer" — keine Treffer.
## Noch offen (Betreiber-Entscheid)
- Formulierung „In der Schweiz entwickelt": Sobald der Serverstandort für alle Subprozessoren/Backups
bestätigt ist, kann ggf. eine präzisere Hosting-Aussage getroffen werden.
- „Purepixel / Kingstickers Ziberi" (Impressum-Copyright): rechtsverbindliche Betreiberbezeichnung bestätigen.

View File

@@ -1,6 +1,6 @@
{ {
"name": "lageplan", "name": "lageplan",
"version": "1.6.9", "version": "1.7.0",
"description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation", "description": "Feuerwehr Lageplan - Krokier-App für Einsatzdokumentation",
"private": true, "private": true,
"scripts": { "scripts": {

View File

@@ -1,187 +1,207 @@
'use client' 'use client'
import Link from 'next/link' import { LegalPage } from '@/components/legal/legal-page'
import { ArrowLeft, Shield, Server, Trash2, Mail } from 'lucide-react' import { legal, isPlaceholder } from '@/config/legal'
function Val({ value }: { value: string }) {
if (isPlaceholder(value)) {
return <span className="text-amber-600 dark:text-amber-400 font-medium">{value}</span>
}
return <span className="text-foreground font-medium">{value}</span>
}
export default function DatenschutzPage() { export default function DatenschutzPage() {
const d = legal.documents.privacy
return ( return (
<div className="min-h-screen bg-gradient-to-br from-slate-900 via-slate-800 to-slate-900 px-4 py-12"> <LegalPage
<div className="max-w-3xl mx-auto"> title="Datenschutzerklärung"
<Link href="/" className="text-sm text-gray-400 hover:text-gray-300 inline-flex items-center gap-1 mb-8"> version={d.version}
<ArrowLeft className="w-3.5 h-3.5" /> publishedAt={d.publishedAt}
Zurück zur Startseite intro={`Diese Erklärung informiert transparent darüber, welche Personendaten ${legal.projectName} bearbeitet, zu welchem Zweck und wer daran beteiligt ist. Sie orientiert sich am Schweizer Datenschutzgesetz (DSG).`}
</Link> >
<section>
<h2>1. Verantwortlicher</h2>
<p>
Verantwortlich für die Datenbearbeitung ist der Betreiber der Plattform{' '}
<Val value={legal.operatorName} /> (<Val value={legal.operatorLegalForm} />),{' '}
<Val value={legal.postalAddress} />.
</p>
<p><strong>Datenschutzkontakt:</strong> <Val value={legal.privacyContactEmail} /></p>
</section>
<div className="bg-card rounded-xl shadow-2xl p-8 md:p-12 border border-border"> <section>
<div className="flex items-center gap-3 mb-6"> <h2>2. Welche Daten werden bearbeitet?</h2>
<div className="w-10 h-10 bg-red-600 rounded-full flex items-center justify-center"> <ul>
<Shield className="w-5 h-5 text-white" /> <li><strong>Registrierungs-/Kontodaten:</strong> Name, E-Mail-Adresse, Organisationsname, Rolle. Das Passwort wird mit einem geeigneten Einwegverfahren (bcrypt) gehasht und nie im Klartext gespeichert.</li>
</div> <li><strong>Einsatz-/Inhaltsdaten:</strong> Lagepläne, Journaleinträge, Zeichnungen, Koordinaten, Symbole, hochgeladene Dateien (Logos, Planbilder, Symbole) sowie zugehörige Projekt- und Organisationsdaten.</li>
<h1 className="text-3xl font-bold text-foreground">Datenschutzerklärung</h1> <li><strong>Protokoll-/Sicherheitsdaten:</strong> technische Zugriffs- und Fehlerprotokolle (z.B. Zeitpunkt, ungefähre technische Angaben) zur Gewährleistung von Betrieb und Sicherheit.</li>
</div> <li><strong>Zustimmungsnachweise:</strong> welche Version der Nutzungsbedingungen/Datenschutzerklärung/Organisationsbestätigung wann akzeptiert wurde.</li>
<li><strong>Unterstützungsbeiträge (optional):</strong> bei freiwilligen Beiträgen der Betrag sowie freiwillig angegebener Name/Nachricht (Zahlungsabwicklung siehe Ziffer 9).</li>
</ul>
</section>
<p className="text-muted-foreground mb-8"> <section>
Gültig ab 1. Januar 2026 Lageplan.ch, betrieben in der Schweiz. <h2>3. Zwecke</h2>
</p> <ul>
<li>Bereitstellung und Betrieb der Applikation, Authentifizierung und Benutzer-/Organisationsverwaltung</li>
<li>Echtzeit-Synchronisierung von Einsatzdaten zwischen berechtigten Teammitgliedern</li>
<li>Erstellung von Rapporten und PDF-/PNG-Exporten</li>
<li>Gewährleistung von Sicherheit, Stabilität und Missbrauchsprävention</li>
<li>Technischer Support und Fehlerbehebung</li>
<li>Erfüllung rechtlicher Pflichten (soweit anwendbar)</li>
</ul>
</section>
{/* 1. Verantwortlicher */} <section>
<section className="mb-8"> <h2>4. Grundlagen der Bearbeitung</h2>
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2"> <p>
<span className="text-red-500 font-mono text-sm">01</span> Die Bearbeitung erfolgt insbesondere zur Erfüllung des Nutzungsverhältnisses (Vertrag), aufgrund
Verantwortlicher berechtigter Interessen am sicheren und stabilen Betrieb sowie wo erforderlich gestützt auf Ihre
</h2> Einwilligung. Zwingende gesetzliche Bestimmungen bleiben vorbehalten.
<p className="text-muted-foreground text-sm leading-relaxed"> </p>
Verantwortlich für die Datenverarbeitung auf lageplan.ch ist der Betreiber der Plattform. </section>
Bei Fragen zum Datenschutz wenden Sie sich bitte an die im Impressum angegebene Kontaktadresse
oder per E-Mail an <span className="text-foreground font-medium">datenschutz@lageplan.ch</span>.
</p>
</section>
{/* 2. Welche Daten */} <section id="dienstleister" className="scroll-mt-20">
<section className="mb-8"> <h2>5. Empfänger und Dienstleister (Subprozessoren)</h2>
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2"> <p>
<span className="text-red-500 font-mono text-sm">02</span> Für den technischen Betrieb können sorgfältig ausgewählte Dienstleister eingesetzt werden. Nachfolgend
Welche Daten werden erhoben? die tatsächlich eingesetzten bzw. optional konfigurierbaren Dienste. Einige Karten-, Geocoding- und
</h2> Wetterdienste werden direkt aus Ihrem Browser aufgerufen; dabei kann die aufgerufene Kachel-/Standort-
<div className="space-y-3 text-sm text-muted-foreground leading-relaxed"> Anfrage (und damit Ihre IP-Adresse) an den jeweiligen Anbieter übermittelt werden.
<p><strong className="text-foreground">Registrierungsdaten:</strong> Name, E-Mail-Adresse, Organisationsname, Passwort (verschlüsselt gespeichert).</p> </p>
<p><strong className="text-foreground">Einsatzdaten:</strong> Lagepläne, Journal-Einträge, Zeichnungen, Koordinaten, Symbole und zugehörige Projektdaten, die Sie in der Applikation erstellen.</p> <div className="overflow-x-auto">
<p><strong className="text-foreground">Technische Daten:</strong> IP-Adresse, Browser-Typ, Zugriffszeitpunkt ausschliesslich für den Betrieb und die Sicherheit der Plattform.</p> <table className="w-full text-xs border-collapse mt-2">
<p><strong className="text-foreground">Hochgeladene Dateien:</strong> Logos, Planbilder und Symbole, die Sie über die Applikation hochladen.</p> <thead>
</div> <tr className="text-left text-foreground border-b border-border">
</section> <th className="py-1.5 pr-3">Dienst</th>
<th className="py-1.5 pr-3">Zweck</th>
{/* 3. Zweck */} <th className="py-1.5 pr-3">Daten</th>
<section className="mb-8"> <th className="py-1.5">Region</th>
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2"> </tr>
<span className="text-red-500 font-mono text-sm">03</span> </thead>
Zweck der Datenverarbeitung <tbody>
</h2> {legal.processors.map(p => (
<div className="space-y-2 text-sm text-muted-foreground leading-relaxed"> <tr key={p.name} className="border-b border-border/50 align-top">
<p>Ihre Daten werden ausschliesslich für folgende Zwecke verwendet:</p> <td className="py-1.5 pr-3 text-foreground font-medium">{p.name}{p.optional ? ' *' : ''}</td>
<ul className="list-disc pl-5 space-y-1"> <td className="py-1.5 pr-3">{p.purpose}</td>
<li>Bereitstellung und Betrieb der Lageplan-Applikation</li> <td className="py-1.5 pr-3">{p.data}</td>
<li>Authentifizierung und Benutzerverwaltung</li> <td className="py-1.5">{isPlaceholder(p.region) ? <span className="text-amber-600 dark:text-amber-400">{p.region}</span> : p.region}</td>
<li>Echtzeit-Synchronisierung von Einsatzdaten zwischen Teammitgliedern</li> </tr>
<li>Erstellung von Einsatzrapports und PDF-Exporten</li> ))}
<li>Technischer Support und Fehlerbehebung</li> </tbody>
</ul> </table>
</div>
</section>
{/* 4. Betrieb und Wartung */}
<section className="mb-8">
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2">
<span className="text-red-500 font-mono text-sm">04</span>
Betrieb, Wartung und Support
</h2>
<div className="space-y-3 text-sm text-muted-foreground leading-relaxed">
<p>
Für den sicheren und zuverlässigen Betrieb der Plattform können autorisierte Mitarbeitende
des Betreibers im Rahmen ihrer Aufgaben auf Systemdaten zugreifen. Dies geschieht ausschliesslich zu folgenden Zwecken:
</p>
<ul className="list-disc pl-5 space-y-1">
<li>Sicherstellung der Systemverfügbarkeit und -stabilität</li>
<li>Fehlerbehebung und technischer Support auf Anfrage</li>
<li>Durchführung von Wartungs- und Aktualisierungsarbeiten</li>
<li>Gewährleistung der Datensicherheit und Missbrauchsprävention</li>
</ul>
<p>
Der Zugriff erfolgt unter Einhaltung strikter Vertraulichkeitspflichten und wird auf das
betrieblich notwendige Minimum beschränkt. Personenbezogene Daten werden nicht an Dritte weitergegeben
und ausschliesslich im Rahmen der oben genannten Zwecke verarbeitet.
</p>
</div>
</section>
{/* 5. Hosting & Speicherung */}
<section className="mb-8">
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2">
<Server className="w-4 h-4 text-red-500" />
<span><span className="text-red-500 font-mono text-sm mr-2">05</span>Hosting und Speicherung</span>
</h2>
<div className="space-y-2 text-sm text-muted-foreground leading-relaxed">
<p>
Alle Daten werden auf Servern in der <strong className="text-foreground">Schweiz</strong> gespeichert.
Es findet keine Übermittlung von Daten ins Ausland statt.
</p>
<p>
Die Daten werden in einer PostgreSQL-Datenbank gespeichert. Dateien (Logos, Bilder) werden in einem
S3-kompatiblen Objektspeicher (MinIO) abgelegt. Die Übertragung erfolgt verschlüsselt via HTTPS/TLS.
</p>
</div>
</section>
{/* 6. Datenlöschung */}
<section className="mb-8">
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2">
<Trash2 className="w-4 h-4 text-red-500" />
<span><span className="text-red-500 font-mono text-sm mr-2">06</span>Datenlöschung und Konto-Auflösung</span>
</h2>
<div className="space-y-2 text-sm text-muted-foreground leading-relaxed">
<p>
Sie haben jederzeit das Recht, Ihr Konto und Ihre Organisation inklusive aller zugehörigen Daten
vollständig zu löschen. Dies können Sie selbstständig über die Applikation durchführen
(Einstellungen Organisation löschen).
</p>
<p>
Bei der Löschung werden sämtliche Daten unwiderruflich entfernt: Benutzerkonten, Projekte,
Lagepläne, Journal-Einträge, Rapports, hochgeladene Dateien und alle zugehörigen Metadaten.
</p>
<p>
Alternativ können Sie die Löschung per E-Mail an <span className="text-foreground font-medium">datenschutz@lageplan.ch</span> beantragen.
</p>
</div>
</section>
{/* 7. Rechte */}
<section className="mb-8">
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2">
<span className="text-red-500 font-mono text-sm">07</span>
Ihre Rechte
</h2>
<div className="space-y-2 text-sm text-muted-foreground leading-relaxed">
<p>Gemäss dem Schweizer Datenschutzgesetz (DSG) und der DSGVO haben Sie folgende Rechte:</p>
<ul className="list-disc pl-5 space-y-1">
<li><strong className="text-foreground">Auskunftsrecht:</strong> Sie können Auskunft über Ihre gespeicherten Daten verlangen.</li>
<li><strong className="text-foreground">Berichtigungsrecht:</strong> Sie können die Korrektur unrichtiger Daten verlangen.</li>
<li><strong className="text-foreground">Löschungsrecht:</strong> Sie können die Löschung Ihrer Daten verlangen.</li>
<li><strong className="text-foreground">Datenportabilität:</strong> Sie können Ihre Daten in einem gängigen Format anfordern.</li>
<li><strong className="text-foreground">Widerspruchsrecht:</strong> Sie können der Verarbeitung Ihrer Daten widersprechen.</li>
</ul>
</div>
</section>
{/* 8. Cookies */}
<section className="mb-8">
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2">
<span className="text-red-500 font-mono text-sm">08</span>
Cookies
</h2>
<p className="text-sm text-muted-foreground leading-relaxed">
Die Applikation verwendet ausschliesslich <strong className="text-foreground">technisch notwendige Cookies</strong> für
die Authentifizierung (Session-Token). Es werden keine Tracking-, Analyse- oder Werbe-Cookies eingesetzt.
Es werden keine Daten an Dritte weitergegeben.
</p>
</section>
{/* 9. Kontakt */}
<section className="mb-4">
<h2 className="text-xl font-semibold text-foreground mb-3 flex items-center gap-2">
<Mail className="w-4 h-4 text-red-500" />
<span><span className="text-red-500 font-mono text-sm mr-2">09</span>Kontakt</span>
</h2>
<p className="text-sm text-muted-foreground leading-relaxed">
Bei Fragen oder Anliegen zum Datenschutz kontaktieren Sie uns unter:{' '}
<span className="text-foreground font-medium">datenschutz@lageplan.ch</span>
</p>
</section>
</div> </div>
<p className="text-xs">* nur aktiv, sofern konfiguriert/genutzt (z.B. Zahlungsabwicklung, E-Mail-Versand).</p>
<p>Personendaten werden nicht zu Werbezwecken an Dritte verkauft.</p>
</section>
<div className="text-center mt-6 text-xs text-gray-500"> <section>
© {new Date().getFullYear()} Lageplan.ch Alle Rechte vorbehalten. <h2>6. Bekanntgabe ins Ausland</h2>
</div> <p>
</div> Je nach eingesetztem Dienstleister (z.B. Zahlungsanbieter, konfigurierter E-Mail-Anbieter, globale
</div> Kartendienste) kann eine Bearbeitung ausserhalb der Schweiz erfolgen. Der Betreiber wählt Dienstleister
sorgfältig aus. Eine ausschliessliche Speicherung in der Schweiz wird nicht pauschal zugesichert; die
konkreten Verarbeitungsregionen sind soweit bekannt in der Tabelle unter Ziffer 5 bzw. in{' '}
<code>docs/legal/subprocessors.md</code> aufgeführt. Offene Punkte sind mit Platzhaltern gekennzeichnet.
</p>
</section>
<section>
<h2>7. Sicherheit</h2>
<p>
Es werden angemessene technische und organisatorische Massnahmen eingesetzt (u.a. Rollen- und
Mandantentrennung, Passwort-Hashing, verschlüsselte Übertragung via HTTPS/TLS, Zugriffskontrollen,
Rate-Limiting). Ein vollständiger Schutz vor allen Risiken kann jedoch nicht garantiert werden.
</p>
</section>
<section>
<h2>8. Aufbewahrung und Löschung</h2>
<p>
Personendaten werden so lange aufbewahrt, wie es für die genannten Zwecke oder gesetzliche Pflichten
erforderlich ist. Produktivdaten werden nach Abschluss des Löschvorgangs entfernt. Daten in
Sicherungskopien (Backups) können bis zum Ablauf des regulären Backup-Zyklus fortbestehen
(Aufbewahrungsfrist der Backups: <Val value={legal.backupRetention} />). Die detaillierten Fristen sind in{' '}
<code>docs/legal/retention-and-deletion.md</code> dokumentiert.
</p>
</section>
<section>
<h2>9. Zahlungsabwicklung (freiwillige Beiträge)</h2>
<p>
Freiwillige Unterstützungsbeiträge werden über <strong>Stripe</strong> abgewickelt. Zahlungsdaten
(z.B. Kreditkarte, Twint) werden direkt bei Stripe erfasst und nicht in der Applikation gespeichert.
An Stripe werden der Betrag sowie ein freiwillig angegebener Name/Nachricht übermittelt. Es werden keine
wiederkehrenden Zahlungen eingerichtet. Details siehe{' '}
<a href="/unterstuetzen">Unterstützungsseite</a>.
</p>
</section>
<section>
<h2>10. Karten- und Geodienste</h2>
<p>
Für Karten, Adresssuche und Wetter-/Höhendaten werden Dienste von OpenStreetMap (Tiles/Nominatim),
Esri (Satellitenbilder), swisstopo (Landeskarte) sowie Open-Meteo/Open-Elevation genutzt. Diese Aufrufe
erfolgen aus Ihrem Browser; dabei kann Ihre IP-Adresse an die jeweiligen Anbieter übermittelt werden.
Karten-, Adress- und Geodaten können unvollständig, ungenau oder veraltet sein.
</p>
</section>
<section>
<h2>11. Cookies und lokale Speichertechnologien</h2>
<p>
Es wird ein einziges, technisch notwendiges Cookie gesetzt: <code>auth-token</code> (Session/Anmeldung,
httpOnly). Es werden keine Tracking-, Analyse- oder Werbe-Cookies eingesetzt.
</p>
<p>
Im lokalen Speicher (localStorage) Ihres Geräts werden zur Funktion technisch notwendige Angaben
abgelegt, u.a.: zwischengespeicherte Zeichnungen/Offline-Warteschlange, Cache-Zeitstempel für
Offline-Karten, Theme-Einstellung, zuletzt geöffnetes Projekt, Onboarding-Status und der Zustand des
Cookie-Hinweises. Diese Daten verbleiben auf Ihrem Gerät.
</p>
</section>
<section>
<h2>12. Protokolldaten</h2>
<p>
Zum Betrieb und zur Sicherheit können technische Protokolle anfallen. Diese werden auf das notwendige
Mass beschränkt; sensible Inhalte werden nach Möglichkeit von Protokollen ferngehalten.
</p>
</section>
<section>
<h2>13. Ihre Rechte</h2>
<p>Im Rahmen des anwendbaren Rechts stehen Ihnen insbesondere folgende Rechte zu:</p>
<ul>
<li><strong>Auskunft</strong> über die bearbeiteten Daten</li>
<li><strong>Berichtigung</strong> unrichtiger Daten</li>
<li><strong>Löschung</strong> (im Rahmen gesetzlicher Pflichten)</li>
<li><strong>Datenherausgabe/-export</strong> in einem gängigen Format</li>
<li><strong>Widerspruch</strong> gegen bestimmte Bearbeitungen</li>
<li><strong>Beschwerde</strong> bei der zuständigen Datenschutzbehörde (in der Schweiz: EDÖB)</li>
</ul>
<p>
Angemeldete Nutzer können Zustimmungen, Datenexport und Kontolöschung unter{' '}
<a href="/konto/datenschutz">Konto &amp; Datenschutz</a> einsehen bzw. anfordern. Anfragen richten Sie an{' '}
<Val value={legal.privacyContactEmail} />; der Eingang wird bestätigt.
</p>
</section>
<section>
<h2>14. Sicherheitsvorfälle</h2>
<p>
Bei einem Sicherheitsvorfall mit Risiko für betroffene Personen prüft der Betreiber die erforderlichen
Massnahmen und Informationspflichten nach einem definierten internen Prozess.
</p>
</section>
<section>
<h2>15. Änderungen dieser Datenschutzerklärung</h2>
<p>
Diese Datenschutzerklärung kann angepasst werden. Massgebend ist die jeweils veröffentlichte Version
(siehe Kopf dieser Seite). Bei wesentlichen Änderungen wird angemessen informiert.
</p>
</section>
</LegalPage>
) )
} }

View File

@@ -104,17 +104,20 @@ export default function ImpressumPage() {
Privatsphäre sowie auf Schutz vor Missbrauch ihrer persönlichen Daten. Privatsphäre sowie auf Schutz vor Missbrauch ihrer persönlichen Daten.
</p> </p>
<p className="mt-2"> <p className="mt-2">
Wir halten diese Bestimmungen ein. Persönliche Daten werden streng vertraulich behandelt und Persönliche Daten werden vertraulich behandelt und nicht an Dritte verkauft. Für den technischen
weder an Dritte verkauft noch weitergegeben. Betrieb können sorgfältig ausgewählte Hosting-, Karten-, E-Mail-, Zahlungs- und
Infrastruktur-Dienstleister eingesetzt werden. Einzelheiten enthält die{' '}
<a href="/datenschutz" className="text-red-600 hover:text-red-500">Datenschutzerklärung</a>.
</p> </p>
<p className="mt-2"> <p className="mt-2">
Die Nutzung der Applikation erfordert eine Registrierung. Dabei werden folgende Daten Die Nutzung der Applikation erfordert eine Registrierung. Dabei werden insbesondere Name,
gespeichert: Name, E-Mail-Adresse, Organisationsname. Die Daten werden ausschliesslich für E-Mail-Adresse und Organisationsname gespeichert. Passwörter werden mit einem geeigneten
den Betrieb der Applikation verwendet und auf Servern in der Schweiz gehostet. Einwegverfahren (bcrypt) gehasht und nie im Klartext gespeichert.
</p> </p>
<p className="mt-2"> <p className="mt-2">
Einsatzdaten (Lagepläne, Journaleinträge, Zeichnungen) werden verschlüsselt gespeichert und Einsatzdaten (Lagepläne, Journaleinträge, Zeichnungen) sind nach dem Rollen- und
sind nur für berechtigte Benutzer der jeweiligen Organisation zugänglich. Mandantenkonzept nur für berechtigte Benutzer der jeweiligen Organisation zugänglich. Die
Übertragung erfolgt über HTTPS/TLS.
</p> </p>
</section> </section>

View File

@@ -0,0 +1,186 @@
'use client'
import { LegalPage } from '@/components/legal/legal-page'
import { legal } from '@/config/legal'
export default function NutzungsbedingungenPage() {
const d = legal.documents.terms
return (
<LegalPage
title="Nutzungsbedingungen"
version={d.version}
publishedAt={d.publishedAt}
intro={`Diese Nutzungsbedingungen regeln die Nutzung von ${legal.projectName} (Website, Anwendung, Benutzerkonten, Organisationen, Exporte und unterstützende Funktionen).`}
>
<section>
<h2>1. Geltungsbereich</h2>
<p>
Diese Bedingungen gelten für die Website, die Anwendung, Benutzerkonten, Organisationen, Exporte und
unterstützende Funktionen von {legal.projectName}. <strong>Betreiber</strong> ist der Anbieter der
Plattform; <strong>Benutzer</strong> ist jede natürliche Person mit Konto; <strong>Organisation</strong>
{' '}ist eine Feuerwehr oder Einheit mit eigenem Mandanten; <strong>Plattform</strong> bezeichnet die
Gesamtheit der bereitgestellten Dienste.
</p>
</section>
<section>
<h2>2. Zweck von {legal.projectName}</h2>
<p>{legal.projectName} ist ein unterstützendes Werkzeug für:</p>
<ul>
<li>Erstellung und Verwaltung von Lage- und Objektplänen</li>
<li>Dokumentation und Visualisierung</li>
<li>Zusammenarbeit</li>
<li>Export von Unterlagen</li>
<li>unterstützende Berechnungen</li>
</ul>
</section>
<section>
<h2>3. Kein Einsatzleitsystem</h2>
<ul>
<li>{legal.projectName} ist <strong>kein Alarmierungssystem</strong>.</li>
<li>{legal.projectName} ist <strong>kein Einsatzleitsystem</strong> und ersetzt keine Einsatzleitung.</li>
<li>{legal.projectName} ersetzt keine Leitstelle.</li>
<li>{legal.projectName} ersetzt keine gesetzlich oder organisatorisch vorgeschriebenen Unterlagen.</li>
<li>{legal.projectName} darf <strong>nicht als einzige Informationsquelle</strong> verwendet werden.</li>
<li>Die Organisation muss unabhängig aktuelle <strong>Offline- und Notfallunterlagen</strong> bereithalten.</li>
</ul>
</section>
<section>
<h2>4. Fachliche Prüfung</h2>
<ul>
<li>Pläne müssen von fachkundigen Personen geprüft und freigegeben werden.</li>
<li>Adressen, Distanzen, Symbole, Massstäbe, Hydranten, Zufahrten und Berechnungen können fehlerhaft oder veraltet sein.</li>
<li>Automatisch erzeugte oder berechnete Angaben sind Hilfsmittel.</li>
<li>Für operative Entscheidungen bleibt die zuständige Feuerwehr bzw. Organisation verantwortlich.</li>
<li>Änderungen an Gebäuden, Zufahrten, Wasserbezugsstellen oder Gefahren müssen durch die Organisation aktualisiert werden.</li>
</ul>
</section>
<section>
<h2>5. Benutzerkonten und Organisationen</h2>
<ul>
<li>Benutzer müssen richtige Angaben machen.</li>
<li>Zugangsdaten dürfen nicht weitergegeben werden.</li>
<li>Organisationen müssen Berechtigungen regelmässig kontrollieren.</li>
<li>Ausgetretene Benutzer müssen entfernt werden.</li>
<li>Organisationsadministratoren müssen zur Vertretung ihrer Organisation berechtigt sein.</li>
<li>Aktivitäten innerhalb eines Organisationskontos können protokolliert werden.</li>
</ul>
</section>
<section>
<h2>6. Verbotene Inhalte</h2>
<p>Ohne separate schriftliche Vereinbarung ist insbesondere untersagt, folgende Inhalte zu speichern:</p>
<ul>
<li>Patientennamen, Gesundheitsdaten, medizinische Befunde</li>
<li>Fotos identifizierbarer verletzter oder verstorbener Personen</li>
<li>polizeiliche Ermittlungsinformationen</li>
<li>Passwörter, Alarmcodes, Schlüsselcodes, Tresorcodes</li>
<li>private Telefonnummern ohne rechtliche Grundlage</li>
<li>Ausweiskopien und weitere besonders schützenswerte Personendaten</li>
<li>rechtswidrige oder urheberrechtlich unzulässige Inhalte</li>
<li>Informationen, welche die Sicherheit von Personen oder kritischer Infrastruktur unangemessen gefährden</li>
</ul>
<p>
Auch reine Objektinformationen können vertraulich oder sicherheitsrelevant sein und dürfen nur
berechtigten Personen zugänglich gemacht werden.
</p>
</section>
<section>
<h2>7. Verfügbarkeit</h2>
<ul>
<li>Es besteht kein Anspruch auf ununterbrochene Verfügbarkeit.</li>
<li>Es besteht kein Service-Level-Agreement (SLA).</li>
<li>Wartungen und Störungen sind möglich.</li>
<li>Funktionen dürfen weiterentwickelt, eingeschränkt oder eingestellt werden.</li>
<li>Exporte und Offline-Kopien liegen in der Verantwortung der Organisation.</li>
<li>Es besteht keine Garantie auf dauerhafte Speicherung ohne eigene Sicherung.</li>
</ul>
<p>{legal.projectName} wird ohne garantierte Verfügbarkeit und ohne SLA bereitgestellt.</p>
</section>
<section>
<h2>8. Kostenloser Betrieb</h2>
<ul>
<li>Die Plattform wird grundsätzlich kostenlos angeboten.</li>
<li>Es besteht kein Anspruch darauf, dass alle Funktionen dauerhaft kostenlos bleiben.</li>
<li>Wesentliche Änderungen werden angemessen kommuniziert.</li>
<li>Eine spätere kostenpflichtige Leistung entsteht nur nach ausdrücklicher separater Zustimmung.</li>
<li>Es gibt keine automatische kostenpflichtige Verlängerung.</li>
</ul>
</section>
<section>
<h2>9. Unterstützungsbeiträge</h2>
<ul>
<li>Beiträge sind freiwillig und erfolgen ohne Anspruch auf eine Gegenleistung.</li>
<li>Beiträge begründen keinen Anspruch auf Funktionen, Support, Verfügbarkeit oder bevorzugte Behandlung.</li>
<li>Zahlungsanbieter können Gebühren abziehen.</li>
<li>Es wird keine steuerlich abzugsfähige Spendenbescheinigung versprochen.</li>
<li>Bei versehentlicher Zahlung besteht eine Kontaktmöglichkeit zur Prüfung einer Rückerstattung ({legal.supportEmail}).</li>
<li>Es wird keine Gemeinnützigkeit oder Steuerbefreiung behauptet, solange diese nicht schriftlich bestätigt ist.</li>
</ul>
</section>
<section>
<h2>10. Geistiges Eigentum</h2>
<ul>
<li>Die Rechte an der Software verbleiben beim Betreiber bzw. den jeweiligen Rechteinhabern.</li>
<li>Rechte an eigenen Inhalten verbleiben bei den Benutzern bzw. Organisationen.</li>
<li>Der Betreiber erhält die für den technischen Betrieb notwendige, einfache Nutzungslizenz an hochgeladenen Inhalten.</li>
<li>Benutzer sind für die hochgeladenen Inhalte und deren Rechtmässigkeit verantwortlich.</li>
<li>Karten-, Symbol-, Icon- und Drittanbieter-Lizenzen sind zu beachten (siehe <a href="/impressum">Impressum</a> und Lizenzdokumentation).</li>
<li>Eine Quellenangabe ersetzt nicht automatisch eine erforderliche Nutzungslizenz.</li>
</ul>
</section>
<section>
<h2>11. Haftung</h2>
<p>Im gesetzlich zulässigen Umfang gilt:</p>
<ul>
<li>keine Garantie für Vollständigkeit, Aktualität oder Fehlerfreiheit der Inhalte;</li>
<li>keine Garantie für Karten- oder Drittanbieterdaten;</li>
<li>keine Garantie für Berechnungsergebnisse;</li>
<li>die Verantwortung für fachliche Kontrolle und operative Entscheidungen liegt bei der Organisation;</li>
<li>indirekte Schäden, Datenverlust und Betriebsunterbruch werden nur im gesetzlich zulässigen Umfang begrenzt.</li>
</ul>
<p>
<strong>Zwingende gesetzliche Haftungsbestimmungen bleiben vorbehalten. Insbesondere wird die Haftung
für rechtswidrige Absicht oder grobe Fahrlässigkeit nicht im Voraus ausgeschlossen.</strong>
</p>
</section>
<section>
<h2>12. Sperrung und Beendigung</h2>
<ul>
<li>Sperrung bei Missbrauch oder Sicherheitsrisiken ist möglich.</li>
<li>Eine Exportmöglichkeit wird bereitgestellt, soweit technisch und rechtlich möglich.</li>
<li>Löschung erfolgt nach definierten Fristen; Daten in Backups können bis zum Ablauf des Backup-Zyklus fortbestehen.</li>
<li>Bei Einstellung des Projekts erfolgt eine angemessene Vorankündigung, soweit möglich.</li>
<li>Bei akuten Sicherheitsproblemen können sofortige Massnahmen ergriffen werden.</li>
</ul>
</section>
<section>
<h2>13. Änderungen der Bedingungen</h2>
<ul>
<li>Die Bedingungen werden versioniert.</li>
<li>Über wesentliche Änderungen wird informiert.</li>
<li>Bei wesentlichen Änderungen wird eine erneute ausdrückliche Zustimmung verlangt.</li>
<li>Frühere Versionen bleiben intern nachvollziehbar.</li>
</ul>
</section>
<section>
<h2>14. Anwendbares Recht</h2>
<p>
Es gilt {legal.governingLaw}. Gerichtsstand, soweit zulässig und sinnvoll: {legal.jurisdiction}.
Zwingende Bestimmungen zum Schutz von Konsumentinnen und Konsumenten bleiben vorbehalten.
</p>
</section>
</LegalPage>
)
}

View File

@@ -0,0 +1,143 @@
'use client'
import { LegalPage } from '@/components/legal/legal-page'
import { legal, isPlaceholder } from '@/config/legal'
function Val({ value }: { value: string }) {
return isPlaceholder(value)
? <span className="text-amber-600 dark:text-amber-400 font-medium">{value}</span>
: <span className="text-foreground font-medium">{value}</span>
}
export default function DatenschutzvereinbarungPage() {
const d = legal.documents.dataProcessingAgreement
return (
<LegalPage
title="Datenschutzvereinbarung für Organisationen"
version={d.version}
publishedAt={d.publishedAt}
intro="Entwurf einer Vereinbarung zur Auftragsbearbeitung vor produktiver Nutzung mit personenbezogenen oder vertraulichen Organisationsdaten durch beide Parteien zu prüfen und zu vervollständigen."
>
<section>
<h2>1. Parteien</h2>
<p>
<strong>Auftragsbearbeiter (Betreiber):</strong> <Val value={legal.operatorName} /> (<Val value={legal.operatorLegalForm} />),{' '}
<Val value={legal.postalAddress} />, Kontakt <Val value={legal.privacyContactEmail} />.
</p>
<p><strong>Verantwortliche (Organisation):</strong> die jeweilige Feuerwehr/Organisation als Mandant.</p>
</section>
<section>
<h2>2. Gegenstand und Dauer</h2>
<p>
Gegenstand ist die Bearbeitung von Personendaten im Auftrag der Organisation im Rahmen der Nutzung von
{' '}{legal.projectName}. Die Vereinbarung gilt für die Dauer des Nutzungsverhältnisses.
</p>
</section>
<section>
<h2>3. Zweck</h2>
<p>Bereitstellung, Betrieb und Support der Plattform gemäss Weisung der Organisation.</p>
</section>
<section>
<h2>4. Kategorien betroffener Personen</h2>
<ul>
<li>Mitglieder und Funktionsträger der Organisation (Benutzer)</li>
<li>ggf. weitere Personen, soweit von der Organisation im Rahmen der Nutzung erfasst</li>
</ul>
</section>
<section>
<h2>5. Datenkategorien</h2>
<ul>
<li>Konto-/Kontaktdaten (Name, E-Mail, Rolle)</li>
<li>Inhalts-/Einsatzdaten (Pläne, Journale, Zeichnungen, Uploads)</li>
<li>Protokoll-/Nutzungsdaten</li>
</ul>
<p>
Besonders schützenswerte Personendaten dürfen ohne gesonderte schriftliche Vereinbarung nicht bearbeitet
werden (siehe <a href="/nutzungsbedingungen">Nutzungsbedingungen</a>).
</p>
</section>
<section>
<h2>6. Weisungen der Organisation</h2>
<p>Der Betreiber bearbeitet Personendaten nur gemäss den dokumentierten Weisungen der Organisation und dieser Vereinbarung.</p>
</section>
<section>
<h2>7. Vertraulichkeit</h2>
<p>Zum Zugriff berechtigte Personen sind zur Vertraulichkeit verpflichtet.</p>
</section>
<section>
<h2>8. Technische und organisatorische Massnahmen (TOM)</h2>
<p>
Es gelten die unter <a href="/sicherheit">Sicherheit</a> beschriebenen Massnahmen; der detaillierte
Stand ist in <code>docs/legal/technical-organizational-measures.md</code> dokumentiert (Anhang A).
</p>
</section>
<section>
<h2>9. Unterauftragnehmer</h2>
<p>
Der Betreiber setzt die in der <a href="/datenschutz#dienstleister">Dienstleisterliste</a> genannten
Unterauftragnehmer ein (Anhang B). Über Änderungen wird angemessen informiert.
</p>
</section>
<section>
<h2>10. Datenstandorte</h2>
<p>Verarbeitungsregionen gemäss Dienstleisterliste; offene Punkte: <Val value={legal.serverLocation} />.</p>
</section>
<section>
<h2>11. Unterstützung bei Betroffenenrechten</h2>
<p>Der Betreiber unterstützt die Organisation angemessen bei der Erfüllung von Auskunfts-, Berichtigungs-, Lösch- und Exportbegehren.</p>
</section>
<section>
<h2>12. Sicherheitsvorfälle</h2>
<p>Der Betreiber informiert die Organisation ohne unangemessene Verzögerung über Sicherheitsvorfälle mit Risiko für betroffene Personen.</p>
</section>
<section>
<h2>13. Löschung und Rückgabe</h2>
<p>
Nach Vertragsende werden Personendaten gelöscht oder zurückgegeben, soweit keine gesetzliche
Aufbewahrungspflicht besteht. Daten in Backups können bis zum Ablauf des Backup-Zyklus fortbestehen
(<Val value={legal.backupRetention} />).
</p>
</section>
<section>
<h2>14. Datenexport</h2>
<p>Die Organisation kann ihre Daten in einem gängigen Format exportieren, soweit technisch möglich.</p>
</section>
<section>
<h2>15. Kontrolle und Nachweis</h2>
<p>Der Betreiber stellt der Organisation auf Anfrage angemessene Nachweise zur Einhaltung dieser Vereinbarung zur Verfügung.</p>
</section>
<section>
<h2>16. Kontaktstellen</h2>
<p>Betreiber: <Val value={legal.privacyContactEmail} />. Organisation: durch die Organisation zu benennen.</p>
</section>
<section>
<h2>Anhänge</h2>
<ul>
<li><strong>Anhang A TOM:</strong> <a href="/sicherheit">Sicherheit</a> / interne TOM-Dokumentation</li>
<li><strong>Anhang B Unterauftragnehmer:</strong> <a href="/datenschutz#dienstleister">Dienstleisterliste</a></li>
</ul>
<p className="text-xs">
Dieser Entwurf enthält keine abgeschlossene Vereinbarung und keine Unterschriften. Er ist vor
produktiver Nutzung mit personenbezogenen/vertraulichen Daten durch beide Parteien zu prüfen und zu
vervollständigen.
</p>
</section>
</LegalPage>
)
}

View File

@@ -52,7 +52,7 @@ export default function LandingPage() {
name: 'Was kostet Lageplan?', name: 'Was kostet Lageplan?',
acceptedAnswer: { acceptedAnswer: {
'@type': 'Answer', '@type': 'Answer',
text: 'Nichts. Lageplan ist kostenlos für alle Feuerwehren in der Schweiz. Die Entwicklung wird durch freiwillige Spenden finanziert.', text: 'Lageplan wird Feuerwehren grundsätzlich kostenlos zur Verfügung gestellt. Der Betrieb wird durch freiwillige Unterstützungsbeiträge mitfinanziert. Ein Anspruch darauf, dass alle Funktionen dauerhaft kostenlos bleiben, besteht nicht.',
}, },
}, },
{ {
@@ -84,7 +84,7 @@ export default function LandingPage() {
name: 'Wo werden meine Daten gespeichert?', name: 'Wo werden meine Daten gespeichert?',
acceptedAnswer: { acceptedAnswer: {
'@type': 'Answer', '@type': 'Answer',
text: 'Alle Daten werden auf Servern in der Schweiz gespeichert. Die Applikation ist DSG- und DSGVO-konform.', text: 'Kontodaten werden in einer Datenbank gespeichert. Die eingesetzten Dienstleister und Speicherorte sind in der Datenschutzerklärung transparent aufgeführt. Lageplan orientiert sich am Schweizer Datenschutzgesetz (DSG).',
}, },
}, },
{ {
@@ -237,10 +237,10 @@ export default function LandingPage() {
<div className="max-w-5xl mx-auto"> <div className="max-w-5xl mx-auto">
<div className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8"> <div className="grid grid-cols-2 md:grid-cols-4 gap-6 md:gap-8">
{[ {[
{ value: '117', label: 'FKS-Signaturen', sub: 'Offizielle taktische Zeichen' }, { value: '117', label: 'FKS-Signaturen', sub: 'Taktische Zeichen (Bildquelle FKS)' },
{ value: '100%', label: 'Swiss Hosted', sub: 'Server in der Schweiz' }, { value: 'CH', label: 'Swiss Made', sub: 'In der Schweiz entwickelt' },
{ value: '0.—', label: 'Kostenlos', sub: 'Für alle Feuerwehren' }, { value: '0.—', label: 'Kostenlos', sub: 'Grundnutzung für Feuerwehren' },
{ value: '24/7', label: 'Verfügbar', sub: 'Browser-basiert, jedes Gerät' }, { value: 'Web', label: 'Überall nutzbar', sub: 'Browser, Tablet, Smartphone' },
].map((stat, i) => ( ].map((stat, i) => (
<div key={i} className="text-center"> <div key={i} className="text-center">
<p className="text-3xl sm:text-4xl font-extrabold text-red-600">{stat.value}</p> <p className="text-3xl sm:text-4xl font-extrabold text-red-600">{stat.value}</p>
@@ -357,8 +357,8 @@ export default function LandingPage() {
{ icon: FileText, title: 'Einsatzjournal', desc: 'Journal mit Zeitstempel, SOMA-Checkliste und Pendenzenliste — alles an einem Ort.' }, { icon: FileText, title: 'Einsatzjournal', desc: 'Journal mit Zeitstempel, SOMA-Checkliste und Pendenzenliste — alles an einem Ort.' },
{ icon: Smartphone, title: 'Installierbare App (PWA)', desc: 'Auf dem Homescreen installierbar — funktioniert offline wie eine native App. Desktop, Tablet & Smartphone.' }, { icon: Smartphone, title: 'Installierbare App (PWA)', desc: 'Auf dem Homescreen installierbar — funktioniert offline wie eine native App. Desktop, Tablet & Smartphone.' },
{ icon: Users, title: 'Echtzeit-Zusammenarbeit', desc: 'Mehrere Benutzer können gleichzeitig am selben Lageplan arbeiten.' }, { icon: Users, title: 'Echtzeit-Zusammenarbeit', desc: 'Mehrere Benutzer können gleichzeitig am selben Lageplan arbeiten.' },
{ icon: Clock, title: 'Audit Trail', desc: 'Lückenlose Protokollierung aller Aktionen mit Zeitstempel für Nachvollziehbarkeit.' }, { icon: Clock, title: 'Nachvollziehbarkeit', desc: 'Protokollierung von Aktionen mit Zeitstempel für bessere Nachvollziehbarkeit.' },
{ icon: Lock, title: 'Sicher & Schweiz', desc: 'Rollenbasierte Zugriffskontrolle. Gehostet in der Schweiz — DSG/DSGVO-konform.' }, { icon: Lock, title: 'Sicherheit & Datenschutz', desc: 'Rollenbasierte Zugriffskontrolle und Mandantentrennung. Angemessene technische und organisatorische Schutzmassnahmen — Details in der Datenschutzerklärung.' },
{ icon: FileText, title: 'PDF-Export', desc: 'Lagepläne als PDF exportieren mit Metadaten, Datum und Einsatzinformationen.' }, { icon: FileText, title: 'PDF-Export', desc: 'Lagepläne als PDF exportieren mit Metadaten, Datum und Einsatzinformationen.' },
].map((f, i) => ( ].map((f, i) => (
<div key={i} className="bg-white rounded-xl p-6 border border-gray-100 hover:shadow-lg transition-shadow"> <div key={i} className="bg-white rounded-xl p-6 border border-gray-100 hover:shadow-lg transition-shadow">
@@ -439,7 +439,7 @@ export default function LandingPage() {
{[ {[
{ {
q: 'Was kostet Lageplan?', q: 'Was kostet Lageplan?',
a: 'Nichts. Lageplan ist kostenlos für alle Feuerwehren in der Schweiz. Die Entwicklung wird durch freiwillige Spenden finanziert.', a: 'Lageplan wird Feuerwehren grundsätzlich kostenlos zur Verfügung gestellt. Der Betrieb wird durch freiwillige Unterstützungsbeiträge mitfinanziert. Ein Anspruch darauf, dass alle Funktionen dauerhaft kostenlos bleiben, besteht nicht.',
}, },
{ {
q: 'Brauche ich eine Installation?', q: 'Brauche ich eine Installation?',
@@ -455,7 +455,7 @@ export default function LandingPage() {
}, },
{ {
q: 'Wo werden meine Daten gespeichert?', q: 'Wo werden meine Daten gespeichert?',
a: 'Alle Daten werden auf Servern in der Schweiz gespeichert. Die Applikation ist DSG- und DSGVO-konform.', a: 'Kontodaten werden in einer Datenbank gespeichert. Die eingesetzten Dienstleister und Speicherorte sind in der Datenschutzerklärung transparent aufgeführt. Lageplan orientiert sich am Schweizer Datenschutzgesetz (DSG).',
}, },
{ {
q: 'Welche Symbole sind verfügbar?', q: 'Welche Symbole sind verfügbar?',
@@ -508,7 +508,11 @@ export default function LandingPage() {
</div> </div>
<p className="text-sm text-gray-500">Digitale Lagepläne für die Schweizer Feuerwehr</p> <p className="text-sm text-gray-500">Digitale Lagepläne für die Schweizer Feuerwehr</p>
<p className="text-sm text-gray-500 mt-1">Ein Projekt von Pepe Ziberi</p> <p className="text-sm text-gray-500 mt-1">Ein Projekt von Pepe Ziberi</p>
<p className="text-sm text-gray-500">Gehostet in der Schweiz Swiss Made 🇨🇭</p> <p className="text-sm text-gray-500">In der Schweiz entwickelt Swiss Made 🇨🇭</p>
<p className="text-xs text-gray-400 mt-2 max-w-xs">
Unterstützendes Werkzeug ohne garantierte Verfügbarkeit. Kein Einsatzleit- oder
Alarmierungssystem. Angaben vor Verwendung fachlich prüfen.
</p>
</div> </div>
<div className="text-sm text-gray-500"> <div className="text-sm text-gray-500">
<p className="font-medium text-gray-700 mb-2">Kontakt</p> <p className="font-medium text-gray-700 mb-2">Kontakt</p>
@@ -526,6 +530,11 @@ export default function LandingPage() {
<p className="font-medium text-gray-700 mb-2">Rechtliches</p> <p className="font-medium text-gray-700 mb-2">Rechtliches</p>
<Link href="/impressum" className="block hover:text-gray-700">Impressum</Link> <Link href="/impressum" className="block hover:text-gray-700">Impressum</Link>
<Link href="/datenschutz" className="block hover:text-gray-700 mt-1">Datenschutz</Link> <Link href="/datenschutz" className="block hover:text-gray-700 mt-1">Datenschutz</Link>
<Link href="/nutzungsbedingungen" className="block hover:text-gray-700 mt-1">Nutzungsbedingungen</Link>
<Link href="/verantwortungsvolle-nutzung" className="block hover:text-gray-700 mt-1">Verantwortungsvolle Nutzung</Link>
<Link href="/sicherheit" className="block hover:text-gray-700 mt-1">Sicherheit</Link>
<Link href="/organisationen/datenschutzvereinbarung" className="block hover:text-gray-700 mt-1">Datenschutzvereinbarung (Org.)</Link>
<Link href="/unterstuetzen" className="block hover:text-gray-700 mt-1">Unterstützen</Link>
</div> </div>
</div> </div>
<div className="border-t border-gray-100 mt-8 pt-6 text-center space-y-1"> <div className="border-t border-gray-100 mt-8 pt-6 text-center space-y-1">
@@ -548,8 +557,8 @@ function SupportSection() {
<Coffee className="w-10 h-10 text-red-600 mx-auto mb-4" /> <Coffee className="w-10 h-10 text-red-600 mx-auto mb-4" />
<h2 className="text-3xl font-bold text-gray-900">Projekt unterstützen</h2> <h2 className="text-3xl font-bold text-gray-900">Projekt unterstützen</h2>
<p className="mt-3 text-lg text-gray-500 max-w-xl mx-auto"> <p className="mt-3 text-lg text-gray-500 max-w-xl mx-auto">
Lageplan ist und bleibt kostenlos. Wenn du die Weiterentwicklung unterstützen möchtest, Lageplan wird grundsätzlich kostenlos angeboten. Wenn du die Weiterentwicklung unterstützen
freue ich mich über einen freiwilligen Beitrag. möchtest, freue ich mich über einen freiwilligen Beitrag ohne Anspruch auf eine Gegenleistung.
</p> </p>
</div> </div>
@@ -571,7 +580,7 @@ function SupportSection() {
</div> </div>
<p className="text-gray-500 mb-6"> <p className="text-gray-500 mb-6">
Wähle einen Betrag auf unserer Spendenseite Zahlung sicher via Stripe (Kreditkarte, Twint, Apple Pay, Google Pay). Wähle einen Betrag auf der Unterstützungsseite Zahlung über Stripe (Kreditkarte, Twint, Apple Pay, Google Pay).
</p> </p>
<Link href="/spenden"> <Link href="/spenden">
@@ -585,8 +594,10 @@ function SupportSection() {
{/* Trust note */} {/* Trust note */}
<div className="mt-8 text-center"> <div className="mt-8 text-center">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
100% deiner Spende fliesst direkt in Serverkosten und Weiterentwicklung. Beiträge helfen, Hosting, Infrastruktur und Weiterentwicklung zu finanzieren.
<br />Kein Unternehmen, keine Investoren nur ein Feuerwehrmann mit einer Idee. Ein Beitrag ist freiwillig und erfolgt ohne Anspruch auf eine Gegenleistung; Gebühren des
Zahlungsanbieters können abgezogen werden.
<br />Ein Projekt aus der Feuerwehr-Praxis entwickelt in der Freizeit.
</p> </p>
</div> </div>
</div> </div>

View File

@@ -0,0 +1,59 @@
'use client'
import { LegalPage } from '@/components/legal/legal-page'
import { legal } from '@/config/legal'
export default function SicherheitPage() {
return (
<LegalPage
title="Sicherheit"
version={legal.documents.privacy.version}
publishedAt={legal.documents.privacy.publishedAt}
intro={`Übersicht über die technischen und organisatorischen Sicherheitsmassnahmen von ${legal.projectName}. Diese Darstellung ist allgemein gehalten; ein vollständiger Schutz vor allen Risiken kann nicht garantiert werden.`}
>
<section>
<h2>Grundsatz</h2>
<p>
Wir setzen angemessene technische und organisatorische Sicherheitsmassnahmen ein. Ein vollständiger
Schutz vor allen Risiken kann jedoch nicht garantiert werden. Sicherheit ist ein fortlaufender Prozess.
</p>
</section>
<section>
<h2>Technische Massnahmen (Auszug)</h2>
<ul>
<li><strong>Zugriffstrennung:</strong> Mandantentrennung je Organisation sowie ein Rollen- und Rechtekonzept (Server-Admin, Organisations-Admin, Bearbeiter, Betrachter).</li>
<li><strong>Passwörter:</strong> werden mit einem geeigneten Einwegverfahren (bcrypt) gehasht, nie im Klartext gespeichert.</li>
<li><strong>Übertragung:</strong> verschlüsselt über HTTPS/TLS; HSTS aktiviert.</li>
<li><strong>Sitzungen:</strong> httpOnly-Session-Cookie, in Produktion mit <code>secure</code>-Flag.</li>
<li><strong>Web-Härtung:</strong> Sicherheits-Header (u.a. Content-Security-Policy, <code>X-Content-Type-Options</code>, Referrer-Policy, Schutz vor Clickjacking über <code>frame-ancestors</code>).</li>
<li><strong>Missbrauchsschutz:</strong> Rate-Limiting für Login, Registrierung, Passwort-Reset, Kontaktformular und geteilte Ansichten.</li>
<li><strong>Uploads:</strong> Validierung und Zugriffsschutz für hochgeladene Dateien.</li>
</ul>
</section>
<section>
<h2>Organisatorische Massnahmen</h2>
<ul>
<li>Zugriff auf Systemdaten nur im betrieblich notwendigen Umfang.</li>
<li>Trennung von Entwicklungs-/Test- und Produktionsumgebung.</li>
<li>Regelmässige Aktualisierung von Abhängigkeiten.</li>
<li>Definierter Prozess für Sicherheitsvorfälle (siehe interne Dokumentation).</li>
</ul>
<p className="text-xs">
Der detaillierte, laufend gepflegte Ist-Zustand inkl. offener Punkte ist intern in{' '}
<code>docs/legal/technical-organizational-measures.md</code> dokumentiert.
</p>
</section>
<section>
<h2>Sicherheitslücke melden</h2>
<p>
Wenn Sie eine mögliche Sicherheitslücke entdecken, melden Sie diese bitte vertraulich an{' '}
<strong>{legal.supportEmail}</strong>. Bitte nutzen Sie gefundene Schwachstellen nicht aus und geben Sie
uns angemessene Zeit zur Behebung.
</p>
</section>
</LegalPage>
)
}

View File

@@ -46,5 +46,10 @@ export default function sitemap(): MetadataRoute.Sitemap {
changeFrequency: 'yearly', changeFrequency: 'yearly',
priority: 0.3, priority: 0.3,
}, },
{ url: `${baseUrl}/nutzungsbedingungen`, lastModified: new Date(), changeFrequency: 'yearly', priority: 0.3 },
{ url: `${baseUrl}/verantwortungsvolle-nutzung`, lastModified: new Date(), changeFrequency: 'yearly', priority: 0.3 },
{ url: `${baseUrl}/sicherheit`, lastModified: new Date(), changeFrequency: 'yearly', priority: 0.3 },
{ url: `${baseUrl}/unterstuetzen`, lastModified: new Date(), changeFrequency: 'yearly', priority: 0.3 },
{ url: `${baseUrl}/organisationen/datenschutzvereinbarung`, lastModified: new Date(), changeFrequency: 'yearly', priority: 0.3 },
] ]
} }

View File

@@ -96,7 +96,7 @@ export default function SpendenPage() {
<div> <div>
<h3 className="font-bold text-gray-900 mb-2">Wohin fliesst deine Spende?</h3> <h3 className="font-bold text-gray-900 mb-2">Wohin fliesst deine Spende?</h3>
<ul className="space-y-2 text-sm text-gray-600"> <ul className="space-y-2 text-sm text-gray-600">
<li className="flex items-center gap-2"><Shield className="w-4 h-4 text-red-500 shrink-0" /> Server-Hosting in der Schweiz (monatliche Kosten)</li> <li className="flex items-center gap-2"><Shield className="w-4 h-4 text-red-500 shrink-0" /> Server-Hosting und Infrastruktur (monatliche Kosten)</li>
<li className="flex items-center gap-2"><Sparkles className="w-4 h-4 text-red-500 shrink-0" /> Entwicklung neuer Features (PWA, Vorlagen, Einsatzpläne)</li> <li className="flex items-center gap-2"><Sparkles className="w-4 h-4 text-red-500 shrink-0" /> Entwicklung neuer Features (PWA, Vorlagen, Einsatzpläne)</li>
<li className="flex items-center gap-2"><Coffee className="w-4 h-4 text-red-500 shrink-0" /> Domain, SSL-Zertifikate und Infrastruktur</li> <li className="flex items-center gap-2"><Coffee className="w-4 h-4 text-red-500 shrink-0" /> Domain, SSL-Zertifikate und Infrastruktur</li>
</ul> </ul>
@@ -223,12 +223,24 @@ export default function SpendenPage() {
)} )}
<div className="mt-4 flex flex-wrap items-center justify-center gap-3 text-xs text-gray-400"> <div className="mt-4 flex flex-wrap items-center justify-center gap-3 text-xs text-gray-400">
<span className="flex items-center gap-1"><Shield className="w-3.5 h-3.5" /> Sichere Zahlung via Stripe</span> <span className="flex items-center gap-1"><Shield className="w-3.5 h-3.5" /> Zahlung über Stripe</span>
<span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Kreditkarte</span> <span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Kreditkarte</span>
<span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Twint</span> <span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Twint</span>
<span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Apple Pay</span> <span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Apple Pay</span>
<span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Google Pay</span> <span className="flex items-center gap-1"><Check className="w-3.5 h-3.5" /> Google Pay</span>
</div> </div>
{/* Rechtlicher Hinweis zum freiwilligen Beitrag */}
<div className="mt-5 rounded-lg bg-gray-50 border border-gray-100 p-4 text-xs text-gray-500 leading-relaxed">
<strong className="text-gray-700">Freiwilliger Unterstützungsbeitrag.</strong> Ein Beitrag ist
freiwillig und erfolgt ohne Anspruch auf eine Gegenleistung. Daraus entsteht insbesondere kein
Anspruch auf Funktionen, Support, Verfügbarkeit, bevorzugte Behandlung oder eine bestimmte
Weiterentwicklung. Zahlungs- und Transaktionsgebühren des Zahlungsanbieters (Stripe) können vom
Beitrag abgezogen werden. Es wird keine steuerlich abzugsfähige Spendenbescheinigung ausgestellt,
solange keine entsprechende behördliche Anerkennung besteht. Es handelt sich um eine einmalige
Zahlung; es wird keine wiederkehrende Zahlung eingerichtet. Details zur Zahlungsabwicklung siehe{' '}
<Link href="/datenschutz" className="text-red-600 underline">Datenschutzerklärung</Link>.
</div>
</div> </div>
{/* Story */} {/* Story */}
@@ -236,21 +248,22 @@ export default function SpendenPage() {
<h3 className="font-bold text-gray-900 mb-3">Die Geschichte hinter Lageplan</h3> <h3 className="font-bold text-gray-900 mb-3">Die Geschichte hinter Lageplan</h3>
<p className="text-sm text-gray-600 leading-relaxed"> <p className="text-sm text-gray-600 leading-relaxed">
Lageplan wird seit über 2 Jahren von einem aktiven Feuerwehrmann in seiner Freizeit entwickelt. Lageplan wird seit über 2 Jahren von einem aktiven Feuerwehrmann in seiner Freizeit entwickelt.
Die App ist und bleibt <strong>kostenlos</strong> weil jede Feuerwehr Zugang zu guten Werkzeugen haben soll, Die App wird <strong>grundsätzlich kostenlos</strong> angeboten weil jede Feuerwehr Zugang zu guten
unabhängig vom Budget. Es gibt zwar kommerzielle Alternativen, aber die Idee war immer: Werkzeugen haben soll, unabhängig vom Budget. Die Idee war immer:
Ein Werkzeug <em>von der Feuerwehr, für die Feuerwehr</em>. Ein Werkzeug <em>von der Feuerwehr, für die Feuerwehr</em>.
</p> </p>
<p className="text-sm text-gray-600 leading-relaxed mt-3"> <p className="text-sm text-gray-600 leading-relaxed mt-3">
Mit deiner Spende hilfst du, dass das so bleibt. Jeder Franken fliesst direkt in Beiträge helfen, Hosting, Infrastruktur und Weiterentwicklung mitzufinanzieren. Danke für deine
Serverkosten und Weiterentwicklung. Danke für deine Unterstützung! Unterstützung!
</p> </p>
</div> </div>
{/* Trust */} {/* Trust */}
<div className="mt-8 text-center"> <div className="mt-8 text-center">
<p className="text-sm text-gray-500"> <p className="text-sm text-gray-500">
100% deiner Spende fliesst direkt in Serverkosten und Weiterentwicklung. Beiträge werden nach Abzug allfälliger Zahlungsgebühren für Hosting, Infrastruktur und
<br />Kein Unternehmen, keine Investoren nur ein Feuerwehrmann mit einer Idee. Weiterentwicklung eingesetzt.
<br />Ein Projekt aus der Feuerwehr-Praxis entwickelt in der Freizeit.
</p> </p>
<div className="mt-6"> <div className="mt-6">
<Link href="/"> <Link href="/">

View File

@@ -0,0 +1,55 @@
'use client'
import Link from 'next/link'
import { Heart } from 'lucide-react'
import { LegalPage } from '@/components/legal/legal-page'
import { legal } from '@/config/legal'
export default function UnterstuetzenPage() {
return (
<LegalPage
title="Freiwilliger Unterstützungsbeitrag"
version={legal.documents.terms.version}
publishedAt={legal.documents.terms.publishedAt}
draft={false}
intro={`${legal.projectName} wird grundsätzlich kostenlos angeboten. Freiwillige Beiträge helfen bei der Finanzierung von Hosting, Infrastruktur und Weiterentwicklung.`}
>
<section>
<p>
Ein Beitrag ist <strong>freiwillig</strong> und erfolgt ohne Anspruch auf eine bestimmte Gegenleistung.
Daraus entsteht insbesondere kein Anspruch auf Funktionen, Support, Verfügbarkeit, bevorzugte Behandlung
oder eine bestimmte Weiterentwicklung.
</p>
<p>
Zahlungs- und Transaktionsgebühren des eingesetzten Zahlungsanbieters (Stripe) können vom Beitrag
abgezogen werden. {legal.projectName} stellt keine steuerlich abzugsfähige Spendenbescheinigung aus,
solange keine entsprechende behördliche Anerkennung besteht.
</p>
<p>
Es handelt sich um eine <strong>einmalige Zahlung</strong>; es wird keine automatisch wiederkehrende
Zahlung eingerichtet. Angaben zur Zahlungsabwicklung enthält die{' '}
<a href="/datenschutz">Datenschutzerklärung</a>.
</p>
</section>
<section>
<h2>Beitrag leisten</h2>
<p>Auf der Zahlungsseite kannst du einen Betrag wählen. Die Zahlung erfolgt über Stripe.</p>
<Link
href="/spenden"
className="not-prose inline-flex items-center gap-2 rounded-lg bg-red-600 hover:bg-red-700 text-white px-5 py-2.5 font-semibold no-underline print:hidden"
>
<Heart className="w-4 h-4" /> Zur Zahlungsseite
</Link>
</section>
<section>
<h2>Versehentliche Zahlung</h2>
<p>
Falls eine Zahlung versehentlich erfolgt ist, melde dich bitte unter <strong>{legal.supportEmail}</strong>{' '}
zur Prüfung einer Rückerstattung.
</p>
</section>
</LegalPage>
)
}

View File

@@ -0,0 +1,66 @@
'use client'
import { LegalPage } from '@/components/legal/legal-page'
import { legal } from '@/config/legal'
export default function VerantwortungsvolleNutzungPage() {
const d = legal.documents.responsibleUse
return (
<LegalPage
title="Verantwortungsvolle Nutzung"
version={d.version}
publishedAt={d.publishedAt}
intro={`${legal.projectName} ist ein unterstützendes Werkzeug. Diese Seite erklärt, wie es sicher und verantwortungsvoll eingesetzt wird — damit es im Einsatz zu keinen Missverständnissen kommt.`}
>
<section>
<h2>Was {legal.projectName} ist und was nicht</h2>
<p>
{legal.projectName} hilft beim Erstellen, Verwalten, Visualisieren und Exportieren von Lage- und
Objektplänen sowie bei unterstützenden Berechnungen. Es ist <strong>kein Alarmierungssystem, keine
Leitstelle und kein Einsatzleitsystem</strong> und ersetzt keine behördlich vorgeschriebenen Unterlagen.
</p>
</section>
<section>
<h2>Grundregeln für den Einsatz</h2>
<ul>
<li>Haltet unabhängig von {legal.projectName} <strong>aktuelle Offline- und Notfallunterlagen</strong> bereit.</li>
<li>Verwendet {legal.projectName} <strong>nicht als einzige Informationsquelle</strong> für operative Entscheidungen.</li>
<li>Lasst Pläne, Adressen, Symbole, Distanzen und Berechnungen vor der operativen Verwendung durch <strong>fachkundige Personen prüfen</strong>.</li>
<li>Rechnet mit möglichen Störungen oder Nichtverfügbarkeit es besteht keine garantierte Verfügbarkeit und kein SLA.</li>
<li>Erstellt regelmässig <strong>Exporte</strong> wichtiger Pläne als Rückfallebene.</li>
</ul>
</section>
<section>
<h2>Umgang mit sensiblen Daten</h2>
<p>
Speichert ohne separate schriftliche Vereinbarung <strong>keine besonders schützenswerten Daten</strong>
{' '}(z.B. Patienten-, Gesundheits-, Polizei-, Passwort-, Zugangs- oder Schlüsselcode-Daten). Auch reine
Objektinformationen können sicherheitsrelevant sein und dürfen nur berechtigten Personen zugänglich
gemacht werden. Details siehe <a href="/nutzungsbedingungen">Nutzungsbedingungen</a> und{' '}
<a href="/datenschutz">Datenschutzerklärung</a>.
</p>
</section>
<section>
<h2>Aktualität von Karten und Berechnungen</h2>
<p>
Karten-, Adress- und Geodaten können unvollständig, ungenau oder veraltet sein. Berechnungen (z.B.
Distanzen, Druckverlust, Höhen) sind <strong>Richtwerte</strong> und ersetzen keine Lagebeurteilung.
Änderungen an Gebäuden, Zufahrten, Wasserbezugsstellen oder Gefahren müssen durch die Organisation
gepflegt werden.
</p>
</section>
<section>
<h2>Warum das wichtig ist</h2>
<p>
{legal.projectName} wird mit viel Eigenleistung entwickelt und Feuerwehren kostenlos zur Verfügung
gestellt. Eine verantwortungsvolle Nutzung sorgt dafür, dass das Projekt langfristig sicher und
kostenlos angeboten werden kann und dass im Ernstfall keine falschen Erwartungen entstehen.
</p>
</section>
</LegalPage>
)
}

View File

@@ -239,7 +239,7 @@ export function SettingsTab({ onNavigateTab }: SettingsTabProps) {
<Mail className="w-5 h-5 text-primary" /> <Mail className="w-5 h-5 text-primary" />
E-Mail / SMTP Konfiguration E-Mail / SMTP Konfiguration
</h3> </h3>
<p className="text-sm text-muted-foreground mb-4">SMTP-Server für den E-Mail-Versand konfigurieren. Empfohlen: TLS auf Port 587. Passwörter werden verschlüsselt in der Datenbank gespeichert.</p> <p className="text-sm text-muted-foreground mb-4">SMTP-Server für den E-Mail-Versand konfigurieren. Empfohlen: TLS auf Port 587. Zugangsdaten werden serverseitig gespeichert und im Interface maskiert.</p>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4"> <div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div><Label>SMTP Host</Label><Input value={smtpHost} onChange={e => setSmtpHost(e.target.value)} placeholder="smtp.gmail.com" /></div> <div><Label>SMTP Host</Label><Input value={smtpHost} onChange={e => setSmtpHost(e.target.value)} placeholder="smtp.gmail.com" /></div>
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">

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>
)
}

164
src/config/legal.ts Normal file
View File

@@ -0,0 +1,164 @@
/**
* Zentrale, typisierte Betreiber- und Rechtskonfiguration für Lageplan.
*
* WARUM: Rechtliche Angaben (Betreiber, Kontakt, Serverstandort, Dokumentversionen)
* dürfen NICHT an mehreren Stellen widersprüchlich hartcodiert werden. Alle Legal-Seiten,
* das Impressum, die Datenschutzerklärung, der Consent-Flow und die Export-Footer lesen
* ihre Angaben aus dieser einen Quelle.
*
* PLATZHALTER: Felder, die der Betreiber rechtlich verbindlich festlegen muss, tragen als
* Default einen deutlich sichtbaren Platzhalter im Format `[NAME]`. Diese Platzhalter dürfen
* NICHT stillschweigend durch erfundene Werte ersetzt werden. Setze die echten Werte über
* Umgebungsvariablen (siehe unten) oder trage sie hier direkt ein, sobald sie feststehen.
*
* VALIDIERUNG: `assertLegalConfig()` warnt in Development und beim Build, wenn Pflichtangaben
* noch Platzhalter sind. In Produktion werden Platzhalter in der UI als solche kenntlich
* gemacht (siehe `isPlaceholder()`), damit nie ein erfundener Wert als echt erscheint.
*/
/** Präfix, an dem ein noch nicht ausgefüllter Pflichtwert erkannt wird. */
const PLACEHOLDER_PREFIX = '['
/** True, wenn der Wert noch ein unausgefüllter Platzhalter ist. */
export function isPlaceholder(value: string | null | undefined): boolean {
return !value || (value.trim().startsWith(PLACEHOLDER_PREFIX) && value.trim().endsWith(']'))
}
/** Liest eine Umgebungsvariable, fällt sonst auf den Platzhalter/Default zurück. */
function env(name: string, fallback: string): string {
const v = process.env[name]
return v && v.trim().length > 0 ? v.trim() : fallback
}
export interface ExternalProcessor {
/** Anzeigename des Dienstleisters */
name: string
/** Zweck der Bearbeitung */
purpose: string
/** Verarbeitete Datenarten (grob) */
data: string
/** Sitz / relevante Verarbeitungsregion, soweit bekannt; sonst Platzhalter */
region: string
/** true, wenn der Dienst nur bei Bedarf/optionaler Konfiguration aktiv ist */
optional?: boolean
}
export interface LegalDocumentMeta {
/** Interne, bewusst gesetzte Versionsangabe (nicht automatisch aus Dateiänderung ableiten!) */
version: string
/** Veröffentlichungsdatum ISO (YYYY-MM-DD) */
publishedAt: string
}
export const legal = {
// ─── Betreiber ─────────────────────────────────────────────
/** Anzeigename des Projekts */
projectName: 'Lageplan',
/**
* Rechtlich verbindlicher Name des Betreibers. Im Repository wird das Projekt öffentlich
* "Pepe Ziberi / Purepixel" zugeschrieben; die rechtsverbindliche Bezeichnung inkl.
* Rechtsform muss der Betreiber bestätigen.
*/
operatorName: env('LEGAL_OPERATOR_NAME', '[RECHTLICHER NAME DES BETREIBERS]'),
/** Rechtsform (z.B. Einzelunternehmen, Verein, GmbH) muss festgelegt werden */
operatorLegalForm: env('LEGAL_OPERATOR_FORM', '[RECHTSFORM]'),
/** Vollständige Postadresse des Betreibers */
postalAddress: env('LEGAL_POSTAL_ADDRESS', '[POSTADRESSE]'),
// ─── Kontakt ───────────────────────────────────────────────
/** Allgemeine Support-/Kontaktadresse (im Repo öffentlich verwendet) */
supportEmail: env('LEGAL_SUPPORT_EMAIL', 'app@lageplan.ch'),
/**
* Datenschutz-Kontakt. Default = im Repository bereits verwendete Adresse
* (nicht erfunden). Der Betreiber muss bestätigen, dass dieses Postfach aktiv überwacht wird.
*/
privacyContactEmail: env('LEGAL_PRIVACY_EMAIL', 'datenschutz@lageplan.ch'),
// ─── Domains ───────────────────────────────────────────────
siteDomain: env('LEGAL_SITE_DOMAIN', 'lageplan.ch'),
appDomain: env('LEGAL_APP_DOMAIN', 'app.lageplan.ch'),
// ─── Recht & Gerichtsstand ────────────────────────────────
country: 'Schweiz',
governingLaw: 'Schweizer Recht',
/** Gerichtsstand nur angeben, wenn juristisch sinnvoll/bestätigt; sonst Platzhalter. */
jurisdiction: env('LEGAL_JURISDICTION', '[GERICHTSSTAND]'),
// ─── Infrastruktur ────────────────────────────────────────
/** Hostinganbieter muss bestätigt werden (im Repo nicht eindeutig belegt) */
hostingProvider: env('LEGAL_HOSTING_PROVIDER', '[HOSTINGANBIETER]'),
/** Serverstandort muss für ALLE Anbieter, Backups und Logs bestätigt werden */
serverLocation: env('LEGAL_SERVER_LOCATION', '[SERVERSTANDORT]'),
/** Aufbewahrungsfrist von Sicherungskopien muss festgelegt werden */
backupRetention: env('LEGAL_BACKUP_RETENTION', '[BACKUP-AUFBEWAHRUNGSFRIST]'),
// ─── Externe Dienstleister (Ist-Zustand aus dem Code abgeleitet) ──
// Nichts erfinden: nur tatsächlich im Code kontaktierte Dienste. Sitz/Region, die nicht
// sicher bekannt ist, bleibt Platzhalter.
processors: [
{ name: 'OpenStreetMap (Tiles)', purpose: 'Basiskarte', data: 'Kachel-Koordinaten (Kartenausschnitt)', region: '[REGION OSM Foundation, EU/UK]' },
{ name: 'Esri ArcGIS World Imagery', purpose: 'Satellitenkarte', data: 'Kachel-Koordinaten', region: '[REGION]' },
{ name: 'swisstopo (geo.admin.ch)', purpose: 'Schweizer Landeskarte', data: 'Kachel-Koordinaten', region: 'Schweiz' },
{ name: 'Nominatim (OpenStreetMap)', purpose: 'Adress-/Reverse-Geocoding', data: 'Adress-/Ortssuche, GPS-Koordinaten', region: '[REGION OSM Foundation]' },
{ name: 'Open-Meteo', purpose: 'Wind- und Höhendaten', data: 'Koordinaten', region: '[REGION EU]' },
{ name: 'Open-Elevation', purpose: 'Höhendaten (Fallback)', data: 'Koordinaten', region: '[REGION]' },
{ name: 'Stripe', purpose: 'Zahlungsabwicklung freiwilliger Unterstützungsbeiträge', data: 'Betrag, freiwilliger Name/Nachricht, Zahlungsdaten (direkt bei Stripe)', region: '[REGION u.a. USA/EU; Auslandbearbeitung möglich]', optional: true },
{ name: 'SMTP-/E-Mail-Anbieter', purpose: 'System-E-Mails (Verifizierung, Reset, Kontakt, Einladungen)', data: 'E-Mail-Adresse, Nachrichteninhalt', region: '[REGION abhängig vom konfigurierten Anbieter]', optional: true },
{ name: 'MinIO (Objektspeicher)', purpose: 'Datei-/Bild-Uploads (Pläne, Logos, Symbole)', data: 'hochgeladene Dateien', region: '[SERVERSTANDORT]' },
{ name: 'PostgreSQL (Datenbank)', purpose: 'Primäre Datenhaltung', data: 'Konto-, Organisations-, Plan- und Journaldaten', region: '[SERVERSTANDORT]' },
] as ExternalProcessor[],
// ─── Rechtsdokumente: bewusst gesetzte Versionen ──────────
// WICHTIG: Version manuell erhöhen, wenn sich der Inhalt WESENTLICH ändert (löst Re-Consent aus).
// Nicht automatisch aus minimalen Dateiänderungen ableiten.
documents: {
terms: { version: '1.0.0-draft', publishedAt: '2026-07-23' },
privacy: { version: '1.0.0-draft', publishedAt: '2026-07-23' },
organizationDeclaration: { version: '1.0.0-draft', publishedAt: '2026-07-23' },
responsibleUse: { version: '1.0.0-draft', publishedAt: '2026-07-23' },
dataProcessingAgreement: { version: '1.0.0-draft', publishedAt: '2026-07-23' },
} as Record<string, LegalDocumentMeta>,
/** Hinweis, der auf allen sorgfältig erstellten, aber juristisch ungeprüften Texten steht. */
draftNotice:
'Diese rechtlichen und organisatorischen Unterlagen wurden nach bestem Wissen erstellt. ' +
'Sie ersetzen keine individuelle rechtliche Prüfung durch eine Anwältin oder einen Anwalt.',
} as const
/** Pflichtfelder, die vor Produktivbetrieb ausgefüllt sein müssen. */
const REQUIRED_FIELDS: { key: string; value: string }[] = [
{ key: 'operatorName', value: legal.operatorName },
{ key: 'operatorLegalForm', value: legal.operatorLegalForm },
{ key: 'postalAddress', value: legal.postalAddress },
{ key: 'privacyContactEmail', value: legal.privacyContactEmail },
{ key: 'hostingProvider', value: legal.hostingProvider },
{ key: 'serverLocation', value: legal.serverLocation },
{ key: 'backupRetention', value: legal.backupRetention },
]
/** Liste der noch offenen Pflichtangaben (Platzhalter). */
export function missingLegalFields(): string[] {
return REQUIRED_FIELDS.filter(f => isPlaceholder(f.value)).map(f => f.key)
}
/**
* Warnt beim Build/Start, wenn Pflichtangaben noch Platzhalter sind.
* Bricht den Build NICHT ab (damit Entwicklung möglich bleibt), macht das Risiko aber sichtbar.
*/
export function assertLegalConfig(): void {
const missing = missingLegalFields()
if (missing.length > 0) {
const msg = `⚠️ [legal.ts] Offene rechtliche Pflichtangaben (noch Platzhalter): ${missing.join(', ')}. ` +
`Bitte in src/config/legal.ts bzw. via Umgebungsvariablen (LEGAL_*) setzen, bevor produktiv gegangen wird.`
// In Produktion als Fehler-Log, in Dev als Warnung — nie stillschweigend.
if (process.env.NODE_ENV === 'production') console.error(msg)
else console.warn(msg)
}
}
// Beim Laden der Konfiguration einmalig prüfen (serverseitig).
if (typeof window === 'undefined') {
assertLegalConfig()
}
export type LegalConfig = typeof legal