5.4 (Teil 6): page-header in payments/invoices/legal/coupons/reports/newsletter

Component minimal-additiv erweitert (Defaults unveraendert):
- size=sm (28px) fuer legal-requests/show
- description-width=720px fuer breite Fliesstexte

Umgestellt (letzter grosser Batch):
- payments/index, payments/plans (720px, Aktionen)
- invoices/index, coupons/index (720px, Badge via :meta, keine Aktionen)
- legal-requests/index (Standard), legal-requests/show (sm, Status-Badge)
- reports/slow-requests (Logdatei-Zaehler-Badge als Aktion)
- newsletter/sync (Sync-Status via :meta, Dry-Run/Test-Sync-Buttons)

CSS-Hash unveraendert -> keine neuen Klassen. 74 Tests gruen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Kevin Adametz 2026-07-01 10:36:33 +00:00
parent 25a409521c
commit 8e90eb24e2
9 changed files with 97 additions and 136 deletions

View file

@ -12,18 +12,24 @@
'description' => '',
/**
* Titel-Größe: md (Standard, 30px) | lg (Landing/Hero-Listen, 34px).
* Titel-Größe: sm (28px) | md (Standard, 30px) | lg (Landing/Hero-Listen, 34px).
* lg vergrößert zusätzlich die Beschreibung und den Button-Abstand.
*/
'size' => 'md',
/** Maximale Breite der Beschreibung: 640px (Standard) | 720px (breite Fließtexte) */
'descriptionWidth' => '640px',
])
@php
$size = in_array($size, ['md', 'lg'], true) ? $size : 'md';
$titleClass = $size === 'lg'
? 'text-[34px] tracking-[-0.7px] leading-[1.1]'
: 'text-[30px] tracking-[-0.6px] leading-[1.15]';
$size = in_array($size, ['sm', 'md', 'lg'], true) ? $size : 'md';
$titleClass = match ($size) {
'sm' => 'text-[28px] tracking-[-0.5px] leading-[1.15]',
'lg' => 'text-[34px] tracking-[-0.7px] leading-[1.1]',
default => 'text-[30px] tracking-[-0.6px] leading-[1.15]',
};
$descriptionClass = $size === 'lg' ? 'text-[13.5px]' : 'text-[13px]';
$descriptionMaxWidth = $descriptionWidth === '720px' ? 'max-w-[720px]' : 'max-w-[640px]';
$actionsGap = $size === 'lg' ? 'gap-3' : 'gap-2';
@endphp
@ -52,7 +58,7 @@
</h1>
@if (filled($description))
<p class="{{ $descriptionClass }} leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
<p class="{{ $descriptionClass }} leading-[1.55] mt-2 m-0 {{ $descriptionMaxWidth }} text-[color:var(--color-ink-2)]">
{{ $description }}
</p>
@endif

View file

@ -14,21 +14,17 @@ new #[Layout('components.layouts.app'), Title('Gutscheine')] class extends Compo
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-wrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Administration · Finanzen') }}</span>
<span class="badge warn">{{ __('Vertagt') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Gutscheine') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[720px] text-[color:var(--color-ink-2)]">
{{ __('Coupons sind in der Initialmigration vertagt (Entscheidung D-16). Eine Wiedereinführung wird später separat evaluiert – ggf. direkt über Stripe-Coupons.') }}
</p>
</div>
</header>
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Finanzen')"
:title="__('Gutscheine')"
:description="__('Coupons sind in der Initialmigration vertagt (Entscheidung D-16). Eine Wiedereinführung wird später separat evaluiert – ggf. direkt über Stripe-Coupons.')"
description-width="720px"
>
<x-slot:meta>
<span class="badge warn">{{ __('Vertagt') }}</span>
</x-slot:meta>
</x-portal.page-header>
<article class="panel">
<div class="panel-head">

View file

@ -122,21 +122,17 @@ new #[Layout('components.layouts.app'), Title('Legacy Rechnungen')] class extend
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-wrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Administration · Finanzen') }}</span>
<span class="badge hub">{{ __('Legacy-Rechnungsarchiv') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Legacy Rechnungen') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[720px] text-[color:var(--color-ink-2)]">
{{ __('Legacy-Rechnungsarchiv mit read-only Übersicht, Filtern und PDF-Download. Der neue Stripe-Rechnungslauf folgt separat in Phase 8.') }}
</p>
</div>
</header>
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Finanzen')"
:title="__('Legacy Rechnungen')"
:description="__('Legacy-Rechnungsarchiv mit read-only Übersicht, Filtern und PDF-Download. Der neue Stripe-Rechnungslauf folgt separat in Phase 8.')"
description-width="720px"
>
<x-slot:meta>
<span class="badge hub">{{ __('Legacy-Rechnungsarchiv') }}</span>
</x-slot:meta>
</x-portal.page-header>
@if ($stats['unmapped_count'] > 0)
<div class="px-4 py-3 rounded-[5px] border text-[12.5px] flex items-start gap-3

View file

@ -60,20 +60,12 @@ new #[Layout('components.layouts.app'), Title('Recht & Compliance')] class exten
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<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">{{ __('Administration · Recht & Compliance') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Recht & Compliance') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
{{ __('DSGVO-Anonymisierung, Persönlichkeitsrechte und Meldungen zu Pressemitteilungen – manuelle Bearbeitung, keine KI.') }}
</p>
</div>
</header>
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Recht & Compliance')"
:title="__('Recht & Compliance')"
:description="__('DSGVO-Anonymisierung, Persönlichkeitsrechte und Meldungen zu Pressemitteilungen – manuelle Bearbeitung, keine KI.')"
/>
{{-- ============== KPI-Reihe ============== --}}
<section class="grid grid-cols-1 gap-4 sm:grid-cols-2">

View file

@ -83,24 +83,21 @@ new #[Layout('components.layouts.app'), Title('Anfrage · Recht & Compliance')]
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-wrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Recht & Compliance · Anfrage') }}</span>
<span class="badge dot {{ $this->statusBadgeClass($request->status) }}">{{ $request->status->label() }}</span>
</div>
<h1 class="text-[28px] font-bold tracking-[-0.5px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ $request->type->label() }}
</h1>
</div>
<div class="flex flex-wrap items-center gap-2 flex-shrink-0">
<x-portal.page-header
size="sm"
:badge="__('Admin Backend')"
:eyebrow="__('Recht & Compliance · Anfrage')"
:title="$request->type->label()"
>
<x-slot:meta>
<span class="badge dot {{ $this->statusBadgeClass($request->status) }}">{{ $request->status->label() }}</span>
</x-slot:meta>
<x-slot:actions>
<flux:button icon="arrow-left" variant="filled" href="{{ route('admin.legal-requests.index') }}" wire:navigate>
{{ __('Zurück') }}
</flux:button>
</div>
</header>
</x-slot:actions>
</x-portal.page-header>
@if ($notification)
<div x-data="{ show: true }" x-init="setTimeout(() => show = false, 3000)" x-show="show" x-transition

View file

@ -88,34 +88,28 @@ new #[Layout('components.layouts.app'), Title('Newsletter Sync')] class extends
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-wrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Administration · Newsletter') }}</span>
@if ($syncConfig['enabled'])
<span class="badge ok dot">{{ __('Sync aktiv') }}</span>
@else
<span class="badge dot">{{ __('Sync deaktiviert') }}</span>
@endif
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Newsletter Synchronisierung') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
{{ __('Vorbereitung fuer die kuenftige externe API-Anbindung. Aktuell ist nur das technische Grundgeruest aktiv.') }}
</p>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Newsletter')"
:title="__('Newsletter Synchronisierung')"
:description="__('Vorbereitung fuer die kuenftige externe API-Anbindung. Aktuell ist nur das technische Grundgeruest aktiv.')"
>
<x-slot:meta>
@if ($syncConfig['enabled'])
<span class="badge ok dot">{{ __('Sync aktiv') }}</span>
@else
<span class="badge dot">{{ __('Sync deaktiviert') }}</span>
@endif
</x-slot:meta>
<x-slot:actions>
<flux:button size="sm" variant="filled" icon="eye" wire:click="triggerDryRun">
{{ __('Dry Run') }}
</flux:button>
<flux:button size="sm" variant="primary" icon="play" wire:click="triggerTestSync">
{{ __('Test-Sync ausfuehren') }}
</flux:button>
</div>
</header>
</x-slot:actions>
</x-portal.page-header>
@if ($dryRunMessage)
<div class="px-4 py-3 rounded-[5px] border text-[12.5px] flex items-start gap-3

View file

@ -156,28 +156,22 @@ new #[Layout('components.layouts.app'), Title('Zahlungen')] class extends Compon
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-wrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Administration · Finanzen') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Zahlungen') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[720px] text-[color:var(--color-ink-2)]">
{{ __('Stripe-Abos, Einmalkäufe und der lokale Rechnungsausgang (STR-/MAN-Kreis) auf einen Blick. Stripe bleibt Zahlungs- und Belegquelle — diese Übersicht spiegelt die per Webhook synchronisierten Daten.') }}
</p>
</div>
<div class="flex items-center gap-2">
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Finanzen')"
:title="__('Zahlungen')"
:description="__('Stripe-Abos, Einmalkäufe und der lokale Rechnungsausgang (STR-/MAN-Kreis) auf einen Blick. Stripe bleibt Zahlungs- und Belegquelle — diese Übersicht spiegelt die per Webhook synchronisierten Daten.')"
description-width="720px"
>
<x-slot:actions>
<flux:button size="sm" variant="filled" icon="archive-box" :href="route('admin.invoices.index')" wire:navigate>
{{ __('Legacy-Rechnungen') }}
</flux:button>
<flux:button size="sm" variant="primary" icon="rectangle-stack" :href="route('admin.payments.plans')" wire:navigate>
{{ __('Tarife & Pakete') }}
</flux:button>
</div>
</header>
</x-slot:actions>
</x-portal.page-header>
@if ($notification)
<div x-data="{ show: true }" x-init="setTimeout(() => show = false, 3500)" x-show="show" x-transition

View file

@ -127,25 +127,19 @@ new #[Layout('components.layouts.app'), Title('Tarife & Pakete')] class extends
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<div class="min-w-0">
<div class="flex items-center gap-3 mb-3 flex-wrap">
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
<span class="eyebrow muted">{{ __('Administration · Finanzen') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Tarife & Pakete') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[720px] text-[color:var(--color-ink-2)]">
{{ __('Preise, Kontingente und Limits der Tarife pflegen. Änderungen erscheinen sofort auf der Buchungs-Seite und werden direkt nach Stripe synchronisiert.') }}
</p>
</div>
<div class="flex items-center gap-2">
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Finanzen')"
:title="__('Tarife & Pakete')"
:description="__('Preise, Kontingente und Limits der Tarife pflegen. Änderungen erscheinen sofort auf der Buchungs-Seite und werden direkt nach Stripe synchronisiert.')"
description-width="720px"
>
<x-slot:actions>
<flux:button size="sm" variant="filled" icon="credit-card" :href="route('admin.payments.index')" wire:navigate>
{{ __('Zahlungen') }}
</flux:button>
</div>
</header>
</x-slot:actions>
</x-portal.page-header>
@if ($savedMessage)
<div class="px-4 py-3 rounded-[5px] border text-[12.5px] flex items-start gap-3

View file

@ -53,24 +53,16 @@ new #[Layout('components.layouts.app'), Title('Performance Reports')] class exte
<div class="space-y-8">
{{-- ============== PAGE HEADER ============== --}}
<header class="page-header">
<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">{{ __('Administration · Reports') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Performance Reports') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
{{ __('Auswertung der Slow-Admin-Request-Logs aus dem admin_slow Log-Kanal.') }}
</p>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<x-portal.page-header
:badge="__('Admin Backend')"
:eyebrow="__('Administration · Reports')"
:title="__('Performance Reports')"
:description="__('Auswertung der Slow-Admin-Request-Logs aus dem admin_slow Log-Kanal.')"
>
<x-slot:actions>
<span class="badge hub">{{ __('Logdateien') }}: {{ $report['summary']['files'] }}</span>
</div>
</header>
</x-slot:actions>
</x-portal.page-header>
{{-- ============== FILTER-PANEL ============== --}}
<article class="panel">