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>
This commit is contained in:
parent
092ee0e918
commit
0a3e52d603
112 changed files with 8464 additions and 1649 deletions
|
|
@ -90,46 +90,75 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
}
|
||||
}; ?>
|
||||
|
||||
<div class="space-y-6">
|
||||
@if(session('success'))
|
||||
<div class="rounded-md border border-green-200 bg-green-50 px-4 py-3 text-sm text-green-800 dark:border-green-800 dark:bg-green-900/20 dark:text-green-300">
|
||||
<div class="space-y-8">
|
||||
@php
|
||||
$statusClass = match ($pr->status->value) {
|
||||
'published' => 'ok',
|
||||
'review' => 'warn',
|
||||
'rejected' => 'err',
|
||||
default => 'hub',
|
||||
};
|
||||
@endphp
|
||||
|
||||
@if (session('success'))
|
||||
<div class="px-4 py-3 rounded-[5px] border text-[12.5px]
|
||||
bg-[color:var(--color-ok-soft)] border-[color:var(--color-ok)]/30 text-[color:var(--color-gain-deep)]">
|
||||
{{ session('success') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<flux:card>
|
||||
<div class="flex flex-wrap items-start justify-between gap-3">
|
||||
<div class="flex-1">
|
||||
<div class="flex items-center gap-2">
|
||||
<flux:badge :color="$statusColor">{{ $pr->status->label() }}</flux:badge>
|
||||
<flux:badge color="zinc" size="sm">{{ strtoupper($pr->language) }}</flux:badge>
|
||||
<flux:badge color="zinc" size="sm">{{ $pr->portal->label() }}</flux:badge>
|
||||
</div>
|
||||
<flux:heading size="xl" class="mt-2">{{ $pr->title }}</flux:heading>
|
||||
<flux:text class="mt-1 text-sm text-zinc-500">
|
||||
{{ __('Firma') }}: {{ $pr->company?->name ?? '–' }} ·
|
||||
{{ __('Kategorie') }}: {{ $categoryName }} ·
|
||||
{{ __('Autor') }}: {{ $pr->user?->name ?? '–' }}
|
||||
</flux:text>
|
||||
</div>
|
||||
<div class="flex gap-2">
|
||||
<flux:button variant="ghost" icon="pencil" href="{{ route('admin.press-releases.edit', $pr->id) }}" wire:navigate>
|
||||
{{ __('Bearbeiten') }}
|
||||
</flux:button>
|
||||
<flux:button variant="ghost" icon="arrow-left" href="{{ route('admin.press-releases.index') }}" wire:navigate>
|
||||
{{ __('Zurück') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
@if (session('error'))
|
||||
<div class="px-4 py-3 rounded-[5px] border text-[12.5px]
|
||||
bg-[color:var(--color-err-soft)] border-[color:var(--color-err)]/30 text-[color:var(--color-loss)]">
|
||||
{{ session('error') }}
|
||||
</div>
|
||||
</flux:card>
|
||||
@endif
|
||||
|
||||
{{-- Status-Aktionen --}}
|
||||
@if($pr->status === \App\Enums\PressReleaseStatus::Review)
|
||||
<flux:card>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
<flux:text weight="medium" class="text-yellow-700 dark:text-yellow-400">
|
||||
{{-- ============== 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-wrap">
|
||||
<span class="badge hub dot">{{ __('Admin Backend') }}</span>
|
||||
<span class="eyebrow muted">{{ __('Content · Pressemitteilung') }}</span>
|
||||
<span @class(['badge', $statusClass])>{{ $pr->status->label() }}</span>
|
||||
<span class="badge hub">{{ strtoupper($pr->language) }}</span>
|
||||
<span class="badge hub">{{ $pr->portal->label() }}</span>
|
||||
</div>
|
||||
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
|
||||
{{ $pr->title }}
|
||||
</h1>
|
||||
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[720px] text-[color:var(--color-ink-2)]">
|
||||
<strong class="font-semibold text-[color:var(--color-ink)]">{{ __('Firma') }}:</strong>
|
||||
{{ $pr->company?->name ?? '–' }}
|
||||
<span class="text-[color:var(--color-bg-rule)] mx-1">·</span>
|
||||
<strong class="font-semibold text-[color:var(--color-ink)]">{{ __('Kategorie') }}:</strong>
|
||||
{{ $categoryName }}
|
||||
<span class="text-[color:var(--color-bg-rule)] mx-1">·</span>
|
||||
<strong class="font-semibold text-[color:var(--color-ink)]">{{ __('Autor') }}:</strong>
|
||||
{{ $pr->user?->name ?? '–' }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 flex-shrink-0">
|
||||
<flux:button variant="ghost" icon="pencil" href="{{ route('admin.press-releases.edit', $pr->id) }}" wire:navigate>
|
||||
{{ __('Bearbeiten') }}
|
||||
</flux:button>
|
||||
<flux:button variant="ghost" icon="arrow-left" href="{{ route('admin.press-releases.index') }}" wire:navigate>
|
||||
{{ __('Zurück') }}
|
||||
</flux:button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
{{-- ============== STATUS-WORKFLOW ============== --}}
|
||||
@if ($pr->status === \App\Enums\PressReleaseStatus::Review)
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Status-Workflow') }}</span>
|
||||
<span class="badge warn dot">{{ __('Wartet auf Prüfung') }}</span>
|
||||
</div>
|
||||
<div class="p-5 flex flex-wrap items-center gap-3">
|
||||
<p class="text-[13px] text-[color:var(--color-ink-2)] m-0 flex-1 min-w-[200px]">
|
||||
{{ __('Diese PM wartet auf Prüfung.') }}
|
||||
</flux:text>
|
||||
</p>
|
||||
<flux:modal.trigger name="confirm-show-publish">
|
||||
<flux:button type="button" variant="primary">{{ __('Veröffentlichen') }}</flux:button>
|
||||
</flux:modal.trigger>
|
||||
|
|
@ -137,136 +166,162 @@ new #[Layout('components.layouts.app'), Title('Pressemitteilung')] class extends
|
|||
<flux:button type="button" variant="danger">{{ __('Ablehnen') }}</flux:button>
|
||||
</flux:modal.trigger>
|
||||
</div>
|
||||
</flux:card>
|
||||
</article>
|
||||
@endif
|
||||
@if($pr->status === \App\Enums\PressReleaseStatus::Published)
|
||||
<flux:card>
|
||||
<div class="flex items-center gap-3">
|
||||
@if ($pr->status === \App\Enums\PressReleaseStatus::Published)
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Status-Workflow') }}</span>
|
||||
<span class="badge ok dot">{{ __('Live') }}</span>
|
||||
</div>
|
||||
<div class="p-5 flex flex-wrap items-center gap-3">
|
||||
@if ($pr->hits > 0)
|
||||
<p class="text-[13px] text-[color:var(--color-ink-2)] m-0 flex-1 min-w-[200px]">
|
||||
<strong class="text-[color:var(--color-ink)] font-semibold">{{ number_format($pr->hits) }}</strong>
|
||||
{{ __('Aufrufe seit Veröffentlichung') }}
|
||||
</p>
|
||||
@endif
|
||||
<flux:modal.trigger name="confirm-show-archive">
|
||||
<flux:button type="button" variant="ghost">{{ __('Archivieren') }}</flux:button>
|
||||
</flux:modal.trigger>
|
||||
@if($pr->hits > 0)
|
||||
<flux:text class="text-sm text-zinc-500">{{ number_format($pr->hits) }} {{ __('Aufrufe') }}</flux:text>
|
||||
@endif
|
||||
</div>
|
||||
</flux:card>
|
||||
</article>
|
||||
@endif
|
||||
|
||||
<div class="grid gap-6 lg:grid-cols-[1fr,280px]">
|
||||
{{-- Text --}}
|
||||
<flux:card>
|
||||
<div class="prose prose-zinc dark:prose-invert max-w-none">
|
||||
{!! nl2br(e($pr->text)) !!}
|
||||
{{-- ============== TEXT + SIDEBAR ============== --}}
|
||||
<div class="grid gap-6 lg:grid-cols-[1fr,300px]">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Inhalt') }}</span>
|
||||
</div>
|
||||
</flux:card>
|
||||
<div class="p-5">
|
||||
<div class="prose prose-zinc dark:prose-invert max-w-none text-[color:var(--color-ink)]">
|
||||
{!! nl2br(e($pr->text)) !!}
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
|
||||
{{-- Details --}}
|
||||
<div class="space-y-4">
|
||||
<flux:card>
|
||||
<flux:heading size="sm" class="mb-3">{{ __('Details') }}</flux:heading>
|
||||
<dl class="space-y-2 text-sm">
|
||||
<aside class="space-y-4">
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Details') }}</span>
|
||||
</div>
|
||||
<dl class="p-5 space-y-2.5 text-[12.5px]">
|
||||
<div class="flex justify-between gap-2">
|
||||
<dt class="text-zinc-500">{{ __('Status') }}</dt>
|
||||
<dd class="font-medium">{{ $pr->status->label() }}</dd>
|
||||
<dt class="text-[color:var(--color-ink-3)]">{{ __('Status') }}</dt>
|
||||
<dd class="font-semibold text-[color:var(--color-ink)]">{{ $pr->status->label() }}</dd>
|
||||
</div>
|
||||
<div class="flex justify-between gap-2">
|
||||
<dt class="text-zinc-500">{{ __('Erstellt') }}</dt>
|
||||
<dd>{{ $pr->created_at->format('d.m.Y H:i') }}</dd>
|
||||
<dt class="text-[color:var(--color-ink-3)]">{{ __('Erstellt') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">{{ $pr->created_at->format('d.m.Y H:i') }}</dd>
|
||||
</div>
|
||||
@if($pr->published_at)
|
||||
@if ($pr->published_at)
|
||||
<div class="flex justify-between gap-2">
|
||||
<dt class="text-zinc-500">{{ __('Veröffentlicht') }}</dt>
|
||||
<dd>{{ $pr->published_at->format('d.m.Y H:i') }}</dd>
|
||||
<dt class="text-[color:var(--color-ink-3)]">{{ __('Veröffentlicht') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">{{ $pr->published_at->format('d.m.Y H:i') }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
<div class="flex justify-between gap-2">
|
||||
<dt class="text-zinc-500">{{ __('Aufrufe') }}</dt>
|
||||
<dd>{{ number_format($pr->hits) }}</dd>
|
||||
<dt class="text-[color:var(--color-ink-3)]">{{ __('Aufrufe') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)] font-semibold">{{ number_format($pr->hits) }}</dd>
|
||||
</div>
|
||||
@if($pr->keywords)
|
||||
<div>
|
||||
<dt class="text-zinc-500">{{ __('Stichwörter') }}</dt>
|
||||
<dd class="mt-1">{{ $pr->keywords }}</dd>
|
||||
@if ($pr->keywords)
|
||||
<div class="pt-2 border-t border-[color:var(--color-bg-rule)]">
|
||||
<dt class="text-[color:var(--color-ink-3)] mb-1">{{ __('Stichwörter') }}</dt>
|
||||
<dd class="text-[color:var(--color-ink)]">{{ $pr->keywords }}</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if($pr->backlink_url)
|
||||
<div>
|
||||
<dt class="text-zinc-500">{{ __('Backlink') }}</dt>
|
||||
<dd class="mt-1 break-all">
|
||||
<a href="{{ $pr->backlink_url }}" target="_blank" class="text-blue-600 underline dark:text-blue-400">
|
||||
@if ($pr->backlink_url)
|
||||
<div class="pt-2 border-t border-[color:var(--color-bg-rule)]">
|
||||
<dt class="text-[color:var(--color-ink-3)] mb-1">{{ __('Backlink') }}</dt>
|
||||
<dd class="break-all">
|
||||
<a href="{{ $pr->backlink_url }}" target="_blank"
|
||||
class="text-[color:var(--color-hub)] underline underline-offset-2 decoration-[color:var(--color-hub)]/40 hover:decoration-[color:var(--color-hub)]">
|
||||
{{ $pr->backlink_url }}
|
||||
</a>
|
||||
</dd>
|
||||
</div>
|
||||
@endif
|
||||
@if($pr->no_export)
|
||||
<div class="rounded bg-zinc-100 px-2 py-1 text-xs text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300">
|
||||
{{ __('Kein Export') }}
|
||||
@if ($pr->no_export)
|
||||
<div class="mt-2 pt-2 border-t border-[color:var(--color-bg-rule)]">
|
||||
<span class="badge hub">{{ __('Kein Export') }}</span>
|
||||
</div>
|
||||
@endif
|
||||
</dl>
|
||||
</flux:card>
|
||||
</article>
|
||||
|
||||
@if($pr->images->isNotEmpty())
|
||||
<flux:card>
|
||||
<flux:heading size="sm" class="mb-3">{{ __('Bilder') }}</flux:heading>
|
||||
<div class="space-y-2">
|
||||
@foreach($pr->images as $image)
|
||||
<div class="flex items-center gap-2 text-sm">
|
||||
<flux:icon.photo class="size-4 text-zinc-400" />
|
||||
<span class="truncate text-zinc-600 dark:text-zinc-400">{{ basename($image->path) }}</span>
|
||||
@if($image->is_preview)
|
||||
<flux:badge size="sm" color="blue">{{ __('Preview') }}</flux:badge>
|
||||
@if ($pr->images->isNotEmpty())
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Bilder') }}</span>
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ $pr->images->count() }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-5 space-y-2">
|
||||
@foreach ($pr->images as $image)
|
||||
<div class="flex items-center gap-2 text-[12.5px]">
|
||||
<flux:icon.photo class="size-4 flex-shrink-0 text-[color:var(--color-ink-3)]" />
|
||||
<span class="truncate text-[color:var(--color-ink-2)]">{{ basename($image->path) }}</span>
|
||||
@if ($image->is_preview)
|
||||
<span class="badge hub">{{ __('Preview') }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</flux:card>
|
||||
</article>
|
||||
@endif
|
||||
</div>
|
||||
</aside>
|
||||
</div>
|
||||
|
||||
@if($statusLogs->isNotEmpty())
|
||||
<flux:card>
|
||||
<flux:heading size="sm" class="mb-3">{{ __('Status-Verlauf') }}</flux:heading>
|
||||
<ol class="space-y-3 border-s border-zinc-200 ps-4 dark:border-zinc-700">
|
||||
@foreach($statusLogs as $log)
|
||||
<li class="text-sm">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
@php
|
||||
$color = match($log->to_status?->value) {
|
||||
'published' => 'green',
|
||||
'review' => 'yellow',
|
||||
'rejected' => 'red',
|
||||
'archived' => 'blue',
|
||||
default => 'zinc',
|
||||
};
|
||||
@endphp
|
||||
<flux:badge size="sm" :color="$color">{{ $log->to_status?->label() ?? $log->to_status }}</flux:badge>
|
||||
@if($log->from_status)
|
||||
<span class="text-xs text-zinc-500">
|
||||
{{ __('von') }} {{ $log->from_status->label() }}
|
||||
{{-- ============== STATUS-VERLAUF ============== --}}
|
||||
@if ($statusLogs->isNotEmpty())
|
||||
<article class="panel">
|
||||
<div class="panel-head">
|
||||
<span class="section-eyebrow">{{ __('Status-Verlauf') }}</span>
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ $statusLogs->count() }} {{ __('Einträge') }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-5">
|
||||
<ol class="space-y-3 border-s border-[color:var(--color-bg-rule)] ps-4">
|
||||
@foreach ($statusLogs as $log)
|
||||
<li class="text-[12.5px]">
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
@php
|
||||
$logClass = match ($log->to_status?->value) {
|
||||
'published' => 'ok',
|
||||
'review' => 'warn',
|
||||
'rejected' => 'err',
|
||||
default => 'hub',
|
||||
};
|
||||
@endphp
|
||||
<span @class(['badge', $logClass])>{{ $log->to_status?->label() ?? $log->to_status }}</span>
|
||||
@if ($log->from_status)
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ __('von') }} {{ $log->from_status->label() }}
|
||||
</span>
|
||||
@endif
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">·</span>
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">
|
||||
{{ $log->created_at->format('d.m.Y H:i') }}
|
||||
</span>
|
||||
@if ($log->changedBy)
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">·</span>
|
||||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ $log->changedBy->name }}</span>
|
||||
@endif
|
||||
@if ($log->source !== 'admin')
|
||||
<span class="badge hub">{{ $log->source }}</span>
|
||||
@endif
|
||||
</div>
|
||||
@if ($log->reason)
|
||||
<p class="mt-1.5 text-[color:var(--color-ink-2)] m-0">{{ $log->reason }}</p>
|
||||
@endif
|
||||
<span class="text-xs text-zinc-500">·</span>
|
||||
<span class="text-xs text-zinc-500">
|
||||
{{ $log->created_at->format('d.m.Y H:i') }}
|
||||
</span>
|
||||
@if($log->changedBy)
|
||||
<span class="text-xs text-zinc-500">·</span>
|
||||
<span class="text-xs text-zinc-500">{{ $log->changedBy->name }}</span>
|
||||
@endif
|
||||
@if($log->source !== 'admin')
|
||||
<flux:badge size="xs" color="zinc">{{ $log->source }}</flux:badge>
|
||||
@endif
|
||||
</div>
|
||||
@if($log->reason)
|
||||
<p class="mt-1 text-zinc-600 dark:text-zinc-400">{{ $log->reason }}</p>
|
||||
@endif
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
</flux:card>
|
||||
</li>
|
||||
@endforeach
|
||||
</ol>
|
||||
</div>
|
||||
</article>
|
||||
@endif
|
||||
|
||||
@if($pr->status === \App\Enums\PressReleaseStatus::Review)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue