CMS: Standorte an der Marke, Seitenbaum mit Vorschau, Referenzen-Modul

Nachtrag 11: Herkunfts-Sektion liest Ort, Entfernung und Rolle aus der
Markenverwaltung (drei neue Felder an cms_brands, BrandOriginSeeder),
Kartenpositionen im Code, „Marken“ in der Sidebar.

Nachtrag 12: Inhalte-Editor zeigt die Seiten der Website mit ihren
Sektionen in Reihenfolge (config/cms_pages.php, CmsPageTree), Felder in
Klartext, fehlende Sektionen aus der Sprachdatei anlegbar, Vorschau der
echten Seite im iframe mit Sprung zur Sektion; Anker an allen lebenden
Sektionen nachgezogen.

Nachtrag 13: Referenzen als eigenes Modul (CmsReference, Portal-Seite,
/referenzen mit Fallback auf die Markengalerien); Immobilien-Projekte
aus Sidebar und CMS-Übersicht genommen, Route bleibt.

Dazu .gitignore für _b2in_interior_shop/ und docs/.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-09-15 14:58:45 +00:00
parent 7953a8c9a1
commit 90aa079cf7
50 changed files with 2580 additions and 324 deletions

View file

@ -1,4 +1,4 @@
<section class="section-padding flex items-center relative border-b border-border/30">
<section id="seitenkopf" class="section-padding flex items-center relative border-b border-border/30">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 bg-[hsl(var(--hero-container))] rounded-[20px] w-[95%]">
<div class="grid lg:grid-cols-2 gap-16 items-center">
<div class="space-y-8 slide-right delay-200">

View file

@ -1,4 +1,4 @@
<section class="{{ $bg }} text-secondary-foreground section-padding">
<section id="cta" class="{{ $bg }} text-secondary-foreground section-padding">
<div class="container-narrow text-center">
<div class="spacing-section">
<h2 class="text-section-title text-white slide-up delay-300">

View file

@ -1,6 +1,6 @@
{{-- Livewire braucht ein Wurzelelement. Solange kein Display veröffentlicht ist, bleibt es leer –
lieber gar keine Sektion als ein Rahmen mit ewigem Ladebalken. --}}
<div>
<div id="displays">
@if ($displayId !== null)
<section class="section-padding {{ $bg }}">
<div class="container-padding">

View file

@ -1,6 +1,6 @@
@php($faqCounter = 0)
<div x-data="{ openIndex: null }">
<section class="section-padding {{ $bg }}">
<section id="faq" class="section-padding {{ $bg }}">
<div class="container-padding">
{{-- Section Title --}}
<div class="text-center mb-16">

View file

@ -1,4 +1,4 @@
<section class="py-8 bg-background border-b border-border/30">
<section id="gruender" class="py-8 bg-background border-b border-border/30">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex flex-col sm:flex-row items-center gap-6 sm:gap-8">
@if(isset($content['image']))

View file

@ -1,4 +1,4 @@
<section class="section-padding flex items-center relative border-b border-border/30">
<section id="hero" class="section-padding flex items-center relative border-b border-border/30">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 bg-hero-container rounded-[20px] w-[95%]">
<div class="grid lg:grid-cols-2 gap-12 lg:gap-16 items-center">
{{-- Left Content --}}

View file

@ -1,5 +1,5 @@
@if (!empty($content))
<section class="relative h-[30vh] min-h-[220px] overflow-hidden">
<section id="bild" class="relative h-[30vh] min-h-[220px] overflow-hidden">
<x-web-picture
src="{{ theme_image_url($content['image'] ?? '') }}"
alt="{{ $content['image_alt'] ?? 'Dekoratives Bild' }}"

View file

