Initial commit

This commit is contained in:
Ronald Huynen
2026-03-23 21:37:59 +01:00
commit 2547717edb
2193 changed files with 972171 additions and 0 deletions

View File

@@ -0,0 +1,212 @@
<div class="reaction-button-wrapper">
<div class="reaction-button-container">
@if ($shouldShow)
@if ($canReact)
<!-- User can react - show interactive button -->
@if ($reactedByReactor)
<!-- Active state - using the defined pattern -->
<div class="reaction-container flex flex-col items-center">
<button
wire:click="unReact"
onclick="event.stopPropagation();"
class="reaction-button reaction-button--active group flex items-center space-x-1 p-2 rounded-lg transition-all duration-200 {{ $inverseColors ? 'text-white hover:text-gray-200 active:text-gray-300' : 'text-theme-reaction hover:text-theme-reaction-hover active:text-theme-primary' }}"
title="{{ __('Click to remove :reaction', ['reaction' => $typeName]) }}"
>
<div class="reaction-icon {{ $size ?? 'w-10 h-10' }} transform transition-transform">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
{!! $iconSvg !!}
</svg>
</div>
</button>
@if ($showCounter)
<div class="reaction-count-container flex items-center justify-center -mt-2">
<span class="text-xs bg-transparent px-2 py-0.5 {{ $count > 0 ? ($inverseColors ? 'text-white' : 'text-theme-primary') : 'text-transparent' }}">
{{ $count > 0 ? $count : '0' }}
</span>
</div>
@endif
</div>
@else
<!-- Inactive state - zero reactions -->
<div class="reaction-container flex flex-col items-center">
<button
wire:click="react"
onclick="event.stopPropagation();"
class="reaction-button reaction-button--inactive group flex items-center space-x-1 p-2 rounded-lg transition-all duration-200 {{ $inverseColors ? 'text-gray-300 hover:text-white active:text-white' : 'text-theme-primary hover:text-theme-reaction active:text-theme-reaction' }}"
title="{{ __('Click to :reaction', ['reaction' => $typeName]) }}"
>
<div class="reaction-icon {{ $size ?? 'w-10 h-10' }} transform transition-transform">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5">
{!! $iconSvg !!}
</svg>
</div>
</button>
@if ($showCounter)
<div class="reaction-count-container flex items-center justify-center -mt-2">
<span class="text-xs bg-transparent px-2 py-0.5 {{ $count > 0 ? ($inverseColors ? 'text-white' : 'text-theme-primary') : 'text-transparent' }}">
{{ $count > 0 ? $count : '0' }}
</span>
</div>
@endif
</div>
@endif
@elseif ($disabledReason === 'not_logged_in' && $redirectUrl)
<!-- Guest: clickable button that redirects to login solid icon when liked, hidden when no likes -->
@if ($count > 0)
<div class="reaction-container flex flex-col items-center">
<a href="{{ route('login') }}?redirect={{ urlencode($redirectUrl) }}" onclick="event.stopPropagation();">
<div class="reaction-button group flex items-center space-x-1 p-2 rounded-lg transition-all duration-200 {{ $inverseColors ? 'text-white hover:text-gray-200' : 'text-theme-primary hover:text-theme-reaction' }}">
<div class="reaction-icon {{ $size ?? 'w-10 h-10' }} transform transition-transform">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
{!! $iconSvg !!}
</svg>
</div>
</div>
</a>
@if ($showCounter)
<div class="reaction-count-container flex items-center justify-center -mt-2">
<span class="text-xs bg-transparent px-2 py-0.5 {{ $inverseColors ? 'text-white' : 'text-theme-primary' }}">
{{ $count }}
</span>
</div>
@endif
</div>
@endif
@else
<!-- User cannot react - show disabled button with tooltip (only for specific reasons) -->
@if ($disabledReasonText)
<!-- Disabled with tooltip - applying the same pattern -->
<div class="reaction-container flex flex-col items-center">
<div class="reaction-button-disabled relative">
<div
class="reaction-button reaction-button--disabled group flex items-center space-x-1 p-2 rounded-lg cursor-pointer hover:text-theme-muted active:cursor-not-allowed select-none {{ $count > 0 ? 'text-theme-primary' : 'text-theme-primary' }}"
onclick="event.stopPropagation(); this.nextElementSibling.classList.toggle('opacity-100'); this.nextElementSibling.classList.toggle('opacity-0'); setTimeout(() => { this.nextElementSibling.classList.add('opacity-0'); this.nextElementSibling.classList.remove('opacity-100'); }, 5000);"
role="button"
tabindex="0"
onkeydown="if(event.key === 'Enter' || event.key === ' ') { event.preventDefault(); this.click(); }"
>
<div class="reaction-icon {{ $size ?? 'w-10 h-10' }} transform group-hover:scale-100 transition-transform">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="{{ $count > 0 ? 'currentColor' : 'none' }}" stroke="currentColor" stroke-width="1">
{!! $iconSvg !!}
</svg>
</div>
</div>
<!-- Click-activated tooltip -->
<div class="absolute right-0 bottom-full mb-2 px-3 py-2 bg-theme-primary text-white text-base rounded-lg opacity-0 pointer-events-none transition-opacity duration-1000 whitespace-nowrap" style="z-index: 9999;">
{{ __($disabledReasonText) }}
</div>
</div>
@if ($showCounter)
<div class="reaction-count-container flex items-center justify-center -mt-2">
<span class="text-xs bg-transparent px-2 py-0.5 {{ $count > 0 ? ($inverseColors ? 'text-white' : 'text-theme-primary') : 'text-transparent' }}">
{{ $count > 0 ? $count : '0' }}
</span>
</div>
@endif
</div>
@else
<!-- Show count only for other disabled reasons - hide entirely when no reactions -->
@if ($showCounter && $count > 0)
<div class="reaction-container flex flex-col items-center">
<div class="reaction-display group flex items-center space-x-1 p-2">
<div class="reaction-icon {{ $size ?? 'w-10 h-10' }} {{ $inverseColors ? 'text-white' : 'text-theme-primary' }}">
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor">
{!! $iconSvg !!}
</svg>
</div>
</div>
<div class="reaction-count-container flex items-center justify-center -mt-2">
<span class="text-xs bg-transparent px-2 py-0.5 {{ $inverseColors ? 'text-white' : 'text-theme-primary' }}">
{{ $count }}
</span>
</div>
</div>
@endif
@endif
@endif
@endif
{{-- Component is completely hidden if shouldShow is false --}}
</div>
<!-- Reservation Confirmation Modal -->
@if($typeName === 'reserve' && $targetModel && is_object($targetModel))
<x-jetstream.dialog-modal wire:model.live="showConfirmModal" maxWidth="2xl">
<x-slot name="title">
{{ __('Confirm reservation') }}
</x-slot>
<x-slot name="content">
<div class="space-y-4">
<p class="text-theme-primary">{{ __('Are you sure you want to reserve a spot for this event?') }}</p>
@if(method_exists($targetModel, 'translations') && $targetModel->translations->first())
<div class="bg-theme-surface rounded-lg p-4 space-y-2">
<div>
<span class="font-semibold text-theme-primary">{{ __('Event') }}:</span>
<span class="text-theme-primary">{{ $targetModel->translations->first()->title }}</span>
</div>
@if(isset($targetModel->meeting))
@if(isset($targetModel->meeting->venue))
<div>
<span class="font-semibold text-theme-primary">{{ __('Venue') }}:</span>
<span class="text-theme-primary">{{ $targetModel->meeting->venue }}</span>
</div>
@endif
@if(isset($targetModel->meeting->address))
<div>
<span class="font-semibold text-theme-primary">{{ __('Address') }}:</span>
<span class="text-theme-primary">{{ $targetModel->meeting->address }}</span>
</div>
@endif
@if(isset($targetModel->meeting->from))
<div>
<span class="font-semibold text-theme-primary">{{ __('Date & Time') }}:</span>
<span class="text-theme-primary">{{ \Illuminate\Support\Carbon::parse($targetModel->meeting->from)->isoFormat('dddd D MMMM YYYY, H:mm') }}</span>
</div>
@endif
@endif
</div>
@endif
</div>
</x-slot>
<x-slot name="footer">
<x-jetstream.secondary-button no-spinner wire:click="$set('showConfirmModal', false)">
{{ __('Cancel') }}
</x-jetstream.secondary-button>
<x-jetstream.button wire:click="confirmReaction" class="ml-3 bg-theme-brand">
{{ __('Confirm reservation') }}
</x-jetstream.button>
</x-slot>
</x-jetstream.dialog-modal>
@endif
</div>
@push('scripts')
<script>
document.addEventListener('livewire:init', () => {
// Listen for reaction events
Livewire.on('reaction-added', (data) => {});
Livewire.on('reaction-removed', (data) => {});
Livewire.on('reaction-error', (data) => {
// Show error message to user
if (data && data.message) {
alert(data.message);
} else if (typeof data === 'string') {
alert(data);
}
});
});
</script>
@endpush