diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index a54d83c..3862da7 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -76,7 +76,7 @@

{{ $formatNumber($averagePerAnalyst) }}

-

Prior 1 Fälle/p> +

Prior 1 Fälle

{{ $formatNumber($highRiskAlerts->count()) }}

diff --git a/resources/views/livewire/transaction-review.blade.php b/resources/views/livewire/transaction-review.blade.php index 8864078..4110e16 100644 --- a/resources/views/livewire/transaction-review.blade.php +++ b/resources/views/livewire/transaction-review.blade.php @@ -113,7 +113,7 @@ new #[Layout('components.layouts.app')] class extends Component { ->selectRaw('status, COUNT(*) AS total, COALESCE(SUM(amount), 0) AS volume') ->groupBy('status') ->get() - ->mapWithKeys(fn ($row) => [ + ->mapWithKeys(fn($row) => [ $row->status => [ 'count' => (int) $row->total, 'amount' => (float) $row->volume, @@ -148,7 +148,7 @@ new #[Layout('components.layouts.app')] class extends Component { $search = trim($this->search); return Transaction::query() - ->when($this->status !== 'all', fn (Builder $query) => $query->where('status', $this->status)) + ->when($this->status !== 'all', fn(Builder $query) => $query->where('status', $this->status)) ->when($search !== '', function (Builder $query) use ($search) { $like = '%' . $search . '%'; @@ -166,88 +166,88 @@ new #[Layout('components.layouts.app')] class extends Component { }; ?> @php - /** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $transactions */ - $transactions = $this->transactions; - /** @var \App\Models\Transaction|null $selected */ - $selected = $this->selectedTransaction; - $metrics = $this->metrics; - $byStatus = collect($metrics['by_status']); - $resolveMetrics = static fn (string $status): array => $byStatus->get($status, ['count' => 0, 'amount' => 0.0]); - $formatAmount = static fn (float $value): string => number_format($value, 2, ',', '.') . ' €'; +/** @var \Illuminate\Contracts\Pagination\LengthAwarePaginator $transactions */ +$transactions = $this->transactions; +/** @var \App\Models\Transaction|null $selected */ +$selected = $this->selectedTransaction; +$metrics = $this->metrics; +$byStatus = collect($metrics['by_status']); +$resolveMetrics = static fn (string $status): array => $byStatus->get($status, ['count' => 0, 'amount' => 0.0]); +$formatAmount = static fn (float $value): string => number_format($value, 2, ',', '.') . ' €'; - $confirmed = $resolveMetrics(Transaction::STATUS_TRUE_POSITIVE); - $falseAlarm = $resolveMetrics(Transaction::STATUS_FALSE_POSITIVE); - $cleared = $resolveMetrics(Transaction::STATUS_CLEARED); - $total = $metrics['total']; +$confirmed = $resolveMetrics(Transaction::STATUS_TRUE_POSITIVE); +$falseAlarm = $resolveMetrics(Transaction::STATUS_FALSE_POSITIVE); +$cleared = $resolveMetrics(Transaction::STATUS_CLEARED); +$total = $metrics['total']; - $statusStyles = [ - 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)]', - ]; +$statusStyles = [ +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 => '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.', - ]; - $statusSummaryPhrases = [ - Transaction::STATUS_TRUE_POSITIVE => [ - 'Verdachtsindikatoren bestätigen Treffer.', - 'Risikotreiber stehen weiter auf Rot.', - 'Sofortmaßnahmen priorisieren und dokumentieren.', - 'Ermittlungsteam unverzüglich koordinieren.', - 'Audit-Trail aktuell halten und teilen.', - ], - Transaction::STATUS_FALSE_POSITIVE => [ - 'Alarm widerlegt, Dokumentation abschließen.', - 'Risikoschwelle fällt unter interne Limits.', - 'Regelwerk für Fehlalarm analysieren.', - 'Stakeholder über Entwarnung informieren.', - 'Kontrollnachweis im Archiv sichern.', - ], - Transaction::STATUS_CLEARED => [ - 'Keine Auffälligkeiten, Vorgang freigegeben.', - 'Monitoringdaten bestätigen Referenzverhalten.', - 'Freigabe mit Vier-Augen-Prinzip dokumentieren.', - 'Compliance-Team über Abschluss informieren.', - 'Revisionsunterlagen vollständig archivieren.', - ], - ]; - $statusSummary = array_map( - static fn (array $phrases): string => $phrases[0] ?? 'Status gemäß aktuellem Prüfpfad.', - $statusSummaryPhrases - ); - $statusSummaryBanner = [ - Transaction::STATUS_TRUE_POSITIVE => [ - 'classes' => 'border border-rose-400/60 bg-gradient-to-r from-rose-500/20 via-rose-500/10 to-rose-400/20 text-rose-900 shadow-[0_30px_70px_-50px_rgba(244,63,94,0.75)] dark:border-rose-500/50 dark:from-rose-500/20 dark:via-rose-500/10 dark:to-rose-400/20 dark:text-rose-100', - ], - Transaction::STATUS_FALSE_POSITIVE => [ - 'classes' => 'border border-amber-400/60 bg-gradient-to-r from-amber-400/20 via-amber-400/10 to-amber-300/20 text-amber-900 shadow-[0_30px_70px_-50px_rgba(245,158,11,0.65)] dark:border-amber-400/50 dark:from-amber-400/20 dark:via-amber-400/10 dark:to-amber-300/20 dark:text-amber-100', - ], - Transaction::STATUS_CLEARED => [ - 'classes' => 'border border-emerald-400/60 bg-gradient-to-r from-emerald-400/20 via-emerald-400/10 to-emerald-300/20 text-emerald-900 shadow-[0_30px_70px_-50px_rgba(16,185,129,0.65)] dark:border-emerald-400/50 dark:from-emerald-400/20 dark:via-emerald-400/10 dark:to-emerald-300/20 dark:text-emerald-100', - ], - ]; +$statusCopy = [ +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.', +]; +$statusSummaryPhrases = [ +Transaction::STATUS_TRUE_POSITIVE => [ +'Verdachtsindikatoren bestätigen Treffer.', +'Risikotreiber stehen weiter auf Rot.', +'Sofortmaßnahmen priorisieren und dokumentieren.', +'Ermittlungsteam unverzüglich koordinieren.', +'Audit-Trail aktuell halten und teilen.', +], +Transaction::STATUS_FALSE_POSITIVE => [ +'Alarm widerlegt, Dokumentation abschließen.', +'Risikoschwelle fällt unter interne Limits.', +'Regelwerk für Fehlalarm analysieren.', +'Stakeholder über Entwarnung informieren.', +'Kontrollnachweis im Archiv sichern.', +], +Transaction::STATUS_CLEARED => [ +'Keine Auffälligkeiten, Vorgang freigegeben.', +'Monitoringdaten bestätigen Referenzverhalten.', +'Freigabe mit Vier-Augen-Prinzip dokumentieren.', +'Compliance-Team über Abschluss informieren.', +'Revisionsunterlagen vollständig archivieren.', +], +]; +$statusSummary = array_map( +static fn (array $phrases): string => $phrases[0] ?? 'Status gemäß aktuellem Prüfpfad.', +$statusSummaryPhrases +); +$statusSummaryBanner = [ +Transaction::STATUS_TRUE_POSITIVE => [ +'classes' => 'border border-rose-400/60 bg-gradient-to-r from-rose-500/20 via-rose-500/10 to-rose-400/20 text-rose-900 shadow-[0_30px_70px_-50px_rgba(244,63,94,0.75)] dark:border-rose-500/50 dark:from-rose-500/20 dark:via-rose-500/10 dark:to-rose-400/20 dark:text-rose-100', +], +Transaction::STATUS_FALSE_POSITIVE => [ +'classes' => 'border border-amber-400/60 bg-gradient-to-r from-amber-400/20 via-amber-400/10 to-amber-300/20 text-amber-900 shadow-[0_30px_70px_-50px_rgba(245,158,11,0.65)] dark:border-amber-400/50 dark:from-amber-400/20 dark:via-amber-400/10 dark:to-amber-300/20 dark:text-amber-100', +], +Transaction::STATUS_CLEARED => [ +'classes' => 'border border-emerald-400/60 bg-gradient-to-r from-emerald-400/20 via-emerald-400/10 to-emerald-300/20 text-emerald-900 shadow-[0_30px_70px_-50px_rgba(16,185,129,0.65)] dark:border-emerald-400/50 dark:from-emerald-400/20 dark:via-emerald-400/10 dark:to-emerald-300/20 dark:text-emerald-100', +], +]; - $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', - ], - ]; - $riskExplanation = 'Der Risikowert (0–100) fasst Monitoring-Regeln, KYC-Risiken, Negativmeldungen sowie Verhaltens- und Geodaten zu einer Priorität zusammen. Werte über 80 kennzeichnen Sofortmaßnahmen, 50–79 markieren erhöhte Aufmerksamkeit, alles darunter gilt als Routineprüfung.'; +$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', +], +]; +$riskExplanation = 'Der Risikowert (0–100) fasst Monitoring-Regeln, KYC-Risiken, Negativmeldungen sowie Verhaltens- und Geodaten zu einer Priorität zusammen. Werte über 80 kennzeichnen Sofortmaßnahmen, 50–79 markieren erhöhte Aufmerksamkeit, alles darunter gilt als Routineprüfung.'; @endphp
@@ -259,7 +259,7 @@ new #[Layout('components.layouts.app')] class extends Component {

