From e1713f6d6e61c796bf8e44b9692ed9989e937a35 Mon Sep 17 00:00:00 2001 From: syntaxbullet Date: Mon, 22 Jun 2026 10:45:54 +0200 Subject: [PATCH] feat: manage impressum page via payload --- package.json | 1 + src/collections/Pages/impressumFields.ts | 81 ++++++ src/collections/Pages/index.ts | 21 +- src/payload-types.ts | 71 +++++ src/scripts/preload-impressum-page-cms.ts | 41 +++ src/spa/impressumContent.ts | 295 +++++++++++++++++++++ src/spa/pages/Impressum.tsx | 299 +++++++++------------- 7 files changed, 625 insertions(+), 184 deletions(-) create mode 100644 src/collections/Pages/impressumFields.ts create mode 100644 src/scripts/preload-impressum-page-cms.ts create mode 100644 src/spa/impressumContent.ts diff --git a/package.json b/package.json index 506b625..5b10b25 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "payload": "cross-env NODE_OPTIONS=--no-deprecation payload", "preload:contact-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-contact-page-cms.ts", "preload:home-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-home-page-cms.ts", + "preload:impressum-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-impressum-page-cms.ts", "preload:participation-page-cms": "cross-env NODE_OPTIONS=--no-deprecation tsx src/scripts/preload-participation-page-cms.ts", "reinstall": "cross-env NODE_OPTIONS=--no-deprecation rm -rf node_modules && rm pnpm-lock.yaml && pnpm --ignore-workspace install", "start": "cross-env NODE_OPTIONS=--no-deprecation next start", diff --git a/src/collections/Pages/impressumFields.ts b/src/collections/Pages/impressumFields.ts new file mode 100644 index 0000000..199ad19 --- /dev/null +++ b/src/collections/Pages/impressumFields.ts @@ -0,0 +1,81 @@ +import type { Field } from 'payload' + +import { impressumContent } from '@/spa/impressumContent' + +const text = (name: string, label: string, defaultValue?: string): Field => ({ + name, + type: 'text', + label, + defaultValue, +}) + +const textarea = (name: string, label: string, defaultValue?: string): Field => ({ + name, + type: 'textarea', + label, + defaultValue, +}) + +const sectionAdmin = (description: string) => ({ + description, + initCollapsed: true, +}) + +export const impressumFields: Field[] = [ + { + name: 'impressum', + label: 'Impressum page content', + type: 'group', + admin: { + description: + 'Edit the Impressum page content. Legal section body blocks are stored as JSON so nested paragraphs, lists, notices, addresses, and subsections can keep their frontend order.', + }, + fields: [ + { + name: 'hero', + label: '01 · Hero', + type: 'group', + admin: sectionAdmin('Top legal page heading and description line.'), + fields: [ + text('backLabel', 'Back link label', impressumContent.hero.backLabel), + text('eyebrow', 'Eyebrow', impressumContent.hero.eyebrow), + textarea('heading', 'Heading', impressumContent.hero.heading), + textarea('descriptionHtml', 'Description HTML', impressumContent.hero.descriptionHtml), + ], + }, + { + name: 'navigation', + label: '02 · Side navigation', + type: 'group', + admin: sectionAdmin('Sticky table-of-contents title and related legal-page link.'), + fields: [ + text('tocTitle', 'TOC title', impressumContent.navigation.tocTitle), + text('sideLinkLabel', 'Side link label', impressumContent.navigation.sideLinkLabel), + text('sideLinkUrl', 'Side link URL', impressumContent.navigation.sideLinkUrl), + ], + }, + { + name: 'sections', + label: '03 · Legal sections', + type: 'array', + dbName: 'imp_sections', + admin: sectionAdmin('Each section controls one heading, table-of-contents label, and ordered JSON body blocks.'), + defaultValue: impressumContent.sections, + fields: [ + text('id', 'Anchor ID'), + text('tocLabel', 'TOC label'), + text('title', 'Section title'), + { + name: 'items', + label: 'Content blocks JSON', + type: 'json', + admin: { + description: + 'Array of blocks: paragraph, legal, address, list, or subsection. Paragraph/legal blocks use HTML strings for inline links.', + }, + }, + ], + }, + ], + }, +] diff --git a/src/collections/Pages/index.ts b/src/collections/Pages/index.ts index 4c06a36..490e73b 100644 --- a/src/collections/Pages/index.ts +++ b/src/collections/Pages/index.ts @@ -10,6 +10,7 @@ import { MediaBlock } from '../../blocks/MediaBlock/config' import { hero } from '@/heros/config' import { contactFields } from './contactFields' import { homeFields } from './homeFields' +import { impressumFields } from './impressumFields' import { participationFields } from './participationFields' import { slugField } from 'payload' import { populatePublishedAt } from '../../hooks/populatePublishedAt' @@ -48,8 +49,19 @@ const isParticipationPage = (_: unknown, siblingData?: { slug?: string; spaPath? return spaPath === '/teilnahme' || slug === 'teilnahme' || slug === 'participation' || title === 'teilnahme' || title === 'participation' } +const isImpressumPage = (_: unknown, siblingData?: { slug?: string; spaPath?: string; title?: string }) => { + const slug = siblingData?.slug + const spaPath = siblingData?.spaPath + const title = siblingData?.title?.toLowerCase() + + return spaPath === '/impressum' || slug === 'impressum' || title === 'impressum' +} + const isManagedSpaPage = (_: unknown, siblingData?: { slug?: string; spaPath?: string; title?: string }) => - isHomePage(undefined, siblingData) || isContactPage(undefined, siblingData) || isParticipationPage(undefined, siblingData) + isHomePage(undefined, siblingData) || + isContactPage(undefined, siblingData) || + isParticipationPage(undefined, siblingData) || + isImpressumPage(undefined, siblingData) export const Pages: CollectionConfig<'pages'> = { slug: 'pages', @@ -138,6 +150,13 @@ export const Pages: CollectionConfig<'pages'> = { fields: participationFields, label: 'Participation Page', }, + { + admin: { + condition: (data) => isImpressumPage(undefined, data), + }, + fields: impressumFields, + label: 'Impressum Page', + }, { name: 'meta', label: 'SEO', diff --git a/src/payload-types.ts b/src/payload-types.ts index a257361..5d0f91f 100644 --- a/src/payload-types.ts +++ b/src/payload-types.ts @@ -913,6 +913,50 @@ export interface Page { }; }; }; + /** + * Edit the Impressum page content. Legal section body blocks are stored as JSON so nested paragraphs, lists, notices, addresses, and subsections can keep their frontend order. + */ + impressum?: { + /** + * Top legal page heading and description line. + */ + hero?: { + backLabel?: string | null; + eyebrow?: string | null; + heading?: string | null; + descriptionHtml?: string | null; + }; + /** + * Sticky table-of-contents title and related legal-page link. + */ + navigation?: { + tocTitle?: string | null; + sideLinkLabel?: string | null; + sideLinkUrl?: string | null; + }; + /** + * Each section controls one heading, table-of-contents label, and ordered JSON body blocks. + */ + sections?: + | { + id?: string | null; + tocLabel?: string | null; + title?: string | null; + /** + * Array of blocks: paragraph, legal, address, list, or subsection. Paragraph/legal blocks use HTML strings for inline links. + */ + items?: + | { + [k: string]: unknown; + } + | unknown[] + | string + | number + | boolean + | null; + }[] + | null; + }; meta?: { title?: string | null; /** @@ -2514,6 +2558,33 @@ export interface PagesSelect { }; }; }; + impressum?: + | T + | { + hero?: + | T + | { + backLabel?: T; + eyebrow?: T; + heading?: T; + descriptionHtml?: T; + }; + navigation?: + | T + | { + tocTitle?: T; + sideLinkLabel?: T; + sideLinkUrl?: T; + }; + sections?: + | T + | { + id?: T; + tocLabel?: T; + title?: T; + items?: T; + }; + }; meta?: | T | { diff --git a/src/scripts/preload-impressum-page-cms.ts b/src/scripts/preload-impressum-page-cms.ts new file mode 100644 index 0000000..938e50f --- /dev/null +++ b/src/scripts/preload-impressum-page-cms.ts @@ -0,0 +1,41 @@ +import 'dotenv/config' + +import config from '@payload-config' +import { getPayload } from 'payload' + +import { impressumContent } from '@/spa/impressumContent' + +async function main() { + const payload = await getPayload({ config }) + + const pageResult = await payload.find({ + collection: 'pages', + limit: 1, + pagination: false, + draft: true, + overrideAccess: true, + where: { + or: [{ spaPath: { equals: '/impressum' } }, { slug: { equals: 'impressum' } }], + }, + }) + + const page = pageResult.docs[0] + if (!page) throw new Error('Impressum page not found. Expected spaPath=/impressum or slug=impressum.') + + await payload.update({ + collection: 'pages', + id: page.id, + overrideAccess: true, + context: { disableRevalidate: true }, + data: { + impressum: impressumContent, + } as never, + }) + + payload.logger.info(`Preloaded Impressum CMS fields for page ${page.id}`) +} + +main().catch((error) => { + console.error(error) + process.exit(1) +}) diff --git a/src/spa/impressumContent.ts b/src/spa/impressumContent.ts new file mode 100644 index 0000000..aefca2b --- /dev/null +++ b/src/spa/impressumContent.ts @@ -0,0 +1,295 @@ +export type LegalContentItem = + | { type: 'paragraph'; html: string; variant?: 'body' | 'strong' | 'italic' } + | { type: 'address'; lines: string[] } + | { type: 'legal'; html: string } + | { type: 'list'; items: string[] } + | { type: 'subsection'; num: string; title: string; items: LegalContentItem[] } + +export type LegalSectionContent = { + id: string + tocLabel: string + title: string + items: LegalContentItem[] +} + +export const vmAddressLines = [ + 'VM Verbands-Management GmbH', + 'Edelsbergstr. 8', + '80686 München', + 'Tel. +49 (0) 89 – 57007-0', + 'Fax +49 (0) 89 – 57007 260', + 'info@vm-verbands-management.de', + 'HRB 110222 AG München, gesetzl. vertreten durch Friedrich Marx', +] + +const navyLink = 'style="color:#111D55;font-weight:600"' +const navyPlainLink = 'style="color:#111D55"' + +export const impressumContent = { + hero: { + backLabel: 'Zurück zur Website', + eyebrow: 'Legal', + heading: 'Impressum', + descriptionHtml: + 'Angaben gemäß §5 TMG · Zur Datenschutzerklärung →', + }, + navigation: { + tocTitle: 'Inhalt', + sideLinkLabel: 'Datenschutzerklärung →', + sideLinkUrl: '/datenschutz', + }, + sections: [ + { + id: 'betreiber', + tocLabel: 'Angaben gem. §5 TMG', + title: 'Angaben gemäß §5 TMG', + items: [ + { type: 'paragraph', variant: 'strong', html: 'Informationen über den Verantwortlichen' }, + { type: 'address', lines: vmAddressLines }, + { type: 'paragraph', variant: 'strong', html: 'Angaben zum Webseitenbetreiber' }, + { type: 'address', lines: vmAddressLines }, + { + type: 'paragraph', + html: 'Inhaltlich Verantwortlicher gemäß TMG: VM Verbands-Management GmbH, gesetzl. vertreten durch Friedrich Marx', + }, + ], + }, + { + id: 'datenschutz', + tocLabel: 'Datenschutz', + title: 'Datenschutz & Datenverarbeitung', + items: [ + { + type: 'paragraph', + html: `Alle personenbezogenen Informationen werden vertraulich und gemäß den gesetzlichen Vorschriften behandelt, wie in der Datenschutzerklärung erläutert. Den Schutz der persönlichen Daten nehmen wir als Webseitenbetreiber sehr ernst.`, + }, + { + type: 'paragraph', + html: 'Ein Besuch unserer Webseiten ist möglich, ohne Angaben zu Ihrer Person zu machen. Es werden bei jedem Zugriff auf unsere Website Nutzungsdaten durch Ihren Internetbrowser übermittelt und in Protokolldaten (Server-Logfiles) gespeichert. Zu diesen gespeicherten Daten gehören z. B. Name der aufgerufenen Seite, Datum und Uhrzeit des Abrufs, übertragene Datenmenge und der anfragende Provider (IP-Adresse). Diese Daten dienen ausschließlich der Gewährleistung eines störungsfreien Betriebs unserer Website und zur Verbesserung unseres Angebotes. Eine Zuordnung dieser Daten zu einer bestimmten Person ist nicht möglich.', + }, + { type: 'paragraph', html: 'Es erfolgt keine Zusammenführung dieser Daten mit anderen Datenquellen.' }, + ], + }, + { + id: 'kontaktformular', + tocLabel: 'Kontaktformular', + title: 'Erhebung und Verarbeitung beim Kontaktformular', + items: [ + { + type: 'paragraph', + html: 'Sofern Sie uns über unsere Kontaktdaten eine E-Mail übersenden bzw. uns über das Kontaktformular informieren, erheben wir Ihre personenbezogenen Daten (Name, E-Mail-Adresse, Telefonnummer, Betreff, Nachrichtentext) nur in dem von Ihnen zur Verfügung gestellten Umfang. Die Datenverarbeitung dient dem Zweck der Kontaktaufnahme.', + }, + { type: 'paragraph', html: 'Mit Absenden Ihrer Nachricht willigen Sie in die Verarbeitung der übermittelten Daten ein.' }, + { type: 'legal', html: 'Eine Verarbeitung erfolgt auf Grundlage des Art. 6 (1) lit. a DSGVO mit Ihrer Einwilligung.' }, + { + type: 'paragraph', + html: 'Sie können diese Einwilligung jederzeit durch Mitteilung an uns widerrufen. Ihre E-Mail-Adresse wird nur zur Bearbeitung Ihrer Anfrage genutzt. Ihre Daten werden anschließend gelöscht, sofern Sie der weitergehenden Verarbeitung und Nutzung nicht zugestimmt haben.', + }, + ], + }, + { + id: 'betroffenenrechte', + tocLabel: 'Betroffenenrechte', + title: 'Betroffenenrechte', + items: [ + { + type: 'paragraph', + html: 'Bei Vorliegen der gesetzlichen Voraussetzungen stehen Ihnen folgende Rechte nach Art. 15 bis 20 DSGVO zu:', + }, + { + type: 'list', + items: [ + 'Recht auf Auskunft', + 'Recht auf Berichtigung', + 'Recht auf Löschung', + 'Recht auf Einschränkung der Verarbeitung', + 'Recht auf Datenübertragbarkeit', + ], + }, + { + type: 'legal', + html: 'Außerdem steht Ihnen nach Art. 21 (1) DSGVO ein Widerspruchsrecht gegen die Verarbeitungen zu, die auf Art. 6 (1) f DSGVO beruhen, sowie gegen die Verarbeitung zum Zwecke von Direktwerbung.', + }, + { + type: 'paragraph', + html: `Auf Wunsch können Sie uns kontaktieren. Die Kontaktdaten finden Sie unter Kontakt.`, + }, + ], + }, + { + id: 'aufsichtsbehoerde', + tocLabel: 'Beschwerderecht', + title: 'Beschwerderecht bei der Aufsichtsbehörde', + items: [ + { + type: 'paragraph', + html: 'Sie haben gemäß Art. 77 DSGVO das Recht, sich bei der Aufsichtsbehörde zu beschweren, wenn Sie der Ansicht sind, dass die Verarbeitung Ihrer personenbezogenen Daten nicht rechtmäßig erfolgt.', + }, + { type: 'paragraph', variant: 'strong', html: 'Die zuständige Aufsichtsbehörde:' }, + { + type: 'address', + lines: [ + 'Bayerisches Landesamt für Datenschutzaufsicht', + 'Postfach 606', + '91511 Ansbach', + 'Deutschland', + 'Tel. 09853-1300', + 'Fax 09853-981300', + 'E-Mail: poststelle@lda.bayern.de', + ], + }, + ], + }, + { + id: 'verantwortlicher', + tocLabel: 'Verantwortlicher', + title: 'Name und Anschrift des Verantwortlichen', + items: [ + { + type: 'paragraph', + html: 'Verantwortlicher im Sinne der Datenschutz-Grundverordnung und sonstiger datenschutzrechtlicher Bestimmungen ist der Webseitenbetreiber:', + }, + { type: 'address', lines: vmAddressLines }, + ], + }, + { + id: 'privacy-policy', + tocLabel: 'Privacy Policy', + title: 'Erklärung zum Datenschutz (Privacy Policy)', + items: [ + { + type: 'paragraph', + variant: 'italic', + html: 'Die Betreiber des Portals nehmen den Schutz der privaten Daten ernst. Die besondere Beachtung der Privatsphäre bei der Verarbeitung persönlicher Daten ist ein wichtiges Anliegen. Persönliche Daten werden gemäß den Bestimmungen des Bundesdatenschutzgesetzes BDSG verwendet; die Betreiber dieser Website verpflichten sich zur Verschwiegenheit. Diese Webseiten können Links zu Webseiten anderer Anbieter enthalten, auf die sich diese Datenschutzerklärung nicht erstreckt. Weitere wichtige Informationen finden sich auch in den Allgemeinen Nutzungsbedingungen.', + }, + { + type: 'subsection', + num: '1', + title: 'Personenbezogene Daten', + items: [ + { + type: 'paragraph', + html: 'Personenbezogene Daten sind Informationen, die dazu genutzt werden können, die Identität zu erfahren. Darunter fallen Informationen wie richtiger Name, Adresse, Postanschrift, Telefonnummer. Informationen, die nicht direkt mit der wirklichen Identität in Verbindung gebracht werden (wie zum Beispiel favorisierte Webseiten oder Anzahl der Nutzer einer Site) fallen nicht darunter.', + }, + { + type: 'paragraph', + html: 'Die statistische Auswertung anonymisierter Datensätze bleibt vorbehalten. Wir nutzen die persönlichen Daten zu Zwecken der technischen Administration der Webseiten und zur Kundenverwaltung nur im jeweils dafür erforderlichen Umfang. Darüber hinaus werden persönliche Daten nur dann gespeichert, wenn diese freiwillig angegeben werden.', + }, + ], + }, + { + type: 'subsection', + num: '2', + title: 'Weitergabe personenbezogener Daten', + items: [ + { + type: 'paragraph', + html: 'Wir verwenden personenbezogene Informationen nur für diese Webseite. Wir geben die Informationen nicht ohne ausdrückliches Einverständnis an Dritte weiter. Sollten im Rahmen der Auftragsdatenverarbeitung Daten an Dienstleister weitergegeben werden, so sind diese an das Bundesdatenschutzgesetz BDSG, andere gesetzliche Vorschriften und an diese Privacy Policy gebunden.', + }, + { + type: 'paragraph', + html: 'Erhebungen beziehungsweise Übermittlungen persönlicher Daten an staatliche Einrichtungen und Behörden erfolgen nur im Rahmen zwingender Rechtsvorschriften.', + }, + { + type: 'paragraph', + html: `Bitte beachten Sie gesondert die Hinweise zur Datenverarbeitung im Zusammenhang mit Google reCAPTCHA unter Datenschutz.`, + }, + ], + }, + { + type: 'subsection', + num: '3', + title: 'Kinder', + items: [ + { + type: 'paragraph', + html: 'Personen unter 18 Jahren sollten ohne Zustimmung der Eltern oder Erziehungsberechtigten keine personenbezogenen Daten an uns übermitteln. Wir fordern keine personenbezogenen Daten von Kindern an, sammeln diese nicht und geben sie nicht an Dritte weiter.', + }, + ], + }, + { + type: 'subsection', + num: '4', + title: 'Recht auf Widerruf', + items: [ + { + type: 'paragraph', + html: 'Wenn Sie uns personenbezogene Daten überlassen haben, können Sie diese jederzeit im Benutzerprofil wieder ändern und löschen. Für eine vollständige Löschung des Accounts bitte an den Webmaster wenden. Bis zu diesem Zeitpunkt erfolgte Beiträge in Foren, Kommentaren, Terminankündigungen und Artikeln bleiben allerdings unter Umständen erhalten – Informationen dazu auch bei den allgemeinen Nutzungsbedingungen.', + }, + ], + }, + { + type: 'subsection', + num: '5', + title: 'Links zu anderen Websites', + items: [ + { + type: 'paragraph', + html: 'Unser Online-Angebot enthält Links zu anderen Websites. Wir haben keinen Einfluss darauf, dass deren Betreiber die Datenschutzbestimmungen einhalten.', + }, + ], + }, + { + type: 'subsection', + num: '6', + title: 'Beiträge', + items: [ + { + type: 'paragraph', + html: 'Die Beiträge auf unserer Seite sind für jeden zugänglich. Beiträge sollten vor der Veröffentlichung sorgfältig darauf überprüft werden, ob sie Angaben enthalten, die nicht für die Öffentlichkeit bestimmt sind. Die Beiträge werden möglicherweise in Suchmaschinen erfasst und auch ohne gezielten Aufruf dieser Website weltweit zugreifbar.', + }, + { + type: 'paragraph', + html: 'Trotz sorgfältiger inhaltlicher Kontrolle übernehmen wir keine Haftung für die Inhalte externer Links. Für den Inhalt der verlinkten Seiten sind ausschließlich deren Betreiber verantwortlich. Alle hier verwendeten Namen, Begriffe, Zeichen und Grafiken können Marken- oder Warenzeichen im Besitze ihrer rechtlichen Eigentümer sein. Die Rechte aller erwähnten und benutzten Marken- und Warenzeichen liegen ausschließlich bei deren Besitzern.', + }, + ], + }, + { + type: 'subsection', + num: '7', + title: 'Fragen und Kommentare', + items: [ + { type: 'paragraph', html: 'Bei Fragen und für Anregungen und Kommentare zum Thema Datenschutz bitte per Mail an den Webmaster wenden.' }, + { type: 'address', lines: vmAddressLines }, + ], + }, + ], + }, + { + id: 'bildnachweise', + tocLabel: 'Bildnachweise', + title: 'Bildnachweise', + items: [ + { + type: 'list', + items: [ + 'Headline Maximilianeum: © Dietmar Egerer, München', + 'Ingrid Großmann, Taufkirchen', + 'EWIF Deutschland, München', + 'icon.icons.com', + 'Bilder zum Bayerischen Mittelstandspreis: © VM Verbands-Management GmbH, gesetzl. vertreten durch Friedrich Marx', + ], + }, + ], + }, + { + id: 'haftung', + tocLabel: 'Haftungsausschluss', + title: 'Haftungsausschluss', + items: [ + { + type: 'paragraph', + html: 'Trotz sorgfältiger inhaltlicher Kontrolle übernehmen wir keine Haftung für die Inhalte externer Links. Für den Inhalt der verlinkten Seiten sind ausschließlich deren Betreiber verantwortlich.', + }, + { + type: 'paragraph', + html: 'Alle hier verwendeten Namen, Begriffe, Zeichen und Grafiken können Marken- oder Warenzeichen im Besitze ihrer rechtlichen Eigentümer sein. Die Rechte aller erwähnten und benutzten Marken- und Warenzeichen liegen ausschließlich bei deren Besitzern.', + }, + { + type: 'paragraph', + html: `Einen allgemeinen Haftungsausschluss finden Sie unter: disclaimer.de`, + }, + ], + }, + ] satisfies LegalSectionContent[], +} diff --git a/src/spa/pages/Impressum.tsx b/src/spa/pages/Impressum.tsx index 2080b24..44bf07e 100644 --- a/src/spa/pages/Impressum.tsx +++ b/src/spa/pages/Impressum.tsx @@ -1,215 +1,105 @@ -import React from 'react'; -import { Link } from '@/spa/router'; -import { ArrowLeft } from 'lucide-react'; -import { useIsMobile } from '@/spa/hooks/useIsMobile'; +import React from 'react' +import { ArrowLeft } from 'lucide-react' -const ANTHRA = '#1A2030'; -const NAVY = '#111D55'; -const GOLD = '#EFBF04'; -const CREAM = '#EFE5E3'; -const FF = '"IBM Plex Sans", sans-serif'; -const FB = '"Inter", sans-serif'; +import { useCmsRoute } from '@/spa/cmsRoute' +import { impressumContent, type LegalContentItem, type LegalSectionContent } from '@/spa/impressumContent' +import { useIsMobile } from '@/spa/hooks/useIsMobile' +import { Link } from '@/spa/router' -const TOC = [ - { id: 'betreiber', label: 'Angaben gem. §5 TMG' }, - { id: 'datenschutz', label: 'Datenschutz' }, - { id: 'kontaktformular', label: 'Kontaktformular' }, - { id: 'betroffenenrechte', label: 'Betroffenenrechte' }, - { id: 'aufsichtsbehoerde', label: 'Beschwerderecht' }, - { id: 'verantwortlicher', label: 'Verantwortlicher' }, - { id: 'privacy-policy', label: 'Privacy Policy' }, - { id: 'bildnachweise', label: 'Bildnachweise' }, - { id: 'haftung', label: 'Haftungsausschluss' }, -]; +const ANTHRA = '#1A2030' +const NAVY = '#111D55' +const GOLD = '#EFBF04' +const CREAM = '#EFE5E3' +const FF = '"IBM Plex Sans", sans-serif' +const FB = '"Inter", sans-serif' -const ADDR = ( - <> - VM Verbands-Management GmbH
- Edelsbergstr. 8
- 80686 München
- Tel. +49 (0) 89 – 57007-0
- Fax +49 (0) 89 – 57007 260
- info@vm-verbands-management.de
- HRB 110222 AG München, gesetzl. vertreten durch Friedrich Marx - -); +type ImpressumCms = Partial + +const fallbackText = (value: unknown, fallback: string) => (typeof value === 'string' && value.length > 0 ? value : fallback) + +const fallbackArray = (value: unknown, fallback: T[]) => (Array.isArray(value) ? (value as T[]) : fallback) + +const isLegalItem = (item: unknown): item is LegalContentItem => + Boolean(item && typeof item === 'object' && 'type' in item && typeof (item as { type?: unknown }).type === 'string') export default function Impressum() { - const isMobile = useIsMobile(); + const isMobile = useIsMobile() + const cms = (useCmsRoute()?.doc?.impressum || {}) as ImpressumCms + const hero = { ...impressumContent.hero, ...(cms.hero || {}) } + const navigation = { ...impressumContent.navigation, ...(cms.navigation || {}) } + const sections = fallbackArray(cms.sections, impressumContent.sections) return (
- - {/* ── HERO ──────────────────────────────────────────────────────────── */}
(e.currentTarget.style.color = '#fff')} - onMouseLeave={e => (e.currentTarget.style.color = 'rgba(255,255,255,0.4)')} + onMouseEnter={(e) => (e.currentTarget.style.color = '#fff')} + onMouseLeave={(e) => (e.currentTarget.style.color = 'rgba(255,255,255,0.4)')} > - Zurück zur Website + {fallbackText(hero.backLabel, impressumContent.hero.backLabel)}
- Legal + {fallbackText(hero.eyebrow, impressumContent.hero.eyebrow)}

