presseportale/resources/views/livewire/customer/bookings.blade.php
Kevin Adametz a000238ca8 User Panel: Phase-8-Abschluss, Titelbild/Lizenzen/Zeitzonen und KI-Pruef-Pipeline
Phase 8 (Rest) + Umbauten vom 10./11.06.:
- Ein Titelbild pro PM (Cover 1280x580), SVG-Platzhalter-Set + Picker,
  PressReleaseCoverImage-Resolver
- Lizenz-/Rechteformular nach "Lizenztyp Bildupload" (7 Lizenztypen,
  Personen-/Sachrechte-Status, bedingte Pflichtfelder, Risikohinweise)
- Veroeffentlichungs-Box vereinfacht (Embargo aus der Form-UI entfernt),
  geplante Termine in Europe/Berlin (Speicherung UTC, DISPLAY_TIMEZONE)
- Quota-Stub (users.press_release_quota) + monatlicher Reset-Command
- Einreichungs-Modal einheitlich in Show/Create/Edit; Ghost-Buttons auf
  filled; PM-Editor-Layout responsive entkoppelt (.pr-editor-layout)

KI-Pruef-Pipeline (Phasen 1-5 des Entwicklungsplans):
- API-Haertung: status nicht mehr per API setzbar, eigene Submit-Route
  durch denselben Funnel (Blacklist, Quota, Status-Log)
- Klassifikation Rot/Gelb/Gruen asynchron (Queue classification,
  OpenAI-Treiber + deterministischer Fallback), ki_audits-Audit-Log
- Routing: Rot -> rejected + Mail, Gelb -> Review-Queue, Gruen ->
  Auto-Publish; Scheduler publiziert nur gruene faellige PMs
- Content-Score 0-100 -> Stufe (Standard/Geprueft/Hochwertig) inkl.
  Editor-Panel und Badges; Re-Klassifikation/-Score bei Aenderung
- Admin: KI-Badge + Filter, On-Demand-Pruefung mit Anbieter-Override

Suite: 442 passed, 4 skipped.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 08:30:13 +00:00

390 lines
23 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.

