export const COMMON_APP_NAMESPACE = 'studio-system'; export type CommonAppComponent = { key: string; name: string; description: string; repository: string; kind: 'Deployment'; workloadName: string; serviceName?: string; }; export const commonAppComponents: CommonAppComponent[] = [ { key: 'studio-admin-fe', name: 'Admin FE', description: '관리자 웹 콘솔 프론트엔드입니다.', repository: 'studio/studio-admin-fe', kind: 'Deployment', workloadName: 'studio-admin-fe', serviceName: 'studio-admin-fe' }, { key: 'studio-admin-be', name: 'Admin BE', description: '관리자 웹 콘솔 API 서버입니다.', repository: 'studio/studio-admin-be', kind: 'Deployment', workloadName: 'studio-admin-be', serviceName: 'studio-admin-be' }, { key: 'studio-auth-be', name: 'Auth BE', description: '공통 인증, 사용자, 라이센스 API 서버입니다.', repository: 'studio/studio-auth-be', kind: 'Deployment', workloadName: 'studio-auth-be', serviceName: 'studio-auth-be' }, { key: 'platform-gateway', name: 'Platform Gateway', description: 'Headscale, Agent, 클러스터 연동용 게이트웨이입니다.', repository: 'studio/platform-gateway', kind: 'Deployment', workloadName: 'platform-gateway', serviceName: 'platform-gateway' } ]; export function formatPipelineTime(value?: number | null) { if (!value) return '-'; const ms = value < 10_000_000_000 ? value * 1000 : value; return new Date(ms).toLocaleString(); } export function formatPipelineRelativeTime(value?: number | null) { if (!value) return '-'; const ms = value < 10_000_000_000 ? value * 1000 : value; const seconds = Math.max(0, Math.floor((Date.now() - ms) / 1000)); if (seconds < 60) return '방금 전'; const minutes = Math.floor(seconds / 60); if (minutes < 60) return `${minutes}분 전`; const hours = Math.floor(minutes / 60); if (hours < 24) return `${hours}시간 전`; const days = Math.floor(hours / 24); if (days < 30) return `${days}일 전`; return new Intl.DateTimeFormat('ko-KR', { month: 'short', day: 'numeric' }).format(new Date(ms)); } export function formatRepositoryName(repository: string) { return repository.replace('/', ' / '); } export function pipelineStatusTone(status?: string | null): 'green' | 'amber' | 'red' | 'blue' | 'neutral' { const value = (status ?? '').toLowerCase(); if (['success', 'succeeded', 'passed'].includes(value)) return 'green'; if (['running', 'started'].includes(value)) return 'blue'; if (['pending', 'queued', 'blocked', 'created'].includes(value)) return 'amber'; if (['failure', 'failed', 'error', 'killed', 'declined'].includes(value)) return 'red'; return 'neutral'; } export function pipelineStatusDotClass(status?: string | null) { const value = (status ?? '').toLowerCase(); if (['success', 'succeeded', 'passed'].includes(value)) return 'bg-emerald-400 shadow-[0_0_0_3px_rgba(52,211,153,0.15)]'; if (['running', 'started'].includes(value)) return 'bg-sky-400 shadow-[0_0_0_3px_rgba(56,189,248,0.15)]'; if (['pending', 'queued', 'blocked', 'created'].includes(value)) return 'bg-amber-400 shadow-[0_0_0_3px_rgba(251,191,36,0.15)]'; if (['failure', 'failed', 'error', 'killed', 'declined'].includes(value)) return 'bg-rose-400 shadow-[0_0_0_3px_rgba(251,113,133,0.15)]'; return 'bg-muted-foreground/60'; } export function isActivePipelineStatus(status?: string | null) { const value = (status ?? '').toLowerCase(); return ['running', 'started', 'pending', 'queued', 'blocked', 'created'].includes(value); }