Frontend: Editorial-Relaunch der öffentlichen Strecke + Ausgaben-Routing

Öffentliche Seiten auf gemeinsames Editorial-Design (x-web.site-header/-footer,
Design-Tokens) und Ausgaben-Präfix /{edition}/ (de|en) umgestellt.

- Routing: neue Middleware SetEdition (Locale + URL::defaults), /{edition}-Gruppe
  in routes/web.php, Root-Redirect auf /de, 301 für Legacy-.html-URLs,
  Baseline-Default in AppServiceProvider.
- Neue URL-Schemata: /{edition}/press-release/{slug}, /{edition}/category/{slug}.
- Ausgabe = Sprache: DE/EN-Umschalter (Region/CH/AT entfernt); EditorialClock
  und Livewire-Komponenten sprachdynamisch.
- Detail-, Kategorie- und Veröffentlichen-Seite mit echten Daten neu aufgebaut.
- Suche aktiviert: Volt-Komponente livewire/web/search (Titel/Text/Keywords +
  Firma + Rubrik, Filter, Sortierung, Pagination, URL-Parameter q/category/sort).
- Rubriken-Navigation statt Übersichtsseite: Helper CategoryNavigation;
  web/kategorien.blade.php + Route entfernt (Legacy-301).
- Tests: Edition-Routing, Kategorie-Seite/-Navigation, Detail, Veröffentlichen,
  Suche, EditorialClock. Doku in "Echte öffentliche Unterseiten.md" aktualisiert.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Kevin Adametz 2026-06-16 16:39:28 +00:00
parent a0547208d3
commit 253141c6dc
64 changed files with 4457 additions and 2971 deletions

View file

@ -1,7 +1,4 @@
@props([
'activeRegion' => 'DE',
'language' => 'Deutsch',
'languageShort' => 'DE',
'navigation' => null,
'ticker' => [],
'marketIndex' => null,
@ -22,29 +19,27 @@
@endphp
@php
\Carbon\Carbon::setLocale('de');
$activeEdition = app()->getLocale();
\Carbon\Carbon::setLocale($activeEdition);
$today = now();
$regions = [
['code' => 'DE', 'flag' => '🇩🇪'],
['code' => 'AT', 'flag' => '🇦🇹'],
['code' => 'CH', 'flag' => '🇨🇭'],
['code' => 'EN', 'flag' => '🌐'],
// Ausgaben = Sprachen (DE/EN). Wechsel führt zur Startseite der jeweiligen Ausgabe.
$editions = [
['code' => 'de', 'label' => 'DE', 'flag' => '🇩🇪'],
['code' => 'en', 'label' => 'EN', 'flag' => '🌐'],
];
$navigation ??= [
['label' => 'Startseite', 'href' => route('home'), 'active' => true],
['label' => 'Wirtschaft', 'href' => route('kategorien')],
['label' => 'Technologie', 'href' => route('kategorien')],
['label' => 'Finanzen', 'href' => route('kategorien')],
['label' => 'Industrie', 'href' => route('kategorien')],
['label' => 'Energie', 'href' => route('kategorien')],
['label' => 'Gesundheit', 'href' => route('kategorien')],
['label' => 'Handel', 'href' => route('kategorien')],
['label' => 'Immobilien', 'href' => route('kategorien')],
['label' => 'Mobilität', 'href' => route('kategorien')],
['label' => 'Alle Rubriken', 'href' => route('kategorien')],
];
// Navigation = echte Rubriken der aktiven Ausgabe. Es gibt keine separate
// Übersichtsseite; jeder Eintrag führt direkt auf die Kategorieseite.
$navigation ??= collect([
['label' => 'Startseite', 'href' => route('web.home'), 'active' => request()->routeIs('web.home')],
])->concat(
\App\Support\CategoryNavigation::items()->map(fn (array $category): array => [
'label' => $category['label'],
'href' => $category['href'],
'active' => request()->routeIs('kategorie') && request()->route('slug') === $category['slug'],
])
)->all();
$activeLabel = collect($navigation)->firstWhere('active', true)['label'] ?? 'Menü';
@endphp
@ -62,16 +57,16 @@
Ausgabe
</span>
@foreach ($regions as $region)
<button type="button"
class="flex items-center gap-1 sm:gap-1.5 px-1.5 sm:px-2.5 py-2 text-[11px] sm:text-[11.5px] whitespace-nowrap -mb-px border-b-2 cursor-pointer transition-colors
@if ($region['code'] === $activeRegion) font-semibold text-ink-on-dark bg-white/[0.06] border-brand
@foreach ($editions as $edition)
<a href="{{ route('web.home', ['edition' => $edition['code']]) }}"
class="flex items-center gap-1 sm:gap-1.5 px-1.5 sm:px-2.5 py-2 text-[11px] sm:text-[11.5px] whitespace-nowrap -mb-px border-b-2 transition-colors
@if ($edition['code'] === $activeEdition) font-semibold text-ink-on-dark bg-white/[0.06] border-brand
@else font-medium text-ink-on-dark-2 border-transparent hover:text-ink-on-dark hover:bg-white/[0.04] @endif"
aria-label="Region {{ $region['code'] }}"
aria-pressed="{{ $region['code'] === $activeRegion ? 'true' : 'false' }}">
<span class="text-[11px] sm:text-[12px] leading-none">{{ $region['flag'] }}</span>
<span>{{ $region['code'] }}</span>
</button>
aria-label="Ausgabe {{ $edition['label'] }}"
@if ($edition['code'] === $activeEdition) aria-current="true" @endif>
<span class="text-[11px] sm:text-[12px] leading-none">{{ $edition['flag'] }}</span>
<span>{{ $edition['label'] }}</span>
</a>
@endforeach
<span class="flex-1"></span>
@ -84,8 +79,8 @@
<path d="M1 6h10M6 1c2 1.5 2 8.5 0 10M6 1c-2 1.5-2 8.5 0 10" stroke="currentColor" stroke-width="1" fill="none" />
</svg>
<strong class="text-ink-on-dark font-semibold">
<span class="hidden lg:inline">{{ $language }}</span>
<span class="lg:hidden">{{ $languageShort }}</span>
<span class="hidden lg:inline">{{ $activeEdition === 'en' ? 'English' : 'Deutsch' }}</span>
<span class="lg:hidden">{{ strtoupper($activeEdition) }}</span>
</strong>
</span>
<span class="hidden lg:inline-block w-px h-[14px] bg-ink-on-dark-rule"></span>
@ -101,7 +96,7 @@
x-data="{ searchOpen: false }"
@keydown.escape.window="searchOpen = false">
<div class="max-w-layout mx-auto px-4 sm:px-6 lg:px-8 py-3 lg:py-[18px] flex items-center gap-3 sm:gap-4 lg:gap-6">
<a href="{{ route('home') }}" class="block cursor-pointer group flex-shrink-0" aria-label="{{ $brandName }}{{ $brandAccent }} Startseite">
<a href="{{ route('web.home') }}" class="block cursor-pointer group flex-shrink-0" aria-label="{{ $brandName }}{{ $brandAccent }} Startseite">
<div class="text-[22px] sm:text-[24px] lg:text-[28px] font-semibold leading-none tracking-[-0.5px]">
<x-web.brand-mark :brand="$themeKey" />
</div>