feat(legal): redesign Impressum and Datenschutz with PawFeed branding

This commit is contained in:
admin committed 2026-06-21 21:46:30 +02:00
1 parent ac19d0fd2c
commit 195df0154d
3 files changed
+285 -162

No files matched your search

+126 -89
View File
@@ -1,6 +1,8 @@
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { LegalLayout, LegalSection } from "@/components/legal/LegalLayout";
function decode(b64: string): string {
try {
@@ -10,112 +12,147 @@ function decode(b64: string): string {
}
}
const IMP_NAME = "RGFuaWVsIEJhcmFjIE9ubGluZWRpZW5zdGU=";
const IMP_NAME = "RGFuaWVsIEJhcmFjIE9ubGluZWRpZW5zdGU=";
const IMP_STREET = "QWxwZW52ZWlsY2hlbndlZyA3";
const IMP_CITY = "ODIxNDAgT2xjaGluZw==";
const IMP_EMAIL = "d2VibWFzdGVyQHBhd2ZlZWQub3Jn";
const IMP_CITY = "ODIxNDAgT2xjaGluZw==";
const IMP_EMAIL = "d2VibWFzdGVyQHBhd2ZlZWQub3Jn";
export default function DatenschutzPage() {
const [revealed, setRevealed] = useState(false);
useEffect(() => { setRevealed(true); }, []);
useEffect(() => {
setRevealed(true);
}, []);
const name = revealed ? decode(IMP_NAME) : "––";
const name = revealed ? decode(IMP_NAME) : "––";
const street = revealed ? decode(IMP_STREET) : "––";
const city = revealed ? decode(IMP_CITY) : "––";
const email = revealed ? decode(IMP_EMAIL) : "––";
const city = revealed ? decode(IMP_CITY) : "––";
const email = revealed ? decode(IMP_EMAIL) : "––";
const emailLink = revealed ? (
<a href={`mailto:${email}`} className="text-orange-600 dark:text-orange-400 hover:underline">
{email}
</a>
) : <>––</>;
return (
<div className="max-w-2xl mx-auto px-4 py-12 prose prose-sm dark:prose-invert">
<h1>Datenschutzerklärung</h1>
<LegalLayout title="Datenschutzerklärung" subtitle="Stand: Juni 2026 · gemäß DSGVO">
<LegalSection number="1" title="Verantwortlicher">
<address className="not-italic space-y-0.5">
<p className="font-medium text-foreground">{name}</p>
<p>{street}</p>
<p>{city}</p>
<p>E-Mail: {emailLink}</p>
</address>
</LegalSection>
<h2>1. Verantwortlicher</h2>
<address className="not-italic">
<p>{name}</p>
<p>{street}</p>
<p>{city}</p>
{revealed ? (
<p>E-Mail: <a href={`mailto:${email}`}>{email}</a></p>
) : (
<p>E-Mail: ––</p>
)}
</address>
<LegalSection number="2" title="Erhebung und Verarbeitung personenbezogener Daten">
<p>Wir erheben und verarbeiten personenbezogene Daten nur, soweit dies zur Bereitstellung unserer Plattform erforderlich ist oder Sie uns Daten freiwillig mitgeteilt haben:</p>
<ul className="list-disc pl-4 space-y-1 mt-2">
<li>Kontodaten (E-Mail-Adresse, Passwort-Hash) zur Authentifizierung</li>
<li>Profilangaben (Name des Haustiers, Tierart, Fotos, Biografie)</li>
<li>Von Ihnen erstellte Inhalte (Beiträge, Kommentare, Nachrichten)</li>
<li>Nutzungsdaten (IP-Adresse, Zeitstempel, Seitenaufrufe)</li>
</ul>
</LegalSection>
<h2>2. Erhebung und Verarbeitung personenbezogener Daten</h2>
<p>
Wir erheben und verarbeiten personenbezogene Daten nur, soweit dies zur Bereitstellung
unserer Plattform erforderlich ist oder Sie uns Daten freiwillig mitgeteilt haben.
</p>
<ul>
<li>Kontodaten (E-Mail-Adresse, Passwort-Hash) zur Authentifizierung</li>
<li>Profilangaben (Name des Haustiers, Tierart, Fotos, Biografie)</li>
<li>Von Ihnen erstellte Inhalte (Beiträge, Kommentare, Nachrichten)</li>
<li>Nutzungsdaten (IP-Adresse, Zeitstempel, Seitenaufrufe)</li>
</ul>
<LegalSection number="3" title="Rechtsgrundlagen der Verarbeitung">
<p>
Die Verarbeitung personenbezogener Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b
DSGVO (Vertragserfüllung) sowie Art. 6 Abs. 1 lit. f DSGVO (berechtigte Interessen,
insbesondere Betrieb und Sicherheit der Plattform).
</p>
</LegalSection>
<h2>3. Rechtsgrundlagen der Verarbeitung</h2>
<p>
Die Verarbeitung personenbezogener Daten erfolgt auf Grundlage von Art. 6 Abs. 1 lit. b
DSGVO (Vertragserfüllung) sowie Art. 6 Abs. 1 lit. f DSGVO (berechtigte Interessen,
insbesondere Betrieb und Sicherheit der Plattform).
</p>
<LegalSection number="4" title="Drittanbieter und Auftragsverarbeiter">
<ul className="space-y-2">
{[
{ name: "Clerk Inc.", detail: "Authentifizierungsdienst (USA) · Datenübertragung auf Basis von EU-SCC" },
{ name: "Supabase Inc.", detail: "Datenbankhosting (AWS EU-West) · AVV abgeschlossen" },
{ name: "Cloudflare Inc.", detail: "Medienspeicherung und CDN (USA/EU) · AVV abgeschlossen" },
{ name: "Mux Inc.", detail: "Videoverarbeitung (USA) · SCC-Basis" },
].map((p) => (
<li key={p.name} className="flex flex-col gap-0.5">
<span className="font-medium text-foreground">{p.name}</span>
<span>{p.detail}</span>
</li>
))}
</ul>
</LegalSection>
<h2>4. Drittanbieter und Auftragsverarbeiter</h2>
<ul>
<li><strong>Clerk Inc.</strong> – Authentifizierungsdienst (USA). Datenübertragung auf Basis von EU-SCC.</li>
<li><strong>Supabase Inc.</strong> – Datenbankhosting (AWS EU-West). AVV abgeschlossen.</li>
<li><strong>Cloudflare Inc.</strong> – Medienspeicherung und CDN (USA/EU). AVV abgeschlossen.</li>
<li><strong>Mux Inc.</strong> – Videoverarbeitung (USA). SCC-Basis.</li>
</ul>
<LegalSection number="5" title="Cookies und Tracking">
<p>
Wir verwenden technisch notwendige Cookies zur Sitzungsverwaltung. Tracking-Cookies oder
Cookies zu Werbezwecken werden ohne Ihre ausdrückliche Einwilligung nicht gesetzt.
</p>
</LegalSection>
<h2>5. Cookies und Tracking</h2>
<p>
Wir verwenden technisch notwendige Cookies zur Sitzungsverwaltung. Tracking-Cookies oder
Cookies zu Werbezwecken werden ohne Ihre ausdrückliche Einwilligung nicht gesetzt.
</p>
<LegalSection number="6" title="Speicherdauer">
<p>
Personenbezogene Daten werden gelöscht, sobald der Zweck der Verarbeitung entfällt und
keine gesetzlichen Aufbewahrungspflichten entgegenstehen. Kontodaten werden nach
Löschung des Kontos innerhalb von 30 Tagen gelöscht.
</p>
</LegalSection>
<h2>6. Speicherdauer</h2>
<p>
Personenbezogene Daten werden gelöscht, sobald der Zweck der Verarbeitung entfällt und
keine gesetzlichen Aufbewahrungspflichten entgegenstehen. Kontodaten werden nach Löschung
des Kontos innerhalb von 30 Tagen gelöscht.
</p>
<LegalSection number="7" title="Ihre Rechte">
<ul className="grid grid-cols-2 gap-1.5">
{[
"Auskunft (Art. 15 DSGVO)",
"Berichtigung (Art. 16 DSGVO)",
"Löschung (Art. 17 DSGVO)",
"Einschränkung (Art. 18 DSGVO)",
"Datenübertragbarkeit (Art. 20 DSGVO)",
"Widerspruchsrecht (Art. 21 DSGVO)",
].map((right) => (
<li key={right} className="flex items-start gap-1.5">
<span className="mt-1 w-1.5 h-1.5 rounded-full bg-orange-500 flex-shrink-0" />
{right}
</li>
))}
</ul>
<p className="mt-3">Zur Ausübung Ihrer Rechte wenden Sie sich per E-Mail an {emailLink}.</p>
</LegalSection>
<h2>7. Ihre Rechte</h2>
<ul>
<li>Auskunft (Art. 15 DSGVO)</li>
<li>Berichtigung (Art. 16 DSGVO)</li>
<li>Löschung (Art. 17 DSGVO)</li>
<li>Einschränkung der Verarbeitung (Art. 18 DSGVO)</li>
<li>Datenübertragbarkeit (Art. 20 DSGVO)</li>
<li>Widerspruchsrecht (Art. 21 DSGVO)</li>
</ul>
<p>
Zur Ausübung Ihrer Rechte wenden Sie sich per E-Mail an{" "}
{revealed ? <a href={`mailto:${email}`}>{email}</a> : "––"}.
</p>
<LegalSection number="8" title="Beschwerderecht">
<p>
Sie haben das Recht, sich bei einer Datenschutz-Aufsichtsbehörde zu beschweren.
Zuständig ist das Bayerische Landesamt für Datenschutzaufsicht (BayLDA),{" "}
<a
href="https://www.lda.bayern.de"
target="_blank"
rel="noopener noreferrer"
className="text-orange-600 dark:text-orange-400 hover:underline"
>
www.lda.bayern.de
</a>
.
</p>
</LegalSection>
<h2>8. Beschwerderecht</h2>
<p>
Sie haben das Recht, sich bei einer Datenschutz-Aufsichtsbehörde zu beschweren. Zuständig
ist das Bayerische Landesamt für Datenschutzaufsicht (BayLDA),{" "}
<a href="https://www.lda.bayern.de" target="_blank" rel="noopener noreferrer">
www.lda.bayern.de
</a>
.
</p>
<LegalSection number="9" title="Minderjährigenschutz">
<p>
Unser Angebot richtet sich nicht an Personen unter 13 Jahren. Wir erheben wissentlich
keine personenbezogenen Daten von Kindern unter 13 Jahren.
</p>
</LegalSection>
<h2>9. Minderjährigenschutz</h2>
<p>
Unser Angebot richtet sich nicht an Personen unter 13 Jahren. Wir erheben wissentlich keine
personenbezogenen Daten von Kindern unter 13 Jahren.
</p>
<LegalSection number="10" title="Aktualität dieser Erklärung">
<p>
Stand: Juni 2026. Durch die Weiterentwicklung unserer Plattform kann es notwendig
werden, diese Erklärung zu ändern. Die jeweils aktuelle Fassung ist stets auf dieser
Seite abrufbar.
</p>
</LegalSection>
<h2>10. Aktualität dieser Erklärung</h2>
<p>Stand: Juni 2026. Durch die Weiterentwicklung unserer Plattform kann es notwendig werden,
diese Erklärung zu ändern.</p>
</div>
<div className="bg-card border rounded-xl p-6 flex items-center justify-between">
<p className="text-sm text-muted-foreground">Weitere rechtliche Informationen</p>
<div className="flex gap-4">
<Link href="/impressum" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
Impressum
</Link>
<Link href="/nutzungsbedingungen" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
AGB
</Link>
</div>
</div>
</LegalLayout>
);
}
+84 -73
View File
@@ -1,6 +1,8 @@
"use client";
import { useEffect, useState } from "react";
import Link from "next/link";
import { LegalLayout, LegalSection } from "@/components/legal/LegalLayout";
function decode(b64: string): string {
try {
@@ -10,95 +12,104 @@ function decode(b64: string): string {
}
}
// Base64-encoded contact data — decoded client-side only; crawlers see placeholders.
const IMP_NAME = "RGFuaWVsIEJhcmFjIE9ubGluZWRpZW5zdGU=";
const IMP_NAME = "RGFuaWVsIEJhcmFjIE9ubGluZWRpZW5zdGU=";
const IMP_STREET = "QWxwZW52ZWlsY2hlbndlZyA3";
const IMP_CITY = "ODIxNDAgT2xjaGluZw==";
const IMP_EMAIL = "d2VibWFzdGVyQHBhd2ZlZWQub3Jn";
const IMP_CITY = "ODIxNDAgT2xjaGluZw==";
const IMP_EMAIL = "d2VibWFzdGVyQHBhd2ZlZWQub3Jn";
export default function ImpressumPage() {
const [revealed, setRevealed] = useState(false);
useEffect(() => { setRevealed(true); }, []);
useEffect(() => {
setRevealed(true);
}, []);
const name = revealed ? decode(IMP_NAME) : "––";
const street = revealed ? decode(IMP_STREET) : "––";
const city = revealed ? decode(IMP_CITY) : "––";
const email = revealed ? decode(IMP_EMAIL) : "––";
return (
<div className="max-w-2xl mx-auto px-4 py-12 prose prose-sm dark:prose-invert">
<h1>Impressum</h1>
<h2>Angaben gemäß § 5 TMG</h2>
{revealed ? (
<address className="not-italic">
<p>{decode(IMP_NAME)}</p>
<p>{decode(IMP_STREET)}</p>
<p>{decode(IMP_CITY)}</p>
<LegalLayout title="Impressum" subtitle="Angaben gemäß § 5 TMG">
<LegalSection number="1" title="Diensteanbieter">
<address className="not-italic space-y-0.5">
<p className="font-medium text-foreground">{name}</p>
<p>{street}</p>
<p>{city}</p>
</address>
) : (
<address className="not-italic opacity-0 select-none" aria-hidden>
<p>&#8203;</p>
<p>&#8203;</p>
<p>&#8203;</p>
</address>
)}
</LegalSection>
<h2>Kontakt</h2>
{revealed ? (
<LegalSection number="2" title="Kontakt">
<p>
E-Mail:{" "}
<a href={`mailto:${decode(IMP_EMAIL)}`}>{decode(IMP_EMAIL)}</a>
{revealed ? (
<a
href={`mailto:${email}`}
className="text-orange-600 dark:text-orange-400 hover:underline"
>
{email}
</a>
) : (
"––"
)}
</p>
) : (
<p className="opacity-0 select-none" aria-hidden>
&#8203;
</LegalSection>
<LegalSection number="3" title="Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV">
<address className="not-italic space-y-0.5">
<p className="font-medium text-foreground">{name}</p>
<p>{street}</p>
<p>{city}</p>
</address>
</LegalSection>
<LegalSection number="4" title="Streitschlichtung">
<p>
Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung bereit:{" "}
<a
href="https://ec.europa.eu/consumers/odr/"
target="_blank"
rel="noopener noreferrer"
className="text-orange-600 dark:text-orange-400 hover:underline"
>
ec.europa.eu/consumers/odr
</a>
. Wir nehmen nicht an Streitbeilegungsverfahren vor einer Verbraucherschlichtungsstelle teil.
</p>
)}
</LegalSection>
<h2>Verantwortlich für den Inhalt nach § 55 Abs. 2 RStV</h2>
{revealed ? (
<address className="not-italic">
<p>{decode(IMP_NAME)}</p>
<p>{decode(IMP_STREET)}</p>
<p>{decode(IMP_CITY)}</p>
</address>
) : (
<address className="not-italic opacity-0 select-none" aria-hidden>
<p>&#8203;</p>
</address>
)}
<LegalSection number="5" title="Haftung für Inhalte">
<p>
Als Diensteanbieter sind wir gemäß § 7 Abs. 1 TMG für eigene Inhalte nach den
allgemeinen Gesetzen verantwortlich. Nach §§ 8–10 TMG sind wir nicht verpflichtet,
übermittelte oder gespeicherte fremde Informationen zu überwachen.
</p>
</LegalSection>
<h2>Streitschlichtung</h2>
<p>
Die Europäische Kommission stellt eine Plattform zur Online-Streitbeilegung (OS) bereit:{" "}
<a href="https://ec.europa.eu/consumers/odr/" target="_blank" rel="noopener noreferrer">
https://ec.europa.eu/consumers/odr/
</a>
. Wir sind nicht bereit oder verpflichtet, an Streitbeilegungsverfahren vor einer
Verbraucherschlichtungsstelle teilzunehmen.
</p>
<LegalSection number="6" title="Haftung für Links">
<p>
Unser Angebot enthält Links zu externen Websites. Für deren Inhalte ist stets der
jeweilige Anbieter verantwortlich. Eine dauerhafte Kontrolle der verlinkten Seiten ist
ohne konkrete Anhaltspunkte einer Rechtsverletzung nicht zumutbar.
</p>
</LegalSection>
<h2>Haftung für Inhalte</h2>
<p>
Als Diensteanbieter sind wir gemäß § 7 Abs. 1 TMG für eigene Inhalte auf diesen Seiten nach
den allgemeinen Gesetzen verantwortlich. Nach §§ 8 bis 10 TMG sind wir als Diensteanbieter
jedoch nicht verpflichtet, übermittelte oder gespeicherte fremde Informationen zu
überwachen oder nach Umständen zu forschen, die auf eine rechtswidrige Tätigkeit hinweisen.
</p>
<LegalSection number="7" title="Urheberrecht">
<p>
Die durch uns erstellten Inhalte und Werke unterliegen dem deutschen Urheberrecht.
Vervielfältigung, Bearbeitung und Verbreitung außerhalb der Grenzen des Urheberrechts
bedürfen der schriftlichen Zustimmung.
</p>
</LegalSection>
<h2>Haftung für Links</h2>
<p>
Unser Angebot enthält Links zu externen Websites Dritter, auf deren Inhalte wir keinen
Einfluss haben. Für die Inhalte der verlinkten Seiten ist stets der jeweilige Anbieter oder
Betreiber der Seiten verantwortlich.
</p>
<h2>Urheberrecht</h2>
<p>
Die durch die Seitenbetreiber erstellten Inhalte und Werke auf diesen Seiten unterliegen
dem deutschen Urheberrecht. Die Vervielfältigung, Bearbeitung, Verbreitung und jede Art der
Verwertung außerhalb der Grenzen des Urheberrechtes bedürfen der schriftlichen Zustimmung
des jeweiligen Autors bzw. Erstellers.
</p>
</div>
<div className="bg-card border rounded-xl p-6 flex items-center justify-between">
<p className="text-sm text-muted-foreground">Weitere rechtliche Informationen</p>
<div className="flex gap-4">
<Link href="/datenschutz" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
Datenschutz
</Link>
<Link href="/nutzungsbedingungen" className="text-sm text-orange-600 dark:text-orange-400 hover:underline">
AGB
</Link>
</div>
</div>
</LegalLayout>
);
}
+75
View File
@@ -0,0 +1,75 @@
import Link from "next/link";
import { PawPrint, ArrowLeft } from "lucide-react";
export function LegalLayout({
title,
subtitle,
children,
}: {
title: string;
subtitle?: string;
children: React.ReactNode;
}) {
return (
<div className="min-h-screen bg-muted/30">
<div className="max-w-2xl mx-auto px-4 py-10">
{/* Back link */}
<Link
href="/"
className="inline-flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors mb-8 group"
>
<ArrowLeft className="h-4 w-4 group-hover:-translate-x-0.5 transition-transform" />
Zurück zu PawFeed
</Link>
{/* Logo */}
<div className="flex items-center gap-3 mb-8">
<div className="w-10 h-10 bg-orange-500 rounded-xl flex items-center justify-center shadow-sm">
<PawPrint className="h-5 w-5 text-white" />
</div>
<span className="text-2xl font-bold tracking-tight">PawFeed</span>
</div>
{/* Page title card */}
<div className="bg-card border rounded-2xl px-8 py-7 shadow-sm mb-5">
<h1 className="text-2xl font-bold text-foreground">{title}</h1>
{subtitle && (
<p className="text-sm text-muted-foreground mt-1">{subtitle}</p>
)}
</div>
{/* Sections */}
<div className="space-y-3">{children}</div>
{/* Footer */}
<p className="mt-10 text-center text-xs text-muted-foreground">
© {new Date().getFullYear()} PawFeed · Alle Rechte vorbehalten
</p>
</div>
</div>
);
}
export function LegalSection({
number,
title,
children,
}: {
number: string | number;
title: string;
children: React.ReactNode;
}) {
return (
<div className="bg-card border rounded-xl p-6">
<div className="flex items-center gap-3 mb-4">
<span className="flex-shrink-0 w-6 h-6 bg-orange-500/10 text-orange-600 dark:text-orange-400 rounded-md text-xs font-bold flex items-center justify-center">
{number}
</span>
<h2 className="text-sm font-semibold text-foreground">{title}</h2>
</div>
<div className="text-sm text-muted-foreground leading-relaxed space-y-2 pl-9">
{children}
</div>
</div>
);
}