14-04-2026

This commit is contained in:
Kevin Adametz 2026-04-14 18:07:45 +02:00
parent f58c709945
commit 0f82fea88a
72 changed files with 7414 additions and 148 deletions

View file

@ -0,0 +1,45 @@
<div class="timeline">
@forelse($incident->activities as $activity)
<div class="timeline-item {{ $activity->type === 'status_change' ? 'timeline-item-secondary' : '' }}">
<div class="timeline-indicator bg-{{ match($activity->type) {
'status_change' => 'info',
'email' => 'primary',
'call' => 'success',
'ticket' => 'warning',
'provider_response' => 'secondary',
default => 'light border'
} }}">
<i class="ion {{ $activity->type_icon }}"></i>
</div>
<div class="timeline-content">
<div class="d-flex justify-content-between align-items-start">
<div>
<span class="badge badge-light">{{ $activity->type_label }}</span>
<strong class="ml-1">{{ $activity->title }}</strong>
</div>
<small class="text-muted text-nowrap ml-2">
{{ $activity->created_at->format('d.m.Y H:i') }}
&mdash; {{ $activity->author }}
</small>
</div>
@if($activity->content)
<div class="mt-1 text-muted">{{ $activity->content }}</div>
@endif
</div>
</div>
@empty
<p class="text-muted">Noch keine Aktivitäten.</p>
@endforelse
</div>
<style>
.timeline { position: relative; padding-left: 2.5rem; }
.timeline::before { content: ''; position: absolute; left: 1rem; top: 0; bottom: 0; width: 2px; background: #e9ecef; }
.timeline-item { position: relative; margin-bottom: 1.25rem; }
.timeline-indicator {
position: absolute; left: -2.5rem; width: 2rem; height: 2rem;
border-radius: 50%; display: flex; align-items: center; justify-content: center;
font-size: 0.875rem; color: #fff;
}
.timeline-content { background: #f8f9fa; border-radius: 0.375rem; padding: 0.75rem 1rem; }
</style>

View file

@ -0,0 +1,100 @@
<div class="modal fade" id="createIncidentModal" tabindex="-1" role="dialog">
<div class="modal-dialog modal-lg" role="document">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Neuen Incident anlegen</h5>
<button type="button" class="close" data-dismiss="modal">
<span>&times;</span>
</button>
</div>
<form method="POST" action="{{ route('admin.payment-dashboard.store') }}">
@csrf
<div class="modal-body">
@if($errors->any())
<div class="alert alert-danger">
<ul class="mb-0">
@foreach($errors->all() as $error)
<li>{{ $error }}</li>
@endforeach
</ul>
</div>
@endif
<div class="form-row">
<div class="form-group col-md-8">
<label>Titel <span class="text-danger">*</span></label>
<input type="text" name="title" class="form-control" value="{{ old('title') }}" required>
</div>
<div class="form-group col-md-4">
<label>Erkannt am <span class="text-danger">*</span></label>
<input type="datetime-local" name="detected_at" class="form-control"
value="{{ old('detected_at', now()->format('Y-m-d\TH:i')) }}" required>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label>Anbieter <span class="text-danger">*</span></label>
<select name="provider" class="custom-select" required>
<option value="payone" {{ old('provider', 'payone') === 'payone' ? 'selected' : '' }}>PAYONE</option>
{{-- <option value="stripe" {{ old('provider') === 'stripe' ? 'selected' : '' }}>Stripe</option> --}}
<option value="paypal" {{ old('provider') === 'paypal' ? 'selected' : '' }}>PayPal</option>
{{-- <option value="mollie" {{ old('provider') === 'mollie' ? 'selected' : '' }}>Mollie</option> --}}
<option value="other" {{ old('provider') === 'other' ? 'selected' : '' }}>Sonstige</option>
</select>
</div>
<div class="form-group col-md-4">
<label>Typ <span class="text-danger">*</span></label>
<select name="type" class="custom-select" required>
<option value="outage" {{ old('type') === 'outage' ? 'selected' : '' }}>Ausfall</option>
<option value="ipn_error" {{ old('type') === 'ipn_error' ? 'selected' : '' }}>IPN-Fehler</option>
<option value="payment_failure" {{ old('type', 'payment_failure') === 'payment_failure' ? 'selected' : '' }}>Zahlungsfehler</option>
<option value="slow_response" {{ old('type') === 'slow_response' ? 'selected' : '' }}>Langsame Antwort</option>
<option value="other" {{ old('type') === 'other' ? 'selected' : '' }}>Sonstiges</option>
</select>
</div>
<div class="form-group col-md-4">
<label>Schwere <span class="text-danger">*</span></label>
<select name="severity" class="custom-select" required>
<option value="low" {{ old('severity') === 'low' ? 'selected' : '' }}>Niedrig</option>
<option value="medium" {{ old('severity', 'medium') === 'medium' ? 'selected' : '' }}>Mittel</option>
<option value="high" {{ old('severity') === 'high' ? 'selected' : '' }}>Hoch</option>
<option value="critical" {{ old('severity') === 'critical' ? 'selected' : '' }}>Kritisch</option>
</select>
</div>
</div>
<div class="form-row">
<div class="form-group col-md-4">
<label>Betroffene Bestellungen</label>
<input type="number" name="affected_orders" class="form-control" min="0"
value="{{ old('affected_orders', 0) }}">
</div>
<div class="form-group col-md-4">
<label>Betroffener Umsatz ()</label>
<input type="number" name="affected_revenue" class="form-control" min="0" step="0.01"
value="{{ old('affected_revenue', '0.00') }}">
</div>
<div class="form-group col-md-4">
<label>Ticket-Nummer</label>
<input type="text" name="ticket_number" class="form-control"
value="{{ old('ticket_number') }}" placeholder="z.B. PAYONE-12345">
</div>
</div>
<div class="form-group">
<label>Beschreibung</label>
<textarea name="description" class="form-control" rows="3"
placeholder="Was ist passiert?">{{ old('description') }}</textarea>
</div>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-dismiss="modal">Abbrechen</button>
<button type="submit" class="btn btn-danger">
<i class="ion ion-md-alert"></i> Incident anlegen
</button>
</div>
</form>
</div>
</div>
</div>

View file

@ -0,0 +1,71 @@
<div class="table-responsive">
<table class="table table-sm table-hover">
<thead class="thead-light">
<tr>
<th>Schwere</th>
<th>Titel</th>
<th>Anbieter</th>
<th>Typ</th>
<th>Status</th>
<th>Erkannt</th>
<th>Dauer</th>
@if(isset($showActions) && $showActions)
<th></th>
@endif
</tr>
</thead>
<tbody>
@forelse($incidents as $incident)
<tr>
<td>
<span class="badge badge-{{ $incident->severity_color }}">
{{ $incident->severity_label }}
</span>
</td>
<td>
<a href="{{ route('admin.payment-dashboard.show', $incident) }}">
{{ $incident->title }}
</a>
@if($incident->ticket_number)
<span class="text-muted small ml-1">#{{ $incident->ticket_number }}</span>
@endif
</td>
<td><span class="badge badge-secondary">{{ $incident->provider_label }}</span></td>
<td>
<i class="ion {{ $incident->type_icon }}"></i>
{{ $incident->type_label }}
</td>
<td>
<span class="badge badge-{{ $incident->status_color }}">
{{ $incident->status_label }}
</span>
</td>
<td class="text-nowrap">{{ $incident->detected_at->format('d.m.Y H:i') }}</td>
<td class="text-nowrap">{{ $incident->duration }}</td>
@if(isset($showActions) && $showActions)
<td>
<div class="d-flex gap-1">
<form method="POST" action="{{ route('admin.payment-dashboard.status.update', $incident) }}" class="d-inline">
@csrf
@method('PATCH')
<select name="status" class="custom-select custom-select-sm" onchange="this.form.submit()" style="width:auto">
@foreach(['open' => 'Offen', 'in_progress' => 'In Bearb.', 'waiting_provider' => 'Wartet', 'resolved' => 'Gelöst', 'closed' => 'Geschl.'] as $value => $label)
<option value="{{ $value }}" {{ $incident->status === $value ? 'selected' : '' }}>{{ $label }}</option>
@endforeach
</select>
</form>
<a href="{{ route('admin.payment-dashboard.show', $incident) }}" class="btn btn-sm btn-outline-secondary">
<i class="ion ion-md-open"></i>
</a>
</div>
</td>
@endif
</tr>
@empty
<tr>
<td colspan="8" class="text-center text-muted py-3">Keine Incidents vorhanden.</td>
</tr>
@endforelse
</tbody>
</table>
</div>

View file

@ -0,0 +1,64 @@
<div class="row mb-4">
<div class="col-sm-6 col-xl-2">
<div class="card {{ $stats['critical_open'] > 0 ? 'border-danger' : '' }}">
<div class="card-body text-center">
<div class="text-muted small">Offene Incidents</div>
<div class="display-4 font-weight-bold {{ $stats['open_incidents'] > 0 ? 'text-danger' : 'text-success' }}">
{{ $stats['open_incidents'] }}
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-2">
<div class="card">
<div class="card-body text-center">
<div class="text-muted small">In Bearbeitung</div>
<div class="display-4 font-weight-bold {{ $stats['in_progress'] > 0 ? 'text-warning' : 'text-muted' }}">
{{ $stats['in_progress'] }}
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-2">
<div class="card">
<div class="card-body text-center">
<div class="text-muted small">PAYONE (30 Tage)</div>
<div class="display-4 font-weight-bold text-info">
{{ $stats['payone_incidents_30d'] }}
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-2">
<div class="card">
<div class="card-body text-center">
<div class="text-muted small">Gelöst (Monat)</div>
<div class="display-4 font-weight-bold text-success">
{{ $stats['resolved_this_month'] }}
</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-2">
<div class="card">
<div class="card-body text-center">
<div class="text-muted small">Erfolgsrate Zahlung</div>
<div class="display-4 font-weight-bold {{ $transactionStats['success_rate'] < 90 ? 'text-warning' : 'text-success' }}">
{{ $transactionStats['success_rate'] }}%
</div>
<div class="text-muted" style="font-size:0.7rem">letzte {{ $transactionStats['days'] }} Tage</div>
</div>
</div>
</div>
<div class="col-sm-6 col-xl-2">
<div class="card {{ $transactionStats['failed'] > 0 ? 'border-warning' : '' }}">
<div class="card-body text-center">
<div class="text-muted small">Fehlgeschlagen</div>
<div class="display-4 font-weight-bold {{ $transactionStats['failed'] > 0 ? 'text-danger' : 'text-success' }}">
{{ $transactionStats['failed'] }}
</div>
<div class="text-muted" style="font-size:0.7rem">letzte {{ $transactionStats['days'] }} Tage</div>
</div>
</div>
</div>
</div>