Files
WebAPP/resources/views/events/show.blade.php
Rhino ad60e7a9f9 Spielerpositionen, Statistiken, Fahrgemeinschaften, Spielfeld-Visualisierung
- PlayerPosition Enum (7 Handball-Positionen) mit Label/ShortLabel
- Spielerstatistik pro Spiel (Tore, Würfe, TW-Paraden, Bemerkung)
- Position-Dropdown in Spieler-Editor und Event-Stats-Formular
- Statistik-Seite: TW zuerst, Trennlinie, Feldspieler, Position-Badges
- Spielfeld-SVG mit Ampel-Performance (grün/gelb/rot)
- Anklickbare Spieler im Spielfeld öffnen Detail-Modal
- Fahrgemeinschaften (Anbieten, Zuordnen, Zurückziehen)
- Übersetzungen in allen 6 Sprachen (de, en, pl, ru, ar, tr)
- .gitignore für Laravel hinzugefügt
- Demo-Daten mit Positionen und Statistiken

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-02 11:47:34 +01:00

620 lines
40 KiB
PHP
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<x-layouts.app :title="$event->title">
{{-- Cancelled Banner --}}
@if ($event->status === \App\Enums\EventStatus::Cancelled)
<div class="bg-red-600 text-white text-center py-3 px-4 rounded-lg mb-6 font-semibold">
{{ __('events.cancelled_banner') }}
</div>
@endif
@if ($event->status === \App\Enums\EventStatus::Draft)
<div class="bg-yellow-500 text-white text-center py-3 px-4 rounded-lg mb-6 font-semibold">
{{ __('events.draft_banner') }}
</div>
@endif
{{-- Header --}}
<div class="bg-white rounded-lg shadow p-6 mb-6">
<div class="flex items-start justify-between flex-wrap gap-2">
<div>
<div class="flex items-center gap-2 mb-2">
<x-event-type-badge :type="$event->type" />
<span class="text-xs text-gray-500">{{ $event->team->name }}</span>
</div>
<h1 class="text-2xl font-bold {{ $event->status === \App\Enums\EventStatus::Cancelled ? 'line-through text-gray-400' : 'text-gray-900' }}">
{{ $event->title }}
</h1>
@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
</div>
@if (auth()->user()->canAccessAdminPanel())
<a href="{{ route('admin.events.edit', $event) }}" class="text-sm text-blue-600 hover:underline">{{ __('ui.edit') }}</a>
@endif
</div>
<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" 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" 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><br>
@endif
@if ($event->address_text)
<span class="text-gray-500">{{ $event->address_text }}</span>
@endif
</div>
</div>
@endif
</div>
{{-- Navigation Button --}}
@if ($event->hasCoordinates())
<div class="mt-4">
<a href="https://www.openstreetmap.org/directions?engine=graphhopper_car&route=&to={{ $event->location_lat }}%2C{{ $event->location_lng }}"
class="hidden sm:inline-flex items-center gap-1 bg-blue-600 text-white text-sm px-4 py-2 rounded-md hover:bg-blue-700"
target="_blank" rel="noopener">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
{{ __('events.plan_route') }}
</a>
<a href="geo:{{ $event->location_lat }},{{ $event->location_lng }}"
class="sm:hidden inline-flex items-center gap-1 bg-blue-600 text-white text-sm px-4 py-2 rounded-md hover:bg-blue-700">
<svg class="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 20l-5.447-2.724A1 1 0 013 16.382V5.618a1 1 0 011.447-.894L9 7m0 13l6-3m-6 3V7m6 10l4.553 2.276A1 1 0 0021 18.382V7.618a1 1 0 00-.553-.894L15 4m0 13V4m0 0L9 7"/></svg>
{{ __('events.start_navigation') }}
</a>
</div>
@endif
</div>
{{-- Karte --}}
@if ($event->hasCoordinates())
<div class="bg-white rounded-lg shadow mb-6 overflow-hidden relative z-0">
<div id="map" class="h-64 w-full"></div>
</div>
@endif
{{-- Beschreibung --}}
@if ($event->description_html)
<div class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-3">{{ __('events.description') }}</h2>
<div class="prose prose-sm max-w-none text-gray-700">
{!! app(\App\Services\HtmlSanitizerService::class)->sanitize($event->description_html) !!}
</div>
</div>
@endif
{{-- Dateien --}}
@if ($event->files->isNotEmpty())
<div id="files" class="bg-white rounded-lg shadow p-6 mb-6 overflow-hidden">
<h2 class="text-lg font-semibold mb-3">{{ __('events.files') }}</h2>
<div class="grid gap-2" x-data>
@foreach ($event->files->sortBy('category.name') as $file)
<div @click="$dispatch('open-file-preview', @js($file->previewData()))"
class="flex items-center gap-3 border border-gray-100 rounded-md px-3 sm:px-4 py-3 hover:bg-gray-50 cursor-pointer transition-colors min-w-0">
@if ($file->isImage())
<img src="{{ route('files.preview', $file) }}" alt="" class="flex-shrink-0 w-9 h-9 rounded-lg object-cover bg-gray-100" loading="lazy">
@elseif ($file->isPdf())
<div class="flex-shrink-0 w-9 h-9 rounded-lg flex items-center justify-center bg-red-100 text-red-600 font-bold text-xs">
PDF
</div>
@else
<div class="flex-shrink-0 w-9 h-9 rounded-lg flex items-center justify-center
{{ match($file->iconType()) {
'word' => 'bg-blue-100 text-blue-600',
'excel' => 'bg-green-100 text-green-600',
default => 'bg-gray-100 text-gray-600',
} }}">
<svg class="w-4 h-4" fill="currentColor" viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 00-2 2v16a2 2 0 002 2h12a2 2 0 002-2V8l-6-6zm-1 2l5 5h-5V4z"/></svg>
</div>
@endif
<div class="min-w-0 flex-1">
<p class="text-sm font-medium text-gray-900 truncate">{{ $file->original_name }}</p>
<div class="flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-gray-500 mt-0.5">
<span class="inline-flex items-center px-1.5 py-0.5 rounded bg-gray-100 text-gray-600 font-medium">{{ $file->category->name }}</span>
<span>{{ $file->humanSize() }}</span>
</div>
</div>
<svg class="w-4 h-4 text-gray-400 flex-shrink-0" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5l7 7-7 7"/></svg>
</div>
@endforeach
</div>
</div>
<x-file-preview-modal />
@endif
{{-- Teilnehmer --}}
<div id="participants" class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-3">{{ __('events.participants') }}</h2>
@php
$yesCount = $event->participants->where('status', \App\Enums\ParticipantStatus::Yes)->count();
$noCount = $event->participants->where('status', \App\Enums\ParticipantStatus::No)->count();
$openCount = $event->participants->where('status', \App\Enums\ParticipantStatus::Unknown)->count();
@endphp
<div class="flex gap-4 text-sm mb-4">
<span class="text-green-700 font-medium">{{ $yesCount }} {{ __('events.confirmations') }}</span>
<span class="text-red-700 font-medium">{{ $noCount }} {{ __('events.rejections') }}</span>
<span class="text-gray-500">{{ $openCount }} {{ __('events.open_responses') }}</span>
</div>
<div class="divide-y divide-gray-100">
@if ($event->type === \App\Enums\EventType::Meeting)
{{-- Besprechung: User-basierte Teilnehmer --}}
@foreach ($event->participants->sortBy(fn($p) => $p->user->name ?? '') as $participant)
<div class="py-2 flex items-center justify-between gap-2 flex-wrap">
<div class="flex items-center gap-2">
<img src="{{ $participant->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="text-sm font-medium text-gray-900">{{ $participant->user->name ?? '' }}</span>
</div>
@if (!auth()->user()->isDsgvoRestricted())
@if ($participant->user_id === auth()->id() || auth()->user()->canAccessAdminPanel())
@if ($event->status !== \App\Enums\EventStatus::Cancelled)
<form method="POST" action="{{ route('participants.update', $event) }}" class="flex gap-1">
@csrf
<input type="hidden" name="user_id" value="{{ $participant->user_id }}">
<button type="submit" name="status" value="yes"
class="px-2 py-1 text-xs rounded-md {{ $participant->status === \App\Enums\ParticipantStatus::Yes ? 'bg-green-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-green-100' }}">
{{ __('ui.yes') }}
</button>
<button type="submit" name="status" value="no"
class="px-2 py-1 text-xs rounded-md {{ $participant->status === \App\Enums\ParticipantStatus::No ? 'bg-red-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-red-100' }}">
{{ __('ui.no') }}
</button>
<button type="submit" name="status" value="unknown"
class="px-2 py-1 text-xs rounded-md {{ $participant->status === \App\Enums\ParticipantStatus::Unknown ? 'bg-gray-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
{{ __('ui.open') }}
</button>
</form>
@endif
@endif
@endif
</div>
@endforeach
@else
{{-- Reguläre Events: Spieler-basierte Teilnehmer --}}
@foreach ($event->participants->sortBy('player.last_name') as $participant)
<div class="py-2 flex items-center justify-between gap-2 flex-wrap">
<div class="flex items-center gap-2">
<img src="{{ $participant->player->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="text-sm font-medium text-gray-900">{{ $participant->player->full_name }}</span>
</div>
@if (!auth()->user()->isDsgvoRestricted())
@if ($userChildIds->contains($participant->player_id) || auth()->user()->canAccessAdminPanel())
@if ($event->status !== \App\Enums\EventStatus::Cancelled)
<form method="POST" action="{{ route('participants.update', $event) }}" class="flex gap-1">
@csrf
<input type="hidden" name="player_id" value="{{ $participant->player_id }}">
<button type="submit" name="status" value="yes"
class="px-2 py-1 text-xs rounded-md {{ $participant->status === \App\Enums\ParticipantStatus::Yes ? 'bg-green-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-green-100' }}">
{{ __('ui.yes') }}
</button>
<button type="submit" name="status" value="no"
class="px-2 py-1 text-xs rounded-md {{ $participant->status === \App\Enums\ParticipantStatus::No ? 'bg-red-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-red-100' }}">
{{ __('ui.no') }}
</button>
<button type="submit" name="status" value="unknown"
class="px-2 py-1 text-xs rounded-md {{ $participant->status === \App\Enums\ParticipantStatus::Unknown ? 'bg-gray-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-gray-200' }}">
{{ __('ui.open') }}
</button>
</form>
@endif
@endif
@endif
</div>
@endforeach
@endif
</div>
</div>
{{-- Catering --}}
@if ($event->type->hasCatering())
<div id="catering" class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-3">{{ __('events.catering') }}</h2>
@if ($event->status !== \App\Enums\EventStatus::Cancelled && !auth()->user()->isDsgvoRestricted())
<form method="POST" action="{{ route('catering.update', $event) }}" class="mb-4">
@csrf
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex gap-1">
<button type="submit" name="status" value="yes"
class="px-3 py-1.5 text-sm rounded-md {{ $myCatering && $myCatering->status === \App\Enums\CateringStatus::Yes ? 'bg-green-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-green-100' }}">
{{ __('events.bring_something') }}
</button>
<button type="submit" name="status" value="no"
class="px-3 py-1.5 text-sm rounded-md {{ $myCatering && $myCatering->status === \App\Enums\CateringStatus::No ? 'bg-red-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-red-100' }}">
{{ __('events.bring_nothing') }}
</button>
</div>
<input type="text" name="note" placeholder="{{ __('events.catering_note_placeholder') }}" value="{{ $myCatering?->note }}"
class="flex-1 px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
</form>
@elseif (auth()->user()->isDsgvoRestricted())
<p class="text-sm text-orange-600 mb-4">{{ __('ui.dsgvo_restricted_hint') }}</p>
@endif
@php
$cateringsWithContribution = $event->caterings->where('status', \App\Enums\CateringStatus::Yes);
$cateringsWithdrawn = (auth()->user()->canAccessAdminPanel() && \App\Models\Setting::isFeatureVisibleFor('catering_history', auth()->user()))
? $event->caterings->where('status', \App\Enums\CateringStatus::No)
: collect();
@endphp
@if ($cateringsWithContribution->isNotEmpty() || $cateringsWithdrawn->isNotEmpty())
<div class="divide-y divide-gray-100">
@foreach ($cateringsWithContribution as $catering)
<div class="py-2 text-sm flex items-center gap-2">
<img src="{{ $catering->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="font-medium text-gray-900">{{ $catering->user->name }}</span>
@if ($catering->note)
<span class="text-gray-600"> {{ $catering->note }}</span>
@endif
</div>
@endforeach
@foreach ($cateringsWithdrawn as $catering)
@php
$userHistory = $cateringHistory->filter(
fn ($log) => ($log->properties['new']['user_id'] ?? null) == $catering->user_id
);
@endphp
@if ($userHistory->isNotEmpty())
{{-- Chronologische Verlaufseinträge --}}
@foreach ($userHistory as $log)
@php $newStatus = $log->properties['new']['status'] ?? 'unknown'; @endphp
<div class="py-2 text-sm flex items-center justify-between gap-2 opacity-40">
<div class="flex items-center gap-2">
<img src="{{ $catering->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="font-medium text-gray-400 {{ $newStatus === 'no' ? 'line-through' : '' }}">{{ $catering->user->name }}</span>
<span class="text-xs text-gray-400">({{ $newStatus === 'yes' ? __('events.signed_up') : __('events.withdrawn') }})</span>
</div>
@if (auth()->user()->isStaff())
<span class="text-xs text-gray-400 whitespace-nowrap">{{ $log->created_at->format('d.m.Y, H:i') }}</span>
@endif
</div>
@endforeach
@else
{{-- Fallback ohne Verlaufsdaten --}}
<div class="py-2 text-sm flex items-center justify-between gap-2 opacity-40">
<div class="flex items-center gap-2">
<img src="{{ $catering->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="font-medium text-gray-400 line-through">{{ $catering->user->name }}</span>
<span class="text-xs text-gray-400">({{ __('events.withdrawn') }})</span>
</div>
@if (auth()->user()->isStaff())
<span class="text-xs text-gray-400 whitespace-nowrap">{{ $catering->updated_at->format('d.m.Y, H:i') }}</span>
@endif
</div>
@endif
@endforeach
</div>
@else
<p class="text-sm text-gray-500">{{ __('events.no_catering_yet') }}</p>
@endif
</div>
@endif
{{-- Zeitnehmer --}}
@if ($event->type->hasTimekeepers())
<div id="timekeeper" class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-3">{{ __('events.timekeeper') }}</h2>
@if ($event->status !== \App\Enums\EventStatus::Cancelled && !auth()->user()->isDsgvoRestricted())
<form method="POST" action="{{ route('timekeeper.update', $event) }}" class="mb-4">
@csrf
<div class="flex gap-1">
<button type="submit" name="status" value="yes"
class="px-3 py-1.5 text-sm rounded-md {{ $myTimekeeper && $myTimekeeper->status === \App\Enums\CateringStatus::Yes ? 'bg-green-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-green-100' }}">
{{ __('events.timekeeper_yes') }}
</button>
<button type="submit" name="status" value="no"
class="px-3 py-1.5 text-sm rounded-md {{ $myTimekeeper && $myTimekeeper->status === \App\Enums\CateringStatus::No ? 'bg-red-600 text-white' : 'bg-gray-100 text-gray-600 hover:bg-red-100' }}">
{{ __('events.timekeeper_no') }}
</button>
</div>
</form>
@elseif (auth()->user()->isDsgvoRestricted())
<p class="text-sm text-orange-600 mb-4">{{ __('ui.dsgvo_restricted_hint') }}</p>
@endif
@php
$timekeepersYes = $event->timekeepers->where('status', \App\Enums\CateringStatus::Yes);
$timekeepersWithdrawn = (auth()->user()->canAccessAdminPanel() && \App\Models\Setting::isFeatureVisibleFor('catering_history', auth()->user()))
? $event->timekeepers->where('status', \App\Enums\CateringStatus::No)
: collect();
@endphp
@if ($timekeepersYes->isNotEmpty() || $timekeepersWithdrawn->isNotEmpty())
<div class="divide-y divide-gray-100">
@foreach ($timekeepersYes as $tk)
<div class="py-2 text-sm flex items-center gap-2">
<img src="{{ $tk->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="font-medium text-gray-900">{{ $tk->user->name }}</span>
</div>
@endforeach
@foreach ($timekeepersWithdrawn as $tk)
@php
$tkHistory = $timekeeperHistory->filter(
fn ($log) => ($log->properties['new']['user_id'] ?? null) == $tk->user_id
);
@endphp
@if ($tkHistory->isNotEmpty())
@foreach ($tkHistory as $log)
@php $newStatus = $log->properties['new']['status'] ?? 'unknown'; @endphp
<div class="py-2 text-sm flex items-center justify-between gap-2 opacity-40">
<div class="flex items-center gap-2">
<img src="{{ $tk->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="font-medium text-gray-400 {{ $newStatus === 'no' ? 'line-through' : '' }}">{{ $tk->user->name }}</span>
<span class="text-xs text-gray-400">({{ $newStatus === 'yes' ? __('events.signed_up') : __('events.withdrawn') }})</span>
</div>
@if (auth()->user()->isStaff())
<span class="text-xs text-gray-400 whitespace-nowrap">{{ $log->created_at->format('d.m.Y, H:i') }}</span>
@endif
</div>
@endforeach
@else
<div class="py-2 text-sm flex items-center justify-between gap-2 opacity-40">
<div class="flex items-center gap-2">
<img src="{{ $tk->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0">
<span class="font-medium text-gray-400 line-through">{{ $tk->user->name }}</span>
<span class="text-xs text-gray-400">({{ __('events.withdrawn') }})</span>
</div>
@if (auth()->user()->isStaff())
<span class="text-xs text-gray-400 whitespace-nowrap">{{ $tk->updated_at->format('d.m.Y, H:i') }}</span>
@endif
</div>
@endif
@endforeach
</div>
@else
<p class="text-sm text-gray-500">{{ __('events.no_timekeeper_yet') }}</p>
@endif
</div>
@endif
{{-- Fahrgemeinschaften --}}
@if ($event->type->hasCarpool())
<div id="carpool" class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-3">{{ __('events.carpool') }}</h2>
@if ($event->status !== \App\Enums\EventStatus::Cancelled && !auth()->user()->isDsgvoRestricted())
{{-- Eigenes Angebot --}}
@if ($myCarpool)
<div class="mb-4 p-4 bg-blue-50 border border-blue-200 rounded-lg">
<div class="flex items-center gap-2 mb-3">
<span class="text-sm font-semibold text-blue-700">{{ __('events.carpool_my_offer') }}</span>
</div>
<form method="POST" action="{{ route('carpool.offer', $event) }}" class="flex flex-col sm:flex-row gap-3 mb-2">
@csrf
<div class="flex items-center gap-2">
<label for="carpool-seats" class="text-sm text-gray-700 whitespace-nowrap">{{ __('events.carpool_seats') }}:</label>
<input type="number" name="seats" id="carpool-seats" min="1" max="9" value="{{ $myCarpool->seats }}"
class="w-16 px-2 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<input type="text" name="note" placeholder="{{ __('events.carpool_note_placeholder') }}" value="{{ $myCarpool->note }}"
class="flex-1 px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<button type="submit" class="px-3 py-1.5 text-sm font-medium text-white bg-blue-600 rounded-md hover:bg-blue-700 whitespace-nowrap">
{{ __('events.carpool_update') }}
</button>
</form>
<form method="POST" action="{{ route('carpool.withdraw', $event) }}" class="inline"
onsubmit="return confirm(@js(__('events.carpool_withdraw_confirm')))">
@csrf
<button type="submit" class="text-sm text-red-600 hover:text-red-800 hover:underline">
{{ __('events.carpool_withdraw') }}
</button>
</form>
</div>
@else
<form method="POST" action="{{ route('carpool.offer', $event) }}" class="mb-4">
@csrf
<div class="flex flex-col sm:flex-row gap-3">
<div class="flex items-center gap-2">
<label for="carpool-seats-new" class="text-sm text-gray-700 whitespace-nowrap">{{ __('events.carpool_seats') }}:</label>
<input type="number" name="seats" id="carpool-seats-new" min="1" max="9" value="3"
class="w-16 px-2 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
</div>
<input type="text" name="note" placeholder="{{ __('events.carpool_note_placeholder') }}"
class="flex-1 px-3 py-1.5 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500">
<button type="submit" class="px-3 py-1.5 text-sm font-medium text-white bg-green-600 rounded-md hover:bg-green-700 whitespace-nowrap">
{{ __('events.carpool_offer') }}
</button>
</div>
</form>
@endif
@error('seats') <p class="text-red-600 text-xs mb-3">{{ $message }}</p> @enderror
@error('carpool') <p class="text-red-600 text-xs mb-3">{{ $message }}</p> @enderror
@elseif (auth()->user()->isDsgvoRestricted())
<p class="text-sm text-orange-600 mb-4">{{ __('ui.dsgvo_restricted_hint') }}</p>
@endif
{{-- Liste aller Fahrten --}}
@if ($event->carpools->isNotEmpty())
<div class="space-y-3">
@foreach ($event->carpools as $carpool)
@php
$isOwn = $carpool->user_id === auth()->id();
$passengerCount = $carpool->passengers->count();
$remaining = $carpool->seats - $passengerCount;
$fillPercent = $carpool->seats > 0 ? ($passengerCount / $carpool->seats) * 100 : 0;
$assignedChildIds = $carpool->passengers->pluck('player_id')->toArray();
$assignableChildren = $userChildIds->filter(fn ($id) => !in_array($id, $assignedChildIds));
@endphp
<div class="p-4 border rounded-lg {{ $isOwn ? 'border-blue-300 bg-blue-50/30' : 'border-gray-200' }}">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center gap-2">
<img src="{{ $carpool->driver->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-7 h-7 rounded-full object-cover flex-shrink-0">
<span class="text-sm font-semibold text-gray-900">{{ $carpool->driver->name }}</span>
@if ($isOwn)
<span class="text-xs bg-blue-100 text-blue-700 px-1.5 py-0.5 rounded">{{ __('events.carpool_my_offer') }}</span>
@endif
</div>
<div class="text-sm text-gray-600">
{{ __('events.carpool_seats_count', ['free' => $remaining, 'total' => $carpool->seats]) }}
</div>
</div>
{{-- Fortschrittsbalken --}}
<div class="w-full bg-gray-200 rounded-full h-1.5 mb-2">
<div class="h-1.5 rounded-full transition-all {{ $fillPercent >= 100 ? 'bg-red-500' : ($fillPercent >= 60 ? 'bg-yellow-500' : 'bg-green-500') }}"
style="width: {{ min($fillPercent, 100) }}%"></div>
</div>
@if ($carpool->note)
<p class="text-xs text-gray-500 mb-2">{{ $carpool->note }}</p>
@endif
{{-- Passagiere --}}
@if ($carpool->passengers->isNotEmpty())
<div class="flex flex-wrap gap-1.5 mb-2">
@foreach ($carpool->passengers as $passenger)
<span class="inline-flex items-center gap-1 bg-gray-100 text-gray-700 text-xs px-2 py-1 rounded-full">
{{ $passenger->player->full_name }}
@if ($event->status !== \App\Enums\EventStatus::Cancelled && ($passenger->added_by === auth()->id() || auth()->user()->isAdmin()))
<form method="POST" action="{{ route('carpool.leave', $event) }}" class="inline">
@csrf
<input type="hidden" name="carpool_id" value="{{ $carpool->id }}">
<input type="hidden" name="player_id" value="{{ $passenger->player_id }}">
<button type="submit" class="text-red-400 hover:text-red-600 ml-0.5" title="{{ __('events.carpool_leave') }}">
<svg class="w-3.5 h-3.5" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/></svg>
</button>
</form>
@endif
</span>
@endforeach
</div>
@endif
{{-- Kinder zuordnen --}}
@if ($event->status !== \App\Enums\EventStatus::Cancelled && !auth()->user()->isDsgvoRestricted() && $remaining > 0 && $assignableChildren->isNotEmpty())
<div class="flex flex-wrap gap-1.5">
@foreach ($assignableChildren as $childId)
@php $child = $userChildren->firstWhere('id', $childId); @endphp
@if ($child)
<form method="POST" action="{{ route('carpool.join', $event) }}" class="inline">
@csrf
<input type="hidden" name="carpool_id" value="{{ $carpool->id }}">
<input type="hidden" name="player_id" value="{{ $childId }}">
<button type="submit" class="inline-flex items-center gap-1 text-xs px-2 py-1 rounded-full border border-green-300 text-green-700 bg-green-50 hover:bg-green-100 transition">
<svg class="w-3 h-3" fill="none" viewBox="0 0 24 24" stroke="currentColor"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/></svg>
{{ $child->full_name }}
</button>
</form>
@endif
@endforeach
</div>
@elseif ($remaining <= 0 && !$isOwn)
<p class="text-xs text-red-500">{{ __('events.carpool_full') }}</p>
@endif
</div>
@endforeach
</div>
@else
<p class="text-sm text-gray-500">{{ __('events.no_carpool_yet') }}</p>
@endif
</div>
@endif
{{-- Kommentare --}}
<div id="comments" class="bg-white rounded-lg shadow p-6 mb-6">
<h2 class="text-lg font-semibold mb-3">{{ __('events.comments') }}</h2>
@if ($event->status !== \App\Enums\EventStatus::Cancelled && !auth()->user()->isDsgvoRestricted())
<form method="POST" action="{{ route('comments.store', $event) }}" class="mb-4">
@csrf
<div class="flex gap-2">
<input type="text" name="body" placeholder="{{ __('events.comment_placeholder') }}" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-md text-sm focus:ring-2 focus:ring-blue-500 focus:border-blue-500" maxlength="1000">
<button type="submit" class="bg-blue-600 text-white px-4 py-2 rounded-md text-sm hover:bg-blue-700">{{ __('ui.send') }}</button>
</div>
@error('body')
<p class="text-red-600 text-xs mt-1">{{ $message }}</p>
@enderror
</form>
@elseif (auth()->user()->isDsgvoRestricted())
<p class="text-sm text-orange-600 mb-4">{{ __('ui.dsgvo_restricted_hint') }}</p>
@endif
@if ($event->comments->isEmpty())
<p class="text-sm text-gray-500">{{ __('events.no_comments') }}</p>
@else
<div class="space-y-3">
@foreach ($event->comments->sortByDesc('created_at') as $comment)
@if ($comment->isDeleted() && !auth()->user()->isStaff())
@continue
@endif
<div class="flex justify-between items-start gap-2 {{ $comment->isDeleted() ? 'opacity-50' : '' }}">
<div class="flex gap-2">
<img src="{{ $comment->user->getAvatarUrl() ?? asset('images/profil_empty.png') }}" alt="" class="w-6 h-6 rounded-full object-cover flex-shrink-0 mt-0.5">
<div>
<div class="flex items-center gap-2">
<span class="text-sm font-medium text-gray-900">{{ $comment->user->name }}</span>
<span class="text-xs text-gray-400">{{ $comment->created_at->diffForHumans() }}</span>
</div>
@if ($comment->isDeleted())
<p class="text-sm text-gray-400 italic">{{ $comment->body }} <span class="text-xs">({{ __('events.deleted_label') }})</span></p>
@else
<p class="text-sm text-gray-700 mt-0.5">{{ $comment->body }}</p>
@endif
</div>
</div>
@if (!$comment->isDeleted() && auth()->user()->isStaff())
<form method="POST" action="{{ route('admin.comments.destroy', $comment) }}" onsubmit="return confirm(@js(__('events.confirm_delete_comment')))">
@csrf
@method('DELETE')
<button type="submit" class="text-xs text-red-500 hover:text-red-700">{{ __('ui.delete') }}</button>
</form>
@endif
</div>
@endforeach
</div>
@endif
</div>
<div class="mb-6">
<a href="{{ route('events.index') }}" class="text-sm text-blue-600 hover:underline">&larr; {{ __('events.back_to_list') }}</a>
</div>
{{-- Leaflet.js Karte --}}
@if ($event->hasCoordinates())
@push('styles')
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY=" crossorigin="" />
@endpush
@push('scripts')
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js" integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo=" crossorigin=""></script>
<script>
document.addEventListener('DOMContentLoaded', function() {
const map = L.map('map').setView([@js($event->location_lat), @js($event->location_lng)], 15);
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>'
}).addTo(map);
const popupText = document.createElement('span');
popupText.textContent = @json($event->location_name ?? $event->title);
L.marker([@js($event->location_lat), @js($event->location_lng)])
.addTo(map)
.bindPopup(popupText)
.openPopup();
});
</script>
@endpush
@endif
</x-layouts.app>