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:
@@ -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>
|
||||||
|
|||||||
Reference in New Issue
Block a user