# Flux CMS Integration – Entwicklungsplan

> **Ziel:** Integration des flux-cms Packages für die b2in-Webseite.  
> **Scope:** Nur b2in – weitere Subseiten (b2a, stileigentum, style2own) folgen später.  
> **Stand:** 2026-03-18

---

## Ausgangslage

### Was existiert
- **Package:** `packages/flux-cms/` (core, components, starter-components) liegt im Projekt
- **Composer:** `packages/*/*` Repository-Pfad ist in `composer.json` konfiguriert
- **Frontend:** Vollständige b2in-Webseite mit ~15 Seiten
- **Content-Quelle:** `config/content.php` – statische PHP-Arrays pro Theme
- **Content-Zugriff:** Livewire-Sections laden via `config("content.themes.{$theme}.{$section}")`
- **Immobilien/Projekte:** Komplett in `config/content.php` definiert (Slug, Titel, Preise, Galerie, Quick Facts, Investment Case, Location etc.), Route `immobilien/{slug}` liest direkt aus Config
- **Admin-Portal:** `portal.b2in.test` mit eigenem CMS (Cabinets/Displays), User-/Partner-Management
- **Domain-System:** Multi-Domain-Setup in `config/domains.php`

### Was fehlt
- flux-cms/core ist **nicht** als Composer-Dependency installiert
- Keine `app/helpers.php` mit `cms()`/`tcms()`/`cms_media_url()`
- Keine `config/flux-cms.php` publiziert
- Keine `flux_cms_*` Datenbanktabellen
- Keine Admin-Views/Routes für das CMS
- Keine Medienbibliothek
- Kein Immobilien-Model (Projekte leben in Config)

### Scope-Einschränkung Phase 1

**Nur diese Tabellen werden initial benötigt:**
- `flux_cms_contents` – Alle Seiteninhalte (Key-Value mit Übersetzungen)
- `flux_cms_media` – Medienbibliothek (Bilder, PDFs)

**Nicht benötigt (kommt später bei Bedarf):**
- ~~`flux_cms_news_items`~~ – Nachrichteneinträge
- ~~`flux_cms_industries`~~ – Branchen
- ~~`flux_cms_faqs`~~ – FAQ-Einträge
- ~~`flux_cms_downloads`~~ – Downloads
- ~~`flux_cms_linkedin_posts`~~ – LinkedIn-Posts
- ~~`flux_cms_search_index`~~ – Suchindex

**Zusätzlich benötigt:**
- Neues **Immobilien/Projekte-Model** mit eigenem CRUD im CMS-Backend (ersetzt die statischen Projekte aus `config/content.php`)

---

## Phasen-Übersicht

| Phase | Beschreibung | Abhängig von |
|-------|-------------|--------------|
| **1** | Package-Installation & Infrastruktur | – |
| **2** | Immobilien-Model & CRUD | Phase 1 |
| **3** | Content-Migration (config → DB) | Phase 1 |
| **4** | CMS Admin-Backend | Phase 1 |
| **5** | Frontend-Umstellung (config → cms) | Phase 2, 3 |
| **6** | Medienbibliothek & Bilder | Phase 1, 4 |
| **7** | Tests | Phase 2–6 |
| **8** | Feinschliff & Dokumentation | Phase 5–7 |

---

## Phase 1 – Package-Installation & Infrastruktur

### 1.1 Composer-Dependency installieren
```bash
composer require flux-cms/core:@dev
composer require intervention/image
```

**Prüfen:**
- [ ] `FluxCmsServiceProvider` wird automatisch geladen
- [ ] Keine Konflikte mit bestehenden Dependencies

### 1.2 Konfiguration publizieren
```bash
php artisan vendor:publish --tag=flux-cms-config
```

**Anpassen in `config/flux-cms.php`:**
- `default_locale` → `'de'`
- `locales` → `['de' => 'Deutsch', 'en' => 'English']`
- `media.profiles` → an b2in-Bildgrößen anpassen
- `routes.enabled` → `false` (eigene Admin-Routes im Portal)

### 1.3 Migrations ausführen

**Nur die benötigten Migrations:**
- `create_flux_cms_contents_table`
- `create_flux_cms_media_table`

Die restlichen Migrations (news, industries, faqs, downloads, linkedin, search_index) werden **nicht ausgeführt** – sie liegen im Package und können bei Bedarf später migriert werden.

**Optionen:**
- A) Alle Migrations laufen lassen (Tabellen existieren, werden aber nicht genutzt) – einfacher
- B) Nur selektiv migrieren – sauberer, erfordert ggf. Anpassung am ServiceProvider

