Interior-Feinschliff: Hero-Slideshow, Lektorat, Rechtstexte, Aufräumen und Korrekturen 08.10.

Startseite und Heroes
- Hero-Slideshow (11 Motive inkl. Febrü) mit Infokarte, Laufzeit-Linie, größeren Klickflächen,
  Goldrahmen und mobiler Karte unter dem Bild; Zeile über der Überschrift, neue Vertrauenspunkte
- Hero-Stil auf Marken, Über uns und Kontakt übertragen, Breadcrumb entfernt
- Gründer-Leiste, Vision-Box auf Ocker, Showroom-Display in Box mit „Größer ansehen“/Vollbild
- Akzentfarbe #a8741f, größere Serif-Überschriften in der Interior-Welt
- Marken: Bildpause mit Zitat; Vereine aktualisiert und verlinkt

Inhalte
- Lektorat aller angezeigten Texte, /about-Korrekturen, Rechtstexte (DDG, TDDDG, Hosting Hetzner)
- Ablauf Maßanfertigung überall einheitlich, Magazin-Beitrag neu, alte Beiträge offline,
  deutsches Datum/Lesezeit, FAQ Projektausstattung, Otterpohl
- Referenzen-Seite per CMS-Schalter ausblendbar

CMS und Technik
- Tote Sektionen, Felder, Seiten und Komponenten entfernt und archiviert (dev/2026-08-28/archiv-*)
- Entwurf /entwurf, Farb-Schalter und /welcome entfernt; Test-Routen nur noch lokal
- Seitenbaum-Beschreibungen geprüft, fehlende CMS-Felder ergänzt, Markenliste aus „Kernmarken“
- Admin-Bildvorschauen repariert, verwendete Bilder in die Mediathek übernommen
- Seeder für das Deployment siehe dev/2026-08-28/entwicklungsplan.md

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-10-08 14:00:15 +00:00
parent 4b07bbdcae
commit fcb10be120
406 changed files with 22549 additions and 3798 deletions

View file

@ -0,0 +1,125 @@
<?php
namespace Database\Seeders;
use App\Models\CmsArticle;
use App\Models\CmsBrand;
use FluxCms\Core\Models\CmsContent;
use FluxCms\Core\Models\CmsMedia;
use FluxCms\Core\Services\MediaConversionService;
use Illuminate\Database\Seeder;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Str;
/**
* Übernimmt die Bilder, die Seeder unter public/img/assets/ abgelegt haben (Marken, Magazin,
* Slideshow …), in die Mediathek (07.10.2026) – damit sie im Bildauswahl-Dialog erscheinen.
*
* Name in der Mediathek ist der bisherige Pfad (z. B. `marken/cabinet/ankleide-solitaer-eiche-tano.webp`).
* Die Editoren speichern beim Auswählen genau diesen Namen, also bleiben alle Verweise gültig; der
* Admin (media_url()) zeigt das Bild aus der Mediathek. Die Datei wird kopiert, das Original unter
* public/img/assets/ bleibt liegen – die Website liefert weiter von dort (WebP-Fassungen).
*
* Übernommen wird nur, was Marken, Magazin oder CMS-Einträge (samt Standardtexten) tatsächlich
* verwenden. Bereits vorhandene Einträge werden übersprungen – mehrfaches Ausführen ist unbedenklich.
*
* php artisan db:seed --class=AssetMediaImportSeeder
*/
class AssetMediaImportSeeder extends Seeder
{
public function run(): void
{
$disk = config('flux-cms.media.disk', 'public');
$originals = config('flux-cms.media.originals_path', 'cms/media/originals');
$service = app(MediaConversionService::class);
$imported = 0;
foreach ($this->referencedAssets() as $value => $collection) {
if (CmsMedia::query()->where('filename', $value)->exists()) {
continue;
}
$source = public_path('img/assets/'.$value);
$extension = strtolower(pathinfo($source, PATHINFO_EXTENSION));
$path = $originals.'/'.Str::random(40).'.'.$extension;
Storage::disk($disk)->put($path, file_get_contents($source));
$isSvg = $extension === 'svg';
$dimensions = $isSvg ? false : @getimagesize($source);
$media = CmsMedia::create([
'filename' => $value,
'disk' => $disk,
'path' => $path,
'type' => 'image',
'mime_type' => $isSvg ? 'image/svg+xml' : (mime_content_type($source) ?: 'image/'.$extension),
'file_size' => filesize($source),
'original_width' => $dimensions ? $dimensions[0] : null,
'original_height' => $dimensions ? $dimensions[1] : null,
'collection' => $collection,
'conversions' => [],
]);
if (! $isSvg) {
$service->generateThumbnail($media);
}
$imported++;
}
$this->command?->info($imported === 0
? 'Mediathek: alle verwendeten Bilder sind bereits übernommen.'
: "Mediathek: {$imported} Bilder aus public/img/assets übernommen.");
}
/**
* Bildpfade, die Marken, Magazin oder CMS verwenden und die unter public/img/assets/ liegen,
* mit der Sammlung, in die sie kommen (die erste Quelle zählt: Marken, Magazin, Website).
*
* @return array<string, string>
*/
public function referencedAssets(): array
{
$values = [];
$collect = function (mixed $value, string $collection) use (&$collect, &$values): void {
if (is_array($value)) {
array_walk($value, fn ($item) => $collect($item, $collection));
return;
}
if (! is_string($value) || ! preg_match('/\.(jpe?g|png|webp|svg|gif)$/i', $value)) {
return;
}
$value = ltrim(trim($value), '/');
if (str_contains($value, '://') || str_starts_with($value, 'storage/') || str_starts_with($value, 'immobile/')) {
return;
}
if (is_file(public_path('img/assets/'.$value))) {
$values[$value] ??= $collection;
}
};
foreach (CmsBrand::query()->get() as $brand) {
$collect([$brand->logo, $brand->image, $brand->gallery, $brand->sections], 'Marken');
}
foreach (CmsArticle::query()->get() as $article) {
$collect($article->toArray(), 'Magazin');
}
foreach (CmsContent::query()->get() as $row) {
$collect($row->getTranslations('value'), 'Website');
}
$collect(trans('b2in.themes.'.config('app.theme', 'b2in'), [], 'de'), 'Website');
ksort($values);
return $values;
}
}