feat(legal): readable legal pages and corrected legal texts

Legal pages rendered admin text as bare paragraphs. LegalPage now has a
honeycomb header, a sticky table of contents with scroll tracking (a
collapsible one on mobile), anchor links, a reading progress bar and a
revision date pulled from a "Stand:" line. lib/legal-blocks.ts derives
lists, cookie/provider entries with labelled facts and safe links from
plain text, so the pages stay editable in the admin without markup.

Legal corrections (German law as of 2026):
- Widerrufsbelehrung now follows the official model (Anlage 1 und 2 zu
  Art. 246a EGBGB): recipient name/address/e-mail filled from company
  data, deadline rule, return period, refund details, diminished value
  and the model withdrawal form. The incomplete notice extended the
  withdrawal period to up to 12 months and 14 days. Honey is no longer
  declared perishable.
- Impressum: TMG references replaced by DDG; editorial contact gains an
  address.
- Datenschutz: removes the self-hosted vs. hosting-provider
  contradiction, adds PayPal/Klarna, reviews, back-in-stock
  notifications, storage period, no automated decisions, withdrawal of
  consent and the Art. 21 objection notice; newsletter double opt-in.
- AGB: payment methods and delivery area point to the live shipping
  page; adds contract-text and input-correction information.
- Cookie policy: storage period and provider for every entry.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
This commit is contained in:
Nick SzaunigandClaude Opus 5.5 committed 2026-09-26 07:02:12 +02:00
1 parent 86cc0ad310
commit 4ab1a7edae
9 files changed
+707 -84

No files matched your search

+3 -11
View File
@@ -1,7 +1,6 @@
import type { Metadata } from "next";
import { LegalPage } from "@/components/legal-page";
import { CookieSettingsButton } from "@/components/cookie-consent";
import { Container } from "@/components/ui";
import { getContentValue, parseSections } from "@/lib/content";
import { getLocale } from "@/i18n/locale";
import { getDictionary } from "@/i18n/get-dictionary";
@@ -13,15 +12,8 @@ export default async function CookieRichtliniePage() {
const t = getDictionary(locale);
const sections = parseSections(await getContentValue("legal.cookieRichtlinie", locale));
return (
<>
<LegalPage
title={t.cookieRichtliniePage.title}
noticeText={t.legalPage.reviewNotice}
sections={sections}
/>
<Container className="max-w-3xl pb-20 -mt-10">
<CookieSettingsButton />
</Container>
</>
<LegalPage title={t.cookieRichtliniePage.title} noticeText={t.legalPage.reviewNotice} sections={sections}>
<CookieSettingsButton />
</LegalPage>
);
}
+13 -8
View File
@@ -1,20 +1,25 @@
import type { Metadata } from "next";
import { LegalPage } from "@/components/legal-page";
import { getContentValue, parseSections } from "@/lib/content";
import { getCompanyInfo } from "@/lib/company-info";
import { getLocale } from "@/i18n/locale";
import { getDictionary } from "@/i18n/get-dictionary";
export const metadata: Metadata = { title: "Widerrufsbelehrung", alternates: { canonical: "/widerrufsbelehrung" } };
/**
* Platzhalter im Belehrungstext fuer Name, Anschrift und E-Mail des
* Empfaengers. Das amtliche Muster verlangt diese Angaben an zwei Stellen
* (Belehrung und Formular); aus den Firmendaten eingesetzt, bleiben sie bei
* einem Umzug oder neuer Adresse automatisch richtig.
*/
const PROVIDER_PLACEHOLDER = "{{anbieter}}";
export default async function WiderrufPage() {
const locale = await getLocale();
const t = getDictionary(locale);
const sections = parseSections(await getContentValue("legal.widerruf", locale));
return (
<LegalPage
title={t.widerrufPage.title}
noticeText={t.legalPage.reviewNotice}
sections={sections}
/>
);
const [company, raw] = await Promise.all([getCompanyInfo(), getContentValue("legal.widerruf", locale)]);
const provider = `${company.name}, ${company.address.street}, ${company.address.zip} ${company.address.city}, E-Mail: ${company.email}`;
const sections = parseSections(raw.replaceAll(PROVIDER_PLACEHOLDER, provider));
return <LegalPage title={t.widerrufPage.title} noticeText={t.legalPage.reviewNotice} sections={sections} />;
}