29-05-2026 Optimierungen Fixes am Code
This commit is contained in:
parent
e8c47b7553
commit
4bb9094207
31 changed files with 5141 additions and 76 deletions
|
|
@ -0,0 +1,509 @@
|
|||
|
||||
|
||||
**Datum:** 12. Mai 2026 **Status:** Technisches Implementierungs-Konzept **Tech-Stack:** Laravel 12+, Livewire 4 / Volt, Tailwind CSS (v4), Alpine.js (über Livewire) **Bezug:** Konzept-Update 3 (Multi-Brand-Architektur), Konzept-Update 4 (Positionierung), Brand-Landing-Konzept businessportal24
|
||||
|
||||
> **IST-Stand 21.05.2026**: Multi-Brand-Architektur ist umgesetzt
|
||||
> (`config/domains.php`, `ThemeServiceProvider`, getrennte Vite-Builds
|
||||
> `portal` + `web`). Die Hub-Migration des User Backends ist als
|
||||
> eigene Roadmap in `dev/frontend/hub-flux/` dokumentiert (Phasen 0–7
|
||||
> abgeschlossen, Phase 8 in Planung). Der hier beschriebene Brand-Context
|
||||
> wird ueber `View::share()` global aufgeloest.
|
||||
|
||||
---
|
||||
|
||||
## 1. Leitprinzipien
|
||||
|
||||
Vier Regeln, an denen sich jede technische Entscheidung in diesem Dokument messen muss:
|
||||
|
||||
1. **Ein Codebase, viele Brands.** Kein Branch pro Portal, keine duplizierten Views. Differenzierung über Konfiguration, CSS-Variablen und gezielte View-Overrides.
|
||||
2. **Brand-Awareness zentral aufgelöst, nicht in Komponenten verteilt.** Eine Komponente fragt nicht „bin ich auf businessportal24?". Sie konsumiert eine `$brand`-Context-Variable und rendert entsprechend.
|
||||
3. **Livewire/Volt nur wo nötig.** Statische Komponenten bleiben pures Blade. Reaktivität ist ein Kostenfaktor (Server-Roundtrips, Hydration, State-Management) – sie muss verdient werden.
|
||||
4. **Solo-tauglich heißt: jede Entscheidung muss in 6 Monaten noch verständlich sein.** Lieber explizit als clever.
|
||||
|
||||
## 2. Brand-Auflösung (Multi-Tenant-Pattern)
|
||||
|
||||
### Brand-Resolution-Pipeline
|
||||
|
||||
```
|
||||
Request → Middleware → BrandResolver → Brand-Context im Container
|
||||
→ View-Pfad-Override
|
||||
→ Config-Override
|
||||
→ Layout-Auswahl
|
||||
```
|
||||
|
||||
**Schritt 1: Domain-Mapping**
|
||||
|
||||
Die `brands`-Tabelle aus Update 3 enthält pro Brand mindestens:
|
||||
|
||||
- `slug` (z.B. `businessportal24`, `presseecho`, `hub`)
|
||||
- `primary_domain` (z.B. `businessportal24.com`)
|
||||
- `theme_key` (z.B. `bp24`, `pe`) – Verweis auf CSS-Token-Set
|
||||
- `config_path` (z.B. `brands/businessportal24.php`)
|
||||
- `is_publisher_hub` (boolean)
|
||||
-ist zu prüfen, teils schon im System angelegt!
|
||||
|
||||
**Schritt 2: Middleware**
|
||||
|
||||
```php
|
||||
// app/Http/Middleware/ResolveBrand.php
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$brand = Cache::rememberForever(
|
||||
"brand.domain.{$request->getHost()}",
|
||||
fn() => Brand::query()
|
||||
->where('primary_domain', $request->getHost())
|
||||
->orWhereJsonContains('aliases', $request->getHost())
|
||||
->firstOrFail()
|
||||
);
|
||||
|
||||
app()->instance(Brand::class, $brand);
|
||||
View::share('brand', $brand);
|
||||
Config::set('brand', $brand->config());
|
||||
|
||||
return $next($request);
|
||||
}
|
||||
```
|
||||
|
||||
Cache ist hier wichtig – die Domain-zu-Brand-Auflösung passiert bei jedem Request. `rememberForever` mit explizitem Cache-Bust beim Brand-Update.
|
||||
-ist zu prüfen, teils schon im System angelegt!
|
||||
|
||||
**Schritt 3: Brand im Container**
|
||||
|
||||
Jede Klasse kann via Dependency Injection auf die aktuelle Brand zugreifen:
|
||||
|
||||
```php
|
||||
public function __construct(private Brand $brand) {}
|
||||
```
|
||||
|
||||
In Blade-Templates ist `$brand` durch `View::share()` direkt verfügbar.
|
||||
|
||||
### Lokale Entwicklung
|
||||
|
||||
Lokal arbeiten mit `.test`-Domains in Docker (devserver) auf dem Server via Treafik:
|
||||
|
||||
- `businessportal24.test`
|
||||
- `presseecho.test`
|
||||
- `pressekonto.test`
|
||||
|
||||
Alle zeigen auf dieselbe Codebase, die Middleware löst per Hostname auf. Keine Subdomains, keine Port-Tricks – schmerzfreies lokales Multi-Brand-Setup.
|
||||
|
||||
## 3. Theming-System (Tailwind v4 + CSS Custom Properties)
|
||||
|
||||
### Empfehlung: Tailwind v4
|
||||
|
||||
Falls die Migration auf v4 noch offen ist: **jetzt machen**. Die `@theme`-Direktive in v4 macht Multi-Brand-Theming dramatisch einfacher als das v3-Config-Konstrukt. Native CSS-Variablen, keine PostCSS-Akrobatik mehr.
|
||||
|
||||
### Token-Architektur
|
||||
|
||||
Drei Ebenen (Beispiel ):
|
||||
|
||||
```css
|
||||
/* Ebene 1: Globale Design-Tokens (markenneutral) */
|
||||
@theme {
|
||||
--font-serif: 'Source Serif 4', Georgia, serif;
|
||||
--font-sans: 'Inter', system-ui, sans-serif;
|
||||
|
||||
--spacing-section: 5rem;
|
||||
--spacing-section-tight: 3rem;
|
||||
|
||||
--radius-card: 2px; /* fast keine Rundungen, editorial */
|
||||
}
|
||||
|
||||
/* Ebene 2: Semantische Tokens (markenneutral, aber rollenbasiert) */
|
||||
:root {
|
||||
--color-text-primary: var(--brand-text);
|
||||
--color-text-muted: var(--brand-text-muted);
|
||||
--color-surface: var(--brand-surface);
|
||||
--color-accent: var(--brand-accent);
|
||||
--color-cta-bg: var(--brand-cta-bg);
|
||||
--color-cta-fg: var(--brand-cta-fg);
|
||||
--color-hub-transition: var(--brand-hub-bg);
|
||||
}
|
||||
|
||||
/* Ebene 3: Brand-spezifische Werte */
|
||||
[data-brand="businessportal24"] {
|
||||
--brand-text: #1a1a1a;
|
||||
--brand-text-muted: #6b6b6b;
|
||||
--brand-surface: #fafaf7; /* warmer off-white */
|
||||
--brand-accent: #d94e1f; /* gedämpftes Orange */
|
||||
--brand-cta-bg: #d94e1f;
|
||||
--brand-cta-fg: #ffffff;
|
||||
--brand-hub-bg: #1a2540; /* dunkelblau, Störer */
|
||||
}
|
||||
|
||||
[data-brand="presseecho"] {
|
||||
--brand-text: #f0f0e8;
|
||||
--brand-text-muted: #a0a098;
|
||||
--brand-surface: #1f2620; /* dunkelgrün-anthrazit */
|
||||
--brand-accent: #5a8a6b; /* gedämpftes Grün */
|
||||
--brand-cta-bg: #5a8a6b;
|
||||
--brand-cta-fg: #ffffff;
|
||||
--brand-hub-bg: #1a2540; /* Hub-Farbe bleibt konstant! */
|
||||
}
|
||||
```
|
||||
|
||||
### Brand-Aktivierung im Layout
|
||||
|
||||
```blade
|
||||
{{-- resources/views/layouts/brand.blade.php --}}
|
||||
<!DOCTYPE html>
|
||||
<html lang="de" data-brand="{{ $brand->slug }}">
|
||||
<head>
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
{{-- Brand-CSS wird im app.css via @import geladen, oder optional separat: --}}
|
||||
@if($brand->has_custom_css)
|
||||
<link rel="stylesheet" href="{{ asset("themes/{$brand->slug}.css") }}">
|
||||
@endif
|
||||
</head>
|
||||
<body class="bg-[var(--color-surface)] text-[var(--color-text-primary)]">
|
||||
{{ $slot }}
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
**Wichtig:** Der `data-brand`-Attribut auf `<html>` ist der einzige Hebel, der den gesamten Look umschaltet. Alle Tailwind-Utilities, die brand-spezifische Werte nutzen, greifen über CSS-Variablen darauf zu.
|
||||
|
||||
### Pragmatische Tailwind-Nutzung
|
||||
|
||||
Die Komponenten schreiben **nicht** `bg-orange-600` (das wäre brand-spezifisch im Markup festgenagelt). Stattdessen:
|
||||
|
||||
```blade
|
||||
<button class="bg-[var(--color-cta-bg)] text-[var(--color-cta-fg)] hover:opacity-90">
|
||||
Mitteilung einreichen
|
||||
</button>
|
||||
```
|
||||
|
||||
Oder noch sauberer mit eigenen Tailwind-Utility-Klassen, die in `app.css` definiert werden:
|
||||
|
||||
```css
|
||||
@layer components {
|
||||
.btn-cta {
|
||||
@apply bg-[var(--color-cta-bg)] text-[var(--color-cta-fg)]
|
||||
px-6 py-3 rounded-sm font-medium hover:opacity-90 transition;
|
||||
}
|
||||
.btn-hub {
|
||||
@apply bg-[var(--color-hub-transition)] text-white
|
||||
px-8 py-6 block;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
So bleibt das Markup brand-agnostisch und die Stilfragen zentralisiert.
|
||||
|
||||
## 4. Komponenten-Hierarchie und Engine-Wahl
|
||||
|
||||
### Drei Render-Modi, drei Anwendungsbereiche
|
||||
|
||||
|Modus|Wann verwenden|Performance|Beispiele|
|
||||
|---|---|---|---|
|
||||
|**Blade Component**|Statisches Markup, keine Interaktion|⚡⚡⚡|TopBar, Footer, PressItem, StatsRow|
|
||||
|**Volt (Single-File)**|Lokaler State, einfache Reaktivität, Lifecycle einfach|⚡⚡|AdHocTicker, HeroSlider, Search|
|
||||
|**Klassisches Livewire**|Komplexe Komponenten mit Services, Events, mehrere Methoden|⚡|PressEditor, NewsroomDashboard|
|
||||
|
||||
### Volt: konkrete Empfehlung
|
||||
|
||||
Volt ist für dieses Projekt **die richtige Wahl als Default für reaktive Komponenten** – aber nicht für statische. Die Gründe:
|
||||
|
||||
**Pro Volt:**
|
||||
|
||||
- Single-File-Komponenten: PHP-Logik + Blade-Template + Tailwind-Klassen in einer Datei. Solo-Entwickler-Freundlichkeit ist hoch.
|
||||
- Funktionale API ist deutlich weniger Boilerplate als klassische Livewire-Klassen.
|
||||
- Volt-Komponenten lassen sich genau wie Livewire-Komponenten lazy-laden (`<livewire:lazy ...>`), was für Above-the-fold-Performance wichtig ist.
|
||||
|
||||
**Kontra Volt:**
|
||||
|
||||
- Für reine Display-Komponenten ist Volt overkill. Eine `<x-press-item>` ohne State soll keine Livewire-Komponente sein – Hydration und Wire-Tracking sind unnötige Kosten.
|
||||
- Wenn eine Komponente Services injiziert, ein eigenes Test-Setup braucht oder mehr als ~150 Zeilen wächst, ist eine klassische Livewire-Klasse besser strukturierbar.
|
||||
|
||||
**Faustregel:**
|
||||
|
||||
> Renderst du HTML ohne Server-Interaktion? → Blade Component. Brauchst du `wire:model`, `wire:click`, Polling oder reaktiven State? → Volt. Wird die Komponente komplex, hat Services, eigene Tests? → Klassisches Livewire.
|
||||
|
||||
### Konkrete Komponenten-Inventur aus den Screens
|
||||
|
||||
Aufgeschlüsselt nach Engine. Das ist die direkte Übersetzung der Screens in technische Bausteine:
|
||||
|
||||
#### Blade-Komponenten (statisch, hochfrequent wiederverwendet)
|
||||
|
||||
```
|
||||
<x-brand.top-bar> -- Wirtschafts-Ticker, Sprachen, Newsletter/RSS
|
||||
<x-brand.header> -- Logo, Suche, CTAs
|
||||
<x-brand.rubriken-nav> -- Hauptnavigation (Wirtschaft, Tech, Finanzen...)
|
||||
<x-brand.footer> -- Footer mit Cross-Brand-Hinweis
|
||||
|
||||
<x-press.item> -- Standard-Listen-Eintrag mit Slots für Varianten
|
||||
<x-press.item-hero> -- Große Hero-Variante
|
||||
<x-press.item-sidebar> -- Kompakte Sidebar-Variante (mit Nummerierung)
|
||||
<x-press.byline> -- Quelle · Zeit · Lesezeit (wiederverwendbar)
|
||||
|
||||
<x-ui.section-header> -- "§ 01" + Label + H2 (das Editorial-Pattern)
|
||||
<x-ui.stats-row> -- Drei-/Vier-Spalten-Statistik
|
||||
<x-ui.button> -- CTA-Button mit Varianten (primary, secondary, hub)
|
||||
<x-ui.badge> -- Branchen-Marker, "Geprüft"-Label
|
||||
|
||||
<x-hub.transition-block> -- DER dunkelblaue Störer (siehe Briefing)
|
||||
<x-hub.cta> -- Inline Hub-CTA (Variante des Störers)
|
||||
|
||||
<x-quality.standard-footer> -- "Alle Pressemitteilungen werden geprüft..."
|
||||
```
|
||||
|
||||
#### Volt-Komponenten (reaktiv, isolierter State)
|
||||
|
||||
```
|
||||
<livewire:ad-hoc-ticker /> -- Auto-refresh alle 30s, Polling
|
||||
<livewire:hero-slider /> -- 3 Top-Meldungen, auto-rotate mit Alpine
|
||||
<livewire:press-search /> -- Suche im Header
|
||||
<livewire:press-list-filter /> -- "Alle · Heute · Diese Woche" Tabs
|
||||
<livewire:newsroom-list /> -- Newsroom-Sidebar mit "heute aktiv" Polling
|
||||
<livewire:branchen-index /> -- Live-Werte mit ± Indikatoren
|
||||
<livewire:termine-week /> -- Termine-Karussell mit Wochen-Navigation
|
||||
```
|
||||
|
||||
#### Klassisches Livewire (komplex, services)
|
||||
|
||||
```
|
||||
PressSubmissionForm -- Mehrstufige Einreichung (auf Hub)
|
||||
NewsroomManager -- Profil-Verwaltung (auf Hub)
|
||||
AdminReviewQueue -- Redaktions-Tool (auf Hub)
|
||||
```
|
||||
|
||||
Auffällig: die **Brand-Portale brauchen kaum klassisches Livewire**. Das ist konsistent zur Architektur aus Update 3 – Brand-Portale sind primär Lese-Oberflächen, der State liegt im Hub.
|
||||
|
||||
## 5. Brand-Differenzierung in Komponenten
|
||||
|
||||
Drei Mechanismen, in aufsteigender Eingriffstiefe:
|
||||
|
||||
### 5a. Konfiguration über Brand-Config
|
||||
|
||||
Der einfachste Fall: eine Komponente verhält sich anders je nach Brand-Konfiguration. (Beispiel)
|
||||
siehe: config/domains.php
|
||||
|
||||
```php
|
||||
|
||||
return [
|
||||
'name' => 'businessportal24',
|
||||
'tagline' => 'Pressemitteilungen · DACH',
|
||||
'press_item_layout' => 'timeline', // vs. 'topic'
|
||||
'show_market_ticker' => true,
|
||||
'show_branchen_index' => true,
|
||||
'hero_variant' => 'top-meldung', // vs. 'topic-cluster'
|
||||
'rubriken' => ['Wirtschaft', 'Technologie', /* ... */],
|
||||
];
|
||||
|
||||
|
||||
return [
|
||||
'name' => 'presseecho',
|
||||
'tagline' => 'Branchen-Pressearchiv',
|
||||
'press_item_layout' => 'topic',
|
||||
'show_market_ticker' => false,
|
||||
'show_branchen_index' => false,
|
||||
'hero_variant' => 'topic-cluster',
|
||||
'rubriken' => [/* andere Reihenfolge, andere Schwerpunkte */],
|
||||
];
|
||||
```
|
||||
|
||||
Komponenten lesen daraus:
|
||||
|
||||
```blade
|
||||
@if($brand->config('show_market_ticker'))
|
||||
<x-brand.market-ticker />
|
||||
@endif
|
||||
|
||||
<x-press.item :layout="$brand->config('press_item_layout')" :item="$item" />
|
||||
```
|
||||
|
||||
**Das ist die häufigste Form der Differenzierung** – und sie reicht für ~80 % aller Fälle.
|
||||
|
||||
### 5b. Slots und Defaults in Komponenten
|
||||
|
||||
Wenn eine Komponente strukturell gleich ist, aber Inhalte/Sprache abweichen:
|
||||
|
||||
```blade
|
||||
{{-- resources/views/components/hub/transition-block.blade.php --}}
|
||||
@props([
|
||||
'title' => $brand->config('hub_cta.title') ?? 'Pressemitteilung einreichen',
|
||||
'description' => $brand->config('hub_cta.description'),
|
||||
'buttonText' => $brand->config('hub_cta.button') ?? 'Zum Publisher-Bereich',
|
||||
])
|
||||
|
||||
<aside class="btn-hub flex flex-col gap-4">
|
||||
<h3 class="text-xl font-medium">{{ $title }}</h3>
|
||||
<p class="text-white/80">
|
||||
{{ $description ?? $slot }}
|
||||
</p>
|
||||
<a href="{{ route('hub.submit', ['brand' => $brand->slug]) }}"
|
||||
class="btn-cta inline-flex items-center gap-2 w-fit">
|
||||
{{ $buttonText }}
|
||||
<span aria-hidden="true">↗</span>
|
||||
</a>
|
||||
</aside>
|
||||
```
|
||||
|
||||
Brand-Texte stehen in Config, Komponente bleibt eine.
|
||||
|
||||
### 5c. View-Override pro Brand (Eskalations-Pfad)
|
||||
|
||||
Für die seltenen Fälle, in denen eine Brand wirklich ein anderes Markup braucht: Laravel kann View-Pfade brand-spezifisch erweitern.
|
||||
|
||||
```php
|
||||
// app/Providers/BrandServiceProvider.php
|
||||
public function boot(): void
|
||||
{
|
||||
$this->app['view']->prependLocation(
|
||||
resource_path("views/themes/{$brand->slug}")
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
Dann sucht Laravel View-Dateien zuerst unter `resources/views/themes/presseecho/components/press/item.blade.php`, dann unter dem Standard-Pfad. **Nur** für die Komponenten, die wirklich anders sein müssen, wird eine Override-Datei angelegt.
|
||||
|
||||
> **Disziplin-Regel:** View-Overrides sind die letzte Eskalationsstufe. Erst versuchen, mit Config + Slots auszukommen. Override-Dateien verdoppeln Wartungsaufwand – jeder Bugfix muss mehrfach gemacht werden.
|
||||
|
||||
## 6. Datei-Struktur (Beispiel, siehe akutelle Struktur und optimiere falls nötig )
|
||||
|
||||
```
|
||||
app/
|
||||
├── Brand/
|
||||
│ ├── Brand.php # Eloquent Model
|
||||
│ ├── BrandManager.php # Service, im Container
|
||||
│ └── BrandResolver.php # Domain → Brand
|
||||
├── Http/
|
||||
│ └── Middleware/
|
||||
│ └── ResolveBrand.php
|
||||
├── Livewire/
|
||||
│ ├── Brand/ # Brand-Portal-spezifisch
|
||||
│ │ ├── AdHocTicker.php
|
||||
│ │ ├── HeroSlider.php
|
||||
│ │ └── PressSearch.php
|
||||
│ └── Hub/ # Hub-spezifisch
|
||||
│ ├── PressSubmissionForm.php
|
||||
│ └── NewsroomManager.php
|
||||
├── View/
|
||||
│ └── Components/
|
||||
│ ├── Brand/
|
||||
│ ├── Press/
|
||||
│ ├── Hub/
|
||||
│ ├── Ui/
|
||||
│ └── Quality/
|
||||
└── Providers/
|
||||
└── BrandServiceProvider.php
|
||||
|
||||
config/
|
||||
└── brands/
|
||||
├── businessportal24.php
|
||||
├── presseecho.php
|
||||
└── hub.php
|
||||
|
||||
resources/
|
||||
├── css/
|
||||
│ ├── app.css # Tailwind base + semantische Tokens
|
||||
│ └── themes/
|
||||
│ ├── businessportal24.css # Brand-Tokens (optional separat)
|
||||
│ └── presseecho.css
|
||||
├── js/
|
||||
│ └── app.js
|
||||
└── views/
|
||||
├── layouts/
|
||||
│ ├── brand.blade.php # Brand-Portal-Layout
|
||||
│ └── hub.blade.php # Hub-Layout
|
||||
├── components/ # Standard-Komponenten
|
||||
│ ├── brand/
|
||||
│ ├── press/
|
||||
│ ├── hub/
|
||||
│ ├── ui/
|
||||
│ └── quality/
|
||||
├── livewire/ # Volt-Komponenten
|
||||
│ ├── ad-hoc-ticker.blade.php
|
||||
│ ├── hero-slider.blade.php
|
||||
│ └── press-search.blade.php
|
||||
├── pages/ # Konkrete Seiten-Templates
|
||||
│ ├── home.blade.php
|
||||
│ └── veroeffentlichen.blade.php
|
||||
└── themes/ # NUR Brand-Overrides
|
||||
└── presseecho/
|
||||
└── components/
|
||||
└── ... # nur was wirklich anders ist
|
||||
```
|
||||
|
||||
## 7. Performance-Strategie
|
||||
|
||||
Vier konkrete Hebel, die in dieser Reihenfolge ausgeschöpft werden:
|
||||
|
||||
**1. Aggressive View-Caching für statisches Markup.** Press-Listen, Newsroom-Sidebars, Statistik-Zeilen können mit Tag-basiertem Cache gepuffert werden. Neue Mitteilung → relevante Tags invalidieren.
|
||||
|
||||
```php
|
||||
Cache::tags(['press_list', "brand.{$brand->slug}"])
|
||||
->remember('home.aktuelle-meldungen', now()->addMinutes(5), fn() => /* ... */);
|
||||
```
|
||||
|
||||
**2. Volt-Komponenten lazy laden, wo sinnvoll.** Below-the-fold-Komponenten (Branchen-Index, Termine, Newsroom-Liste) als `lazy`:
|
||||
|
||||
```blade
|
||||
<livewire:branchen-index lazy />
|
||||
```
|
||||
|
||||
Sie laden erst beim Scroll, blockieren nicht das initiale Render.
|
||||
|
||||
**3. Asset-Pipeline: ein Bundle, alle Brands.** Über die CSS-Variablen-Strategie ist kein Per-Brand-Build nötig. Ein Vite-Build, der für alle Brands gilt. Spart Komplexität und Cache-Invalidierung.
|
||||
|
||||
**4. Brand-Resolution cachen.** Die Domain-zu-Brand-Auflösung ist `rememberForever` (siehe Middleware). Cache-Bust nur beim Brand-Update über Model-Observer.
|
||||
|
||||
## 8. Migration der Bestands-Inhalte
|
||||
|
||||
Quer zu allem oben: die ~100.000 Bestandsmitteilungen sind im neuem System migriert! Drei Punkte, die das Komponenten-Design beeinflussen:
|
||||
|
||||
- **`<x-press.item>` muss tolerant gegenüber unvollständigen Daten sein.** Alte Mitteilungen haben evtl. keine Lesezeit-Schätzung, keine Branchen-Zuordnung, keine sauberen Bilder. Komponente rendert auch dann sauber.
|
||||
- **Permalink-Stabilität.** Die alte URL-Struktur muss erhalten bleiben (Strategie-Dokument: Tombstone-Modell). Das ist ein Routing-Thema, kein Komponenten-Thema – aber die Komponenten dürfen keine URLs hardcoden, sondern nur `route()`-Helpers nutzen.
|
||||
- **Brand-Zuordnung der Bestände.** Wie in Update 3 festgelegt: am Start ist jede Mitteilung beiden Brands zugewiesen. Komponenten brauchen dafür keine Sonderlogik – sie filtern nach Brand-Kontext, und der Pool ist eben (am Anfang) für beide Brands derselbe.
|
||||
|
||||
## 9. Entwicklungs-Reihenfolge (Empfehlung)
|
||||
|
||||
Konkrete Bauplan-Sequenz, die früh nutzbare Ergebnisse liefert:
|
||||
|
||||
**Sprint 1 – Fundament**
|
||||
|
||||
- Brand-Model, Middleware, Resolver
|
||||
- Theming-Setup (Tailwind v4, CSS-Variablen, zwei Brand-Themes)
|
||||
- Layout `brand.blade.php`
|
||||
- Grundlegende UI-Komponenten (`button`, `badge`, `section-header`)
|
||||
|
||||
**Sprint 2 – Statisches Markup für businessportal24**
|
||||
|
||||
- TopBar, Header, RubrikenNav, Footer
|
||||
- `<x-press.item>` und seine Varianten
|
||||
- StatsRow, QualityStandardFooter
|
||||
- Statische Version der Veröffentlichen-Landing (ohne Reaktivität)
|
||||
|
||||
**Sprint 3 – Reaktive Komponenten**
|
||||
|
||||
- AdHocTicker (Volt + Polling)
|
||||
- HeroSlider (Volt + Alpine)
|
||||
- PressList mit Filter-Tabs
|
||||
- Hub-Transition-Block mit Cross-Domain-Auth-Übergabe
|
||||
|
||||
**Sprint 4 – Hub-Anbindung**
|
||||
|
||||
- Sanctum-Setup für Cross-Domain
|
||||
- Hub-Routing für `?brand=businessportal24`-Parameter
|
||||
- Einreichungs-Flow im Hub (klassisches Livewire)
|
||||
|
||||
**Sprint 5 – Zweite Brand aufschalten**
|
||||
|
||||
- `presseecho.test` lokal
|
||||
- Brand-Config für presseecho
|
||||
- Erste Override-Komponente: `topic-cluster`-Hero
|
||||
- Testen: was funktioniert ohne Override, was braucht eines?
|
||||
|
||||
Ab Sprint 5 wird die eigentliche Stresstest-Frage beantwortet: **Hält die Architektur, wenn die zweite Brand wirklich anders aussehen soll?** Wenn an Sprint 5 viele Overrides nötig werden, ist die Config-Schicht zu dünn – dann iterieren.
|
||||
|
||||
## 10. technische Punkte
|
||||
|
||||
- **Tailwind v4:** wenn das Projekt noch nicht migriert ist, sollte das _vor_ Sprint 1 entschieden werden. v4 macht das CSS-Variablen-Setup deutlich eleganter.
|
||||
- **Sanctum-Cookie-Domain für Cross-Domain-Auth:** Detail aus Update 3, muss vor Sprint 4 final geklärt sein. Same-Site-Strategie, SPA-Mode oder klassischer Token-Flow?
|
||||
- **CDN/Asset-Hosting:** Brand-Bilder, Press-Item-Fotos – kommen vom Hub
|
||||
- **Translations:** DACH-Sprachschalter auf der Startseite –(de (ohne parameter) / de-at / de-ch / en) ist eine reine Inhalts-Filterung. Bei Mehrsprachigkeit de/en: i18n-Setup
|
||||
- **Polling-Frequenzen:** AdHocTicker, Newsroom-Liste – wie oft refreshen, ohne dass die Server-Last bei wachsendem Traffic problematisch wird? Anfangswerte: Ticker 30s, Newsroom 60s, Branchen-Index 5 min.
|
||||
|
||||
---
|
||||
|
||||
_Dieses Konzept ist die technische Brücke zwischen Architektur (Update 3), Positionierung (Update 4) und Implementation. Es legt fest, wie Komponenten strukturiert werden, damit die Brand-Differenzierung skaliert – ohne in eine Codebase-Duplikation zu kippen. Anpassungen sollten dokumentiert und mit den Update-Dokumenten abgeglichen werden._
|
||||
Loading…
Add table
Add a link
Reference in a new issue