AFC-Kontrollzentrum

-

Finanzkriminalitäts-Workbench

+

Verdachtsfall-Qualifizierungs-​Workbench

Überwachen Sie verdächtige Transaktionen im DAX-Portfolio, priorisieren Sie Alarme und orchestrieren Sie das manuelle Review im Vier-Augen-Prozess.

@@ -291,19 +291,19 @@ new #[Layout('components.layouts.app')] class extends Component {
@foreach ($statusLegend as $key => $legend) -
-
- +
+ -

{{ $legend['title'] }}

-
-

- {{ $legend['description'] }} -

+

{{ $legend['title'] }}

+

+ {{ $legend['description'] }} +

+
@endforeach
@@ -317,7 +317,7 @@ new #[Layout('components.layouts.app')] class extends Component {
@foreach ($this->statusOptions as $value => $label) - + @endforeach @forelse ($transactions as $transaction) - @php - $selectedState = $transaction->id === $this->selectedTransactionId; - $buttonClasses = [ - '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]', - 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400/70', - ]; + @php + $selectedState = $transaction->id === $this->selectedTransactionId; + $buttonClasses = [ + '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]', + 'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-indigo-400/70', + ]; - if ($selectedState) { - $buttonClasses[] = 'border-s-4 border-indigo-500/80 bg-indigo-500/10 shadow-lg shadow-indigo-500/20 ring-2 ring-indigo-400/70 ring-offset-2 ring-offset-white dark:bg-indigo-500/20 dark:ring-offset-zinc-900 scale-[1.01]'; - } else { - $buttonClasses[] = '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'; - } - @endphp - - @empty -
- Keine Transaktionen für die aktuellen Filterkriterien gefunden. Bitte Seed-Daten neu laden oder Filter anpassen.
+ +
+

