['except' => ''], 'status' => ['except' => 'all'], 'page' => ['except' => 1], ]; public function mount(): void { $this->selectedTransactionId = Transaction::query() ->orderByDesc('risk_score') ->orderByDesc('executed_at') ->value('id'); } public function selectTransaction(int $transactionId): void { $this->selectedTransactionId = $transactionId; } public function updatingSearch(): void { $this->resetPage(); } public function updatedSearch(): void { $this->refreshSelection(); } public function updatingStatus(): void { $this->resetPage(); } public function updatedStatus(): 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 Prüfaufträge', Transaction::STATUS_TRUE_POSITIVE => 'Bestätigte Treffer', Transaction::STATUS_FALSE_POSITIVE => 'Fehlalarme', Transaction::STATUS_CLEARED => 'Freigegebene Vorgänge', ]; } #[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 metrics(): array { $baseQuery = $this->filteredQuery(); /** @var Collection $grouped */ $grouped = (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, ], ]); return [ 'by_status' => $grouped->toArray(), 'total' => [ 'count' => $grouped->sum('count'), 'amount' => $grouped->sum('amount'), ], ]; } #[Computed] public function selectedTransaction(): ?Transaction { $transaction = $this->transactions->firstWhere('id', $this->selectedTransactionId); if (! $transaction && $this->selectedTransactionId) { $transaction = Transaction::query() ->with('company') ->find($this->selectedTransactionId); } return $transaction ?? $this->transactions->first(); } protected function filteredQuery(): Builder { $search = trim($this->search); return Transaction::query() ->when($this->status !== 'all', fn(Builder $query) => $query->where('status', $this->status)) ->when($search !== '', function (Builder $query) use ($search) { $like = '%' . $search . '%'; $query->where(function (Builder $inner) use ($like) { $inner->where('reference', 'like', $like) ->orWhere('counterparty', 'like', $like) ->orWhereHas('company', function (Builder $company) use ($like) { $company->where('name', 'like', $like) ->orWhere('legal_name', 'like', $like) ->orWhere('ticker', 'like', $like); }); }); }); } }; ?> @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, ',', '.') . ' €'; $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)]', ]; $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.'; @endphp

AFC-Kontrollzentrum

Verdachtsfall-Qualifizierungs-​Workbench

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

Gesamtfälle

{{ $total['count'] }}

Volumen: {{ $formatAmount($total['amount']) }}

Bestätigte Treffer

{{ $confirmed['count'] }}

Volumen: {{ $formatAmount($confirmed['amount']) }}

Fehlalarme

{{ $falseAlarm['count'] }}

Volumen: {{ $formatAmount($falseAlarm['amount']) }}

Freigegeben

{{ $cleared['count'] }}

Volumen: {{ $formatAmount($cleared['amount']) }}

@foreach ($statusLegend as $key => $legend)

{{ $legend['title'] }}

{{ $legend['description'] }}

@endforeach

Aktive Prüfwarteschlange

Alle untenstehenden Meldungen benötigen eine finale Analystenentscheidung.

@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', ]; 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.
@endforelse
@if ($transactions->hasPages())
Seite {{ $transactions->currentPage() }} von {{ $transactions->lastPage() }}
@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
Statuszusammenfassung {{ $selected->statusLabel() }}

{{ $summaryText }}

{{ $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') }}

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.
@endif