12-05-2026 Frontend dev
This commit is contained in:
parent
405df0a122
commit
5b8bdf4182
779 changed files with 480564 additions and 6241 deletions
171
resources/views/livewire/admin/newsletter/sync.blade.php
Normal file
171
resources/views/livewire/admin/newsletter/sync.blade.php
Normal file
|
|
@ -0,0 +1,171 @@
|
|||
<?php
|
||||
|
||||
use App\Models\NewsletterSubscription;
|
||||
use App\Services\Admin\AdminPerformanceCache;
|
||||
use App\Services\Newsletter\NewsletterSyncService;
|
||||
use Livewire\Attributes\Layout;
|
||||
use Livewire\Attributes\Title;
|
||||
use Livewire\Volt\Component;
|
||||
|
||||
new #[Layout('components.layouts.app'), Title('Newsletter Sync')] class extends Component
|
||||
{
|
||||
public ?string $syncMessage = null;
|
||||
|
||||
public ?string $dryRunMessage = null;
|
||||
|
||||
public function triggerDryRun(): void
|
||||
{
|
||||
$subscription = NewsletterSubscription::query()->latest('id')->first();
|
||||
|
||||
if ($subscription === null) {
|
||||
$this->dryRunMessage = 'Dry-Run: Kein Datensatz fuer Vorschau vorhanden.';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$action = ($subscription->is_confirmed && $subscription->unsubscribed_at === null)
|
||||
? 'subscribe'
|
||||
: 'unsubscribe';
|
||||
|
||||
$this->dryRunMessage = "Dry-Run: Es wuerde {$action} fuer Subscription #{$subscription->id} ({$subscription->email}) ausgefuehrt.";
|
||||
}
|
||||
|
||||
public function triggerTestSync(): void
|
||||
{
|
||||
$subscription = NewsletterSubscription::query()->latest('id')->first();
|
||||
|
||||
if ($subscription === null) {
|
||||
$this->syncMessage = 'Kein Datensatz fuer Test-Sync vorhanden.';
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
app(NewsletterSyncService::class)->syncSubscription($subscription);
|
||||
|
||||
$action = ($subscription->is_confirmed && $subscription->unsubscribed_at === null)
|
||||
? 'subscribe'
|
||||
: 'unsubscribe';
|
||||
|
||||
$this->syncMessage = "Test-Sync ausgefuehrt ({$action}) fuer Subscription #{$subscription->id}.";
|
||||
}
|
||||
|
||||
public function with(): array
|
||||
{
|
||||
return [
|
||||
'stats' => $this->stats(),
|
||||
'syncConfig' => [
|
||||
'enabled' => (bool) config('newsletter.sync.enabled'),
|
||||
'provider' => (string) config('newsletter.sync.provider'),
|
||||
'endpoint' => (string) (config('newsletter.sync.endpoint') ?? '-'),
|
||||
'timeout' => (int) config('newsletter.sync.timeout', 10),
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array{total: int, confirmed: int, pending: int, unsubscribed: int}
|
||||
*/
|
||||
private function stats(): array
|
||||
{
|
||||
return app(AdminPerformanceCache::class)->remember(AdminPerformanceCache::NewsletterStats, AdminPerformanceCache::StatsTtl, function (): array {
|
||||
$stats = NewsletterSubscription::query()
|
||||
->toBase()
|
||||
->selectRaw('COUNT(*) as total')
|
||||
->selectRaw('SUM(CASE WHEN is_confirmed = ? THEN 1 ELSE 0 END) as confirmed', [true])
|
||||
->selectRaw('SUM(CASE WHEN is_confirmed = ? THEN 1 ELSE 0 END) as pending', [false])
|
||||
->selectRaw('SUM(CASE WHEN unsubscribed_at IS NOT NULL THEN 1 ELSE 0 END) as unsubscribed')
|
||||
->first();
|
||||
|
||||
return [
|
||||
'total' => (int) ($stats->total ?? 0),
|
||||
'confirmed' => (int) ($stats->confirmed ?? 0),
|
||||
'pending' => (int) ($stats->pending ?? 0),
|
||||
'unsubscribed' => (int) ($stats->unsubscribed ?? 0),
|
||||
];
|
||||
});
|
||||
}
|
||||
}; ?>
|
||||
|
||||
<div class="space-y-6">
|
||||
<flux:card>
|
||||
<div class="flex items-start justify-between gap-4">
|
||||
<div class="space-y-2">
|
||||
<flux:heading size="lg">{{ __('Newsletter Synchronisierung') }}</flux:heading>
|
||||
<flux:text class="text-zinc-500 dark:text-zinc-400">
|
||||
{{ __('Vorbereitung fuer die kuenftige externe API-Anbindung. Aktuell ist nur das technische Grundgeruest aktiv.') }}
|
||||
</flux:text>
|
||||
</div>
|
||||
|
||||
<div class="flex flex-col items-end gap-2">
|
||||
@if ($syncConfig['enabled'])
|
||||
<flux:badge color="green" icon="check" size="sm">{{ __('Sync aktiv') }}</flux:badge>
|
||||
@else
|
||||
<flux:badge color="zinc" icon="pause" size="sm">{{ __('Sync deaktiviert') }}</flux:badge>
|
||||
@endif
|
||||
|
||||
<div class="flex gap-2">
|
||||
<flux:button size="sm" variant="ghost" icon="eye" wire:click="triggerDryRun">
|
||||
{{ __('Dry Run') }}
|
||||
</flux:button>
|
||||
|
||||
<flux:button size="sm" icon="play" wire:click="triggerTestSync">
|
||||
{{ __('Test-Sync ausfuehren') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</flux:card>
|
||||
|
||||
@if ($dryRunMessage)
|
||||
<flux:card>
|
||||
<flux:text class="text-sm text-zinc-600 dark:text-zinc-300">{{ $dryRunMessage }}</flux:text>
|
||||
</flux:card>
|
||||
@endif
|
||||
|
||||
@if ($syncMessage)
|
||||
<flux:card>
|
||||
<flux:text class="text-sm">{{ $syncMessage }}</flux:text>
|
||||
</flux:card>
|
||||
@endif
|
||||
|
||||
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2 lg:grid-cols-4">
|
||||
<flux:card>
|
||||
<flux:text class="text-sm text-zinc-500 dark:text-zinc-400">{{ __('Gesamt') }}</flux:text>
|
||||
<flux:text size="xl" weight="bold">{{ $stats['total'] }}</flux:text>
|
||||
</flux:card>
|
||||
|
||||
<flux:card>
|
||||
<flux:text class="text-sm text-zinc-500 dark:text-zinc-400">{{ __('Bestaetigt') }}</flux:text>
|
||||
<flux:text size="xl" weight="bold">{{ $stats['confirmed'] }}</flux:text>
|
||||
</flux:card>
|
||||
|
||||
<flux:card>
|
||||
<flux:text class="text-sm text-zinc-500 dark:text-zinc-400">{{ __('Unbestaetigt') }}</flux:text>
|
||||
<flux:text size="xl" weight="bold">{{ $stats['pending'] }}</flux:text>
|
||||
</flux:card>
|
||||
|
||||
<flux:card>
|
||||
<flux:text class="text-sm text-zinc-500 dark:text-zinc-400">{{ __('Abgemeldet') }}</flux:text>
|
||||
<flux:text size="xl" weight="bold">{{ $stats['unsubscribed'] }}</flux:text>
|
||||
</flux:card>
|
||||
</div>
|
||||
|
||||
<flux:card>
|
||||
<flux:heading size="sm">{{ __('Konfiguration') }}</flux:heading>
|
||||
|
||||
<div class="mt-4 grid grid-cols-1 gap-3 sm:grid-cols-2">
|
||||
<div>
|
||||
<flux:text class="text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{{ __('Provider') }}</flux:text>
|
||||
<flux:text class="mt-1">{{ $syncConfig['provider'] }}</flux:text>
|
||||
</div>
|
||||
<div>
|
||||
<flux:text class="text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{{ __('Timeout') }}</flux:text>
|
||||
<flux:text class="mt-1">{{ $syncConfig['timeout'] }}s</flux:text>
|
||||
</div>
|
||||
<div class="sm:col-span-2">
|
||||
<flux:text class="text-xs uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{{ __('Endpoint') }}</flux:text>
|
||||
<flux:text class="mt-1 break-all">{{ $syncConfig['endpoint'] }}</flux:text>
|
||||
</div>
|
||||
</div>
|
||||
</flux:card>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue