From c4b319c928fa8c7a30b44a743f10b2218020a66f Mon Sep 17 00:00:00 2001 From: chyoo Date: Thu, 9 Jul 2026 11:01:31 +0900 Subject: [PATCH] improve common app pipeline history layout --- src/App.tsx | 26 ++++++++++++++---- src/pages/common-apps/CommonAppBuildPage.tsx | 29 +++++++++++++++----- src/pages/common-apps/common-app-api.ts | 2 ++ 3 files changed, 44 insertions(+), 13 deletions(-) diff --git a/src/App.tsx b/src/App.tsx index 2f41e8b..d46517f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -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 = { 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 ( )) - ) : resources.length === 0 ? ( + ) : visibleResources.length === 0 ? (
표시할 런타임 리소스가 없습니다.
) : null} - {!loading && resources.slice(0, 8).map((item) => ( + {!loading && visibleResources.slice(0, 8).map((item) => (
diff --git a/src/pages/common-apps/CommonAppBuildPage.tsx b/src/pages/common-apps/CommonAppBuildPage.tsx index 6fa6cb4..69a86fe 100644 --- a/src/pages/common-apps/CommonAppBuildPage.tsx +++ b/src/pages/common-apps/CommonAppBuildPage.tsx @@ -16,6 +16,8 @@ export function CommonAppBuildPage() { const [selectedRepoId, setSelectedRepoId] = useState(''); const [history, setHistory] = useState([]); const [historyPage, setHistoryPage] = useState(1); + const [historyTotal, setHistoryTotal] = useState(null); + const [historyTotalPages, setHistoryTotalPages] = useState(null); const [hasNextHistoryPage, setHasNextHistoryPage] = useState(false); const [loading, setLoading] = useState(true); const [historyLoading, setHistoryLoading] = useState(false); @@ -64,6 +66,8 @@ export function CommonAppBuildPage() { let canceled = false; if (!selectedRepoId) { setHistory([]); + setHistoryTotal(null); + setHistoryTotalPages(null); setHasNextHistoryPage(false); return; } @@ -73,11 +77,15 @@ export function CommonAppBuildPage() { .then((result) => { if (canceled) return; setHistory(result.pipelines ?? []); + setHistoryTotal(result.total ?? null); + setHistoryTotalPages(result.totalPages ?? null); setHasNextHistoryPage(Boolean(result.hasNext)); }) .catch((error: Error) => { if (canceled) return; setHistory([]); + setHistoryTotal(null); + setHistoryTotalPages(null); setHasNextHistoryPage(false); toast({ type: 'error', title: '파이프라인 이력 조회 실패', message: error.message }); }) @@ -139,7 +147,10 @@ export function CommonAppBuildPage() {

{formatRepositoryName(pipeline.repository)}

-

{pipeline.message || '-'}

+

+ #{pipeline.number ?? '-'} + {pipeline.message || '-'} +

); })} @@ -158,31 +169,35 @@ export function CommonAppBuildPage() {
표시할 파이프라인 이력이 없습니다.
) : null} {history.map((pipeline) => ( -
-
+
+
#{pipeline.number ?? pipeline.id}

{pipeline.message || '-'}

-
-
{isActivePipelineStatus(pipeline.status) ? : null} {pipeline.status || '-'} +
+
{pipeline.branch || '-'} SHA1 {formatShortSha(pipeline.commitSha)} 소요 {formatDuration(pipeline.durationSeconds)} - {formatPipelineRelativeTime(pipeline.createdAt)} + {formatPipelineRelativeTime(pipeline.createdAt)}
))}
- 현재 {historyPage}페이지 + + {historyTotal !== null && historyTotalPages !== null + ? `총 ${historyTotal}건 · ${historyPage} / ${historyTotalPages}` + : `현재 ${historyPage}페이지`} +