- Impressum + {fallbackText(hero.heading, impressumContent.hero.heading)}

-

- Angaben gemäß §5 TMG · Zur Datenschutzerklärung → -

+

- {/* ── BODY ──────────────────────────────────────────────────────────── */}
- - {/* LEFT – sticky TOC */} {!isMobile && ( )} - {/* RIGHT – content */}
- -
-

Informationen über den Verantwortlichen

- {ADDR} -

Angaben zum Webseitenbetreiber

- {ADDR} -

Inhaltlich Verantwortlicher gemäß TMG: VM Verbands-Management GmbH, gesetzl. vertreten durch Friedrich Marx

-
- -
-

Alle personenbezogenen Informationen werden vertraulich und gemäß den gesetzlichen Vorschriften behandelt, wie in der Datenschutzerklärung erläutert. Den Schutz der persönlichen Daten nehmen wir als Webseitenbetreiber sehr ernst.

-

Ein Besuch unserer Webseiten ist möglich, ohne Angaben zu Ihrer Person zu machen. Es werden bei jedem Zugriff auf unsere Website Nutzungsdaten durch Ihren Internetbrowser übermittelt und in Protokolldaten (Server-Logfiles) gespeichert. Zu diesen gespeicherten Daten gehören z. B. Name der aufgerufenen Seite, Datum und Uhrzeit des Abrufs, übertragene Datenmenge und der anfragende Provider (IP-Adresse). Diese Daten dienen ausschließlich der Gewährleistung eines störungsfreien Betriebs unserer Website und zur Verbesserung unseres Angebotes. Eine Zuordnung dieser Daten zu einer bestimmten Person ist nicht möglich.