{{ $transaction->channel }}

+

{{ $transaction->reference }}

+
+ +
+

+ {{ number_format($transaction->amount, 2, ',', '.') }} {{ $transaction->currency }} +

+
+ + + Risiko {{ $transaction->risk_score }} + + + + {{ $statusCopy[$transaction->status] ?? '' }} +
+
+ + @empty +
+ Keine Transaktionen für die aktuellen Filterkriterien gefunden. Bitte Seed-Daten neu laden oder Filter anpassen. +
@endforelse
@if ($transactions->hasPages()) -
- - - Seite {{ $transactions->currentPage() }} von {{ $transactions->lastPage() }} - - + + Seite {{ $transactions->currentPage() }} von {{ $transactions->lastPage() }} + + -
+ Weiter › + +
@endif
@if ($selected) - @php - $summaryStyles = $statusSummaryBanner[$selected->status] ?? [ - 'classes' => 'border border-zinc-300/70 bg-zinc-100/80 text-zinc-700 dark:border-zinc-600/60 dark:bg-zinc-800/60 dark:text-zinc-200', - ]; - $summaryText = implode(' ', $statusSummaryPhrases[$selected->status] ?? ['Status gemäß aktuellem Prüfpfad.']); - @endphp + @php + $summaryStyles = $statusSummaryBanner[$selected->status] ?? [ + 'classes' => 'border border-zinc-300/70 bg-zinc-100/80 text-zinc-700 dark:border-zinc-600/60 dark:bg-zinc-800/60 dark:text-zinc-200', + ]; + $summaryText = implode(' ', $statusSummaryPhrases[$selected->status] ?? ['Status gemäß aktuellem Prüfpfad.']); + @endphp -
-
- Statuszusammenfassung - - {{ $selected->statusLabel() }} - +
+
+ Statuszusammenfassung + + {{ $selected->statusLabel() }} + +
+

+ {{ $summaryText }} +

+
+ +
+
+
+

{{ $selected->company->legal_name }}

+

{{ $selected->company->summary }}

