597 lines
31 KiB
PHP
597 lines
31 KiB
PHP
<?php
|
||
|
||
use App\Enums\PressReleaseStatus;
|
||
use App\Models\BillingAddress;
|
||
use App\Models\Company;
|
||
use App\Models\Contact;
|
||
use App\Models\PressRelease;
|
||
use App\Models\Profile;
|
||
use App\Models\User;
|
||
use App\Services\Customer\CustomerCompanyContext;
|
||
use Illuminate\Database\Eloquent\Builder;
|
||
use Illuminate\Support\Carbon;
|
||
use Livewire\Attributes\Layout;
|
||
use Livewire\Attributes\Title;
|
||
use Livewire\Volt\Component;
|
||
|
||
new #[Layout('components.layouts.app'), Title('Mein Dashboard')] class extends Component
|
||
{
|
||
public function with(): array
|
||
{
|
||
$user = auth()->user();
|
||
$context = app(CustomerCompanyContext::class);
|
||
$selectedCompanyId = $context->selectedCompanyId($user);
|
||
$selectedCompany = $selectedCompanyId === null
|
||
? null
|
||
: $context->findFor($user, $selectedCompanyId);
|
||
|
||
$pressReleaseQuery = PressRelease::withoutGlobalScopes()
|
||
->where('user_id', $user->id)
|
||
->when($selectedCompanyId !== null, fn ($query) => $query->where('company_id', $selectedCompanyId));
|
||
|
||
$now = Carbon::now();
|
||
$currentStart = $now->copy()->startOfMonth();
|
||
$previousStart = $now->copy()->subMonthNoOverflow()->startOfMonth();
|
||
$previousEnd = $now->copy()->subMonthNoOverflow()->endOfMonth();
|
||
|
||
$stats = (clone $pressReleaseQuery)
|
||
->toBase()
|
||
->selectRaw('COUNT(*) as total')
|
||
->selectRaw('SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as published', [PressReleaseStatus::Published->value])
|
||
->selectRaw('SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as review', [PressReleaseStatus::Review->value])
|
||
->selectRaw('SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) as draft', [PressReleaseStatus::Draft->value])
|
||
->selectRaw('SUM(CASE WHEN created_at >= ? THEN 1 ELSE 0 END) as current_month', [$currentStart])
|
||
->selectRaw('SUM(CASE WHEN created_at BETWEEN ? AND ? THEN 1 ELSE 0 END) as previous_month', [$previousStart, $previousEnd])
|
||
->first();
|
||
|
||
$recent = (clone $pressReleaseQuery)
|
||
->with('company:id,name')
|
||
->latest()
|
||
->limit(5)
|
||
->get(['id', 'title', 'status', 'portal', 'company_id', 'created_at', 'published_at']);
|
||
|
||
$profile = $user->profile;
|
||
$billingAddress = $user->billingAddress;
|
||
|
||
return [
|
||
'user' => $user,
|
||
'selectedCompany' => $selectedCompany,
|
||
'stats' => [
|
||
'total' => (int) ($stats->total ?? 0),
|
||
'published' => (int) ($stats->published ?? 0),
|
||
'review' => (int) ($stats->review ?? 0),
|
||
'draft' => (int) ($stats->draft ?? 0),
|
||
'deltaMonth' => (int) ($stats->current_month ?? 0) - (int) ($stats->previous_month ?? 0),
|
||
],
|
||
'profileCompleteness' => $this->profileCompleteness($profile),
|
||
'billingCompleteness' => $this->billingCompleteness($billingAddress),
|
||
'qualityHints' => $this->qualityHints(
|
||
$user,
|
||
$profile,
|
||
$billingAddress,
|
||
$selectedCompany,
|
||
$pressReleaseQuery,
|
||
),
|
||
'recent' => $recent,
|
||
'companies' => $context->latestCompaniesFor($user),
|
||
'companiesTotal' => $context->companyCountFor($user),
|
||
'bridgeStatus' => [
|
||
/* Heute hardcoded — perspektivisch aus echtem Sync-Service. */
|
||
'presseecho' => ['state' => 'connected', 'subline' => __('Archiv · Branchen-Tiefe')],
|
||
'businessportal24' => ['state' => 'connected', 'subline' => __('Wirtschaft · Live')],
|
||
],
|
||
];
|
||
}
|
||
|
||
/**
|
||
* Heuristische Profil-Vollständigkeit in %.
|
||
* 6 Kernfelder, jedes ~17 %.
|
||
*/
|
||
private function profileCompleteness(?Profile $profile): int
|
||
{
|
||
if (! $profile) {
|
||
return 0;
|
||
}
|
||
|
||
$fields = [
|
||
'salutation_key',
|
||
'first_name',
|
||
'last_name',
|
||
'phone',
|
||
'address',
|
||
'country_code',
|
||
];
|
||
|
||
$filled = collect($fields)->filter(fn (string $field) => filled($profile->{$field}))->count();
|
||
|
||
return (int) round(($filled / count($fields)) * 100);
|
||
}
|
||
|
||
/**
|
||
* Rechnungsadressen-Vollständigkeit in %.
|
||
* Ohne hinterlegte Adresse: 0; sonst je nach gefüllten Pflichtfeldern.
|
||
*/
|
||
private function billingCompleteness(?BillingAddress $address): int
|
||
{
|
||
if (! $address) {
|
||
return 0;
|
||
}
|
||
|
||
$fields = ['name', 'address1', 'postal_code', 'city', 'country_code'];
|
||
$filled = collect($fields)->filter(fn (string $field) => filled($address->{$field}))->count();
|
||
|
||
return (int) round(($filled / count($fields)) * 100);
|
||
}
|
||
|
||
/**
|
||
* @return list<array{icon: string, title: string, description: string, href: string, action: string, percent?: int}>
|
||
*/
|
||
private function qualityHints(
|
||
User $user,
|
||
?Profile $profile,
|
||
?BillingAddress $billingAddress,
|
||
?Company $selectedCompany,
|
||
Builder $pressReleaseQuery,
|
||
): array {
|
||
$hints = [];
|
||
|
||
$profilePercent = $this->profileCompleteness($profile);
|
||
if ($profilePercent < 100) {
|
||
$hints[] = [
|
||
'icon' => 'user',
|
||
'title' => __('Profil unvollständig'),
|
||
'description' => __('Ergänzen Sie Salutation, Telefon und Adresse für eine saubere Kundenakte.'),
|
||
'href' => route('me.profile').'#profil',
|
||
'action' => __('Profil öffnen'),
|
||
'percent' => $profilePercent,
|
||
];
|
||
}
|
||
|
||
$billingPercent = $this->billingCompleteness($billingAddress);
|
||
if ($billingPercent < 100) {
|
||
$hints[] = [
|
||
'icon' => 'archive-box',
|
||
'title' => $billingAddress
|
||
? __('Rechnungsadresse unvollständig')
|
||
: __('Rechnungsadresse fehlt'),
|
||
'description' => __('Hinterlegen Sie eine Rechnungsadresse, damit spätere Buchungen sauber abgerechnet werden können.'),
|
||
'href' => route('me.profile').'#rechnungsadresse',
|
||
'action' => __('Rechnungsadresse ergänzen'),
|
||
'percent' => $billingPercent,
|
||
];
|
||
}
|
||
|
||
if ($selectedCompany) {
|
||
$contactsCount = Contact::withoutGlobalScopes()
|
||
->where('company_id', $selectedCompany->id)
|
||
->count();
|
||
|
||
if ($contactsCount === 0) {
|
||
$hints[] = [
|
||
'icon' => 'user-group',
|
||
'title' => __('Keine Pressekontakte hinterlegt'),
|
||
'description' => __('Ergänzen Sie Pressekontakte für diese Firma.'),
|
||
'href' => route('me.press-kits.show', $selectedCompany->id),
|
||
'action' => __('Firma öffnen'),
|
||
];
|
||
}
|
||
} else {
|
||
$unassignedPressReleasesCount = (clone $pressReleaseQuery)
|
||
->whereNull('company_id')
|
||
->count();
|
||
|
||
if ($unassignedPressReleasesCount > 0) {
|
||
$hints[] = [
|
||
'icon' => 'newspaper',
|
||
'title' => trans_choice(
|
||
':count Pressemitteilung ohne Firma|:count Pressemitteilungen ohne Firma',
|
||
$unassignedPressReleasesCount,
|
||
['count' => $unassignedPressReleasesCount],
|
||
),
|
||
'description' => __('Ordnen Sie Legacy-Pressemitteilungen einer Firma zu, damit Portal und Pressekontakte eindeutig sind.'),
|
||
'href' => route('me.press-releases.index', ['company' => 'unassigned']),
|
||
'action' => __('Pressemitteilungen prüfen'),
|
||
];
|
||
}
|
||
}
|
||
|
||
return $hints;
|
||
}
|
||
}; ?>
|
||
|
||
<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">{{ __('User Backend') }}</span>
|
||
<span class="eyebrow muted">{{ __('Mein Bereich · A · 01') }}</span>
|
||
</div>
|
||
<h1 class="text-[34px] font-bold tracking-[-0.7px] leading-[1.1] m-0 text-[color:var(--color-ink)]">
|
||
{{ __('Mein 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)]">{{ $user->name }}</strong>.
|
||
@if ($selectedCompany)
|
||
{{ __('Übersicht für :company', ['company' => $selectedCompany->name]) }} —
|
||
@endif
|
||
{{ __('Hier sehen Sie Status und Reichweite Ihres Kundenkontos für presseecho und businessportal24.') }}
|
||
</p>
|
||
</div>
|
||
|
||
<div class="flex items-center gap-3 flex-shrink-0">
|
||
@if ($selectedCompany)
|
||
<span class="badge hub dot">
|
||
{{ __('Aktive Firma:') }} <strong class="font-semibold">{{ $selectedCompany->name }}</strong>
|
||
</span>
|
||
@else
|
||
<a href="{{ route('me.press-kits.index') }}" wire:navigate
|
||
class="inline-flex items-center gap-2 px-3 py-1.5 rounded-[4px] text-[12px] font-semibold whitespace-nowrap bg-[color:var(--color-warn-soft)] border border-[color:var(--color-warn)]/30 text-[color:var(--color-accent-deep)] hover:bg-[color:var(--color-warn-soft)]/80 transition">
|
||
<flux:icon.exclamation-triangle class="size-[13px] flex-shrink-0" />
|
||
{{ __('Keine Firma zugeordnet') }}
|
||
<span class="underline underline-offset-[3px] decoration-[color:var(--color-accent-deep)]/40">{{ __('zuordnen') }} →</span>
|
||
</a>
|
||
@endif
|
||
</div>
|
||
</header>
|
||
|
||
{{-- ============== STAT-CARDS — KPI-Reihe ============== --}}
|
||
<section class="grid grid-cols-2 gap-4 sm:grid-cols-4">
|
||
<x-portal.stat-card variant="primary" :label="__('Gesamt')" :value="$stats['total']">
|
||
<x-slot:meta>{{ now()->format('Y') }}</x-slot:meta>
|
||
<x-slot:trend>
|
||
@php
|
||
$delta = $stats['deltaMonth'];
|
||
$deltaLabel = $delta === 0
|
||
? __('keine Veränderung ggü. Vormonat')
|
||
: trans_choice(':sign:abs ggü. Vormonat', abs($delta), [
|
||
'sign' => $delta > 0 ? '+' : '−',
|
||
'abs' => abs($delta),
|
||
]);
|
||
@endphp
|
||
<span class="flex items-center gap-1">
|
||
@if ($delta > 0)
|
||
<flux:icon.arrow-trending-up class="size-[11px] text-[color:var(--color-ok)]" />
|
||
@elseif ($delta < 0)
|
||
<flux:icon.arrow-trending-down class="size-[11px] text-[color:var(--color-loss)]" />
|
||
@else
|
||
<flux:icon.minus class="size-[11px] text-[color:var(--color-ink-4)]" />
|
||
@endif
|
||
{{ $deltaLabel }}
|
||
</span>
|
||
</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
|
||
<x-portal.stat-card variant="ok" :label="__('Veröffentlicht')" :value="$stats['published']">
|
||
<x-slot:meta>
|
||
<span class="badge ok" style="font-size:9.5px;padding:1px 6px;">{{ __('live') }}</span>
|
||
</x-slot:meta>
|
||
<x-slot:trend>{{ __('auf beiden Portalen') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
|
||
<x-portal.stat-card variant="warn" :label="__('In Prüfung')" :value="$stats['review']">
|
||
<x-slot:meta>{{ __('Ø 4 h') }}</x-slot:meta>
|
||
<x-slot:trend>{{ __('redaktionelle Prüfung') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
|
||
<x-portal.stat-card variant="muted" :label="__('Entwürfe')" :value="$stats['draft']">
|
||
<x-slot:meta>{{ __('privat') }}</x-slot:meta>
|
||
<x-slot:trend>{{ __('gespeichert, nicht eingereicht') }}</x-slot:trend>
|
||
</x-portal.stat-card>
|
||
</section>
|
||
|
||
{{-- ============== ZWEISPALTEN-GRID ============== --}}
|
||
<section class="grid gap-6 lg:grid-cols-[2fr_1fr]">
|
||
|
||
{{-- LINKS: Pressemitteilungen-Liste / Empty-State --}}
|
||
<article class="panel">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Meine letzten Pressemitteilungen') }}</span>
|
||
<div class="flex items-center gap-3">
|
||
<span class="text-[11.5px] text-[color:var(--color-ink-3)]">
|
||
{{ $recent->count() }} {{ __('von') }} {{ $stats['total'] }}
|
||
</span>
|
||
<a href="{{ route('me.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>
|
||
</div>
|
||
|
||
@forelse ($recent as $pr)
|
||
@php
|
||
$badgeClass = match ($pr->status->value) {
|
||
'published' => 'ok',
|
||
'review' => 'warn',
|
||
'rejected' => 'err',
|
||
'archived', 'draft' => 'muted',
|
||
default => 'hub',
|
||
};
|
||
$portal = $pr->portal?->value ?? 'both';
|
||
$showPe = in_array($portal, ['presseecho', 'both'], true);
|
||
$showBp = in_array($portal, ['businessportal24', 'both'], true);
|
||
$primaryDate = $pr->status === PressReleaseStatus::Published && $pr->published_at
|
||
? $pr->published_at
|
||
: $pr->created_at;
|
||
@endphp
|
||
<a href="{{ route('me.press-releases.show', $pr->id) }}" wire:navigate
|
||
class="flex items-center justify-between gap-4 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">
|
||
PM-{{ $pr->id }}
|
||
<span class="text-[color:var(--color-ink-4)] mx-1">·</span>
|
||
{{ $pr->company?->name ?? __('Ohne Firma') }}
|
||
<span class="text-[color:var(--color-ink-4)] mx-1">·</span>
|
||
{{ $primaryDate?->format('d.m.Y') }}
|
||
</p>
|
||
</div>
|
||
<div class="flex items-center gap-1.5 shrink-0">
|
||
@if ($showPe)
|
||
<span class="portal-pill pe"><span class="pdot"></span>presseecho</span>
|
||
@endif
|
||
@if ($showBp)
|
||
<span class="portal-pill bp"><span class="pdot"></span>businessportal24</span>
|
||
@endif
|
||
<span class="badge {{ $badgeClass }} dot">{{ $pr->status->label() }}</span>
|
||
</div>
|
||
</a>
|
||
@empty
|
||
<div class="px-10 py-14 flex flex-col items-center text-center">
|
||
<div class="w-16 h-16 rounded-[6px] flex items-center justify-center mb-5 relative
|
||
bg-[color:var(--color-hub-soft)] border border-[color:var(--color-hub-soft-2)] text-[color:var(--color-hub)]">
|
||
<flux:icon.newspaper class="size-7" />
|
||
<span class="absolute -top-1.5 -right-1.5 w-5 h-5 rounded-full text-[10px] font-bold flex items-center justify-center font-mono
|
||
bg-[color:var(--color-accent)] text-white">0</span>
|
||
</div>
|
||
<div class="text-[16px] font-semibold m-0 text-[color:var(--color-ink)]">
|
||
{{ __('Noch keine Pressemitteilungen') }}
|
||
</div>
|
||
<p class="text-[13px] leading-[1.55] mt-2 m-0 max-w-[460px] text-[color:var(--color-ink-3)]">
|
||
{{ __('Starten Sie mit einer ersten Mitteilung für die aktive Firma oder Ihr Kundenkonto. Veröffentlichung erfolgt nach redaktioneller Prüfung auf beiden Portalen.') }}
|
||
</p>
|
||
<div class="mt-6 flex items-center gap-2.5">
|
||
<flux:button variant="primary" icon="plus" href="{{ route('me.press-releases.create') }}" wire:navigate>
|
||
{{ __('Erste Pressemitteilung erstellen') }}
|
||
</flux:button>
|
||
</div>
|
||
|
||
<div class="mt-9 grid gap-3 w-full max-w-[560px] grid-cols-1 sm:grid-cols-3">
|
||
@foreach ([
|
||
['num' => '01', 'label' => __('Firma zuordnen')],
|
||
['num' => '02', 'label' => __('Mitteilung verfassen')],
|
||
['num' => '03', 'label' => __('Zur Prüfung senden')],
|
||
] as $step)
|
||
<div class="text-left px-3 py-2.5 rounded-[3px]
|
||
bg-[color:var(--color-bg-elev)] border border-[color:var(--color-bg-rule)]">
|
||
<div class="font-mono text-[9.5px] tracking-[0.16em] font-bold mb-1 text-[color:var(--color-accent-deep)]">
|
||
{{ $step['num'] }}
|
||
</div>
|
||
<div class="text-[11.5px] font-semibold leading-tight text-[color:var(--color-ink)]">
|
||
{{ $step['label'] }}
|
||
</div>
|
||
</div>
|
||
@endforeach
|
||
</div>
|
||
</div>
|
||
@endforelse
|
||
|
||
<div class="px-5 py-3 border-t border-[color:var(--color-bg-rule)] flex items-center gap-2.5 text-[11.5px]
|
||
bg-[color:var(--color-bg-elev)] text-[color:var(--color-ink-3)]">
|
||
<flux:icon.shield-check class="size-[12px] text-[color:var(--color-hub)] flex-shrink-0" />
|
||
{{ __('Tipp: Geprüfte Mitteilungen erscheinen i. d. R. binnen') }}
|
||
<strong class="font-semibold text-[color:var(--color-ink-2)]">{{ __('4 Stunden') }}</strong>
|
||
{{ __('werktags auf beiden Portalen.') }}
|
||
</div>
|
||
</article>
|
||
|
||
{{-- RECHTS: Datenqualität --}}
|
||
<article class="panel">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Datenqualität') }}</span>
|
||
@if (count($qualityHints) > 0)
|
||
<span class="badge warn dot">{{ count($qualityHints) }} {{ __('offen') }}</span>
|
||
@else
|
||
<span class="badge ok dot">{{ __('vollständig') }}</span>
|
||
@endif
|
||
</div>
|
||
|
||
<div class="px-5 py-5">
|
||
<p class="text-[12px] leading-[1.55] m-0 mb-4 text-[color:var(--color-ink-3)]">
|
||
{{ __('Diese Hinweise helfen, Ihr User Backend vollständig und sauber zu halten.') }}
|
||
</p>
|
||
|
||
@if (count($qualityHints) > 0)
|
||
<div class="space-y-3">
|
||
@foreach ($qualityHints as $hint)
|
||
<x-portal.hint-card
|
||
:icon="$hint['icon']"
|
||
:title="$hint['title']"
|
||
:percent="$hint['percent'] ?? null"
|
||
:href="$hint['href']"
|
||
:action="$hint['action']"
|
||
>
|
||
{{ $hint['description'] }}
|
||
</x-portal.hint-card>
|
||
@endforeach
|
||
</div>
|
||
@else
|
||
<div class="flex flex-col items-center text-center py-6">
|
||
<flux:icon.check-badge class="size-8 text-[color:var(--color-ok)] mb-2" />
|
||
<div class="text-[13px] font-semibold text-[color:var(--color-ink)]">
|
||
{{ __('Alles im grünen Bereich') }}
|
||
</div>
|
||
<p class="text-[11.5px] text-[color:var(--color-ink-3)] mt-1 m-0">
|
||
{{ __('Profil, Rechnungsadresse und Firmen-Daten sind vollständig.') }}
|
||
</p>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</article>
|
||
</section>
|
||
|
||
{{-- ============== UNTERER GRID: FIRMEN + BRAND-BRIDGE ============== --}}
|
||
<section class="grid gap-6 lg:grid-cols-[2fr_1fr]">
|
||
|
||
{{-- Firmen --}}
|
||
<article class="panel">
|
||
<div class="panel-head">
|
||
<span class="section-eyebrow">{{ __('Meine Firmen') }}</span>
|
||
<div class="flex items-center gap-3">
|
||
<span class="badge hub" style="font-size:9.5px;padding:1px 6px;">
|
||
{{ $companiesTotal }} {{ __('zugeordnet') }}
|
||
</span>
|
||
<a href="{{ route('me.press-kits.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 Firmen anzeigen') }} →
|
||
</a>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="p-6">
|
||
@if ($companies->isNotEmpty())
|
||
<div class="grid gap-3 grid-cols-1 md:grid-cols-2">
|
||
@foreach ($companies as $company)
|
||
<a href="{{ route('me.press-kits.show', $company->id) }}" wire:navigate
|
||
class="block relative rounded-[5px] p-5 transition-colors
|
||
bg-[color:var(--color-bg-elev)] border border-[color:var(--color-bg-rule)]
|
||
hover:border-[color:var(--color-hub)]/50">
|
||
<div class="flex items-center gap-3 mb-2">
|
||
<span class="w-10 h-10 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)] text-[12px] font-bold">
|
||
{{ \Illuminate\Support\Str::of($company->name)->take(2)->upper() }}
|
||
</span>
|
||
<div class="min-w-0">
|
||
<div class="text-[13.5px] font-semibold truncate text-[color:var(--color-ink)]">
|
||
{{ $company->name }}
|
||
</div>
|
||
<div class="text-[11px] mt-0.5 text-[color:var(--color-ink-3)]">
|
||
{{ __('Presse-Kit öffnen') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</a>
|
||
@endforeach
|
||
</div>
|
||
@if ($companiesTotal > $companies->count())
|
||
<div class="mt-4 text-[11.5px] leading-[1.5] text-[color:var(--color-ink-3)]">
|
||
{{ __('Die zehn neuesten Firmen werden hier als Vorschau angezeigt.') }}
|
||
<a href="{{ route('me.press-kits.index') }}" wire:navigate
|
||
class="font-semibold text-[color:var(--color-hub)] hover:underline underline-offset-[3px] decoration-[color:var(--color-hub)]/30">
|
||
{{ __('Zur vollständigen Firmenliste') }} →
|
||
</a>
|
||
</div>
|
||
@endif
|
||
@else
|
||
<div class="grid gap-3 grid-cols-1 md:grid-cols-2">
|
||
<div class="relative rounded-[5px] p-5 transition-colors
|
||
border border-dashed border-[color:var(--color-bg-rule)] hover:bg-[color:var(--color-bg-elev)]">
|
||
<div class="flex items-center gap-3 mb-3">
|
||
<span class="w-10 h-10 rounded-[4px] flex items-center justify-center
|
||
border border-[color:var(--color-bg-rule)] bg-[color:var(--color-bg-elev)]
|
||
text-[color:var(--color-ink-4)]">
|
||
<flux:icon.building-office class="size-[18px]" />
|
||
</span>
|
||
<div>
|
||
<div class="text-[13.5px] font-semibold text-[color:var(--color-ink-2)]">
|
||
{{ __('Firma hinzufügen') }}
|
||
</div>
|
||
<div class="text-[11px] mt-0.5 text-[color:var(--color-ink-3)]">
|
||
{{ __('Slot frei') }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<p class="text-[11.5px] leading-[1.5] m-0 text-[color:var(--color-ink-3)]">
|
||
{{ __('Pressestellen, für die Sie Mitteilungen erstellen — mit eigenem Logo, Kontaktperson und Themen-Tags.') }}
|
||
</p>
|
||
</div>
|
||
|
||
<div class="p-5 rounded-[5px]
|
||
bg-[color:var(--color-bg-elev)] border border-[color:var(--color-bg-rule)]">
|
||
<div class="eyebrow muted mb-2">{{ __('Hinweis') }}</div>
|
||
<div class="text-[13px] leading-[1.55] m-0 text-[color:var(--color-ink-2)]">
|
||
{{ __('Keine Firmen zugeordnet. Wenn hier eine Firma fehlen sollte, prüfen Sie bitte die Firmenverwaltung oder wenden Sie sich an den Support.') }}
|
||
</div>
|
||
<div class="mt-3">
|
||
<flux:button size="sm" variant="ghost" href="{{ route('me.press-kits.index') }}" wire:navigate>
|
||
{{ __('Firmen öffnen') }}
|
||
</flux:button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
@endif
|
||
</div>
|
||
</article>
|
||
|
||
{{-- Brand-Bridge (dark) --}}
|
||
<article class="panel-dark">
|
||
<div class="panel-head">
|
||
<span class="eyebrow on-dark">{{ __('Brand-Bridge') }}</span>
|
||
<span class="font-mono text-[10px] tracking-[0.14em] uppercase text-[color:var(--color-ink-on-dark-3)]">A · B</span>
|
||
</div>
|
||
|
||
<div class="px-5 py-5">
|
||
<div class="text-[12.5px] leading-[1.55] m-0 mb-4 text-[color:var(--color-ink-on-dark-2)]">
|
||
{{ __('Ein Konto, beide Portale — Veröffentlichungen werden parallel auf presseecho und businessportal24 ausgespielt.') }}
|
||
</div>
|
||
|
||
<div class="grid gap-3 grid-cols-2">
|
||
{{-- Konstantes dunkles Hub-Blau (Phase 5): bleibt in beiden Modi gleich. --}}
|
||
<div class="rounded-[4px] px-3.5 py-3 border bg-[color:var(--color-panel-dark-2)] border-white/5">
|
||
<div class="flex items-center gap-2 mb-1.5">
|
||
<span class="dot-pe"></span>
|
||
<span class="text-[11px] font-bold tracking-[0.14em] uppercase text-white/85">presseecho</span>
|
||
</div>
|
||
<div class="font-mono text-[15px] font-semibold text-white tabular-nums">
|
||
{{ __($bridgeStatus['presseecho']['state']) }}
|
||
</div>
|
||
<div class="text-[10.5px] mt-0.5 text-[color:var(--color-ink-on-dark-3)]">
|
||
{{ $bridgeStatus['presseecho']['subline'] }}
|
||
</div>
|
||
</div>
|
||
<div class="rounded-[4px] px-3.5 py-3 border bg-[color:var(--color-panel-dark-2)] border-white/5">
|
||
<div class="flex items-center gap-2 mb-1.5">
|
||
<span class="dot-bp"></span>
|
||
<span class="text-[11px] font-bold tracking-[0.14em] uppercase text-white/85">businessportal24</span>
|
||
</div>
|
||
<div class="font-mono text-[15px] font-semibold text-white tabular-nums">
|
||
{{ __($bridgeStatus['businessportal24']['state']) }}
|
||
</div>
|
||
<div class="text-[10.5px] mt-0.5 text-[color:var(--color-ink-on-dark-3)]">
|
||
{{ $bridgeStatus['businessportal24']['subline'] }}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<hr class="mt-5 mb-4 border-0 h-px bg-white/10" />
|
||
|
||
<div class="space-y-2 text-[11.5px] text-[color:var(--color-ink-on-dark-2)]">
|
||
<div class="flex items-center justify-between">
|
||
<span>{{ __('API-Status') }}</span>
|
||
<span class="flex items-center gap-1.5 text-white">
|
||
<span class="w-1.5 h-1.5 rounded-full bg-emerald-400 animate-pulse"></span>
|
||
{{ __('operational') }}
|
||
</span>
|
||
</div>
|
||
<div class="flex items-center justify-between">
|
||
<span>{{ __('Tarif') }}</span>
|
||
<span class="font-mono text-white">{{ __('Starter') }}</span>
|
||
</div>
|
||
</div>
|
||
</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 · Publisher-Hub</span>
|
||
<span class="flex items-center gap-5">
|
||
<a href="{{ route('me.security') }}" wire:navigate class="hover:text-[color:var(--color-hub)]">{{ __('Sicherheit') }}</a>
|
||
<a href="{{ route('me.tokens.index') }}" wire:navigate class="hover:text-[color:var(--color-hub)]">{{ __('API & Tokens') }}</a>
|
||
<a href="{{ route('me.profile') }}" wire:navigate class="hover:text-[color:var(--color-hub)]">{{ __('Profil') }}</a>
|
||
</span>
|
||
</footer>
|
||
</div>
|