@php
$seoData = isset($seo) && is_array($seo) ? $seo : [];
$pageTitle = $seoData['title'] ?? trim($__env->yieldContent('title', 'Coaster World'));
$pageDescription = $seoData['description'] ?? __('ui.brand_tagline');
$pageCanonical = $seoData['canonical'] ?? url()->current();
$pageImage = $seoData['image'] ?? url('/coaster-world-logo.png');
$pageType = $seoData['type'] ?? 'website';
$indexableRoute = request()->routeIs(
'home', 'explore', 'parks.show', 'coasters.show', 'attractions.show',
'experts.index', 'community.photos.index', 'community.photos.entity', 'community.photos.show',
'site.about', 'site.contact', 'site.legal', 'site.privacy', 'site.terms', 'site.cookies'
);
$pageRobots = $seoData['robots'] ?? ($indexableRoute
? 'index,follow,max-image-preview:large,max-snippet:-1,max-video-preview:-1'
: (request()->routeIs('search') ? 'noindex,follow' : 'noindex,nofollow'));
@endphp
@php
$cwCanonicalAppUrl = rtrim((string) config('app.url'), '/');
$cwCanonicalParts = parse_url($cwCanonicalAppUrl);
$cwCanonicalHttps = app()->environment('production')
&& is_array($cwCanonicalParts)
&& strtolower((string) ($cwCanonicalParts['scheme'] ?? '')) === 'https'
&& !empty($cwCanonicalParts['host']);
$cwCanonicalOrigin = $cwCanonicalHttps
? 'https://'.strtolower((string) $cwCanonicalParts['host'])
.(isset($cwCanonicalParts['port']) ? ':'.(int) $cwCanonicalParts['port'] : '')
: '';
@endphp
@if($cwCanonicalHttps)
@endif
{{ $pageTitle }}
@if(!empty($seoData['json_ld']))
@endif
@vite(['resources/css/app.css', 'resources/js/app.js'])
@stack('head')
@auth
@php
$headerNotificationStats = \App\Support\SafeCache::rememberArray(
'cw:header-notifications:v1931:'.auth()->id(),
now()->addSeconds(8),
function (): array {
$stats = auth()->user()->coasterWorldNotifications()
->whereNull('read_at')
->where(function ($query): void {
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
->selectRaw('COUNT(*) as unread_count, COALESCE(MAX(id), 0) as latest_id')
->toBase()
->first();
return [
'unread_count' => (int) ($stats->unread_count ?? 0),
'latest_id' => (int) ($stats->latest_id ?? 0),
];
}
);
$headerUnreadNotifications = (int) ($headerNotificationStats['unread_count'] ?? 0);
$headerLatestNotificationId = (int) ($headerNotificationStats['latest_id'] ?? 0);
$headerRecentNotifications = auth()->user()->coasterWorldNotifications()
->where(function ($query): void {
$query->whereNull('expires_at')->orWhere('expires_at', '>', now());
})
->latest()
->limit(5)
->get();
@endphp
@endauth
{{ __('ui.a11y_skip_content') }}
@php
$headerInitials = 'CW';
if (auth()->check()) {
$headerNameParts = preg_split('/\s+/u', trim((string) auth()->user()->name)) ?: [];
$headerInitials = collect($headerNameParts)
->filter()
->take(2)
->map(fn ($part) => mb_strtoupper(mb_substr((string) $part, 0, 1)))
->implode('');
$headerInitials = $headerInitials !== '' ? $headerInitials : 'CW';
}
$headerSearchLabels = [
'park' => __('ui.type_park'),
'coaster' => __('ui.type_coaster'),
'attraction' => __('ui.type_attraction'),
'user' => __('ui.type_user'),
'group' => __('ui.type_group'),
'manufacturer' => __('ui.type_manufacturer'),
'country' => __('ui.type_country'),
];
$headerAccessAllowed = function (string $access): bool {
return match ($access) {
'auth' => auth()->check(),
'admin' => auth()->check() && (bool) auth()->user()->is_admin,
default => true,
};
};
$headerCategories = collect(app(\App\Services\Navigation\HeaderNavigationService::class)->categories())
->filter(fn (array $category) => $headerAccessAllowed((string) ($category['access'] ?? 'all')))
->values();
$headerDesktopCategories = $headerCategories->filter(fn (array $category) => (bool) ($category['desktop_visible'] ?? true))->values();
$headerMobileCategories = $headerCategories->filter(fn (array $category) => (bool) ($category['mobile_visible'] ?? true))->values();
$headerLocales = [
'fr' => ['flag' => '/images/flags/fr.svg', 'label' => 'Français'],
'en' => ['flag' => '/images/flags/en.svg', 'label' => 'English'],
'de' => ['flag' => '/images/flags/de.svg', 'label' => 'Deutsch'],
'es' => ['flag' => '/images/flags/es.svg', 'label' => 'Español'],
'it' => ['flag' => '/images/flags/it.svg', 'label' => 'Italiano'],
];
$headerLabel = function (array $entry): string {
$key = (string) ($entry['label'] ?? '');
$translated = $key !== '' ? __($key) : '';
if ($translated === $key && !empty($entry['fallback_label'])) {
return (string) $entry['fallback_label'];
}
return $translated;
};
$headerItemAvailable = function (array $item, string $surface = 'desktop') use ($headerAccessAllowed): bool {
if (!$headerAccessAllowed((string) ($item['access'] ?? 'all'))) {
return false;
}
if ($surface === 'desktop' && !((bool) ($item['desktop_visible'] ?? true))) {
return false;
}
if ($surface === 'mobile' && !((bool) ($item['mobile_visible'] ?? true))) {
return false;
}
if (!empty($item['user_param'])) {
if (!auth()->check()) {
return false;
}
$attribute = (string) $item['user_param'];
return trim((string) auth()->user()->{$attribute}) !== '';
}
return true;
};
$headerItemUrl = function (array $item): string {
if (!empty($item['url'])) {
return (string) $item['url'];
}
$params = (array) ($item['params'] ?? []);
if (!empty($item['user_param']) && auth()->check()) {
$attribute = (string) $item['user_param'];
$params[] = auth()->user()->{$attribute};
}
$url = route((string) $item['route'], $params);
if (!empty($item['query']) && is_array($item['query'])) {
$url .= '?'.http_build_query($item['query']);
}
if (!empty($item['fragment'])) {
$url .= '#'.ltrim((string) $item['fragment'], '#');
}
return $url;
};
$headerRouteActive = function (array $patterns): bool {
return $patterns !== [] && request()->routeIs(...$patterns);
};
$headerItemActive = function (array $item) use ($headerRouteActive): bool {
$patterns = array_values((array) ($item['active'] ?? []));
if (!$headerRouteActive($patterns)) {
return false;
}
foreach ((array) ($item['active_query'] ?? []) as $key => $expected) {
$default = $key === 'kind' ? 'all' : null;
if ((string) request()->query($key, $default) !== (string) $expected) {
return false;
}
}
return true;
};
@endphp
@yield('content')
@guest
@include('partials.auth-modal')
@endguest
@stack('scripts')