-

Es erfolgt keine Zusammenführung dieser Daten mit anderen Datenquellen.

-
- -
-

Sofern Sie uns über unsere Kontaktdaten eine E-Mail übersenden bzw. uns über das Kontaktformular informieren, erheben wir Ihre personenbezogenen Daten (Name, E-Mail-Adresse, Telefonnummer, Betreff, Nachrichtentext) nur in dem von Ihnen zur Verfügung gestellten Umfang. Die Datenverarbeitung dient dem Zweck der Kontaktaufnahme.

-

Mit Absenden Ihrer Nachricht willigen Sie in die Verarbeitung der übermittelten Daten ein.

- Eine Verarbeitung erfolgt auf Grundlage des Art. 6 (1) lit. a DSGVO mit Ihrer Einwilligung. -

Sie können diese Einwilligung jederzeit durch Mitteilung an uns widerrufen. Ihre E-Mail-Adresse wird nur zur Bearbeitung Ihrer Anfrage genutzt. Ihre Daten werden anschließend gelöscht, sofern Sie der weitergehenden Verarbeitung und Nutzung nicht zugestimmt haben.

-
- -
-

Bei Vorliegen der gesetzlichen Voraussetzungen stehen Ihnen folgende Rechte nach Art. 15 bis 20 DSGVO zu:

