Files
einfach-produktiv/app/widerruf/page.tsx
T
Marco 7a3ae0ea53 Add /widerruf and /agb legal pages, fix newsletter modal scroll jank
- New /widerruf page: CMS-driven content via getLegalPage("widerruf"),
  sidebar TOC + "Nachhaltig handeln" callout, TrustRow, and a download
  card for the Muster-Widerrufsformular PDF attachment.
- New /agb page, same legal-page shell pattern as Impressum/Datenschutz.
- lib/payload.ts: getLegalPage now also resolves the optional `attachment`
  media relation (depth=1) for legal pages that have a downloadable file.
- NewsletterModal: replaced the body overflow/position scroll-lock hacks
  (which fought with the sticky Navbar and the global scroll-smooth CSS)
  with a wheel/touch event interceptor that never touches scroll position
  at all, plus `preventScroll: true` on all modal/drawer focus() calls to
  stop the browser's implicit scrollIntoView from nudging the page for
  elements near the sticky navbar's reserved scroll-padding-top zone.
  Same preventScroll fix applied to VersandModal and the Navbar mobile
  drawer for consistency.
- globals.css: scrollbar-gutter: stable, so a modal's overflow:hidden
  lock never causes a scrollbar-width layout shift.
- Divider: stagger its reveal animation slightly (delay 0.3s) behind the
  section above it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-19 19:57:28 +00:00

118 lines
5.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import type { Metadata } from "next";
import Link from "next/link";
import { Reveal } from "../components/Reveal";
import { Footer } from "../components/Footer";
import { TrustRow } from "../components/TrustRow";
import { RichText, extractHeadings } from "../components/RichText";
import { SectionTOC } from "../components/SectionTOC";
import { getLegalPage } from "../lib/payload";
export const metadata: Metadata = {
title: "Widerrufsrecht",
description: "Widerrufsbelehrung und Muster-Widerrufsformular für einfach produktiv.",
alternates: { canonical: "/widerruf" },
};
export default async function WiderrufPage() {
const page = await getLegalPage("widerruf");
const headings = page ? extractHeadings(page.content) : [];
return (
<>
<main className="flex flex-col flex-1 bg-bg-base">
<Reveal className="flex flex-col gap-3 items-start pb-6 pt-10 px-[var(--layout-padding-x)] w-full">
<p className="flex items-center gap-2 text-body-sm text-text-muted">
<Link href="/" className="hover:text-brand transition-colors">Startseite</Link>
<span></span>
<span className="text-text-primary">Widerruf</span>
</p>
<p
className="font-semibold text-h-feature text-text-primary"
style={{ fontFamily: "var(--font-lora)" }}
>
Widerrufsrecht
</p>
<p className="text-body text-text-muted">Stand: Juli 2026</p>
</Reveal>
<div className="flex flex-col lg:flex-row gap-8 lg:gap-12 items-start pb-10 pt-2 px-[var(--layout-padding-x)] w-full">
<div className="hidden lg:flex flex-col gap-6 w-[22.5rem] shrink-0 lg:sticky lg:top-32 lg:self-start">
<SectionTOC sections={headings} />
{/* Static, not part of the CMS content — same reasoning as
the Impressum/Datenschutz/AGB pages' own callout cards. */}
<div className="bg-bg-muted flex flex-col gap-3 items-start p-6 rounded-md w-full">
<img alt="" src="/icon-trust-leaf.png" className="size-7 object-contain" />
<p
className="font-semibold text-body text-text-primary"
style={{ fontFamily: "var(--font-lora)" }}
>
Nachhaltig handeln
</p>
<p className="text-body-sm text-text-muted">
Wir setzen auf digitale Produkte und vermeiden unnötige Ressourcenverschwendung.
</p>
<p className="text-body-sm text-text-muted">Für eine bessere Zukunft für uns alle.</p>
<div className="h-[0.125rem] w-8 bg-brand" />
</div>
</div>
<div className="w-full lg:flex-1 min-w-0 flex flex-col gap-8">
{page ? (
<RichText content={page.content} />
) : (
<p className="text-body text-text-muted">Inhalte werden gerade aktualisiert.</p>
)}
{page?.attachment && (
<a
href={page.attachment.url}
target="_blank"
rel="noopener noreferrer"
download
className="group flex items-center gap-5 bg-bg-muted hover:bg-bg-white border border-border rounded-md p-6 transition-colors"
>
<div className="flex size-12 shrink-0 items-center justify-center rounded-sm bg-bg-white border border-border text-brand">
<svg viewBox="0 0 24 24" className="size-6" fill="none" aria-hidden="true">
<path
d="M6 2h9l5 5v13a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2Z"
stroke="currentColor"
strokeWidth="1.5"
strokeLinejoin="round"
/>
<path d="M15 2v5h5" stroke="currentColor" strokeWidth="1.5" strokeLinejoin="round" />
<path
d="M8.5 17.5v-5h1.4c.94 0 1.6.62 1.6 1.5s-.66 1.5-1.6 1.5H8.5m5.2 2v-5h1.2c1.16 0 1.9.9 1.9 2.5s-.74 2.5-1.9 2.5h-1.2Z"
stroke="currentColor"
strokeWidth="1.3"
strokeLinecap="round"
strokeLinejoin="round"
/>
</svg>
</div>
<div className="flex flex-col gap-0.5 flex-1 min-w-0">
<p className="font-semibold text-body text-text-primary">
{page.attachment.title || "Muster-Widerrufsformular (PDF)"}
</p>
<p className="text-body-sm text-text-muted">Herunterladen</p>
</div>
<svg
viewBox="0 0 24 24"
className="size-5 shrink-0 text-text-muted group-hover:text-brand group-hover:translate-y-0.5 transition-all"
fill="none"
aria-hidden="true"
>
<path d="M12 4v13m0 0-5-5m5 5 5-5M5 21h14" stroke="currentColor" strokeWidth="1.75" strokeLinecap="round" strokeLinejoin="round" />
</svg>
</a>
)}
</div>
</div>
<TrustRow />
</main>
<Footer />
</>
);
}