P2503 · Konfigurationen · F-170

Personen / Team Content Type

REFINED P2503-97 Erstellt: 2026-05-11 · Refined: 2026-05-25

User Story

Als Nutzer moechte ich die SOLCOM-Mitarbeitenden mit Foto, Funktion und Kontaktinformationen sehen koennen, um Ansprechpartner schnell zu finden und Vertrauen aufzubauen.

Technische Beschreibung

Generischer Custom-Content-Type node.type.person mit Feldern Name, Funktion, Foto, Telefon, Email, LinkedIn-URL. View-Mode card_quote rendert via neuer prop-driven SDC byte_theme:card-person (zweiter prop-driven Sibling neben card, nach ADR-0009-Pattern). Editor-Platzierungs-Anker ist eine Canvas-Code-Component CardPerson.jsx, die node.person via JSON:API laedt.

Granulare Vacancy↔Person-Verknuepfung (Personio-Mapping per recruiter_id, ER-Feld pro Vacancy) ist deferred zu Folge-Story. Im MVP nutzen wir config_pages.career_settings.field_default_recruiter als simplen Fallback.

Architektur (locked 2026-05-25)

Decision-Authority: ADR-0046. Verwandte ADRs: ADR-0009 (prop-driven Card-Pattern), ADR-0026 (Vacancy-Editorial-Exclusion), ADR-0004 (Conditional-Schema-Anti-Pattern).

node.type.person                                       ← Story-Scope
  field_name              string,    required
  field_funktion          string,    required
  field_photo             image,     required
  field_phone             telephone, required  (klickbar als tel:-Link)
  field_email             email,     optional  (klickbar als mailto:-Link)
  field_linkedin_url      link,      optional  (klickbar als externer Link)

entity_view_display.node.person.card_quote             ← Story-Scope
  rendert via SDC: byte_theme:card-person

components/card-person/                                ← Story-Scope
  card-person.component.yml   (prop-driven, kein bubble-card-frame)
  card-person.twig            (Figma-Layout 62110-27008 + Kontakt-Stack)
  card-person.css             (Tailwind @apply, .solcom-card-person)
  tests/src/Kernel/CardPersonComponentTest.php

canvas-components/CardPerson.jsx                       ← Story-Scope
  prop: personNid (number)
  fetch via JsonApiClient + SWR → node.person
  render: <card-person name=... funktion=... photo=...
                        phone=... email=... linkedin_url=... />

config_pages.career_settings.field_default_recruiter   ← MVP-Fallback
  ER → node.person   (eine Default-Person, im Vacancy-Detail
                          auto-instanziert via Canvas-Template)

Vacancy↔Person granulare Verknuepfung               ← Folge-Story
  (Personio-Mapping per recruiter_id, ER-Feld auf node.vacancy,
   Verhandlung von ADR-0026)

Akzeptanzkriterien

AKBeschreibung
AK1 Es existiert ein Content-Type person mit den Feldern Name, Funktion, Foto (alle Pflicht), Telefon (Pflicht) sowie Email und LinkedIn-URL (optional). Type-Label „Person", machine_name person.
AK2 Ein Editor kann unter /node/add/person eine Person anlegen. Fehlt eines der Pflichtfelder, erscheint eine Validation-Meldung; sind alle gefuellt, wird der Node gespeichert und ist unter /admin/content?type=person auffindbar.
AK3 Im View-Mode card_quote rendert node.person die SDC card-person: Foto rund links (160px), Funktion klein + Name gross rechts, darunter Kontakt-Stack mit tel:-Link, mailto:-Link, externem LinkedIn-Link.
AK4 Optional-Felder (Email, LinkedIn) werden ausgeblendet, wenn leer. Kein Empty-Render-Artefakt im DOM (kein leeres <li>, kein dead Anchor).
AK5 Im Canvas-Editor kann ein Editor einen Block „Card · Person" platzieren, eine Person auswaehlen und publish — die Person erscheint live auf der Canvas-Page an der gewaehlten Position.
AK6 Die gerenderte Card matched Figma-Frame 62110-27008 bei Desktop 1280px (Visual-Diff via solcom-figma-reviewer). Abweichungen durch die Kontakt-Extension sind in der Section <section id="figma-extensions"> dieses Story-Hubs dokumentiert.
AK7 Die Card ist responsive bei Tablet 768px und Mobile 375px (kein horizontaler Scroll, lesbare Type-Sizes). Layout darf bei kleinen Viewports von horizontal auf vertikal umschalten.

QA-Plan (testbare Click-Pfade)

