feat: add status summary cards to All Applications page

- Show running, deploying, pending, stopped, failed counts as color-coded cards
- Cards displayed at top of All Applications page for quick status overview
- Skeleton loading state includes card placeholders
This commit is contained in:
keyhan
2026-04-06 14:44:57 +03:30
parent 8243ac7df2
commit 5d281b85d6
@@ -47,12 +47,34 @@ export default function AdminAppsPage() {
onError: () => toast.error('Failed to delete application'), onError: () => toast.error('Failed to delete application'),
}); });
// Compute status counts from apps
const statusCounts = apps.reduce(
(acc, app) => {
const status = app.deployments?.[0]?.status || 'pending';
if (status === 'running') acc.running++;
else if (status === 'stopped') acc.stopped++;
else if (status === 'failed' || status === 'build_failed') acc.failed++;
else if (status === 'building' || status === 'deploying') acc.deploying++;
else acc.pending++;
return acc;
},
{ running: 0, stopped: 0, failed: 0, deploying: 0, pending: 0 },
);
if (isLoading) { if (isLoading) {
return ( return (
<div className="space-y-6"> <div className="space-y-6">
<div className="page-header"> <div className="page-header">
<div className="skeleton h-8 w-56" /> <div className="skeleton h-8 w-56" />
</div> </div>
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
{[1, 2, 3, 4, 5].map((i) => (
<div key={i} className="card py-4">
<div className="skeleton h-8 w-12 mx-auto mb-2" />
<div className="skeleton h-3 w-16 mx-auto" />
</div>
))}
</div>
<div className="skeleton h-10 w-full rounded-xl" /> <div className="skeleton h-10 w-full rounded-xl" />
<div className="space-y-3"> <div className="space-y-3">
{[1, 2, 3, 4].map((i) => ( {[1, 2, 3, 4].map((i) => (
@@ -82,6 +104,30 @@ export default function AdminAppsPage() {
</div> </div>
</div> </div>
{/* Status Summary Cards */}
<div className="grid grid-cols-2 md:grid-cols-5 gap-3">
<div className="card py-4 text-center border-l-4 border-l-green-500">
<p className="text-2xl font-bold text-green-600">{statusCounts.running}</p>
<p className="text-xs text-gray-500 font-medium mt-1">Running</p>
</div>
<div className="card py-4 text-center border-l-4 border-l-blue-500">
<p className="text-2xl font-bold text-blue-600">{statusCounts.deploying}</p>
<p className="text-xs text-gray-500 font-medium mt-1">Deploying</p>
</div>
<div className="card py-4 text-center border-l-4 border-l-yellow-500">
<p className="text-2xl font-bold text-yellow-600">{statusCounts.pending}</p>
<p className="text-xs text-gray-500 font-medium mt-1">Pending</p>
</div>
<div className="card py-4 text-center border-l-4 border-l-gray-400">
<p className="text-2xl font-bold text-gray-500">{statusCounts.stopped}</p>
<p className="text-xs text-gray-500 font-medium mt-1">Stopped</p>
</div>
<div className="card py-4 text-center border-l-4 border-l-red-500">
<p className="text-2xl font-bold text-red-600">{statusCounts.failed}</p>
<p className="text-xs text-gray-500 font-medium mt-1">Failed</p>
</div>
</div>
{/* Search */} {/* Search */}
<div className="relative"> <div className="relative">
<span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-base">🔍</span> <span className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-base">🔍</span>