From 802d1ba46468150dc2581e510b824e3b8f85d9f3 Mon Sep 17 00:00:00 2001 From: HYOJIN Date: Wed, 15 Apr 2026 12:55:24 +0900 Subject: [PATCH] =?UTF-8?q?feat(ui):=20KCG=20=EB=B8=8C=EB=9E=9C=EB=94=A9?= =?UTF-8?q?=20=EC=A0=81=EC=9A=A9=20=EB=B0=8F=20=EB=A0=88=EC=9D=B4=EC=95=84?= =?UTF-8?q?=EC=9B=83=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?(#48)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - S&P/SNP 텍스트를 KCG로 변경 (타이틀, 사이드바, 대시보드) - 사이드 메뉴 한글화 (모니터링, 통계, API 키, 관리자) - 테넌트 → 부서 텍스트 변경 - MainLayout 헤더/사이드바 레퍼런스 디자인 적용 (아이콘, 인디케이터, 알약 역할 스위처) - ApiHubLayout 헤더/사이드바 레퍼런스 디자인 적용 (도메인 색상 팔레트, 도트 인디케이터) - 서비스 상태 카드 서비스 코드 제거 - 대시보드 배너 설명 텍스트 변경 - 도메인 이미지 파일명 한글 변경 Co-Authored-By: Claude Opus 4.6 (1M context) --- frontend/index.html | 2 +- .../domains/{compliance.jpg => 규정 준수.jpg} | Bin .../images/domains/{ais.jpg => 선박 신호.jpg} | Bin .../domains/{ship.jpg => 선박 정보.jpg} | Bin .../domains/{risk.jpg => 위험 지표.jpg} | Bin .../domains/{company.jpg => 조선소 정보.jpg} | Bin frontend/src/layouts/ApiHubLayout.tsx | 219 ++++++------ frontend/src/layouts/MainLayout.tsx | 337 +++++++++++++----- .../src/pages/apihub/ApiHubDashboardPage.tsx | 4 +- .../pages/monitoring/ServiceStatusPage.tsx | 1 - 10 files changed, 365 insertions(+), 198 deletions(-) rename frontend/public/images/domains/{compliance.jpg => 규정 준수.jpg} (100%) rename frontend/public/images/domains/{ais.jpg => 선박 신호.jpg} (100%) rename frontend/public/images/domains/{ship.jpg => 선박 정보.jpg} (100%) rename frontend/public/images/domains/{risk.jpg => 위험 지표.jpg} (100%) rename frontend/public/images/domains/{company.jpg => 조선소 정보.jpg} (100%) diff --git a/frontend/index.html b/frontend/index.html index 0dd76ac..3b3b6a6 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -8,7 +8,7 @@ - SNP Connection Monitoring + KCG Connection Monitoring
diff --git a/frontend/public/images/domains/compliance.jpg b/frontend/public/images/domains/규정 준수.jpg similarity index 100% rename from frontend/public/images/domains/compliance.jpg rename to frontend/public/images/domains/규정 준수.jpg diff --git a/frontend/public/images/domains/ais.jpg b/frontend/public/images/domains/선박 신호.jpg similarity index 100% rename from frontend/public/images/domains/ais.jpg rename to frontend/public/images/domains/선박 신호.jpg diff --git a/frontend/public/images/domains/ship.jpg b/frontend/public/images/domains/선박 정보.jpg similarity index 100% rename from frontend/public/images/domains/ship.jpg rename to frontend/public/images/domains/선박 정보.jpg diff --git a/frontend/public/images/domains/risk.jpg b/frontend/public/images/domains/위험 지표.jpg similarity index 100% rename from frontend/public/images/domains/risk.jpg rename to frontend/public/images/domains/위험 지표.jpg diff --git a/frontend/public/images/domains/company.jpg b/frontend/public/images/domains/조선소 정보.jpg similarity index 100% rename from frontend/public/images/domains/company.jpg rename to frontend/public/images/domains/조선소 정보.jpg diff --git a/frontend/src/layouts/ApiHubLayout.tsx b/frontend/src/layouts/ApiHubLayout.tsx index 813660b..331c72a 100644 --- a/frontend/src/layouts/ApiHubLayout.tsx +++ b/frontend/src/layouts/ApiHubLayout.tsx @@ -26,6 +26,20 @@ const parseIconPaths = (iconPath: string | null): string[] => { return matches.length > 0 ? matches : [iconPath]; }; +/** 도메인 인덱스를 기반으로 색상 팔레트 반환 */ +const DOMAIN_COLORS = [ + { bg: 'bg-indigo-500', dot: 'bg-indigo-400', badge: 'bg-indigo-900/60 text-indigo-300' }, + { bg: 'bg-violet-500', dot: 'bg-violet-400', badge: 'bg-violet-900/60 text-violet-300' }, + { bg: 'bg-sky-500', dot: 'bg-sky-400', badge: 'bg-sky-900/60 text-sky-300' }, + { bg: 'bg-emerald-500', dot: 'bg-emerald-400', badge: 'bg-emerald-900/60 text-emerald-300' }, + { bg: 'bg-amber-500', dot: 'bg-amber-400', badge: 'bg-amber-900/60 text-amber-300' }, + { bg: 'bg-rose-500', dot: 'bg-rose-400', badge: 'bg-rose-900/60 text-rose-300' }, + { bg: 'bg-teal-500', dot: 'bg-teal-400', badge: 'bg-teal-900/60 text-teal-300' }, + { bg: 'bg-orange-500', dot: 'bg-orange-400', badge: 'bg-orange-900/60 text-orange-300' }, +] as const; + +const getDomainColor = (index: number) => DOMAIN_COLORS[index % DOMAIN_COLORS.length]; + interface FlatDomainGroup { domain: string; iconPath: string | null; @@ -103,49 +117,33 @@ const ApiHubLayoutInner = () => { const isSearching = searchQuery.trim().length > 0; + const totalApiCount = useMemo(() => domainGroups.reduce((sum, dg) => sum + dg.apis.length, 0), [domainGroups]); + return (
{/* Sidebar */} - {/* Main content */} -
- {/* Header */} -
+
+ {/* 헤더 */} +
-
+
+ {/* 테마 토글 */} - Role: -
+ {/* 역할 스위처 (알약 형태) */} +
{ROLES.map((role) => (
- {/* Content */} -
+ {/* 콘텐츠 */} +
diff --git a/frontend/src/layouts/MainLayout.tsx b/frontend/src/layouts/MainLayout.tsx index 8d0422f..e91db67 100644 --- a/frontend/src/layouts/MainLayout.tsx +++ b/frontend/src/layouts/MainLayout.tsx @@ -1,50 +1,178 @@ import { useState } from 'react'; -import { Outlet, NavLink } from 'react-router-dom'; +import { Outlet, NavLink, Link } from 'react-router-dom'; import { useAuth } from '../hooks/useAuth'; import { useTheme } from '../hooks/useTheme'; +interface NavItem { + label: string; + path: string; + icon: React.ReactNode; + adminManagerOnly?: boolean; +} + interface NavGroup { label: string; - items: { label: string; path: string }[]; + items: NavItem[]; adminOnly?: boolean; } +const iconProps = { + fill: 'none' as const, + viewBox: '0 0 24 24', + stroke: 'currentColor', + strokeWidth: 1.8, + strokeLinecap: 'round' as const, + strokeLinejoin: 'round' as const, + width: 18, + height: 18, +}; + +const IconDashboard = () => ( + + + + + + +); + +const IconRequestLog = () => ( + + + + + + +); + +const IconServiceStatus = () => ( + + + +); + +const IconServiceStats = () => ( + + + + + +); + +const IconUserStats = () => ( + + + + +); + +const IconApiStats = () => ( + + + + +); + +const IconTenantStats = () => ( + + + + + +); + +const IconUsageTrend = () => ( + + + + +); + +const IconMyKey = () => ( + + + + +); + +const IconKeyRequest = () => ( + + + + +); + +const IconKeyManage = () => ( + + + +); + +const IconService = () => ( + + + + + +); + +const IconDomain = () => ( + + + + + +); + +const IconApiManage = () => ( + + + +); + +const IconSampleCode = () => ( + + + + +); + const navGroups: NavGroup[] = [ { - label: 'Monitoring', + label: '모니터링', items: [ - { label: 'Request Logs', path: '/monitoring/request-logs' }, - { label: 'Service Status', path: '/monitoring/service-status' }, + { label: '요청 로그', path: '/monitoring/request-logs', icon: }, + { label: '서비스 상태', path: '/monitoring/service-status', icon: }, ], }, { - label: 'Statistics', + label: '통계', items: [ - { label: '서비스 통계', path: '/statistics/services' }, - { label: '사용자 통계', path: '/statistics/users' }, - { label: 'API 통계', path: '/statistics/apis' }, - { label: '테넌트 통계', path: '/statistics/tenants' }, - { label: '사용량 추이', path: '/statistics/usage-trend' }, + { label: '서비스 통계', path: '/statistics/services', icon: }, + { label: '사용자 통계', path: '/statistics/users', icon: }, + { label: 'API 통계', path: '/statistics/apis', icon: }, + { label: '부서 통계', path: '/statistics/tenants', icon: }, + { label: '사용량 추이', path: '/statistics/usage-trend', icon: }, ], }, { - label: 'API Keys', + label: 'API 키', items: [ - { label: 'My Keys', path: '/apikeys/my-keys' }, - { label: 'Request', path: '/apikeys/request' }, - { label: 'Admin', path: '/apikeys/admin' }, + { label: '내 키', path: '/apikeys/my-keys', icon: }, + { label: '키 신청', path: '/apikeys/request', icon: }, + { label: '키 관리', path: '/apikeys/admin', icon: , adminManagerOnly: true }, ], }, { - label: 'Admin', + label: '관리자', adminOnly: true, items: [ - { label: 'Services', path: '/admin/services' }, - { label: 'Domains', path: '/admin/domains' }, - { label: 'API 관리', path: '/admin/apis' }, - { label: '공통 샘플 코드', path: '/admin/sample-code' }, - { label: 'Users', path: '/admin/users' }, - { label: 'Tenants', path: '/admin/tenants' }, + { label: '서비스', path: '/admin/services', icon: }, + { label: '도메인', path: '/admin/domains', icon: }, + { label: 'API 관리', path: '/admin/apis', icon: }, + { label: '공통 샘플 코드', path: '/admin/sample-code', icon: }, + { label: '사용자', path: '/admin/users', icon: }, + { label: '부서', path: '/admin/tenants', icon: }, ], }, ]; @@ -55,10 +183,10 @@ const MainLayout = () => { const { user, setRole } = useAuth(); const { theme, toggleTheme } = useTheme(); const [openGroups, setOpenGroups] = useState>({ - Monitoring: true, - Statistics: true, - 'API Keys': true, - Admin: true, + '모니터링': true, + '통계': true, + 'API 키': true, + '관리자': true, }); const toggleGroup = (label: string) => { @@ -70,43 +198,64 @@ const MainLayout = () => { return (
{/* Sidebar */} -