presseportale/resources/views/livewire/admin/newsletter/unsubscribes.blade.php
Kevin Adametz 2cde7284a1 livewire: Volt entfernt — Komponenten als Livewire-4-Single-File-Components, Route::livewire(), Livewire::test(); livewire/volt deinstalliert
- 83 Komponenten: Livewire\Volt\Component → Livewire\Component
- 73 Routen (admin, auth, customer): Volt::route() → Route::livewire()
- 88 Testdateien: Volt::test/actingAs → Livewire::test/actingAs
- VoltServiceProvider gelöscht; composer remove livewire/volt
- config/livewire.php auf v4-Schlüssel (component_layout, component_placeholder,
  component_locations, make_command: SFC ohne ⚡). Der alte Schlüssel layout war
  wirkungslos; alle gerouteten Komponenten setzen ihr Layout selbst.
- CLAUDE.md, AGENTS.md, boost.json, .cursorrules, Skill volt-development
- Roadmap: erledigt; Larastan erfasst SFCs weiterhin nicht (Blade-Dateien)

Suite 2.325 grün, einer übersprungen; Larastan und Pint ohne Meldung;
route:cache und view:cache laufen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-06 13:59:36 +00:00

124 lines
5.7 KiB
PHP

<?php
use App\Enums\EmailNotificationType;
use App\Models\EmailNotificationOptOut;
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Component;
use Livewire\WithPagination;
/*
* Newsletter-Abmeldungen: alle User, die den Newsletter über den Abmelde-Link
* (oder die Mail-Einstellungen) abbestellt haben — mit Zeitpunkt der Abmeldung.
*/
new #[Layout('components.layouts.app'), Title('Newsletter-Abmeldungen')] class extends Component
{
use WithPagination;
public string $search = '';
public function updated(string $property): void
{
if ($property === 'search') {
$this->resetPage();
}
}
public function with(): array
{
return [
'optOuts' => EmailNotificationOptOut::query()
->where('type', EmailNotificationType::Newsletter->value)
->whereHas('user', fn ($query) => $query
->when(trim($this->search) !== '', fn ($inner) => $inner
->where(fn ($match) => $match
->where('name', 'like', '%'.trim($this->search).'%')
->orWhere('email', 'like', '%'.trim($this->search).'%'))))
->with('user:id,name,email,portal,is_active')
->orderByDesc('created_at')
->paginate(50),
];
}
}; ?>
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Newsletter')"
:title="__('Newsletter-Abmeldungen')"
:description="__('User, die den Newsletter über den Abmelde-Link oder die Mail-Einstellungen abbestellt haben. Sie werden bei künftigen Kampagnen automatisch übersprungen.')"
>
<x-slot:actions>
<flux:button variant="filled" icon="arrow-left" href="{{ route('admin.newsletter.campaigns.index') }}" wire:navigate>
{{ __('Zurück') }}
</flux:button>
</x-slot:actions>
</x-portal.page-header>
{{-- ============== ABMELDUNGEN ============== --}}
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Abmeldungen') }}</span>
<div class="flex items-center gap-2">
<flux:input size="sm" icon="magnifying-glass" wire:model.live.debounce.400ms="search"
:placeholder="__('Name oder E-Mail suchen…')" clearable />
<span class="badge hub">{{ number_format($optOuts->total(), 0, ',', '.') }} {{ __('Einträge') }}</span>
</div>
</div>
<flux:table>
<flux:table.columns>
<flux:table.column>{{ __('User') }}</flux:table.column>
<flux:table.column>{{ __('E-Mail') }}</flux:table.column>
<flux:table.column>{{ __('Portal') }}</flux:table.column>
<flux:table.column>{{ __('Status') }}</flux:table.column>
<flux:table.column>{{ __('Abgemeldet am') }}</flux:table.column>
</flux:table.columns>
<flux:table.rows>
@forelse ($optOuts as $optOut)
<flux:table.row :key="$optOut->id">
<flux:table.cell class="whitespace-nowrap">
<a href="{{ route('admin.users.show', $optOut->user->id) }}" wire:navigate
class="text-[12.5px] font-semibold text-[color:var(--color-ink)] hover:underline">
{{ $optOut->user->name }}
</a>
</flux:table.cell>
<flux:table.cell class="whitespace-nowrap">
<span class="text-[12px] text-[color:var(--color-ink-2)]">{{ $optOut->user->email }}</span>
</flux:table.cell>
<flux:table.cell class="whitespace-nowrap">
<span class="text-[12px] text-[color:var(--color-ink-3)]">{{ $optOut->user->portal?->label() ?? '—' }}</span>
</flux:table.cell>
<flux:table.cell class="whitespace-nowrap">
@if ($optOut->user->is_active)
<flux:badge size="sm" color="green">{{ __('aktiv') }}</flux:badge>
@else
<flux:badge size="sm" color="zinc">{{ __('inaktiv') }}</flux:badge>
@endif
</flux:table.cell>
<flux:table.cell class="whitespace-nowrap">
<span class="text-[12px] text-[color:var(--color-ink-2)] tabular-nums">
{{ $optOut->created_at?->format('d.m.Y H:i') ?? '—' }}
</span>
</flux:table.cell>
</flux:table.row>
@empty
<flux:table.row>
<flux:table.cell colspan="5">
<div class="py-8 text-center text-[12px] text-[color:var(--color-ink-3)]">
{{ trim($search) !== ''
? __('Keine Abmeldungen für diese Suche.')
: __('Bislang hat sich niemand vom Newsletter abgemeldet.') }}
</div>
</flux:table.cell>
</flux:table.row>
@endforelse
</flux:table.rows>
</flux:table>
@if ($optOuts->hasPages())
<div class="px-5 py-4 border-t border-[color:var(--color-bg-rule)]">
{{ $optOuts->links() }}
</div>
@endif
</article>
</div>