project_offer Frontend-Decomposition — Layer A (MVP) + Layer B (Post-MVP)
proposed Layered approach aufgelöst — siehe docs/plans/2026-05-14-001-feat-project-offer-sync-bh4sf.html
project_offer) wird in zwei Lieferschichten aufgeteilt. Layer A (MVP — bis 08.07.2026) umfasst alle Baseline-Features: View-Mode-Configs, Pathauto, Canvas-Komposition project_offer.full (nach Figma-Frame 1019-78888), Canvas-Card-Template project_offer.card (nach Frame 985-50077), Drupal-View project_offers_listing mit 5 BEF-Filtern, Canvas-Page /projektpartnerportal, Custom-Block PPP-Cross-Sell. Layer B (Post-MVP) enthält Vollparität (Skill-Badges, Geo-Filter, Merkliste, Matching-Rate, Projekt-Alert). Bundle project_offer spiegelt vacancy (Vacancy-Parität als Schablone), baut aber auf den gleichen SDC-Bausteinen auf.
Kontext
Story P2503-108 (F-290) synchronisiert SF-Publications via BH4SF REST-Pull nach Drupal (ADR-0029). Das Backend-Modul solcom_project_offer (Sub-Tasks P2503-128/129) ist bereits committed. Die Frontend-Seite — also die Darstellung der synced Project-Offers für Besucher — wurde im Refinement-Grilling als eigenständige Scope-Schicht identifiziert.
Das Figma liefert zwei entscheidende Frames für diesen Layer:
- Frame
1019-78888(Detail-Page): Komposition ausbubble-profile,card-meta-list,meta-row,button, Cross-Sell-Slot und Sticky-CTA-Bar. Entspricht konzeptionell dem Vacancy-Detail-Layout (ADR-0014), aber ohne editorialen Workflow (ADR-0026). - Frame
985-50077(List-Card): prop-driven Card-Variante mitcard-meta-list. Ist eine Variante des bestehendenbyte_theme/card-SDC (ADR-0009), kein eigenes SDC.
Das Refinement hat die naive Annahme „project_offer = vacancy 1:1 klonen" korrigiert. Die Unterschiede zur Vacancy-Detail-Page (ADR-0014) sind:
- Kein Featured-Slot (Vacancy hat Hero-Billboard-Slot oben)
- Kein Related-View im Detail (Vacancy hat „Ähnliche Stellen"-Block)
- Cross-Sell-Block statt Related-View: PPP-eigener Custom-Block-Plugin
- Sticky-CTA-Bar in Detail: eigener Canvas-Slot, keine generische Drupal-Block-Region
Gleichzeitig gibt es 9 weiterte Features (Layer B: Geo-Filter, Skill-Badges, Merkliste, …), die technisch komplex sind und für den MVP-Go-Live 08.07.2026 nicht benötigt werden. Sie einzeln in einen Riesen-PR zu packen würde die Review-Last erheblich erhöhen und den MVP-Termin gefährden.
Die zentrale Frage war: Aufteilen in Layer A + B, oder lieber alles auf einmal ausliefern, um spätere Nacharbeit zu vermeiden?
Entscheidung
Zweistufige Frontend-Lieferung. Layer A ist MVP-Pflicht (Sub-Tasks P2503-137 bis P2503-145 im laufenden PR), Layer B wird als separate Story nach MVP-Go-Live geplant (Sub-Tasks P2503-147 bis P2503-152).
Layer A — MVP (in Branch feature/P2503-108)
| Sub-Task | Inhalt | Figma-Ankerpunkt |
|---|---|---|
| P2503-137 | View-Mode-Configs default + card für project_offer | Frame 1019-78888 + 985-50077 |
| P2503-140 | Canvas-Komposition project_offer.full (Detail) | Frame 1019-78888 |
| P2503-141 | Canvas-Card-Template project_offer.card (List-Card) | Frame 985-50077 |
| P2503-142 | Pathauto-Pattern unter /projektpartnerportal/ | — |
| P2503-143 | Drupal-View project_offers_listing + 5 BEF-Filter | Frame 985-50077 (List-Kontext) |
| P2503-144 | Canvas-Page /projektpartnerportal + Custom-Block PPP-Cross-Sell | Frame 985-50077 (Page-Komposition) |
| P2503-145 | MAPPING-Update + ADR-0031 (diese Datei) | — |
Layer B — Post-MVP (separate Story nach 08.07.2026)
| Sub-Task | Inhalt |
|---|---|
| P2503-147 | Skill-Badges (Taxonomy-basierte Pill-Darstellung) |
| P2503-148 | Remote-Anteil-Slider (Range-Widget im Filter) |
| P2503-149 | Suchradius-Filter (Geo-Proximity via BEF + Geocoder) |
| P2503-150 | Merkliste (anonyme Session-Favoriten, User-Feature) |
| P2503-151 | Matching-Rate V1 (faked Score-Display) |
| P2503-152 | Projekt-Alert (Email-Subscription-Feature) |
Vacancy-Parität als Implementierungs-Schablone
Bundle project_offer spiegelt vacancy — Pathauto-Patterns (/projektpartnerportal/{title} analog /stellenangebote/{title}), gleiche SDC-Bausteine (bubble-profile, card-meta-list, button, meta-row). Das Vacancy-Canvas-Template (ADR-0014) dient als Blaupause für project_offer.full, aber die Slot-Konfiguration weicht ab (kein Featured-Hero-Slot, Cross-Sell statt Related-View). Die Canvas-Card-Template-Variante folgt dem Muster von vacancy.card (prop-driven, keine eigenen SDC-Bausteine, nur Konfiguration via Canvas-API).
Rejected alternatives
1 — Alles auf einmal (Layer A + Layer B) im MVP-PR
Scope würde 13 Sub-Tasks (P2503-137 bis P2503-152) plus den Pre-Build-Gate P2503-146 (FCSB-Live-Describe) in einem einzigen PR bündeln. Konsequenz:
- Geo-Filter (P2503-149) hat unbekannte Drupal-Geocoder-Abhängigkeiten, die zu Blocker-Risiko bis Go-Live werden.
- Merkliste (P2503-150) und Projekt-Alert (P2503-152) sind User-Features mit eigenem Auth-Scope — beide hängen von F-400 (User-Linking Phase 2) ab.
- Review-Last für den PR steigt linear mit Sub-Task-Anzahl; Scope-Creep-Risiko ist dokumentiert.
- Rejected: Go-Live 08.07.2026 ist harter Termin. Lieber solider MVP als verzögerter Vollausbau.
2 — Komplett eigenes Bundle ohne Vacancy-Parität
Statt project_offer als Vacancy-Spiegel zu modellieren, könnte ein eigenständiges Bundle mit eigenen Feldnamen, eigenen View-Modes und eigenen SDCs gebaut werden.
- Vorteil: maximale Entkopplung.
- Nachteil: Theme-Drift — zwei fast identische SDC-Konfigurationen (
vacancy.fullundproject_offer.full) divergieren über Zeit, Pflege verdoppelt sich. - Nachteil: Keine Code-Wiederverwertung der vorhandenen Vacancy-Canvas-Templates und SDC-Patterns, die bereits validated sind (ADR-0014, ADR-0013).
- Rejected: Vacancy-Parität ist praxiserprobte Schablone, die Drift-Risiko minimal hält.
3 — Reaktive State-Bibliothek (Vue/React) für Filter
Für den project_offers_listing-View könnten die 5 Filter als Vue- oder React-Komponenten implementiert werden, die per API die Ergebnisliste nachladen.
- Vorteil: flüssigere UX beim Filtern, kein Full-Page-Reload.
- Nachteil: JS-Bundle für einen einzigen View-Filter, der primär SEO-relevante Ergebnisse ausliefert (Publications sind indexiert). BEF + AJAX-View reicht funktional aus.
- Nachteil: erhöhte Komplexität beim Decoupled-Setup (API-Endpoint, CORS, Auth für anonyme User) weit over-engineered für den vorliegenden Use Case.
- Rejected: BEF + AJAX-View ist der etablierte Drupal-Weg für gefilterte Views, kein eigenständiges JS-Framework nötig.
4 — Canvas-Card als eigenes SDC statt Variante von byte_theme/card
Für Frame 985-50077 (List-Card) könnte ein eigenes byte_theme/card-project-offer-SDC angelegt werden.
- Nachteil: Card-Familie (ADR-0009, ADR-0017) hat klare Zuständigkeiten. Ein fünftes Card-SDC für einen single-bundle Use Case widerspricht dem Pattern.
- Figma-Analyse hat bestätigt: Frame
985-50077ist strukturell identisch mit der bestehenden Card-Variante — nur die Prop-Belegung unterscheidet sich (Publication-spezifische Felder). - Rejected: Prop-driven Variante via Canvas-Card-Template ist das richtige Pattern (ADR-0009).
Consequences
Sofortige Konsequenzen (Layer A)
- Zwei neue Canvas-Templates:
canvas.content_template.node.project_offer.full(P2503-140) undcanvas.content_template.node.project_offer.card(P2503-141) wurden als Config-YAML inconfig/sync/angelegt. Figma-Frames1019-78888resp.985-50077sind die visuellen Ankerpunkte. - Pathauto-Pattern:
/projektpartnerportal/[node:title](P2503-142). Neue URL-Struktur ist nach Go-Live kaum noch änderbar — Entscheidung ist bindend. - View
project_offers_listing: 5 BEF-Filter (Branche, Skills, Remote-Anteil, Ort/PLZ, Vollzeit/Teilzeit). Geo-Proximity-Filter explizit Layer B. - Custom-Block-Plugin PPP-Cross-Sell: im Custom-Modul
solcom_project_offeralsBlockBase-Plugin (P2503-144). Dieser Block ersetzt den Related-View-Slot in Detail-Page und List-Page. - Kein Editorial-Workflow (ADR-0026): project_offer-Nodes werden nicht durch Redakteure bearbeitet. Der Drupal-Publish-Status folgt dem SF-Lifecycle (
SC_PublicationStatus__c == "Online"). Die Vacancy-Editorial-Ausnahme gilt analog.
Bewusste Restrisiken
- Layer-B-Schulden: Die 6 Post-MVP-Features sind in Jira als Sub-Tasks erfasst (P2503-147 bis P2503-152), aber noch nicht Bestandteil eines priorisierten Sprints. Risiko: Layer B wird nach Go-Live deprioritisiert und bleibt dauerhaft offen.
- Geo-Filter-Blocker: P2503-149 (Suchradius-Filter) hat unbekannte Geocoder-Drupal-Abhängigkeiten und könnte bei Aufnahme in Layer B seinerseits einen Pre-Build-Gate brauchen.
- Vacancy-Parität-Drift: Wenn Vacancy-Canvas-Templates (ADR-0014) sich weiterentwickeln, muss
project_offer.fullmanuell nachgezogen werden. Kein automatischer Sync.
Folge-Stories (nicht in diesem ADR)
- Layer-B-Sprint-Planung nach Go-Live (Termin offen).
- User-Linking Phase 2 (F-400) als Voraussetzung für Merkliste (P2503-150) und Projekt-Alert (P2503-152).
- FCSB-Live-Describe (P2503-127) als Pre-Build-Gate für Layer-B-Features, die auf Field-Drift sensitiv sind.
Amendment 2026-05-14
- Naming-Update: Bundle
sf_publication→project_offer, Modulsolcom_sf_publication→solcom_project_offer, Viewpublications_listing→project_offers_listing. Alle Canvas-Template-IDs (node.sf_publication.*) entsprechend umgestellt. Der Rename ist als atomarer PR committed (Slice 1 des Plans). Begründung: Domain-Begriff von solcom.de/de/projektportal übernommen; „Publication" ist nur noch Implementierungs-Detail des BH4SF-RecordType. - Layer-A/B-Trennung aufgelöst: Der Grilling-Output vom 2026-05-14 hat ergeben, dass beide Layers MVP-Pflicht sind — ein vollständiges Projektpartnerportal (inkl. Skill-Badges, Remote-Anteil, Geo-Filter, Merkliste, Matching-Rate, Projekt-Alert) ist Stakeholder-Erwartung zum Go-Live 08.07.2026. ADR-0031 bleibt als historisches Dokument für die ursprüngliche Refinement-Linie; die operative Planung liegt in docs/plans/2026-05-14-001-feat-project-offer-sync-bh4sf.html (12 Slices, alle AFK).
Customer-Lens
customer-lens-reviewer wurde für diesen ADR noch nicht konsultiert. Status bleibt proposed. Die Konsultation obliegt dem Orchestrator nach dem Merge des PRs feature/P2503-108. Erst danach kann der ADR auf accepted gesetzt werden.
Voraussichtliche Lens-Anwendung (zur Vorbereitung der Konsultation):
- Figma-Parity-Lens: Frames
1019-78888und985-50077müssen im Canvas-Output erkennbar wiedergegeben werden. Sticky-CTA-Bar in Detail und Cross-Sell-Block in List sind für die Besucher-Journey relevant. - A11y-Lens: Card-Meta-List-SDC und Bubble-Profile-SDC tragen WCAG-2.1-AA-Verantwortung. BEF-Filter-Widgets müssen keyboard-navigierbar sein.
- Performance-Lens (N/A für Layer A): BEF + AJAX-View wird clientseitig keine signifikante JS-Last hinzufügen. Layer B (Geo-Filter) könnte Maps-Integration erfordern — dann Performance-Lens relevant.