Files
AFC-Demo/resources/views/livewire/companies/transactions.blade.php
T

602 lines
32 KiB
PHP
Raw Normal View History

2025-10-24 07:11:28 +02:00
<?php
use App\Models\Company;
use App\Models\Transaction;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Collection;
use Livewire\Attributes\Computed;
use Livewire\Attributes\Layout;
use Livewire\Volt\Component;
use Livewire\WithPagination;
new #[Layout('components.layouts.app')] class extends Component {
use WithPagination;
public Company $company;
public ?int $selectedTransactionId = null;
public string $status = 'all';
public string $channel = 'all';
protected int $perPage = 10;
/**
* @var array<string, array<string, mixed>>
*/
protected $queryString = [
'status' => ['except' => 'all'],
'channel' => ['except' => 'all'],
'page' => ['except' => 1],
'selectedTransactionId' => ['except' => null, 'as' => 'transaction'],
];
public function mount(Company $company, ?int $transaction = null): void
{
$this->company = $company;
if ($transaction) {
$this->selectedTransactionId = $this->baseQuery()
->whereKey($transaction)
->value('id');
}
if (! $this->selectedTransactionId) {
$this->selectedTransactionId = $this->baseQuery()
->orderByDesc('requires_review')
->orderByDesc('risk_score')
->orderByDesc('executed_at')
->value('id');
}
}
public function with(): array
{
return [
'title' => __('Unternehmenstransaktionen'),
];
}
public function selectTransaction(int $transactionId): void
{
$this->selectedTransactionId = $transactionId;
}
public function updatingStatus(): void
{
$this->resetPage();
}
public function updatedStatus(): void
{
$this->refreshSelection();
}
public function updatingChannel(): void
{
$this->resetPage();
}
public function updatedChannel(): void
{
$this->refreshSelection();
}
public function updatedPage(): void
{
$this->refreshSelection();
}
protected function refreshSelection(): void
{
$first = $this->filteredQuery()
->orderByDesc('requires_review')
->orderByDesc('risk_score')
->orderByDesc('executed_at')
->forPage($this->getPage(), $this->perPage)
->first();
$this->selectedTransactionId = $first?->id;
}
#[Computed]
public function statusOptions(): array
{
return [
'all' => __('Alle Status'),
Transaction::STATUS_TRUE_POSITIVE => __('Bestätigte Treffer'),
Transaction::STATUS_FALSE_POSITIVE => __('Fehlalarme'),
Transaction::STATUS_CLEARED => __('Freigegeben'),
];
}
#[Computed]
public function channelOptions(): array
{
$channels = $this->baseQuery()
->select('channel')
->distinct()
->orderBy('channel')
->pluck('channel')
->filter()
->values();
$options = [
'all' => __('Alle Kanäle'),
];
foreach ($channels as $channel) {
$options[$channel] = $channel;
}
return $options;
}
#[Computed]
public function metrics(): array
{
$baseQuery = $this->baseQuery();
$totalCount = (clone $baseQuery)->count();
$totalVolume = (clone $baseQuery)->sum('amount');
$openAlertsQuery = $this->baseQuery()->where('requires_review', true);
$openAlertsCount = (clone $openAlertsQuery)->count();
$openAlertsVolume = (clone $openAlertsQuery)->sum('amount');
$highRiskCount = $totalCount > 0
? $this->baseQuery()->where('risk_score', '>=', 80)->count()
: 0;
$last30DaysQuery = $this->baseQuery()->where('executed_at', '>=', now()->subDays(30));
$last30DaysCount = (clone $last30DaysQuery)->count();
$last30DaysVolume = (clone $last30DaysQuery)->sum('amount');
$statusBreakdown = (clone $baseQuery)
->selectRaw('status, COUNT(*) as total, COALESCE(SUM(amount), 0) as volume')
->groupBy('status')
->get()
->mapWithKeys(fn ($row) => [
$row->status => [
'count' => (int) $row->total,
'amount' => (float) $row->volume,
],
])
->toArray();
return [
'total_count' => $totalCount,
'total_volume' => $totalVolume,
'open_alerts' => [
'count' => $openAlertsCount,
'amount' => $openAlertsVolume,
],
'high_risk_share' => $totalCount > 0 ? (int) round(($highRiskCount / $totalCount) * 100) : 0,
'last_30_days' => [
'count' => $last30DaysCount,
'amount' => $last30DaysVolume,
],
'by_status' => $statusBreakdown,
];
}
#[Computed]
public function transactions(): LengthAwarePaginator
{
return $this->filteredQuery()
->with('company')
->orderByDesc('requires_review')
->orderByDesc('risk_score')
->orderByDesc('executed_at')
->paginate($this->perPage);
}
#[Computed]
public function selectedTransaction(): ?Transaction
{
$transaction = $this->transactions->firstWhere('id', $this->selectedTransactionId);
if (! $transaction && $this->selectedTransactionId) {
$transaction = $this->baseQuery()
->with('company')
->whereKey($this->selectedTransactionId)
->first();
}
return $transaction ?? $this->transactions->first();
}
#[Computed]
public function counterpartyHistory(): Collection
{
$selected = $this->selectedTransaction;
if (! $selected) {
return collect();
}
return $this->baseQuery()
->where('counterparty', $selected->counterparty)
->orderByDesc('executed_at')
->limit(6)
->get();
}
#[Computed]
public function recentAlerts(): Collection
{
return $this->baseQuery()
->where('requires_review', true)
->orderByDesc('executed_at')
->limit(5)
->get();
}
#[Computed]
public function actionChecklist(): array
{
$selected = $this->selectedTransaction;
$items = [
[
'title' => __('KYC- und Screening-Daten abgleichen'),
'description' => __('Prüfen Sie Unternehmens- und Gegenparteidaten gegen Sanktionslisten, PEP-Register und interne Sperrlisten.'),
],
[
'title' => __('Transaktionsverlauf analysieren'),
'description' => __('Bewerten Sie Häufigkeit, Muster und Gegenparteien der letzten Monate, um ungewöhnliche Aktivitäten zu erkennen.'),
],
[
'title' => __('Vier-Augen-Prinzip sicherstellen'),
'description' => __('Organisieren Sie eine Zweitprüfung und dokumentieren Sie alle Entscheidungen revisionssicher.'),
],
];
if ($selected?->status === Transaction::STATUS_TRUE_POSITIVE) {
array_unshift($items, [
'title' => __('Verdachtsmeldung vorbereiten'),
'description' => __('Erstellen Sie den Meldeentwurf für die FIU, sammeln Sie Belege und stellen Sie eine Eskalation sicher.'),
]);
}
return $items;
}
protected function filteredQuery(): Builder
{
return $this->baseQuery()
->when($this->status !== 'all', fn (Builder $query) => $query->where('status', $this->status))
->when($this->channel !== 'all', fn (Builder $query) => $query->where('channel', $this->channel));
}
protected function baseQuery(): Builder
{
return Transaction::query()->where('company_id', $this->company->id);
}
};
?>
@php
/** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $transactions */
$transactions = $this->transactions;
/** @var \App\Models\Transaction|null $selected */
$selected = $this->selectedTransaction;
$metrics = $this->metrics;
$statusOptions = $this->statusOptions;
$channelOptions = $this->channelOptions;
$counterpartyHistory = $this->counterpartyHistory;
$recentAlerts = $this->recentAlerts;
$actionChecklist = $this->actionChecklist;
$formatCurrency = static fn (float $value): string => number_format($value, 2, ',', '.') . ' €';
$formatAmount = static fn (float $value, string $currency): string => number_format($value, 2, ',', '.') . ' ' . $currency;
$statusStyles = [
Transaction::STATUS_TRUE_POSITIVE => 'bg-gradient-to-r from-rose-500/25 via-rose-500/10 to-rose-400/20 text-rose-700 dark:text-rose-200 border border-rose-500/30 shadow-[0_0_25px_-14px_rgba(244,63,94,0.85)]',
Transaction::STATUS_FALSE_POSITIVE => 'bg-gradient-to-r from-amber-400/25 via-amber-400/10 to-amber-300/20 text-amber-700 dark:text-amber-200 border border-amber-400/30 shadow-[0_0_25px_-14px_rgba(251,191,36,0.85)]',
Transaction::STATUS_CLEARED => 'bg-gradient-to-r from-emerald-400/25 via-emerald-400/10 to-emerald-300/20 text-emerald-700 dark:text-emerald-200 border border-emerald-400/30 shadow-[0_0_25px_-14px_rgba(52,211,153,0.85)]',
];
$statusCopy = [
Transaction::STATUS_TRUE_POSITIVE => __('Ein schwerwiegender Verdacht liegt vor. Priorisieren Sie die Eskalation und bereiten Sie eine Verdachtsmeldung vor.'),
Transaction::STATUS_FALSE_POSITIVE => __('Alarm konnte entkräftet werden. Dokumentieren Sie die Begründung und schließen Sie den Fall.'),
Transaction::STATUS_CLEARED => __('Keine Auffälligkeiten. Dokumentieren und archivieren Sie den Prüfschritt.'),
];
@endphp
<div class="flex flex-col gap-8">
<section class="rounded-3xl border border-slate-200/70 bg-white/95 p-8 shadow-sm backdrop-blur dark:border-slate-700/70 dark:bg-slate-900/90">
<div class="flex flex-wrap items-start justify-between gap-6">
<div class="max-w-2xl space-y-2">
<flux:link :href="route('transaction-review')" wire:navigate class="inline-flex items-center gap-2 text-sm font-semibold text-slate-500 hover:text-slate-700 dark:text-slate-300 dark:hover:text-white">
<flux:icon name="arrow-left" class="h-4 w-4" />
{{ __('Zurück zur Transaktionsprüfung') }}
</flux:link>
<flux:heading size="xl">
{{ $company->legal_name }}
</flux:heading>
<flux:text class="text-sm text-slate-500 dark:text-slate-300">
{{ $company->summary }}
</flux:text>
<div class="flex flex-wrap gap-2 text-xs text-slate-600 dark:text-slate-300">
<span class="inline-flex items-center gap-2 rounded-full bg-slate-100/60 px-3 py-1 font-medium uppercase tracking-wide text-slate-600 dark:bg-slate-800/70 dark:text-slate-200">
{{ __('Ticker') }} {{ $company->ticker }}
</span>
<span class="inline-flex items-center gap-2 rounded-full bg-indigo-100/60 px-3 py-1 font-medium uppercase tracking-wide text-indigo-600 dark:bg-indigo-500/20 dark:text-indigo-200">
{{ $company->sector }}
</span>
<span class="inline-flex items-center gap-2 rounded-full bg-emerald-100/60 px-3 py-1 font-medium uppercase tracking-wide text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-100">
{{ __('KYC-Risiko') }} {{ \Illuminate\Support\Str::title($company->kyc_risk_level) }}
</span>
</div>
</div>
<div class="grid grid-cols-2 gap-4 text-sm sm:grid-cols-3">
<div class="rounded-2xl border border-slate-200/70 bg-slate-50/80 p-4 dark:border-slate-700/70 dark:bg-slate-800/80">
<p class="text-xs font-semibold uppercase tracking-wide text-slate-400">{{ __('Transaktionen gesamt') }}</p>
<p class="mt-2 text-2xl font-semibold text-slate-900 dark:text-white">{{ $metrics['total_count'] }}</p>
<p class="text-xs text-slate-500 dark:text-slate-300">{{ __('Volumen') }} {{ $formatCurrency($metrics['total_volume']) }}</p>
</div>
<div class="rounded-2xl border border-emerald-400/40 bg-emerald-400/10 p-4 shadow-sm shadow-emerald-900/10">
<p class="text-xs font-semibold uppercase tracking-wide text-emerald-700 dark:text-emerald-200">{{ __('Offene Alerts') }}</p>
<p class="mt-2 text-2xl font-semibold text-emerald-900 dark:text-emerald-100">{{ $metrics['open_alerts']['count'] }}</p>
<p class="text-xs text-emerald-700 dark:text-emerald-100">{{ __('Volumen') }} {{ $formatCurrency($metrics['open_alerts']['amount']) }}</p>
</div>
<div class="rounded-2xl border border-indigo-400/40 bg-indigo-500/10 p-4 shadow-sm shadow-indigo-900/10">
<p class="text-xs font-semibold uppercase tracking-wide text-indigo-800 dark:text-indigo-200">{{ __('High-Risk-Anteil') }}</p>
<p class="mt-2 text-2xl font-semibold text-indigo-900 dark:text-indigo-100">{{ $metrics['high_risk_share'] }}%</p>
<p class="text-xs text-indigo-700 dark:text-indigo-200">{{ __('Letzte 30 Tage') }} {{ $metrics['last_30_days']['count'] }}</p>
</div>
</div>
</div>
</section>
<div class="grid gap-6 xl:grid-cols-[minmax(0,1.15fr)_minmax(0,1.85fr)]">
<div class="space-y-4">
<div class="rounded-3xl border border-zinc-200/70 bg-white/95 p-6 shadow-sm backdrop-blur dark:border-zinc-700/70 dark:bg-zinc-900/90">
<div class="flex flex-wrap items-center justify-between gap-4">
<div>
<h2 class="text-lg font-semibold text-zinc-900 dark:text-zinc-100">{{ __('Transaktionsliste') }}</h2>
<p class="text-sm text-zinc-500 dark:text-zinc-400">{{ __('Filtern Sie nach Status und Kanal, um relevante Vorgänge für dieses Unternehmen zu analysieren.') }}</p>
</div>
<flux:badge variant="outline">
{{ $transactions->total() }} {{ __('Ergebnisse') }}
</flux:badge>
</div>
<div class="mt-4 flex flex-wrap items-center gap-3">
<flux:select wire:model.live="status">
@foreach ($statusOptions as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
@endforeach
</flux:select>
<flux:select wire:model.live="channel">
@foreach ($channelOptions as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
@endforeach
</flux:select>
</div>
<div class="mt-6 space-y-3">
@forelse ($transactions as $transaction)
<button
wire:click="selectTransaction({{ $transaction->id }})"
wire:key="txn-{{ $transaction->id }}"
@class([
'w-full rounded-2xl border p-4 text-left transition focus:outline-none focus:ring-2 focus:ring-indigo-500/80',
'border-zinc-200/70 bg-white shadow-sm backdrop-blur dark:border-zinc-700/60 dark:bg-zinc-900/80' => $selected?->id !== $transaction->id,
'border-indigo-300/70 bg-indigo-50/80 shadow-md ring-2 ring-indigo-200/60 dark:border-indigo-500/40 dark:bg-indigo-900/50 dark:ring-indigo-500/30' => $selected?->id === $transaction->id,
])>
<div class="flex flex-wrap items-start justify-between gap-4">
<div class="space-y-1">
<p class="text-sm font-semibold text-zinc-800 dark:text-zinc-100">
{{ $transaction->counterparty }}
</p>
<p class="text-xs text-zinc-500 dark:text-zinc-400">
{{ $transaction->counterparty_country }} • {{ $transaction->executed_at?->format('d.m.Y H:i') }}
</p>
</div>
<div class="flex flex-col items-end gap-2">
<span class="text-lg font-semibold text-zinc-900 dark:text-zinc-100">
{{ $formatAmount($transaction->amount, $transaction->currency) }}
</span>
<span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {{ $statusStyles[$transaction->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20' }}">
{{ $transaction->statusLabel() }}
</span>
</div>
</div>
<div class="mt-3 flex flex-wrap items-center gap-2 text-xs text-zinc-500 dark:text-zinc-400">
<span class="inline-flex items-center gap-1 rounded-md bg-slate-100/60 px-2 py-1 font-semibold text-slate-700 dark:bg-slate-800/80 dark:text-slate-200">
Risiko {{ $transaction->risk_score }}
</span>
<span>{{ $transaction->channel }}</span>
<span>•</span>
<span class="font-mono">{{ $transaction->reference }}</span>
<flux:link
:href="route('company.transactions', ['company' => $company->id, 'transaction' => $transaction->id])"
wire:navigate
class="inline-flex items-center gap-1 font-medium text-indigo-600 transition hover:text-indigo-800 dark:text-indigo-300 dark:hover:text-indigo-200"
>
{{ __('Fallansicht öffnen') }}
<flux:icon name="arrow-top-right-on-square" class="h-4 w-4" />
</flux:link>
</div>
</button>
@empty
<div class="rounded-2xl border border-dashed border-zinc-300/70 bg-zinc-50/70 p-6 text-center text-sm text-zinc-500 dark:border-zinc-600/50 dark:bg-zinc-900/70 dark:text-zinc-300">
{{ __('Keine Transaktionen für die aktuellen Filter gefunden.') }}
</div>
@endforelse
</div>
@if ($transactions->hasPages())
<div class="mt-4 flex items-center justify-between gap-3 border-t border-zinc-200/70 bg-zinc-50/70 px-4 py-3 text-sm text-zinc-500 dark:border-zinc-700/60 dark:bg-zinc-900/70 dark:text-zinc-300">
<button
wire:click="previousPage"
wire:loading.attr="disabled"
@disabled($transactions->onFirstPage())
class="inline-flex items-center gap-2 rounded-full border border-zinc-300/70 bg-white/70 px-4 py-1.5 font-medium text-zinc-700 transition hover:bg-white dark:border-zinc-600/70 dark:bg-zinc-800/80 dark:text-zinc-200 dark:hover:bg-zinc-800/60"
>
{{ __('Zurück') }}
</button>
<span class="text-xs font-medium uppercase tracking-wide text-zinc-500 dark:text-zinc-400">
{{ __('Seite') }} {{ $transactions->currentPage() }} {{ __('von') }} {{ $transactions->lastPage() }}
</span>
<button
wire:click="nextPage"
wire:loading.attr="disabled"
@disabled(! $transactions->hasMorePages())
class="inline-flex items-center gap-2 rounded-full border border-zinc-300/70 bg-white/70 px-4 py-1.5 font-medium text-zinc-700 transition hover:bg-white dark:border-zinc-600/70 dark:bg-zinc-800/80 dark:text-zinc-200 dark:hover:bg-zinc-800/60"
>
{{ __('Weiter') }}
</button>
</div>
@endif
</div>
<div class="rounded-3xl border border-zinc-200/70 bg-white/95 p-6 shadow-sm backdrop-blur dark:border-zinc-700/70 dark:bg-zinc-900/90">
<h3 class="text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{{ __('Neueste Alerts') }}</h3>
<ul class="mt-4 space-y-3 text-sm text-zinc-600 dark:text-zinc-300">
@forelse ($recentAlerts as $alert)
<li class="flex items-start justify-between gap-3 rounded-2xl border border-zinc-100/70 bg-zinc-50/70 p-3 dark:border-zinc-700/70 dark:bg-zinc-800/70">
<div>
<p class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $alert->counterparty }}</p>
<p class="text-xs text-zinc-500 dark:text-zinc-400">{{ $alert->executed_at?->format('d.m.Y H:i') }} • {{ $alert->channel }}</p>
</div>
<span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {{ $statusStyles[$alert->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20' }}">
{{ $alert->statusLabel() }}
</span>
</li>
@empty
<li class="rounded-2xl border border-dashed border-zinc-300/60 bg-zinc-50/60 p-4 text-center text-xs text-zinc-500 dark:border-zinc-600/50 dark:bg-zinc-900/60">
{{ __('Aktuell liegen keine offenen Alerts vor.') }}
</li>
@endforelse
</ul>
</div>
</div>
<div class="space-y-5">
@if ($selected)
<div class="rounded-3xl border border-slate-200/70 bg-white/95 p-7 shadow-lg shadow-slate-900/5 backdrop-blur dark:border-slate-700/70 dark:bg-slate-900/90">
<div class="flex flex-wrap items-start justify-between gap-6">
<div class="space-y-2">
<flux:badge variant="outline">
{{ __('Case ID') }} {{ $selected->id }}
</flux:badge>
<h3 class="text-2xl font-semibold text-slate-900 dark:text-white">
{{ $selected->counterparty }} — {{ $formatAmount($selected->amount, $selected->currency) }}
</h3>
<p class="text-sm text-slate-500 dark:text-slate-300">
{{ __('Ausgeführt am') }} {{ $selected->executed_at?->format('d.m.Y, H:i') }} • {{ $selected->channel }}
</p>
</div>
<div class="flex flex-col items-end gap-2">
@php($badgeStyle = $statusStyles[$selected->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20')
<span class="inline-flex items-center gap-2 rounded-full px-3 py-1 text-xs font-semibold {{ $badgeStyle }}">
{{ $selected->statusLabel() }}
</span>
<span class="inline-flex items-center gap-1 rounded-md bg-slate-900/10 px-3 py-1 font-semibold text-slate-800 dark:bg-slate-200/10 dark:text-slate-200">
{{ __('Risikowert') }} {{ $selected->risk_score }}
</span>
</div>
</div>
<div class="mt-6 grid gap-4 sm:grid-cols-2">
<div class="rounded-2xl bg-slate-50/80 p-4 dark:bg-slate-800/70">
<p class="text-xs uppercase tracking-wide text-slate-400">{{ __('Gegenpartei') }}</p>
<p class="mt-1 text-sm font-medium text-slate-900 dark:text-slate-100">{{ $selected->counterparty }}</p>
<p class="text-xs text-slate-500 dark:text-slate-300">{{ $selected->counterparty_country }}</p>
</div>
<div class="rounded-2xl bg-slate-50/80 p-4 dark:bg-slate-800/70">
<p class="text-xs uppercase tracking-wide text-slate-400">{{ __('Referenz') }}</p>
<p class="mt-1 text-sm font-medium text-slate-900 dark:text-slate-100">{{ $selected->reference }}</p>
<p class="text-xs text-slate-500 dark:text-slate-300">{{ $selected->flagged_by }}</p>
</div>
<div class="rounded-2xl bg-slate-50/80 p-4 dark:bg-slate-800/70">
<p class="text-xs uppercase tracking-wide text-slate-400">{{ __('Flagging-Grund') }}</p>
<p class="mt-1 text-sm font-medium text-slate-900 dark:text-slate-100">{{ $selected->flagged_reason }}</p>
</div>
<div class="rounded-2xl bg-slate-50/80 p-4 dark:bg-slate-800/70">
<p class="text-xs uppercase tracking-wide text-slate-400">{{ __('Empfohlene Maßnahme') }}</p>
<p class="mt-1 text-sm font-medium text-slate-900 dark:text-slate-100">{{ $statusCopy[$selected->status] ?? __('Review abschließen und Audit-Trail ergänzen.') }}</p>
</div>
</div>
@if (! empty($selected->signals))
<div class="mt-6">
<h4 class="text-xs font-semibold uppercase tracking-wide text-slate-400">{{ __('Detektionssignale') }}</h4>
<div class="mt-2 flex flex-wrap gap-2">
@foreach ($selected->signals as $signal)
<span class="inline-flex items-center gap-2 rounded-full border border-slate-300/70 bg-slate-100/60 px-3 py-1 text-xs font-medium text-slate-700 dark:border-slate-600 dark:bg-slate-800/60 dark:text-slate-200">
<span class="h-1.5 w-1.5 rounded-full bg-emerald-500"></span>
{{ data_get($signal, 'type') }} — {{ data_get($signal, 'value') }}
</span>
@endforeach
</div>
</div>
@endif
<div class="mt-6 rounded-2xl border border-indigo-200/60 bg-indigo-500/10 p-4 text-sm text-indigo-900 dark:border-indigo-500/30 dark:bg-indigo-500/10 dark:text-indigo-100">
<p class="font-medium">{{ __('Fallleitlinie') }}</p>
<p class="mt-1 leading-relaxed">
{{ __('Führen Sie eine ganzheitliche Bewertung durch: prüfen Sie Geschäftsbeziehung, Transaktionshistorie und Monitoring-Regeln. Stellen Sie sicher, dass alle Entscheidungswege und Maßnahmen dokumentiert und für Audits nachvollziehbar sind.') }}
</p>
</div>
</div>
<div class="rounded-3xl border border-zinc-200/70 bg-white/95 p-6 shadow-sm backdrop-blur dark:border-zinc-700/70 dark:bg-zinc-900/90">
<h4 class="text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{{ __('Aktions-Checkliste') }}</h4>
<ol class="mt-4 space-y-3 text-sm text-zinc-600 dark:text-zinc-300">
@foreach ($actionChecklist as $index => $item)
<li class="flex gap-3 rounded-2xl border border-zinc-100/70 bg-zinc-50/70 p-4 dark:border-zinc-700/70 dark:bg-zinc-800/70">
<span class="mt-1 inline-flex h-6 w-6 shrink-0 items-center justify-center rounded-full bg-indigo-500/10 font-semibold text-indigo-600 dark:bg-indigo-500/20 dark:text-indigo-200">
{{ $index + 1 }}
</span>
<div>
<p class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $item['title'] }}</p>
<p class="text-xs text-zinc-500 dark:text-zinc-400">{{ $item['description'] }}</p>
</div>
</li>
@endforeach
</ol>
</div>
<div class="rounded-3xl border border-zinc-200/70 bg-white/95 p-6 shadow-sm backdrop-blur dark:border-zinc-700/70 dark:bg-zinc-900/90">
<h4 class="text-sm font-semibold uppercase tracking-wide text-zinc-500 dark:text-zinc-400">{{ __('Historie mit dieser Gegenpartei') }}</h4>
<div class="mt-4 space-y-3">
@forelse ($counterpartyHistory as $history)
<div class="flex flex-wrap items-center justify-between gap-3 rounded-2xl border border-zinc-100/70 bg-zinc-50/70 p-4 text-sm text-zinc-600 dark:border-zinc-700/70 dark:bg-zinc-800/70 dark:text-zinc-300">
<div>
<p class="font-semibold text-zinc-900 dark:text-zinc-100">{{ $history->executed_at?->format('d.m.Y H:i') }}</p>
<p class="text-xs text-zinc-500 dark:text-zinc-400">{{ $history->channel }} • {{ $history->reference }}</p>
</div>
<div class="flex flex-col items-end gap-1">
<span class="text-sm font-semibold text-zinc-900 dark:text-zinc-100">{{ $formatAmount($history->amount, $history->currency) }}</span>
<span class="inline-flex items-center gap-1 rounded-md bg-slate-100/60 px-2 py-0.5 text-xs font-semibold text-slate-700 dark:bg-slate-800/80 dark:text-slate-200">
{{ __('Risiko') }} {{ $history->risk_score }}
</span>
</div>
</div>
@empty
<div class="rounded-2xl border border-dashed border-zinc-300/60 bg-zinc-50/60 p-4 text-center text-xs text-zinc-500 dark:border-zinc-600/50 dark:bg-zinc-900/60">
{{ __('Keine weiteren Transaktionen mit dieser Gegenpartei gefunden.') }}
</div>
@endforelse
</div>
</div>
@else
<div class="rounded-3xl border border-dashed border-zinc-300/70 bg-white/80 p-8 text-center text-sm text-zinc-500 shadow-sm dark:border-zinc-600/50 dark:bg-zinc-900/60 dark:text-zinc-300">
{{ __('Wählen Sie links eine Transaktion aus, um die vollständige Fallansicht zu öffnen.') }}
</div>
@endif
</div>
</div>
</div>