presseportale/resources/views/admin/dashboard.blade.php
Kevin Adametz 0a3e52d603 19-05-2026 Rebrand Pressekonto, Hub-Flux UI und Legacy-Media-Migration
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>
2026-05-19 16:36:13 +00:00

217 lines
13 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.

<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>