→ **Empfehlung: Option A** – leere Tabellen stören nicht und vereinfachen spätere Erweiterung.

```bash
php artisan migrate
```

### 1.4 Helper-Funktionen einrichten

**Erstellen:** `app/helpers.php` mit `cms()`, `tcms()`, `cms_media_url()`, `media_url()`

**Registrieren in `composer.json`:**
```json
"autoload": {
    "files": ["app/helpers.php"]
}
```
```bash
composer dump-autoload
```

### 1.5 Storage-Link
```bash
php artisan storage:link
```

**Ergebnis Phase 1:** Package installiert, DB-Tabellen vorhanden, Helper verfügbar.

---

## Phase 2 – Immobilien/Projekte-Model & CRUD

### 2.1 Datenstruktur analysieren

Aktuelle Projekt-Daten aus `config/content.php` (am Beispiel Azizi Creek Views 4):

| Feld | Typ | Beispiel |
|------|-----|---------|
| `slug` | string | `azizi-creek-views-4` |
| `title` | string | `Azizi Developments: Creek Views 4` |
| `location` | string | `Al Jaddaf, Dubai` |
| `status` | string | `NEW LAUNCH` |
| `launch_date` | string | `03.03.2026` |
| `price_from` | integer (AED) | `1125000` |
| `image` | string (Pfad) | `expose/a1/image-4.jpeg` |
| `highlights` | array\<string\> | `['Prime Waterfront Views', ...]` |
| `quick_facts` | array\<{icon, label, value}\> | Typen, Größe, Einheiten, Entwickler |
| `investment_case` | object | `{title, text, views[]}` |
| `gallery` | array\<string\> | Bildpfade |
| `location_info` | object | `{title, map_url, points[]}` |
| `contact` | object | `{title, subtitle, options[]}` |

### 2.2 Migration erstellen

Neue Migration `create_cms_projects_table`:

```php
Schema::create('cms_projects', function (Blueprint $table) {
    $table->id();
    $table->string('slug')->unique();
    $table->json('title');            // translatable
    $table->json('location');         // translatable
    $table->string('status')->nullable();
    $table->date('launch_date')->nullable();
    $table->unsignedInteger('price_from_aed')->nullable();
    $table->string('currency')->default('AED');
    $table->string('image')->nullable();        // CmsMedia Referenz
    $table->json('highlights')->nullable();     // translatable array
    $table->json('quick_facts')->nullable();    // [{icon, label, value}]
    $table->json('investment_case')->nullable(); // {title, text, views[]}
    $table->json('gallery')->nullable();        // [filename, ...]
    $table->json('location_info')->nullable();  // {title, map_url, points[]}
    $table->json('contact')->nullable();        // {title, subtitle, options[]}
    $table->boolean('is_published')->default(false);
    $table->unsignedInteger('order')->default(0);
    $table->timestamps();
});
```

### 2.3 Model erstellen

`App\Models\CmsProject` mit:
- `HasTranslations` (Spatie) für `title`, `location`, `highlights`
- Scopes: `published()`, `ordered()`
- `toFrontendArray()` → kompatibles Array für bestehende Blade-Views
- `getFormattedPrice()` → nutzt `PriceHelper::formatAed()`
- Accessors für `gallery_urls`, `image_url` etc.

### 2.4 Factory & Seeder

- **Factory:** `CmsProjectFactory` für Tests
- **Seeder:** `CmsProjectSeeder` – importiert die bestehenden Projekte aus `config/content.php` in die DB

### 2.5 Admin CRUD (Volt-Komponente)

Admin-View `admin.cms.projects-index`:
- Liste aller Projekte (Titel, Status, Preis, Published, Order)
- Erstellen/Bearbeiten Modal oder Inline
- Felder: alle aus 2.2
- Galerie-Management via MediaPicker
- Bild-Upload via MediaLibraryUploader
- Sortierung per Drag & Drop oder Order-Feld
- Publish/Unpublish Toggle

### 2.6 Route für Immobilien-Show anpassen

```php
// Vorher:
Route::get('/immobilien/{slug}', function (string $slug) {
    $project = config("content.themes.{$theme}.immobilien_projects.projects.{$slug}");
    ...
});

// Nachher:
Route::get('/immobilien/{slug}', function (string $slug) {
    $project = CmsProject::where('slug', $slug)->published()->firstOrFail();
    return view('web.immobilien-show', ['project' => $project->toFrontendArray()]);
});
```

**Ergebnis Phase 2:** Immobilien-Projekte in DB, editierbar im CMS, Frontend nutzt Model.

---

## Phase 3 – Content-Migration (config → DB)

### 3.1 Content-Struktur analysieren

