polish launcher and app component tables

This commit is contained in:
2026-07-02 15:03:13 +09:00
parent d226230b25
commit 25dd90076f
2 changed files with 22 additions and 16 deletions
+3 -12
View File
@@ -838,14 +838,13 @@ export function AppManagementPage() {
} }
/> />
<div className="max-h-[430px] overflow-x-auto overflow-y-auto"> <div className="max-h-[430px] overflow-x-auto overflow-y-auto">
<table className="w-full min-w-[1180px] text-left text-sm"> <table className="w-full min-w-[980px] text-left text-sm">
<thead className="border-b border-border text-xs text-muted-foreground"> <thead className="border-b border-border text-xs text-muted-foreground">
<tr> <tr>
<th className="px-5 py-3 font-medium">구성요소</th> <th className="px-5 py-3 font-medium">구성요소</th>
<th className="px-5 py-3 font-medium">역할</th> <th className="px-5 py-3 font-medium">역할</th>
<th className="px-5 py-3 font-medium">유형</th> <th className="px-5 py-3 font-medium">유형</th>
<th className="px-5 py-3 font-medium">워크로드</th> <th className="px-5 py-3 font-medium">워크로드</th>
<th className="px-5 py-3 font-medium">이미지/산출물</th>
<th className="px-5 py-3 font-medium">포트</th> <th className="px-5 py-3 font-medium">포트</th>
<th className="px-5 py-3 font-medium">소스</th> <th className="px-5 py-3 font-medium">소스</th>
<th className="w-24 px-5 py-3 text-right font-medium">관리</th> <th className="w-24 px-5 py-3 text-right font-medium">관리</th>
@@ -854,8 +853,6 @@ export function AppManagementPage() {
<tbody className="divide-y divide-border"> <tbody className="divide-y divide-border">
{!isAppsLoading && (selectedApp.components ?? []).map((component) => { {!isAppsLoading && (selectedApp.components ?? []).map((component) => {
const running = isComponentRunning(component, runtimeResources); const running = isComponentRunning(component, runtimeResources);
const latestBuildResult = component.buildJobs?.[0]?.image || component.buildJobs?.[0]?.artifact || '';
const imageResult = component.externalImage || latestBuildResult;
const imageSourceLabel = component.imageSource === 'EXTERNAL' ? '외부 이미지' : component.imageSource === 'DATABASE' ? 'DB 이미지' : '소스 빌드'; const imageSourceLabel = component.imageSource === 'EXTERNAL' ? '외부 이미지' : component.imageSource === 'DATABASE' ? 'DB 이미지' : '소스 빌드';
return ( return (
<tr key={component.name} className="hover:bg-muted/20"> <tr key={component.name} className="hover:bg-muted/20">
@@ -869,13 +866,7 @@ export function AppManagementPage() {
<td className="px-5 py-3 text-muted-foreground">{imageSourceLabel}</td> <td className="px-5 py-3 text-muted-foreground">{imageSourceLabel}</td>
<td className="px-5 py-3">{component.type}</td> <td className="px-5 py-3">{component.type}</td>
<td className="px-5 py-3 text-muted-foreground"> <td className="px-5 py-3 text-muted-foreground">
<span className="inline-flex items-center gap-2"> {component.workloadType ?? '-'}
{component.workloadType ?? '-'}
{component.storageEnabled ? <Badge tone="amber">스토리지</Badge> : null}
</span>
</td>
<td className="max-w-[280px] px-5 py-3 text-muted-foreground">
<span className="block truncate font-mono text-xs">{imageResult || '-'}</span>
</td> </td>
<td className="max-w-[220px] px-5 py-3 text-muted-foreground"> <td className="max-w-[220px] px-5 py-3 text-muted-foreground">
<span className="block truncate">{formatComponentPorts(component)}</span> <span className="block truncate">{formatComponentPorts(component)}</span>
@@ -904,7 +895,7 @@ export function AppManagementPage() {
})} })}
{!isAppsLoading && (selectedApp.components ?? []).length === 0 ? ( {!isAppsLoading && (selectedApp.components ?? []).length === 0 ? (
<tr> <tr>
<td colSpan={8} className="px-5 py-6 text-center text-muted-foreground">등록된 구성요소가 없습니다.</td> <td colSpan={7} className="px-5 py-6 text-center text-muted-foreground">등록된 구성요소가 없습니다.</td>
</tr> </tr>
) : null} ) : null}
</tbody> </tbody>
+19 -4
View File
@@ -47,7 +47,7 @@ const launcherComponents: LauncherComponentDefinition[] = [
{ {
key: 'launcher-db', key: 'launcher-db',
name: '런처 DB', name: '런처 DB',
description: '런처 앱, 버전, 사용자 앱 권한 데이터를 저장하는 PostgreSQL입니다.', description: '런처 앱, 버전, 사용자 앱 권한 데이터를 저장합니다.',
type: 'database', type: 'database',
kind: 'StatefulSet', kind: 'StatefulSet',
workloadName: 'studio-launcher-db', workloadName: 'studio-launcher-db',
@@ -95,6 +95,20 @@ function relatedResources(resources: RuntimeResource[], component: LauncherCompo
}); });
} }
function launcherArtifactLabel(version?: LauncherVersion) {
if (!version) return '등록된 릴리즈 없음';
if (version.artifact) return version.artifact;
if (!version.downloadUrl) return '배포 파일 미등록';
const segments = version.downloadUrl.split('/').filter(Boolean);
return segments[segments.length - 1] ?? '배포 파일 미등록';
}
function launcherVersionMeta(version?: LauncherVersion) {
if (!version) return '버전 관리에서 릴리즈를 등록하세요';
const minimum = version.minimumVersion ? `최소 지원 ${version.minimumVersion}` : '최소 지원 미설정';
return `${minimum} · ${version.isActive ? '활성' : '비활성'}`;
}
export function LauncherComponentsPage() { export function LauncherComponentsPage() {
const { toast } = useToast(); const { toast } = useToast();
const [versions, setVersions] = useState<LauncherVersion[]>([]); const [versions, setVersions] = useState<LauncherVersion[]>([]);
@@ -175,7 +189,8 @@ export function LauncherComponentsPage() {
<Card className="p-5"> <Card className="p-5">
<p className="text-sm text-muted-foreground">런처 최신 버전</p> <p className="text-sm text-muted-foreground">런처 최신 버전</p>
<p className="mt-3 text-2xl font-semibold">{activeVersion?.version ?? '-'}</p> <p className="mt-3 text-2xl font-semibold">{activeVersion?.version ?? '-'}</p>
<p className="mt-1 truncate text-xs text-muted-foreground">{activeVersion?.downloadUrl ?? '등록된 릴리즈 없음'}</p> <p className="mt-1 truncate text-xs font-medium text-foreground">{launcherArtifactLabel(activeVersion)}</p>
<p className="mt-1 truncate text-xs text-muted-foreground">{launcherVersionMeta(activeVersion)}</p>
</Card> </Card>
<Card className="p-5"> <Card className="p-5">
<p className="text-sm text-muted-foreground">최근 클라이언트 빌드</p> <p className="text-sm text-muted-foreground">최근 클라이언트 빌드</p>
@@ -261,8 +276,8 @@ export function LauncherComponentsPage() {
) : null} ) : null}
</div> </div>
<div className="overflow-hidden rounded-md border border-border"> <div className="min-w-0 overflow-x-auto rounded-md border border-border">
<table className="w-full min-w-[680px] text-left text-sm"> <table className="w-full min-w-[760px] whitespace-nowrap text-left text-sm">
<thead className="border-b border-border bg-muted/30 text-xs text-muted-foreground"> <thead className="border-b border-border bg-muted/30 text-xs text-muted-foreground">
<tr> <tr>
<th className="px-4 py-3 font-medium">Kind</th> <th className="px-4 py-3 font-medium">Kind</th>