-
    -
  • Recht auf Auskunft
  • -
  • Recht auf Berichtigung
  • -
  • Recht auf Löschung
  • -
  • Recht auf Einschränkung der Verarbeitung
  • -
  • Recht auf Datenübertragbarkeit
  • -
- Außerdem steht Ihnen nach Art. 21 (1) DSGVO ein Widerspruchsrecht gegen die Verarbeitungen zu, die auf Art. 6 (1) f DSGVO beruhen, sowie gegen die Verarbeitung zum Zwecke von Direktwerbung. -

Auf Wunsch können Sie uns kontaktieren. Die Kontaktdaten finden Sie unter Kontakt.

-
- -
-

Sie haben gemäß Art. 77 DSGVO das Recht, sich bei der Aufsichtsbehörde zu beschweren, wenn Sie der Ansicht sind, dass die Verarbeitung Ihrer personenbezogenen Daten nicht rechtmäßig erfolgt.

-

Die zuständige Aufsichtsbehörde:

- - Bayerisches Landesamt für Datenschutzaufsicht
- Postfach 606
- 91511 Ansbach
- Deutschland
- Tel. 09853-1300
- Fax 09853-981300
- E-Mail: poststelle@lda.bayern.de -
-
- -
-

Verantwortlicher im Sinne der Datenschutz-Grundverordnung und sonstiger datenschutzrechtlicher Bestimmungen ist der Webseitenbetreiber:

