presseportale/resources/views/livewire/customer/press-releases/index.blade.php
Kevin Adametz 5b8bdf4182
Some checks are pending
linter / quality (push) Waiting to run
tests / ci (push) Waiting to run
12-05-2026 Frontend dev
2026-05-12 18:32:33 +02:00

201 lines
9.1 KiB
PHP
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.

<?php
use App\Enums\PressReleaseStatus;
use App\Models\PressRelease;
use App\Services\PressRelease\BlacklistViolationException;
use App\Services\PressRelease\PressReleaseService;
use App\Services\Customer\CustomerCompanyContext;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Attributes\Url;
use Livewire\Volt\Component;
use Livewire\WithPagination;
new #[Layout('components.layouts.app'), Title('Meine Pressemitteilungen')] class extends Component
{
use WithPagination;
public string $search = '';
public string $statusFilter = 'all';
#[Url(as: 'company', except: 'all')]
public string $companyFilter = 'all';
public string $sortBy = 'created_at';
public string $sortDir = 'desc';
public function sort(string $column): void
{
if ($this->sortBy === $column) {
$this->sortDir = $this->sortDir === 'asc' ? 'desc' : 'asc';
} else {
$this->sortBy = $column;
$this->sortDir = 'asc';
}
$this->resetPage();
}
public function updatedSearch(): void { $this->resetPage(); }
public function updatedStatusFilter(): void { $this->resetPage(); }
public function updatedCompanyFilter(): void { $this->resetPage(); }
public function submitForReview(int $id): void
{
$pr = $this->findMyPR($id);
if (! $pr) { return; }
try {
app(PressReleaseService::class)->submitForReview($pr);
session()->flash('success', __('Pressemitteilung zur Prüfung eingereicht.'));
} catch (BlacklistViolationException $e) {
session()->flash('error', __('Pressemitteilung wurde automatisch abgelehnt: unzulässiges Wort ":word".', ['word' => $e->word]));
} catch (\LogicException $e) {
session()->flash('error', $e->getMessage());
}
}
public function with(): array
{
$userId = auth()->id();
$context = app(CustomerCompanyContext::class);
$selectedCompanyId = $context->selectedCompanyId(auth()->user());
$prs = PressRelease::withoutGlobalScopes()
->where('user_id', $userId)
->with('company:id,name')
->when($selectedCompanyId !== null, fn ($q) => $q->where('company_id', $selectedCompanyId))
->when($selectedCompanyId === null && $this->companyFilter === 'assigned', fn ($q) => $q->whereNotNull('company_id'))
->when($selectedCompanyId === null && $this->companyFilter === 'unassigned', fn ($q) => $q->whereNull('company_id'))
->when(filled($this->search), function ($q): void {
$term = $this->search;
$q->where('title', 'like', '%'.$term.'%');
})
->when($this->statusFilter !== 'all', fn ($q) => $q->where('status', $this->statusFilter))
->orderBy(in_array($this->sortBy, ['title', 'status', 'created_at']) ? $this->sortBy : 'created_at', $this->sortDir)
->paginate(100);
return [
'pressReleases' => $prs,
'statusOptions' => PressReleaseStatus::cases(),
'selectedCompany' => $context->selectedCompany(auth()->user()),
'hasGlobalCompanyContext' => $selectedCompanyId === null,
];
}
private function findMyPR(int $id): ?PressRelease
{
return PressRelease::withoutGlobalScopes()
->where('id', $id)
->where('user_id', auth()->id())
->first();
}
}; ?>
<div class="space-y-6">
@if(session('success'))
<div class="rounded-md border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-800 dark:border-green-800 dark:bg-green-900/20 dark:text-green-300">
{{ session('success') }}
</div>
@endif
<flux:card>
<div class="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
<div>
<flux:heading size="lg">{{ __('Meine Pressemitteilungen') }}</flux:heading>
@if($selectedCompany)
<flux:subheading>{{ __('Gefiltert auf :company', ['company' => $selectedCompany->name]) }}</flux:subheading>
@endif
</div>
<flux:button variant="primary" icon="plus" href="{{ route('me.press-releases.create') }}" wire:navigate>
{{ __('Neue Pressemitteilung') }}
</flux:button>
</div>
</flux:card>
<flux:card>
<div class="flex flex-col gap-3 sm:flex-row">
<flux:input wire:model.live.debounce.300ms="search" placeholder="{{ __('Titel suchen…') }}" icon="magnifying-glass" class="flex-1" />
<flux:select wire:model.live="statusFilter" class="sm:w-44">
<option value="all">{{ __('Alle Status') }}</option>
@foreach($statusOptions as $s)
<option value="{{ $s->value }}">{{ $s->label() }}</option>
@endforeach
</flux:select>
@if($hasGlobalCompanyContext)
<flux:select wire:model.live="companyFilter" class="sm:w-48">
<option value="all">{{ __('Alle Firmenzuordnungen') }}</option>
<option value="assigned">{{ __('Mit Firma') }}</option>
<option value="unassigned">{{ __('Ohne Firma') }}</option>
</flux:select>
@endif
</div>
</flux:card>
<flux:card class="p-0">
<div class="p-4">
<flux:table>
<flux:table.columns>
<flux:table.column sortable :sorted="$sortBy==='title'" :direction="$sortDir" wire:click="sort('title')">{{ __('Titel') }}</flux:table.column>
<flux:table.column>{{ __('Firma') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy==='status'" :direction="$sortDir" wire:click="sort('status')">{{ __('Status') }}</flux:table.column>
<flux:table.column sortable :sorted="$sortBy==='created_at'" :direction="$sortDir" wire:click="sort('created_at')">{{ __('Erstellt') }}</flux:table.column>
<flux:table.column>{{ __('Aktionen') }}</flux:table.column>
</flux:table.columns>
@forelse($pressReleases as $pr)
<flux:table.row wire:key="{{ $pr->id }}">
<flux:table.cell>
<p class="max-w-xs truncate font-medium">{{ $pr->title }}</p>
</flux:table.cell>
<flux:table.cell>
<flux:text class="text-sm">{{ $pr->company?->name ?? '' }}</flux:text>
</flux:table.cell>
<flux:table.cell>
<flux:badge color="{{ match($pr->status->value) {
'published' => 'green',
'review' => 'yellow',
'rejected' => 'red',
'archived' => 'blue',
default => 'zinc',
} }}">{{ $pr->status->label() }}</flux:badge>
</flux:table.cell>
<flux:table.cell>
<flux:text class="text-sm text-zinc-500">{{ $pr->created_at->format('d.m.Y') }}</flux:text>
</flux:table.cell>
<flux:table.cell>
<div class="flex items-center gap-1">
<flux:button size="sm" variant="ghost" icon="eye" href="{{ route('me.press-releases.show', $pr->id) }}" wire:navigate />
@if(in_array($pr->status->value, ['draft', 'rejected']))
<flux:button size="sm" variant="ghost" icon="pencil" href="{{ route('me.press-releases.edit', $pr->id) }}" wire:navigate />
<flux:button size="sm" variant="ghost" icon="paper-airplane" wire:click="submitForReview({{ $pr->id }})"
wire:confirm="{{ __('Pressemitteilung zur Prüfung einreichen?') }}" />
@endif
</div>
</flux:table.cell>
</flux:table.row>
@empty
<flux:table.row>
<flux:table.cell colspan="5">
<div class="flex flex-col items-center justify-center px-4 py-10 text-center">
<flux:icon.newspaper class="size-10 text-zinc-300" />
<flux:text weight="semibold" class="mt-3">{{ __('Keine Pressemitteilungen gefunden') }}</flux:text>
<flux:text class="mt-1 max-w-md text-sm text-zinc-500">
{{ __('Passen Sie die Filter an oder erstellen Sie eine neue Pressemitteilung.') }}
</flux:text>
<flux:button class="mt-4" size="sm" variant="primary" icon="plus" href="{{ route('me.press-releases.create') }}" wire:navigate>
{{ __('Neue Pressemitteilung') }}
</flux:button>
</div>
</flux:table.cell>
</flux:table.row>
@endforelse
</flux:table>
</div>
</flux:card>
{{ $pressReleases->links() }}
</div>