144 lines
4.9 KiB
PHP
144 lines
4.9 KiB
PHP
<?php
|
|
|
|
use App\Models\Post;
|
|
use Livewire\Attributes\Layout;
|
|
use Livewire\Volt\Component;
|
|
use Livewire\WithPagination;
|
|
|
|
new #[Layout('components.layouts.public')] class extends Component
|
|
{
|
|
use WithPagination;
|
|
|
|
public string $search = '';
|
|
|
|
public function updatedSearch(): void
|
|
{
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function clearSearch(): void
|
|
{
|
|
$this->search = '';
|
|
$this->resetPage();
|
|
}
|
|
|
|
public function highlightSearch(string $text, string $search): string
|
|
{
|
|
if (empty($search)) {
|
|
return e($text);
|
|
}
|
|
|
|
$escapedText = e($text);
|
|
$escapedSearch = e($search);
|
|
|
|
return preg_replace(
|
|
'/('.preg_quote($escapedSearch, '/').')/iu',
|
|
'<mark class="bg-accent/30 rounded px-1">$1</mark>',
|
|
$escapedText
|
|
);
|
|
}
|
|
|
|
public function with(): array
|
|
{
|
|
return [
|
|
'posts' => Post::published()
|
|
->when($this->search, function ($query) {
|
|
$search = $this->search;
|
|
$query->where(function ($q) use ($search) {
|
|
$q->where('title', 'like', "%{$search}%")
|
|
->orWhere('body', 'like', "%{$search}%");
|
|
});
|
|
})
|
|
->latest()
|
|
->paginate(10),
|
|
];
|
|
}
|
|
}; ?>
|
|
|
|
<div class="max-w-4xl mx-auto px-0">
|
|
<flux:heading size="xl" class="text-text text-xl sm:text-2xl lg:text-3xl">{{ __('posts.posts') }}</flux:heading>
|
|
|
|
<!-- Search Bar -->
|
|
<div class="mt-4 sm:mt-6 relative">
|
|
<flux:input
|
|
wire:model.live.debounce.300ms="search"
|
|
placeholder="{{ __('posts.search_placeholder') }}"
|
|
class="w-full min-h-[44px]"
|
|
>
|
|
<x-slot:iconLeading>
|
|
<flux:icon name="magnifying-glass" class="w-5 h-5 text-text/50" />
|
|
</x-slot:iconLeading>
|
|
</flux:input>
|
|
|
|
@if($search)
|
|
<button
|
|
wire:click="clearSearch"
|
|
class="absolute end-3 top-1/2 -translate-y-1/2 text-text/50 hover:text-text min-h-[44px] min-w-[44px] flex items-center justify-center"
|
|
>
|
|
<flux:icon name="x-mark" class="w-5 h-5" />
|
|
</button>
|
|
@endif
|
|
</div>
|
|
|
|
<!-- Search Results Info -->
|
|
@if($search)
|
|
<p class="mt-4 text-sm text-text/70">
|
|
@if($posts->total() > 0)
|
|
{{ __('posts.search_results', ['count' => $posts->total(), 'query' => $search]) }}
|
|
@else
|
|
{{ __('posts.no_results', ['query' => $search]) }}
|
|
@endif
|
|
</p>
|
|
@endif
|
|
|
|
<!-- Posts List -->
|
|
<div class="mt-6 sm:mt-8 space-y-4 sm:space-y-6">
|
|
@forelse($posts as $post)
|
|
<article wire:key="post-{{ $post->id }}" class="bg-white p-4 sm:p-6 rounded-lg shadow-sm hover:shadow-md transition-shadow">
|
|
<h2 class="text-lg sm:text-xl font-semibold text-text">
|
|
<a href="{{ route('posts.show', $post) }}" class="hover:text-primary" wire:navigate>
|
|
@if($search)
|
|
{!! $this->highlightSearch($post->getTitle(), $search) !!}
|
|
@else
|
|
{{ $post->getTitle() }}
|
|
@endif
|
|
</a>
|
|
</h2>
|
|
|
|
<time class="text-xs sm:text-sm text-text/70 mt-2 block">
|
|
{{ $post->published_at?->translatedFormat('d F Y') ?? $post->created_at->translatedFormat('d F Y') }}
|
|
</time>
|
|
|
|
<p class="mt-2 sm:mt-3 text-text text-sm sm:text-base">
|
|
@if($search)
|
|
{!! $this->highlightSearch($post->getExcerpt(), $search) !!}
|
|
@else
|
|
{{ $post->getExcerpt() }}
|
|
@endif
|
|
</p>
|
|
|
|
<a href="{{ route('posts.show', $post) }}" class="text-primary hover:underline mt-3 sm:mt-4 inline-flex items-center min-h-[44px]" wire:navigate>
|
|
{{ __('posts.read_more') }} →
|
|
</a>
|
|
</article>
|
|
@empty
|
|
<div class="empty-state bg-white rounded-lg">
|
|
@if($search)
|
|
<flux:icon name="magnifying-glass" class="empty-state-icon text-text/30" />
|
|
<p class="text-text/70">{{ __('posts.no_results', ['query' => $search]) }}</p>
|
|
<flux:button wire:click="clearSearch" class="mt-4">
|
|
{{ __('posts.clear_search') }}
|
|
</flux:button>
|
|
@else
|
|
<flux:icon name="document-text" class="empty-state-icon text-text/40" />
|
|
<p class="text-text/70">{{ __('posts.no_posts') }}</p>
|
|
@endif
|
|
</div>
|
|
@endforelse
|
|
</div>
|
|
|
|
<div class="mt-8">
|
|
{{ $posts->links() }}
|
|
</div>
|
|
</div>
|