155 lines
9.0 KiB
PHP
155 lines
9.0 KiB
PHP
<div>
|
|
@if (count($calls) > 0)
|
|
<div x-data="{
|
|
navigate: true,
|
|
atStart: true,
|
|
atEnd: false,
|
|
init() {
|
|
this.$nextTick(() => this.updateState());
|
|
this.$refs.track.addEventListener('scroll', () => this.updateState());
|
|
},
|
|
updateState() {
|
|
const t = this.$refs.track;
|
|
this.atStart = t.scrollLeft <= 0;
|
|
this.atEnd = t.scrollLeft + t.offsetWidth >= t.scrollWidth - 1;
|
|
},
|
|
scrollLeft() {
|
|
this.navigate = false;
|
|
this.$refs.track.scrollBy({ left: -this.$refs.track.offsetWidth, behavior: 'smooth' });
|
|
setTimeout(() => { this.navigate = true }, 600);
|
|
},
|
|
scrollRight() {
|
|
this.navigate = false;
|
|
this.$refs.track.scrollBy({ left: this.$refs.track.offsetWidth, behavior: 'smooth' });
|
|
setTimeout(() => { this.navigate = true }, 600);
|
|
},
|
|
}" class="relative group">
|
|
|
|
{{-- Scrollable track --}}
|
|
<div x-ref="track"
|
|
class="overflow-x-auto"
|
|
style="scrollbar-width: none; -ms-overflow-style: none;">
|
|
<div class="flex gap-3" style="width: max-content;">
|
|
<div class="w-2 flex-shrink-0"></div>
|
|
@foreach ($calls as $index => $result)
|
|
<div @click="if (navigate) window.location='{{ route('call.show', ['id' => $result['id']]) }}'"
|
|
class="relative flex flex-col justify-end overflow-hidden rounded-lg shadow-lg flex-shrink-0 cursor-pointer"
|
|
style="width: calc((100vw - 3rem) / 3); min-width: 200px; max-width: 320px; height: 170px;">
|
|
|
|
{{-- Tag color background + overlay --}}
|
|
<div class="absolute inset-0 z-0 bg-{{ $result['tag_color'] ?? 'gray' }}-400"></div>
|
|
<div class="absolute inset-0 z-10 bg-black/50"></div>
|
|
|
|
{{-- Card content --}}
|
|
<div class="relative z-20 flex h-full flex-col justify-between p-4 text-white">
|
|
<div class="flex-1 min-w-0">
|
|
{{-- Deepest tag category badge --}}
|
|
@php $leafCat = !empty($result['tag_categories']) ? end($result['tag_categories']) : null; @endphp
|
|
@if ($leafCat)
|
|
<div class="mb-2">
|
|
<span class="bg-{{ $leafCat['color'] ?? 'gray' }}-400 inline-flex items-center rounded px-1.5 py-0.5 text-xs font-normal text-black">
|
|
{{ $leafCat['name'] }}
|
|
</span>
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Title --}}
|
|
<h3 class="text-lg font-semibold leading-tight truncate">
|
|
{{ $result['title'] }}
|
|
</h3>
|
|
|
|
{{-- Location + expiry badges --}}
|
|
@if (!empty($result['location']) || !empty($result['expiry_badge_text']))
|
|
<div class="mt-2 flex items-center gap-1 overflow-hidden">
|
|
@if (!empty($result['location']))
|
|
<span class="inline-block flex-shrink-0 rounded-sm bg-black px-1.5 pb-0.5 pt-0.5 text-xs font-normal uppercase text-white truncate max-w-[50%]">
|
|
{{ $result['location'] }}
|
|
</span>
|
|
@endif
|
|
@if (!empty($result['expiry_badge_text']))
|
|
<span class="inline-block flex-shrink-0 rounded-sm bg-black px-1.5 pb-0.5 pt-0.5 text-xs font-normal uppercase text-white truncate max-w-[50%]">
|
|
{{ $result['expiry_badge_text'] }}
|
|
</span>
|
|
@endif
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Callable avatar + name --}}
|
|
@if (!empty($result['callable_name']))
|
|
<div class="flex items-center gap-2 mt-3">
|
|
@if (!empty($result['photo']))
|
|
<div class="h-8 w-8 flex-shrink-0 rounded-full outline outline-1 outline-offset-1 outline-white/50 overflow-hidden">
|
|
<img src="{{ $result['photo'] }}"
|
|
alt="{{ $result['callable_name'] }}"
|
|
class="h-full w-full object-cover" />
|
|
</div>
|
|
@endif
|
|
<div class="flex flex-col min-w-0">
|
|
<span class="text-xs font-medium truncate">{{ $result['callable_name'] }}</span>
|
|
@if (!empty($result['callable_location']))
|
|
<span class="text-xs text-white/70 truncate">{{ $result['callable_location'] }}</span>
|
|
@endif
|
|
</div>
|
|
</div>
|
|
@endif
|
|
</div>
|
|
|
|
{{-- Prioritisation score --}}
|
|
@if ($showScore)
|
|
<div class="absolute bottom-3 right-3 z-30 text-xs text-white/70">
|
|
{{ round($result['score'], 2) }}
|
|
</div>
|
|
@endif
|
|
|
|
{{-- Reaction button --}}
|
|
@if ($showReactions ?? true)
|
|
<div class="absolute top-3 right-3 z-30">
|
|
@livewire('reaction-button', [
|
|
'typeName' => 'like',
|
|
'showCounter' => true,
|
|
'reactionCounter' => $result['like_count'],
|
|
'modelClass' => $result['model'],
|
|
'modelId' => $result['id'],
|
|
'size' => 'w-5 h-5',
|
|
'inverseColors' => true,
|
|
], key('like-carousel-' . $result['id'] . '-' . $index))
|
|
</div>
|
|
@endif
|
|
|
|
</div>
|
|
@endforeach
|
|
<div class="w-2 flex-shrink-0"></div>
|
|
</div>
|
|
</div>
|
|
|
|
{{-- Left button — hidden at start --}}
|
|
<button type="button"
|
|
@mousedown.prevent.stop
|
|
@click.prevent.stop="scrollLeft()"
|
|
x-show="!atStart"
|
|
x-cloak
|
|
class="absolute left-2 top-1/2 -translate-y-1/2 z-20 flex h-9 w-9 items-center justify-center rounded-full bg-black text-white transition-all opacity-0 group-hover:opacity-100 group-hover:outline group-hover:outline-2 group-hover:outline-white group-hover:shadow-lg hover:outline-1"
|
|
aria-label="{{ __('Scroll left') }}">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M15 19l-7-7 7-7"/>
|
|
</svg>
|
|
</button>
|
|
|
|
{{-- Right button — hidden at end --}}
|
|
<button type="button"
|
|
@mousedown.prevent.stop
|
|
@click.prevent.stop="scrollRight()"
|
|
x-show="!atEnd"
|
|
x-cloak
|
|
class="absolute right-2 top-1/2 -translate-y-1/2 z-20 flex h-9 w-9 items-center justify-center rounded-full bg-black text-white transition-all opacity-0 group-hover:opacity-100 group-hover:outline group-hover:outline-2 group-hover:outline-white group-hover:shadow-lg hover:outline-1"
|
|
aria-label="{{ __('Scroll right') }}">
|
|
<svg class="w-5 h-5" fill="none" stroke="currentColor" stroke-width="2" viewBox="0 0 24 24">
|
|
<path stroke-linecap="round" stroke-linejoin="round" d="M9 5l7 7-7 7"/>
|
|
</svg>
|
|
</button>
|
|
|
|
</div>
|
|
@endif
|
|
</div>
|