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>
148 lines
7.6 KiB
PHP
148 lines
7.6 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use App\Models\CmsArticle;
|
|
use App\Models\CmsBrand;
|
|
use Database\Seeders\CmsBrandSeeder;
|
|
use Database\Seeders\ContentCorrections20261008Seeder as Corrections;
|
|
use FluxCms\Core\Models\CmsContent;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
|
|
uses(RefreshDatabase::class);
|
|
|
|
/**
|
|
* Korrekturen vom 08.10.2026 (dev/2026-08-28/Korrekturen-2026-10-08.md).
|
|
*/
|
|
function storedSection(string $group, string $key, array $de, array $en = []): CmsContent
|
|
{
|
|
return CmsContent::create(['group' => $group, 'key' => $key, 'type' => 'json', 'order' => 0,
|
|
'value' => array_filter(['de' => $de, 'en' => $en])]);
|
|
}
|
|
|
|
it('aligns the custom-made process in the stored texts and leaves own wording alone', function () {
|
|
$faq = storedSection('faq', 'faq', ['questions' => [
|
|
['question' => 'Beratung?', 'answer' => array_search(Corrections::FAQ_CONSULTATION_DE, Corrections::EXACT, true)],
|
|
['question' => 'Projekt?', 'answer' => array_search(Corrections::FAQ_PROJECT_DE, Corrections::EXACT, true)],
|
|
['question' => 'Eigene Frage', 'answer' => 'Eigene Antwort mit Aufmaß.'],
|
|
]]);
|
|
$origin = storedSection('home', 'origin_section', ['steps' => [
|
|
['description' => 'Im CABINET Store nehmen wir Maß, planen bis ins Detail und stimmen jede Front und jede Innenaufteilung mit Ihnen ab.'],
|
|
]]);
|
|
$detail = storedSection('magazin', 'detail', ['cta_title' => 'Entdecken Sie mehr über <span class="text-white font-medium">Immobilien-Investments</span> und das <span class="text-white font-medium">B2in-Ökosystem</span>.']);
|
|
|
|
$this->seed(Corrections::class);
|
|
$this->seed(Corrections::class);
|
|
|
|
$answers = array_column($faq->refresh()->getTranslation('value', 'de')['questions'], 'answer');
|
|
|
|
expect($answers)->toBe([Corrections::FAQ_CONSULTATION_DE, Corrections::FAQ_PROJECT_DE, 'Eigene Antwort mit Aufmaß.'])
|
|
->and(Corrections::FAQ_PROJECT_DE)->not->toContain('Eskal')->not->toContain('absolute')
|
|
->and($origin->refresh()->getTranslation('value', 'de')['steps'][0]['description'])->toContain('Nach Ihrer Zusage nehmen wir vor Ort exakt Maß.')
|
|
->and($detail->refresh()->getTranslation('value', 'de')['cta_title'])->toContain('Weitere Einblicke rund um Einrichtung, Planung und Projekte');
|
|
});
|
|
|
|
it('updates the CABINET process and the Otterpohl promise in the brands', function () {
|
|
$this->seed(CmsBrandSeeder::class);
|
|
$this->seed(\Database\Seeders\BrandPageTextSeeder::class);
|
|
$this->seed(\Database\Seeders\OtterpohlBrandSeeder::class);
|
|
$this->seed(Corrections::class);
|
|
|
|
$cabinet = json_encode(CmsBrand::where('slug', 'cabinet')->first()->only(['sections', 'services']), JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
|
|
$otterpohl = json_encode(CmsBrand::where('slug', 'tischlerei')->first()->sections, JSON_UNESCAPED_UNICODE);
|
|
|
|
expect($cabinet)->toContain('Angebotsannahme → Anzahlung → professionelles Aufmaß → Feinplanung / Fertigungsfreigabe')
|
|
->toContain('Am Anfang reicht das grobe Maß. Am Ende entscheidet der Millimeter.')
|
|
->toContain('Nach Angebotsannahme und Anzahlung erfolgt das professionelle Aufmaß bei Ihnen vor Ort.')
|
|
->not->toContain('Grundlage jeder Planung')
|
|
->and($otterpohl)->toContain('Das erhöht die Planungssicherheit deutlich.')
|
|
->not->toContain('absolute Planungssicherheit');
|
|
});
|
|
|
|
it('inserts the two Febrü slides at positions 4 and 9 without touching own slides', function () {
|
|
$defaults = __('b2in.themes.b2in.hero.slides', [], 'de');
|
|
$old = array_values(array_filter($defaults, fn ($s) => ! str_contains($s['image'], 'februe')));
|
|
$row = storedSection('home', 'hero', ['title' => 'Hero', 'slides' => $old], ['title' => 'Hero', 'slides' => [['image' => 'eigenes.webp']]]);
|
|
|
|
$this->seed(Corrections::class);
|
|
$this->seed(Corrections::class);
|
|
|
|
$value = $row->refresh()->getTranslations('value');
|
|
$images = array_column($value['de']['slides'], 'image');
|
|
|
|
expect($images)->toHaveCount(11)
|
|
->and($images[3])->toBe('b2in/slideshow/februe-active-2-0-buero.webp')
|
|
->and($images[8])->toBe('b2in/slideshow/februe-elements-soul-grossraum.webp')
|
|
->and(end($images))->toBe('b2in/slideshow/rotpunkt-bad-mk04.webp')
|
|
->and($value['de']['slides'][3]['label'])->toBe('Febrü')
|
|
->and(file_exists(public_path('img/assets/b2in/slideshow/februe-active-2-0-buero.webp')))->toBeTrue();
|
|
|
|
// Eigene Liste ohne die Nachbarbilder bleibt unangetastet.
|
|
expect($value['en']['slides'])->toBe([['image' => 'eigenes.webp']]);
|
|
});
|
|
|
|
it('replaces the former clubs once and keeps a list maintained since', function () {
|
|
$row = storedSection('home', 'sponsoring', ['logos' => [['name' => 'DSC Arminia Bielefeld'], ['name' => 'TC Hövelhof']]],
|
|
['logos' => [['name' => 'Own club']]]);
|
|
|
|
$this->seed(Corrections::class);
|
|
|
|
$value = $row->refresh()->getTranslations('value');
|
|
|
|
expect(array_column($value['de']['logos'], 'name'))->toBe(['DSC Arminia Bielefeld', 'TC Bad Salzuflen', 'TC Hiddenhausen', 'TC Blau Weiss Halle'])
|
|
->and($value['en']['logos'])->toBe([['name' => 'Own club']]);
|
|
});
|
|
|
|
it('rewrites the custom-made article, takes two articles offline and writes dates in German', function () {
|
|
$this->seed(\Database\Seeders\CmsArticleSeeder::class);
|
|
CmsArticle::query()->update(['date_label' => 'August 26, 2026', 'read_time' => '5 min read', 'is_published' => true]);
|
|
CmsArticle::where('slug', Corrections::REWRITTEN_ARTICLE)->first()
|
|
->setTranslation('title', 'de', 'Nach Maß: Warum der Millimeter über den Raum entscheidet')->save();
|
|
|
|
$this->seed(Corrections::class);
|
|
|
|
$article = CmsArticle::where('slug', Corrections::REWRITTEN_ARTICLE)->first();
|
|
|
|
expect($article->getTranslation('title', 'de'))->toContain('und am Anfang grobe Maße reichen')
|
|
->and($article->getTranslation('content', 'de')['sections'][0]['title'])->toBe('Erst die Idee, dann der Millimeter')
|
|
->and($article->date_label)->toBe('26. August 2026')
|
|
->and($article->read_time)->toBe('5 Min. Lesezeit')
|
|
->and(CmsArticle::whereIn('slug', Corrections::UNPUBLISHED_ARTICLES)->where('is_published', true)->count())->toBe(0);
|
|
});
|
|
|
|
it('converts dates and read times to German and leaves German ones alone', function (string $input, string $expected) {
|
|
expect(Corrections::germanDate($input))->toBe($expected);
|
|
})->with([
|
|
['August 26, 2026', '26. August 2026'],
|
|
['März 5, 2026', '5. März 2026'],
|
|
['26. August 2026', '26. August 2026'],
|
|
['Herbst 2026', 'Herbst 2026'],
|
|
]);
|
|
|
|
it('converts read times to German', function () {
|
|
expect(Corrections::germanReadTime('7 min read'))->toBe('7 Min. Lesezeit')
|
|
->and(Corrections::germanReadTime('7 Min. Lesezeit'))->toBe('7 Min. Lesezeit');
|
|
});
|
|
|
|
it('ships the corrected texts for fresh installations', function () {
|
|
$lang = file_get_contents(resource_path('lang/de/b2in.php'));
|
|
|
|
expect($lang)->toContain(Corrections::FAQ_CONSULTATION_DE)
|
|
->toContain(Corrections::FAQ_COST_DE)
|
|
->toContain(Corrections::FAQ_PROJECT_DE)
|
|
->not->toContain('seriöse Zahl')
|
|
->not->toContain('absolute Vertragssicherheit')
|
|
->not->toContain('B2in-Ökosystem')
|
|
->not->toContain('min read');
|
|
});
|
|
|
|
it('adds the club website to an already corrected list', function () {
|
|
$row = storedSection('home', 'sponsoring', ['logos' => [['name' => 'DSC Arminia Bielefeld'], ['name' => 'TC Hiddenhausen', 'url' => 'https://eigene.example']]]);
|
|
|
|
$this->seed(Corrections::class);
|
|
|
|
$logos = $row->refresh()->getTranslation('value', 'de')['logos'];
|
|
|
|
expect($logos[0]['url'])->toBe('https://www.arminia.de')
|
|
->and($logos[1]['url'])->toBe('https://eigene.example');
|
|
});
|