Newwebshop/templates/admin/payment/transactions.html.twig

497 lines
26 KiB
Twig

<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Zahlungstransaktionen - Webshop Admin</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/css/bootstrap.min.css" rel="stylesheet">
<link href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.0/font/bootstrap-icons.css" rel="stylesheet">
</head>
<body>
<div class="container-fluid">
<div class="row">
<!-- Sidebar -->
<nav class="col-md-3 col-lg-2 d-md-block bg-dark sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link text-white" href="/admin/dashboard">
<i class="bi bi-speedometer2"></i> Dashboard
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/admin/payment">
<i class="bi bi-credit-card"></i> Zahlungen
</a>
</li>
<li class="nav-item">
<a class="nav-link active text-white" href="/admin/payment/transactions">
<i class="bi bi-list"></i> Transaktionen
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/admin/payment/paypal">
<i class="bi bi-paypal"></i> PayPal
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/admin/payment/stripe">
<i class="bi bi-credit-card"></i> Stripe
</a>
</li>
<li class="nav-item">
<a class="nav-link text-white" href="/admin/payment/sepa">
<i class="bi bi-bank"></i> SEPA
</a>
</li>
</ul>
</div>
</nav>
<!-- Main content -->
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4">
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
<h1 class="h2">Zahlungstransaktionen</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group me-2">
<button class="btn btn-sm btn-outline-secondary" onclick="exportTransactions()">
<i class="bi bi-download"></i> Export
</button>
<button class="btn btn-sm btn-outline-primary" onclick="refreshTransactions()">
<i class="bi bi-arrow-clockwise"></i> Aktualisieren
</button>
</div>
</div>
</div>
<!-- Flash Messages -->
{% if success_messages %}
{% for message in success_messages %}
<div class="alert alert-success alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
{% if error_messages %}
{% for message in error_messages %}
<div class="alert alert-danger alert-dismissible fade show" role="alert">
{{ message }}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
</div>
{% endfor %}
{% endif %}
<!-- Statistics Cards -->
<div class="row mb-4">
<div class="col-md-3">
<div class="card text-white bg-primary">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Gesamt Transaktionen</h6>
<h4 class="mb-0">{{ total }}</h4>
</div>
<div class="align-self-center">
<i class="bi bi-credit-card fs-1"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-success">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Erfolgreich</h6>
<h4 class="mb-0" id="successfulCount">-</h4>
</div>
<div class="align-self-center">
<i class="bi bi-check-circle fs-1"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-warning">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Ausstehend</h6>
<h4 class="mb-0" id="pendingCount">-</h4>
</div>
<div class="align-self-center">
<i class="bi bi-clock fs-1"></i>
</div>
</div>
</div>
</div>
</div>
<div class="col-md-3">
<div class="card text-white bg-danger">
<div class="card-body">
<div class="d-flex justify-content-between">
<div>
<h6 class="card-title">Fehlgeschlagen</h6>
<h4 class="mb-0" id="failedCount">-</h4>
</div>
<div class="align-self-center">
<i class="bi bi-x-circle fs-1"></i>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Filters -->
<div class="row mb-4">
<div class="col-12">
<div class="card">
<div class="card-body">
<form id="filterForm" class="row g-3">
<div class="col-md-3">
<label for="provider" class="form-label">Zahlungsanbieter</label>
<select class="form-select" id="provider" name="provider">
<option value="">Alle Anbieter</option>
<option value="paypal">PayPal</option>
<option value="stripe">Stripe</option>
<option value="sepa">SEPA</option>
</select>
</div>
<div class="col-md-3">
<label for="status" class="form-label">Status</label>
<select class="form-select" id="status" name="status">
<option value="">Alle Status</option>
<option value="pending">Ausstehend</option>
<option value="completed">Erfolgreich</option>
<option value="failed">Fehlgeschlagen</option>
</select>
</div>
<div class="col-md-3">
<label for="date_from" class="form-label">Von Datum</label>
<input type="date" class="form-control" id="date_from" name="date_from">
</div>
<div class="col-md-3">
<label for="date_to" class="form-label">Bis Datum</label>
<input type="date" class="form-control" id="date_to" name="date_to">
</div>
<div class="col-12">
<button type="submit" class="btn btn-primary">
<i class="bi bi-search"></i> Filtern
</button>
<button type="button" class="btn btn-secondary" onclick="resetFilters()">
<i class="bi bi-arrow-clockwise"></i> Zurücksetzen
</button>
</div>
</form>
</div>
</div>
</div>
</div>
<!-- Transactions Table -->
<div class="row">
<div class="col-12">
<div class="card">
<div class="card-header">
<h5 class="card-title mb-0">Transaktionen</h5>
</div>
<div class="card-body">
<div class="table-responsive">
<table class="table table-striped table-hover">
<thead>
<tr>
<th>ID</th>
<th>Bestellung</th>
<th>Anbieter</th>
<th>Betrag</th>
<th>Status</th>
<th>Datum</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody id="transactionsTable">
{% for transaction in transactions %}
<tr>
<td>
<span class="badge bg-secondary">{{ transaction.id }}</span>
</td>
<td>
<a href="/admin/orders/view/{{ transaction.order_id }}" class="text-decoration-none">
#{{ transaction.order_number }}
</a>
</td>
<td>
<span class="badge bg-primary">{{ transaction.provider|upper }}</span>
</td>
<td>
<strong>{{ transaction.amount|number_format(2, ',', '.') }} €</strong>
<br>
<small class="text-muted">{{ transaction.currency }}</small>
</td>
<td>
{% if transaction.status == 'completed' %}
<span class="badge bg-success">
<i class="bi bi-check-circle"></i> Erfolgreich
</span>
{% elseif transaction.status == 'pending' %}
<span class="badge bg-warning">
<i class="bi bi-clock"></i> Ausstehend
</span>
{% elseif transaction.status == 'failed' %}
<span class="badge bg-danger">
<i class="bi bi-x-circle"></i> Fehlgeschlagen
</span>
{% else %}
<span class="badge bg-secondary">{{ transaction.status }}</span>
{% endif %}
</td>
<td>
{{ transaction.created_at|date('d.m.Y H:i') }}
</td>
<td>
<div class="btn-group btn-group-sm">
<button class="btn btn-outline-primary" onclick="viewTransaction({{ transaction.id }})">
<i class="bi bi-eye"></i>
</button>
<button class="btn btn-outline-info" onclick="retryTransaction({{ transaction.id }})">
<i class="bi bi-arrow-clockwise"></i>
</button>
</div>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<!-- Pagination -->
{% if total_pages > 1 %}
<nav aria-label="Transaktionen Navigation">
<ul class="pagination justify-content-center">
{% if page > 1 %}
<li class="page-item">
<a class="page-link" href="?page={{ page - 1 }}">Zurück</a>
</li>
{% endif %}
{% for p in range(1, total_pages) %}
{% if p == page %}
<li class="page-item active">
<span class="page-link">{{ p }}</span>
</li>
{% else %}
<li class="page-item">
<a class="page-link" href="?page={{ p }}">{{ p }}</a>
</li>
{% endif %}
{% endfor %}
{% if page < total_pages %}
<li class="page-item">
<a class="page-link" href="?page={{ page + 1 }}">Weiter</a>
</li>
{% endif %}
</ul>
</nav>
{% endif %}
</div>
</div>
</div>
</div>
</main>
</div>
</div>
<!-- Transaction Details Modal -->
<div class="modal fade" id="transactionModal" tabindex="-1">
<div class="modal-dialog modal-lg">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title">Transaktionsdetails</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"></button>
</div>
<div class="modal-body" id="transactionModalBody">
<!-- Content will be loaded here -->
</div>
<div class="modal-footer">
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Schließen</button>
</div>
</div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0/dist/js/bootstrap.bundle.min.js"></script>
<script>
// Update statistics
function updateStatistics() {
const transactions = document.querySelectorAll('#transactionsTable tr');
let successful = 0, pending = 0, failed = 0;
transactions.forEach(row => {
const statusCell = row.querySelector('td:nth-child(5)');
if (statusCell) {
const status = statusCell.textContent.toLowerCase();
if (status.includes('erfolgreich')) successful++;
else if (status.includes('ausstehend')) pending++;
else if (status.includes('fehlgeschlagen')) failed++;
}
});
document.getElementById('successfulCount').textContent = successful;
document.getElementById('pendingCount').textContent = pending;
document.getElementById('failedCount').textContent = failed;
}
// View transaction details
function viewTransaction(transactionId) {
fetch(`/admin/payment/transaction/${transactionId}`)
.then(response => response.json())
.then(data => {
const modalBody = document.getElementById('transactionModalBody');
modalBody.innerHTML = `
<div class="row">
<div class="col-md-6">
<h6>Transaktionsinformationen</h6>
<table class="table table-sm">
<tr><td>ID:</td><td>${data.id}</td></tr>
<tr><td>Anbieter:</td><td>${data.provider}</td></tr>
<tr><td>Status:</td><td>${data.status}</td></tr>
<tr><td>Betrag:</td><td>${data.amount} ${data.currency}</td></tr>
<tr><td>Erstellt:</td><td>${data.created_at}</td></tr>
</table>
</div>
<div class="col-md-6">
<h6>Bestellinformationen</h6>
<table class="table table-sm">
<tr><td>Bestellnummer:</td><td>${data.order_number}</td></tr>
<tr><td>Kunde:</td><td>${data.customer_name}</td></tr>
<tr><td>Bestelldatum:</td><td>${data.order_date}</td></tr>
</table>
</div>
</div>
<div class="row mt-3">
<div class="col-12">
<h6>Payment Data</h6>
<pre class="bg-light p-3 rounded">${JSON.stringify(JSON.parse(data.payment_data), null, 2)}</pre>
</div>
</div>
`;
const modal = new bootstrap.Modal(document.getElementById('transactionModal'));
modal.show();
})
.catch(error => {
console.error('Fehler beim Laden der Transaktionsdetails:', error);
alert('Fehler beim Laden der Transaktionsdetails');
});
}
// Retry transaction
function retryTransaction(transactionId) {
if (confirm('Möchten Sie diese Transaktion erneut versuchen?')) {
fetch(`/admin/payment/retry-transaction/${transactionId}`, {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `csrf_token={{ csrf_token }}`
})
.then(response => response.json())
.then(data => {
if (data.success) {
showAlert('Transaktion wird erneut versucht...', 'success');
setTimeout(() => location.reload(), 2000);
} else {
showAlert('Fehler beim erneuten Versuch: ' + data.message, 'danger');
}
})
.catch(error => {
console.error('Retry Fehler:', error);
showAlert('Fehler beim erneuten Versuch', 'danger');
});
}
}
// Export transactions
function exportTransactions() {
const form = document.getElementById('filterForm');
const formData = new FormData(form);
const params = new URLSearchParams(formData);
const date = new Date().toISOString().split('T')[0];
const filename = `transactions_${date}.csv`;
fetch(`/admin/payment/export-transactions?${params.toString()}`)
.then(response => response.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
document.body.appendChild(a);
a.click();
window.URL.revokeObjectURL(url);
document.body.removeChild(a);
})
.catch(error => {
console.error('Export Fehler:', error);
showAlert('Fehler beim Export der Transaktionen', 'danger');
});
}
// Refresh transactions
function refreshTransactions() {
location.reload();
}
// Reset filters
function resetFilters() {
document.getElementById('filterForm').reset();
document.getElementById('filterForm').submit();
}
// Show alert message
function showAlert(message, type) {
const alertDiv = document.createElement('div');
alertDiv.className = `alert alert-${type} alert-dismissible fade show`;
alertDiv.innerHTML = `
${message}
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
const container = document.querySelector('main');
container.insertBefore(alertDiv, container.firstChild);
// Auto-remove after 5 seconds
setTimeout(() => {
if (alertDiv.parentNode) {
alertDiv.remove();
}
}, 5000);
}
// Initialize
document.addEventListener('DOMContentLoaded', function() {
updateStatistics();
// Auto-refresh every 30 seconds
setInterval(() => {
fetch('/admin/payment/transactions-data')
.then(response => response.json())
.then(data => {
// Update table if new transactions
if (data.new_transactions > 0) {
location.reload();
}
})
.catch(error => console.error('Auto-refresh Fehler:', error));
}, 30000);
});
</script>
</body>
</html>