diff --git a/app/components/RichText.tsx b/app/components/RichText.tsx index 6fbac34..129ac05 100644 --- a/app/components/RichText.tsx +++ b/app/components/RichText.tsx @@ -35,7 +35,13 @@ function plainText(node: LexicalNode): string { // "section-1" id from the leading number — immune to copy edits changing // the heading text later, unlike a text-derived slug. Anything else // (headings with no leading number) falls back to a plain slugify. -function headingId(text: string): string { +// Exported — the Impressum page renders some of its own headings outside +// this CMS-driven richText (the "Angaben zum Anbieter"/"Umsatzsteuer"/ +// "Verantwortlich für den Inhalt" sections come straight from +// company-settings, not the richText field, see app/impressum/page.tsx) +// and needs the exact same id-assignment logic so its SectionTOC entries +// actually match the ids those headings render with. +export function headingId(text: string): string { const numbered = text.match(/^(\d+)\./); if (numbered) return `section-${numbered[1]}`; return text diff --git a/app/impressum/components/AnbieterAngaben.tsx b/app/impressum/components/AnbieterAngaben.tsx new file mode 100644 index 0000000..d774c9f --- /dev/null +++ b/app/impressum/components/AnbieterAngaben.tsx @@ -0,0 +1,92 @@ +import type { ReactNode } from "react"; +import { headingId } from "../../components/RichText"; +import type { CompanySettings } from "../../lib/payload"; +import type { TOCSection } from "../../components/SectionTOC"; + +// Renders "Angaben zum Anbieter"/"Umsatzsteuer"/(conditionally) +// "Handelsregister"/"Geschäftsführung"/"Verantwortlich für den Inhalt" +// straight from company-settings, matching RichText.tsx's own heading/ +// paragraph classes so it reads as one continuous page with the CMS +// content below it, not a bolted-on block. This used to be hand-typed +// prose baked into the Impressum's richText (seed-legal-pages.ts on the +// Payload side) — duplicated, and silently out of date the moment an +// admin changed company-settings without also remembering to re-edit the +// Impressum text by hand. Single-sourced here instead, same "structural/ +// brand elements in code, only pull the actual numbers/copy that need +// single-sourcing from data" pattern this page's own Nachhaltigkeit card +// already uses (see page.tsx's comment on that). +// +// Also closes a real compliance gap the old hand-typed text had: it never +// showed Handelsregister/Geschäftsführung at all, even though +// company-settings already models both (§37a HGB/§35a GmbHG) — those +// fields just weren't wired into the Impressum. A sole proprietorship +// (this shop's current legalForm) has neither, so neither section shows +// today, but the moment that changes in company-settings, the Impressum +// picks it up automatically instead of needing a second manual edit. +export function anbieterAngabenHeadings(seller: CompanySettings | null): TOCSection[] { + if (!seller) return []; + const sections = ["Angaben zum Anbieter", "Umsatzsteuer"]; + if (seller.registerCourt && seller.registerNumber) sections.push("Handelsregister"); + if (seller.managingDirector) sections.push("Geschäftsführung"); + sections.push("Verantwortlich für den Inhalt"); + return sections.map((title) => ({ id: headingId(title), title })); +} + +function Heading({ children }: { children: string }) { + return ( +
{children}
; +} + +export function AnbieterAngaben({ seller }: { seller: CompanySettings }) { + return ( +{seller.sellerName}
+{seller.sellerStreet}
++ {seller.sellerZip} {seller.sellerCity} +
+{seller.sellerCountry}
+E-Mail: {seller.sellerEmail}
+ +Umsatzsteuer-Identifikationsnummer gemäß § 27 a Umsatzsteuergesetz:
+{seller.vatId}
+ + {seller.registerCourt && seller.registerNumber && ( + <> +{seller.registerCourt}
+{seller.registerNumber}
+ > + )} + + {seller.managingDirector && ( + <> +{seller.managingDirector}
+ > + )} + +{seller.managingDirector || seller.sellerName}
+{seller.sellerStreet}
++ {seller.sellerZip} {seller.sellerCity} +
+{seller.sellerCountry}
+