diff --git a/app/Livewire/Web/Components/UI/AppointmentWidget.php b/app/Livewire/Web/Components/UI/AppointmentWidget.php index 9475e3e1..5532fda6 100644 --- a/app/Livewire/Web/Components/UI/AppointmentWidget.php +++ b/app/Livewire/Web/Components/UI/AppointmentWidget.php @@ -52,6 +52,32 @@ class AppointmentWidget extends Component $section = cms_theme_section('appointment_widget'); $this->content = is_array($section) ? $section : []; $this->formLoadedAt = time(); + + $this->applyBrandContext(); + } + + /** + * Auf einer Markenseite trägt der Button die Beschriftung dieser Marke. + * + * Die Zulieferung sieht ausdrücklich keinen zweiten CTA-Button vor, sondern eine + * kontextbezogene Beschriftung des vorhandenen Widgets. Da das Widget im Layout steckt und + * nicht je Seite eingebunden wird, liest es den Kontext aus der Route. + */ + private function applyBrandContext(): void + { + $route = request()->route(); + + if ($route?->getName() !== 'marken.show') { + return; + } + + $slug = (string) $route->parameter('slug'); + $brand = CmsBrand::forFrontend()[$slug] ?? null; + $label = is_array($brand) ? trim((string) ($brand['widget_label'] ?? '')) : ''; + + if ($label !== '') { + $this->content['button_label'] = $label; + } } /** diff --git a/app/Models/CmsBrand.php b/app/Models/CmsBrand.php index c847a74d..c027a41c 100644 --- a/app/Models/CmsBrand.php +++ b/app/Models/CmsBrand.php @@ -38,6 +38,10 @@ class CmsBrand extends Model 'logo', 'image', 'services', + 'sections', + 'widget_label', + 'cta_title', + 'cta_text', 'gallery', 'website_url', 'showroom', @@ -52,6 +56,9 @@ class CmsBrand extends Model 'teaser', 'summary', 'description', + 'widget_label', + 'cta_title', + 'cta_text', ]; protected function casts(): array @@ -59,6 +66,7 @@ class CmsBrand extends Model return [ 'highlights' => 'array', 'services' => 'array', + 'sections' => 'array', 'gallery' => 'array', 'is_published' => 'boolean', 'order' => 'integer', @@ -124,6 +132,10 @@ class CmsBrand extends Model 'logo' => '', 'image' => '', 'services' => [], + 'sections' => [], + 'widget_label' => '', + 'cta_title' => '', + 'cta_text' => '', 'gallery' => [], 'website_url' => '', 'showroom' => '', @@ -131,6 +143,7 @@ class CmsBrand extends Model ], $brand, ['slug' => $slug, 'has_detail_page' => false]); $entry['gallery'] = self::normalizeGallery($entry['gallery']); + $entry['sections'] = self::normalizeSections($entry['sections']); $out[$slug] = $entry; } @@ -138,6 +151,81 @@ class CmsBrand extends Model return $out; } + /** + * Seitenabschnitte in eine feste Form bringen. + * + * Jeder Abschnitt hat einen Titel und Absätze. Optional dazu: + * - `claim` hervorgehobene Schlusszeile + * - `quote` Zitat mit `quote_author` + * - `detail` Vertiefung, die im Frontend aufklappbar dargestellt wird + * + * @param mixed $sections + * @return list, claim: string, quote: string, quote_author: string, detail: array{title: string, paragraphs: list}|null}> + */ + public static function normalizeSections($sections): array + { + if (! is_array($sections)) { + return []; + } + + $out = []; + + foreach ($sections as $section) { + if (! is_array($section)) { + continue; + } + + $paragraphs = self::paragraphList($section['paragraphs'] ?? []); + $title = trim((string) ($section['title'] ?? '')); + + if ($title === '' && $paragraphs === []) { + continue; + } + + $detail = null; + + if (is_array($section['detail'] ?? null)) { + $detailParagraphs = self::paragraphList($section['detail']['paragraphs'] ?? []); + $detailTitle = trim((string) ($section['detail']['title'] ?? '')); + + if ($detailTitle !== '' || $detailParagraphs !== []) { + $detail = ['title' => $detailTitle, 'paragraphs' => $detailParagraphs]; + } + } + + $out[] = [ + 'title' => $title, + 'paragraphs' => $paragraphs, + 'claim' => trim((string) ($section['claim'] ?? '')), + 'quote' => trim((string) ($section['quote'] ?? '')), + 'quote_author' => trim((string) ($section['quote_author'] ?? '')), + 'detail' => $detail, + ]; + } + + return $out; + } + + /** + * @param mixed $paragraphs + * @return list + */ + private static function paragraphList($paragraphs): array + { + if (is_string($paragraphs)) { + $paragraphs = preg_split('/\R{2,}/', $paragraphs) ?: []; + } + + if (! is_array($paragraphs)) { + return []; + } + + return array_values(array_filter(array_map( + fn ($p) => trim((string) (is_array($p) ? ($p['text'] ?? '') : $p)), + $paragraphs, + ))); + } + /** * Galerie-Eintraege in eine feste Form bringen. * @@ -216,6 +304,10 @@ class CmsBrand extends Model 'logo' => $this->logo, 'image' => $this->image, 'services' => $this->services ?? [], + 'sections' => self::normalizeSections($this->sections), + 'widget_label' => (string) $this->widget_label, + 'cta_title' => (string) $this->cta_title, + 'cta_text' => (string) $this->cta_text, 'gallery' => self::normalizeGallery($this->gallery), 'website_url' => $this->website_url, 'showroom' => $this->showroom, diff --git a/database/migrations/2026_09_03_084254_add_page_sections_to_cms_brands_table.php b/database/migrations/2026_09_03_084254_add_page_sections_to_cms_brands_table.php new file mode 100644 index 00000000..b786cb72 --- /dev/null +++ b/database/migrations/2026_09_03_084254_add_page_sections_to_cms_brands_table.php @@ -0,0 +1,42 @@ +json('sections')->nullable()->after('services'); + $table->json('widget_label')->nullable()->after('sections'); + $table->json('cta_title')->nullable()->after('widget_label'); + $table->json('cta_text')->nullable()->after('cta_title'); + }); + } + + public function down(): void + { + Schema::table('cms_brands', function (Blueprint $table) { + $table->dropColumn(['sections', 'widget_label', 'cta_title', 'cta_text']); + }); + } +}; diff --git a/database/seeders/BrandPageTextSeeder.php b/database/seeders/BrandPageTextSeeder.php new file mode 100644 index 00000000..d59966be --- /dev/null +++ b/database/seeders/BrandPageTextSeeder.php @@ -0,0 +1,358 @@ +pages() as $slug => $data) { + $brand = CmsBrand::query()->where('slug', $slug)->first(); + + if (! $brand) { + $this->command?->warn("Marke „{$slug}“ nicht gefunden – übersprungen."); + + continue; + } + + foreach (self::FIELDS as $field) { + if (array_key_exists($field, $data)) { + $brand->{$field} = $data[$field]; + } + } + + $brand->save(); + $updated++; + + $this->command?->info("Markenseite „{$slug}“: ".count($data['sections'] ?? []).' Abschnitte übernommen.'); + } + + $this->command?->info("Markenseiten aktualisiert: {$updated}."); + } + + /** + * @return array> + */ + private function pages(): array + { + return [ + 'rotpunkt' => [ + 'tagline' => ['de' => 'Küchen, die Haltung zeigen.', 'en' => 'Kitchens with character.'], + 'widget_label' => ['de' => 'Persönliche Küchenberatung vereinbaren', 'en' => 'Arrange a personal kitchen consultation'], + 'description' => [ + 'de' => '

Eine Küche ist weit mehr als ein funktionaler Raum. Sie ist Treffpunkt, Arbeitsplatz und Mittelpunkt des täglichen Lebens. Und Hand aufs Herz: Wo finden die besten Gespräche statt – und wo enden die meisten Partys? Meistens in der Küche.

' + .'

Rotpunkt verbindet außergewöhnliche Gestaltungsfreiheit, intelligente Stauraumlösungen und durchdachte Materiallösungen zu Küchen, die nicht nur gut aussehen, sondern dauerhaft zum Leben ihrer Besitzer passen.

' + .'

Unsere Aufgabe ist es, genau herauszuarbeiten, welche Lösung zu Ihnen, Ihrem Alltag und Ihren persönlichen Vorstellungen passt. Bei uns in der Bielefelder Rathausstraße entsteht daraus nicht einfach nur eine Küche. Auf Wunsch denken wir das ganze Zuhause mit und ergänzen es bei Bedarf bis ins Detail handwerklich.

', + 'en' => '

A kitchen is far more than a functional room. It is a meeting place, a workspace and the centre of daily life.

', + ], + 'cta_title' => ['de' => 'Ihre Küche. Persönlich geplant.', 'en' => 'Your kitchen. Personally planned.'], + 'cta_text' => [ + 'de' => 'Jeder Raum ist anders. Jeder Alltag stellt andere Anforderungen. Gemeinsam entwickeln wir eine Rotpunkt-Küche, die gestalterisch überzeugt, im Alltag funktioniert und dauerhaft zu Ihnen passt. Persönlich geplant bei B2in in der Rathausstraße 11 in Bielefeld.', + 'en' => 'Every room is different. Together we develop a Rotpunkt kitchen that convinces in design, works day to day and suits you for the long run.', + ], + 'sections' => [ + [ + 'title' => 'Aus Ostwestfalen. Für Ihr Zuhause.', + 'paragraphs' => [ + 'Rotpunkt entwickelt und fertigt seit 1930 hochwertige Küchenmöbel an den ostwestfälischen Standorten Bünde und Preußisch Oldendorf-Getmold.', + 'Regionale Fertigung, jahrzehntelange Erfahrung und die unmittelbare Nähe zum Hersteller sind für uns keine romantischen Randnotizen, sondern die Grundlage für verlässliche Qualität und persönliche Küchenplanung.', + ], + ], + [ + 'title' => 'Gestaltungsfreiheit bis ins Detail', + 'paragraphs' => [ + 'Rotpunkt bietet eine außergewöhnliche Vielfalt an Fronten, Farben, Materialien, Korpusdekoren, Arbeitsplatten, Griffvarianten und grifflosen Lösungen. Mehr als 20 beidseitig ausgeführte Korpusfarben sorgen dafür, dass eine Küche nicht nur außen, sondern auch innen konsequent gestaltet werden kann.', + 'So planen wir keinen vorgegebenen Küchenblock, sondern eine Lösung, die sich an Raum, Architektur und den tatsächlichen Gewohnheiten ihrer Besitzer orientiert.', + ], + 'detail' => [ + 'title' => 'FENIX und authentische Holzoptiken', + 'paragraphs' => [ + 'FENIX ist eine besonders matte Oberfläche mit geringer Lichtreflexion, angenehm weicher Haptik und ausgeprägten Anti-Fingerprint-Eigenschaften. Oberflächliche Mikrokratzer können thermisch behandelt werden. 27 aufeinander abgestimmte Farben eröffnen eine breite Gestaltungsvielfalt.', + 'Auch bei ausgewählten Fronten in Holzoptik zeigt sich die Liebe zum Detail. Synchronporen-Dekore stimmen sichtbare Maserung und fühlbare Struktur präzise aufeinander ab. Dadurch entsteht bereits bei pflegeleichten Melamin-Holzreproduktionen eine natürliche Wirkung.', + 'Durch eine sorgfältige Planung kann die Holzoptik über mehrere Fronten hinweggeführt werden. Das Ergebnis ist ein ruhiges, zusammenhängendes Frontbild – ohne dass dafür zwingend Echtholz eingesetzt werden muss.', + ], + ], + ], + [ + 'title' => 'Mehr Raum. Mehr Ergonomie. Mehr Küche.', + 'paragraphs' => [ + 'Besonders hohe Unterschränke, niedrige Sockelhöhen und zusätzliche Schrankbreiten schaffen mehr nutzbaren Stauraum und eröffnen neue planerische Möglichkeiten. Klingt nach Kleinkram? Vielleicht. Im täglichen Gebrauch macht genau dieser Kleinkram einen erstaunlich großen Unterschied.', + ], + 'detail' => [ + 'title' => 'NEW DIMENSIONS', + 'paragraphs' => [ + 'Mit dem Maßkonzept NEW DIMENSIONS sind beispielsweise Unterschrankhöhen von bis zu 910 Millimetern in Verbindung mit einem nur 50 Millimeter hohen Sockel möglich.', + 'Zusätzliche Schrankbreiten und vielfältige Innenausstattungen helfen uns, den vorhandenen Raum konsequent und ergonomisch zu nutzen. Die Küche wirkt dadurch nicht nur großzügiger – sie ist es tatsächlich.', + ], + ], + ], + [ + 'title' => 'Nachhaltigkeit mit konkreter Substanz', + 'paragraphs' => [ + 'Nachhaltigkeit braucht keine großen Versprechen, sondern nachvollziehbare Fakten. Greenline steht bei Rotpunkt für konkrete Materialien, Zertifizierungen und messbare Unterschiede.', + 'Die Greenline BioBoard Gen2 besteht aus bis zu 90 Prozent recyceltem Holz. 95 Prozent des Rotpunkt-Küchensortiments sind FSC-zertifiziert. Außerdem sind die Produkte nach JIS F4Star hinsichtlich ihrer Formaldehydemissionen zertifiziert.', + ], + 'detail' => [ + 'title' => 'Zahlen, Zertifizierungen und Raumklima', + 'paragraphs' => [ + // ⚠️ Waldflächen-Aussage: laut Zulieferung nur mit ausdrücklicher Herstellerbestätigung + // veröffentlichen. Steht als Punkt in der Deployment-Checkliste. + 'Nach Angaben von Rotpunkt bleibt durch den Einsatz der Greenline-Spanplatten jährlich eine Waldfläche von ungefähr 50 Fußballfeldern erhalten.', + 'Die Greenline BioBoard Gen2 gibt laut Rotpunkt rund 50 Prozent weniger Formaldehyd an die Raumluft ab als herkömmliche Produkte. Das ist insbesondere für ein möglichst gesundes Raumklima relevant.', + 'Die FSC-Zertifizierung wird unter der Kennung FSC C131864 geführt. Entscheidend ist für uns nicht das grüne Etikett, sondern die konkrete und überprüfbare Aussage dahinter.', + ], + ], + ], + [ + 'title' => 'Greenline hält dem Vergleich stand.', + 'paragraphs' => [ + 'Nachhaltigkeit braucht keine großen Versprechen, sondern nachvollziehbare Fakten. Deshalb legen wir offen, welche Materialien Rotpunkt verwendet, welche Zertifizierungen dahinterstehen und welchen konkreten Unterschied Greenline für Umwelt und Raumklima macht.', + 'Sie haben bereits eine vergleichbare Küche im Blick, die beim Thema Nachhaltigkeit mehr verspricht? Bringen Sie die Unterlagen gerne mit. Wir schauen gemeinsam genau hin – ehrlich, nachvollziehbar und ohne Verkaufsnebel.', + ], + ], + [ + 'title' => 'Mehr als Küchen', + 'paragraphs' => [ + 'Die Möglichkeiten von Rotpunkt enden nicht bei der klassischen Küche. Das Möbel- und Planungssystem lässt sich auch für Hauswirtschaftsräume, Ankleiden, Badezimmer und weitere Wohnbereiche einsetzen.', + 'Materialien, Farben und Gestaltungselemente können über mehrere Räume hinweg aufgegriffen werden. So entsteht nicht in jedem Zimmer eine neue Idee, sondern ein zusammenhängendes Interior-Konzept mit einer gemeinsamen gestalterischen Sprache.', + 'Zusammen mit CABINET, Febrü und weiteren ausgewählten Partnern betrachten wir bei Bedarf den gesamten Innenraum – von Möbeln und Beleuchtung bis zu Tapeten, Fußböden und, wenn es sein soll, der letzten Blume.', + ], + ], + [ + 'title' => 'Warum eine Rotpunkt-Küche bei uns?', + 'paragraphs' => [ + 'Bei uns beginnt eine neue Küche nicht mit einem Prospekt, sondern mit Ihrem Raum und der Frage, wie Sie darin leben möchten.', + 'Arbeitsabläufe, Ergonomie, Stauraum, Materialien, Beleuchtung, Geräte und angrenzende Wohnbereiche planen wir als zusammenhängendes Konzept. Auch die Partystellplätze wollen wohlüberlegt sein – also jene Orte, an denen Gäste später stehen, sitzen, reden und vielleicht etwas länger bleiben.', + 'Wir stellen nicht einfach möglichst viele Produkte zusammen. Wir entwickeln eine Küche, die gestalterisch, funktional und wirtschaftlich überzeugt – und begleiten den gesamten Weg von der ersten Idee bis zur professionellen Montage.', + 'Dabei planen wir nicht für den nächsten schnellen Verkauf, sondern für eine möglichst lange und zufriedene Nutzung. Das ist kurzfristig vielleicht nicht immer die wirtschaftlichste Entscheidung für uns. Für eine ehrliche und dauerhafte Kundenbeziehung ist es die einzig richtige.', + ], + ], + [ + 'title' => 'Erleben statt nur erzählen', + 'paragraphs' => [ + 'Eine gute Entscheidung entsteht nicht allein am Planungstisch. Auf Wunsch – und manchmal auch mit etwas freundlicher Überzeugungsarbeit – begleiten wir Sie nach Abstimmung in das nahe Rotpunkt-Werk oder in ausgewählte Ausstellungen der Gerätehersteller.', + 'Dort können Sie Materialien, Fertigung und Technik selbst erleben, Geräte ausprobieren und Fragen direkt vor Ort stellen. Sie müssen sich also nicht allein auf unser Verkaufsgespräch verlassen, sondern erhalten Informationen aus erster Hand, können vergleichen und anschließend mit einem sicheren Gefühl entscheiden.', + 'Wir begleiten Sie dabei, ordnen die unterschiedlichen Möglichkeiten ein und führen Ihre Entscheidungen zu einem stimmigen Gesamtkonzept zusammen.', + ], + ], + [ + 'title' => 'Küchenstudio plus Handwerk', + 'paragraphs' => [ + 'Rotpunkt deckt bereits eine enorme planerische und gestalterische Bandbreite ab. Es geht deshalb ausdrücklich nicht um Sonderanfertigungen um jeden Preis.', + 'Natürlich gibt es trotzdem immer diesen einen planerischen Sonderfall, bei dem selbst hervorragende Standardsysteme an ihre Grenzen kommen. Dann greift unsere dauerhaft verankerte Schnittstelle zu angeschlossenen Tischlereien.', + 'Wir wägen gestalterischen Mehrwert, technische Notwendigkeit und wirtschaftliche Sinnhaftigkeit ehrlich miteinander ab. Am Ende entscheiden aber Ihre Wünsche – nicht unser Drang, Ihnen unbedingt noch eine Sonderlösung zu verkaufen.', + ], + 'detail' => [ + 'title' => 'Wenn das Handwerk den letzten Millimeter übernimmt', + 'paragraphs' => [ + 'Möglich sind beispielsweise passgenaue Nischenlösungen, besondere Verkleidungen, raumhohe Einbauten, Übergänge zu angrenzenden Möbeln oder individuelle Anpassungen an die vorhandene Architektur.', + 'Bei Bedarf endet das Konzept nicht an der Arbeitsplatte. Auch Tapeten, Fußböden, Wandgestaltung, Beleuchtung und weitere Details können einbezogen werden.', + ], + ], + 'claim' => 'Die Möglichkeiten eines modernen Küchenstudios. Die Flexibilität des Handwerks.', + ], + ], + ], + 'februe' => [ + 'tagline' => ['de' => 'Arbeiten, wo man gerne bleibt.', 'en' => 'Work where people like to stay.'], + 'widget_label' => ['de' => 'Persönliche Arbeitsplatzberatung vereinbaren', 'en' => 'Arrange a personal workspace consultation'], + 'description' => [ + 'de' => '

Ein Büro ist kein Parkplatz für Schreibtische. Es ist ein Raum für Konzentration, Austausch, Bewegung und neue Ideen. Und manchmal entsteht die beste Idee eben nicht am Schreibtisch, sondern zwischen Kaffeemaschine, Lounge und einem guten Gespräch.

' + .'

Febrü verbindet ergonomische Arbeitsplätze mit wohnlichem Design, intelligentem Stauraum, durchdachter Akustik und flexiblen Raumstrukturen.

' + .'

Wir entwickeln daraus Arbeitswelten, die zu den Menschen, ihren Aufgaben und ihrer Umgebung passen – vom einzelnen Homeoffice bis zur vollständigen Unternehmensfläche.

', + 'en' => '

An office is not a car park for desks. It is a space for concentration, exchange, movement and new ideas.

', + ], + 'cta_title' => ['de' => 'Ihr Arbeitsplatz. Persönlich geplant.', 'en' => 'Your workspace. Personally planned.'], + 'cta_text' => [ + 'de' => 'Ob Homeoffice, Praxis, Kanzlei, wachsendes Unternehmen oder vollständige Arbeitswelt: Wir planen jeden Arbeitsplatz mit demselben Anspruch. Nur der Umfang verändert sich. Gemeinsam entwickeln wir eine Arbeitsumgebung, die gesundes Arbeiten unterstützt, gestalterisch überzeugt und den Menschen zeigt, dass ihre tägliche Leistung wahrgenommen und wertgeschätzt wird – persönlich geplant bei B2in in der Rathausstraße 11 in Bielefeld.', + 'en' => 'Whether a home office, a practice, a law firm or a complete working environment: we plan every workspace to the same standard. Only the scope changes.', + ], + 'sections' => [ + [ + 'title' => 'Zuhause oder Projekt. Zwei Wege. Derselbe Anspruch.', + 'paragraphs' => [ + 'Unsere Startseite führt bewusst über zwei Wege: Zuhause und Projekt. Denn ein einzelnes Homeoffice stellt andere Anforderungen als eine Büroetage mit fünfzig Arbeitsplätzen.', + 'Unterschiedliche Aufgaben brauchen unterschiedliche Planung und natürlich auch unterschiedliche Kalkulationen. Bei uns gibt es trotzdem nur Kunden – mit unterschiedlichen Räumen, Anforderungen und Stückzahlen. Der Anspruch an Produkt, Beratung und Service bleibt derselbe.', + ], + 'detail' => [ + 'title' => 'Bei uns gibt es Kunden – keine Schubladen', + 'paragraphs' => [ + 'B2B und B2C sind Begriffe aus Präsentationen und Vertriebsschulungen. Früher hieß es Privatkunde oder Firmenkunde. Oder, noch schlimmer: A-, B- oder C-Kunde. Bei diesem Schubladendenken machen wir nicht mit.', + 'Natürlich beeinflusst die Menge den Preis. Wer fünfzig Arbeitsplätze einrichtet, erhält eine andere Kalkulation als jemand, der einen einzelnen Schreibtisch für sein Homeoffice sucht. Alles andere wäre weder ehrlich noch kaufmännisch sinnvoll.', + 'Die Menge verändert aber weder die Qualität des Produktes noch die Sorgfalt der Planung, die persönliche Beratung oder den Service. Ein einzelner Arbeitsplatz im privaten Zuhause verdient dieselbe Aufmerksamkeit wie eine vollständige Büroetage.', + 'Febrü bleibt Febrü. B2in bleibt B2in. Und jeder Kunde erhält eine Lösung, die zu seinem tatsächlichen Bedarf passt.', + ], + ], + 'claim' => 'Ob im Kleinen oder im Großen – die Detailtreue entscheidet.', + ], + [ + 'title' => 'Aus Herford. Für moderne Arbeitswelten.', + 'paragraphs' => [ + 'Febrü entwickelt und produziert hochwertige Büromöbel direkt im ostwestfälischen Herford – nur wenige Kilometer von Bielefeld entfernt. Eine Fertigungstiefe von 95 Prozent ermöglicht kurze Wege, verlässliche Qualität und hohe Flexibilität bei Materialien, Farben und individuellen Anforderungen.', + ], + 'detail' => [ + 'title' => 'Von einem Schreibtisch bis zum vollständigen Bürotrakt', + 'paragraphs' => [ + 'Das Sortiment reicht von ergonomischen und höhenverstellbaren Schreibtischen über Konferenz- und Besprechungstische, Bürostühle und Loungeelemente bis zu Empfangslösungen, Stauraum, Akustikelementen, mobilen Medienmöbeln, Schließfachsystemen und flexiblen Raumstrukturen.', + 'Damit lässt sich ein einzelner Arbeitsplatz genauso überzeugend planen wie eine Praxis, eine Kanzlei, ein Konferenzbereich oder ein vollständiger Bürotrakt.', + ], + ], + 'claim' => 'Made in Herford. Geplant in Bielefeld.', + ], + [ + 'title' => 'Nicht drei Marken. Ein durchgängiger Raum.', + 'paragraphs' => [ + 'Ein Bürotrakt endet nicht am Schreibtisch. Menschen brauchen Orte zum Arbeiten, für Besprechungen und Rückzug – aber ebenso für Pausen, den gemeinsamen Kaffee, das Mittagessen und den informellen Austausch.', + 'Genau hier greifen Febrü, Rotpunkt und CABINET bei uns ineinander: Febrü gestaltet die Arbeitswelt, Rotpunkt die Küche im Bürotrakt und CABINET die millimetergenaue Sonderlösung.', + 'Entscheidend ist nicht, welcher Hersteller welches Möbel liefert. Entscheidend ist, dass am Ende alles zusammengehört. Wir stimmen Arbeitsplätze, Küche, Stauraum, Farben, Materialien, Beleuchtung und Akustik zu einem durchgängigen Konzept aufeinander ab.', + ], + 'detail' => [ + 'title' => 'Wie die drei Marken zusammenspielen', + 'paragraphs' => [ + 'Febrü gestaltet ergonomische Arbeitsplätze, Konferenzbereiche, Loungezonen, Akustik, Stauraum und flexible Raumstrukturen.', + 'Rotpunkt gestaltet Mitarbeiterküchen, Coffee-Points und gemeinsam genutzte Aufenthaltsküchen als selbstverständlichen Bestandteil der Arbeitswelt – und nicht als übrig gebliebene Funktionszeile. Gerade hier entstehen oft Gespräche, Verbindungen und Ideen, für die im offiziellen Meeting kein Platz war.', + 'CABINET übernimmt dort, wo Nischen vollständig genutzt, Schränke von Wand zu Wand und vom Boden bis zur Decke geplant oder besondere Übergänge zwischen Arbeitsplatz, Stauraum und Architektur geschaffen werden sollen. So entstehen beispielsweise maßgefertigte Garderoben, Aktenschränke, Medienlösungen und Stauraumelemente.', + ], + ], + 'claim' => 'Febrü für die Arbeitswelt. Rotpunkt für die Küche. CABINET für die millimetergenaue Sonderlösung. B2in für das Ganze.', + ], + [ + 'title' => 'Ein Arbeitsplatz. Fünfzig Arbeitsplätze. Ein Anspruch.', + 'paragraphs' => [ + 'Ob wir über einen einzelnen Arbeitsplatz oder einen vollständigen Bürotrakt sprechen: Das grundsätzliche Problem bleibt dasselbe. Fühlt sich der Mensch, der dort täglich arbeitet, gesehen und wertgeschätzt – oder wurde ihm lediglich irgendwo ein Tisch hingestellt?', + 'Die Stückzahl verändert den Umfang der Aufgabe, aber nicht ihre Bedeutung. Wir bewerten einen Arbeitsplatz deshalb nicht nach Quadratmetern oder Auftragswert, sondern danach, was er für den Menschen leisten muss.', + ], + 'detail' => [ + 'title' => 'Wertschätzung ist keine Frage der Stückzahl', + 'paragraphs' => [ + 'Gerade in Zeiten des Fachkräftemangels können Unternehmen es sich nicht leisten, Arbeitsbedingungen als Nebensache zu behandeln. Wer Top-Leistung erwartet, muss die passenden Voraussetzungen schaffen. Ein Obstkorb und kostenloses Wasser sind freundlich – ersetzen aber keinen guten Arbeitsplatz.', + 'Besonders deutlich wird das bei teilzeitbeschäftigten Müttern – selbstverständlich genauso bei Vätern und allen anderen Menschen mit familiärer Sorgeverantwortung. Teilzeit bedeutet nicht halbe Leistung. Oft wird in einem kürzeren Zeitfenster hoch konzentriert gearbeitet, während neben dem nächsten Onlinemeeting gleichzeitig Schule, Termine, Betreuung und damit unsere wichtigste gemeinsame Ressource organisiert werden: unser Nachwuchs.', + 'Gerade hier darf der Arbeitsplatz kein Provisorium aus Küchentisch, Beistelltisch oder Schlafzimmerecke sein. Er muss sofort funktionieren, Bewegung ermöglichen, technisch durchdacht sein und sich trotzdem vernünftig in das Zuhause einfügen. Für den Menschen, der ihn täglich nutzt, ist ein einzelner Arbeitsplatz keine kleine Lösung. Er ist seine vollständige Arbeitswelt.', + ], + ], + ], + [ + 'title' => 'Ergonomie, die im Alltag funktioniert', + 'paragraphs' => [ + 'Ein Arbeitsplatz muss sich dem Menschen anpassen – nicht umgekehrt. Höhenverstellbare Schreibtische und ergonomische Bürostühle schaffen Bewegung und unterstützen eine gesunde Haltung.', + 'Ergonomie endet aber nicht bei Tisch und Stuhl. Auch Laufwege, Greifräume, Bildschirmposition, Beleuchtung, Stauraum und der Wechsel zwischen Tätigkeiten bestimmen, wie angenehm und produktiv ein Arbeitstag verläuft.', + ], + 'detail' => [ + 'title' => 'Und nein: Wir meinen nicht nur den Weg zur Kaffeemaschine', + 'paragraphs' => [ + 'Sinnvolle Laufwege sind Teil einer guten Planung – auch wenn Kaffeemaschine und Kühlschrank zweifellos eine gewisse Bedeutung für den Arbeitsalltag haben.', + 'Das gilt für vier Stunden ebenso wie für zehn Stunden am Schreibtisch, im Unternehmen genauso wie im privaten Zuhause. Und natürlich müssen die Möbel gestalterisch zum Raum passen. Wer denkt: „Das sieht doch keiner – außer den Mitarbeitern“, hat den ersten entscheidenden Fehler bereits gemacht. Die Mitarbeiter sehen es. Jeden Tag. Und sie arbeiten darin.', + ], + ], + 'quote' => 'Ich bin, sagen wir, alt genug, um beim Aufstehen gelegentlich zu stöhnen. Und ehrlich gesagt fühle ich mich an manchen Tagen noch etwas älter. Mein Arzt sagt, ich solle weniger sitzen. Mein Beruf findet nun einmal zu einem großen Teil am Computer statt. Ergo brauche ich eine Lösung, die mir mehr Bewegung ermöglicht, ohne dass ich dafür meinen Beruf wechseln muss.', + 'quote_author' => 'Marcel Scheibe, Geschäftsführer von B2in', + ], + [ + 'title' => 'Office meets Interior', + 'paragraphs' => [ + 'Identische Schreibtischreihen werden einem modernen Arbeitstag nicht mehr gerecht. Menschen benötigen Bereiche für Konzentration, spontane Gespräche, Teamarbeit, Videokonferenzen, vertrauliche Besprechungen und erholsame Pausen.', + 'Febrü verbindet dafür klassische Arbeitsplatzmöbel mit wohnlichen Loungeelementen, mobilen Multifunktionslösungen, Stauraum, Akustik und flexibler Raumzonierung. So entsteht nicht für jede Person derselbe Platz, sondern für jede Tätigkeit der richtige.', + ], + 'detail' => [ + 'title' => 'Das Wir und das Ich', + 'paragraphs' => [ + 'Ja, ja – das könnte man unter dem üblichen Verkaufs-Blabla abheften. Dahinter steht aber eine einfache Wahrheit: Verantwortung lässt sich nicht dauerhaft nach außen delegieren. Was bleibt, sind das Wir und das Ich.', + 'Ein Unternehmen muss Bedingungen schaffen, unter denen Menschen gemeinsam gut arbeiten, sich austauschen und konzentrieren können. Der Einzelne erhält die Möglichkeit – und trägt auch die Verantwortung –, daraus das Beste zu machen.', + 'Räume und Möbel schaffen allein noch keine gute Unternehmenskultur. Schlechte Arbeitsbedingungen können eine gute Kultur aber jeden Tag unnötig erschweren.', + ], + ], + 'claim' => 'Das Unternehmen stärkt das Wir. Der Einzelne macht daraus das Beste.', + ], + [ + 'title' => 'Ruhe und Flexibilität sind planbar.', + 'paragraphs' => [ + 'Akustik, mobile Elemente und flexible Raumlösungen helfen dabei, Bereiche für Kommunikation, Konzentration und Rückzug zu schaffen – und sie später an veränderte Teams und Arbeitsweisen anzupassen.', + ], + 'detail' => [ + 'title' => 'Akustik und flexible Raumstrukturen', + 'paragraphs' => [ + 'Hallende Räume, Telefongespräche und Videokonferenzen können Konzentration und Sprachverständlichkeit erheblich beeinträchtigen. Akustikpaneele, Trennwände, Screens und entsprechend geplante Möbel helfen, Schall zu reduzieren.', + 'Mit modularen Möbeln und Raum-in-Raum-Lösungen lassen sich Arbeitsbereiche neu ordnen, ohne bei jeder Veränderung gleich feste Wände bauen zu müssen. Das ist im großen Büro wichtig – und im privaten Zuhause häufig noch wichtiger.', + ], + ], + ], + [ + 'title' => 'Gestaltung, die zur Identität passt', + 'paragraphs' => [ + 'Ein Büro ist ein sichtbarer Teil der Unternehmenskultur. Farben, Materialien und Raumwirkung beeinflussen, wie Mitarbeitende, Kunden und Bewerber ein Unternehmen wahrnehmen. Im Homeoffice muss der Arbeitsplatz professionell funktionieren und sich zugleich in die vorhandene Einrichtung einfügen.', + 'Dank der hohen Fertigungstiefe bietet Febrü eine große Auswahl an Materialien, Dekoren, Stoffen und Gestellfarben. Corporate Design muss nicht am Briefpapier enden. Und ein Homeoffice muss nach Feierabend nicht wie ein kleines Finanzamt aussehen.', + ], + 'detail' => [ + 'title' => 'Und ja: Pflanzen gehören dazu', + 'paragraphs' => [ + 'Eine Pflanze macht aus einem Unternehmen noch kein nachhaltiges Unternehmen. Sie macht aus einem technisch eingerichteten Raum aber häufig erst einen Ort, an dem Menschen gerne arbeiten. Das hat nichts mit Greenwashing zu tun, sondern schlicht mit Atmosphäre und Stil.', + ], + ], + ], + [ + 'title' => 'Nachhaltigkeit, die länger als bis zum nächsten Umzug hält', + 'paragraphs' => [ + 'Nachhaltigkeit beginnt bei der Lebensdauer eines Möbelstücks. Viele Febrü-Möbel sind modular aufgebaut, sodass Komponenten ausgetauscht oder ergänzt werden können, statt bei veränderten Anforderungen das gesamte Möbelstück zu ersetzen.', + ], + 'detail' => [ + 'title' => 'Konkrete Standards statt allgemeiner Versprechen', + 'paragraphs' => [ + 'Febrü produziert mit hoher Fertigungstiefe direkt in Herford. Das sorgt für kurze Wege und kontrollierte Prozesse. Mit laserTEC entstehen hochwertige, fugenlose Kanten ohne zusätzlichen Klebstoff.', + 'Febrü arbeitet nach ISO 9001 und ISO 14001 und verfügt über einen PEFC-Produktkettennachweis.', + 'Ausgewählte Febrü-Tischsysteme und Stauraummöbel tragen den Blauen Engel. Die Aussage bezieht sich ausdrücklich auf die offiziell gelisteten Produktgruppen und nicht pauschal auf das gesamte Sortiment.', + ], + ], + 'claim' => 'Nachhaltig ist nicht das Möbelstück mit dem längsten Werbetext. Nachhaltig ist das Möbelstück, das viele Jahre funktioniert und veränderte Anforderungen mitmacht.', + ], + [ + 'title' => 'Warum Febrü bei uns?', + 'paragraphs' => [ + 'Bei uns beginnt eine Planung nicht mit einem Möbelkatalog, sondern mit den Menschen und der Frage, wie tatsächlich gearbeitet wird. Wo ist Konzentration gefragt? Wo entstehen Gespräche? Welche Bereiche benötigen Diskretion? Und wie viel Stauraum wird wirklich gebraucht?', + 'Wir verbinden Ergonomie, Laufwege, Akustik, Stauraum, Beleuchtung, Kommunikation und Rückzug zu einem Gesamtkonzept. Es geht nicht darum, möglichst viele Möbel in einen Raum zu stellen. Es geht darum, eine bessere Arbeitswelt zu schaffen.', + ], + 'detail' => [ + 'title' => 'Im Homeoffice stellen sich andere Fragen', + 'paragraphs' => [ + 'Wie sichtbar darf der Arbeitsplatz sein? Wo verschwinden Technik und Unterlagen nach Feierabend? Und wie lässt sich professionelles Arbeiten so in die vorhandene Einrichtung integrieren, dass das Zuhause ein Zuhause bleibt?', + 'Auch hier gilt: Ein einzelner Arbeitsplatz ist keine kleinere Aufgabe. Er braucht nur eine andere Antwort als ein vollständiger Bürotrakt.', + ], + ], + ], + [ + 'title' => 'Erleben statt nur aus dem Katalog auswählen', + 'paragraphs' => [ + 'Durch die Nähe zwischen Bielefeld und Herford können Febrü-Produkte nach Abstimmung persönlich erlebt werden. Wir organisieren und begleiten den Besuch der Ausstellung – für einen Schreibtisch genauso wie für einen vollständigen Unternehmensstandort.', + 'Dort lassen sich Materialien anfassen, Stühle und Tische ausprobieren, Akustiklösungen erleben und unterschiedliche Arbeitswelten vergleichen. Sie müssen sich nicht allein auf ein Verkaufsgespräch verlassen.', + ], + 'claim' => 'Ob im Kleinen oder im Großen – die Detailtreue entscheidet.', + ], + ], + ], + ]; + } +} diff --git a/dev/2026-08-28/deploy/dateiliste.txt b/dev/2026-08-28/deploy/dateiliste.txt index e586c67c..44429b4c 100644 --- a/dev/2026-08-28/deploy/dateiliste.txt +++ b/dev/2026-08-28/deploy/dateiliste.txt @@ -1,30 +1,80 @@ .env.example +.gitignore app/Helpers/ThemeHelper.php +app/Http/Controllers/DisplayEmbedController.php app/Livewire/Admin/Cms/CabinetInfoTablet.php +app/Livewire/Web/Components/Sections/AudienceSwitch.php +app/Livewire/Web/Components/Sections/BrandDetail.php +app/Livewire/Web/Components/Sections/BrandOverview.php +app/Livewire/Web/Components/Sections/CoreBrands.php +app/Livewire/Web/Components/Sections/DisplayFeed.php +app/Livewire/Web/Components/Sections/ImmobilienTeaser.php app/Livewire/Web/Components/Sections/MagazinDetail.php app/Livewire/Web/Components/Sections/MagazinList.php +app/Livewire/Web/Components/Sections/MarkenCta.php +app/Livewire/Web/Components/Sections/MarkenGrowth.php +app/Livewire/Web/Components/Sections/MarkenNetwork.php +app/Livewire/Web/Components/Sections/PageHero.php +app/Livewire/Web/Components/Sections/ReferenceProjects.php +app/Livewire/Web/Components/Sections/Sponsoring.php app/Livewire/Web/Components/UI/AnnouncementBar.php +app/Livewire/Web/Components/UI/AppointmentWidget.php app/Livewire/Web/Components/UI/Footer.php app/Models/CmsArticle.php +app/Models/CmsBrand.php app/Providers/ThemeServiceProvider.php app/Support/DisplayModuleSettings.php app/helpers.php config/app.php config/cms_section_map.php +config/seeding.php +database/factories/CmsBrandFactory.php +database/migrations/2026_08_28_110407_create_cms_brands_table.php +database/migrations/2026_08_28_112244_add_summary_and_highlights_to_cms_brands_table.php +database/migrations/2026_09_03_084254_add_page_sections_to_cms_brands_table.php +database/seeders/BrandPageTextSeeder.php database/seeders/CmsArticleSeeder.php +database/seeders/CmsBrandSeeder.php database/seeders/CmsContentSeeder.php database/seeders/DatabaseSeeder.php +database/seeders/DemoBrandGallerySeeder.php +database/seeders/DisplayInteriorBrandingSeeder.php +database/seeders/DisplayMediaUrlNormalizerSeeder.php +database/seeders/InteriorRelaunchCmsSeeder.php +public/_cabinet/assets/b2in-interior-logo-negative.svg +public/_cabinet/assets/b2in-interior-logo-positive.svg public/_cabinet/display/index.html +public/img/assets/sponsoring/arminia-bielefeld.png +public/img/assets/sponsoring/tc-bad-waldliesborn-horn.jpg +public/img/assets/sponsoring/tc-hoevelhof.png +public/img/logos/b2in-interior-logo-negative.png +public/img/logos/b2in-interior-logo-negative.svg +public/img/logos/b2in-interior-logo-positive.png +public/img/logos/b2in-interior-logo-positive.svg resources/lang/de/b2in.php resources/lang/de/ui.php resources/lang/en/b2in.php resources/lang/en/ui.php +resources/views/livewire/admin/cms/brands-index.blade.php resources/views/livewire/admin/cms/cabinet-info-tablet.blade.php resources/views/livewire/admin/cms/content-index.blade.php resources/views/livewire/admin/cms/dashboard.blade.php resources/views/livewire/admin/cms/projects-index.blade.php +resources/views/livewire/web/components/sections/audience-switch.blade.php +resources/views/livewire/web/components/sections/brand-detail.blade.php +resources/views/livewire/web/components/sections/brand-overview.blade.php +resources/views/livewire/web/components/sections/core-brands.blade.php +resources/views/livewire/web/components/sections/display-feed.blade.php +resources/views/livewire/web/components/sections/immobilien-teaser.blade.php resources/views/livewire/web/components/sections/magazin-list.blade.php +resources/views/livewire/web/components/sections/marken-cta.blade.php +resources/views/livewire/web/components/sections/marken-growth.blade.php +resources/views/livewire/web/components/sections/marken-network.blade.php +resources/views/livewire/web/components/sections/page-hero.blade.php +resources/views/livewire/web/components/sections/reference-projects.blade.php +resources/views/livewire/web/components/sections/sponsoring.blade.php resources/views/livewire/web/components/ui/announcement-bar.blade.php +resources/views/livewire/web/components/ui/appointment-widget.blade.php resources/views/livewire/web/components/ui/footer.blade.php resources/views/livewire/web/components/ui/header.blade.php resources/views/vendor/mail/html/header.blade.php @@ -37,77 +87,33 @@ resources/views/web/impressum.blade.php resources/views/web/layouts/web-master.blade.php resources/views/web/magazin-detail.blade.php resources/views/web/magazin.blade.php +resources/views/web/marken-show.blade.php +resources/views/web/marken.blade.php resources/views/web/netzwerk.blade.php resources/views/web/privacy.blade.php +resources/views/web/referenzen.blade.php resources/views/web/terms.blade.php routes/admin.php routes/web.php +tests/Feature/Admin/Cms/CmsBrandsIndexTest.php tests/Feature/AnnouncementBarTest.php +tests/Feature/AppointmentWidgetTest.php +tests/Feature/BrandPageSectionsTest.php +tests/Feature/BrandPageTest.php tests/Feature/CabinetInfoTabletTest.php tests/Feature/Cms/CmsArticleTest.php -tests/Feature/Cms/CmsContentSeederTest.php -tests/Feature/DisplayVersionApiTest.php -tests/Feature/DisplayVersionTest.php -tests/Feature/InteriorPageTest.php -tests/Feature/LiveSiteReviewTest.php -tests/Feature/MagazinPageTest.php -app/Http/Controllers/DisplayEmbedController.php -app/Livewire/Web/Components/Sections/AudienceSwitch.php -app/Livewire/Web/Components/Sections/BrandDetail.php -app/Livewire/Web/Components/Sections/BrandOverview.php -app/Livewire/Web/Components/Sections/CoreBrands.php -app/Livewire/Web/Components/Sections/DisplayFeed.php -app/Livewire/Web/Components/Sections/ImmobilienTeaser.php -app/Livewire/Web/Components/Sections/MarkenCta.php -app/Livewire/Web/Components/Sections/MarkenGrowth.php -app/Livewire/Web/Components/Sections/MarkenNetwork.php -app/Livewire/Web/Components/Sections/PageHero.php -app/Livewire/Web/Components/Sections/ReferenceProjects.php -app/Livewire/Web/Components/Sections/Sponsoring.php -app/Livewire/Web/Components/Ui/AppointmentWidget.php -app/Models/CmsBrand.php -config/seeding.php -database/factories/CmsBrandFactory.php -database/migrations/2026_08_28_110407_create_cms_brands_table.php -database/migrations/2026_08_28_112244_add_summary_and_highlights_to_cms_brands_table.php -database/seeders/CmsBrandSeeder.php -database/seeders/DemoBrandGallerySeeder.php -database/seeders/DisplayInteriorBrandingSeeder.php -database/seeders/DisplayMediaUrlNormalizerSeeder.php -database/seeders/InteriorRelaunchCmsSeeder.php -public/_cabinet/assets/b2in-interior-logo-negative.svg -public/_cabinet/assets/b2in-interior-logo-positive.svg -public/img/assets/sponsoring/ -public/img/logos/b2in-interior-logo-negative.png -public/img/logos/b2in-interior-logo-negative.svg -public/img/logos/b2in-interior-logo-positive.png -public/img/logos/b2in-interior-logo-positive.svg -resources/views/livewire/admin/cms/brands-index.blade.php -resources/views/livewire/web/components/sections/audience-switch.blade.php -resources/views/livewire/web/components/sections/brand-detail.blade.php -resources/views/livewire/web/components/sections/brand-overview.blade.php -resources/views/livewire/web/components/sections/core-brands.blade.php -resources/views/livewire/web/components/sections/display-feed.blade.php -resources/views/livewire/web/components/sections/immobilien-teaser.blade.php -resources/views/livewire/web/components/sections/marken-cta.blade.php -resources/views/livewire/web/components/sections/marken-growth.blade.php -resources/views/livewire/web/components/sections/marken-network.blade.php -resources/views/livewire/web/components/sections/page-hero.blade.php -resources/views/livewire/web/components/sections/reference-projects.blade.php -resources/views/livewire/web/components/sections/sponsoring.blade.php -resources/views/livewire/web/components/ui/appointment-widget.blade.php -resources/views/web/marken-show.blade.php -resources/views/web/marken.blade.php -resources/views/web/referenzen.blade.php -tests/Feature/Admin/Cms/CmsBrandsIndexTest.php -tests/Feature/AppointmentWidgetTest.php -tests/Feature/BrandPageTest.php tests/Feature/Cms/CmsBrandTest.php +tests/Feature/Cms/CmsContentSeederTest.php tests/Feature/DisplayInteriorBrandingTest.php tests/Feature/DisplayMediaUrlNormalizerTest.php +tests/Feature/DisplayVersionApiTest.php +tests/Feature/DisplayVersionTest.php tests/Feature/DisplayWebFeedTest.php tests/Feature/FooterTest.php tests/Feature/HomePageStructureTest.php +tests/Feature/InteriorPageTest.php +tests/Feature/LiveSiteReviewTest.php +tests/Feature/MagazinPageTest.php tests/Feature/MarkenPageTest.php tests/Feature/PreviewHostTest.php tests/Feature/ReferenzenPageTest.php diff --git a/dev/2026-08-28/entwicklungsplan.md b/dev/2026-08-28/entwicklungsplan.md index 200fb4c6..6eaf95a1 100644 --- a/dev/2026-08-28/entwicklungsplan.md +++ b/dev/2026-08-28/entwicklungsplan.md @@ -4,8 +4,8 @@ **Basis:** `B2in_Interior-Konzept.md`, `umsetzungliste.md`, `umsetzung-inhalte.md` **Vorgänger:** `dev/2026-02-27/b2in-website-umsetzungsplan.md` **Rahmen:** 37–39 Std. freigegeben (18 + 8 + 4 + 3 + 4–6) -**Status:** Revision 12 – Befunde abgearbeitet, **AP0–AP6 umgesetzt**. Testbereit; offen sind nur -noch Zulieferungen und die Rechtstexte. +**Status:** Revision 13 – **AP0–AP6 umgesetzt**, Seite abgestimmt, Markentexte Rotpunkt und Febrü +eingearbeitet. Offen: Bildmaterial, Rechtstexte und die Herstellerfreigaben. **Umgebung:** Dev Container, `php artisan …` (nicht `vendor/bin/sail …`) > **Verbindliche Schreibweisen:** **Interior** (nicht „Interieur"), **Febrü**, **Rotpunkt Küchen**, **CABINET**. @@ -16,9 +16,10 @@ noch Zulieferungen und die Rechtstexte. ## 0. Stand & nächste Schritte -**Stand 28.08.2026:** T1 und B1–B13 abgearbeitet, **alle Arbeitspakete AP0–AP6 umgesetzt**, die -Bestandsinhalte auf den Interior-Auftritt umgestellt. `php artisan test --compact`: **826 grün** -(3072 Assertions), Pint sauber, `npm run build:b2in` gebaut. +**Stand 03.09.2026:** T1 und B1–B13 abgearbeitet, **alle Arbeitspakete AP0–AP6 umgesetzt**, die +Bestandsinhalte auf den Interior-Auftritt umgestellt, die Seite abgestimmt und die Markentexte für +Rotpunkt und Febrü eingearbeitet. `php artisan test --compact`: **854 grün** (3150 Assertions), +Pint sauber, `npm run build:b2in` gebaut. **Die Seite ist testbereit** – die ausstehenden Zulieferungen trägt Marcel selbst im CMS nach. | Paket | Stand | @@ -29,6 +30,7 @@ Bestandsinhalte auf den Interior-Auftritt umgestellt. `php artisan test --compac | AP3 Terminanfrage-Widget + WhatsApp-Link | ✅ (Nummer fehlt noch) | | AP6 Sponsoring | ✅ | | Bestandsinhalte FAQ / About / Kontakt / Magazin | ✅ | +| Markenseiten Rotpunkt und Febrü (Zulieferung 31.08.) | ✅ | | AP4 Display-Spiegelung | ✅ | | AP5 Rubrik-Seite „Referenzen" | ✅ | @@ -725,6 +727,81 @@ Dubai-Beiträge bleiben inhaltlich unverändert stehen. --- +### Nachtrag 4 · Markenseiten Rotpunkt und Febrü aus der Zulieferung ✅ + +**Grundlage:** `Rotpunkt_Kuechen_Webseitentext_B2in_FINAL.pdf` und +`Februe_Webseitentext_B2in_FINAL.pdf` (Stand 31.08.2026, im selben Ordner). Beide enthalten neben +dem Seitentext einen Abschnitt „UMSETZUNGSHINWEIS FÜR KEVIN – WEBSTRUKTUR UND CTA“. + +> Die als Aufgabenliste angekündigte Datei `umsetzung-03-09-2026.md` war leer (0 Byte). Umgesetzt +> ist deshalb das, was in den beiden PDFs steht. + +**Was die Zulieferung verlangt:** + +1. Der normale Seitentext bleibt direkt sichtbar. +2. Die mit „AUFKLAPPBARER DETAILBEREICH“ gekennzeichneten Vertiefungen werden aufklappbar. +3. **Kein zweiter CTA-Button.** Stattdessen bekommt das vorhandene schwebende + Terminanfrage-Widget auf der jeweiligen Markenseite eine kontextbezogene Beschriftung. +4. Das Widget bleibt beim Scrollen sichtbar und darf auf dem Smartphone nichts verdecken. +5. Die internen Quellenhinweise am Ende der PDFs werden **nicht** veröffentlicht. + +#### Umsetzung + +**Datenstruktur.** `description` (Einleitung) und `services` (Kacheln) reichten für diese Texte +nicht aus. Neu in `cms_brands`: + +| Feld | Inhalt | +|---|---| +| `sections` | Abschnitte der Seite: Titel, Absätze, optional Schlusszeile, Zitat und Vertiefung | +| `widget_label` | Beschriftung des Terminanfrage-Widgets auf dieser Markenseite | +| `cta_title` / `cta_text` | Abschlussblock der Seite | + +`sections` ist – wie `services`, `highlights` und `gallery` – bewusst **nicht** übersetzbar; die +Texte liegen nur auf Deutsch vor. Die drei Textfelder folgen den übrigen Textfeldern und sind +übersetzbar. `CmsBrand::normalizeSections()` bringt jeden Eintrag in eine feste Form und nimmt +Absätze wahlweise als Liste oder als Textblock entgegen (Leerzeile trennt) – dadurch lässt sich im +Admin einfach in ein Textfeld schreiben. + +**Darstellung.** Die Abschnitte stehen zwischen Einleitung und Leistungen. Vertiefungen sind +aufklappbar, mit `aria-expanded` und `aria-controls`. Bewusst `x-transition` statt `x-collapse`: +Das Collapse-Plugin ist in diesem Projekt nicht registriert, die Bewegung entspricht jetzt dem +FAQ-Akkordeon. Zitate stehen abgesetzt mit Nennung des Sprechers, Schlusszeilen hervorgehoben. + +**Widget-Beschriftung.** Das Widget steckt im Layout, nicht in der Seite – es liest den Kontext +deshalb aus der Route: Auf `marken.show` greift `widget_label` der Marke, sonst die allgemeine +Beschriftung. Ein Test prüft gegen, dass dabei **kein zweiter Button** entsteht. + +**Platz auf dem Smartphone.** Der Footer bekommt unten zusätzlichen Abstand (`pb-28 sm:pb-16`), +und der Button bricht lange Beschriftungen um, statt über den Bildschirmrand zu laufen – „Persönliche +Arbeitsplatzberatung vereinbaren“ ist deutlich länger als das vorherige „Termin anfragen“. + +**Pflegbarkeit.** Die Abschnitte sind vollständig im Admin pflegbar: hinzufügen, löschen, per Pfeil +sortieren, je Abschnitt Überschrift, Text, Schlusszeile, Zitat und Vertiefung. Dazu die drei neuen +Felder für Abschluss und Widget-Beschriftung. + +**Einspielen.** `BrandPageTextSeeder` schreibt die Texte. Er fasst **nur** `tagline`, +`description`, `sections`, `widget_label`, `cta_title` und `cta_text` an – Bilder, Galerie, +Leistungen und Reihenfolge bleiben unangetastet. Ein Test prüft genau das. + +**Nebenbei korrigiert:** Febrüs Claim hieß „Arbeiten, wo man gerne ist.“, die Zulieferung schreibt +„Arbeiten, wo man gerne **bleibt**.“ + +**Tests:** `BrandPageSectionsTest` (17 Fälle: Abschnitte beider Marken, Anzahl der aufklappbaren +Bereiche, Zitat, Schlusszeilen, keine Quellenhinweise im Ausgabe-HTML, markenspezifischer +Abschlussblock mit Rückfall auf den allgemeinen, Widget-Beschriftung je Marke, kein zweiter Button, +Normalisierung, Redaktionsschutz des Seeders) und vier neue Fälle in `Admin/Cms/CmsBrandsIndexTest`. + +> ⚠️ **Vor der Veröffentlichung freigeben lassen.** Die PDFs halten fest: Aussagen nur im belegten +> Umfang veröffentlichen, Herstellerfreigabe einholen, und die Rotpunkt-Aussage zur jährlich +> erhaltenen **Waldfläche von ungefähr 50 Fußballfeldern** nur mit ausdrücklicher Bestätigung +> verwenden. Sie steht als eigener Absatz in der Vertiefung „Zahlen, Zertifizierungen und +> Raumklima“ und lässt sich dort einzeln entfernen. Als Punkt in Abschnitt 8 aufgenommen. + +**Offen:** CABINET hat noch keinen ausführlichen Seitentext – die Seite läuft weiter über +Einleitung und Leistungskacheln. Bilder fehlen weiterhin für alle drei Marken. + +--- + ### AP4 · Display-Spiegelung auf der Website — ✅ umgesetzt **Korrektur gegenüber Revision 1:** Revision 1 empfahl, die Slides nativ zu rendern. Bei 3 Std. @@ -973,6 +1050,12 @@ genügt ein selektives Zurückspielen von `flux_cms_contents`, `display_versions - [ ] Prüfen, welches Display auf der Website laufen soll (`referenzen.display_feed.display_id`). Ohne Eintrag zeigt die Seite das erste aktive Display mit veröffentlichter Playlist. - [ ] Die zwei neuen Magazin-Beiträge von Marcel gegenlesen lassen – sie sind Platzhalter. +- [ ] **Herstellerfreigabe für die Markentexte** (Rotpunkt, Febrü) einholen – so verlangt es die + Zulieferung selbst. +- [ ] **Rotpunkt, Vertiefung „Zahlen, Zertifizierungen und Raumklima“:** Die Aussage zur jährlich + erhaltenen Waldfläche von ungefähr 50 Fußballfeldern darf laut Zulieferung nur mit + ausdrücklicher Bestätigung veröffentlicht werden. Ohne Bestätigung den Absatz entfernen + (Admin: Kernmarken → Rotpunkt → Abschnitte → Vertiefung). - [ ] OG-Bild anlegen: das Layout verweist auf `img/assets/b2in-og-default.jpg`, die Datei existiert nicht. Bis dahin zeigt jede geteilte Seite eine Vorschau ohne Bild. - [ ] Sponsoring-Logo `tc-bad-waldliesborn-horn` liegt nur als JPG vor, also ohne transparenten diff --git a/dev/2026-08-28/preview-deployment.md b/dev/2026-08-28/preview-deployment.md index 737c334b..705fea8e 100644 --- a/dev/2026-08-28/preview-deployment.md +++ b/dev/2026-08-28/preview-deployment.md @@ -179,6 +179,9 @@ php artisan db:seed --class=InteriorRelaunchCmsSeeder --force # Magazin: die zwei neuen Beiträge plus Reihenfolge php artisan db:seed --class=CmsArticleSeeder --force +# Markenseiten Rotpunkt und Febrü (Zulieferung 31.08.) – nur Textfelder, Bilder bleiben +php artisan db:seed --class=BrandPageTextSeeder --force + # Display-Branding auf das Interior-Logo php artisan db:seed --class=DisplayInteriorBrandingSeeder --force @@ -247,6 +250,8 @@ auth_basic_user_file /etc/nginx/.htpasswd-preview; | `/` | Hero → Marken-Teaser → Vision → Sponsoring → Showroom-Display → Immobilien → CTA | | `/marken` | Grauer Seitenkopf mit Bild, drei gerahmte Markenblöcke | | `/marken/cabinet` | Detailseite, Galerie mit den zwei Rubriken (falls Demo-Seeder lief) | +| `/marken/rotpunkt` | Neun Abschnitte, vier davon mit aufklappbarer Vertiefung; Button unten rechts heißt „Persönliche Küchenberatung vereinbaren" | +| `/marken/februe` | Elf Abschnitte, Zitat von Marcel; Button heißt „Persönliche Arbeitsplatzberatung vereinbaren" | | `/referenzen` | Realisierte Projekte, darunter das Showroom-Display im Hochformat | | `/magazin` | Die zwei Interior-Beiträge stehen oben | | `/faq` | Rubriken „Für Ihr Zuhause" und „Für Projekte und Objekte" | diff --git a/resources/views/livewire/admin/cms/brands-index.blade.php b/resources/views/livewire/admin/cms/brands-index.blade.php index e0ca34a4..07d0cba5 100644 --- a/resources/views/livewire/admin/cms/brands-index.blade.php +++ b/resources/views/livewire/admin/cms/brands-index.blade.php @@ -25,6 +25,10 @@ state([ 'logo' => '', 'image' => '', 'services' => [], + 'sections' => [], + 'widgetLabel' => '', + 'ctaTitle' => '', + 'ctaText' => '', 'gallery' => [], 'website_url' => '', 'showroom' => '', @@ -66,7 +70,8 @@ $brands = computed( $openCreate = function () { $this->editingId = null; - $this->reset(['slug', 'brandName', 'tagline', 'teaser', 'summary', 'description', 'logo', 'image', 'website_url', 'showroom']); + $this->reset(['slug', 'brandName', 'tagline', 'teaser', 'summary', 'description', 'logo', 'image', 'website_url', 'showroom', 'widgetLabel', 'ctaTitle', 'ctaText']); + $this->sections = []; $this->highlights = ['', '', '']; $this->services = [['title' => '', 'description' => '']]; $this->gallery = []; @@ -81,6 +86,9 @@ $loadLocaleFields = function (CmsBrand $brand, string $locale) { $this->teaser = $brand->getTranslation('teaser', $locale) ?? ''; $this->summary = $brand->getTranslation('summary', $locale) ?? ''; $this->description = CmsFluxEditorHtmlTransformer::toEditor((string) ($brand->getTranslation('description', $locale) ?? '')); + $this->widgetLabel = $brand->getTranslation('widget_label', $locale) ?? ''; + $this->ctaTitle = $brand->getTranslation('cta_title', $locale) ?? ''; + $this->ctaText = $brand->getTranslation('cta_text', $locale) ?? ''; }; $openEdit = function (int $id) { @@ -101,6 +109,18 @@ $openEdit = function (int $id) { $this->highlights = $brand->highlights ?: ['', '', '']; $this->services = $brand->services ?: [['title' => '', 'description' => '']]; $this->gallery = CmsBrand::normalizeGallery($brand->gallery); + // Absätze im Formular als Textblock: eine Leerzeile trennt sie. + $this->sections = collect(CmsBrand::normalizeSections($brand->sections)) + ->map(fn (array $s) => [ + 'title' => $s['title'], + 'paragraphs' => implode("\n\n", $s['paragraphs']), + 'claim' => $s['claim'], + 'quote' => $s['quote'], + 'quote_author' => $s['quote_author'], + 'detail_title' => $s['detail']['title'] ?? '', + 'detail_paragraphs' => implode("\n\n", $s['detail']['paragraphs'] ?? []), + ]) + ->all(); $this->loadLocaleFields($brand, $this->editLocale); $this->showForm = true; @@ -132,6 +152,26 @@ $removeService = function (int $index) { $this->services = array_values($this->services); }; +$addSection = fn () => $this->sections[] = [ + 'title' => '', 'paragraphs' => '', 'claim' => '', + 'quote' => '', 'quote_author' => '', 'detail_title' => '', 'detail_paragraphs' => '', +]; + +$removeSection = function (int $index) { + unset($this->sections[$index]); + $this->sections = array_values($this->sections); +}; + +$moveSection = function (int $index, int $direction) { + $target = $index + $direction; + + if (! isset($this->sections[$index], $this->sections[$target])) { + return; + } + + [$this->sections[$index], $this->sections[$target]] = [$this->sections[$target], $this->sections[$index]]; +}; + $addGalleryImage = function (string $category = CmsBrand::GALLERY_PROJECT) { $this->gallery[] = [ 'image' => '', @@ -211,6 +251,23 @@ $save = function () { $brand->gallery = CmsBrand::normalizeGallery($this->gallery); + $brand->setTranslation('widget_label', $this->editLocale, trim((string) $this->widgetLabel)); + $brand->setTranslation('cta_title', $this->editLocale, trim((string) $this->ctaTitle)); + $brand->setTranslation('cta_text', $this->editLocale, trim((string) $this->ctaText)); + + // normalizeSections() nimmt die Textblöcke entgegen und trennt sie an den Leerzeilen. + $brand->sections = CmsBrand::normalizeSections(collect($this->sections)->map(fn (array $s) => [ + 'title' => $s['title'] ?? '', + 'paragraphs' => $s['paragraphs'] ?? '', + 'claim' => $s['claim'] ?? '', + 'quote' => $s['quote'] ?? '', + 'quote_author' => $s['quote_author'] ?? '', + 'detail' => [ + 'title' => $s['detail_title'] ?? '', + 'paragraphs' => $s['detail_paragraphs'] ?? '', + ], + ])->all()); + $brand->is_published = $validated['is_published']; $brand->order = $validated['order']; $brand->save(); @@ -390,6 +447,80 @@ $cancelForm = function () { + {{-- Abschnitte der Markenseite --}} +
+
+ Abschnitte der Markenseite + Abschnitt hinzufügen +
+ +
+ Die Abschnitte erscheinen auf der Detailseite unter der Einleitung, in dieser Reihenfolge. + Eine Leerzeile trennt zwei Absätze. Die Vertiefung wird auf + der Seite als aufklappbarer Bereich dargestellt – leer lassen, wenn es keine gibt. +
+ + @if (empty($sections)) +

Noch keine Abschnitte.

+ @else +
+ @foreach ($sections as $i => $section) +
+
+ Abschnitt {{ $i + 1 }} +
+ + + +
+
+ + + + + + + +
+
+ +
+ +
+ +
+

Vertiefung – erscheint aufklappbar

+ + +
+
+ @endforeach +
+ @endif +
+ + {{-- Abschlussblock und Beschriftung des Terminanfrage-Widgets --}} +
+ Abschluss und Terminanfrage + + +

+ Steht auf dem schwebenden Button, solange man auf dieser Markenseite ist. Leer lassen für die + allgemeine Beschriftung. +

+ + + +
+ {{-- Galerie --}}
diff --git a/resources/views/livewire/web/components/sections/brand-detail.blade.php b/resources/views/livewire/web/components/sections/brand-detail.blade.php index 71cdebc6..a3a0339f 100644 --- a/resources/views/livewire/web/components/sections/brand-detail.blade.php +++ b/resources/views/livewire/web/components/sections/brand-detail.blade.php @@ -58,6 +58,71 @@ @endif + {{-- 2b. Abschnitte der Markenseite. Die als Vertiefung gelieferten Blöcke sind aufklappbar, + damit die Seite schnell erfassbar bleibt, ohne dass Inhalt verloren geht. --}} + @if (!empty($brand['sections'])) +
+
+ @foreach ($brand['sections'] as $index => $section) +
+ @if (!empty($section['title'])) +

{{ $section['title'] }}

+ @endif + + @foreach ($section['paragraphs'] as $i => $paragraph) +

{{ $paragraph }}

+ @endforeach + + @if (!empty($section['quote'])) +
+
„{{ $section['quote'] }}"
+ @if (!empty($section['quote_author'])) +
{{ $section['quote_author'] }}
+ @endif +
+ @endif + + @if (!empty($section['detail'])) +
+ + + {{-- x-transition statt x-collapse: das Collapse-Plugin ist in diesem + Projekt nicht registriert, dieselbe Bewegung wie im FAQ-Akkordeon. --}} +
+
+ @foreach ($section['detail']['paragraphs'] as $paragraph) +

{{ $paragraph }}

+ @endforeach +
+
+
+ @endif + + @if (!empty($section['claim'])) +

{{ $section['claim'] }}

+ @endif +
+ @endforeach +
+
+ @endif + {{-- 3. Leistungen --}} @if (!empty($brand['services']))
@@ -215,8 +280,12 @@ {{-- 5. CTA --}}
-

{{ __('ui.brand_cta_title', ['name' => $brand['name']]) }}

-

{{ __('ui.brand_cta_text') }}

+

+ {{ $brand['cta_title'] ?: __('ui.brand_cta_title', ['name' => $brand['name']]) }} +

+

+ {{ $brand['cta_text'] ?: __('ui.brand_cta_text') }} +

- {{-- Der WhatsApp-Button kommt mit AP3, sobald die Nummer bestätigt ist. --}}
diff --git a/resources/views/livewire/web/components/ui/appointment-widget.blade.php b/resources/views/livewire/web/components/ui/appointment-widget.blade.php index ac2b35f7..9d5c965f 100644 --- a/resources/views/livewire/web/components/ui/appointment-widget.blade.php +++ b/resources/views/livewire/web/components/ui/appointment-widget.blade.php @@ -162,8 +162,9 @@