@ -1,6 +1,6 @@
{{-- Bewusst klein und dezent: wer gezielt sucht, findet den Weg; wer wegen Einrichtung
kommt, wird nicht abgelenkt. Keine Preise, keine Renditeversprechen. --}}
<section class="py-12 {{ $bg }}">
<section id="immobilien" class="py-12 {{ $bg }}">
<div class="container-narrow">
<div class="border-t border-border pt-10 text-center">
<h2 class="text-xl font-medium">{!! $content['title'] ?? '' !!}</h2>

View file

@ -1,4 +1,4 @@
<section class="section-padding">
<section id="magazin" class="section-padding">
<div class="container-padding">
<div class="text-center mb-16">
<h1 class="text-hero mb-6">

View file

@ -1,4 +1,4 @@
<section class="section-padding bg-secondary text-secondary-foreground">
<section id="marken-cta" class="section-padding bg-secondary text-secondary-foreground">
<div class="container-narrow text-center spacing-section">
<h2 class="text-section-title text-white">{!! $content['title'] ?? '' !!}</h2>
<p class="text-large max-w-2xl mx-auto leading-relaxed">{{ $content['text'] ?? '' }}</p>

View file

@ -1,6 +1,6 @@
{{-- Optionaler Block laut Spec – über das CMS-Feld `enabled` abschaltbar. --}}
@if ($content['enabled'] ?? true)
<section class="py-16 {{ $bg }}">
<section id="wachstum" class="py-16 {{ $bg }}">
<div class="container-narrow text-center">
<h2 class="text-2xl font-medium">{!! $content['title'] ?? '' !!}</h2>
<p class="text-muted-foreground leading-relaxed mt-4">{{ $content['text'] ?? '' }}</p>

View file

@ -18,7 +18,7 @@
}
@endphp
<section class="section-padding {{ $bg }}">
<section id="netzwerk" class="section-padding {{ $bg }}">
<div class="container-padding">
<div class="container-narrow text-center">
<h2 class="text-section-title">{!! $content['title'] ?? '' !!}</h2>

View file

@ -14,9 +14,12 @@
@endif
</div>
{{-- Ohne gepflegten Fertigungsort bleibt nur die Überschrift und der Ablauf-Block. --}}
@if ($this->locations !== [])
<div class="grid lg:grid-cols-2 gap-10 lg:gap-16 items-center">
{{-- Schematische Karte: Bielefeld als Drehscheibe, die vier Standorte als Speichen.
Keine Geografie, nur Richtung und Entfernung. Unter md tragen die Kacheln alles. --}}
{{-- Schematische Karte: Bielefeld als Drehscheibe, die Standorte als Speichen.
Keine Geografie, nur Richtung und Entfernung. Positionen aus OriginSection::MAP_POSITIONS,
eine Marke ohne Position steht nur in den Kacheln. Unter md tragen die Kacheln alles. --}}
<div class="hidden md:block card-elevated rounded-2xl p-6 lg:p-8 slide-left delay-400">
<svg class="origin-map" viewBox="0 0 640 400" role="img"
aria-label="{{ $content['map_alt'] ?? '' }}">
@ -27,10 +30,10 @@
@endif
@foreach ($this->locations as $index => $location)
@continue(empty($location['map']))
@php
$x = (int) ($location['x'] ?? 0);
$y = (int) ($location['y'] ?? 0);
$left = ($location['label'] ?? 'right') === 'left';
$x = $location['map']['x'];
$y = $location['map']['y'];
$midX = (int) round(($hubX + $x) / 2);
$midY = (int) round(($hubY + $y) / 2);
@endphp
@ -41,10 +44,11 @@
@endforeach
@foreach ($this->locations as $index => $location)
@continue(empty($location['map']))
@php
$x = (int) ($location['x'] ?? 0);
$y = (int) ($location['y'] ?? 0);
$left = ($location['label'] ?? 'right') === 'left';
$x = $location['map']['x'];
$y = $location['map']['y'];
$left = $location['map']['label'] === 'left';
$labelX = $left ? $x - 14 : $x + 14;
@endphp
<circle class="origin-map-dot" cx="{{ $x }}" cy="{{ $y }}" r="7" />
@ -63,10 +67,10 @@
</svg>
</div>
{{-- Die vier Standorte --}}
{{-- Eine Kachel je Marke mit Fertigungsort --}}
<div class="grid sm:grid-cols-2 gap-6 slide-right delay-400">
@foreach ($this->locations as $index => $location)
<div class="card-elevated rounded-2xl p-6 flex flex-col gap-2" wire:key="origin-{{ $index }}">
@foreach ($this->locations as $location)
<div class="card-elevated rounded-2xl p-6 flex flex-col gap-2" wire:key="origin-{{ $location['slug'] }}">
<span class="inline-flex items-center gap-1.5 text-xs font-semibold uppercase tracking-wider text-secondary">
<svg class="w-4 h-4" fill="none" stroke="currentColor" stroke-width="1.5" viewBox="0 0 24 24"
aria-hidden="true">
@ -84,9 +88,9 @@
@if (!empty($location['distance']))
<span class="text-clay">{{ $location['distance'] }} {{ $content['distance_suffix'] ?? '' }}</span>
@endif
@if (!empty($location['link']) && !empty($location['link_text']))
@if (!empty($location['link']) && !empty($content['link_text']))
<a href="{{ $location['link'] }}" class="font-medium text-primary hover:text-secondary transition-colors">
{{ $location['link_text'] }}
{{ $content['link_text'] }}
</a>
@endif
</div>
@ -94,6 +98,7 @@
@endforeach
</div>
</div>
@endif
{{-- Gefertigt oder vertrieben? Der Weg eines Maßmöbels am Beispiel CABINET. --}}
@if ($steps !== [])

