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

602 lines
32 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 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>