<?php
use Livewire\Attributes\Layout;
use Livewire\Attributes\Title;
use Livewire\Volt\Component;
new #[Layout('components.layouts.app'), Title('Buchungen & Add-ons')] class extends Component
{
public function with(): array
{
return [
'creditSummary' => [
'total' => 17,
'bonus' => 12,
'paid' => 5,
'auto_refill' => __('ab 10 Credits empfohlen'),
'validity' => __('Bonus-Credits verfallen monatlich, gekaufte Credits bleiben 24 Monate gültig.'),
],
'currentPlan' => [
'name' => 'Starter',
'price' => '19 €/Mo.',
'press_releases' => '3 PMs/Monat',
'bonus_credits' => 12,
],
'creditPackages' => [
['name' => 'Test', 'credits' => 10, 'price' => '10 €', 'rate' => '1,00 €', 'saving' => null],
['name' => 'Standard', 'credits' => 50, 'price' => '45 €', 'rate' => '0,90 €', 'saving' => '10 %'],
['name' => 'Plus', 'credits' => 150, 'price' => '120 €', 'rate' => '0,80 €', 'saving' => '20 %'],
['name' => 'Pro', 'credits' => 500, 'price' => '375 €', 'rate' => '0,75 €', 'saving' => '25 %'],
['name' => 'Business', 'credits' => 1500, 'price' => '1.050 €', 'rate' => '0,70 €', 'saving' => '30 %'],
],
'serviceGroups' => [
[
'title' => __('Veröffentlichung'),
'description' => __('Basisleistungen rund um Veröffentlichung, Korrektur und Aktualisierung.'),
'services' => [
['name' => __('Standard-PM (Pay-as-you-go)'), 'credits' => '19', 'meta' => __('1 Veröffentlichung')],
['name' => __('PM-Korrektur'), 'credits' => '8', 'meta' => __('Pfad C')],
['name' => __('PM-Update'), 'credits' => '4', 'meta' => __('im ersten Jahr ggf. kostenlos')],
['name' => __('Depublizierung'), 'credits' => '1925', 'meta' => __('abhängig vom Aufwand')],
],
],
[
'title' => __('Bilder'),
'description' => __('Stock- und KI-Bilder für mehr Sichtbarkeit in Listen und Detailseiten.'),
'services' => [
['name' => __('Free-Stock'), 'credits' => '0', 'meta' => __('Unsplash, Pexels')],
['name' => __('Premium-Stock'), 'credits' => '8', 'meta' => __('Adobe, Shutterstock')],
['name' => __('KI-Bild generieren'), 'credits' => '4', 'meta' => __('neues Motiv')],
['name' => __('KI-Bild Re-Generation'), 'credits' => '2', 'meta' => __('Variante erzeugen')],
],
],
[
'title' => __('KI-Textservices'),
'description' => __('Qualität verbessern, Score-Stufe erreichen und bessere Headlines testen.'),
'services' => [
['name' => __('Quality-Check'), 'credits' => '3', 'meta' => __('Stil und Pressestil')],
['name' => __('Lektorat'), 'credits' => '8', 'meta' => __('sprachliche Prüfung')],
['name' => __('Pressetext-Optimierung'), 'credits' => '15', 'meta' => __('Headlines und SEO')],
['name' => __('Headline-Booster'), 'credits' => '5', 'meta' => __('nur Headlines')],
['name' => __('PM aus Stichworten generieren'), 'credits' => '25', 'meta' => __('Entwurf aus Briefing')],
['name' => __('Übersetzung DE/EN'), 'credits' => '12', 'meta' => __('pro Sprachrichtung')],
],
],
[
'title' => __('Distribution'),
'description' => __('Zusätzliche Formate und externe Reichweite für passende Meldungen.'),
'services' => [
['name' => __('PDF-Export mit Branding'), 'credits' => '2', 'meta' => __('für Weitergabe')],
['name' => __('Social-Snippet-Generierung'), 'credits' => '3', 'meta' => __('Kurztexte')],
['name' => __('Verteiler-Versand klein'), 'credits' => '39', 'meta' => __('branchenspezifisch')],
['name' => __('Verteiler-Versand mittel'), 'credits' => '99', 'meta' => __('mehr Empfänger')],
['name' => __('Verteiler-Versand groß'), 'credits' => '199', 'meta' => __('branchenübergreifend')],
],
],
[
'title' => __('Account & Profil'),
'description' => __('Vertrauen, Wiedererkennung und zusätzliche Profilfunktionen.'),
'services' => [
['name' => __('Verifiziertes Firmenprofil'), 'credits' => '79', 'meta' => __('einmalig')],
['name' => __('Custom Subdomain'), 'credits' => '49', 'meta' => __('pro Jahr')],
['name' => __('Erweiterte Statistiken'), 'credits' => '15', 'meta' => __('pro Monat')],
],
],
],
'placements' => [
['name' => __('Highlight Kategorie'), 'credits' => '15', 'duration' => __('3 Tage'), 'tier' => __('Standard'), 'score' => '30+'],
['name' => __('Highlight Kategorie'), 'credits' => '30', 'duration' => __('7 Tage'), 'tier' => __('Standard'), 'score' => '30+'],
['name' => __('Startseite-Highlight'), 'credits' => '39', 'duration' => __('24 h'), 'tier' => __('Geprüft'), 'score' => '60+'],
['name' => __('Startseite-Highlight'), 'credits' => '89', 'duration' => __('3 Tage'), 'tier' => __('Geprüft'), 'score' => '60+'],
['name' => __('Top-Slot Startseite'), 'credits' => '119', 'duration' => __('24 h'), 'tier' => __('Hochwertig'), 'score' => '80+'],
['name' => __('Newsletter-Erwähnung'), 'credits' => '59', 'duration' => __('nächster Versand'), 'tier' => __('Geprüft'), 'score' => '60+'],
['name' => __('Social-Share'), 'credits' => '25', 'duration' => __('offizieller Kanal'), 'tier' => __('Geprüft'), 'score' => '60+'],
],
'activeBookings' => [],
'bookingHistory' => [],
];
}
}; ?>
<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-wrap">
<span class="badge hub dot">{{ __('User Backend') }}</span>
<span class="eyebrow muted">{{ __('Mein Bereich · Finanzen') }}</span>
<span class="badge hub">{{ __('Konzeptstand Mai 2026') }}</span>
</div>
<h1 class="text-[30px] font-bold tracking-[-0.6px] leading-[1.15] m-0 text-[color:var(--color-ink)]">
{{ __('Buchungen & Add-ons') }}
</h1>
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[640px] text-[color:var(--color-ink-2)]">
{{ __('Der Marktplatz für Credit-Pakete, KI-Services, Platzierungen und Firmen-Add-ons. Die Preise folgen dem neuen Credit-Modell: 1 Credit entspricht dem Listenwert von 1 €.') }}
</p>
</div>
<div class="flex items-center gap-2 flex-shrink-0">
<flux:button size="sm" variant="filled" icon="document-text" href="{{ route('me.invoices.index') }}" wire:navigate>
{{ __('Rechnungen') }}
</flux:button>
<flux:button size="sm" variant="primary" icon="plus" disabled>
{{ __('Credits kaufen') }}
</flux:button>
</div>
</header>
{{-- ============== CREDIT-ÜBERSICHT ============== --}}
<section class="grid gap-4 lg:grid-cols-[1.2fr_0.8fr]">
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Credit-Stand') }}</span>
<span class="badge ok dot">{{ __('Auto-Refill vorbereitet') }}</span>
</div>
<div class="p-5 grid gap-5 md:grid-cols-[0.8fr_1.2fr]">
<div>
<div class="text-[42px] font-bold tracking-[-1.2px] leading-none text-[color:var(--color-ink)]">
{{ $creditSummary['total'] }}
</div>
<p class="text-[12.5px] text-[color:var(--color-ink-3)] mt-2 mb-0">
{{ __('verfügbare Credits') }}
</p>
</div>
<div class="grid gap-3 sm:grid-cols-2">
<div class="rounded-[6px] border border-[color:var(--color-bg-rule)] p-4 bg-[color:var(--color-bg-subtle)]">
<div class="text-[12px] text-[color:var(--color-ink-3)]">{{ __('Bonus-Credits') }}</div>
<div class="text-[22px] font-semibold text-[color:var(--color-ink)]">{{ $creditSummary['bonus'] }}</div>
<div class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ __('monatlich verfallend') }}</div>
</div>
<div class="rounded-[6px] border border-[color:var(--color-bg-rule)] p-4 bg-[color:var(--color-bg-subtle)]">
<div class="text-[12px] text-[color:var(--color-ink-3)]">{{ __('Gekaufte Credits') }}</div>
<div class="text-[22px] font-semibold text-[color:var(--color-ink)]">{{ $creditSummary['paid'] }}</div>
<div class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ __('24 Monate gültig') }}</div>
</div>
</div>
<div class="md:col-span-2 px-4 py-3 rounded-[5px] border text-[12.5px] flex items-start gap-3
bg-[color:var(--color-hub-soft)] border-[color:var(--color-hub-soft-2)] text-[color:var(--color-ink-2)]">
<flux:icon.information-circle class="size-[16px] flex-shrink-0 mt-0.5 text-[color:var(--color-hub)]" />
<div class="flex-1">
{{ $creditSummary['validity'] }}
{{ __('Für spätere Checkouts ist Auto-Refill :threshold vorgesehen.', ['threshold' => $creditSummary['auto_refill']]) }}
</div>
</div>
</div>
</article>
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Aktueller Tarif') }}</span>
<span class="badge hub">{{ $currentPlan['name'] }}</span>
</div>
<div class="p-5 space-y-4">
<div>
<div class="text-[28px] font-bold tracking-[-0.7px] text-[color:var(--color-ink)]">
{{ $currentPlan['price'] }}
</div>
<p class="text-[12.5px] text-[color:var(--color-ink-3)] mt-1 mb-0">
{{ __('inkl. :credits Bonus-Credits und :pms', [
'credits' => $currentPlan['bonus_credits'],
'pms' => $currentPlan['press_releases'],
]) }}
</p>
</div>
<div class="rounded-[6px] border border-[color:var(--color-bg-rule)] p-4">
<div class="text-[12px] font-semibold text-[color:var(--color-ink)] mb-1">
{{ __('Nächster sinnvoller Schritt') }}
</div>
<p class="text-[12.5px] text-[color:var(--color-ink-3)] m-0">
{{ __('Bei mehreren PMs mit KI-Optimierung oder Platzierungen ergänzt das Standard-Paket die monatlichen Bonus-Credits am saubersten.') }}
</p>
</div>
</div>
</article>
</section>
{{-- ============== CREDIT-PAKETE ============== --}}
<article class="panel overflow-hidden">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Credit-Pakete') }}</span>
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ __('Volumenrabatt nach Paketgröße') }}</span>
</div>
<flux:table>
<flux:table.columns>
<flux:table.column>{{ __('Paket') }}</flux:table.column>
<flux:table.column>{{ __('Credits') }}</flux:table.column>
<flux:table.column>{{ __('Preis') }}</flux:table.column>
<flux:table.column>{{ __('Effektiv/Credit') }}</flux:table.column>
<flux:table.column>{{ __('Ersparnis') }}</flux:table.column>
<flux:table.column>{{ __('Aktion') }}</flux:table.column>
</flux:table.columns>
@foreach ($creditPackages as $package)
<flux:table.row wire:key="credit-package-{{ $package['name'] }}">
<flux:table.cell>
<span class="text-[13px] font-semibold text-[color:var(--color-ink)]">{{ $package['name'] }}</span>
</flux:table.cell>
<flux:table.cell>{{ number_format($package['credits'], 0, ',', '.') }}</flux:table.cell>
<flux:table.cell>
<span class="font-semibold text-[color:var(--color-ink)]">{{ $package['price'] }}</span>
</flux:table.cell>
<flux:table.cell>{{ $package['rate'] }}</flux:table.cell>
<flux:table.cell>
@if ($package['saving'])
<span class="badge ok">{{ $package['saving'] }}</span>
@else
<span class="text-[12px] text-[color:var(--color-ink-3)]"></span>
@endif
</flux:table.cell>
<flux:table.cell>
<flux:button size="sm" variant="filled" disabled>
{{ __('Kaufen') }}
</flux:button>
</flux:table.cell>
</flux:table.row>
@endforeach
</flux:table>
</article>
{{-- ============== PLATZIERUNGEN ============== --}}
<section class="space-y-4">
<div>
<span class="section-eyebrow">{{ __('Boost & Platzierungen') }}</span>
<h2 class="text-[22px] font-bold tracking-[-0.4px] mt-1 mb-1 text-[color:var(--color-ink)]">
{{ __('Sichtbarkeit buchen, wenn die Score-Stufe passt') }}
</h2>
<p class="text-[12.5px] text-[color:var(--color-ink-3)] max-w-[760px] m-0">
{{ __('Platzierungen bleiben an Qualitätsstufen gekoppelt: Standard reicht für Kategorie-Highlights, Geprüft für Startseite/Newsletter/Social und Hochwertig für den Top-Slot.') }}
</p>
</div>
<div class="grid gap-4 md:grid-cols-2 xl:grid-cols-3">
@foreach ($placements as $placement)
<article class="panel">
<div class="p-5 space-y-4">
<div class="flex items-start justify-between gap-4">
<div class="flex items-start gap-3">
<div class="w-10 h-10 rounded-[6px] flex items-center justify-center bg-[color:var(--color-hub-soft)] text-[color:var(--color-hub)]">
<flux:icon.megaphone class="size-5" />
</div>
<div>
<h3 class="text-[14px] font-semibold text-[color:var(--color-ink)] m-0">
{{ $placement['name'] }}
</h3>
<p class="text-[12px] text-[color:var(--color-ink-3)] mt-1 mb-0">
{{ $placement['duration'] }}
</p>
</div>
</div>
<div class="text-right">
<div class="text-[22px] font-bold text-[color:var(--color-ink)]">{{ $placement['credits'] }}</div>
<div class="text-[11px] text-[color:var(--color-ink-3)]">{{ __('Credits') }}</div>
</div>
</div>
<div class="flex items-center justify-between gap-3 rounded-[6px] border border-[color:var(--color-bg-rule)] p-3">
<div>
<div class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ __('Mindeststufe') }}</div>
<div class="text-[13px] font-semibold text-[color:var(--color-ink)]">{{ $placement['tier'] }}</div>
</div>
<flux:tooltip content="{{ __('Interner Score-Schwellenwert: :score', ['score' => $placement['score']]) }}">
<span class="badge hub">{{ __('Score :score', ['score' => $placement['score']]) }}</span>
</flux:tooltip>
</div>
<flux:button size="sm" variant="primary" class="w-full" disabled>
{{ __('Buchung vorbereiten') }}
</flux:button>
</div>
</article>
@endforeach
</div>
</section>
{{-- ============== SERVICE-MARKTPLATZ ============== --}}
<section class="space-y-4">
<div>
<span class="section-eyebrow">{{ __('Add-on-Marktplatz') }}</span>
<h2 class="text-[22px] font-bold tracking-[-0.4px] mt-1 mb-1 text-[color:var(--color-ink)]">
{{ __('Buchbare Services nach Kategorie') }}
</h2>
</div>
<div class="grid gap-4 xl:grid-cols-2">
@foreach ($serviceGroups as $group)
<article class="panel">
<div class="panel-head">
<div class="flex items-center gap-2">
<flux:icon.sparkles class="size-4 text-[color:var(--color-hub)]" />
<span class="section-eyebrow">{{ $group['title'] }}</span>
</div>
</div>
<div class="p-5">
<p class="text-[12.5px] text-[color:var(--color-ink-3)] mt-0 mb-4">
{{ $group['description'] }}
</p>
<div class="divide-y divide-[color:var(--color-bg-rule)]">
@foreach ($group['services'] as $service)
<div class="py-3 first:pt-0 last:pb-0 flex items-center justify-between gap-4">
<div class="min-w-0">
<div class="text-[13px] font-semibold text-[color:var(--color-ink)]">{{ $service['name'] }}</div>
<div class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ $service['meta'] }}</div>
</div>
<div class="text-right flex-shrink-0">
<div class="text-[15px] font-bold text-[color:var(--color-ink)]">{{ $service['credits'] }}</div>
<div class="text-[10.5px] uppercase tracking-[0.08em] text-[color:var(--color-ink-3)]">{{ __('Credits') }}</div>
</div>
</div>
@endforeach
</div>
</div>
</article>
@endforeach
</div>
</section>
{{-- ============== AKTIVE BUCHUNGEN / VERLAUF ============== --}}
<section class="grid gap-4 lg:grid-cols-2">
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Aktive Buchungen') }}</span>
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ __('läuft aktuell') }}</span>
</div>
<div class="p-5">
@forelse ($activeBookings as $booking)
<div>{{ $booking }}</div>
@empty
<div class="flex flex-col items-center justify-center px-4 py-10 text-center">
<div class="w-14 h-14 rounded-[6px] flex items-center justify-center mb-3
bg-[color:var(--color-hub-soft)] border border-[color:var(--color-hub-soft-2)] text-[color:var(--color-hub)]">
<flux:icon.calendar-days class="size-6" />
</div>
<div class="text-[14px] font-semibold text-[color:var(--color-ink)] mb-1">
{{ __('Noch keine aktiven Buchungen') }}
</div>
<p class="text-[12px] text-[color:var(--color-ink-3)] max-w-md m-0">
{{ __('Gebuchte Highlights, Newsletter-Platzierungen oder Add-ons erscheinen hier mit Laufzeit und zugehöriger Firma.') }}
</p>
</div>
@endforelse
</div>
</article>
<article class="panel">
<div class="panel-head">
<span class="section-eyebrow">{{ __('Verlauf') }}</span>
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">{{ __('verbrauchte Credits') }}</span>
</div>
<div class="p-5">
@forelse ($bookingHistory as $booking)
<div>{{ $booking }}</div>
@empty
<div class="flex flex-col items-center justify-center px-4 py-10 text-center">
<div class="w-14 h-14 rounded-[6px] flex items-center justify-center mb-3
bg-[color:var(--color-bg-subtle)] border border-[color:var(--color-bg-rule)] text-[color:var(--color-ink-3)]">
<flux:icon.clock class="size-6" />
</div>
<div class="text-[14px] font-semibold text-[color:var(--color-ink)] mb-1">
{{ __('Noch kein Buchungsverlauf') }}
</div>
<p class="text-[12px] text-[color:var(--color-ink-3)] max-w-md m-0">
{{ __('Nach dem ersten Checkout werden Verbrauch, Rechnungsbezug und betroffene Pressemitteilung hier nachvollziehbar.') }}
</p>
</div>
@endforelse
</div>
</article>
</section>
</div>