- {ADDR} -
- -
-

- Die Betreiber des Portals nehmen den Schutz der privaten Daten ernst. Die besondere Beachtung der Privatsphäre bei der Verarbeitung persönlicher Daten ist ein wichtiges Anliegen. Persönliche Daten werden gemäß den Bestimmungen des Bundesdatenschutzgesetzes BDSG verwendet; die Betreiber dieser Website verpflichten sich zur Verschwiegenheit. Diese Webseiten können Links zu Webseiten anderer Anbieter enthalten, auf die sich diese Datenschutzerklärung nicht erstreckt. Weitere wichtige Informationen finden sich auch in den Allgemeinen Nutzungsbedingungen. -

- - -

Personenbezogene Daten sind Informationen, die dazu genutzt werden können, die Identität zu erfahren. Darunter fallen Informationen wie richtiger Name, Adresse, Postanschrift, Telefonnummer. Informationen, die nicht direkt mit der wirklichen Identität in Verbindung gebracht werden (wie zum Beispiel favorisierte Webseiten oder Anzahl der Nutzer einer Site) fallen nicht darunter.

-

Die statistische Auswertung anonymisierter Datensätze bleibt vorbehalten. Wir nutzen die persönlichen Daten zu Zwecken der technischen Administration der Webseiten und zur Kundenverwaltung nur im jeweils dafür erforderlichen Umfang. Darüber hinaus werden persönliche Daten nur dann gespeichert, wenn diese freiwillig angegeben werden.