Pflicht-Section fuer ralph-loop-Kompatibilitaet (ADR-0037). Eine Tabelle AK-# / URL / Action / Expected.

AKURLActionExpected
AK1 /admin/structure/types Suche nach „Person" in der Type-Liste Type-Eintrag „Person" sichtbar, machine_name person
AK1 /admin/structure/types/manage/person/fields Feld-Liste pruefen 6 Felder: field_name, field_funktion, field_photo, field_phone, field_email, field_linkedin_url. Required-Flags wie spezifiziert.
AK2 /node/add/person Pflichtfelder leer lassen, „Speichern" klicken 4 Required-Validation-Errors (Name, Funktion, Foto, Telefon)
AK2 /node/add/person Alle Pflichtfelder befuellen (z.B. „Laura Gerardi", „HR Manager", Foto-Upload, „07121/1277-344"), „Speichern" Node erstellt, Redirect auf Detail-Page, Node-ID vergeben
AK3 Kernel-Test tests/src/Kernel/CardPersonComponentTest.php::testCardQuoteRender() Render node.person in card_quote view_mode DOM enthaelt .solcom-card-person, <img>, <h3> mit Namen, <a href="tel:…">, <a href="mailto:…">, <a href="https://linkedin.com/…">
AK4 Kernel-Test mit Person ohne Email + LinkedIn DOM-Inspektion Keine <li> oder <a> fuer Email / LinkedIn; nur Tel-Link sichtbar
AK5 /admin/canvas/<test-page-id> „Add Block" → „Card · Person" → Person-Picker → eine Person waehlen → „Save" → „Publish" Live-Page rendert die Person-Card an der gewaehlten Position
AK6 Live-Page mit Person-Card, Desktop 1280px Browser Screenshot vs. Figma-Frame 62110:27008 (via solcom-figma-reviewer) Pixel-Diff in Toleranz; Kontakt-Stack-Extensions als „intentional" markiert
AK7 Live-Page, Browser-Resize auf 768px und 375px Visual-Inspektion + DevTools-Pruefung auf Overflow Keine horizontalen Scrollbars, Card bleibt lesbar, Layout darf bei <768px auf vertikal umschalten

Figma-Extensions (intentional Drifts gegenueber Frame 62110-27008)

Figma-Baseline ist Frame 62110:27008 (ContentItem, Variant person). Folgende Elemente werden ueber Figma hinaus implementiert — solcom-figma-reviewer muss sie als „intentional Extension" werten, nicht als Drift:

  • Kontakt-Stack unter Name: Liste mit Tel-Link (Pflicht), Email-Link (optional, ausblendbar) und LinkedIn-Link (optional, ausblendbar). Figma zeigt nur Position + Vorname Name.
  • Tel-Schema: <a href="tel:+49…"> mit international formatierter Nummer; Display bleibt lokal („Tel.: 07121/1277-344").
  • Email-Schema: <a href="mailto:…"> mit voller Adresse; Display zeigt die Adresse selbst.
  • LinkedIn-Schema: externer Link mit rel="noopener noreferrer", aria-label falls icon-only.
  • Responsive-Switch: Bei Mobile (<768px) darf das Layout von horizontal auf vertikal umschalten. Figma zeigt nur Desktop.

Build-Workflow (Skill-Kette)

  1. solcom-design-mapping — MAPPING.html-Row fuer Frame 62110:27008 anlegen (heute fehlt). Status: partial mit Verweis auf diese Story und ADR-0046.
  2. solcom-figma-to-sdc — SDC card-person bauen. Inner Loop: Figma-Spec lesen, component.yml erzeugen, Twig + CSS bauen, Re-Check gegen Figma bei jedem Schritt (kein Post-Hoc-Diff).
  3. drupal-canvas — Canvas-Code-Component CardPerson.jsx bauen + via Workbench testen. JsonApiClient + SWR fuer Fetch.
  4. solcom-figma-reviewer — Visual-Compliance-Diff bei Mobile 375 / Tablet 768 / Desktop 1280. Diese Section figma-extensions als Reference fuer intentional Drifts. Kontakt-Stack-Layout-Abweichung muss als „matched mit Extension" abgehakt werden.

Risiken & Verifikations-Punkte

Risiko VC-1 (Vacancy-Canvas-Auto-Instanziierung)

Der MVP-Fallback nimmt an, dass canvas.canvas.node.vacancy.full.yml den CardPerson-Block mit der Default-NID aus config_pages.career_settings.field_default_recruiter automatisch in der Sidebar rendert. Vor Build verifizieren: Enthaelt das Canvas-Template diesen Eintrag, oder muss ein Editor pro Vacancy einen Block manuell setzen? Letzteres bricht den MVP-Workflow und stellt ADR-0026 in Frage. Verantwortlich: erster Sub-Task der Story.

Risiko CP-1 (config_pages-Pflege-UI)

Editor:innen muessen config_pages.career_settings erreichen koennen. Default-Toolbar enthaelt vermutlich keinen Eintrag. Sub-Task „Toolbar-Link / Editor-Onboarding fuer Career-Settings" ist Pflicht.

Risiko A11Y-1 (Kontakt-Stack-Semantik)

Tel/Email/LinkedIn-Stack braucht korrekte tel:/mailto:-Schemas, aria-label fuer icon-only LinkedIn-Link und WCAG-2.1-AA-Kontrast (Figma zeigt weisse Schrift auf dunklem BG, das ist im byte_theme zu validieren). Diff-Zeit-Sache fuer den drupal-twig-sdc-reviewer.

Risiko SWR-1 (Fetch-Performance)

Pro CardPerson-Instance ein JSON:API-Request. Im MVP nur eine Instance pro Vacancy-Detail-Page → unproblematisch. Bei spaeterer Team-Uebersicht (Folge-Story) N+1-Risiko: Sammel-Request via filter[id-in] oder Pre-Hydration im Canvas-Template einplanen.

Risiko DSGVO-1 (Recruiter-Kontakte oeffentlich)

Telefonnummern und Email-Adressen einzelner Mitarbeitender werden oeffentlich gerendert. Stakeholder-Klaerung (HR / Datenschutz) vor Live-Schaltung empfohlen. Liegt ausserhalb des Story-Scopes, aber als „Geschaeftsrisiko" hier vermerkt.

Abhaengigkeiten

  • SDC-Familie: setzt das Pattern aus ADR-0009 fort. Existierende bubble-card-frame-Infrastruktur wird nicht benoetigt, da card-person keinen Bubble-Frame hat.
  • config_pages-Modul: muss aktiviert sein (vermutlich heute schon, da andere Folge-Stories es nutzen).
  • JsonApiClient + SWR: Canvas-Standard-Stack, sollte im byte_theme heute schon verfuegbar sein (vgl. Skill drupal-canvas).
  • Telephone-Field-Modul: Drupal-Core-Modul telephone — einmalig aktivieren via drush en telephone (kein composer require noetig).

Folge-Stories (unter F-170)

  1. Granulare Vacancy↔Person-Verknuepfung (ADR-pflichtig — Personio-Mapping-Option vs. Editorial-Override).
  2. View-Mode card_team + Team-Uebersichts-Drupal-View + Navigations-Menue-Eintrag „Team".
  3. View-Mode full + Person-Detail-Page mit eigener Route (Sitemap-Node N33).
  4. Bio-Feld (text_long, Rich-Editor) fuer Person-Detail-Page.
  5. Optional: card-quote-statement-SDC fuer den im Figma-Frame 62110:27008 als title-Variant enthaltenen Editorial-Quote-Block.

Grilling-Log (2026-05-25)

Refinement-Session via Skill solcom-grill. Stakeholder-Entscheidungen in Reihenfolge:

  1. Scope → Generischer Person-Content-Type (nicht nur Recruiter-Card)
  2. Figma vs. Live → Figma-Layout 62110-27008 ist Target, Live-Layout wird abgeloest, ABER Kontakt-Optionen (Tel/Email/LinkedIn) werden ergaenzt
  3. MVP-View-Modes → Nur card_quote + Vacancy-Sidebar-Integration; card_team + full sind Folge-Stories
  4. Kontakt-Felder → Tel (Pflicht) + Email (optional) + LinkedIn (optional); kein CTA-Button
  5. Vacancy↔Person-Verknuepfung → deferred zu Folge-Story; im MVP nur simpler Default-Recruiter-Fallback via config_pages
  6. Platzierung → Canvas-Code-Component (statt Drupal-Block-Plugin oder hardcoded Twig-Render)
  7. SDC-Topologie → Neue card-person als prop-driven Sibling (nicht Erweiterung von card, nicht dual-mode card-quote)

Customer-Lens-Reviewer-Findings (alle nicht-blockierend): title-Variant-Scope-Klarstellung ↔ in SDC-Description integriert; Vacancy-Canvas-Auto-Instanziierung ↔ als Risiko VC-1 notiert; config_pages-Pflege-UI ↔ als Risiko CP-1 notiert; Canvas-Component-Naming CardPerson.jsx ↔ uebernommen.