diff --git a/frontend/src/design-system/sections/CatalogSection.tsx b/frontend/src/design-system/sections/CatalogSection.tsx index e18a475..0ae5c99 100644 --- a/frontend/src/design-system/sections/CatalogSection.tsx +++ b/frontend/src/design-system/sections/CatalogSection.tsx @@ -11,19 +11,30 @@ import { CATALOG_REGISTRY, type CatalogEntry } from '@shared/constants/catalogRe */ interface AnyMeta { - code: string; + /** 일부 카탈로그는 code 없이 Record key 만 사용 (예: PERFORMANCE_STATUS_META) */ + code?: string; intent?: BadgeIntent; fallback?: { ko: string; en: string }; classes?: string | { bg?: string; text?: string; border?: string }; - label?: string; + /** 문자열 라벨 또는 { ko, en } 객체 라벨 양쪽 지원 */ + label?: string | { ko: string; en: string }; } -function getKoLabel(meta: AnyMeta): string { - return meta.fallback?.ko ?? meta.label ?? meta.code; +function getKoLabel(meta: AnyMeta, fallbackKey: string): string { + if (meta.fallback?.ko) return meta.fallback.ko; + if (meta.label && typeof meta.label === 'object' && 'ko' in meta.label) { + return meta.label.ko; + } + if (typeof meta.label === 'string') return meta.label; + return meta.code ?? fallbackKey; } function getEnLabel(meta: AnyMeta): string | undefined { - return meta.fallback?.en; + if (meta.fallback?.en) return meta.fallback.en; + if (meta.label && typeof meta.label === 'object' && 'en' in meta.label) { + return meta.label.en; + } + return undefined; } function getFallbackClasses(meta: AnyMeta): string | undefined { @@ -55,17 +66,19 @@ function renderBadge(meta: AnyMeta, label: string): ReactNode { } function CatalogBadges({ entry }: { entry: CatalogEntry }) { - const items = Object.values(entry.items) as AnyMeta[]; + // Record key 를 안정적 식별자로 사용 (일부 카탈로그는 meta.code 없음) + const items = Object.entries(entry.items) as [string, AnyMeta][]; return (
- {items.map((meta) => { - const koLabel = getKoLabel(meta); + {items.map(([key, meta]) => { + const displayCode = meta.code ?? key; + const koLabel = getKoLabel(meta, key); const enLabel = getEnLabel(meta); - const trkId = `${entry.showcaseId}-${meta.code}`; + const trkId = `${entry.showcaseId}-${displayCode}`; return ( - + - {meta.code} + {displayCode}
{renderBadge(meta, koLabel)}
diff --git a/frontend/src/features/monitoring/SystemStatusPanel.tsx b/frontend/src/features/monitoring/SystemStatusPanel.tsx index b90222e..3c4ab09 100644 --- a/frontend/src/features/monitoring/SystemStatusPanel.tsx +++ b/frontend/src/features/monitoring/SystemStatusPanel.tsx @@ -101,9 +101,9 @@ export function SystemStatusPanel() { status={stats ? 'CONNECTED' : 'DISCONNECTED'} statusIntent={stats ? 'success' : 'critical'} details={[ - ['선박 분석', stats ? `${stats.total.toLocaleString()}건` : '-'], - ['클러스터', stats ? `${stats.clusterCount}` : '-'], - ['어구 그룹', stats ? `${stats.gearGroups}` : '-'], + ['선박 분석', stats?.total != null ? `${stats.total.toLocaleString()}건` : '-'], + ['클러스터', stats?.clusterCount != null ? `${stats.clusterCount}` : '-'], + ['어구 그룹', stats?.gearGroups != null ? `${stats.gearGroups}` : '-'], ]} /> @@ -124,10 +124,10 @@ export function SystemStatusPanel() { {/* 위험도 분포 */} {stats && (
- - - - + + + +
)}