-
- - -

Wir verwenden personenbezogene Informationen nur für diese Webseite. Wir geben die Informationen nicht ohne ausdrückliches Einverständnis an Dritte weiter. Sollten im Rahmen der Auftragsdatenverarbeitung Daten an Dienstleister weitergegeben werden, so sind diese an das Bundesdatenschutzgesetz BDSG, andere gesetzliche Vorschriften und an diese Privacy Policy gebunden.

-

Erhebungen beziehungsweise Übermittlungen persönlicher Daten an staatliche Einrichtungen und Behörden erfolgen nur im Rahmen zwingender Rechtsvorschriften.

-

Bitte beachten Sie gesondert die Hinweise zur Datenverarbeitung im Zusammenhang mit Google reCAPTCHA unter Datenschutz.

-
- - -

Personen unter 18 Jahren sollten ohne Zustimmung der Eltern oder Erziehungsberechtigten keine personenbezogenen Daten an uns übermitteln. Wir fordern keine personenbezogenen Daten von Kindern an, sammeln diese nicht und geben sie nicht an Dritte weiter.

-
- - -

Wenn Sie uns personenbezogene Daten überlassen haben, können Sie diese jederzeit im Benutzerprofil wieder ändern und löschen. Für eine vollständige Löschung des Accounts bitte an den Webmaster wenden. Bis zu diesem Zeitpunkt erfolgte Beiträge in Foren, Kommentaren, Terminankündigungen und Artikeln bleiben allerdings unter Umständen erhalten – Informationen dazu auch bei den allgemeinen Nutzungsbedingungen.

