Add payment widget to dashboard

This commit is contained in:
DariusIII
2026-05-22 15:12:17 +02:00
parent e3e76c6fc6
commit 97aaa3f665
5 changed files with 123 additions and 0 deletions
@@ -83,6 +83,7 @@ Alpine.data('adminDashboard', () => ({
this._renderUserStats(payload.userStats);
this._renderSystemMetrics(payload.systemMetrics);
this._renderRecentActivity(payload.recent_activity);
this._renderRecentPayments(payload.recent_payments);
this._renderSiteStatus(payload.serviceStatuses, payload.activeIncidents);
this._renderSystemServices(payload.serviceStatuses);
this._lastRefreshAt = Date.now();
@@ -279,6 +280,42 @@ Alpine.data('adminDashboard', () => ({
</div>`;
}).join('');
},
_renderRecentPayments(payments) {
const hasPayments = Array.isArray(payments) && payments.length > 0;
const existing = this.$el.querySelector('[data-widget="recent-payments"]');
if (!hasPayments) {
if (existing) existing.classList.add('hidden');
return;
}
if (existing) existing.classList.remove('hidden');
const widget = this._swapWidget('recent-payments');
if (!widget) return;
const tbody = widget.querySelector('[data-payments-tbody]');
if (!tbody) return;
const settledClasses = 'bg-green-100 dark:bg-green-900 text-green-800 dark:text-green-200';
const pendingClasses = 'bg-yellow-100 dark:bg-yellow-900 text-yellow-800 dark:text-yellow-200';
const otherClasses = 'bg-gray-100 dark:bg-gray-700 text-gray-800 dark:text-gray-200';
tbody.innerHTML = payments.map(p => {
const status = String(p.payment_status ?? '');
const isSettled = status.toLowerCase() === 'settled';
const isPending = status === '' || status.toLowerCase() === 'pending';
const badge = isSettled ? settledClasses : (isPending ? pendingClasses : otherClasses);
const statusLabel = status || 'Pending';
const dateText = p.created_at_formatted ?? p.created_at_human ?? '';
const item = p.item_description ?? '';
const itemTrunc = item.length > 40 ? item.slice(0, 40) + '…' : item;
return `
<tr class="hover:bg-gray-50 dark:hover:bg-gray-900 transition">
<td class="px-4 py-2 whitespace-nowrap text-sm text-gray-600 dark:text-gray-400">${escapeHtml(dateText)}</td>
<td class="px-4 py-2 whitespace-nowrap text-sm font-medium text-gray-900 dark:text-gray-100">${escapeHtml(p.username ?? '')}</td>
<td class="px-4 py-2 text-sm text-gray-700 dark:text-gray-300" title="${escapeHtml(item)}">${escapeHtml(itemTrunc)}</td>
<td class="px-4 py-2 whitespace-nowrap text-sm text-right font-semibold text-gray-900 dark:text-gray-100">${escapeHtml(p.invoice_amount ?? '')}</td>
<td class="px-4 py-2 whitespace-nowrap text-center">
<span class="px-2 py-0.5 inline-flex text-xs leading-5 font-semibold rounded-full ${badge}">${escapeHtml(statusLabel)}</span>
</td>
</tr>`;
}).join('');
},
_renderSiteStatus(services, incidents) {
const widget = this._swapWidget('site-status');
if (!widget) return;
+35
View File
@@ -474,6 +474,41 @@
</div>
</div>
<!-- Recent Payments Widget (deferred) -->
@if(! empty($hasRecentPayments))
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6 border border-gray-200 dark:border-gray-700"
data-widget="recent-payments">
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4 flex items-center justify-between">
<span>
<i class="fas fa-credit-card mr-2 text-emerald-600 dark:text-emerald-400"></i>
Last 5 Payments
</span>
<a href="{{ route('admin.payment-list') }}" class="text-xs text-blue-600 dark:text-blue-400 hover:underline font-normal">View all &rarr;</a>
</h3>
<div data-widget-loading class="space-y-3">
@for($i = 0; $i < 5; $i++)
<div class="h-12 rounded bg-gray-100 dark:bg-gray-900 animate-pulse"></div>
@endfor
</div>
<div data-widget-content class="hidden">
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200 dark:divide-gray-700">
<thead>
<tr>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Date</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">User</th>
<th class="px-4 py-2 text-left text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Item</th>
<th class="px-4 py-2 text-right text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Amount</th>
<th class="px-4 py-2 text-center text-xs font-medium text-gray-500 dark:text-gray-400 uppercase tracking-wider">Status</th>
</tr>
</thead>
<tbody class="divide-y divide-gray-200 dark:divide-gray-700" data-payments-tbody></tbody>
</table>
</div>
</div>
</div>
@endif
<!-- Quick Links -->
<div class="bg-white dark:bg-gray-800 rounded-xl shadow-sm p-6">
<h3 class="text-lg font-semibold text-gray-800 dark:text-gray-200 mb-4">Quick Links</h3>