improve common app pipeline history layout
ci/woodpecker/push/woodpecker Pipeline was successful

This commit is contained in:
2026-07-09 11:01:31 +09:00
parent 15f051757c
commit c4b319c928
3 changed files with 44 additions and 13 deletions
+20 -6
View File
@@ -865,6 +865,17 @@ function isRuntimeResourceHealthy(resource: DashboardRuntimeResource) {
return Number.isFinite(ready) && Number.isFinite(total) && total > 0 && ready === total;
}
function isIgnoredRuntimeResource(resource: DashboardRuntimeResource) {
const normalizedName = resource.name.toLowerCase();
const normalizedStatus = resource.status.toLowerCase();
if (resource.kind === 'Pod' && normalizedStatus === 'completed') return true;
if (resource.kind === 'Job' && ['completed', 'succeeded', 'success'].includes(normalizedStatus)) return true;
if ((resource.kind === 'Pod' || resource.kind === 'Job') && normalizedName.includes('migration') && ['completed', 'succeeded', 'success'].includes(normalizedStatus)) {
return true;
}
return false;
}
function formatJobStatus(status?: string | null) {
const labels: Record<string, string> = {
PENDING: '대기',
@@ -905,8 +916,9 @@ function MetricCards({
const activeApps = dashboardApps.filter((app) => isActiveStatus(app.status)).length;
const activeSessions = sessions.filter((session) => session.state === '활성').length;
const activeWorkTotal = activeWork.builds + activeWork.releases + activeWork.deployments;
const healthyRuntimeCount = runtimeResources.filter(isRuntimeResourceHealthy).length;
const runtimeRate = runtimeResources.length ? Math.round((healthyRuntimeCount / runtimeResources.length) * 100) : null;
const countedRuntimeResources = runtimeResources.filter((resource) => !isIgnoredRuntimeResource(resource));
const healthyRuntimeCount = countedRuntimeResources.filter(isRuntimeResourceHealthy).length;
const runtimeRate = countedRuntimeResources.length ? Math.round((healthyRuntimeCount / countedRuntimeResources.length) * 100) : null;
const cards = [
{
label: '활성 앱',
@@ -932,8 +944,8 @@ function MetricCards({
{
label: '런타임 정상률',
value: loading || runtimeRate === null ? '-' : `${runtimeRate}%`,
delta: runtimeResources.length ? `${healthyRuntimeCount}/${runtimeResources.length}` : '리소스 없음',
hint: 'namespace 리소스 Ready 기준',
delta: countedRuntimeResources.length ? `${healthyRuntimeCount}/${countedRuntimeResources.length}` : '리소스 없음',
hint: '완료된 Job/마이그레이션 제외',
tone: runtimeRate !== null && runtimeRate < 80 ? 'red' as const : 'green' as const
}
];
@@ -1258,6 +1270,8 @@ function AppRegistryTable({
}
function ContainerPanel({ resources, loading, onOpen }: { resources: DashboardRuntimeResource[]; loading: boolean; onOpen: () => void }) {
const visibleResources = resources.filter((resource) => !isIgnoredRuntimeResource(resource));
return (
<Card>
<CardHeader
@@ -1297,10 +1311,10 @@ function ContainerPanel({ resources, loading, onOpen }: { resources: DashboardRu
</div>
</div>
))
) : resources.length === 0 ? (
) : visibleResources.length === 0 ? (
<div className="px-5 py-12 text-center text-sm text-muted-foreground">표시할 런타임 리소스가 없습니다.</div>
) : null}
{!loading && resources.slice(0, 8).map((item) => (
{!loading && visibleResources.slice(0, 8).map((item) => (
<div key={`${item.namespace}-${item.kind}-${item.name}`} className="px-5 py-4">
<div className="flex items-center justify-between gap-3">
<div className="min-w-0">