-
- - -

Unser Online-Angebot enthält Links zu anderen Websites. Wir haben keinen Einfluss darauf, dass deren Betreiber die Datenschutzbestimmungen einhalten.

-
- - -

Die Beiträge auf unserer Seite sind für jeden zugänglich. Beiträge sollten vor der Veröffentlichung sorgfältig darauf überprüft werden, ob sie Angaben enthalten, die nicht für die Öffentlichkeit bestimmt sind. Die Beiträge werden möglicherweise in Suchmaschinen erfasst und auch ohne gezielten Aufruf dieser Website weltweit zugreifbar.

-

Trotz sorgfältiger inhaltlicher Kontrolle übernehmen wir keine Haftung für die Inhalte externer Links. Für den Inhalt der verlinkten Seiten sind ausschließlich deren Betreiber verantwortlich. Alle hier verwendeten Namen, Begriffe, Zeichen und Grafiken können Marken- oder Warenzeichen im Besitze ihrer rechtlichen Eigentümer sein. Die Rechte aller erwähnten und benutzten Marken- und Warenzeichen liegen ausschließlich bei deren Besitzern.

-
- - -

Bei Fragen und für Anregungen und Kommentare zum Thema Datenschutz bitte per Mail an den Webmaster wenden.

- {ADDR} -
-
- -
-
    -
  • Headline Maximilianeum: © Dietmar Egerer, München
  • -
  • Ingrid Großmann, Taufkirchen
  • -
  • EWIF Deutschland, München
  • -
  • icon.icons.com
  • -
  • Bilder zum Bayerischen Mittelstandspreis: © VM Verbands-Management GmbH, gesetzl. vertreten durch Friedrich Marx
  • -
