Link Impressum's seller identity to company-settings, not hand-typed prose

"Angaben zum Anbieter"/"Umsatzsteuer"/"Handelsregister"/"Geschäftsführung"/
"Verantwortlich für den Inhalt" used to be hand-typed into the Impressum's
richText content (seed-legal-pages.ts) with no connection to the same
seller data the invoice PDFs and every email footer already pull from
company-settings — an admin updating one had no reason to remember the
other existed, and the old text was already stale in one concrete way:
it never showed Handelsregister/Geschäftsführung at all even though
company-settings has modeled both since the legal-form work shipped.

Now rendered by a new AnbieterAngaben component, straight from
getCompanySettings(), positioned above the CMS richText (which keeps
only genuinely editorial content: Kontakt, Haftung für Inhalte, Haftung
für Links, Urheberrecht). Same "structural/brand elements in code, only
pull the actual numbers/copy that need single-sourcing from data"
pattern this page's own Nachhaltigkeit sidebar card already used.

RichText.tsx's headingId() is now exported so the new block's headings
get the exact same id-assignment logic as CMS-driven ones, keeping the
SectionTOC sidebar's ids in sync with both sources.

/impressum moves from static to dynamic rendering (it now fetches live
company-settings data, cache: "no-store") — an acceptable tradeoff for
a legally-required page to never show stale seller info.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Marco
2026-07-23 11:10:02 +00:00
parent 039b8ba28d
commit 21c4e9f007
3 changed files with 113 additions and 5 deletions
+14 -4
View File
@@ -7,7 +7,8 @@ import { Footer } from "../components/Footer";
import { RichText, extractHeadings } from "../components/RichText";
import { LiveRichText } from "../components/LiveRichText";
import { SectionTOC } from "../components/SectionTOC";
import { getLegalPage } from "../lib/payload";
import { getLegalPage, getCompanySettings } from "../lib/payload";
import { AnbieterAngaben, anbieterAngabenHeadings } from "./components/AnbieterAngaben";
export const metadata: Metadata = {
title: "Impressum",
@@ -17,8 +18,11 @@ export const metadata: Metadata = {
export default async function ImpressumPage() {
const { isEnabled: isPreview } = await draftMode();
const page = await getLegalPage("impressum", { draft: isPreview });
const headings = page ? extractHeadings(page.content) : [];
const [page, seller] = await Promise.all([getLegalPage("impressum", { draft: isPreview }), getCompanySettings()]);
// Anbieter-Angaben headings first — that block renders above the CMS
// content below, so its TOC entries need to lead too, or the sidebar
// would list sections in a different order than they actually appear.
const headings = [...anbieterAngabenHeadings(seller), ...(page ? extractHeadings(page.content) : [])];
return (
<>
@@ -66,7 +70,13 @@ export default async function ImpressumPage() {
</div>
</div>
<div className="w-full lg:flex-1 min-w-0">
<div className="w-full lg:flex-1 min-w-0 flex flex-col gap-8">
{/* Seller identity (name/address/USt-ID/Handelsregister/
Geschäftsführung) comes straight from company-settings, not
the CMS richText below — single-sourced so it can never
drift out of sync with the same data the invoice PDFs and
every email footer already use. See AnbieterAngaben.tsx. */}
{seller && <AnbieterAngaben seller={seller} />}
{page ? (
isPreview ? <LiveRichText initialContent={page.content} /> : <RichText content={page.content} />
) : (