mivita/dev/payment-dashboard/resources/views/dashboard/developer.blade.php
2026-04-14 18:07:45 +02:00

264 lines
13 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.

@extends('layouts.dashboard')
@section('page-title', 'Entwickler-Dashboard')
@section('content')
{{-- ── Stats ── --}}
<div class="grid-4 mb-6">
<div class="stat-card {{ $stats['open_incidents'] > 0 ? 'danger' : 'ok' }}">
<div class="stat-label">Offen / Wartend</div>
<div class="stat-value">{{ $stats['open_incidents'] }}</div>
<div class="stat-sub">Incidents ohne Lösung</div>
</div>
<div class="stat-card {{ $stats['in_progress'] > 0 ? 'warning' : 'ok' }}">
<div class="stat-label">In Bearbeitung</div>
<div class="stat-value">{{ $stats['in_progress'] }}</div>
<div class="stat-sub">Aktiv bearbeitet</div>
</div>
<div class="stat-card ok">
<div class="stat-label">Gelöst diesen Monat</div>
<div class="stat-value">{{ $stats['resolved_this_month'] }}</div>
<div class="stat-sub">{{ now()->format('F Y') }}</div>
</div>
<div class="stat-card {{ $stats['payone_incidents_30d'] >= 3 ? 'danger' : ($stats['payone_incidents_30d'] >= 1 ? 'warning' : 'ok') }}">
<div class="stat-label">PAYONE (30 Tage)</div>
<div class="stat-value">{{ $stats['payone_incidents_30d'] }}</div>
<div class="stat-sub">Incidents bei PAYONE</div>
</div>
</div>
{{-- ── Anbieter-Status ── --}}
<div class="card mb-6">
<div class="flex items-center justify-between mb-4">
<div class="card-title" style="margin:0">Anbieter-Status</div>
</div>
<div class="provider-grid">
@foreach($providerStats as $key => $provider)
<div class="provider-card {{ $provider['open_incidents'] > 0 ? 'has-issues' : 'ok' }}">
<div class="provider-name">{{ $provider['label'] }}</div>
<div class="provider-incidents">{{ $provider['open_incidents'] }}</div>
<div class="provider-sub">offene Störungen</div>
<div style="margin-top:8px; font-size:11px; color: var(--text-muted);">{{ $provider['total_30d'] }}× in 30 Tagen</div>
@if($provider['last_incident'])
<div style="margin-top:4px; font-size:10px; color: var(--text-muted);">
Zuletzt: {{ $provider['last_incident']->detected_at->format('d.m.Y H:i') }}
</div>
@endif
</div>
@endforeach
</div>
</div>
<div class="grid-2">
{{-- ── Offene Incidents ── --}}
<div>
<div class="flex items-center justify-between mb-4">
<div class="section-title" style="margin:0; border:none; padding:0;">Offene Incidents</div>
<button class="btn btn-primary" onclick="document.getElementById('modal-new').classList.add('open')">
+ Neuer Incident
</button>
</div>
@forelse($openIncidents as $incident)
<div class="card mb-4" style="border-left: 3px solid {{ $incident->severity_color }};">
<div class="flex items-center justify-between mb-4">
<div>
<div style="font-weight:700; margin-bottom:4px;">{{ $incident->title }}</div>
<div class="flex gap-2">
<span class="badge badge-{{ $incident->severity }}">{{ ucfirst($incident->severity) }}</span>
<span class="badge badge-{{ $incident->status }}">{{ $incident->status_label }}</span>
<span style="font-size:11px; color:var(--text-muted); padding: 3px 0;">{{ $incident->provider_label }}</span>
</div>
</div>
<a href="{{ route('payment-dashboard.show', $incident) }}" class="btn btn-ghost" style="font-size:12px;">Detail </a>
</div>
<div style="font-size:12px; color:var(--text-muted); margin-bottom:12px;">
Erkannt: {{ $incident->detected_at->format('d.m.Y H:i') }} · Dauer: {{ $incident->duration }}
@if($incident->ticket_number)
· Ticket: <span class="text-accent">{{ $incident->ticket_number }}</span>
@endif
@if($incident->affected_orders > 0)
· {{ $incident->affected_orders }} Bestellungen betroffen
@endif
</div>
{{-- Letzte Aktivität --}}
@if($incident->activities->count() > 0)
@php $last = $incident->activities->sortByDesc('created_at')->first(); @endphp
<div style="background: var(--surface-2); border: 1px solid var(--border); border-radius:7px; padding:10px 12px; font-size:12px;">
<span style="color:var(--text-muted);">{{ $last->type_icon }} Letzte Aktivität:</span>
<strong>{{ $last->title }}</strong>
<span class="text-muted"> {{ $last->created_at->diffForHumans() }}</span>
</div>
@endif
{{-- Schnell-Status-Update --}}
<form action="{{ route('payment-dashboard.status.update', $incident) }}" method="POST" style="margin-top:12px; display:flex; gap:8px; flex-wrap:wrap;">
@csrf @method('PATCH')
<select name="status" style="width:auto; flex:1;">
<option value="open" {{ $incident->status === 'open' ? 'selected' : '' }}>Offen</option>
<option value="in_progress" {{ $incident->status === 'in_progress' ? 'selected' : '' }}>In Bearbeitung</option>
<option value="waiting_provider" {{ $incident->status === 'waiting_provider' ? 'selected' : '' }}>Wartet auf Anbieter</option>
<option value="resolved" {{ $incident->status === 'resolved' ? 'selected' : '' }}>Gelöst</option>
<option value="closed" {{ $incident->status === 'closed' ? 'selected' : '' }}>Geschlossen</option>
</select>
<button type="submit" class="btn btn-ghost" style="font-size:12px;">Aktualisieren</button>
</form>
</div>
@empty
<div class="card" style="text-align:center; padding:32px; border-color: var(--green);">
<div style="font-size:28px; margin-bottom:8px;"></div>
<div style="font-weight:700; color:var(--green);">Keine offenen Incidents</div>
</div>
@endforelse
</div>
{{-- ── Letzte Aktivitäten ── --}}
<div>
<div class="section-title">Kommunikations-Verlauf</div>
<div class="card">
<div class="timeline">
@forelse($recentActivity as $activity)
<div class="timeline-item">
<div class="timeline-dot"></div>
<div class="timeline-title">
{{ $activity->type_icon }} {{ $activity->title }}
</div>
<div class="timeline-meta">
{{ $activity->type_label }} · {{ $activity->author }} · {{ $activity->created_at->format('d.m.Y H:i') }}
@if($activity->incident)
· <a href="{{ route('payment-dashboard.show', $activity->incident) }}">{{ $activity->incident->title }}</a>
@endif
</div>
@if($activity->content)
<div class="timeline-content">{{ $activity->content }}</div>
@endif
</div>
@empty
<div class="text-muted">Noch keine Aktivitäten erfasst.</div>
@endforelse
</div>
</div>
</div>
</div>
{{-- ── Alle Incidents Tabelle ── --}}
<div class="card" style="margin-top:8px;">
<div class="flex items-center justify-between mb-4">
<div class="card-title" style="margin:0;">Alle Incidents</div>
<span class="text-muted" style="font-size:12px;">{{ $allIncidents->total() }} gesamt</span>
</div>
<div class="table-wrap">
<table>
<thead>
<tr>
<th>#</th>
<th>Titel</th>
<th>Anbieter</th>
<th>Typ</th>
<th>Schwere</th>
<th>Status</th>
<th>Erkannt</th>
<th>Dauer</th>
<th>Ticket</th>
<th></th>
</tr>
</thead>
<tbody>
@forelse($allIncidents as $incident)
<tr>
<td class="text-muted" style="font-size:11px;">#{{ $incident->id }}</td>
<td><strong>{{ $incident->title }}</strong></td>
<td>{{ $incident->provider_label }}</td>
<td style="font-size:12px; color:var(--text-muted);">{{ $incident->type }}</td>
<td><span class="badge badge-{{ $incident->severity }}">{{ ucfirst($incident->severity) }}</span></td>
<td><span class="badge badge-{{ $incident->status }}">{{ $incident->status_label }}</span></td>
<td style="font-size:12px;">{{ $incident->detected_at->format('d.m.Y H:i') }}</td>
<td style="font-size:12px;">{{ $incident->duration }}</td>
<td style="font-size:12px; color:var(--accent);">{{ $incident->ticket_number ?? '' }}</td>
<td><a href="{{ route('payment-dashboard.show', $incident) }}" class="btn btn-ghost" style="font-size:11px; padding:4px 10px;">Detail</a></td>
</tr>
@empty
<tr><td colspan="10" class="text-muted">Noch keine Incidents.</td></tr>
@endforelse
</tbody>
</table>
</div>
<div style="margin-top:16px;">{{ $allIncidents->links() }}</div>
</div>
{{-- ── Modal: Neuer Incident ── --}}
<div class="modal-overlay" id="modal-new" onclick="if(event.target===this)this.classList.remove('open')">
<div class="modal">
<div class="modal-title">Neuen Incident erfassen</div>
<form action="{{ route('payment-dashboard.store') }}" method="POST">
@csrf
<div class="form-group">
<label>Titel *</label>
<input type="text" name="title" placeholder="z.B. IPN-Fehler PayPal via PAYONE" required>
</div>
<div class="form-row">
<div class="form-group">
<label>Anbieter *</label>
<select name="provider">
<option value="payone">PAYONE</option>
<option value="stripe">Stripe</option>
<option value="paypal">PayPal</option>
<option value="mollie">Mollie</option>
<option value="other">Sonstige</option>
</select>
</div>
<div class="form-group">
<label>Typ *</label>
<select name="type">
<option value="ipn_error">IPN-Fehler</option>
<option value="outage">Komplettausfall</option>
<option value="payment_failure">Zahlungsfehler</option>
<option value="slow_response">Langsame Reaktion</option>
<option value="other">Sonstiges</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Schwere *</label>
<select name="severity">
<option value="critical">Critical</option>
<option value="high">High</option>
<option value="medium" selected>Medium</option>
<option value="low">Low</option>
</select>
</div>
<div class="form-group">
<label>Ticket-Nummer</label>
<input type="text" name="ticket_number" placeholder="z.B. PAY-20240112">
</div>
</div>
<div class="form-row">
<div class="form-group">
<label>Betroffene Bestellungen</label>
<input type="number" name="affected_orders" value="0" min="0">
</div>
<div class="form-group">
<label>Betroffener Umsatz ()</label>
<input type="number" name="affected_revenue" value="0" min="0" step="0.01">
</div>
</div>
<div class="form-group">
<label>Erkannt am *</label>
<input type="datetime-local" name="detected_at" value="{{ now()->format('Y-m-d\TH:i') }}" required>
</div>
<div class="form-group">
<label>Beschreibung</label>
<textarea name="description" rows="3" placeholder="Was ist passiert? Fehlermeldung, Kontext..."></textarea>
</div>
<div class="flex gap-2" style="justify-content:flex-end;">
<button type="button" class="btn btn-ghost" onclick="document.getElementById('modal-new').classList.remove('open')">Abbrechen</button>
<button type="submit" class="btn btn-primary">Incident anlegen</button>
</div>
</form>
</div>
</div>
@endsection