-
- -
-

Trotz sorgfältiger inhaltlicher Kontrolle übernehmen wir keine Haftung für die Inhalte externer Links. Für den Inhalt der verlinkten Seiten sind ausschließlich deren Betreiber verantwortlich.

-

Alle hier verwendeten Namen, Begriffe, Zeichen und Grafiken können Marken- oder Warenzeichen im Besitze ihrer rechtlichen Eigentümer sein. Die Rechte aller erwähnten und benutzten Marken- und Warenzeichen liegen ausschließlich bei deren Besitzern.

-

Einen allgemeinen Haftungsausschluss finden Sie unter:{' '} - disclaimer.de -

-
- + {sections.map((section) => ( +
+ {fallbackArray(section.items, []).filter(isLegalItem).map((item, index) => ( + + ))} +
+ ))}
- ); + ) } -/* ── shared styles ── */ const body: React.CSSProperties = { fontFamily: '"Inter", sans-serif', fontSize: 15, @@ -217,7 +107,7 @@ const body: React.CSSProperties = { lineHeight: 1.85, margin: '0 0 16px', overflowWrap: 'anywhere', -}; +} function Section({ id, title, children }: { id: string; title: string; children: React.ReactNode }) { return ( @@ -227,11 +117,48 @@ function Section({ id, title, children }: { id: string; title: string; children: {title} -
- {children} -
+
{children}
- ); + ) +} + +function ContentItem({ item }: { item: LegalContentItem }) { + switch (item.type) { + case 'address': + return + case 'legal': + return + case 'list': + return ( +
    + {item.items.map((entry, index) => ( +
  • {entry}
  • + ))} +
+ ) + case 'subsection': + return ( + + {fallbackArray(item.items, []).filter(isLegalItem).map((subItem, index) => ( + + ))} + + ) + case 'paragraph': + default: + return + } +} + +function Paragraph({ item }: { item: Extract }) { + const variantStyle: React.CSSProperties = + item.variant === 'strong' + ? { fontWeight: 600, color: NAVY, marginBottom: 8 } + : item.variant === 'italic' + ? { fontStyle: 'italic', borderLeft: `3px solid ${GOLD}`, paddingLeft: 16, marginBottom: 32 } + : {} + + return

} function SubSection({ num, title, children }: { num: string; title: string; children: React.ReactNode }) { @@ -243,23 +170,29 @@ function SubSection({ num, title, children }: { num: string; title: string; chil {children} - ); + ) } -function AddressBlock({ children }: { children: React.ReactNode }) { - const isMobile = useIsMobile(); +function AddressBlock({ lines }: { lines: string[] }) { + const isMobile = useIsMobile() return (

- {children} + {lines.map((line, index) => ( + + {line} + {index < lines.length - 1 &&
} +
+ ))}
- ); + ) } -function Legal({ children }: { children: React.ReactNode }) { - const isMobile = useIsMobile(); +function Legal({ html }: { html: string }) { + const isMobile = useIsMobile() return ( -

- {children} -

- ); +

+ ) }