Ö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>
114 lines
4.2 KiB
PHP
114 lines
4.2 KiB
PHP
<!DOCTYPE html>
|
||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||
|
||
@php
|
||
$siteName = $domainName ?? config('app.name', 'Laravel');
|
||
$pageTitle = trim($__env->yieldContent('title')) ?: $siteName;
|
||
$pageDescription = trim($__env->yieldContent('meta_description')) ?: $siteName;
|
||
// Self-Canonical als Default aus der Portal-URL + Request-Pfad (ohne Query):
|
||
// pro Portal/Domain getrennt und unabhängig von URL::forceRootUrl – verhindert Cross-Portal-Duplicate.
|
||
$portalBase = rtrim($domainUrl ?? config('app.url'), '/');
|
||
$canonicalUrl = trim($__env->yieldContent('canonical')) ?: ($portalBase.request()->getPathInfo());
|
||
$ogType = trim($__env->yieldContent('og_type')) ?: 'website';
|
||
$ogImage = trim($__env->yieldContent('og_image'));
|
||
@endphp
|
||
|
||
<meta name="description" content="{{ $pageDescription }}">
|
||
|
||
<title>{{ $pageTitle }}</title>
|
||
|
||
{{-- Kanonische URL --}}
|
||
<link rel="canonical" href="{{ $canonicalUrl }}">
|
||
|
||
{{-- Sprach-Alternates (de/en) – pro Seite optional gesetzt --}}
|
||
@hasSection('hreflang')
|
||
@yield('hreflang')
|
||
@endif
|
||
|
||
{{-- OpenGraph / Social --}}
|
||
<meta property="og:type" content="{{ $ogType }}">
|
||
<meta property="og:site_name" content="{{ $siteName }}">
|
||
<meta property="og:title" content="{{ $pageTitle }}">
|
||
<meta property="og:description" content="{{ $pageDescription }}">
|
||
<meta property="og:url" content="{{ $canonicalUrl }}">
|
||
@if ($ogImage)
|
||
<meta property="og:image" content="{{ $ogImage }}">
|
||
@endif
|
||
<meta name="twitter:card" content="{{ $ogImage ? 'summary_large_image' : 'summary' }}">
|
||
<meta name="twitter:title" content="{{ $pageTitle }}">
|
||
<meta name="twitter:description" content="{{ $pageDescription }}">
|
||
|
||
<!-- Domain-spezifisches Favicon -->
|
||
<link rel="icon" href="{{ asset(\App\Helpers\ThemeHelper::getFaviconPath()) }}">
|
||
|
||
<!-- Fonts -->
|
||
@include('partials.local-fonts')
|
||
|
||
@php
|
||
$font = \App\Helpers\ThemeHelper::getFont();
|
||
$theme = config('app.theme', 'businessportal24');
|
||
@endphp
|
||
|
||
@vite([\App\Helpers\ThemeHelper::getThemeCssPath(), 'resources/js/app.js'], $domainConfig['assets_dir'] ?? 'build/web')
|
||
|
||
<!-- Sticky Header Script -->
|
||
<script>
|
||
document.addEventListener('DOMContentLoaded', function() {
|
||
const topbar = document.getElementById('topbar');
|
||
const header = document.getElementById('header');
|
||
|
||
if (!topbar || !header) return;
|
||
|
||
let topbarHeight = topbar.offsetHeight;
|
||
let isHeaderSticky = false;
|
||
|
||
function updateHeaderPosition() {
|
||
const scrollTop = window.pageYOffset || document.documentElement.scrollTop;
|
||
|
||
if (scrollTop >= topbarHeight && !isHeaderSticky) {
|
||
// TopBar ist nicht mehr sichtbar - Header wird sticky
|
||
header.classList.remove('header-normal');
|
||
header.classList.add('header-sticky');
|
||
isHeaderSticky = true;
|
||
} else if (scrollTop < topbarHeight && isHeaderSticky) {
|
||
// TopBar ist wieder sichtbar - Header wird normal
|
||
header.classList.remove('header-sticky');
|
||
header.classList.add('header-normal');
|
||
isHeaderSticky = false;
|
||
}
|
||
}
|
||
|
||
// Initial check
|
||
updateHeaderPosition();
|
||
|
||
// Listen for scroll events
|
||
window.addEventListener('scroll', updateHeaderPosition);
|
||
|
||
// Listen for resize events (in case topbar height changes)
|
||
window.addEventListener('resize', function() {
|
||
topbarHeight = topbar.offsetHeight;
|
||
updateHeaderPosition();
|
||
});
|
||
});
|
||
</script>
|
||
|
||
<!-- Additional Styles -->
|
||
@stack('styles')
|
||
|
||
</head>
|
||
|
||
<body class="antialiased" style="background-color: hsl(var(--background)); color: hsl(var(--foreground));">
|
||
|
||
<!-- TopBar - statisch oben -->
|
||
|
||
@yield('content')
|
||
<!-- Additional Scripts -->
|
||
@stack('scripts')
|
||
</body>
|
||
|
||
</html>
|