Die `config/content.php` enthält das b2in-Theme mit folgenden Sektionen:

| Page | Section | Typ |
|------|---------|-----|
| global | `announcement_bar` | Text + Links |
| global | `header` | Navigation |
| global | `footer` | Text + Links |
| home | `hero` | Text + Bild + Stats |
| home | `founder_bar` | Text + Bild |
| home | `synergie_section` | Text + Bild |
| home | `vision_section` | Text + Bild |
| home | `ecosystem_core` | Text + Items |
| home | `cta_section` | Text + Link |
| home | `brand_worlds` | Text + Items |
| about | `about_hero`, `our_story`, `our_values`, `leadership_team` | Diverse |
| immobilien | `immobilien_hero_v2`, `immobilien_warum_dubai`, `immobilien_kaufprozess`, `immobilien_bruecke`, `immobilien_mindset`, `immobilien_moebel_vorteil` | Diverse |
| faq | FAQ-Kategorien | Q&A |
| netzwerk | Hero, Stats, Sections | Diverse |
| contact | Form-Info | Text |
| impressum/privacy/terms/cookie-policy | Langtext | HTML |

### 3.2 CmsContentSeeder erstellen

**Strategie:** Alle b2in-Inhalte aus `config/content.php` in `flux_cms_contents` überführen.

- **Key-Schema:** `{page}.{section}.{field}` (z.B. `home.hero.title`, `about.our_story.title`)
- **Typen:** `text`, `html`, `image`, `json` (für Arrays wie `pillars`, `stats`, `navigation`)
- **Gruppen:** `home`, `about`, `immobilien`, `netzwerk`, `faq`, `contact`, `impressum`, `privacy`, `terms`, `cookie_policy`, `global` (Header, Footer)

**Datei:** `database/seeders/CmsContentSeeder.php` – liest `config/content.php` und schreibt in DB.

### 3.3 Seeders ausführen & verifizieren
```bash
php artisan db:seed --class=CmsContentSeeder
```

**Ergebnis Phase 3:** Alle b2in-Inhalte in der DB, abrufbar über `cms('home.hero.title')`.

---

## Phase 4 – CMS Admin-Backend

### 4.1 CMS als eigener Menüpunkt

Das CMS wird im Admin-Portal (`portal.b2in.test`) als **eigener Top-Level-Menüpunkt "CMS"** in der Sidebar integriert.

**Sidebar-Erweiterung** (`resources/views/components/layouts/app/sidebar.blade.php`):
```
CMS
├── Dashboard       (Übersicht: Anzahl Contents, Medien, Projekte)
├── Inhalte         (Content-Editor für Text/HTML/Image/JSON Keys)
├── Projekte        (Immobilien-CRUD – aus Phase 2)
├── Medienbibliothek (Upload, Grid, Conversions)
```

### 4.2 CMS-Layout

**Entscheidung:** Die CMS-Views nutzen das bestehende Admin-Portal-Layout (`admin-master.blade.php` / `app.blade.php`), damit Navigation und User-Menü konsistent bleiben.

→ Das Reference-Layout `layout-cms.blade.php` wird **nicht** als separates Layout genutzt, sondern als Vorlage für die Content-Struktur innerhalb des bestehenden Layouts.

### 4.3 Admin-Views erstellen

Aus den Package-Referenz-Views nur die benötigten kopieren und anpassen:

| View | Quelle | Ziel |
|------|--------|------|
| Dashboard | `admin-reference/cms/dashboard.blade.php` | `livewire/admin/cms/dashboard.blade.php` |
| Content-Editor | `admin-reference/cms/content-index.blade.php` | `livewire/admin/cms/content-index.blade.php` |
| Medienbibliothek | `admin-reference/cms/media-index.blade.php` | `livewire/admin/cms/media-index.blade.php` |
| MediaPicker | `admin-reference/cms/media-picker.blade.php` | `livewire/admin/cms/media-picker.blade.php` |
| MediaUploader | `admin-reference/cms/media-library-uploader.blade.php` | `livewire/admin/cms/media-library-uploader.blade.php` |
| **Projekte** | **Neu erstellen** | `livewire/admin/cms/projects-index.blade.php` |

**Nicht benötigt (vorerst):** news-index, industries-index, faqs-index, linkedin-index, downloads-index, team-index, search-index

### 4.4 Livewire-Komponenten einrichten

```bash
mkdir -p app/Livewire/Admin/Cms/
```

Aus Package kopieren + Namespace anpassen:
- `MediaLibraryUploader.php` → `App\Livewire\Admin\Cms`
- `MediaPicker.php` → `App\Livewire\Admin\Cms`
- `MediaUploader.php` → `App\Livewire\Admin\Cms`

