Umbenennung presseportale → pressekonto in Domains, Themes und Dokumentation. Design-Tokens, Portal-Shell, Customer-Dashboard, Auth- und Admin-PM-Views. Artisan-Befehl migrate:legacy-media mit Tests und Hub-Flux-Entwicklungsdocs. Co-authored-by: Cursor <cursoragent@cursor.com>
217 lines
13 KiB
PHP
217 lines
13 KiB
PHP
<x-layouts.app title="Dashboard">
|
||
<div class="space-y-8">
|
||
|
||
{{-- ============== PAGE HEADER ============== --}}
|
||
<header class="grid items-end gap-8" style="grid-template-columns:1fr auto;">
|
||
<div class="min-w-0">
|
||
<div class="flex items-center gap-3 mb-3 flex-nowrap whitespace-nowrap">
|
||
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
|
||
<span class="eyebrow muted">{{ __('Operations · A · 01') }}</span>
|
||
</div>
|
||
<h1 class="text-[34px] font-bold tracking-[-0.7px] leading-[1.1] m-0 text-[color:var(--color-ink)]">
|
||
{{ __('Admin Dashboard') }}
|
||
</h1>
|
||
<p class="text-[13.5px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
|
||
{{ __('Willkommen zurück, ') }}<strong class="font-semibold text-[color:var(--color-ink)]">{{ auth()->user()->name }}</strong>.
|
||
{{ __('Operations-Übersicht über Pressemitteilungen, Firmen und Konten beider Portale.') }}
|
||
</p>
|
||
</div>
|
||
|
||
<div class="flex items-center gap-3 flex-shrink-0">
|
||
<span class="badge ok dot">{{ __('Alle Systeme operational') }}</span>
|
||
</div>
|
||
</header>
|
||
|
||
{{-- ============== KPI-Reihe (5 Stat-Cards) ============== --}}
|
||
<section class="grid gap-4 grid-cols-2 sm:grid-cols-3 lg:grid-cols-5">
|
||
<a href="{{ route('admin.press-releases.index') }}" wire:navigate class="block focus:outline-none">
|
||
<x-portal.stat-card variant="primary" :label="__('Pressemitteilungen')" :value="number_format($stats['press_releases']['total'])">
|
||
<x-slot:meta>{{ now()->format('Y') }}</x-slot:meta>
|
||
{{-- WICHTIG: Wortlaut „X pub · Y prüf · Z entwurf" exakt beibehalten,
|
||
weil DashboardTest darauf assertet (1 pub, 1 prüf, 1 entwurf). --}}
|
||
<x-slot:trend>
|
||
<span class="flex items-center gap-3">
|
||
<span class="text-[color:var(--color-ok)]">{{ $stats['press_releases']['published'] }} pub</span>
|
||
<span class="text-[color:var(--color-warn)]">{{ $stats['press_releases']['review'] }} prüf</span>
|
||
<span>{{ $stats['press_releases']['draft'] }} entwurf</span>
|
||
</span>
|
||
</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.press-releases.index', ['status' => 'review']) }}" wire:navigate class="block focus:outline-none">
|
||
<x-portal.stat-card variant="warn" :label="__('In Prüfung')" :value="$stats['press_releases']['review']">
|
||
<x-slot:meta>{{ __('queue') }}</x-slot:meta>
|
||
<x-slot:trend>{{ __('warten auf Review') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.companies.index') }}" wire:navigate class="block focus:outline-none">
|
||
<x-portal.stat-card variant="muted" :label="__('Firmen')" :value="number_format($stats['companies'])">
|
||
<x-slot:trend>{{ __('aktiv im CRM') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.contacts.index') }}" wire:navigate class="block focus:outline-none">
|
||
<x-portal.stat-card variant="muted" :label="__('Kontakte')" :value="number_format($stats['contacts'])">
|
||
<x-slot:trend>{{ __('Pressekontakte') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
</a>
|
||
|
||
<a href="{{ route('admin.users.index') }}" wire:navigate class="block focus:outline-none">
|
||
<x-portal.stat-card variant="muted" :label="__('Benutzer')" :value="number_format($stats['users'])">
|
||
<x-slot:trend>{{ __('Portal-Konten') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
</a>
|
||
</section>
|
||
|
||
{{-- ============== ZWEISPALTEN-GRID ============== --}}
|
||
<section class="grid gap-6 lg:grid-cols-[2fr_1fr]">
|
||
|
||
{{-- Letzte Pressemitteilungen --}}
|
||
<article class="panel">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Letzte Pressemitteilungen') }}</span>
|
||
<a href="{{ route('admin.press-releases.index') }}" wire:navigate
|
||
class="text-[12px] font-semibold text-[color:var(--color-hub)] hover:underline underline-offset-[3px] decoration-[color:var(--color-hub)]/30">
|
||
{{ __('Alle anzeigen') }} →
|
||
</a>
|
||
</div>
|
||
|
||
@forelse ($recentPRs as $pr)
|
||
<a href="{{ route('admin.press-releases.show', $pr->id) }}" wire:navigate
|
||
class="flex items-center justify-between gap-3 px-5 py-3 border-b border-[color:var(--color-bg-rule)] last:border-b-0 hover:bg-[color:var(--color-bg)] transition-colors">
|
||
<div class="min-w-0 flex-1">
|
||
<p class="truncate text-[13px] font-medium text-[color:var(--color-ink)] m-0">{{ $pr->title }}</p>
|
||
<p class="text-[11px] text-[color:var(--color-ink-3)] mt-0.5 m-0 truncate">
|
||
{{ $pr->company?->name ?? '–' }}
|
||
<span class="text-[color:var(--color-ink-4)] mx-1">·</span>
|
||
{{ $pr->user?->name ?? '–' }}
|
||
<span class="text-[color:var(--color-ink-4)] mx-1">·</span>
|
||
{{ $pr->created_at->format('d.m.Y') }}
|
||
</p>
|
||
</div>
|
||
<span @class([
|
||
'badge shrink-0',
|
||
'ok' => $pr->status->value === 'published',
|
||
'warn' => $pr->status->value === 'review',
|
||
'err' => $pr->status->value === 'rejected',
|
||
'hub' => in_array($pr->status->value, ['archived', 'draft'], true),
|
||
])>
|
||
{{ $pr->status->label() }}
|
||
</span>
|
||
</a>
|
||
@empty
|
||
<p class="px-5 py-8 text-center text-[12.5px] text-[color:var(--color-ink-3)]">
|
||
{{ __('Noch keine Pressemitteilungen.') }}
|
||
</p>
|
||
@endforelse
|
||
</article>
|
||
|
||
{{-- Warteschlange Prüfung --}}
|
||
<article class="panel">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Zur Prüfung') }}</span>
|
||
@if ($stats['press_releases']['review'] > 0)
|
||
<span class="badge warn dot">
|
||
{{ $stats['press_releases']['review'] }} {{ __('offen') }}
|
||
</span>
|
||
@else
|
||
<span class="badge ok dot">{{ __('leer') }}</span>
|
||
@endif
|
||
</div>
|
||
|
||
@forelse ($pendingReviews as $pr)
|
||
<a href="{{ route('admin.press-releases.show', $pr->id) }}" wire:navigate
|
||
class="block px-5 py-3 border-b border-[color:var(--color-bg-rule)] last:border-b-0 hover:bg-[color:var(--color-bg)] transition-colors">
|
||
<p class="truncate text-[13px] font-medium text-[color:var(--color-ink)] m-0">{{ $pr->title }}</p>
|
||
<p class="text-[11px] text-[color:var(--color-ink-3)] mt-0.5 m-0 truncate">
|
||
{{ $pr->company?->name ?? '–' }}
|
||
<span class="text-[color:var(--color-ink-4)] mx-1">·</span>
|
||
{{ $pr->portal->label() }}
|
||
<span class="text-[color:var(--color-ink-4)] mx-1">·</span>
|
||
{{ $pr->created_at->format('d.m.Y') }}
|
||
</p>
|
||
</a>
|
||
@empty
|
||
<p class="px-5 py-8 text-center text-[12.5px] text-[color:var(--color-ink-3)]">
|
||
{{ __('Keine PMs in der Prüfwarteschlange.') }}
|
||
</p>
|
||
@endforelse
|
||
|
||
@if ($stats['press_releases']['review'] > count($pendingReviews))
|
||
<div class="px-5 py-3 border-t border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)]">
|
||
<a href="{{ route('admin.press-releases.index', ['status' => 'review']) }}" wire:navigate
|
||
class="text-[12px] font-semibold text-[color:var(--color-hub)] hover:underline underline-offset-[3px] decoration-[color:var(--color-hub)]/30">
|
||
+ {{ $stats['press_releases']['review'] - count($pendingReviews) }} {{ __('weitere') }} →
|
||
</a>
|
||
</div>
|
||
@endif
|
||
</article>
|
||
</section>
|
||
|
||
{{-- ============== NEWSLETTER + QUICK ACTIONS ============== --}}
|
||
<section class="grid gap-6 lg:grid-cols-[1fr_2fr]">
|
||
|
||
{{-- Newsletter-Stat als panel-warm Block --}}
|
||
<article class="panel-warm relative overflow-hidden">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Newsletter') }}</span>
|
||
<span class="badge hub">{{ __('bestätigt') }}</span>
|
||
</div>
|
||
<div class="px-5 py-5">
|
||
<div class="stat-num text-[color:var(--color-ink)]">
|
||
{{ number_format($stats['newsletter']) }}
|
||
</div>
|
||
<p class="text-[12px] text-[color:var(--color-ink-3)] mt-2 m-0">
|
||
{{ __('Aktive Newsletter-Abonnenten über beide Portale.') }}
|
||
</p>
|
||
<a href="{{ route('admin.newsletter.sync') }}" wire:navigate
|
||
class="inline-flex items-center gap-1 mt-3 text-[12px] font-semibold text-[color:var(--color-hub)] hover:underline underline-offset-[3px] decoration-[color:var(--color-hub)]/30">
|
||
{{ __('Sync verwalten') }} →
|
||
</a>
|
||
</div>
|
||
</article>
|
||
|
||
{{-- Quick-Actions Panel --}}
|
||
<article class="panel">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Quick Actions') }}</span>
|
||
</div>
|
||
<div class="grid gap-3 p-5 grid-cols-2 md:grid-cols-4">
|
||
@foreach ([
|
||
['icon' => 'newspaper', 'label' => __('Pressemitteilungen'), 'route' => 'admin.press-releases.index'],
|
||
['icon' => 'building-office', 'label' => __('Firmen'), 'route' => 'admin.companies.index'],
|
||
['icon' => 'document-text', 'label' => __('Rechnungen'), 'route' => 'admin.invoices.index'],
|
||
['icon' => 'cog', 'label' => __('Voreinstellungen'), 'route' => 'admin.presets.index'],
|
||
] as $action)
|
||
<a href="{{ route($action['route']) }}" wire:navigate
|
||
class="group flex flex-col items-start gap-2 p-4 rounded-[5px] transition-colors
|
||
bg-[color:var(--color-bg-elev)] border border-[color:var(--color-bg-rule)]
|
||
hover:border-[color:var(--color-hub)]/40 hover:bg-[color:var(--color-bg)]">
|
||
<span class="w-9 h-9 rounded-[4px] flex items-center justify-center
|
||
bg-[color:var(--color-hub-soft)] border border-[color:var(--color-hub-soft-2)]
|
||
text-[color:var(--color-hub)] group-hover:bg-[color:var(--color-hub)] group-hover:text-white transition-colors">
|
||
<flux:icon :name="$action['icon']" class="size-[18px]" />
|
||
</span>
|
||
<span class="text-[12px] font-semibold text-[color:var(--color-ink-2)] leading-tight">
|
||
{{ $action['label'] }}
|
||
</span>
|
||
</a>
|
||
@endforeach
|
||
</div>
|
||
</article>
|
||
</section>
|
||
|
||
{{-- ============== FOOTER ============== --}}
|
||
<footer class="flex items-center justify-between pt-4 pb-2 text-[11px]
|
||
border-t border-[color:var(--color-bg-rule)] text-[color:var(--color-ink-3)]">
|
||
<span>© {{ now()->format('Y') }} pressekonto.de · Admin Backend</span>
|
||
<span class="flex items-center gap-5">
|
||
<a href="{{ route('admin.users.index') }}" wire:navigate class="hover:text-[color:var(--color-hub)]">{{ __('Benutzer') }}</a>
|
||
<a href="{{ route('admin.roles.index') }}" wire:navigate class="hover:text-[color:var(--color-hub)]">{{ __('Rollen & Rechte') }}</a>
|
||
<a href="{{ route('admin.reports.slow-requests') }}" wire:navigate class="hover:text-[color:var(--color-hub)]">{{ __('Performance') }}</a>
|
||
</span>
|
||
</footer>
|
||
</div>
|
||
</x-layouts.app>
|