View file

@ -1,4 +1,4 @@
<section class="section-padding">
<section id="geschichte" class="section-padding">
<div class="container-padding text-center">
<div class="text-center mb-16 slide-up delay-200">
<h2 class="text-section-title text-foreground mb-12">

View file

@ -21,7 +21,7 @@ function renderHeroIcon($iconName, $style = 'outline') {
}
}
@endphp
<section class="section-padding bg-accent">
<section id="werte" class="section-padding bg-accent">
<div class="container-padding">
<div class="text-center mb-16 slide-up delay-300">
<h2 class="text-section-title">{!! $content['title'] !!}</h2>

View file

@ -1,6 +1,6 @@
{{-- Seitenkopf für Unterseiten – abgesetzte graue Fläche wie der Hero auf Startseite und
Netzwerkseite, damit er sich klar von den Blöcken darunter absetzt. --}}
<section class="section-padding flex items-center relative border-b border-border/30 {{ $bg }}">
<section id="seitenkopf" class="section-padding flex items-center relative border-b border-border/30 {{ $bg }}">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-16 lg:py-20 bg-hero-container rounded-[20px] w-[95%]">
{{-- Breadcrumb: Home → Marken --}}
<nav aria-label="Breadcrumb" class="mb-8">

View file

@ -1,4 +1,4 @@
<section class="{{ $bg }} text-secondary-foreground section-padding">
<section id="cta" class="{{ $bg }} text-secondary-foreground section-padding">
<div class="container-narrow text-center">
<div class="spacing-section">
<h2 class="text-section-title text-white slide-up delay-300">

View file

@ -1,4 +1,4 @@
<section class="section-padding {{ $bg }}">
<section id="projekte" class="section-padding {{ $bg }}">
<div class="container-padding">
<div class="text-center max-w-2xl mx-auto">
<h2 class="text-section-title">{!! $content['title'] ?? '' !!}</h2>
@ -13,7 +13,7 @@
<figure class="card-elevated rounded-2xl overflow-hidden group" wire:key="ref-{{ $project['key'] }}">
<div class="relative overflow-hidden">
<img src="{{ theme_image_url($project['image']) }}"
alt="{{ $project['caption'] ?: $project['brand'] }}" loading="lazy"
alt="{{ $project['caption'] ?: ($project['brands'][0]['name'] ?? '') }}" loading="lazy"
class="w-full aspect-[4/3] object-cover transition-transform duration-300 group-hover:scale-105" />
<span class="absolute top-3 left-3 rounded-full bg-black/60 px-3 py-1 text-xs font-medium text-white backdrop-blur-sm">
{{ __('ui.brand_gallery_badge_project') }}
@ -23,13 +23,32 @@
@if (!empty($project['caption']))
<p class="font-medium">{{ $project['caption'] }}</p>
@endif
@if ($project['has_detail_page'])
<a href="{{ route('marken.show', ['slug' => $project['brand_slug']]) }}"
class="text-sm text-secondary underline underline-offset-2 hover:no-underline mt-1 inline-block">
{{ $project['brand'] }}
</a>
@else
<p class="text-sm text-muted-foreground mt-1">{{ $project['brand'] }}</p>
@if (!empty($project['meta']))
<p class="text-xs uppercase tracking-wider text-clay mt-1">{{ $project['meta'] }}</p>
@endif
@if (!empty($project['description']))
<p class="text-sm text-muted-foreground leading-relaxed mt-2">{{ $project['description'] }}</p>
@endif
@if (!empty($project['gallery']))
{{-- Weitere Bilder der Referenz als kleine Reihe --}}
<div class="flex gap-2 mt-3 overflow-x-auto">
@foreach ($project['gallery'] as $item)
<img src="{{ theme_image_url($item['image']) }}" alt="{{ $item['caption'] ?: $project['caption'] }}"
loading="lazy" class="h-14 w-20 shrink-0 rounded-md object-cover" />
@endforeach
</div>
@endif
@if (!empty($project['brands']))
<p class="text-sm mt-2 flex flex-wrap gap-x-3 gap-y-1">
@foreach ($project['brands'] as $brand)
@if (!empty($brand['link']))
<a href="{{ $brand['link'] }}"
class="text-secondary underline underline-offset-2 hover:no-underline">{{ $brand['name'] }}</a>
@else
<span class="text-muted-foreground">{{ $brand['name'] }}</span>
@endif
@endforeach
</p>
@endif
</figcaption>
</figure>

View file

@ -1,4 +1,4 @@
<section class="section-padding {{ $bg }}">
<section id="engagement" class="section-padding {{ $bg }}">
<div class="container-padding">
<div class="text-center mb-12 slide-up delay-300">
<h2 class="text-section-title">{!! $content['title'] ?? '' !!}</h2>

View file

@ -1,4 +1,4 @@
<section class="section-padding {{ $bg }}">
<section id="vision" class="section-padding {{ $bg }}">
<div class="container-padding">
<div class="grid lg:grid-cols-2 gap-12 lg:gap-16 items-center">

View file

@ -1,6 +1,6 @@
<div>
<!-- Hero Section with Contact Form -->
<section class="py-10 flex items-center relative overflow-hidden">
<section id="kontakt" class="py-10 flex items-center relative overflow-hidden">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-20 bg-[hsl(var(--hero-container))] rounded-[20px] w-[95%]">
<div class="grid lg:grid-cols-2 gap-12 items-center">
<!-- Left Side - Hero Text -->

View file

@ -2,7 +2,7 @@
Immobilien dunkel mit negativem Logo (Tokens `--footer-*` in theme-b2in.css, Logo in mount()).
pb-28 auf kleinen Schirmen: darunter schwebt das Terminanfrage-Widget, das sonst die
letzten Zeilen des Footers verdecken wuerde. --}}
<footer class="section-footer section-padding pb-28 sm:pb-16">
<footer id="footer" class="section-footer section-padding pb-28 sm:pb-16">
<div class="container-padding">
{{-- Main Content --}}
<div class="text-center spacing-section">