-

- {{ $summaryText }} +

+ + Prüfung erforderlich + + @php($detailStatusStyle = $statusStyles[$selected->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20') + + Status: {{ $selected->statusLabel() }} + + + {{ number_format($selected->amount, 2, ',', '.') }} {{ $selected->currency }} + +
+ + + Risikoscore {{ $selected->risk_score }} + + + + {{ $selected->executed_at?->format('d.m.Y, H:i') }} +
+
+
+ +
+
+
Statusbegründung
+
+ {{ $statusSummary[$selected->status] ?? 'Status gemäß aktuellem Prüfpfad.' }} +
+
+
+
Gegenpartei
+
{{ $selected->counterparty }}
+
{{ $selected->counterparty_country }}
+
+
+
Zahlungskanal & Referenz
+
{{ $selected->channel }}
+
{{ $selected->reference }}
+
+
+
Flagging-Engine
+
{{ $selected->flagged_by }}
+
{{ $selected->flagged_reason }}
+
+
+ + @if (! empty($selected->signals)) +
+

Detektionssignale

+
+ @foreach ($selected->signals as $signal) + + + {{ data_get($signal, 'type') }} · {{ data_get($signal, 'value') }} + + @endforeach +
+
+ @endif + +
+

Empfohlene Maßnahme

+

+ {{ $statusCopy[$selected->status] ?? 'Review abschließen und vollständigen Audit-Trail dokumentieren.' }}

-
-
-
-

{{ $selected->company->legal_name }}

-

{{ $selected->company->summary }}

-
-
- - Prüfung erforderlich - - @php($detailStatusStyle = $statusStyles[$selected->status] ?? 'bg-zinc-500/10 text-zinc-600 border border-zinc-500/20') - - Status: {{ $selected->statusLabel() }} - - - {{ number_format($selected->amount, 2, ',', '.') }} {{ $selected->currency }} - -
- - - Risikoscore {{ $selected->risk_score }} - - - - {{ $selected->executed_at?->format('d.m.Y, H:i') }} -
-
-
- -
-
-
Statusbegründung
-
- {{ $statusSummary[$selected->status] ?? 'Status gemäß aktuellem Prüfpfad.' }} -
-
-
-
Gegenpartei
-
{{ $selected->counterparty }}
-
{{ $selected->counterparty_country }}
-
-
-
Zahlungskanal & Referenz
-
{{ $selected->channel }}
-
{{ $selected->reference }}
-
-
-
Flagging-Engine
-
{{ $selected->flagged_by }}
-
{{ $selected->flagged_reason }}
-
-
- - @if (! empty($selected->signals)) -
-

Detektionssignale

-
- @foreach ($selected->signals as $signal) - - - {{ data_get($signal, 'type') }} · {{ data_get($signal, 'value') }} - - @endforeach -
-
- @endif - -
-

Empfohlene Maßnahme

-

- {{ $statusCopy[$selected->status] ?? 'Review abschließen und vollständigen Audit-Trail dokumentieren.' }} -

-
- -
- - {{ __('Fallansicht maximieren') }} - - -
+
+ + {{ __('Fallansicht maximieren') }} + +
+
-
-

Unternehmensprofil

-
    -
  • - Rechtlicher Name - {{ $selected->company->legal_name }} -
  • -
  • - Ticker - {{ $selected->company->ticker }} -
  • -
  • - Sektor - {{ $selected->company->sector }} -
  • -
  • - Hauptsitz - {{ $selected->company->headquarters }} -
  • -
  • - Land - {{ $selected->company->country }} -
  • -
  • - KYC-Risikostufe - - {{ Str::title($selected->company->kyc_risk_level) }} - -
  • -
-
+
+

Unternehmensprofil

+
    +
  • + Rechtlicher Name + {{ $selected->company->legal_name }} +
  • +
  • + Ticker + {{ $selected->company->ticker }} +
  • +
  • + Sektor + {{ $selected->company->sector }} +
  • +
  • + Hauptsitz + {{ $selected->company->headquarters }} +
  • +
  • + Land + {{ $selected->company->country }} +
  • +
  • + KYC-Risikostufe + + {{ Str::title($selected->company->kyc_risk_level) }} + +
  • +
+
@else -
- Wählen Sie links einen Alarm aus, um den vollständigen Prüfkontext einzusehen. -
+
+ Wählen Sie links einen Alarm aus, um den vollständigen Prüfkontext einzusehen. +
@endif
-
+
\ No newline at end of file