### 4.5 Admin-Routes registrieren

In `routes/admin.php` innerhalb der bestehenden `auth`-Middleware-Gruppe:

```php
// Flux CMS Routes
Volt::route('admin/cms', 'admin.cms.dashboard')->name('cms.dashboard');
Volt::route('admin/cms/content', 'admin.cms.content-index')->name('cms.content.index');
Volt::route('admin/cms/media', 'admin.cms.media-index')->name('cms.media.index');
Volt::route('admin/cms/projects', 'admin.cms.projects-index')->name('cms.projects.index');
```

**Ergebnis Phase 4:** Funktionierendes Admin-Backend unter `portal.b2in.test/admin/cms` mit Dashboard, Content-Editor, Medienbibliothek und Projekte-CRUD.

---

## Phase 5 – Frontend-Umstellung (config → cms)

### 5.1 Strategie: Dualer Betrieb mit Fallback

Die Umstellung erfolgt inkrementell. Jede Section einzeln umstellen, mit Fallback auf `config()`:

```php
// Vorher (Hero.php):
$this->content = config("content.themes.{$theme}.hero", []);

// Nachher:
$this->content = $this->loadFromCms('home.hero');

// Fallback-Methode in einem Trait oder Base-Class:
protected function loadFromCms(string $group): array
{
    $cmsContent = app(CmsContentService::class)->getGroup($group);
    if (empty($cmsContent)) {
        $theme = config('app.theme', 'b2in');
        $section = Str::afterLast($group, '.');
        return config("content.themes.{$theme}.{$section}", []);
    }
    return $cmsContent;
}
```

### 5.2 Sections schrittweise umstellen

**Reihenfolge (nach Priorität / Sichtbarkeit):**

1. **Home-Page Sections:**
   - [ ] Hero → `home.hero`
   - [ ] FounderBar → `home.founder_bar`
   - [ ] ContentSection → `home.{section}` (dynamisch)
   - [ ] VisionSection → `home.vision_section`
   - [ ] EcosystemCore → `home.ecosystem_core`
   - [ ] CTASection → `home.cta_section`

2. **Globale Elemente:**
   - [ ] Header (Navigation) → `global.header`
   - [ ] Footer → `global.footer`
   - [ ] AnnouncementBar → `global.announcement_bar`

3. **Immobilien-Seite (statischer Content):**
   - [ ] HeroV2 → `immobilien.hero_v2`
   - [ ] WarumDubai → `immobilien.warum_dubai`
   - [ ] Kaufprozess → `immobilien.kaufprozess`
   - [ ] Brücke → `immobilien.bruecke`
   - [ ] Mindset → `immobilien.mindset`
   - [ ] MöbelVorteil → `immobilien.moebel_vorteil`
   - [ ] Projekte-Liste → **CmsProject::published()->ordered()** (aus Phase 2)

4. **Unterseiten:**
   - [ ] About
   - [ ] FAQ
   - [ ] Contact
   - [ ] Impressum / Privacy / Terms / Cookie-Policy
   - [ ] Netzwerk
   - [ ] Service / Portfolio

### 5.3 Immobilien-Route umstellen

```php
// routes/web.php – von config auf Model:
Route::get('/immobilien/{slug}', function (string $slug) {
    $project = \App\Models\CmsProject::where('slug', $slug)
        ->published()
        ->firstOrFail();

    return view('web.immobilien-show', ['project' => $project->toFrontendArray()]);
})->name('immobilien.show');
```

Die `immobilien.blade.php` Projekte-Liste ebenfalls umstellen:
```php
// Vorher: $projects = config("content.themes.{$theme}.immobilien_projects", []);
// Nachher: $projects = CmsProject::published()->ordered()->get();
```

### 5.4 Bilder umstellen

```diff
- asset('img/assets/' . $heroV2['image'])
+ cms_media_url('immobilien.hero_v2.image', 'hero')
```

**Ergebnis Phase 5:** b2in-Frontend liest Inhalte aus DB + CmsProject-Model, editierbar über Admin.

---

## Phase 6 – Medienbibliothek & Bilder

### 6.1 Bestehende Bilder importieren

Alle b2in-Bilder (aus `public/img/assets/`) in die CMS-Medienbibliothek importieren:
- `b2in/` – allgemeine b2in-Bilder (Hero, Founder, Sections)
- `expose/` – Immobilien-Projektbilder

### 6.2 CmsMediaSeeder

Seeder erstellt `CmsMedia`-Einträge für alle bestehenden Bilder und verknüpft sie mit den CmsContent-Keys vom Typ `image`.

