Teilen-Funktion: Öffentliche Share-Seite mit OG-Meta-Tags und Share-Button
- Öffentliche Route /e/{event} für Social-Media-Crawler (WhatsApp, Facebook)
- Share-View mit OG-Meta-Tags (Titel, Datum, Bild) für Link-Vorschau
- Teilen-Button auf Event-Detailseite (Web Share API + Clipboard-Fallback)
- Buttons: Teilen (helles Blau) + Bearbeiten (Standard-Blau)
- Hinweistext mit 3,5s Anzeige nach Link-Kopieren
- Event-Typ-Logos als neue Bilddateien
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
138
resources/views/events/share.blade.php
Normal file
138
resources/views/events/share.blade.php
Normal file
@@ -0,0 +1,138 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ app()->getLocale() }}" dir="{{ app()->getLocale() === 'ar' ? 'rtl' : 'ltr' }}">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>{{ $event->title }} - {{ $appName }}</title>
|
||||
|
||||
{{-- OG Meta Tags für Social-Media-Vorschau --}}
|
||||
@php
|
||||
$ogDescription = $event->start_at->translatedFormat(__('ui.date_format_long')) . ' ' . __('ui.clock');
|
||||
if ($event->location_name) {
|
||||
$ogDescription .= ' · ' . $event->location_name;
|
||||
}
|
||||
@endphp
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="{{ route('events.share', $event) }}">
|
||||
<meta property="og:title" content="{{ $event->title }}">
|
||||
<meta property="og:description" content="{{ $ogDescription }}">
|
||||
<meta property="og:image" content="{{ $event->imageUrl() }}">
|
||||
<meta property="og:site_name" content="{{ $appName }}">
|
||||
|
||||
{{-- Twitter Card --}}
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta name="twitter:title" content="{{ $event->title }}">
|
||||
<meta name="twitter:description" content="{{ $ogDescription }}">
|
||||
<meta name="twitter:image" content="{{ $event->imageUrl() }}">
|
||||
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
@include('components.tailwind-config')
|
||||
@php $favicon = \App\Models\Setting::get('app_favicon'); @endphp
|
||||
@if ($favicon)
|
||||
<link rel="icon" href="{{ asset('storage/' . $favicon) }}">
|
||||
@else
|
||||
<link rel="icon" href="{{ asset('favicon.ico') }}">
|
||||
@endif
|
||||
</head>
|
||||
<body class="min-h-screen bg-gray-100 flex flex-col">
|
||||
<main class="flex-1 flex items-center justify-center px-4 py-12">
|
||||
<div class="w-full max-w-md">
|
||||
{{-- App-Logo --}}
|
||||
<div class="text-center mb-6">
|
||||
@php $logoLogin = \App\Models\Setting::get('app_logo_login'); @endphp
|
||||
<img src="{{ $logoLogin ? asset('storage/' . $logoLogin) : asset('images/vereinos_logo.png') }}"
|
||||
alt="{{ $appName }}" class="mx-auto h-16 mb-2 object-contain">
|
||||
<p class="text-sm text-gray-500">{{ $appName }}</p>
|
||||
</div>
|
||||
|
||||
<div class="bg-white rounded-lg shadow-md overflow-hidden">
|
||||
{{-- Event-Bild --}}
|
||||
<div class="flex justify-center bg-gray-50 py-6">
|
||||
<img src="{{ $event->imageUrl() }}" alt="{{ $event->type->label() }}"
|
||||
class="h-24 w-24 object-contain">
|
||||
</div>
|
||||
|
||||
<div class="p-6">
|
||||
{{-- Abgesagt-Banner --}}
|
||||
@if ($event->status === \App\Enums\EventStatus::Cancelled)
|
||||
<div class="bg-red-600 text-white text-center py-2 px-3 rounded-md mb-4 text-sm font-semibold">
|
||||
{{ __('events.share_cancelled_notice') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
{{-- Event-Typ Badge + Team --}}
|
||||
<div class="mb-2">
|
||||
<x-event-type-badge :type="$event->type" />
|
||||
<span class="text-xs text-gray-500 ml-1">{{ $event->team->name }}</span>
|
||||
</div>
|
||||
|
||||
{{-- Titel --}}
|
||||
<h1 class="text-xl font-bold {{ $event->status === \App\Enums\EventStatus::Cancelled ? 'line-through text-gray-400' : 'text-gray-900' }}">
|
||||
{{ $event->title }}
|
||||
</h1>
|
||||
|
||||
{{-- Gegner und Ergebnis (bei Spielen) --}}
|
||||
@if ($event->type->isGameType() && $event->opponent)
|
||||
<p class="text-sm text-gray-600 mt-1">
|
||||
{{ __('events.vs') }} {{ $event->opponent }}
|
||||
@if ($event->hasScore())
|
||||
<span class="font-semibold ml-2">{{ $event->scoreDisplay() }}</span>
|
||||
@endif
|
||||
</p>
|
||||
@endif
|
||||
|
||||
{{-- Datum und Uhrzeit --}}
|
||||
<div class="mt-4 space-y-2 text-sm text-gray-700">
|
||||
<div class="flex items-center gap-2">
|
||||
<svg class="w-4 h-4 text-gray-400 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z"/>
|
||||
</svg>
|
||||
<span>
|
||||
{{ $event->start_at->translatedFormat(__('ui.date_format_long')) }} {{ __('ui.clock') }}
|
||||
@if ($event->end_at)
|
||||
– {{ $event->end_at->format('H:i') }} {{ __('ui.clock') }}
|
||||
@endif
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@if ($event->location_name || $event->address_text)
|
||||
<div class="flex items-start gap-2">
|
||||
<svg class="w-4 h-4 text-gray-400 mt-0.5 shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"/>
|
||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"/>
|
||||
</svg>
|
||||
<div>
|
||||
@if ($event->location_name)
|
||||
<span class="font-medium">{{ $event->location_name }}</span>
|
||||
@endif
|
||||
@if ($event->location_name && $event->address_text)
|
||||
<br>
|
||||
@endif
|
||||
@if ($event->address_text)
|
||||
<span class="text-gray-500">{{ $event->address_text }}</span>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
{{-- CTA-Button --}}
|
||||
<div class="mt-6">
|
||||
<a href="{{ route('events.show', $event) }}"
|
||||
class="block w-full text-center bg-blue-600 text-white py-3 rounded-md font-medium hover:bg-blue-700 transition">
|
||||
{{ __('events.share_view_in_app') }}
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{{-- Footer --}}
|
||||
<div class="text-center mt-6 text-sm text-gray-500">
|
||||
<a href="/impressum" class="hover:underline">{{ __('ui.footer_impressum') }}</a>
|
||||
<span class="mx-2">|</span>
|
||||
<a href="/datenschutz" class="hover:underline">{{ __('ui.footer_privacy') }}</a>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user