update transaction view

This commit is contained in:
Bob Molitor
2025-10-20 21:06:01 +02:00
parent d17b90bcb9
commit 2f7432c64d
3 changed files with 94 additions and 49 deletions
+21 -21
View File
@@ -50,35 +50,35 @@ class DaxCompanySeeder extends Seeder
];
$channels = [
'SEPA Credit Transfer',
'International Wire',
'SEPA-Überweisung',
'Auslandsüberweisung',
'SWIFT MT103',
'Cross-Border ACH',
'High-Value Clearing',
'Trade Finance Settlement',
'Grenzüberschreitendes ACH',
'Hochwertiges Clearing',
'Handelsfinanzierungs-Abwicklung',
];
$flaggedReasons = [
'Counterparty appeared on consolidated sanctions screening list.',
'Transaction amount exceeded historical average by 300%.',
'Unusual payment corridor for the industry segment.',
'Multiple layered transfers flagged by transaction monitoring.',
'Beneficiary bank located in heightened risk jurisdiction.',
'Rapid movement of funds through nested accounts.',
'Adverse media match with counterparty executive.',
'Gegenpartei erscheint auf konsolidierter Sanktionsliste.',
'Transaktionsbetrag liegt 300 % über dem historischen Durchschnitt.',
'Unüblicher Zahlungskorridor für das Branchensegment.',
'Mehrere gestapelte Überweisungen im Monitoring erkannt.',
'Begünstigte Bank sitzt in Hochrisiko-Jurisdiktion.',
'Schnelle Mittelbewegungen über verschachtelte Konten.',
'Negativmeldung zur Geschäftsleitung der Gegenpartei gefunden.',
];
$analysts = ['AML Radar', 'Sanctions Sentinel', 'Behavioral Analytics', 'Network Intelligence', 'Adverse Media Scan'];
$analysts = ['AML-Radar', 'Sanktionswächter', 'Verhaltensanalyse', 'Netzwerk-Intelligence', 'Adverse-Media-Scan'];
$signalCatalog = [
['type' => 'Watchlist Match', 'value' => 'EU Consolidated List'],
['type' => 'PEP Screening', 'value' => 'Tier 2 Politically Exposed Person'],
['type' => 'Geo Risk', 'value' => 'High-risk jurisdiction: TR'],
['type' => 'Velocity', 'value' => '6 linked payments within 24h'],
['type' => 'Structuring', 'value' => 'Smurfing pattern detected'],
['type' => 'Negative News', 'value' => 'Recent fraud investigation reported'],
['type' => 'Beneficial Ownership', 'value' => 'Opaque corporate structure'],
['type' => 'Industry Deviation', 'value' => 'Trade value outside peer median by 210%'],
['type' => 'Sanktionsliste', 'value' => 'Treffer EU-Konsolidierte Liste'],
['type' => 'PEP-Screening', 'value' => 'Politisch exponierte Person, Stufe 2'],
['type' => 'Geo-Risiko', 'value' => 'Hochrisikoland: TR'],
['type' => 'Transaktionsdichte', 'value' => '6 verknüpfte Zahlungen binnen 24h'],
['type' => 'Strukturierung', 'value' => 'Hinweise auf Smurfing-Muster'],
['type' => 'Negativnachrichten', 'value' => 'Aktuelle Betrugsermittlungen gemeldet'],
['type' => 'Wirtschaftlich Berechtigter', 'value' => 'Intransparente Eigentümerstruktur'],
['type' => 'Branchenabweichung', 'value' => 'Handelswert 210 % über Peergroup'],
];
$statusPool = [
+1 -1
View File
@@ -1,5 +1,5 @@
{
"name": "frontend",
"name": "trusted_ai",
"lockfileVersion": 3,
"requires": true,
"packages": {
@@ -174,60 +174,101 @@ new #[Layout('components.layouts.app')] class extends Component {
$total = $metrics['total'];
$statusStyles = [
Transaction::STATUS_TRUE_POSITIVE => 'bg-rose-500/15 text-rose-700 dark:text-rose-300 border border-rose-500/30',
Transaction::STATUS_FALSE_POSITIVE => 'bg-amber-500/15 text-amber-700 dark:text-amber-300 border border-amber-500/30',
Transaction::STATUS_CLEARED => 'bg-emerald-500/15 text-emerald-700 dark:text-emerald-300 border border-emerald-500/30',
Transaction::STATUS_TRUE_POSITIVE => 'bg-gradient-to-r from-rose-500/25 via-rose-500/10 to-rose-400/25 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/25 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/25 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 => 'Zur Sonderprüfung eskalieren und Ermittlungsakte anlegen.',
Transaction::STATUS_FALSE_POSITIVE => 'Anomalie begründen, Dokumentation ergänzen und Vorgang schließen.',
Transaction::STATUS_TRUE_POSITIVE => 'Sofortige Sonderprüfung einleiten und Ermittlungsakte anlegen.',
Transaction::STATUS_FALSE_POSITIVE => 'Anomalie begründen, Dokumentation ergänzen und Alarm schließen.',
Transaction::STATUS_CLEARED => 'Review-Ergebnis dokumentieren und Kontrollnachweis archivieren.',
];
$statusLegend = [
Transaction::STATUS_TRUE_POSITIVE => [
'title' => 'Bestätigter Treffer',
'description' => 'Ermittlungsindikatoren bestätigen einen hinreichenden Verdacht. Fallverantwortliche Person informieren, Belege sichern und Maßnahmenplan starten.',
'accent' => 'text-rose-500 dark:text-rose-300',
],
Transaction::STATUS_FALSE_POSITIVE => [
'title' => 'Fehlalarm',
'description' => 'Auslösung plausibel widerlegt (z. B. KYC-Daten, Geschäftslogik). Entscheidungsgrund dokumentieren und Regeloptimierung anstoßen.',
'accent' => 'text-amber-500 dark:text-amber-200',
],
Transaction::STATUS_CLEARED => [
'title' => 'Freigegeben',
'description' => 'Keine verdächtigen Auffälligkeiten. Abschluss mit Vier-Augen-Freigabe und revisionssicherer Ablage des Prüfpfads.',
'accent' => 'text-emerald-500 dark:text-emerald-300',
],
];
@endphp
<div class="flex flex-col gap-6">
<div class="rounded-3xl border border-slate-200/60 bg-gradient-to-r from-slate-900 via-slate-800 to-slate-900 p-8 text-white shadow-xl dark:border-slate-700/60 dark:from-slate-950 dark:via-slate-900 dark:to-slate-950">
<div class="flex flex-wrap items-end justify-between gap-6">
<div class="space-y-2 max-w-xl">
<div class="relative overflow-hidden rounded-3xl border border-slate-200/60 bg-slate-950/95 p-8 text-white shadow-[0_30px_80px_-45px_rgba(15,23,42,0.9)] dark:border-slate-800/60">
<span class="pointer-events-none absolute -left-24 top-1/2 aspect-square w-72 -translate-y-1/2 rounded-full bg-sky-500/15 blur-3xl"></span>
<span class="pointer-events-none absolute -right-16 -top-16 aspect-square w-64 rounded-full bg-indigo-500/15 blur-3xl"></span>
<div class="relative flex flex-wrap items-end justify-between gap-6">
<div class="max-w-xl space-y-3">
<p class="inline-flex items-center gap-2 rounded-full border border-white/25 bg-white/10 px-3 py-1 text-xs font-semibold uppercase tracking-[0.18em] text-slate-200 shadow-sm">
AFC-Kontrollzentrum
</p>
<h1 class="text-2xl font-semibold tracking-tight sm:text-3xl">Finanzkriminalitäts-Workbench</h1>
<p class="text-sm text-slate-300 sm:text-base">
Überwachen Sie verdächtige Transaktionen im DAX-Portfolio und steuern Sie das manuelle Review nahtlos.
<p class="text-sm leading-relaxed text-slate-300 sm:text-base">
Überwachen Sie verdächtige Transaktionen im DAX-Portfolio, priorisieren Sie Alarme und orchestrieren Sie das manuelle Review im Vier-Augen-Prozess.
</p>
</div>
<div class="grid grid-cols-2 gap-4 text-sm sm:text-base md:grid-cols-4">
<div class="rounded-2xl border border-white/10 bg-white/10 p-4 text-center backdrop-blur">
<div class="rounded-2xl border border-white/10 bg-white/10 p-4 text-center backdrop-blur-sm shadow-lg shadow-slate-900/30">
<p class="text-xs font-medium uppercase tracking-wide text-slate-200">Gesamtfälle</p>
<p class="text-2xl font-semibold text-white">{{ $total['count'] }}</p>
<p class="mt-1 text-xs text-slate-300">Volumen: {{ $formatAmount($total['amount']) }}</p>
</div>
<div class="rounded-2xl border border-rose-400/40 bg-rose-500/10 p-4 text-center backdrop-blur">
<p class="text-xs font-medium uppercase tracking-wide text-rose-100">Bestätigte Treffer</p>
<p class="text-2xl font-semibold text-rose-200">{{ $confirmed['count'] }}</p>
<p class="mt-1 text-xs text-rose-100">Volumen: {{ $formatAmount($confirmed['amount']) }}</p>
<div class="rounded-2xl border border-rose-400/40 bg-rose-500/15 p-4 text-center backdrop-blur-sm shadow-lg shadow-rose-900/40">
<p class="text-xs font-medium uppercase tracking-wide text-rose-50">Bestätigte Treffer</p>
<p class="text-2xl font-semibold text-rose-100">{{ $confirmed['count'] }}</p>
<p class="mt-1 text-xs text-rose-100/80">Volumen: {{ $formatAmount($confirmed['amount']) }}</p>
</div>
<div class="rounded-2xl border border-amber-400/40 bg-amber-500/10 p-4 text-center backdrop-blur">
<p class="text-xs font-medium uppercase tracking-wide text-amber-100">Fehlalarme</p>
<div class="rounded-2xl border border-amber-400/40 bg-amber-400/15 p-4 text-center backdrop-blur-sm shadow-lg shadow-amber-900/30">
<p class="text-xs font-medium uppercase tracking-wide text-amber-50">Fehlalarme</p>
<p class="text-2xl font-semibold text-amber-100">{{ $falseAlarm['count'] }}</p>
<p class="mt-1 text-xs text-amber-100">Volumen: {{ $formatAmount($falseAlarm['amount']) }}</p>
<p class="mt-1 text-xs text-amber-100/80">Volumen: {{ $formatAmount($falseAlarm['amount']) }}</p>
</div>
<div class="rounded-2xl border border-emerald-400/40 bg-emerald-500/10 p-4 text-center backdrop-blur">
<p class="text-xs font-medium uppercase tracking-wide text-emerald-100">Freigegeben</p>
<div class="rounded-2xl border border-emerald-400/40 bg-emerald-400/15 p-4 text-center backdrop-blur-sm shadow-lg shadow-emerald-900/30">
<p class="text-xs font-medium uppercase tracking-wide text-emerald-50">Freigegeben</p>
<p class="text-2xl font-semibold text-emerald-100">{{ $cleared['count'] }}</p>
<p class="mt-1 text-xs text-emerald-100">Volumen: {{ $formatAmount($cleared['amount']) }}</p>
<p class="mt-1 text-xs text-emerald-100/80">Volumen: {{ $formatAmount($cleared['amount']) }}</p>
</div>
</div>
</div>
</div>
<div class="grid gap-4 rounded-3xl border border-zinc-200/60 bg-white/95 p-6 shadow-sm backdrop-blur dark:border-zinc-700/70 dark:bg-zinc-900/90 md:grid-cols-3">
@foreach ($statusLegend as $key => $legend)
<div class="flex flex-col gap-3 rounded-2xl border border-zinc-100/60 bg-slate-50/80 p-4 shadow-sm transition hover:-translate-y-0.5 hover:shadow-md dark:border-zinc-700/60 dark:bg-zinc-800/70">
<div class="flex items-center gap-2">
<span class="inline-flex h-2.5 w-2.5 rounded-full bg-gradient-to-r @class([
'from-rose-500 to-rose-400' => $key === Transaction::STATUS_TRUE_POSITIVE,
'from-amber-400 to-amber-300' => $key === Transaction::STATUS_FALSE_POSITIVE,
'from-emerald-400 to-emerald-300' => $key === Transaction::STATUS_CLEARED,
])"></span>
<p class="text-sm font-semibold {{ $legend['accent'] }}">{{ $legend['title'] }}</p>
</div>
<p class="text-sm leading-relaxed text-zinc-600 dark:text-zinc-300">
{{ $legend['description'] }}
</p>
</div>
@endforeach
</div>
<div class="flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between">
<div class="space-y-1">
<h2 class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Aktive Alert-Warteschlange</h2>
<h2 class="text-xl font-semibold text-zinc-900 dark:text-zinc-100">Aktive Prüfwarteschlange</h2>
<p class="text-sm text-zinc-500 dark:text-zinc-400">
Alle untenstehenden Meldungen benötigen eine finale Analystenentscheidung.
</p>
</div>
<div class="flex w-full flex-col gap-3 sm:w-auto sm:flex-row">
<div class="flex w-full flex-col gap-3 sm:w-auto sm:flex-row sm:items-center">
<flux:select wire:model.live="status" class="w-full sm:w-56">
@foreach ($this->statusOptions as $value => $label)
<option value="{{ $value }}">{{ $label }}</option>
@@ -258,10 +299,10 @@ new #[Layout('components.layouts.app')] class extends Component {
<button
wire:click="selectTransaction({{ $transaction->id }})"
type="button"
class="flex w-full flex-col gap-3 px-4 py-4 text-left transition sm:grid sm:grid-cols-[1.6fr_1fr_0.7fr]"
class="flex w-full flex-col gap-3 px-4 py-4 text-left transition duration-200 sm:grid sm:grid-cols-[1.6fr_1fr_0.7fr]"
@class([
'bg-indigo-500/10 dark:bg-indigo-500/20 border-s-4 border-indigo-500/80',
'hover:bg-zinc-50/80 dark:hover:bg-zinc-800/70 border-s-transparent' => ! $selectedState,
'border-s-4 border-indigo-500/80 bg-indigo-500/10 shadow-inner shadow-indigo-500/10 dark:bg-indigo-500/20' => $selectedState,
'border-s-4 border-transparent hover:-translate-y-0.5 hover:border-indigo-400/70 hover:bg-zinc-50/70 hover:shadow-lg hover:shadow-zinc-900/20 dark:hover:bg-zinc-800/60' => ! $selectedState,
])
>
<div class="space-y-2">
@@ -344,6 +385,10 @@ new #[Layout('components.layouts.app')] class extends Component {
<span class="inline-flex items-center rounded-full bg-slate-900/90 px-3 py-1 text-xs font-semibold uppercase tracking-wide text-white dark:bg-slate-200/20">
Prüfung erforderlich
</span>
@php($detailStatusStyle = $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 {{ $detailStatusStyle }}">
Status: {{ $selected->statusLabel() }}
</span>
<span class="text-3xl font-semibold text-slate-900 dark:text-white">
{{ number_format($selected->amount, 2, ',', '.') }} {{ $selected->currency }}
</span>
@@ -429,7 +474,7 @@ new #[Layout('components.layouts.app')] class extends Component {
</div>
@else
<div class="rounded-3xl border border-dashed border-zinc-300/70 bg-white/80 p-6 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 einen Alert aus, um den vollständigen Prüfkontext einzusehen.
Wählen Sie links einen Alarm aus, um den vollständigen Prüfkontext einzusehen.
</div>
@endif
</div>