### 6.3 Bildprofile definieren

In `config/flux-cms.php`:
- `hero` → 1920×800 (Hero-Banner)
- `card` → 768×512 (Kacheln, Sections)
- `thumbnail` → 400×300 (Listen, Übersichten)
- `avatar` → 400×400 (Team-Fotos, Founder)
- `gallery` → 1200×900 (Projekt-Galerie)
- `og_image` → 1200×630 (Social Sharing)

### 6.4 Conversions generieren

```bash
php artisan flux-cms:clear-cache
```

**Ergebnis Phase 6:** Alle Bilder in Medienbibliothek, Conversions generiert, URLs aufgelöst.

---

## Phase 7 – Tests

### 7.1 Referenz-Tests kopieren (selektiv)
```bash
mkdir -p tests/Feature/Cms
cp packages/flux-cms/core/tests-reference/Feature/Cms/CmsMediaTest.php tests/Feature/Cms/
cp packages/flux-cms/core/tests-reference/Feature/Cms/CmsContentServiceTest.php tests/Feature/Cms/
```

### 7.2 Projektspezifische Tests

- **CmsProjectTest** – CRUD, published/unpublished, toFrontendArray(), Validierung
- **CmsContentSeederTest** – Prüft ob alle Keys aus config in DB existieren
- **CmsAdminAccessTest** – Prüft Zugriffskontrolle auf CMS-Admin-Routen
- **ImmobilienRouteTest** – Prüft `/immobilien/{slug}` mit DB-Daten
- **FrontendFallbackTest** – Prüft dualen Betrieb (CMS → config Fallback)
- **MediaIntegrationTest** – Upload, Conversion, URL-Auflösung

### 7.3 Tests ausführen
```bash
php artisan test --compact --filter=Cms
php artisan test --compact --filter=Immobilien
```

**Ergebnis Phase 7:** Alle CMS-Funktionalitäten sind getestet.

---

## Phase 8 – Feinschliff & Dokumentation

### 8.1 Cache & Performance
- CMS-Content-Cache aktivieren
- Eager Loading optimieren
- Prüfen: Keine N+1-Queries auf Frontseiten

### 8.2 config/content.php aufräumen
- Nach vollständiger Umstellung: b2in-Theme-Daten als `@deprecated` markieren
- Noch nicht entfernen (Fallback für andere Themes!)
- Immobilien-Projekte aus Config entfernen (leben jetzt in DB)

### 8.3 Sidebar-Berechtigungen
- CMS-Zugang über Spatie-Permission absichern (`permission:manage-cms`)
- CMS-Menüpunkt nur für berechtigte User anzeigen

### 8.4 Dokumentation
- Diesen Plan aktualisieren mit Status
- Notizen für Integration weiterer Subseiten (b2a, stileigentum, style2own)

---

## Offene Fragen / Entscheidungen

| # | Frage | Entscheidung |
|---|-------|-------------|
| 1 | Alle flux-cms Migrations laufen lassen oder nur contents + media? | Empfehlung: Alle (leere Tabellen stören nicht) |
| 2 | CmsProject: Eigene Migration oder flux-cms erweitern? | Eigene Migration im Projekt |
| 3 | FAQ-Daten: Vorerst in config belassen oder direkt in `flux_cms_faqs`? | Vorerst config |
| 4 | Magazin: Eigenes System belassen oder später auf CMS? | Vorerst belassen |
| 5 | Andere Themes (b2a etc.) weiterhin via `config()`? | Ja |

---

## Fortschritt

| Phase | Status | Notizen |
|-------|--------|---------|
| 1 – Installation & Infrastruktur | ✅ Fertig | 2026-03-18: Package installiert, Config angepasst, Migrations gelaufen, Helpers eingerichtet, 434 Tests grün |
| 2 – Immobilien-Model & CRUD | ✅ Fertig | 2026-03-18: Migration, Model, Factory, Seeder, 9 Tests grün. CRUD Admin-View folgt in Phase 4. |
| 3 – Content-Migration | ✅ Fertig | 2026-03-18: 61 Sections in 8 Gruppen migriert, Section-als-JSON-Ansatz, 8 Tests grün |
| 4 – CMS Admin-Backend | ✅ Fertig | 2026-03-18: Dashboard, Content-Editor, Projekte-CRUD, Medienbibliothek, Sidebar-Menü, MediaPicker/Uploader, 16 Tests grün |
| 5 – Frontend-Umstellung | ⬜ Offen | |
| 6 – Medienbibliothek | ⬜ Offen | |
| 7 – Tests | ⬜ Offen | |j
| 8 – Feinschliff | ⬜ Offen | |
