feat(design): 디자인 시스템 폰트 업스케일 및 전체 탭 토큰 적용 #169
22
CLAUDE.md
22
CLAUDE.md
@ -127,6 +127,28 @@ wing/
|
||||
|
||||
## 진행 중 작업 (완료 후 삭제)
|
||||
|
||||
### 폰트 크기 업스케일 작업 (진행 중)
|
||||
|
||||
반드시 `memory/font-upscale-plan.md`를 읽고 Phase 진행 상황을 확인할 것.
|
||||
|
||||
**토큰 변경 매핑 (이름 유지, 값만 변경):**
|
||||
- `caption`/`label-2`/`title-6`: 11px → **12px** (0.75rem)
|
||||
- `label-1`/`title-5`: 12px → **13px** (0.8125rem)
|
||||
- `body-2`/`title-4`: 13px → **14px** (0.875rem)
|
||||
- `body-1`/`title-3`: 14px → **16px** (1rem)
|
||||
|
||||
**네비게이션 클래스 교체:**
|
||||
- TopBar 메인탭: `text-title-4` → `text-title-2` (16px)
|
||||
- SubMenuBar 서브탭: `text-title-5` → `text-title-4` (14px)
|
||||
|
||||
**작업 범위:**
|
||||
- Phase 1: tailwind.config.js + base.css 토큰 값 수정
|
||||
- Phase 2: components.css 하드코딩(27곳) + wing.css `.wing-tab` text-xs→text-caption
|
||||
- Phase 3: TopBar.tsx + SubMenuBar.tsx 클래스 교체
|
||||
- Phase 4: text-xs→text-caption, text-sm→text-body-2 스크립트 교체 (design 페이지 제외, 608건)
|
||||
- Phase 5: prediction 탭 인라인 fontSize 수정
|
||||
- Phase 6 (보류): wing-header-bar 패딩 — 폰트 변경 후 유저 확인 후 진행
|
||||
|
||||
### 디자인 시스템 폰트+색상 통일 작업
|
||||
|
||||
compact 후 반드시 `memory/design-system-work.md`를 읽고 작업 상태(완료/미완료 컴포넌트)를 확인할 것.
|
||||
|
||||
@ -120,7 +120,7 @@ export function LoginPage() {
|
||||
</label>
|
||||
<div className="relative">
|
||||
<span
|
||||
className="absolute text-sm text-fg-disabled pointer-events-none"
|
||||
className="absolute text-body-2 text-fg-disabled pointer-events-none"
|
||||
style={{ left: 12, top: '50%', transform: 'translateY(-50%)' }}
|
||||
>
|
||||
<svg
|
||||
@ -174,7 +174,7 @@ export function LoginPage() {
|
||||
</label>
|
||||
<div className="relative">
|
||||
<span
|
||||
className="absolute text-sm text-fg-disabled pointer-events-none"
|
||||
className="absolute text-body-2 text-fg-disabled pointer-events-none"
|
||||
style={{ left: 12, top: '50%', transform: 'translateY(-50%)' }}
|
||||
>
|
||||
<svg
|
||||
@ -249,7 +249,7 @@ export function LoginPage() {
|
||||
color: '#67e8f9',
|
||||
}}
|
||||
>
|
||||
<span className="text-sm shrink-0 mt-px">
|
||||
<span className="text-body-2 shrink-0 mt-px">
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
@ -303,7 +303,7 @@ export function LoginPage() {
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isLoading}
|
||||
className="w-full text-color-accent text-sm font-bold rounded-md border"
|
||||
className="w-full text-color-accent text-body-2 font-bold rounded-md border"
|
||||
style={{
|
||||
padding: '12px',
|
||||
background: isLoading
|
||||
|
||||
@ -21,7 +21,7 @@ export function SubMenuBar({ activeMainTab }: SubMenuBarProps) {
|
||||
key={item.id}
|
||||
onClick={() => setActiveSubTab(item.id)}
|
||||
className={`
|
||||
px-4 py-2.5 text-title-5 font-medium transition-all duration-200
|
||||
px-4 py-2.5 text-title-4 font-medium transition-all duration-200
|
||||
font-korean tracking-navigation
|
||||
${activeSubTab === item.id ? 'text-color-accent' : 'text-fg-sub hover:text-fg'}
|
||||
`}
|
||||
|
||||
@ -56,11 +56,7 @@ export function TopBar({ activeTab, onTabChange }: TopBarProps) {
|
||||
className="flex items-center hover:opacity-80 transition-opacity cursor-pointer"
|
||||
title="홈으로 이동"
|
||||
>
|
||||
<img
|
||||
src="/wing_logo_white.svg"
|
||||
alt="WING 해양환경 위기대응"
|
||||
className="h-3.5 wing-logo"
|
||||
/>
|
||||
<img src="/wing_logo_white.svg" alt="WING 해양환경 위기대응" className="h-5 wing-logo" />
|
||||
</button>
|
||||
|
||||
{/* Divider */}
|
||||
@ -87,7 +83,7 @@ export function TopBar({ activeTab, onTabChange }: TopBarProps) {
|
||||
onClick={handleClick}
|
||||
title={tab.label}
|
||||
className={`
|
||||
px-2.5 xl:px-4 py-2 text-title-4 font-bold transition-all duration-200
|
||||
px-2.5 xl:px-4 py-2 text-title-2 font-bold transition-all duration-200
|
||||
font-korean tracking-navigation border-b-2 border-transparent
|
||||
${isIncident ? 'ml-1' : ''}
|
||||
${isMonitor ? 'ml-1 flex items-center gap-1.5' : ''}
|
||||
@ -127,7 +123,7 @@ export function TopBar({ activeTab, onTabChange }: TopBarProps) {
|
||||
{/* Right Section */}
|
||||
<div className="flex items-center gap-3">
|
||||
{/* Status Badge */}
|
||||
{/* <div className="flex items-center gap-2 px-3 py-1.5 bg-[rgba(239,68,68,0.1)] border border-[rgba(239,68,68,0.2)] rounded-sm text-xs font-medium text-color-danger animate-pulse">
|
||||
{/* <div className="flex items-center gap-2 px-3 py-1.5 bg-[rgba(239,68,68,0.1)] border border-[rgba(239,68,68,0.2)] rounded-sm text-caption font-medium text-color-danger animate-pulse">
|
||||
<div className="w-1.5 h-1.5 rounded-full bg-color-danger animate-pulse" />
|
||||
사고 진행중
|
||||
</div> */}
|
||||
|
||||
@ -141,7 +141,7 @@ export function BacktrackReplayBar({
|
||||
className="w-2 h-2 rounded-full bg-color-tertiary"
|
||||
style={{ boxShadow: '0 0 8px rgba(168,85,247,0.5)' }}
|
||||
/>
|
||||
<span className="text-xs font-bold">역추적 리플레이</span>
|
||||
<span className="text-caption font-bold">역추적 리플레이</span>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-1.5">
|
||||
@ -180,7 +180,7 @@ export function BacktrackReplayBar({
|
||||
{/* Play/Pause */}
|
||||
<button
|
||||
onClick={handlePlayClick}
|
||||
className="shrink-0 w-9 h-9 rounded-full flex items-center justify-center text-sm cursor-pointer"
|
||||
className="shrink-0 w-9 h-9 rounded-full flex items-center justify-center text-body-2 cursor-pointer"
|
||||
style={{
|
||||
background: isPlaying ? 'var(--color-tertiary)' : 'rgba(168,85,247,0.15)',
|
||||
border: `2px solid ${isPlaying ? 'var(--color-tertiary)' : 'rgba(168,85,247,0.4)'}`,
|
||||
|
||||
@ -414,7 +414,10 @@ export function MapView({
|
||||
longitude: lng,
|
||||
latitude: lat,
|
||||
content: (
|
||||
<div className="text-xs font-korean" style={{ minWidth: '180px', maxWidth: '260px' }}>
|
||||
<div
|
||||
className="text-caption font-korean"
|
||||
style={{ minWidth: '180px', maxWidth: '260px' }}
|
||||
>
|
||||
<div className="font-semibold mb-1.5 pb-1 border-b border-[rgba(0,0,0,0.12)]">
|
||||
{String(category ?? '민감자원')}
|
||||
</div>
|
||||
@ -535,7 +538,7 @@ export function MapView({
|
||||
longitude: d.lon,
|
||||
latitude: d.lat,
|
||||
content: (
|
||||
<div className="text-xs">
|
||||
<div className="text-caption">
|
||||
<strong>
|
||||
{modelKey} 입자 #{(d.particle ?? 0) + 1}
|
||||
</strong>
|
||||
@ -604,7 +607,7 @@ export function MapView({
|
||||
longitude: info.coordinate?.[0] ?? 0,
|
||||
latitude: info.coordinate?.[1] ?? 0,
|
||||
content: (
|
||||
<div className="text-xs" style={{ minWidth: '140px' }}>
|
||||
<div className="text-caption" style={{ minWidth: '140px' }}>
|
||||
<strong style={{ color: PRIORITY_COLORS[d.priority] }}>{d.name}</strong>
|
||||
<br />
|
||||
우선순위: {PRIORITY_LABELS[d.priority] || d.priority}
|
||||
@ -919,7 +922,7 @@ export function MapView({
|
||||
longitude: info.coordinate[0],
|
||||
latitude: info.coordinate[1],
|
||||
content: (
|
||||
<div className="text-xs leading-relaxed" style={{ minWidth: 180 }}>
|
||||
<div className="text-caption leading-relaxed" style={{ minWidth: 180 }}>
|
||||
<strong className="text-color-warning">
|
||||
{dispersionResult.substance} 대기확산 면적
|
||||
</strong>
|
||||
@ -1009,7 +1012,7 @@ export function MapView({
|
||||
longitude: d.lon,
|
||||
latitude: d.lat,
|
||||
content: (
|
||||
<div className="text-xs" style={{ minWidth: '130px' }}>
|
||||
<div className="text-caption" style={{ minWidth: '130px' }}>
|
||||
<div className="flex items-center gap-1 mb-1">
|
||||
<span>{SENSITIVE_ICONS[d.type]}</span>
|
||||
<strong style={{ color: SENSITIVE_COLORS[d.type] }}>{d.name}</strong>
|
||||
@ -1458,13 +1461,13 @@ function MapControls({ center, zoom }: { center: [number, number]; zoom: number
|
||||
<div className="flex flex-col gap-1">
|
||||
<button
|
||||
onClick={() => map?.zoomIn()}
|
||||
className="w-[28px] h-[28px] bg-[color-mix(in_srgb,var(--bg-elevated)_85%,transparent)] backdrop-blur-sm border border-stroke rounded-sm text-fg-sub flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all text-xs"
|
||||
className="w-[28px] h-[28px] bg-[color-mix(in_srgb,var(--bg-elevated)_85%,transparent)] backdrop-blur-sm border border-stroke rounded-sm text-fg-sub flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all text-caption"
|
||||
>
|
||||
+
|
||||
</button>
|
||||
<button
|
||||
onClick={() => map?.zoomOut()}
|
||||
className="w-[28px] h-[28px] bg-[color-mix(in_srgb,var(--bg-elevated)_85%,transparent)] backdrop-blur-sm border border-stroke rounded-sm text-fg-sub flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all text-xs"
|
||||
className="w-[28px] h-[28px] bg-[color-mix(in_srgb,var(--bg-elevated)_85%,transparent)] backdrop-blur-sm border border-stroke rounded-sm text-fg-sub flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all text-caption"
|
||||
>
|
||||
−
|
||||
</button>
|
||||
@ -1575,7 +1578,7 @@ function MapLegend({
|
||||
className="flex items-center gap-1.5 mt-2 rounded"
|
||||
style={{ padding: '6px', background: 'rgba(168,85,247,0.08)' }}
|
||||
>
|
||||
<div className="text-xs">🧭</div>
|
||||
<div className="text-caption">🧭</div>
|
||||
<span className="text-caption text-fg-disabled">풍향 (방사형)</span>
|
||||
</div>
|
||||
</div>
|
||||
@ -1888,7 +1891,9 @@ function BacktrackReplayBar({
|
||||
minWidth: '340px',
|
||||
}}
|
||||
>
|
||||
<div className="text-sm text-color-tertiary font-mono font-bold">{progress.toFixed(0)}%</div>
|
||||
<div className="text-body-2 text-color-tertiary font-mono font-bold">
|
||||
{progress.toFixed(0)}%
|
||||
</div>
|
||||
<div className="flex-1 h-1 bg-border relative rounded-[2px]">
|
||||
<div
|
||||
className="h-full rounded-[2px]"
|
||||
|
||||
@ -79,15 +79,15 @@
|
||||
--font-size-title-1: 1.125rem;
|
||||
--font-size-subtitle: 0.9375rem;
|
||||
--font-size-title-2: 1rem;
|
||||
--font-size-title-3: 0.875rem;
|
||||
--font-size-title-4: 0.8125rem;
|
||||
--font-size-title-5: 0.75rem;
|
||||
--font-size-title-6: 0.6875rem;
|
||||
--font-size-body-1: 0.875rem;
|
||||
--font-size-body-2: 0.8125rem;
|
||||
--font-size-label-1: 0.75rem;
|
||||
--font-size-label-2: 0.6875rem;
|
||||
--font-size-caption: 0.6875rem;
|
||||
--font-size-title-3: 1rem;
|
||||
--font-size-title-4: 0.875rem;
|
||||
--font-size-title-5: 0.8125rem;
|
||||
--font-size-title-6: 0.75rem;
|
||||
--font-size-body-1: 1rem;
|
||||
--font-size-body-2: 0.875rem;
|
||||
--font-size-label-1: 0.8125rem;
|
||||
--font-size-label-2: 0.75rem;
|
||||
--font-size-caption: 0.75rem;
|
||||
/* typography — font-weight */
|
||||
--font-weight-thin: 300;
|
||||
--font-weight-regular: 400;
|
||||
|
||||
@ -79,7 +79,7 @@
|
||||
border-radius: 6px;
|
||||
color: var(--fg-default);
|
||||
font-family: var(--font-korean);
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
@ -104,7 +104,7 @@
|
||||
|
||||
.prd-date-input,
|
||||
.prd-time-input {
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
color-scheme: dark;
|
||||
}
|
||||
|
||||
@ -112,7 +112,7 @@
|
||||
.prd-time-input::-webkit-datetime-edit {
|
||||
color: var(--fg-sub);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
letter-spacing: 0.3px;
|
||||
}
|
||||
|
||||
@ -192,7 +192,7 @@
|
||||
background: #1a1f2e;
|
||||
color: var(--fg-default);
|
||||
padding: 10px;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--font-korean);
|
||||
}
|
||||
|
||||
@ -279,7 +279,7 @@
|
||||
|
||||
.combo-item {
|
||||
padding: 7px 10px;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--font-korean);
|
||||
color: var(--fg-sub);
|
||||
cursor: pointer;
|
||||
@ -310,7 +310,7 @@
|
||||
gap: 4px;
|
||||
padding: 5px 4px;
|
||||
border-radius: 5px;
|
||||
font-size: 9px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
font-family: var(--font-korean);
|
||||
cursor: pointer;
|
||||
@ -329,7 +329,7 @@
|
||||
|
||||
/* .prd-mc.on::before {
|
||||
content: '✓ ';
|
||||
font-size: 9px;
|
||||
font-size: 0.6875rem;
|
||||
color: var(--color-accent);
|
||||
} */
|
||||
|
||||
@ -371,7 +371,7 @@
|
||||
border: 1px solid rgba(6, 182, 212, 0.2);
|
||||
border-radius: 6px;
|
||||
color: var(--color-accent);
|
||||
font-size: 9px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
@ -552,7 +552,7 @@
|
||||
}
|
||||
|
||||
.tll {
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
@ -621,7 +621,7 @@
|
||||
border: 1px solid var(--color-boom);
|
||||
border-radius: 4px;
|
||||
padding: 4px 8px;
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--color-boom);
|
||||
white-space: nowrap;
|
||||
font-family: var(--font-korean);
|
||||
@ -672,7 +672,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
|
||||
.tlsl {
|
||||
@ -700,7 +700,7 @@
|
||||
border: 1px solid var(--stroke-default);
|
||||
background: var(--bg-card);
|
||||
color: var(--fg-sub);
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-korean);
|
||||
@ -751,7 +751,7 @@
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
transition: background 0.15s;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-sub);
|
||||
font-family: var(--font-korean);
|
||||
}
|
||||
@ -836,7 +836,7 @@
|
||||
}
|
||||
|
||||
.layer-count {
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-mono);
|
||||
}
|
||||
@ -857,7 +857,7 @@
|
||||
border: 1px solid rgba(245, 158, 11, 0.4);
|
||||
border-radius: 8px;
|
||||
padding: 8px 16px;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-boom);
|
||||
font-family: var(--font-korean);
|
||||
@ -890,7 +890,7 @@
|
||||
border-radius: 4px;
|
||||
color: var(--color-accent);
|
||||
font-family: var(--font-mono);
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
text-align: right;
|
||||
outline: none;
|
||||
@ -927,7 +927,7 @@
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
|
||||
@ -937,7 +937,7 @@
|
||||
border: 1px solid var(--stroke-default);
|
||||
background: var(--bg-card);
|
||||
color: var(--fg-disabled);
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 700;
|
||||
cursor: pointer;
|
||||
font-family: var(--font-mono);
|
||||
@ -1124,7 +1124,7 @@
|
||||
|
||||
.lyr-h1-cnt {
|
||||
margin-left: auto;
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-mono);
|
||||
@ -1153,7 +1153,7 @@
|
||||
cursor: pointer;
|
||||
border-radius: 3px;
|
||||
transition: background 0.15s;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 600;
|
||||
color: var(--fg-sub);
|
||||
font-family: var(--font-korean);
|
||||
@ -1177,7 +1177,7 @@
|
||||
|
||||
.lyr-h2-cnt {
|
||||
margin-left: auto;
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-mono);
|
||||
@ -1200,7 +1200,7 @@
|
||||
gap: 8px;
|
||||
padding: 4px 8px;
|
||||
cursor: pointer;
|
||||
font-size: 11px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-sub);
|
||||
transition:
|
||||
color 0.15s,
|
||||
@ -1216,7 +1216,7 @@
|
||||
|
||||
.lyr-cnt {
|
||||
margin-left: auto;
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
font-weight: 400;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-mono);
|
||||
@ -1330,7 +1330,7 @@
|
||||
}
|
||||
|
||||
.lyr-ccustom label {
|
||||
font-size: 9px;
|
||||
font-size: 0.6875rem;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-korean);
|
||||
}
|
||||
@ -1354,7 +1354,7 @@
|
||||
border-radius: var(--radius-sm);
|
||||
}
|
||||
.lyr-style-label {
|
||||
font-size: 9px;
|
||||
font-size: 0.6875rem;
|
||||
font-weight: 700;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-korean);
|
||||
@ -1371,7 +1371,7 @@
|
||||
margin-top: 6px;
|
||||
}
|
||||
.lyr-style-name {
|
||||
font-size: 10px;
|
||||
font-size: 0.75rem;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-korean);
|
||||
min-width: 32px;
|
||||
@ -1396,7 +1396,7 @@
|
||||
cursor: pointer;
|
||||
}
|
||||
.lyr-style-val {
|
||||
font-size: 9px;
|
||||
font-size: 0.6875rem;
|
||||
color: var(--fg-disabled);
|
||||
font-family: var(--font-mono);
|
||||
min-width: 28px;
|
||||
|
||||
@ -181,7 +181,7 @@
|
||||
}
|
||||
|
||||
.wing-tab {
|
||||
@apply flex-1 py-2 px-1 text-xs font-semibold rounded-md text-center cursor-pointer font-korean;
|
||||
@apply flex-1 py-2 px-1 text-caption font-semibold rounded-md text-center cursor-pointer font-korean;
|
||||
transition: all 0.15s;
|
||||
color: var(--fg-disabled);
|
||||
background: transparent;
|
||||
|
||||
@ -148,7 +148,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
{/* ── 섹션 1: 헤더 ── */}
|
||||
<div className="pb-10 mb-12 border-b border-solid" style={{ borderColor: dividerColor }}>
|
||||
<p
|
||||
className="font-mono text-sm uppercase tracking-widest mb-3"
|
||||
className="font-mono text-body-2 uppercase tracking-widest mb-3"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
Components
|
||||
@ -244,7 +244,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-xs font-mono" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textMuted }}>
|
||||
텍스트 + 아이콘 버튼
|
||||
</span>
|
||||
</div>
|
||||
@ -298,7 +298,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="text-xs font-mono" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textMuted }}>
|
||||
아이콘 전용 버튼
|
||||
</span>
|
||||
</div>
|
||||
@ -339,7 +339,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
<div key={size.label} className="flex items-center justify-between gap-8">
|
||||
{/* 라벨 */}
|
||||
<span
|
||||
className="font-mono text-sm w-36 shrink-0"
|
||||
className="font-mono text-body-2 w-36 shrink-0"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
{size.label}
|
||||
@ -349,7 +349,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
<div className="flex-1 flex items-center">
|
||||
<button
|
||||
type="button"
|
||||
className="rounded-md font-semibold text-sm"
|
||||
className="rounded-md font-semibold text-body-2"
|
||||
style={{
|
||||
height: `${size.heightPx}px`,
|
||||
paddingLeft: `${size.px}px`,
|
||||
@ -380,7 +380,10 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
<div className="flex flex-col gap-8">
|
||||
{/* Flexible */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<span className="font-mono text-sm font-bold" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-mono text-body-2 font-bold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
Flexible
|
||||
</span>
|
||||
<div className="flex items-center gap-4">
|
||||
@ -461,7 +464,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<span className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
콘텐츠에 맞게 너비가 자동으로 조정됩니다.
|
||||
</span>
|
||||
</div>
|
||||
@ -469,7 +472,10 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
|
||||
{/* Fixed */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<span className="font-mono text-sm font-bold" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-mono text-body-2 font-bold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
Fixed
|
||||
</span>
|
||||
<div className="flex items-center gap-4">
|
||||
@ -511,7 +517,10 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
<div style={{ height: '1px', flex: 1, backgroundColor: annotationColor }} />
|
||||
</div>
|
||||
</div>
|
||||
<span className="font-mono text-xs ml-4" style={{ color: t.textSecondary }}>
|
||||
<span
|
||||
className="font-mono text-caption ml-4"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
너비가 고정된 버튼입니다.
|
||||
</span>
|
||||
</div>
|
||||
@ -533,7 +542,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
{ resolution: '해상도 320', width: '248px', maxWidth: '248px', padding: 16 },
|
||||
].map((item) => (
|
||||
<div key={item.resolution} className="flex flex-col gap-3">
|
||||
<span className="font-mono text-sm" style={{ color: t.textSecondary }}>
|
||||
<span className="font-mono text-body-2" style={{ color: t.textSecondary }}>
|
||||
{item.resolution}
|
||||
</span>
|
||||
<div className="flex items-center gap-6">
|
||||
@ -597,7 +606,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
{VARIANTS.map((variant) => (
|
||||
<th
|
||||
key={variant}
|
||||
className="font-mono text-xs font-semibold text-center pb-4"
|
||||
className="font-mono text-caption font-semibold text-center pb-4"
|
||||
style={{
|
||||
color: t.textSecondary,
|
||||
padding: '8px 12px',
|
||||
@ -615,7 +624,7 @@ export const ButtonContent = ({ theme }: ButtonContentProps) => {
|
||||
<tr key={row.state}>
|
||||
{/* 상태 라벨 */}
|
||||
<td
|
||||
className="font-mono text-xs font-medium"
|
||||
className="font-mono text-caption font-medium"
|
||||
style={{
|
||||
color: t.textSecondary,
|
||||
padding: rowIdx === 0 ? '8px 12px 8px 0' : '8px 12px 8px 0',
|
||||
|
||||
@ -347,7 +347,7 @@ const TransparencyRow = ({
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="font-bold text-sm" style={{ color: isDark ? '#dfe2f3' : '#0f172a' }}>
|
||||
<span className="font-bold text-body-2" style={{ color: isDark ? '#dfe2f3' : '#0f172a' }}>
|
||||
{label}
|
||||
</span>
|
||||
<div className="rounded-xl p-4" style={{ backgroundColor: sectionCardBg }}>
|
||||
@ -453,7 +453,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
{/* ── 섹션 1: 헤더 ── */}
|
||||
<div className="pb-10 mb-12 border-b border-solid" style={{ borderColor: dividerColor }}>
|
||||
<p
|
||||
className="font-mono text-sm uppercase tracking-widest mb-3"
|
||||
className="font-mono text-body-2 uppercase tracking-widest mb-3"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
Foundations
|
||||
@ -476,7 +476,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<button
|
||||
key={tab}
|
||||
onClick={() => setActiveColorTab(tab)}
|
||||
className="px-5 py-2.5 text-sm font-semibold border-none cursor-pointer bg-transparent"
|
||||
className="px-5 py-2.5 text-body-2 font-semibold border-none cursor-pointer bg-transparent"
|
||||
style={{
|
||||
color: isActive ? t.textAccent : t.textMuted,
|
||||
borderBottom: isActive ? `2px solid ${t.textAccent}` : '2px solid transparent',
|
||||
@ -505,7 +505,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
>
|
||||
--{'{property}'}-{'{role}'}[-{'{variant}'}]
|
||||
</div>
|
||||
<p className="text-sm leading-relaxed" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2 leading-relaxed" style={{ color: t.textSecondary }}>
|
||||
모든 컬러 토큰은{' '}
|
||||
<strong style={{ color: t.textPrimary }}>Property-Role-Variant</strong> 3계층 구조를
|
||||
따릅니다. Property는 색상이 적용되는 CSS 속성, Role은 의미 기반 역할, Variant는 상태
|
||||
@ -551,17 +551,17 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-sm font-bold shrink-0 w-16"
|
||||
className="font-mono text-body-2 font-bold shrink-0 w-16"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{row.prop}
|
||||
</span>
|
||||
<div className="flex-1">
|
||||
<div className="text-sm" style={{ color: t.textSecondary }}>
|
||||
<div className="text-body-2" style={{ color: t.textSecondary }}>
|
||||
{row.desc}
|
||||
</div>
|
||||
</div>
|
||||
<span className="font-mono text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{row.example}
|
||||
</span>
|
||||
</div>
|
||||
@ -581,7 +581,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
style={{ border: `1px solid ${dividerColor}` }}
|
||||
>
|
||||
<div
|
||||
className="px-5 py-3 text-xs font-bold uppercase tracking-wider"
|
||||
className="px-5 py-3 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -608,12 +608,12 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-sm font-semibold"
|
||||
className="font-mono text-body-2 font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{row.name}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
{row.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -626,7 +626,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
style={{ border: `1px solid ${dividerColor}` }}
|
||||
>
|
||||
<div
|
||||
className="px-5 py-3 text-xs font-bold uppercase tracking-wider"
|
||||
className="px-5 py-3 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -650,12 +650,12 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-sm font-semibold"
|
||||
className="font-mono text-body-2 font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{row.name}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
{row.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -672,7 +672,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
Semantic Tokens
|
||||
</h2>
|
||||
<p className="text-sm mb-6" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2 mb-6" style={{ color: t.textSecondary }}>
|
||||
용도에 따라 의미를 부여한 토큰. 테마 전환 시 값이 변경됩니다.
|
||||
</p>
|
||||
|
||||
@ -745,7 +745,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
},
|
||||
].map((group) => (
|
||||
<div key={group.title} className="mb-8">
|
||||
<h3 className="text-sm font-bold mb-3" style={{ color: t.textAccent }}>
|
||||
<h3 className="text-body-2 font-bold mb-3" style={{ color: t.textAccent }}>
|
||||
{group.title}
|
||||
</h3>
|
||||
<div
|
||||
@ -754,7 +754,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
>
|
||||
{/* 헤더 */}
|
||||
<div
|
||||
className="grid grid-cols-[80px_1fr_1fr_80px_1fr] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[80px_1fr_1fr_80px_1fr] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -776,21 +776,21 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-xs line-through"
|
||||
className="font-mono text-caption line-through"
|
||||
style={{ color: t.textMuted }}
|
||||
>
|
||||
{tk.legacy}
|
||||
</span>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{tk.name}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
{tk.desc}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
{tk.value}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
@ -804,7 +804,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<span className="font-mono text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{hexToRgb(tk.value)}
|
||||
</span>
|
||||
</div>
|
||||
@ -823,7 +823,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
Palette Tokens
|
||||
</h2>
|
||||
<p className="text-sm mb-6" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2 mb-6" style={{ color: t.textSecondary }}>
|
||||
fg · bg · stroke 모든 맥락에서 사용되는 색상 원본. Property 접두사 없이{' '}
|
||||
<code className="font-mono" style={{ color: t.textAccent }}>
|
||||
--color-*
|
||||
@ -836,7 +836,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
style={{ border: `1px solid ${dividerColor}` }}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[80px_1fr_80px_1fr_1fr] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[80px_1fr_80px_1fr_1fr] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -882,16 +882,19 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : '#fafafa',
|
||||
}}
|
||||
>
|
||||
<span className="font-mono text-xs line-through" style={{ color: t.textMuted }}>
|
||||
<span
|
||||
className="font-mono text-caption line-through"
|
||||
style={{ color: t.textMuted }}
|
||||
>
|
||||
{tk.legacy}
|
||||
</span>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{tk.name}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
{tk.value}
|
||||
</span>
|
||||
<div className="flex items-center gap-2">
|
||||
@ -905,11 +908,11 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
flexShrink: 0,
|
||||
}}
|
||||
/>
|
||||
<span className="font-mono text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{hexToRgb(tk.value)}
|
||||
</span>
|
||||
</div>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
{tk.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -925,7 +928,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
Non-color Tokens
|
||||
</h2>
|
||||
<p className="text-sm mb-6" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2 mb-6" style={{ color: t.textSecondary }}>
|
||||
타이포그래피, 라운딩 등 색상 외 토큰.
|
||||
</p>
|
||||
|
||||
@ -934,7 +937,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
style={{ border: `1px solid ${dividerColor}` }}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[80px_1fr_1fr_1fr] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[80px_1fr_1fr_1fr] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -979,17 +982,20 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : '#fafafa',
|
||||
}}
|
||||
>
|
||||
<span className="font-mono text-xs line-through" style={{ color: t.textMuted }}>
|
||||
<span
|
||||
className="font-mono text-caption line-through"
|
||||
style={{ color: t.textMuted }}
|
||||
>
|
||||
{tk.legacy}
|
||||
</span>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{tk.name}
|
||||
</span>
|
||||
<span
|
||||
className="text-xs font-mono px-2 py-0.5 rounded"
|
||||
className="text-caption font-mono px-2 py-0.5 rounded"
|
||||
style={{
|
||||
color: t.textAccent,
|
||||
backgroundColor: isDark ? 'rgba(6,182,212,0.05)' : 'rgba(6,182,212,0.08)',
|
||||
@ -997,7 +1003,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
>
|
||||
{tk.category}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
{tk.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -1015,7 +1021,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-3" style={{ color: t.textPrimary }}>
|
||||
프라이머리 색상(primary color)
|
||||
</h2>
|
||||
<p className="mb-8 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-8 text-body-2" style={{ color: t.textSecondary }}>
|
||||
Primary 색상은 해양 방제 시스템의 핵심 인터랙션 요소에 사용됩니다. Cyan~Blue
|
||||
그라디언트가 주요 액션 버튼과 강조 요소에 적용됩니다.
|
||||
</p>
|
||||
@ -1023,7 +1029,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Light Mode */}
|
||||
<div className="rounded-xl p-6" style={{ backgroundColor: '#f5f5f5' }}>
|
||||
<p className="font-bold text-sm mb-4" style={{ color: '#1e293b' }}>
|
||||
<p className="font-bold text-body-2 mb-4" style={{ color: '#1e293b' }}>
|
||||
Light Mode
|
||||
</p>
|
||||
<ColorScaleBar
|
||||
@ -1041,7 +1047,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
|
||||
{/* Dark Mode */}
|
||||
<div className="rounded-xl p-6" style={{ backgroundColor: '#1a1a2e' }}>
|
||||
<p className="font-bold text-sm mb-4" style={{ color: '#fff' }}>
|
||||
<p className="font-bold text-body-2 mb-4" style={{ color: '#fff' }}>
|
||||
Dark Mode
|
||||
</p>
|
||||
<ColorScaleBar
|
||||
@ -1067,7 +1073,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-3" style={{ color: t.textPrimary }}>
|
||||
세컨더리 색상(secondary color)
|
||||
</h2>
|
||||
<p className="mb-8 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-8 text-body-2" style={{ color: t.textSecondary }}>
|
||||
Secondary 색상은 UI의 배경과 구조적 요소에 사용됩니다. Navy 계열로 다크 모드의
|
||||
깊이감과 계층 구조를 표현합니다.
|
||||
</p>
|
||||
@ -1075,7 +1081,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<div className="flex flex-col gap-6">
|
||||
{/* Light Mode */}
|
||||
<div className="rounded-xl p-6" style={{ backgroundColor: '#f5f5f5' }}>
|
||||
<p className="font-bold text-sm mb-4" style={{ color: '#1e293b' }}>
|
||||
<p className="font-bold text-body-2 mb-4" style={{ color: '#1e293b' }}>
|
||||
Light Mode
|
||||
</p>
|
||||
<ColorScaleBar
|
||||
@ -1093,7 +1099,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
|
||||
{/* Dark Mode */}
|
||||
<div className="rounded-xl p-6" style={{ backgroundColor: '#1a1a2e' }}>
|
||||
<p className="font-bold text-sm mb-4" style={{ color: '#fff' }}>
|
||||
<p className="font-bold text-body-2 mb-4" style={{ color: '#fff' }}>
|
||||
Dark Mode
|
||||
</p>
|
||||
<ColorScaleBar
|
||||
@ -1119,11 +1125,11 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-3" style={{ color: t.textPrimary }}>
|
||||
그레이 색상(gray color) / 네추럴, 중립 색상
|
||||
</h2>
|
||||
<p className="mb-2 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-2 text-body-2" style={{ color: t.textSecondary }}>
|
||||
Gray 색상은 주로 배경, 텍스트, 구분 선에 사용되며, 시각적 집중을 방해하지 않고
|
||||
콘텐츠에 초점을 맞추도록 도와주는 중립적인 색상이다.
|
||||
</p>
|
||||
<p className="mb-8 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-8 text-body-2" style={{ color: t.textSecondary }}>
|
||||
표준형 스타일의 그레이 색상은 주요 색상과 선명한 모드에서의 조화를 고려해 블루
|
||||
그레이 계열을 사용한다.
|
||||
</p>
|
||||
@ -1141,7 +1147,7 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<h2 className="text-2xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
Transparent
|
||||
</h2>
|
||||
<p className="mb-8 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-8 text-body-2" style={{ color: t.textSecondary }}>
|
||||
투명도와 음영을 활용하여 정보의 집중도를 조절합니다. 배경의 음영 처리는 투명도 65%를
|
||||
사용합니다.
|
||||
</p>
|
||||
@ -1174,12 +1180,12 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
<div className="mb-16">
|
||||
<div className="mb-6">
|
||||
<p
|
||||
className="font-mono text-sm uppercase tracking-widest mb-1"
|
||||
className="font-mono text-body-2 uppercase tracking-widest mb-1"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
Primitive
|
||||
</p>
|
||||
<p className="text-sm" style={{ color: t.textMuted }}>
|
||||
<p className="text-body-2" style={{ color: t.textMuted }}>
|
||||
UI 전반에서 사용하는 기본 색조 팔레트. 테마와 무관하게 고정된 값입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -1214,17 +1220,17 @@ export const ColorPaletteContent = ({ theme }: ColorPaletteContentProps) => {
|
||||
/>
|
||||
{/* 토큰명 */}
|
||||
<span
|
||||
className="font-mono text-sm font-semibold ml-4"
|
||||
className="font-mono text-body-2 font-semibold ml-4"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{token.name}
|
||||
</span>
|
||||
{/* HEX + RGB */}
|
||||
<div className="ml-auto text-right">
|
||||
<div className="font-mono text-sm" style={{ color: t.textPrimary }}>
|
||||
<div className="font-mono text-body-2" style={{ color: t.textPrimary }}>
|
||||
{token.hex}
|
||||
</div>
|
||||
<div className="font-mono text-xs" style={{ color: t.textMuted }}>
|
||||
<div className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{hexToRgb(token.hex)}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -13,7 +13,7 @@ export const ComponentsContent = () => {
|
||||
>
|
||||
시스템 컴포넌트 카탈로그
|
||||
</h1>
|
||||
<p className="text-[#bcc9cd] font-korean text-sm leading-5 font-medium max-w-2xl">
|
||||
<p className="text-[#bcc9cd] font-korean text-body-2 leading-5 font-medium max-w-2xl">
|
||||
WING-OPS 해상 물류를 위한 시각적 아이덴티티 시스템입니다. 정밀도와 미션 크리티컬한 운영을
|
||||
위해 설계된 고밀도 산업용 인터페이스입니다.
|
||||
</p>
|
||||
|
||||
@ -256,7 +256,7 @@ const ComponentsOverview = ({ theme, onNavigate }: ComponentsOverviewProps) => {
|
||||
{/* ── 헤더 영역 ── */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<span
|
||||
className="font-mono text-xs font-semibold uppercase"
|
||||
className="font-mono text-caption font-semibold uppercase"
|
||||
style={{ letterSpacing: '1.4px', color: t.textAccent }}
|
||||
>
|
||||
Components
|
||||
@ -264,7 +264,7 @@ const ComponentsOverview = ({ theme, onNavigate }: ComponentsOverviewProps) => {
|
||||
<h1 className="font-sans text-4xl font-bold leading-tight" style={{ color: t.textPrimary }}>
|
||||
Overview
|
||||
</h1>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
재사용 가능한 UI 컴포넌트 카탈로그입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -307,7 +307,10 @@ const ComponentsOverview = ({ theme, onNavigate }: ComponentsOverviewProps) => {
|
||||
|
||||
{/* 카드 라벨 */}
|
||||
<div className="px-5 py-4">
|
||||
<span className="font-sans text-sm font-semibold" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-sans text-body-2 font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{card.label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -154,7 +154,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
style={{ backgroundColor: t.textAccent, boxShadow: t.systemActiveShadow }}
|
||||
/>
|
||||
<span
|
||||
className="font-mono text-xs leading-4 uppercase"
|
||||
className="font-mono text-caption leading-4 uppercase"
|
||||
style={{ letterSpacing: '1.2px', color: t.textAccent }}
|
||||
>
|
||||
System Active
|
||||
@ -192,11 +192,14 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
/>
|
||||
{/* 정보 */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="font-mono text-xs leading-4" style={{ color: t.textAccent }}>
|
||||
<span
|
||||
className="font-mono text-caption leading-4"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{item.token}
|
||||
</span>
|
||||
<span
|
||||
className="font-korean text-sm leading-5 font-bold"
|
||||
className="font-korean text-body-2 leading-5 font-bold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{item.hex}
|
||||
@ -233,7 +236,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
{item.token}
|
||||
</span>
|
||||
<span
|
||||
className="font-korean text-sm font-bold pb-2"
|
||||
className="font-korean text-body-2 font-bold pb-2"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{item.hex}
|
||||
@ -263,7 +266,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
{item.token}
|
||||
</span>
|
||||
<span className={item.sampleClass}>{item.sampleText}</span>
|
||||
<span className="font-korean text-xs" style={{ color: item.descColor }}>
|
||||
<span className="font-korean text-caption" style={{ color: item.descColor }}>
|
||||
{item.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -297,7 +300,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
/>
|
||||
<div className="flex flex-col gap-1">
|
||||
<span
|
||||
className="font-korean text-sm font-bold"
|
||||
className="font-korean text-body-2 font-bold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{item.name}
|
||||
@ -435,7 +438,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
<div className="grid grid-cols-2 gap-8">
|
||||
{/* radius-sm */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="font-mono text-xs leading-4" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption leading-4" style={{ color: t.textMuted }}>
|
||||
{t.radiusSmLabel}
|
||||
</span>
|
||||
<div
|
||||
@ -453,7 +456,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
Small Elements
|
||||
</span>
|
||||
<p
|
||||
className="font-korean text-xs leading-[19.5px] mt-1"
|
||||
className="font-korean text-caption leading-[19.5px] mt-1"
|
||||
style={{ color: t.radiusDescText }}
|
||||
>
|
||||
Applied to tactical buttons, search inputs, and micro-cards for a precise, sharp
|
||||
@ -463,7 +466,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
</div>
|
||||
{/* radius-md */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="font-mono text-xs leading-4" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption leading-4" style={{ color: t.textMuted }}>
|
||||
{t.radiusMdLabel}
|
||||
</span>
|
||||
<div
|
||||
@ -481,7 +484,7 @@ export const DesignContent = ({ theme }: DesignContentProps) => {
|
||||
Structural Panels
|
||||
</span>
|
||||
<p
|
||||
className="font-korean text-xs leading-[19.5px] mt-1"
|
||||
className="font-korean text-caption leading-[19.5px] mt-1"
|
||||
style={{ color: t.radiusDescText }}
|
||||
>
|
||||
Applied to telemetry cards, floating modals, and primary operational panels to
|
||||
|
||||
@ -50,7 +50,10 @@ export const FloatContent = ({ theme }: FloatContentProps) => {
|
||||
<h1 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
Float
|
||||
</h1>
|
||||
<p className="font-korean text-sm leading-5 mt-1" style={{ color: t.textSecondary }}>
|
||||
<p
|
||||
className="font-korean text-body-2 leading-5 mt-1"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
화면 위에 떠서 표시되는 UI 패턴 카탈로그 — Modal, Dropdown, Overlay, Toast
|
||||
</p>
|
||||
</div>
|
||||
@ -70,7 +73,7 @@ export const FloatContent = ({ theme }: FloatContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-sans text-sm font-bold leading-5"
|
||||
className="font-sans text-body-2 font-bold leading-5"
|
||||
style={{ color: isActive ? t.textAccent : t.textMuted }}
|
||||
>
|
||||
{label}
|
||||
|
||||
@ -174,7 +174,7 @@ const FoundationsOverview = ({ theme, onNavigate }: FoundationsOverviewProps) =>
|
||||
{/* ── 헤더 영역 ── */}
|
||||
<div className="flex flex-col gap-3">
|
||||
<span
|
||||
className="font-mono text-xs font-semibold uppercase"
|
||||
className="font-mono text-caption font-semibold uppercase"
|
||||
style={{ letterSpacing: '1.4px', color: t.textAccent }}
|
||||
>
|
||||
Foundations
|
||||
@ -182,7 +182,7 @@ const FoundationsOverview = ({ theme, onNavigate }: FoundationsOverviewProps) =>
|
||||
<h1 className="font-sans text-4xl font-bold leading-tight" style={{ color: t.textPrimary }}>
|
||||
Overview
|
||||
</h1>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
디자인의 기반이 되는 핵심 요소 사용 기준입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -225,7 +225,10 @@ const FoundationsOverview = ({ theme, onNavigate }: FoundationsOverviewProps) =>
|
||||
|
||||
{/* 카드 라벨 */}
|
||||
<div className="px-5 py-4">
|
||||
<span className="font-sans text-sm font-semibold" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-sans text-body-2 font-semibold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{card.label}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -324,7 +324,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h1 className="font-sans text-3xl leading-9 font-bold" style={{ color: t.textPrimary }}>
|
||||
Layout
|
||||
</h1>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
WING-OPS는 데스크톱 전용 고정 뷰포트 애플리케이션입니다. 화면 전체를 채우는 고정
|
||||
레이아웃(100vh)으로, flex 기반의 패널 구조를 사용합니다. KRDS 가이드라인을 기반으로
|
||||
xlarge / xxlarge 구간에 최적화되어 있습니다.
|
||||
@ -364,7 +364,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
Breakpoint
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
화면 크기에 따라 반응형 레이아웃을 사용하여 환경에 최적화된 구조로 표시됩니다. WING-OPS
|
||||
사용 구간(xl, 2xl)은 cyan으로 강조되어 있습니다.
|
||||
</p>
|
||||
@ -499,7 +499,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
borderColor: isDark ? 'rgba(76,215,246,0.45)' : 'rgba(6,182,212,0.40)',
|
||||
}}
|
||||
/>
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
WING-OPS 사용 중
|
||||
</span>
|
||||
</div>
|
||||
@ -511,7 +511,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
borderColor: isDark ? 'rgba(140,144,159,0.25)' : 'rgba(148,163,184,0.30)',
|
||||
}}
|
||||
/>
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
미지원 (1280px 미만)
|
||||
</span>
|
||||
</div>
|
||||
@ -525,7 +525,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
Grid
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
컬럼, 마진, 거터로 구성된 그리드 시스템입니다. 데스크톱 전용으로 xl / 2xl 두 구간만
|
||||
지원합니다.
|
||||
</p>
|
||||
@ -548,7 +548,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
style={{ borderColor: isDark ? 'rgba(66,71,84,0.20)' : '#e2e8f0' }}
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
Breakpoint
|
||||
</span>
|
||||
<span
|
||||
@ -562,10 +562,10 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
Width
|
||||
</span>
|
||||
<span className="font-mono text-sm" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-body-2" style={{ color: t.textPrimary }}>
|
||||
{spec.width}
|
||||
</span>
|
||||
</div>
|
||||
@ -683,7 +683,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<span className="font-mono text-base" style={{ color: '#f97316' }}>
|
||||
⚠
|
||||
</span>
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
<strong style={{ color: '#f97316' }}>1280px 미만 미지원</strong> — Mobile / Tablet
|
||||
구간(xs / s / md / lg)은 데스크톱 전용 운영 정책에 따라 지원하지 않습니다.
|
||||
</span>
|
||||
@ -696,7 +696,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
App Shell
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
WING-OPS 애플리케이션의 기본 레이아웃 구조와 KRDS Sub-page 영역 매핑입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -720,7 +720,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<span className="font-mono text-sm font-bold" style={{ color: '#3b82f6' }}>
|
||||
<span className="font-mono text-body-2 font-bold" style={{ color: '#3b82f6' }}>
|
||||
TopBar
|
||||
</span>
|
||||
<span
|
||||
@ -932,7 +932,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
Spacing
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
UI 요소 간의 간격과 여백을 정의하여 콘텐츠의 위계와 가독성을 조율합니다. Tailwind
|
||||
spacing 토큰과 직결되며, 막대 길이는 실제 px 비율입니다.
|
||||
</p>
|
||||
@ -1000,7 +1000,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
4pt Grid
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
모든 여백과 간격을 4point 단위로 설정해 규칙성을 확보합니다. 컴팩트한 컴포넌트의 경우,
|
||||
2의 배수 단위를 제한적으로 사용합니다.
|
||||
</p>
|
||||
@ -1048,7 +1048,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
>
|
||||
{item.label}
|
||||
</span>
|
||||
<span className="font-korean text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textPrimary }}>
|
||||
{item.text}
|
||||
</span>
|
||||
</div>
|
||||
@ -1145,7 +1145,10 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
minWidth: '180px',
|
||||
}}
|
||||
>
|
||||
<span className="font-korean text-xs font-bold" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-korean text-caption font-bold"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
카드 타이틀
|
||||
</span>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
@ -1185,7 +1188,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
디자인 시스템 진실 소스
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
UI 요소의 레이어 스택 순서입니다. 높은 z-index가 위에 표시되며, 이 값은 디자인 시스템의
|
||||
이상적 설계 값으로 실제 코드는 이 값에 맞춰 정정되어야 합니다.
|
||||
</p>
|
||||
@ -1216,7 +1219,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
className="w-2 h-2 rounded-full shrink-0"
|
||||
style={{ backgroundColor: layer.color }}
|
||||
/>
|
||||
<span className="font-mono text-xs font-bold" style={{ color: layer.color }}>
|
||||
<span className="font-mono text-caption font-bold" style={{ color: layer.color }}>
|
||||
{layer.name}
|
||||
</span>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
@ -1234,7 +1237,7 @@ export const LayoutContent = ({ theme }: LayoutContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
Reference
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
App Shell CSS 클래스와 KRDS Grid 규칙 비교 — 코드 작성 시 참조용입니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@ -70,17 +70,17 @@ export const RadiusContent = ({ theme }: RadiusContentProps) => {
|
||||
<h1 className="font-sans text-3xl leading-9 font-bold" style={{ color: t.textPrimary }}>
|
||||
Radius
|
||||
</h1>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
Radius는 컴포넌트 혹은 콘텐츠 모서리의 둥글기를 표현합니다.
|
||||
</p>
|
||||
</div>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
Radius는 UI 구성 요소의 모서리를 둥글게 처리하여 부드럽고 현대적인 느낌을 제공합니다.
|
||||
일관된 Radius 값은 브랜드 아이덴티티를 강화하고, 사용자 경험을 향상시키며, 다양한 화면과
|
||||
컨텍스트에서 시각적 일관성을 유지하는 데 중요한 역할을 합니다.
|
||||
</p>
|
||||
<ul
|
||||
className="flex flex-col gap-1 list-disc list-inside font-korean text-sm"
|
||||
className="flex flex-col gap-1 list-disc list-inside font-korean text-body-2"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
<li>
|
||||
@ -203,7 +203,7 @@ export const RadiusContent = ({ theme }: RadiusContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
컴포넌트 매핑
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
wing.css 컴포넌트 클래스에 적용된 Radius 토큰입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -233,7 +233,7 @@ export const RadiusContent = ({ theme }: RadiusContentProps) => {
|
||||
|
||||
{/* 정보 */}
|
||||
<div className="flex flex-col gap-1.5 flex-1">
|
||||
<span className="font-mono text-xs font-bold" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption font-bold" style={{ color: t.textPrimary }}>
|
||||
{item.className}
|
||||
</span>
|
||||
<div className="flex flex-row flex-wrap gap-2">
|
||||
|
||||
@ -207,7 +207,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* ── 섹션 1: 헤더 ── */}
|
||||
<div className="pb-10 mb-12 border-b border-solid" style={{ borderColor: dividerColor }}>
|
||||
<p
|
||||
className="font-mono text-sm uppercase tracking-widest mb-3"
|
||||
className="font-mono text-body-2 uppercase tracking-widest mb-3"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
Components
|
||||
@ -225,7 +225,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
<h2 className="text-2xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
Input Field
|
||||
</h2>
|
||||
<p className="text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||||
단일 행 텍스트를 입력받는 필드입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -246,7 +246,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* Prefix label */}
|
||||
<div className="flex flex-col items-center gap-0.5" style={{ width: '70px' }}>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Prefix
|
||||
@ -272,7 +272,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* Input label */}
|
||||
<div className="flex flex-col items-center gap-0.5">
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Input
|
||||
@ -295,7 +295,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* Suffix label */}
|
||||
<div className="flex flex-col items-center gap-0.5" style={{ width: '70px' }}>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Suffix
|
||||
@ -377,7 +377,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* Suffix 텍스트 */}
|
||||
<span
|
||||
className="shrink-0 ml-2 font-mono text-sm"
|
||||
className="shrink-0 ml-2 font-mono text-body-2"
|
||||
style={{ color: fieldPlaceholder }}
|
||||
>
|
||||
원
|
||||
@ -402,7 +402,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Container
|
||||
@ -437,7 +437,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||||
/>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Clear Button
|
||||
@ -468,7 +468,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Container
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
입력 필드의 외곽 영역입니다. 테두리, 곡률, 내부 여백을 정의합니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
@ -519,13 +519,13 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
height: 44px (Medium)
|
||||
</p>
|
||||
<p className="font-mono text-xs mt-1" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||||
padding: 12px (좌우)
|
||||
</p>
|
||||
<p className="font-mono text-xs mt-1" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||||
border-radius: 6px
|
||||
</p>
|
||||
</div>
|
||||
@ -543,14 +543,14 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Placeholder
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
값이 입력되지 않았을 때 표시되는 안내 텍스트입니다. 입력 시 사라집니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
<div className="flex gap-6">
|
||||
{/* 플레이스홀더 있는 필드 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
플레이스홀더 있음
|
||||
</span>
|
||||
<div
|
||||
@ -570,7 +570,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* 빈 필드 (플레이스홀더 없음) */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
플레이스홀더 없음
|
||||
</span>
|
||||
<div
|
||||
@ -597,14 +597,17 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Label
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
입력 필드의 용도를 설명하는 텍스트입니다. 필수 항목은 * 표시로 구분합니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
<div className="flex gap-8">
|
||||
{/* 일반 라벨 */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-sm font-semibold mb-1.5" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="text-body-2 font-semibold mb-1.5"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
이름
|
||||
</span>
|
||||
<div
|
||||
@ -624,7 +627,10 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* 필수 라벨 */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-sm font-semibold mb-1.5" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="text-body-2 font-semibold mb-1.5"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
이메일 <span style={{ color: '#ef4444' }}>*</span>
|
||||
</span>
|
||||
<div
|
||||
@ -655,7 +661,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Input Text
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
사용자가 실제로 입력한 텍스트입니다. 플레이스홀더보다 진한 색상으로 표시됩니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
@ -673,7 +679,10 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
>
|
||||
홍길동
|
||||
</div>
|
||||
<div className="flex gap-6 text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<div
|
||||
className="flex gap-6 text-caption font-mono"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
<span>font-size: 14px</span>
|
||||
<span>color: textPrimary</span>
|
||||
<span>font-weight: 400</span>
|
||||
@ -692,14 +701,14 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Clear Icon
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
입력값이 있을 때 표시되는 초기화 버튼입니다. 클릭 시 입력값을 삭제합니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
<div className="flex gap-6">
|
||||
{/* 텍스트 입력 + Clear 아이콘 표시 */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
입력값 있음 (Clear 표시)
|
||||
</span>
|
||||
<div
|
||||
@ -736,7 +745,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* 빈 상태 (Clear 미표시) */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
입력값 없음 (Clear 미표시)
|
||||
</span>
|
||||
<div
|
||||
@ -767,7 +776,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Helper Text
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
입력 필드 하단에 표시되는 보조 텍스트입니다. 안내 또는 에러 메시지로 사용됩니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
@ -787,7 +796,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
>
|
||||
비밀번호
|
||||
</div>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
영문, 숫자 포함 8자 이상
|
||||
</span>
|
||||
</div>
|
||||
@ -807,7 +816,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
>
|
||||
비밀번호
|
||||
</div>
|
||||
<span className="text-xs" style={{ color: '#ef4444' }}>
|
||||
<span className="text-caption" style={{ color: '#ef4444' }}>
|
||||
필수 입력 항목입니다.
|
||||
</span>
|
||||
</div>
|
||||
@ -828,7 +837,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
<div key={row.state} className="flex items-center gap-8">
|
||||
{/* 왼쪽: State 라벨 + 뱃지 */}
|
||||
<div className="flex items-center gap-2 shrink-0" style={{ width: '200px' }}>
|
||||
<span className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
State
|
||||
</span>
|
||||
<span
|
||||
@ -891,7 +900,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
<h2 className="text-2xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
Text Area
|
||||
</h2>
|
||||
<p className="text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||||
여러 줄의 텍스트를 입력받는 필드입니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -911,7 +920,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* Input Area label */}
|
||||
<div className="flex flex-col items-center gap-0.5" style={{ width: '80px' }}>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Input Area
|
||||
@ -933,7 +942,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* Placeholder label */}
|
||||
<div className="flex flex-col items-center gap-0.5">
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Placeholder
|
||||
@ -955,7 +964,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
{/* Character Counter label */}
|
||||
<div className="flex flex-col items-center gap-0.5" style={{ width: '110px' }}>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Character Counter
|
||||
@ -1042,7 +1051,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
}}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Container
|
||||
@ -1075,7 +1084,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
style={{ width: '1px', height: '10px', backgroundColor: annotationColor }}
|
||||
/>
|
||||
<span
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: annotationColor }}
|
||||
>
|
||||
Resize Handle
|
||||
@ -1103,7 +1112,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Container
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
텍스트 영역의 외곽 컨테이너입니다. 기본 높이 112px이며 사용자가 리사이즈할 수
|
||||
있습니다.
|
||||
</p>
|
||||
@ -1154,16 +1163,16 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
</div>
|
||||
|
||||
<div className="pt-2">
|
||||
<p className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
height: 112px (default)
|
||||
</p>
|
||||
<p className="font-mono text-xs mt-1" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||||
padding: 12px
|
||||
</p>
|
||||
<p className="font-mono text-xs mt-1" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||||
border-radius: 6px
|
||||
</p>
|
||||
<p className="font-mono text-xs mt-1" style={{ color: t.textSecondary }}>
|
||||
<p className="font-mono text-caption mt-1" style={{ color: t.textSecondary }}>
|
||||
resize: vertical
|
||||
</p>
|
||||
</div>
|
||||
@ -1181,14 +1190,14 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Placeholder
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
값이 입력되지 않았을 때 표시되는 안내 텍스트입니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
<div className="flex gap-6">
|
||||
{/* 플레이스홀더 있는 TextArea */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
플레이스홀더 있음
|
||||
</span>
|
||||
<div
|
||||
@ -1209,7 +1218,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* 빈 TextArea */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
플레이스홀더 없음
|
||||
</span>
|
||||
<div
|
||||
@ -1236,14 +1245,17 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Label
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
텍스트 영역의 용도를 설명하는 라벨입니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
<div className="flex gap-8">
|
||||
{/* 기본 라벨 */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-sm font-semibold mb-1.5" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="text-body-2 font-semibold mb-1.5"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
내용
|
||||
</span>
|
||||
<div
|
||||
@ -1264,7 +1276,10 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* 필수(*) 라벨 */}
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="text-sm font-semibold mb-1.5" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="text-body-2 font-semibold mb-1.5"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
비고 <span style={{ color: '#ef4444' }}>*</span>
|
||||
</span>
|
||||
<div
|
||||
@ -1296,7 +1311,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Input Text
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
사용자가 입력한 여러 줄의 텍스트입니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
@ -1316,7 +1331,10 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
>
|
||||
{'오늘 점검 내용을 기록합니다.\n상세 내용은 아래와 같습니다.'}
|
||||
</div>
|
||||
<div className="flex gap-6 text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<div
|
||||
className="flex gap-6 text-caption font-mono"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
<span>font-size: 14px</span>
|
||||
<span>color: textPrimary</span>
|
||||
<span>line-height: 1.6</span>
|
||||
@ -1335,14 +1353,14 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Clear Icon
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
입력값 초기화 버튼입니다. 텍스트 영역 우상단에 표시됩니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
<div className="flex gap-6">
|
||||
{/* 텍스트 있는 상태 (Clear 표시) */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
입력값 있음 (Clear 표시)
|
||||
</span>
|
||||
<div
|
||||
@ -1380,7 +1398,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
|
||||
{/* 빈 상태 (Clear 미표시) */}
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="text-xs font-mono" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textSecondary }}>
|
||||
입력값 없음 (Clear 미표시)
|
||||
</span>
|
||||
<div
|
||||
@ -1412,7 +1430,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
Helper Text
|
||||
</h3>
|
||||
</div>
|
||||
<p className="mb-5 text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="mb-5 text-body-2" style={{ color: t.textSecondary }}>
|
||||
텍스트 영역 하단의 도움말 또는 에러 메시지입니다.
|
||||
</p>
|
||||
<div className="rounded-xl p-8" style={{ backgroundColor: sectionCardBg }}>
|
||||
@ -1434,10 +1452,10 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
내용을 입력하세요
|
||||
</div>
|
||||
<div className="flex items-center justify-between" style={{ width: '240px' }}>
|
||||
<span className="text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption" style={{ color: t.textMuted }}>
|
||||
상세 내용을 입력해 주세요
|
||||
</span>
|
||||
<span className="text-xs font-mono" style={{ color: t.textMuted }}>
|
||||
<span className="text-caption font-mono" style={{ color: t.textMuted }}>
|
||||
0/500
|
||||
</span>
|
||||
</div>
|
||||
@ -1459,7 +1477,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
>
|
||||
내용을 입력하세요
|
||||
</div>
|
||||
<span className="text-xs" style={{ color: '#ef4444' }}>
|
||||
<span className="text-caption" style={{ color: '#ef4444' }}>
|
||||
필수 입력 항목입니다.
|
||||
</span>
|
||||
</div>
|
||||
@ -1480,7 +1498,7 @@ export const TextFieldContent = ({ theme }: TextFieldContentProps) => {
|
||||
<div key={`ta-${row.state}`} className="flex items-start gap-8">
|
||||
{/* 왼쪽: State 라벨 + 뱃지 */}
|
||||
<div className="flex items-center gap-2 shrink-0 pt-3" style={{ width: '200px' }}>
|
||||
<span className="font-mono text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textSecondary }}>
|
||||
State
|
||||
</span>
|
||||
<span
|
||||
|
||||
@ -307,18 +307,18 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h1 className="font-sans text-3xl leading-9 font-bold" style={{ color: t.textPrimary }}>
|
||||
Typography
|
||||
</h1>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
WING-OPS 인터페이스에서 사용되는 타이포그래피 체계입니다. 폰트 패밀리, 크기, 두께를
|
||||
토큰과 컴포넌트 클래스로 정의하여 시각적 계층 구조와 일관성을 유지합니다.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col gap-2">
|
||||
<h3 className="font-korean text-sm font-bold" style={{ color: t.textPrimary }}>
|
||||
<h3 className="font-korean text-body-2 font-bold" style={{ color: t.textPrimary }}>
|
||||
개요
|
||||
</h3>
|
||||
<ul
|
||||
className="flex flex-col gap-1 list-disc list-inside font-korean text-sm"
|
||||
className="flex flex-col gap-1 list-disc list-inside font-korean text-body-2"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
<li>
|
||||
@ -337,7 +337,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
글꼴
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
사용자의 디바이스 환경을 고려하여, 시스템 폰트와 웹 폰트를 조합하여 사용합니다. 한국어
|
||||
UI에 최적화된 폰트 스택으로 다양한 기기에서 일관된 가독성을 보장합니다.
|
||||
</p>
|
||||
@ -352,7 +352,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
}}
|
||||
>
|
||||
<pre
|
||||
className="font-mono text-sm leading-6"
|
||||
className="font-mono text-body-2 leading-6"
|
||||
style={{ color: isDark ? '#c0c8dc' : '#475569' }}
|
||||
>
|
||||
<span style={{ color: t.textAccent }}>font-family</span>
|
||||
@ -390,7 +390,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
</div>
|
||||
<div className="px-5 py-5 flex flex-col gap-4">
|
||||
<div
|
||||
className="font-mono text-xs leading-5 rounded px-3 py-2"
|
||||
className="font-mono text-caption leading-5 rounded px-3 py-2"
|
||||
style={{
|
||||
color: isDark ? '#9ba3b8' : '#64748b',
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : 'rgba(0,0,0,0.02)',
|
||||
@ -398,7 +398,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
>
|
||||
{font.stack}
|
||||
</div>
|
||||
<p className="font-korean text-xs leading-5" style={{ color: t.textSecondary }}>{font.usage}</p>
|
||||
<p className="font-korean text-caption leading-5" style={{ color: t.textSecondary }}>{font.usage}</p>
|
||||
<div className="flex flex-col gap-3 pt-2">
|
||||
<div className="flex flex-col gap-1">
|
||||
<span className="font-mono text-caption uppercase" style={{ letterSpacing: '1px', color: t.textMuted }}>Regular</span>
|
||||
@ -421,7 +421,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h2 className="font-sans text-2xl leading-8 font-bold" style={{ color: t.textPrimary }}>
|
||||
타입 스케일
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-5" style={{ color: t.textSecondary }}>
|
||||
Display, Heading, Body, Navigation, Label의 5가지 용도 카테고리에 맞게 조합하여
|
||||
사용합니다.
|
||||
</p>
|
||||
@ -434,7 +434,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h3 className="font-sans text-lg font-bold" style={{ color: t.textPrimary }}>
|
||||
{category.name}
|
||||
</h3>
|
||||
<p className="font-korean text-xs leading-5" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-caption leading-5" style={{ color: t.textSecondary }}>
|
||||
{category.description}
|
||||
</p>
|
||||
</div>
|
||||
@ -445,7 +445,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
style={{ border: `1px solid ${isDark ? 'rgba(66,71,84,0.20)' : '#e2e8f0'}` }}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[1fr_80px_80px_80px_90px_140px] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[1fr_80px_80px_80px_90px_140px] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -467,20 +467,23 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : '#fafafa',
|
||||
}}
|
||||
>
|
||||
<span className="font-mono text-xs font-semibold" style={{ color: t.textAccent }}>
|
||||
<span
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{row.token}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{row.px}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{row.weight}
|
||||
</span>
|
||||
<span
|
||||
className="font-mono text-xs"
|
||||
className="font-mono text-caption"
|
||||
style={{ color: t.textMuted }}
|
||||
>{`${(row.lineHeight * 100).toFixed(0)}%`}</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{row.letterSpacing}
|
||||
</span>
|
||||
<span
|
||||
@ -510,7 +513,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
>
|
||||
<div className="shrink-0 w-[100px]">
|
||||
<div
|
||||
className="font-mono text-xs font-semibold"
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{row.token}
|
||||
@ -533,7 +536,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
{row.sample}
|
||||
</span>
|
||||
</div>
|
||||
<div className="shrink-0 text-xs font-korean" style={{ color: t.textMuted }}>
|
||||
<div className="shrink-0 text-caption font-korean" style={{ color: t.textMuted }}>
|
||||
{row.role}
|
||||
</div>
|
||||
</div>
|
||||
@ -551,7 +554,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
폰트 두께 토큰
|
||||
</h2>
|
||||
<p className="text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||||
기본 두께 Regular(400), 강조 Bold(700). Medium(500)은 레이블과 소제목, Thin(300)은
|
||||
장식적 대형 텍스트에 사용합니다.
|
||||
</p>
|
||||
@ -561,7 +564,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
style={{ border: `1px solid ${isDark ? 'rgba(66,71,84,0.20)' : '#e2e8f0'}` }}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[1fr_80px_1fr_1fr] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[1fr_80px_1fr_1fr] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -606,17 +609,20 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : '#fafafa',
|
||||
}}
|
||||
>
|
||||
<span className="font-mono text-xs font-semibold" style={{ color: t.textAccent }}>
|
||||
<span
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{row.token}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{row.value}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption" style={{ color: t.textSecondary }}>
|
||||
{row.name}
|
||||
</span>
|
||||
<span
|
||||
className="font-korean text-sm"
|
||||
className="font-korean text-body-2"
|
||||
style={{ color: t.textPrimary, fontWeight: Number(row.value) }}
|
||||
>
|
||||
{row.preview}
|
||||
@ -632,7 +638,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
줄 높이 토큰
|
||||
</h2>
|
||||
<p className="text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||||
대형 텍스트는 타이트하게(1.3), 본문은 여유롭게(1.6). 가독성과 공간 효율의 균형을
|
||||
맞춥니다.
|
||||
</p>
|
||||
@ -642,7 +648,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
style={{ border: `1px solid ${isDark ? 'rgba(66,71,84,0.20)' : '#e2e8f0'}` }}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[1fr_80px_100px_1fr] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[1fr_80px_100px_1fr] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -677,16 +683,19 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : '#fafafa',
|
||||
}}
|
||||
>
|
||||
<span className="font-mono text-xs font-semibold" style={{ color: t.textAccent }}>
|
||||
<span
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{row.token}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{row.value}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{row.pct}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption" style={{ color: t.textSecondary }}>
|
||||
{row.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -700,7 +709,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
<h2 className="text-xl font-bold mb-2" style={{ color: t.textPrimary }}>
|
||||
자간 토큰
|
||||
</h2>
|
||||
<p className="text-sm" style={{ color: t.textSecondary }}>
|
||||
<p className="text-body-2" style={{ color: t.textSecondary }}>
|
||||
카테고리별로 자간을 정의합니다. Display는 넓게, Body는 기본값을 사용합니다.
|
||||
</p>
|
||||
</div>
|
||||
@ -709,7 +718,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
style={{ border: `1px solid ${isDark ? 'rgba(66,71,84,0.20)' : '#e2e8f0'}` }}
|
||||
>
|
||||
<div
|
||||
className="grid grid-cols-[1fr_80px_140px_1fr] gap-2 px-4 py-2.5 text-xs font-bold uppercase tracking-wider"
|
||||
className="grid grid-cols-[1fr_80px_140px_1fr] gap-2 px-4 py-2.5 text-caption font-bold uppercase tracking-wider"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.04)' : '#f1f5f9',
|
||||
color: t.textMuted,
|
||||
@ -760,10 +769,13 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
backgroundColor: isDark ? 'rgba(255,255,255,0.02)' : '#fafafa',
|
||||
}}
|
||||
>
|
||||
<span className="font-mono text-xs font-semibold" style={{ color: t.textAccent }}>
|
||||
<span
|
||||
className="font-mono text-caption font-semibold"
|
||||
style={{ color: t.textAccent }}
|
||||
>
|
||||
{row.token}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{row.value}
|
||||
</span>
|
||||
<span
|
||||
@ -776,7 +788,7 @@ export const TypographyContent = ({ theme }: TypographyContentProps) => {
|
||||
>
|
||||
{row.tw}
|
||||
</span>
|
||||
<span className="text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="text-caption" style={{ color: t.textSecondary }}>
|
||||
{row.category}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -167,7 +167,7 @@ export const ButtonCatalogSection = () => {
|
||||
<div className="border-b border-solid border-[#1e2a42] pt-4 pr-6 pb-4 pl-6 flex flex-row gap-3 items-center justify-start self-stretch shrink-0 relative">
|
||||
<div className="bg-[#06b6d4] rounded-xl shrink-0 w-1 h-4 relative" />
|
||||
<div
|
||||
className="text-[#22d3ee] text-left font-korean text-xs leading-4 font-medium uppercase relative flex items-center justify-start"
|
||||
className="text-[#22d3ee] text-left font-korean text-caption leading-4 font-medium uppercase relative flex items-center justify-start"
|
||||
style={{ letterSpacing: '1.2px' }}
|
||||
>
|
||||
제어 인터페이스: 버튼
|
||||
@ -185,7 +185,7 @@ export const ButtonCatalogSection = () => {
|
||||
className="pt-px pr-2 pb-[17.5px] pl-2 flex flex-col gap-0 items-start justify-start flex-1 min-w-0 relative"
|
||||
>
|
||||
<div
|
||||
className="text-[#64748b] text-left font-korean text-xs font-medium uppercase relative flex items-center justify-start"
|
||||
className="text-[#64748b] text-left font-korean text-caption font-medium uppercase relative flex items-center justify-start"
|
||||
style={{ letterSpacing: '-0.55px' }}
|
||||
>
|
||||
{header}
|
||||
@ -207,7 +207,7 @@ export const ButtonCatalogSection = () => {
|
||||
>
|
||||
{/* 버튼 유형 레이블 */}
|
||||
<div className="pt-[31.5px] pr-2 pb-[31.5px] pl-2 flex flex-col gap-0 items-start justify-start flex-1 min-w-0 relative">
|
||||
<div className="text-[#bcc9cd] text-left font-korean text-xs font-medium relative flex items-center justify-start">
|
||||
<div className="text-[#bcc9cd] text-left font-korean text-caption font-medium relative flex items-center justify-start">
|
||||
{row.label}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -129,7 +129,7 @@ export const CardSection = () => {
|
||||
<div className="text-white text-left font-sans font-bold text-4xl leading-10 relative flex items-center justify-start">
|
||||
24.8
|
||||
</div>
|
||||
<div className="text-[#64748b] text-left font-sans font-semibold text-sm leading-5 relative flex items-center justify-start">
|
||||
<div className="text-[#64748b] text-left font-sans font-semibold text-body-2 leading-5 relative flex items-center justify-start">
|
||||
노트 (knots)
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -61,7 +61,7 @@ export const IconBadgeSection = () => {
|
||||
<div className="bg-[#e89337] rounded-xl shrink-0 w-1 h-4 relative"></div>
|
||||
<div className="flex flex-col gap-0 items-start justify-start shrink-0 relative">
|
||||
<div
|
||||
className="text-[#22d3ee] text-left font-korean text-xs leading-4 font-medium uppercase relative flex items-center justify-start"
|
||||
className="text-[#22d3ee] text-left font-korean text-caption leading-4 font-medium uppercase relative flex items-center justify-start"
|
||||
style={{ letterSpacing: '1.2px' }}
|
||||
>
|
||||
마이크로 컨트롤: 아이콘 버튼
|
||||
@ -113,7 +113,7 @@ export const IconBadgeSection = () => {
|
||||
<div className="bg-[#93000a] rounded-xl shrink-0 w-1 h-4 relative"></div>
|
||||
<div className="flex flex-col gap-0 items-start justify-start shrink-0 relative">
|
||||
<div
|
||||
className="text-[#22d3ee] text-left font-korean text-xs leading-4 font-medium uppercase relative flex items-center justify-start"
|
||||
className="text-[#22d3ee] text-left font-korean text-caption leading-4 font-medium uppercase relative flex items-center justify-start"
|
||||
style={{ letterSpacing: '1.2px' }}
|
||||
>
|
||||
마이크로 컨트롤: 아이콘 버튼
|
||||
|
||||
@ -36,10 +36,10 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
<h2 className="font-sans text-xl font-bold" style={{ color: t.textPrimary }}>
|
||||
Dropdown
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
트리거 요소에{' '}
|
||||
<code
|
||||
className="font-mono text-xs px-1.5 py-0.5 rounded"
|
||||
className="font-mono text-caption px-1.5 py-0.5 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.08)' : 'rgba(6,182,212,0.06)',
|
||||
color: t.textAccent,
|
||||
@ -50,7 +50,7 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
로 부착되는 선택 목록. 5개 이상의 선택지가 있는 단일 선택에 사용한다. 프로젝트 공통
|
||||
컴포넌트는{' '}
|
||||
<code
|
||||
className="font-mono text-xs px-1.5 py-0.5 rounded"
|
||||
className="font-mono text-caption px-1.5 py-0.5 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.08)' : 'rgba(6,182,212,0.06)',
|
||||
color: t.textAccent,
|
||||
@ -84,7 +84,7 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-6">
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
유출 유형
|
||||
</span>
|
||||
<ComboBox
|
||||
@ -98,7 +98,7 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
<span className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
예측 알고리즘
|
||||
</span>
|
||||
<ComboBox
|
||||
@ -112,7 +112,7 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<p className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<p className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
위 컴포넌트는{' '}
|
||||
<code className="font-mono" style={{ color: t.textAccent }}>
|
||||
@common/components/ui/ComboBox
|
||||
@ -373,7 +373,7 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className="py-2.5 px-4">
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
{row.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -423,10 +423,16 @@ export const FloatDropdownContent = ({ theme }: FloatDropdownContentProps) => {
|
||||
: t.cardBorder,
|
||||
}}
|
||||
>
|
||||
<span className="font-korean text-sm font-medium" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-korean text-body-2 font-medium"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
{item.title}
|
||||
</span>
|
||||
<span className="font-korean text-xs leading-5" style={{ color: t.textSecondary }}>
|
||||
<span
|
||||
className="font-korean text-caption leading-5"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
{item.desc}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -88,9 +88,9 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<h2 className="font-sans text-xl font-bold" style={{ color: t.textPrimary }}>
|
||||
Modal
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
<code
|
||||
className="font-mono text-xs px-1.5 py-0.5 rounded"
|
||||
className="font-mono text-caption px-1.5 py-0.5 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.08)' : 'rgba(6,182,212,0.06)',
|
||||
color: t.textAccent,
|
||||
@ -156,7 +156,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
<span className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
{SIZE_CONFIG[activeSize].desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -166,7 +166,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-sm font-medium transition-opacity hover:opacity-80"
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-body-2 font-medium transition-opacity hover:opacity-80"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.15)' : 'rgba(6,182,212,0.12)',
|
||||
borderColor: t.textAccent,
|
||||
@ -178,7 +178,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsConfirmOpen(true)}
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-sm font-medium transition-opacity hover:opacity-80"
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-body-2 font-medium transition-opacity hover:opacity-80"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(239,68,68,0.10)' : 'rgba(239,68,68,0.06)',
|
||||
borderColor: 'rgba(239,68,68,0.40)',
|
||||
@ -200,7 +200,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
className="rounded-lg border border-solid p-5 flex flex-col gap-3"
|
||||
style={{ backgroundColor: t.cardBg, borderColor: t.cardBorder }}
|
||||
>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
Confirm은 독립 컴포넌트가 아닌 <strong>Modal의 variant</strong>다. 타이틀 + 단문 메시지
|
||||
+ 취소/확인 2버튼 구성. 파괴적 작업(삭제, 초기화) 전 사용자 의도를 확인한다.
|
||||
</p>
|
||||
@ -423,7 +423,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
style={{ backgroundColor: t.cardBg, borderColor: t.cardBorder }}
|
||||
>
|
||||
<span
|
||||
className="font-mono text-sm rounded border border-solid px-2 py-0.5 shrink-0"
|
||||
className="font-mono text-body-2 rounded border border-solid px-2 py-0.5 shrink-0"
|
||||
style={{ color: t.textAccent, borderColor: t.cardBorder }}
|
||||
>
|
||||
{row.range}
|
||||
@ -434,7 +434,10 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
>
|
||||
{row.status}
|
||||
</span>
|
||||
<span className="font-korean text-xs leading-5" style={{ color: t.textSecondary }}>
|
||||
<span
|
||||
className="font-korean text-caption leading-5"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
{row.desc}
|
||||
</span>
|
||||
</div>
|
||||
@ -480,7 +483,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
}}
|
||||
>
|
||||
<div className="py-2.5 px-4">
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{item.component}
|
||||
</span>
|
||||
</div>
|
||||
@ -496,7 +499,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className="py-2.5 px-4">
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
{item.trigger}
|
||||
</span>
|
||||
</div>
|
||||
@ -533,7 +536,10 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
className="flex items-center justify-between px-5 py-4 border-b border-solid shrink-0"
|
||||
style={{ borderColor: modalBorder }}
|
||||
>
|
||||
<span className="font-korean text-sm font-medium" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-korean text-body-2 font-medium"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
Modal Preview — {SIZE_CONFIG[activeSize].label} ({SIZE_CONFIG[activeSize].width})
|
||||
</span>
|
||||
<button
|
||||
@ -542,16 +548,16 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
className="w-7 h-7 rounded flex items-center justify-center hover:opacity-70 transition-opacity"
|
||||
style={{ backgroundColor: isDark ? 'rgba(66,71,84,0.25)' : '#f1f5f9' }}
|
||||
>
|
||||
<span className="font-mono text-sm" style={{ color: t.textMuted }}>
|
||||
<span className="font-mono text-body-2" style={{ color: t.textMuted }}>
|
||||
✕
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
<div className="px-5 py-5 flex flex-col gap-3 overflow-y-auto">
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
이 모달은{' '}
|
||||
<code
|
||||
className="font-mono text-xs px-1 rounded"
|
||||
className="font-mono text-caption px-1 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.08)' : 'rgba(6,182,212,0.06)',
|
||||
color: t.textAccent,
|
||||
@ -568,7 +574,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
className="rounded border border-solid px-3 py-2.5"
|
||||
style={{ borderColor: t.cardBorder }}
|
||||
>
|
||||
<span className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
{label}
|
||||
</span>
|
||||
</div>
|
||||
@ -582,7 +588,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(false)}
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-sm transition-opacity hover:opacity-70"
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-body-2 transition-opacity hover:opacity-70"
|
||||
style={{ borderColor: t.cardBorder, color: t.textMuted }}
|
||||
>
|
||||
취소
|
||||
@ -590,7 +596,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsModalOpen(false)}
|
||||
className="px-4 py-2 rounded font-korean text-sm font-medium transition-opacity hover:opacity-80"
|
||||
className="px-4 py-2 rounded font-korean text-body-2 font-medium transition-opacity hover:opacity-80"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.18)' : 'rgba(6,182,212,0.14)',
|
||||
color: t.textAccent,
|
||||
@ -624,13 +630,16 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<span style={{ color: '#ef4444', fontSize: '16px' }}>⚠</span>
|
||||
<span className="font-korean text-sm font-medium" style={{ color: t.textPrimary }}>
|
||||
<span
|
||||
className="font-korean text-body-2 font-medium"
|
||||
style={{ color: t.textPrimary }}
|
||||
>
|
||||
항목을 삭제하시겠습니까?
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="px-5 py-4">
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
삭제된 데이터는 복구할 수 없습니다. 계속 진행하시겠습니까?
|
||||
</p>
|
||||
</div>
|
||||
@ -641,7 +650,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsConfirmOpen(false)}
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-sm transition-opacity hover:opacity-70"
|
||||
className="px-4 py-2 rounded border border-solid font-korean text-body-2 transition-opacity hover:opacity-70"
|
||||
style={{ borderColor: t.cardBorder, color: t.textMuted }}
|
||||
>
|
||||
취소
|
||||
@ -649,7 +658,7 @@ export const FloatModalContent = ({ theme }: FloatModalContentProps) => {
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setIsConfirmOpen(false)}
|
||||
className="px-4 py-2 rounded font-korean text-sm font-medium transition-opacity hover:opacity-80"
|
||||
className="px-4 py-2 rounded font-korean text-body-2 font-medium transition-opacity hover:opacity-80"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(239,68,68,0.18)' : 'rgba(239,68,68,0.12)',
|
||||
color: '#ef4444',
|
||||
|
||||
@ -63,10 +63,10 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
<h2 className="font-sans text-xl font-bold" style={{ color: t.textPrimary }}>
|
||||
Overlay
|
||||
</h2>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
지도 컨테이너 위에
|
||||
<code
|
||||
className="font-mono text-xs px-1.5 py-0.5 mx-1 rounded"
|
||||
className="font-mono text-caption px-1.5 py-0.5 mx-1 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.08)' : 'rgba(6,182,212,0.06)',
|
||||
color: t.textAccent,
|
||||
@ -133,12 +133,12 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className="py-2.5 px-4">
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
{row.overlay}
|
||||
</span>
|
||||
</div>
|
||||
<div className="py-2.5 px-4">
|
||||
<span className="font-korean text-xs" style={{ color: t.textSecondary }}>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
{row.modal}
|
||||
</span>
|
||||
</div>
|
||||
@ -284,7 +284,7 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
className="rounded-lg border border-solid p-5 flex flex-col gap-4"
|
||||
style={{ backgroundColor: t.cardBg, borderColor: t.cardBorder }}
|
||||
>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
<strong>ScatPopup</strong>은 지도 마커에 앵커된 컨텍스트 팝업이다. Modal(fixed 뷰포트
|
||||
중앙)과 달리 마커 위치에서 동적으로 좌표를 계산하며, 지도 패닝·줌 시 위치가 함께
|
||||
업데이트된다.
|
||||
@ -307,7 +307,7 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
<span className="font-mono text-caption" style={{ color: t.textMuted }}>
|
||||
{item.label}
|
||||
</span>
|
||||
<span className="font-mono text-xs" style={{ color: t.textAccent }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textAccent }}>
|
||||
{item.value}
|
||||
</span>
|
||||
<span className="font-korean text-caption" style={{ color: t.textSecondary }}>
|
||||
@ -323,7 +323,7 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
borderColor: 'rgba(234,179,8,0.25)',
|
||||
}}
|
||||
>
|
||||
<span className="font-korean text-xs" style={{ color: '#eab308' }}>
|
||||
<span className="font-korean text-caption" style={{ color: '#eab308' }}>
|
||||
주의: ScatPopup은 MapLibre GL JS의 Popup/Marker 컴포넌트가 아닌 React DOM으로 구현됨.
|
||||
지도 컨테이너 내부에 position: absolute로 렌더링된다.
|
||||
</span>
|
||||
@ -377,7 +377,7 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
}}
|
||||
>
|
||||
<div className="py-2.5 px-3">
|
||||
<span className="font-mono text-xs" style={{ color: t.textPrimary }}>
|
||||
<span className="font-mono text-caption" style={{ color: t.textPrimary }}>
|
||||
{item.component}
|
||||
</span>
|
||||
</div>
|
||||
@ -413,7 +413,10 @@ export const FloatOverlayContent = ({ theme }: FloatOverlayContentProps) => {
|
||||
</span>
|
||||
</div>
|
||||
<div className="py-2.5 px-3">
|
||||
<span className="font-korean text-xs leading-5" style={{ color: t.textSecondary }}>
|
||||
<span
|
||||
className="font-korean text-caption leading-5"
|
||||
style={{ color: t.textSecondary }}
|
||||
>
|
||||
{item.desc}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -84,10 +84,10 @@ export const FloatToastContent = ({ theme }: FloatToastContentProps) => {
|
||||
미구현 — 설계 사양
|
||||
</span>
|
||||
</div>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
화면을 차단하지 않는 비파괴적 알림.
|
||||
<code
|
||||
className="font-mono text-xs px-1.5 py-0.5 mx-1 rounded"
|
||||
className="font-mono text-caption px-1.5 py-0.5 mx-1 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(76,215,246,0.08)' : 'rgba(6,182,212,0.06)',
|
||||
color: t.textAccent,
|
||||
@ -97,7 +97,7 @@ export const FloatToastContent = ({ theme }: FloatToastContentProps) => {
|
||||
</code>
|
||||
에 위치하며 일정 시간 후 자동으로 사라진다. 현재 프로젝트에서는{' '}
|
||||
<code
|
||||
className="font-mono text-xs px-1.5 py-0.5 mx-1 rounded"
|
||||
className="font-mono text-caption px-1.5 py-0.5 mx-1 rounded"
|
||||
style={{
|
||||
backgroundColor: isDark ? 'rgba(239,68,68,0.08)' : 'rgba(239,68,68,0.05)',
|
||||
color: '#ef4444',
|
||||
@ -129,7 +129,7 @@ export const FloatToastContent = ({ theme }: FloatToastContentProps) => {
|
||||
className="rounded-lg border border-solid p-5 flex flex-col gap-4"
|
||||
style={{ backgroundColor: t.cardBg, borderColor: t.cardBorder }}
|
||||
>
|
||||
<p className="font-korean text-xs" style={{ color: t.textMuted }}>
|
||||
<p className="font-korean text-caption" style={{ color: t.textMuted }}>
|
||||
버튼 클릭 시 화면 우하단에 Toast가 표시됩니다. 3초 후 자동으로 사라집니다.
|
||||
</p>
|
||||
<div className="flex gap-2 flex-wrap">
|
||||
@ -280,7 +280,7 @@ export const FloatToastContent = ({ theme }: FloatToastContentProps) => {
|
||||
<span className="font-mono text-lg" style={{ color: cfg.color }}>
|
||||
{cfg.icon}
|
||||
</span>
|
||||
<span className="font-mono text-sm font-bold" style={{ color: cfg.color }}>
|
||||
<span className="font-mono text-body-2 font-bold" style={{ color: cfg.color }}>
|
||||
{cfg.label}
|
||||
</span>
|
||||
</div>
|
||||
@ -311,7 +311,7 @@ export const FloatToastContent = ({ theme }: FloatToastContentProps) => {
|
||||
className="rounded-lg border border-solid p-5 flex flex-col gap-4"
|
||||
style={{ backgroundColor: t.cardBg, borderColor: t.cardBorder }}
|
||||
>
|
||||
<p className="font-korean text-sm leading-6" style={{ color: t.textSecondary }}>
|
||||
<p className="font-korean text-body-2 leading-6" style={{ color: t.textSecondary }}>
|
||||
Toast는 앱 어디서든 호출해야 하므로 <strong>Zustand store + useToast hook</strong>{' '}
|
||||
패턴을 권장한다. ToastContainer는 App.tsx 최상위에 한 번만 렌더링한다.
|
||||
</p>
|
||||
@ -387,7 +387,7 @@ export const FloatToastContent = ({ theme }: FloatToastContentProps) => {
|
||||
<span className="font-mono text-base shrink-0" style={{ color: cfg.color }}>
|
||||
{cfg.icon}
|
||||
</span>
|
||||
<span className="font-korean text-sm flex-1" style={{ color: t.textPrimary }}>
|
||||
<span className="font-korean text-body-2 flex-1" style={{ color: t.textPrimary }}>
|
||||
{toast.message}
|
||||
</span>
|
||||
<button
|
||||
|
||||
@ -6,7 +6,7 @@ interface AdminPlaceholderProps {
|
||||
const AdminPlaceholder = ({ label }: AdminPlaceholderProps) => (
|
||||
<div className="flex flex-col items-center justify-center h-full gap-3">
|
||||
<div className="text-4xl opacity-20">🚧</div>
|
||||
<div className="text-sm font-korean text-fg-sub font-semibold">{label}</div>
|
||||
<div className="text-body-2 font-korean text-fg-sub font-semibold">{label}</div>
|
||||
<div className="text-label-2 font-korean text-fg-disabled">해당 기능은 준비 중입니다.</div>
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -107,7 +107,7 @@ const AdminSidebar = ({ activeMenu, onSelect }: AdminSidebarProps) => {
|
||||
>
|
||||
{/* 헤더 */}
|
||||
<div className="px-4 py-3 border-b border-stroke bg-bg-elevated shrink-0">
|
||||
<div className="text-xs font-bold text-fg font-korean flex items-center gap-1.5">
|
||||
<div className="text-caption font-bold text-fg font-korean flex items-center gap-1.5">
|
||||
<span>⚙️</span> 관리자 설정
|
||||
</div>
|
||||
</div>
|
||||
@ -129,7 +129,7 @@ const AdminSidebar = ({ activeMenu, onSelect }: AdminSidebarProps) => {
|
||||
color: hasActiveChild ? 'var(--color-accent)' : 'var(--fg-default)',
|
||||
}}
|
||||
>
|
||||
<span className="text-sm">{section.icon}</span>
|
||||
<span className="text-body-2">{section.icon}</span>
|
||||
<span className="flex-1 text-left">{section.label}</span>
|
||||
<span
|
||||
className="text-caption text-fg-disabled transition-transform"
|
||||
|
||||
@ -103,7 +103,7 @@ function AssetUploadPanel() {
|
||||
{/* 헤더 */}
|
||||
<div className="px-6 py-4 border-b border-stroke flex-shrink-0">
|
||||
<h1 className="text-lg font-bold text-fg font-korean">자산 현행화</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
자산 데이터를 업로드하여 현행화합니다
|
||||
</p>
|
||||
</div>
|
||||
@ -115,7 +115,7 @@ function AssetUploadPanel() {
|
||||
<div className="flex-1 max-w-[560px] space-y-4">
|
||||
<div className="rounded-lg border border-stroke bg-bg-surface overflow-hidden">
|
||||
<div className="px-5 py-3 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">파일 업로드</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">파일 업로드</h2>
|
||||
</div>
|
||||
<div className="px-5 py-4 space-y-4">
|
||||
{/* 드롭존 */}
|
||||
@ -135,12 +135,12 @@ function AssetUploadPanel() {
|
||||
>
|
||||
<div className="text-3xl mb-2 opacity-40">📁</div>
|
||||
{selectedFile ? (
|
||||
<div className="text-xs font-semibold text-color-accent font-korean mb-1">
|
||||
<div className="text-caption font-semibold text-color-accent font-korean mb-1">
|
||||
{selectedFile.name}
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="text-xs font-semibold text-fg-sub font-korean mb-1">
|
||||
<div className="text-caption font-semibold text-fg-sub font-korean mb-1">
|
||||
파일을 드래그하거나 클릭하여 업로드
|
||||
</div>
|
||||
<div className="text-caption text-fg-disabled font-korean mb-3">
|
||||
@ -148,7 +148,7 @@ function AssetUploadPanel() {
|
||||
</div>
|
||||
<button
|
||||
type="button"
|
||||
className="px-4 py-1.5 text-xs font-semibold rounded-md bg-color-accent text-bg-0
|
||||
className="px-4 py-1.5 text-caption font-semibold rounded-md bg-color-accent text-bg-0
|
||||
hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
@ -176,7 +176,7 @@ function AssetUploadPanel() {
|
||||
<select
|
||||
value={assetCategory}
|
||||
onChange={(e) => setAssetCategory(e.target.value)}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md
|
||||
text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
{ASSET_CATEGORIES.map((c) => (
|
||||
@ -195,7 +195,7 @@ function AssetUploadPanel() {
|
||||
<select
|
||||
value={jurisdiction}
|
||||
onChange={(e) => setJurisdiction(e.target.value)}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md
|
||||
text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
{JURISDICTIONS.map((j) => (
|
||||
@ -212,7 +212,7 @@ function AssetUploadPanel() {
|
||||
업로드 방식
|
||||
</label>
|
||||
<div className="flex gap-4">
|
||||
<label className="flex items-center gap-1.5 cursor-pointer text-xs text-fg-sub font-korean">
|
||||
<label className="flex items-center gap-1.5 cursor-pointer text-caption text-fg-sub font-korean">
|
||||
<input
|
||||
type="radio"
|
||||
checked={uploadMode === 'add'}
|
||||
@ -221,7 +221,7 @@ function AssetUploadPanel() {
|
||||
/>
|
||||
추가 (기존 + 신규)
|
||||
</label>
|
||||
<label className="flex items-center gap-1.5 cursor-pointer text-xs text-fg-sub font-korean">
|
||||
<label className="flex items-center gap-1.5 cursor-pointer text-caption text-fg-sub font-korean">
|
||||
<input
|
||||
type="radio"
|
||||
checked={uploadMode === 'replace'}
|
||||
@ -238,7 +238,7 @@ function AssetUploadPanel() {
|
||||
type="button"
|
||||
onClick={handleUpload}
|
||||
disabled={!selectedFile || uploaded}
|
||||
className={`w-full py-2.5 text-xs font-semibold rounded-md transition-all font-korean disabled:opacity-50 ${
|
||||
className={`w-full py-2.5 text-caption font-semibold rounded-md transition-all font-korean disabled:opacity-50 ${
|
||||
uploaded
|
||||
? 'bg-[rgba(34,197,94,0.15)] text-color-success border border-status-green/30'
|
||||
: 'bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)]'
|
||||
@ -255,7 +255,7 @@ function AssetUploadPanel() {
|
||||
{/* 수정 권한 체계 */}
|
||||
<div className="rounded-lg border border-stroke bg-bg-surface overflow-hidden">
|
||||
<div className="px-5 py-3 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">수정 권한 체계</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">수정 권한 체계</h2>
|
||||
</div>
|
||||
<div className="px-5 py-4 space-y-2">
|
||||
{PERM_ITEMS.map((p) => (
|
||||
@ -264,13 +264,15 @@ function AssetUploadPanel() {
|
||||
className="flex items-center gap-3 px-4 py-3 bg-bg-elevated border border-stroke rounded-md"
|
||||
>
|
||||
<div
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center text-sm flex-shrink-0"
|
||||
className="w-8 h-8 rounded-full flex items-center justify-center text-body-2 flex-shrink-0"
|
||||
style={{ background: p.bg }}
|
||||
>
|
||||
{p.icon}
|
||||
</div>
|
||||
<div>
|
||||
<div className={`text-xs font-bold font-korean ${p.color}`}>{p.role}</div>
|
||||
<div className={`text-caption font-bold font-korean ${p.color}`}>
|
||||
{p.role}
|
||||
</div>
|
||||
<div className="text-caption text-fg-disabled font-korean mt-0.5">
|
||||
{p.desc}
|
||||
</div>
|
||||
@ -283,7 +285,7 @@ function AssetUploadPanel() {
|
||||
{/* 최근 업로드 이력 */}
|
||||
<div className="rounded-lg border border-stroke bg-bg-surface overflow-hidden">
|
||||
<div className="px-5 py-3 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">최근 업로드 이력</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">최근 업로드 이력</h2>
|
||||
</div>
|
||||
<div className="px-5 py-4 space-y-2">
|
||||
{uploadHistory.length === 0 ? (
|
||||
@ -297,7 +299,9 @@ function AssetUploadPanel() {
|
||||
className="flex justify-between items-center px-4 py-3 bg-bg-elevated border border-stroke rounded-md"
|
||||
>
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-fg font-korean">{h.fileNm}</div>
|
||||
<div className="text-caption font-semibold text-fg font-korean">
|
||||
{h.fileNm}
|
||||
</div>
|
||||
<div className="text-caption text-fg-disabled mt-0.5 font-korean">
|
||||
{formatDate(h.regDtm)} · {h.uploaderNm} · {h.uploadCnt.toLocaleString()}건
|
||||
</div>
|
||||
|
||||
@ -119,8 +119,8 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-stroke-1">
|
||||
<h2 className="text-sm font-semibold text-fg">게시판 관리</h2>
|
||||
<span className="text-xs text-fg-disabled">총 {data?.totalCount ?? 0}건</span>
|
||||
<h2 className="text-body-2 font-semibold text-fg">게시판 관리</h2>
|
||||
<span className="text-caption text-fg-disabled">총 {data?.totalCount ?? 0}건</span>
|
||||
</div>
|
||||
|
||||
{/* 카테고리 탭 + 검색 */}
|
||||
@ -130,7 +130,7 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
<button
|
||||
key={tab.code}
|
||||
onClick={() => handleCategoryChange(tab.code)}
|
||||
className={`px-3 py-1 text-xs rounded-full transition-colors ${
|
||||
className={`px-3 py-1 text-caption rounded-full transition-colors ${
|
||||
activeCategory === tab.code
|
||||
? 'bg-blue-500/20 text-blue-400 font-medium'
|
||||
: 'text-fg-disabled hover:text-fg-sub hover:bg-bg-elevated'
|
||||
@ -146,11 +146,11 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
value={searchInput}
|
||||
onChange={(e) => setSearchInput(e.target.value)}
|
||||
placeholder="제목/작성자 검색"
|
||||
className="px-2 py-1 text-xs rounded bg-bg-elevated border border-stroke-1 text-fg placeholder:text-text-4 w-48"
|
||||
className="px-2 py-1 text-caption rounded bg-bg-elevated border border-stroke-1 text-fg placeholder:text-text-4 w-48"
|
||||
/>
|
||||
<button
|
||||
type="submit"
|
||||
className="px-2 py-1 text-xs rounded bg-bg-elevated border border-stroke-1 text-fg-sub hover:bg-bg-card"
|
||||
className="px-2 py-1 text-caption rounded bg-bg-elevated border border-stroke-1 text-fg-sub hover:bg-bg-card"
|
||||
>
|
||||
검색
|
||||
</button>
|
||||
@ -162,7 +162,7 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
disabled={selected.size === 0 || deleting}
|
||||
className="px-3 py-1 text-xs rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 text-caption rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
{deleting ? '삭제 중...' : `선택 삭제 (${selected.size})`}
|
||||
</button>
|
||||
@ -170,7 +170,7 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
|
||||
{/* 테이블 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
<table className="w-full text-xs">
|
||||
<table className="w-full text-caption">
|
||||
<thead className="sticky top-0 bg-bg-surface z-10">
|
||||
<tr className="border-b border-stroke-1 text-fg-disabled">
|
||||
<th className="w-8 py-2 text-center">
|
||||
@ -222,7 +222,7 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
<button
|
||||
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
||||
disabled={page <= 1}
|
||||
className="px-2 py-1 text-xs rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
className="px-2 py-1 text-caption rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
@ -234,7 +234,7 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
<button
|
||||
key={p}
|
||||
onClick={() => setPage(p)}
|
||||
className={`w-7 h-7 text-xs rounded ${
|
||||
className={`w-7 h-7 text-caption rounded ${
|
||||
p === page
|
||||
? 'bg-blue-500/20 text-blue-400 font-medium'
|
||||
: 'text-fg-disabled hover:bg-bg-elevated'
|
||||
@ -247,7 +247,7 @@ export default function BoardMgmtPanel({ initialCategory = '' }: BoardMgmtPanelP
|
||||
<button
|
||||
onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
|
||||
disabled={page >= totalPages}
|
||||
className="px-2 py-1 text-xs rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
className="px-2 py-1 text-caption rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
|
||||
@ -99,13 +99,15 @@ function CleanupEquipPanel() {
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">방제장비 현황</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">총 {filtered.length}개 기관</p>
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
총 {filtered.length}개 기관
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<select
|
||||
value={regionFilter}
|
||||
onChange={handleFilterChange(setRegionFilter)}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="전체">전체 관할청</option>
|
||||
<option value="남해">남해청</option>
|
||||
@ -117,7 +119,7 @@ function CleanupEquipPanel() {
|
||||
<select
|
||||
value={typeFilter}
|
||||
onChange={handleFilterChange(setTypeFilter)}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="전체">전체 유형</option>
|
||||
{typeOptions.map((t) => (
|
||||
@ -129,7 +131,7 @@ function CleanupEquipPanel() {
|
||||
<select
|
||||
value={equipFilter}
|
||||
onChange={handleFilterChange(setEquipFilter)}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="전체">전체 장비</option>
|
||||
<option value="방제선">방제선</option>
|
||||
@ -146,11 +148,11 @@ function CleanupEquipPanel() {
|
||||
setSearchTerm(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-56 px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-56 px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
<button
|
||||
onClick={load}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-bg-elevated border border-stroke text-fg-sub hover:border-color-accent hover:text-color-accent transition-all font-korean"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-bg-elevated border border-stroke text-fg-sub hover:border-color-accent hover:text-color-accent transition-all font-korean"
|
||||
>
|
||||
새로고침
|
||||
</button>
|
||||
@ -160,7 +162,7 @@ function CleanupEquipPanel() {
|
||||
{/* 테이블 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
) : (
|
||||
@ -217,7 +219,7 @@ function CleanupEquipPanel() {
|
||||
<tr>
|
||||
<td
|
||||
colSpan={11}
|
||||
className="px-6 py-10 text-center text-xs text-fg-disabled font-korean"
|
||||
className="px-6 py-10 text-center text-caption text-fg-disabled font-korean"
|
||||
>
|
||||
조회된 기관이 없습니다.
|
||||
</td>
|
||||
|
||||
@ -211,7 +211,7 @@ const HEADERS = [
|
||||
function HrTable({ rows, loading }: { rows: HrCollectItem[]; loading: boolean }) {
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<table className="w-full text-caption border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-bg-elevated text-t3 uppercase tracking-wide">
|
||||
{HEADERS.map((h) => (
|
||||
@ -317,10 +317,10 @@ export default function CollectHrPanel() {
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-stroke-1 shrink-0">
|
||||
<h2 className="text-sm font-semibold text-t1">인사정보 수집 현황</h2>
|
||||
<h2 className="text-body-2 font-semibold text-t1">인사정보 수집 현황</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
{lastUpdate && (
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
갱신:{' '}
|
||||
{lastUpdate.toLocaleTimeString('ko-KR', {
|
||||
hour: '2-digit',
|
||||
@ -332,7 +332,7 @@ export default function CollectHrPanel() {
|
||||
<button
|
||||
onClick={fetchData}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-caption rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<svg
|
||||
className={`w-3.5 h-3.5 ${loading ? 'animate-spin' : ''}`}
|
||||
@ -354,11 +354,11 @@ export default function CollectHrPanel() {
|
||||
|
||||
{/* 상태 표시줄 */}
|
||||
<div className="flex items-center gap-3 px-5 py-2 shrink-0 border-b border-stroke-1 bg-bg-base">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-emerald-500/10 text-emerald-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-emerald-500/10 text-emerald-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
수집 완료 {completedCount}건
|
||||
</span>
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
전체 {rows.length}건 (활성: {activeCount} / 비활성: {rows.length - activeCount})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -130,7 +130,9 @@ const DispersingZonePanel = () => {
|
||||
onClick={() => handleToggleExpand(zone)}
|
||||
>
|
||||
<span className={`w-3 h-3 rounded-sm shrink-0 ${swatchColor}`} />
|
||||
<span className="flex-1 text-xs font-semibold text-fg font-korean">{info.label}</span>
|
||||
<span className="flex-1 text-caption font-semibold text-fg font-korean">
|
||||
{info.label}
|
||||
</span>
|
||||
{/* 토글 스위치 */}
|
||||
<button
|
||||
onClick={(e) => {
|
||||
@ -209,7 +211,7 @@ const DispersingZonePanel = () => {
|
||||
<div className="w-[280px] bg-bg-surface border-l border-stroke flex flex-col overflow-hidden shrink-0">
|
||||
{/* 헤더 */}
|
||||
<div className="px-4 py-4 border-b border-stroke shrink-0">
|
||||
<h1 className="text-sm font-bold text-fg font-korean">유처리제 제한구역</h1>
|
||||
<h1 className="text-body-2 font-bold text-fg font-korean">유처리제 제한구역</h1>
|
||||
<p className="text-label-2 text-fg-disabled mt-0.5 font-korean">해양환경관리법 기준</p>
|
||||
</div>
|
||||
|
||||
|
||||
@ -186,7 +186,7 @@ const LayerFormModal = ({ mode, initialData, onClose, onSaved }: LayerFormModalP
|
||||
};
|
||||
|
||||
const inputCls =
|
||||
'w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none';
|
||||
'w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none';
|
||||
const labelCls = 'block text-label-2 font-semibold text-fg-sub font-korean mb-1.5';
|
||||
|
||||
return (
|
||||
@ -194,7 +194,7 @@ const LayerFormModal = ({ mode, initialData, onClose, onSaved }: LayerFormModalP
|
||||
<div className="bg-bg-surface border border-stroke rounded-lg shadow-lg w-[480px] max-h-[90vh] flex flex-col">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke shrink-0">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">
|
||||
{mode === 'create' ? '레이어 등록' : '레이어 수정'}
|
||||
</h2>
|
||||
<button onClick={onClose} className="text-fg-disabled hover:text-fg transition-colors">
|
||||
@ -214,7 +214,7 @@ const LayerFormModal = ({ mode, initialData, onClose, onSaved }: LayerFormModalP
|
||||
? handleParentChange(e.target.value)
|
||||
: handleField('upLayerCd', e.target.value)
|
||||
}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none"
|
||||
>
|
||||
<option value="">(없음)</option>
|
||||
{options
|
||||
@ -311,7 +311,7 @@ const LayerFormModal = ({ mode, initialData, onClose, onSaved }: LayerFormModalP
|
||||
<select
|
||||
value={form.useYn}
|
||||
onChange={(e) => handleField('useYn', e.target.value)}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none"
|
||||
>
|
||||
<option value="Y">사용</option>
|
||||
<option value="N">미사용</option>
|
||||
@ -329,14 +329,14 @@ const LayerFormModal = ({ mode, initialData, onClose, onSaved }: LayerFormModalP
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-3 py-1.5 text-xs border border-stroke text-fg-disabled rounded hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
className="px-3 py-1.5 text-caption border border-stroke text-fg-disabled rounded hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={saving}
|
||||
className="px-3 py-1.5 text-xs bg-color-accent text-bg-0 rounded hover:opacity-90 disabled:opacity-50 transition-all font-korean"
|
||||
className="px-3 py-1.5 text-caption bg-color-accent text-bg-0 rounded hover:opacity-90 disabled:opacity-50 transition-all font-korean"
|
||||
>
|
||||
{saving ? '저장 중...' : mode === 'create' ? '등록' : '저장'}
|
||||
</button>
|
||||
@ -449,11 +449,11 @@ const LayerPanel = () => {
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">레이어 관리</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">총 {total}개</p>
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">총 {total}개</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setModal({ mode: 'create' })}
|
||||
className="px-3 py-1.5 text-xs font-semibold bg-color-accent text-bg-0 rounded hover:opacity-90 transition-opacity font-korean"
|
||||
className="px-3 py-1.5 text-caption font-semibold bg-color-accent text-bg-0 rounded hover:opacity-90 transition-opacity font-korean"
|
||||
>
|
||||
신규 등록
|
||||
</button>
|
||||
@ -465,12 +465,12 @@ const LayerPanel = () => {
|
||||
onChange={(e) => setSearchInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
placeholder="레이어코드 / 레이어명 검색"
|
||||
className="flex-1 px-3 py-1.5 text-xs bg-bg-elevated border border-stroke rounded text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="flex-1 px-3 py-1.5 text-caption bg-bg-elevated border border-stroke rounded text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
<select
|
||||
value={filterUseYn}
|
||||
onChange={(e) => setFilterUseYn(e.target.value)}
|
||||
className="px-2 py-1.5 text-xs bg-bg-elevated border border-stroke rounded text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-2 py-1.5 text-caption bg-bg-elevated border border-stroke rounded text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">전체</option>
|
||||
<option value="Y">사용</option>
|
||||
@ -478,7 +478,7 @@ const LayerPanel = () => {
|
||||
</select>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
className="px-3 py-1.5 text-xs border border-stroke text-fg-sub rounded hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
className="px-3 py-1.5 text-caption border border-stroke text-fg-sub rounded hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
>
|
||||
검색
|
||||
</button>
|
||||
@ -487,7 +487,7 @@ const LayerPanel = () => {
|
||||
|
||||
{/* 오류 메시지 */}
|
||||
{error && (
|
||||
<div className="px-6 py-2 text-xs text-red-400 bg-[rgba(239,68,68,0.05)] border-b border-stroke shrink-0 font-korean">
|
||||
<div className="px-6 py-2 text-caption text-red-400 bg-[rgba(239,68,68,0.05)] border-b border-stroke shrink-0 font-korean">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
@ -495,7 +495,7 @@ const LayerPanel = () => {
|
||||
{/* 테이블 영역 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-full text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-full text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
) : (
|
||||
@ -539,7 +539,7 @@ const LayerPanel = () => {
|
||||
<tr>
|
||||
<td
|
||||
colSpan={10}
|
||||
className="px-4 py-12 text-center text-fg-disabled text-sm font-korean"
|
||||
className="px-4 py-12 text-center text-fg-disabled text-body-2 font-korean"
|
||||
>
|
||||
데이터가 없습니다.
|
||||
</td>
|
||||
@ -551,15 +551,15 @@ const LayerPanel = () => {
|
||||
className="border-b border-stroke hover:bg-[rgba(255,255,255,0.02)] transition-colors"
|
||||
>
|
||||
{/* 번호 */}
|
||||
<td className="px-4 py-3 text-xs text-fg-disabled font-mono">
|
||||
<td className="px-4 py-3 text-caption text-fg-disabled font-mono">
|
||||
{(page - 1) * PAGE_SIZE + idx + 1}
|
||||
</td>
|
||||
{/* 레이어코드 */}
|
||||
<td className="px-4 py-3 text-label-2 text-fg-sub font-mono">{item.layerCd}</td>
|
||||
{/* 레이어명 */}
|
||||
<td className="px-4 py-3 text-xs text-fg font-korean">{item.layerNm}</td>
|
||||
<td className="px-4 py-3 text-caption text-fg font-korean">{item.layerNm}</td>
|
||||
{/* 레이어전체명 */}
|
||||
<td className="px-4 py-3 text-xs text-fg-sub font-korean max-w-[200px]">
|
||||
<td className="px-4 py-3 text-caption text-fg-sub font-korean max-w-[200px]">
|
||||
<span className="block truncate" title={item.layerFullNm}>
|
||||
{item.layerFullNm}
|
||||
</span>
|
||||
@ -575,7 +575,7 @@ const LayerPanel = () => {
|
||||
{item.wmsLayerNm ?? <span className="text-fg-disabled">-</span>}
|
||||
</td>
|
||||
{/* 정렬순서 */}
|
||||
<td className="px-4 py-3 text-xs text-fg-disabled text-center font-mono">
|
||||
<td className="px-4 py-3 text-caption text-fg-disabled text-center font-mono">
|
||||
{item.sortOrd}
|
||||
</td>
|
||||
{/* 등록일시 */}
|
||||
@ -614,13 +614,13 @@ const LayerPanel = () => {
|
||||
<div className="flex items-center justify-center gap-1.5 flex-nowrap">
|
||||
<button
|
||||
onClick={() => setModal({ mode: 'edit', data: item })}
|
||||
className="px-3 py-1 text-xs rounded bg-[rgba(6,182,212,0.15)] text-color-accent hover:bg-[rgba(6,182,212,0.25)] font-korean whitespace-nowrap"
|
||||
className="px-3 py-1 text-caption rounded bg-[rgba(6,182,212,0.15)] text-color-accent hover:bg-[rgba(6,182,212,0.25)] font-korean whitespace-nowrap"
|
||||
>
|
||||
수정
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleDelete(item.layerCd)}
|
||||
className="px-3 py-1 text-xs rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 font-korean whitespace-nowrap"
|
||||
className="px-3 py-1 text-caption rounded bg-red-500/20 text-red-400 hover:bg-red-500/30 font-korean whitespace-nowrap"
|
||||
>
|
||||
삭제
|
||||
</button>
|
||||
|
||||
@ -78,7 +78,7 @@ function MapBaseModal({
|
||||
<div className="bg-bg-surface border border-stroke rounded-lg shadow-lg w-[520px] max-h-[90vh] flex flex-col">
|
||||
{/* 모달 헤더 */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">
|
||||
{isEdit ? '지도 수정' : '지도 등록'}
|
||||
</h2>
|
||||
<button onClick={onClose} className="text-fg-disabled hover:text-fg transition-colors">
|
||||
@ -108,7 +108,7 @@ function MapBaseModal({
|
||||
value={form.mapNm}
|
||||
onChange={(e) => setField('mapNm', e.target.value)}
|
||||
placeholder="지도 이름을 입력하세요"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -123,7 +123,7 @@ function MapBaseModal({
|
||||
onChange={(e) => setField('mapKey', e.target.value)}
|
||||
placeholder="고유 식별 키 (영문/숫자)"
|
||||
disabled={isEdit}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -135,7 +135,7 @@ function MapBaseModal({
|
||||
<select
|
||||
value={form.mapLevelCd}
|
||||
onChange={(e) => setField('mapLevelCd', e.target.value)}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">선택</option>
|
||||
{MAP_LEVEL_OPTIONS.map((opt) => (
|
||||
@ -156,7 +156,7 @@ function MapBaseModal({
|
||||
value={form.mapSrc}
|
||||
onChange={(e) => setField('mapSrc', e.target.value)}
|
||||
placeholder="타일 URL 또는 파일 경로"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -170,7 +170,7 @@ function MapBaseModal({
|
||||
value={form.mapDc}
|
||||
onChange={(e) => setField('mapDc', e.target.value)}
|
||||
placeholder="지도에 대한 설명을 입력하세요"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean resize-none"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean resize-none"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -193,7 +193,7 @@ function MapBaseModal({
|
||||
}`}
|
||||
/>
|
||||
</button>
|
||||
<span className="text-xs text-fg-sub font-korean">
|
||||
<span className="text-caption text-fg-sub font-korean">
|
||||
{form.useYn === 'Y' ? '사용' : '미사용'}
|
||||
</span>
|
||||
</div>
|
||||
@ -208,14 +208,14 @@ function MapBaseModal({
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-xs border border-stroke text-fg-sub rounded-md hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
className="px-4 py-2 text-caption border border-stroke text-fg-sub rounded-md hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={saving}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all disabled:opacity-50 font-korean"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all disabled:opacity-50 font-korean"
|
||||
>
|
||||
{saving ? '저장 중...' : isEdit ? '수정' : '등록'}
|
||||
</button>
|
||||
@ -350,11 +350,11 @@ function MapBasePanel() {
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">지도 관리</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">총 {total}건</p>
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">총 {total}건</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => openModal(null)}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean"
|
||||
>
|
||||
+ 등록
|
||||
</button>
|
||||
@ -375,7 +375,7 @@ function MapBasePanel() {
|
||||
|
||||
{/* 테이블 영역 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
<table className="w-full text-xs">
|
||||
<table className="w-full text-caption">
|
||||
<thead className="sticky top-0 bg-bg-surface z-10">
|
||||
<tr className="border-b border-stroke text-fg-disabled">
|
||||
<th className="w-12 py-3 text-center">번호</th>
|
||||
@ -433,7 +433,7 @@ function MapBasePanel() {
|
||||
<td className="py-3 text-center">
|
||||
<button
|
||||
onClick={() => openModal(item)}
|
||||
className="px-3 py-1 text-xs rounded bg-[rgba(6,182,212,0.15)] text-color-accent hover:bg-[rgba(6,182,212,0.25)]"
|
||||
className="px-3 py-1 text-caption rounded bg-[rgba(6,182,212,0.15)] text-color-accent hover:bg-[rgba(6,182,212,0.25)]"
|
||||
>
|
||||
수정
|
||||
</button>
|
||||
@ -441,7 +441,7 @@ function MapBasePanel() {
|
||||
<td className="py-3 text-center">
|
||||
<button
|
||||
onClick={() => handleDelete(item)}
|
||||
className="px-3 py-1 text-xs rounded bg-red-500/20 text-red-400 hover:bg-red-500/30"
|
||||
className="px-3 py-1 text-caption rounded bg-red-500/20 text-red-400 hover:bg-red-500/30"
|
||||
>
|
||||
삭제
|
||||
</button>
|
||||
@ -452,7 +452,7 @@ function MapBasePanel() {
|
||||
</tbody>
|
||||
</table>
|
||||
{!loading && items.length === 0 && (
|
||||
<div className="flex items-center justify-center h-32 text-xs text-fg-disabled font-korean">
|
||||
<div className="flex items-center justify-center h-32 text-caption text-fg-disabled font-korean">
|
||||
등록된 지도가 없습니다.
|
||||
</div>
|
||||
)}
|
||||
@ -464,7 +464,7 @@ function MapBasePanel() {
|
||||
<button
|
||||
onClick={() => setPage((p) => Math.max(1, p - 1))}
|
||||
disabled={page <= 1}
|
||||
className="px-2 py-1 text-xs rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
className="px-2 py-1 text-caption rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
>
|
||||
<
|
||||
</button>
|
||||
@ -476,7 +476,7 @@ function MapBasePanel() {
|
||||
<button
|
||||
key={p}
|
||||
onClick={() => setPage(p)}
|
||||
className={`w-7 h-7 text-xs rounded ${
|
||||
className={`w-7 h-7 text-caption rounded ${
|
||||
p === page
|
||||
? 'bg-blue-500/20 text-blue-400 font-medium'
|
||||
: 'text-fg-disabled hover:bg-bg-elevated'
|
||||
@ -489,7 +489,7 @@ function MapBasePanel() {
|
||||
<button
|
||||
onClick={() => setPage((p) => Math.min(totalPages, p + 1))}
|
||||
disabled={page >= totalPages}
|
||||
className="px-2 py-1 text-xs rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
className="px-2 py-1 text-caption rounded text-fg-disabled hover:bg-bg-elevated disabled:opacity-30"
|
||||
>
|
||||
>
|
||||
</button>
|
||||
|
||||
@ -124,7 +124,7 @@ function MenusPanel() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-fg-disabled text-sm font-korean">메뉴 설정을 불러오는 중...</div>
|
||||
<div className="text-fg-disabled text-body-2 font-korean">메뉴 설정을 불러오는 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -136,14 +136,14 @@ function MenusPanel() {
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">메뉴 관리</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
메뉴 표시 여부, 순서, 라벨, 아이콘을 관리합니다
|
||||
</p>
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSave}
|
||||
disabled={!hasChanges || saving}
|
||||
className={`px-4 py-2 text-xs font-semibold rounded-md transition-all font-korean ${
|
||||
className={`px-4 py-2 text-caption font-semibold rounded-md transition-all font-korean ${
|
||||
hasChanges && !saving
|
||||
? 'bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)]'
|
||||
: 'bg-bg-card text-fg-disabled cursor-not-allowed'
|
||||
@ -188,7 +188,7 @@ function MenusPanel() {
|
||||
<DragOverlay>
|
||||
{activeMenu ? (
|
||||
<div className="flex items-center gap-3 px-4 py-3 rounded-md border border-color-accent bg-bg-surface shadow-lg opacity-90 max-w-[700px]">
|
||||
<span className="text-fg-disabled text-xs">⠿</span>
|
||||
<span className="text-fg-disabled text-caption">⠿</span>
|
||||
<span className="text-title-2">{activeMenu.icon}</span>
|
||||
<span className="text-title-4 font-semibold text-fg font-korean">
|
||||
{activeMenu.label}
|
||||
|
||||
@ -45,24 +45,24 @@ function formatTime(iso: string | null): string {
|
||||
|
||||
function StatusCell({ row }: { row: NumericalDataStatus }) {
|
||||
if (row.lastStatus === 'COMPLETED') {
|
||||
return <span className="text-emerald-400 text-xs">정상</span>;
|
||||
return <span className="text-emerald-400 text-caption">정상</span>;
|
||||
}
|
||||
if (row.lastStatus === 'FAILED') {
|
||||
return (
|
||||
<span className="text-red-400 text-xs">
|
||||
<span className="text-red-400 text-caption">
|
||||
오류{row.consecutiveFailures > 0 ? ` (${row.consecutiveFailures}회 연속)` : ''}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
if (row.lastStatus === 'STARTED') {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 text-cyan-400 text-xs">
|
||||
<span className="inline-flex items-center gap-1 text-cyan-400 text-caption">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse" />
|
||||
실행 중
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return <span className="text-t3 text-xs">-</span>;
|
||||
return <span className="text-t3 text-caption">-</span>;
|
||||
}
|
||||
|
||||
function StatusBadge({
|
||||
@ -76,7 +76,7 @@ function StatusBadge({
|
||||
}) {
|
||||
if (loading) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-bg-elevated text-t2">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-bg-elevated text-t2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse" />
|
||||
조회 중...
|
||||
</span>
|
||||
@ -84,7 +84,7 @@ function StatusBadge({
|
||||
}
|
||||
if (errorCount === total && total > 0) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-red-500/10 text-red-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-red-500/10 text-red-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-red-400" />
|
||||
연계 오류
|
||||
</span>
|
||||
@ -92,14 +92,14 @@ function StatusBadge({
|
||||
}
|
||||
if (errorCount > 0) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-yellow-500/10 text-yellow-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-yellow-500/10 text-yellow-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-yellow-400" />
|
||||
일부 오류 ({errorCount}/{total})
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-emerald-500/10 text-emerald-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-emerald-500/10 text-emerald-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
정상
|
||||
</span>
|
||||
@ -118,7 +118,7 @@ const TABLE_HEADERS = [
|
||||
function ForecastTable({ rows, loading }: { rows: NumericalDataStatus[]; loading: boolean }) {
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<table className="w-full text-caption border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-bg-elevated text-t3 uppercase tracking-wide">
|
||||
{TABLE_HEADERS.map((h) => (
|
||||
@ -193,10 +193,10 @@ export default function MonitorForecastPanel() {
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-stroke-1 shrink-0">
|
||||
<h2 className="text-sm font-semibold text-t1">수치예측자료 모니터링</h2>
|
||||
<h2 className="text-body-2 font-semibold text-t1">수치예측자료 모니터링</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
{lastUpdate && (
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
갱신:{' '}
|
||||
{lastUpdate.toLocaleTimeString('ko-KR', {
|
||||
hour: '2-digit',
|
||||
@ -208,7 +208,7 @@ export default function MonitorForecastPanel() {
|
||||
<button
|
||||
onClick={() => void fetchData()}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-caption rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<svg
|
||||
className={`w-3.5 h-3.5 ${loading ? 'animate-spin' : ''}`}
|
||||
@ -234,7 +234,7 @@ export default function MonitorForecastPanel() {
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-4 py-2.5 text-xs font-medium border-b-2 transition-colors ${
|
||||
className={`px-4 py-2.5 text-caption font-medium border-b-2 transition-colors ${
|
||||
activeTab === tab.id
|
||||
? 'border-cyan-400 text-cyan-400'
|
||||
: 'border-transparent text-t3 hover:text-t2'
|
||||
@ -248,7 +248,9 @@ export default function MonitorForecastPanel() {
|
||||
{/* 상태 표시줄 */}
|
||||
<div className="flex items-center gap-3 px-5 py-2 shrink-0 border-b border-stroke-1 bg-bg-base">
|
||||
<StatusBadge loading={loading} errorCount={errorCount} total={totalCount} />
|
||||
{!loading && totalCount > 0 && <span className="text-xs text-t3">모델 {totalCount}개</span>}
|
||||
{!loading && totalCount > 0 && (
|
||||
<span className="text-caption text-t3">모델 {totalCount}개</span>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* 테이블 */}
|
||||
|
||||
@ -84,7 +84,7 @@ function StatusBadge({
|
||||
}) {
|
||||
if (loading) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-bg-elevated text-t2">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-bg-elevated text-t2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse" />
|
||||
조회 중...
|
||||
</span>
|
||||
@ -92,7 +92,7 @@ function StatusBadge({
|
||||
}
|
||||
if (errorCount === total) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-red-500/10 text-red-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-red-500/10 text-red-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-red-400" />
|
||||
연계 오류
|
||||
</span>
|
||||
@ -100,14 +100,14 @@ function StatusBadge({
|
||||
}
|
||||
if (errorCount > 0) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-yellow-500/10 text-yellow-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-yellow-500/10 text-yellow-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-yellow-400" />
|
||||
일부 오류 ({errorCount}/{total})
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-emerald-500/10 text-emerald-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-emerald-500/10 text-emerald-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
정상
|
||||
</span>
|
||||
@ -130,7 +130,7 @@ function KhoaTable({ rows, loading }: { rows: KhoaRow[]; loading: boolean }) {
|
||||
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<table className="w-full text-caption border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-bg-elevated text-t3 uppercase tracking-wide">
|
||||
{headers.map((h) => (
|
||||
@ -172,11 +172,11 @@ function KhoaTable({ rows, loading }: { rows: KhoaRow[]; loading: boolean }) {
|
||||
<td className="px-3 py-2 text-t2">{fmt(row.data?.tide_level, 0)}</td>
|
||||
<td className="px-3 py-2">
|
||||
{row.error ? (
|
||||
<span className="text-red-400 text-xs">오류</span>
|
||||
<span className="text-red-400 text-caption">오류</span>
|
||||
) : row.data ? (
|
||||
<span className="text-emerald-400 text-xs">정상</span>
|
||||
<span className="text-emerald-400 text-caption">정상</span>
|
||||
) : (
|
||||
<span className="text-t3 text-xs">-</span>
|
||||
<span className="text-t3 text-caption">-</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
@ -201,7 +201,7 @@ function KmaUltraTable({ rows, loading }: { rows: KmaUltraRow[]; loading: boolea
|
||||
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<table className="w-full text-caption border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-bg-elevated text-t3 uppercase tracking-wide">
|
||||
{headers.map((h) => (
|
||||
@ -241,11 +241,11 @@ function KmaUltraTable({ rows, loading }: { rows: KmaUltraRow[]; loading: boolea
|
||||
<td className="px-3 py-2 text-t2">{fmt(row.data?.humidity, 0)}</td>
|
||||
<td className="px-3 py-2">
|
||||
{row.error ? (
|
||||
<span className="text-red-400 text-xs">오류</span>
|
||||
<span className="text-red-400 text-caption">오류</span>
|
||||
) : row.data ? (
|
||||
<span className="text-emerald-400 text-xs">정상</span>
|
||||
<span className="text-emerald-400 text-caption">정상</span>
|
||||
) : (
|
||||
<span className="text-t3 text-xs">-</span>
|
||||
<span className="text-t3 text-caption">-</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
@ -261,7 +261,7 @@ function MarineTable({ rows, loading }: { rows: MarineRow[]; loading: boolean })
|
||||
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<table className="w-full text-caption border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-bg-elevated text-t3 uppercase tracking-wide">
|
||||
{headers.map((h) => (
|
||||
@ -294,11 +294,11 @@ function MarineTable({ rows, loading }: { rows: MarineRow[]; loading: boolean })
|
||||
<td className="px-3 py-2 text-t2">{fmt(row.data?.temperature)}</td>
|
||||
<td className="px-3 py-2">
|
||||
{row.error ? (
|
||||
<span className="text-red-400 text-xs">오류</span>
|
||||
<span className="text-red-400 text-caption">오류</span>
|
||||
) : row.data ? (
|
||||
<span className="text-emerald-400 text-xs">정상</span>
|
||||
<span className="text-emerald-400 text-caption">정상</span>
|
||||
) : (
|
||||
<span className="text-t3 text-xs">-</span>
|
||||
<span className="text-t3 text-caption">-</span>
|
||||
)}
|
||||
</td>
|
||||
</tr>
|
||||
@ -441,10 +441,10 @@ export default function MonitorRealtimePanel() {
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-stroke-1 shrink-0">
|
||||
<h2 className="text-sm font-semibold text-t1">실시간 관측자료 모니터링</h2>
|
||||
<h2 className="text-body-2 font-semibold text-t1">실시간 관측자료 모니터링</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
{lastUpdate && (
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
갱신:{' '}
|
||||
{lastUpdate.toLocaleTimeString('ko-KR', {
|
||||
hour: '2-digit',
|
||||
@ -456,7 +456,7 @@ export default function MonitorRealtimePanel() {
|
||||
<button
|
||||
onClick={handleRefresh}
|
||||
disabled={isLoading}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-caption rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<svg
|
||||
className={`w-3.5 h-3.5 ${isLoading ? 'animate-spin' : ''}`}
|
||||
@ -482,7 +482,7 @@ export default function MonitorRealtimePanel() {
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-4 py-2.5 text-xs font-medium border-b-2 transition-colors ${
|
||||
className={`px-4 py-2.5 text-caption font-medium border-b-2 transition-colors ${
|
||||
activeTab === tab.id
|
||||
? 'border-cyan-400 text-cyan-400'
|
||||
: 'border-transparent text-t3 hover:text-t2'
|
||||
@ -496,7 +496,7 @@ export default function MonitorRealtimePanel() {
|
||||
{/* 상태 표시줄 */}
|
||||
<div className="flex items-center gap-3 px-5 py-2 shrink-0 border-b border-stroke-1 bg-bg-base">
|
||||
<StatusBadge loading={isLoading} errorCount={errorCount} total={totalCount} />
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
{activeTab === 'khoa' && `관측소 ${totalCount}개`}
|
||||
{activeTab === 'kma-ultra' && `지점 ${totalCount}개`}
|
||||
{activeTab === 'kma-marine' && `해역 ${totalCount}개`}
|
||||
|
||||
@ -300,7 +300,7 @@ function StatusBadge({
|
||||
}) {
|
||||
if (loading) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-bg-elevated text-t2">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-bg-elevated text-t2">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-cyan-400 animate-pulse" />
|
||||
조회 중...
|
||||
</span>
|
||||
@ -309,7 +309,7 @@ function StatusBadge({
|
||||
const offCount = total - onCount;
|
||||
if (offCount === total) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-red-500/10 text-red-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-red-500/10 text-red-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-red-400" />
|
||||
전체 OFF
|
||||
</span>
|
||||
@ -317,14 +317,14 @@ function StatusBadge({
|
||||
}
|
||||
if (offCount > 0) {
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-yellow-500/10 text-yellow-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-yellow-500/10 text-yellow-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-yellow-400" />
|
||||
일부 OFF ({offCount}/{total})
|
||||
</span>
|
||||
);
|
||||
}
|
||||
return (
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-xs bg-emerald-500/10 text-emerald-400">
|
||||
<span className="inline-flex items-center gap-1 px-2 py-0.5 rounded text-caption bg-emerald-500/10 text-emerald-400">
|
||||
<span className="w-1.5 h-1.5 rounded-full bg-emerald-400" />
|
||||
전체 정상
|
||||
</span>
|
||||
@ -376,7 +376,7 @@ const HEADERS = [
|
||||
function VesselTable({ rows, loading }: { rows: VesselMonitorRow[]; loading: boolean }) {
|
||||
return (
|
||||
<div className="overflow-auto">
|
||||
<table className="w-full text-xs border-collapse">
|
||||
<table className="w-full text-caption border-collapse">
|
||||
<thead>
|
||||
<tr className="bg-bg-elevated text-t3 uppercase tracking-wide">
|
||||
{HEADERS.map((h) => (
|
||||
@ -462,10 +462,10 @@ export default function MonitorVesselPanel() {
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-5 py-3 border-b border-stroke-1 shrink-0">
|
||||
<h2 className="text-sm font-semibold text-t1">선박위치정보 모니터링</h2>
|
||||
<h2 className="text-body-2 font-semibold text-t1">선박위치정보 모니터링</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
{lastUpdate && (
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
갱신:{' '}
|
||||
{lastUpdate.toLocaleTimeString('ko-KR', {
|
||||
hour: '2-digit',
|
||||
@ -477,7 +477,7 @@ export default function MonitorVesselPanel() {
|
||||
<button
|
||||
onClick={fetchData}
|
||||
disabled={loading}
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
className="flex items-center gap-1.5 px-3 py-1.5 text-caption rounded bg-bg-elevated hover:bg-bg-card text-t2 disabled:opacity-50 disabled:cursor-not-allowed transition-colors"
|
||||
>
|
||||
<svg
|
||||
className={`w-3.5 h-3.5 ${loading ? 'animate-spin' : ''}`}
|
||||
@ -500,7 +500,7 @@ export default function MonitorVesselPanel() {
|
||||
{/* 상태 표시줄 */}
|
||||
<div className="flex items-center gap-3 px-5 py-2 shrink-0 border-b border-stroke-1 bg-bg-base">
|
||||
<StatusBadge loading={loading} onCount={onCount} total={rows.length} />
|
||||
<span className="text-xs text-t3">
|
||||
<span className="text-caption text-t3">
|
||||
연계 채널 {rows.length}개 (ON: {onCount} / OFF: {rows.length - onCount})
|
||||
</span>
|
||||
</div>
|
||||
|
||||
@ -557,7 +557,7 @@ function RolePermTab({
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center text-fg-disabled text-sm font-korean">
|
||||
<div className="flex-1 flex items-center justify-center text-fg-disabled text-body-2 font-korean">
|
||||
역할을 선택하세요
|
||||
</div>
|
||||
)}
|
||||
@ -567,7 +567,7 @@ function RolePermTab({
|
||||
<div className="fixed inset-0 z-50 flex items-center justify-center bg-black/50">
|
||||
<div className="w-[400px] bg-bg-surface rounded-lg border border-stroke shadow-2xl">
|
||||
<div className="px-5 py-4 border-b border-stroke">
|
||||
<h3 className="text-sm font-bold text-fg font-korean">새 역할 추가</h3>
|
||||
<h3 className="text-body-2 font-bold text-fg font-korean">새 역할 추가</h3>
|
||||
</div>
|
||||
<div className="px-5 py-4 flex flex-col gap-3">
|
||||
<div>
|
||||
@ -581,7 +581,7 @@ function RolePermTab({
|
||||
setNewRoleCode(e.target.value.toUpperCase().replace(/[^A-Z0-9_]/g, ''))
|
||||
}
|
||||
placeholder="CUSTOM_ROLE"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
영문 대문자, 숫자, 언더스코어만 허용 (생성 후 변경 불가)
|
||||
@ -596,7 +596,7 @@ function RolePermTab({
|
||||
value={newRoleName}
|
||||
onChange={(e) => setNewRoleName(e.target.value)}
|
||||
placeholder="사용자 정의 역할"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -608,7 +608,7 @@ function RolePermTab({
|
||||
value={newRoleDesc}
|
||||
onChange={(e) => setNewRoleDesc(e.target.value)}
|
||||
placeholder="역할에 대한 설명"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
{createError && (
|
||||
@ -620,14 +620,14 @@ function RolePermTab({
|
||||
<div className="px-5 py-3 border-t border-stroke flex justify-end gap-2">
|
||||
<button
|
||||
onClick={() => setShowCreateForm(false)}
|
||||
className="px-4 py-2 text-xs text-fg-disabled border border-stroke rounded-md hover:bg-bg-surface-hover font-korean"
|
||||
className="px-4 py-2 text-caption text-fg-disabled border border-stroke rounded-md hover:bg-bg-surface-hover font-korean"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
<button
|
||||
onClick={handleCreateRole}
|
||||
disabled={!newRoleCode || !newRoleName || creating}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean disabled:opacity-50"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean disabled:opacity-50"
|
||||
>
|
||||
{creating ? '생성 중...' : '생성'}
|
||||
</button>
|
||||
@ -815,7 +815,7 @@ function UserPermTab({ roles, permTree, rolePerms }: UserPermTabProps) {
|
||||
onFocus={() => setShowDropdown(true)}
|
||||
placeholder={loadingUsers ? '불러오는 중...' : '이름, 계정, 조직으로 검색...'}
|
||||
disabled={loadingUsers}
|
||||
className="w-full max-w-sm px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean disabled:opacity-50"
|
||||
className="w-full max-w-sm px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean disabled:opacity-50"
|
||||
/>
|
||||
{showDropdown && filteredUsers.length > 0 && (
|
||||
<div className="absolute left-0 top-full mt-1 w-full max-w-sm bg-bg-surface border border-stroke rounded-md shadow-xl z-20 overflow-auto max-h-52">
|
||||
@ -826,7 +826,7 @@ function UserPermTab({ roles, permTree, rolePerms }: UserPermTabProps) {
|
||||
className="w-full px-3 py-2 text-left hover:bg-bg-surface-hover transition-colors flex items-center gap-2"
|
||||
>
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="text-xs font-semibold text-fg font-korean truncate">
|
||||
<div className="text-caption font-semibold text-fg font-korean truncate">
|
||||
{user.name}
|
||||
{user.rank && (
|
||||
<span className="ml-1 text-caption text-fg-disabled font-korean">
|
||||
@ -848,7 +848,7 @@ function UserPermTab({ roles, permTree, rolePerms }: UserPermTabProps) {
|
||||
</div>
|
||||
)}
|
||||
{showDropdown && !loadingUsers && filteredUsers.length === 0 && searchQuery && (
|
||||
<div className="absolute left-0 top-full mt-1 w-full max-w-sm bg-bg-surface border border-stroke rounded-md shadow-xl z-20 px-3 py-2 text-xs text-fg-disabled font-korean">
|
||||
<div className="absolute left-0 top-full mt-1 w-full max-w-sm bg-bg-surface border border-stroke rounded-md shadow-xl z-20 px-3 py-2 text-caption text-fg-disabled font-korean">
|
||||
검색 결과 없음
|
||||
</div>
|
||||
)}
|
||||
@ -954,13 +954,13 @@ function UserPermTab({ roles, permTree, rolePerms }: UserPermTabProps) {
|
||||
</table>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center text-fg-disabled text-sm font-korean">
|
||||
<div className="flex-1 flex items-center justify-center text-fg-disabled text-body-2 font-korean">
|
||||
역할을 하나 이상 할당하면 유효 권한이 표시됩니다
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div className="flex-1 flex items-center justify-center text-fg-disabled text-sm font-korean">
|
||||
<div className="flex-1 flex items-center justify-center text-fg-disabled text-body-2 font-korean">
|
||||
사용자를 선택하세요
|
||||
</div>
|
||||
)}
|
||||
@ -1180,7 +1180,7 @@ function PermissionsPanel() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
);
|
||||
@ -1194,7 +1194,7 @@ function PermissionsPanel() {
|
||||
style={{ flexShrink: 0 }}
|
||||
>
|
||||
<div>
|
||||
<h1 className="text-sm font-bold text-fg font-korean">권한 관리</h1>
|
||||
<h1 className="text-body-2 font-bold text-fg font-korean">권한 관리</h1>
|
||||
<p className="text-caption text-fg-disabled mt-0.5 font-korean">
|
||||
역할별 리소스 × CRUD 권한 설정
|
||||
</p>
|
||||
@ -1203,7 +1203,7 @@ function PermissionsPanel() {
|
||||
<div className="flex items-center gap-1 p-1 bg-bg-elevated rounded-lg border border-stroke">
|
||||
<button
|
||||
onClick={() => setActiveTab('role')}
|
||||
className={`px-4 py-1.5 text-xs font-semibold rounded-md transition-all font-korean ${
|
||||
className={`px-4 py-1.5 text-caption font-semibold rounded-md transition-all font-korean ${
|
||||
activeTab === 'role'
|
||||
? 'bg-color-accent text-bg-0 shadow-[0_0_8px_rgba(6,182,212,0.25)]'
|
||||
: 'text-fg-disabled hover:text-fg-sub'
|
||||
@ -1213,7 +1213,7 @@ function PermissionsPanel() {
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setActiveTab('user')}
|
||||
className={`px-4 py-1.5 text-xs font-semibold rounded-md transition-all font-korean ${
|
||||
className={`px-4 py-1.5 text-caption font-semibold rounded-md transition-all font-korean ${
|
||||
activeTab === 'user'
|
||||
? 'bg-color-accent text-bg-0 shadow-[0_0_8px_rgba(6,182,212,0.25)]'
|
||||
: 'text-fg-disabled hover:text-fg-sub'
|
||||
|
||||
@ -136,7 +136,7 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
<div className="flex items-center justify-between mb-3">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">{title}</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">총 {total}개</p>
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">총 {total}개</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
@ -146,12 +146,12 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
onChange={(e) => setSearchInput(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
placeholder="레이어코드 / 레이어명 검색"
|
||||
className="flex-1 px-3 py-1.5 text-xs bg-bg-elevated border border-stroke rounded text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="flex-1 px-3 py-1.5 text-caption bg-bg-elevated border border-stroke rounded text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
<select
|
||||
value={filterUseYn}
|
||||
onChange={(e) => setFilterUseYn(e.target.value)}
|
||||
className="px-2 py-1.5 text-xs bg-bg-elevated border border-stroke rounded text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-2 py-1.5 text-caption bg-bg-elevated border border-stroke rounded text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">전체</option>
|
||||
<option value="Y">사용</option>
|
||||
@ -159,7 +159,7 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
</select>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
className="px-3 py-1.5 text-xs border border-stroke text-fg-sub rounded hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
className="px-3 py-1.5 text-caption border border-stroke text-fg-sub rounded hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
>
|
||||
검색
|
||||
</button>
|
||||
@ -168,7 +168,7 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
|
||||
{/* 오류 메시지 */}
|
||||
{error && (
|
||||
<div className="px-6 py-2 text-xs text-red-400 bg-[rgba(239,68,68,0.05)] border-b border-stroke shrink-0 font-korean">
|
||||
<div className="px-6 py-2 text-caption text-red-400 bg-[rgba(239,68,68,0.05)] border-b border-stroke shrink-0 font-korean">
|
||||
{error}
|
||||
</div>
|
||||
)}
|
||||
@ -176,7 +176,7 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
{/* 테이블 영역 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-full text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-full text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
) : (
|
||||
@ -217,7 +217,7 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
<tr>
|
||||
<td
|
||||
colSpan={9}
|
||||
className="px-4 py-12 text-center text-fg-disabled text-sm font-korean"
|
||||
className="px-4 py-12 text-center text-fg-disabled text-body-2 font-korean"
|
||||
>
|
||||
데이터가 없습니다.
|
||||
</td>
|
||||
@ -228,12 +228,12 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
key={item.layerCd}
|
||||
className="border-b border-stroke hover:bg-[rgba(255,255,255,0.02)] transition-colors"
|
||||
>
|
||||
<td className="px-4 py-3 text-xs text-fg-disabled font-mono">
|
||||
<td className="px-4 py-3 text-caption text-fg-disabled font-mono">
|
||||
{(page - 1) * PAGE_SIZE + idx + 1}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-label-2 text-fg-sub font-mono">{item.layerCd}</td>
|
||||
<td className="px-4 py-3 text-xs text-fg font-korean">{item.layerNm}</td>
|
||||
<td className="px-4 py-3 text-xs text-fg-sub font-korean max-w-[200px]">
|
||||
<td className="px-4 py-3 text-caption text-fg font-korean">{item.layerNm}</td>
|
||||
<td className="px-4 py-3 text-caption text-fg-sub font-korean max-w-[200px]">
|
||||
<span className="block truncate" title={item.layerFullNm}>
|
||||
{item.layerFullNm}
|
||||
</span>
|
||||
@ -246,7 +246,7 @@ const SensitiveLayerPanel = ({ categoryCode, title }: SensitiveLayerPanelProps)
|
||||
<td className="px-4 py-3 text-label-2 text-fg-sub font-mono">
|
||||
{item.wmsLayerNm ?? <span className="text-fg-disabled">-</span>}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-xs text-fg-disabled text-center font-mono">
|
||||
<td className="px-4 py-3 text-caption text-fg-disabled text-center font-mono">
|
||||
{item.sortOrd}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-label-2 text-fg-disabled font-mono">
|
||||
|
||||
@ -54,7 +54,7 @@ function SettingsPanel() {
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
);
|
||||
@ -64,7 +64,7 @@ function SettingsPanel() {
|
||||
<div className="flex flex-col h-full">
|
||||
<div className="px-6 py-4 border-b border-stroke">
|
||||
<h1 className="text-lg font-bold text-fg font-korean">시스템 설정</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
사용자 등록 및 권한 관련 시스템 설정을 관리합니다
|
||||
</p>
|
||||
</div>
|
||||
@ -74,7 +74,7 @@ function SettingsPanel() {
|
||||
{/* 사용자 등록 설정 */}
|
||||
<div className="rounded-lg border border-stroke bg-bg-surface overflow-hidden">
|
||||
<div className="px-5 py-3 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">사용자 등록 설정</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">사용자 등록 설정</h2>
|
||||
<p className="text-label-2 text-fg-disabled mt-0.5 font-korean">
|
||||
신규 사용자 등록 시 적용되는 정책을 설정합니다
|
||||
</p>
|
||||
@ -140,7 +140,7 @@ function SettingsPanel() {
|
||||
{/* OAuth 설정 */}
|
||||
<div className="rounded-lg border border-stroke bg-bg-surface overflow-hidden">
|
||||
<div className="px-5 py-3 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">Google OAuth 설정</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">Google OAuth 설정</h2>
|
||||
<p className="text-label-2 text-fg-disabled mt-0.5 font-korean">
|
||||
Google 계정 로그인 시 자동 승인할 이메일 도메인을 설정합니다
|
||||
</p>
|
||||
@ -162,7 +162,7 @@ function SettingsPanel() {
|
||||
value={oauthDomainInput}
|
||||
onChange={(e) => setOauthDomainInput(e.target.value)}
|
||||
placeholder="gcsc.co.kr, example.com"
|
||||
className="flex-1 px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="flex-1 px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
<button
|
||||
onClick={async () => {
|
||||
@ -183,7 +183,7 @@ function SettingsPanel() {
|
||||
savingOAuth ||
|
||||
oauthDomainInput.trim() === (oauthSettings?.autoApproveDomains || '')
|
||||
}
|
||||
className={`px-4 py-2 text-xs font-semibold rounded-md transition-all font-korean whitespace-nowrap ${
|
||||
className={`px-4 py-2 text-caption font-semibold rounded-md transition-all font-korean whitespace-nowrap ${
|
||||
oauthDomainInput.trim() !== (oauthSettings?.autoApproveDomains || '')
|
||||
? 'bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)]'
|
||||
: 'bg-bg-card text-fg-disabled cursor-not-allowed'
|
||||
@ -220,7 +220,7 @@ function SettingsPanel() {
|
||||
{/* 현재 설정 상태 요약 */}
|
||||
<div className="rounded-lg border border-stroke bg-bg-surface overflow-hidden">
|
||||
<div className="px-5 py-3 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">설정 상태 요약</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">설정 상태 요약</h2>
|
||||
</div>
|
||||
<div className="px-5 py-4">
|
||||
<div className="flex flex-col gap-3 text-label-1 font-korean">
|
||||
|
||||
@ -71,7 +71,7 @@ function SortableMenuItem({
|
||||
<circle cx="9" cy="14" r="1.5" />
|
||||
</svg>
|
||||
</button>
|
||||
<span className="text-fg-disabled text-xs font-mono w-6 text-center shrink-0">
|
||||
<span className="text-fg-disabled text-caption font-mono w-6 text-center shrink-0">
|
||||
{idx + 1}
|
||||
</span>
|
||||
{isEditing ? (
|
||||
@ -152,14 +152,14 @@ function SortableMenuItem({
|
||||
<button
|
||||
onClick={() => onMove(idx, -1)}
|
||||
disabled={idx === 0}
|
||||
className="w-7 h-7 rounded border border-stroke bg-bg-elevated text-fg-disabled text-xs flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
className="w-7 h-7 rounded border border-stroke bg-bg-elevated text-fg-disabled text-caption flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
>
|
||||
▲
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onMove(idx, 1)}
|
||||
disabled={idx === totalCount - 1}
|
||||
className="w-7 h-7 rounded border border-stroke bg-bg-elevated text-fg-disabled text-xs flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
className="w-7 h-7 rounded border border-stroke bg-bg-elevated text-fg-disabled text-caption flex items-center justify-center hover:bg-bg-surface-hover hover:text-fg transition-all disabled:opacity-30 disabled:cursor-not-allowed"
|
||||
>
|
||||
▼
|
||||
</button>
|
||||
|
||||
@ -87,7 +87,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
<div className="bg-bg-surface border border-stroke rounded-lg shadow-lg w-[480px] max-h-[90vh] flex flex-col">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<h2 className="text-sm font-bold text-fg font-korean">사용자 등록</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">사용자 등록</h2>
|
||||
<button onClick={onClose} className="text-fg-disabled hover:text-fg transition-colors">
|
||||
<svg
|
||||
width="16"
|
||||
@ -115,7 +115,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
value={account}
|
||||
onChange={(e) => setAccount(e.target.value)}
|
||||
placeholder="로그인 계정 ID"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -129,7 +129,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
value={password}
|
||||
onChange={(e) => setPassword(e.target.value)}
|
||||
placeholder="초기 비밀번호"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -143,7 +143,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
placeholder="실명"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -157,7 +157,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
value={rank}
|
||||
onChange={(e) => setRank(e.target.value)}
|
||||
placeholder="예: 팀장, 주임 등"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -169,7 +169,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
<select
|
||||
value={orgSn}
|
||||
onChange={(e) => setOrgSn(e.target.value !== '' ? Number(e.target.value) : '')}
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">소속 없음</option>
|
||||
{allOrgs.map((org) => (
|
||||
@ -191,7 +191,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
value={email}
|
||||
onChange={(e) => setEmail(e.target.value)}
|
||||
placeholder="이메일 주소"
|
||||
className="w-full px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="w-full px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@ -217,7 +217,7 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
onChange={() => toggleRole(role.sn)}
|
||||
style={{ accentColor: color }}
|
||||
/>
|
||||
<span className="text-xs font-korean" style={{ color }}>
|
||||
<span className="text-caption font-korean" style={{ color }}>
|
||||
{role.name}
|
||||
</span>
|
||||
<span className="text-caption text-fg-disabled font-mono">{role.code}</span>
|
||||
@ -237,14 +237,14 @@ function RegisterModal({ allRoles, allOrgs, onClose, onSuccess }: RegisterModalP
|
||||
<button
|
||||
type="button"
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-xs border border-stroke text-fg-sub rounded-md hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
className="px-4 py-2 text-caption border border-stroke text-fg-sub rounded-md hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={submitting}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all disabled:opacity-50 font-korean"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all disabled:opacity-50 font-korean"
|
||||
>
|
||||
{submitting ? '등록 중...' : '등록'}
|
||||
</button>
|
||||
@ -332,7 +332,7 @@ function UserDetailModal({ user, allOrgs, onClose, onUpdated }: UserDetailModalP
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h2 className="text-sm font-bold text-fg font-korean">사용자 정보</h2>
|
||||
<h2 className="text-body-2 font-bold text-fg font-korean">사용자 정보</h2>
|
||||
<p className="text-caption text-fg-disabled font-mono mt-0.5">{user.account}</p>
|
||||
</div>
|
||||
<button onClick={onClose} className="text-fg-disabled hover:text-fg transition-colors">
|
||||
@ -364,7 +364,7 @@ function UserDetailModal({ user, allOrgs, onClose, onUpdated }: UserDetailModalP
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={(e) => setName(e.target.value)}
|
||||
className="w-full px-3 py-1.5 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-1.5 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
@ -377,7 +377,7 @@ function UserDetailModal({ user, allOrgs, onClose, onUpdated }: UserDetailModalP
|
||||
value={rank}
|
||||
onChange={(e) => setRank(e.target.value)}
|
||||
placeholder="예: 팀장"
|
||||
className="w-full px-3 py-1.5 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-1.5 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -387,7 +387,7 @@ function UserDetailModal({ user, allOrgs, onClose, onUpdated }: UserDetailModalP
|
||||
<select
|
||||
value={orgSn}
|
||||
onChange={(e) => setOrgSn(e.target.value !== '' ? Number(e.target.value) : '')}
|
||||
className="w-full px-3 py-1.5 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-full px-3 py-1.5 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">소속 없음</option>
|
||||
{allOrgs.map((org) => (
|
||||
@ -427,7 +427,7 @@ function UserDetailModal({ user, allOrgs, onClose, onUpdated }: UserDetailModalP
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
placeholder="새 비밀번호 입력"
|
||||
className="w-full px-3 py-1.5 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
className="w-full px-3 py-1.5 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-mono"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
@ -535,7 +535,7 @@ function UserDetailModal({ user, allOrgs, onClose, onUpdated }: UserDetailModalP
|
||||
<div className="flex items-center justify-end px-6 py-3 border-t border-stroke">
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-4 py-2 text-xs border border-stroke text-fg-sub rounded-md hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
className="px-4 py-2 text-caption border border-stroke text-fg-sub rounded-md hover:bg-[rgba(255,255,255,0.04)] transition-all font-korean"
|
||||
>
|
||||
닫기
|
||||
</button>
|
||||
@ -680,7 +680,7 @@ function UsersPanel() {
|
||||
<div className="flex items-center gap-3">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">사용자 관리</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
총 {filteredUsers.length}명
|
||||
</p>
|
||||
</div>
|
||||
@ -698,7 +698,7 @@ function UsersPanel() {
|
||||
setOrgFilter(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">전체 소속</option>
|
||||
{allOrgs.map((org) => (
|
||||
@ -711,7 +711,7 @@ function UsersPanel() {
|
||||
<select
|
||||
value={statusFilter}
|
||||
onChange={(e) => setStatusFilter(e.target.value)}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="">전체 상태</option>
|
||||
<option value="PENDING">승인대기</option>
|
||||
@ -726,11 +726,11 @@ function UsersPanel() {
|
||||
placeholder="이름, 계정 검색..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-56 px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-56 px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
<button
|
||||
onClick={() => setShowRegisterModal(true)}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_12px_rgba(6,182,212,0.3)] transition-all font-korean"
|
||||
>
|
||||
+ 사용자 등록
|
||||
</button>
|
||||
@ -740,7 +740,7 @@ function UsersPanel() {
|
||||
{/* 테이블 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
) : (
|
||||
@ -781,7 +781,7 @@ function UsersPanel() {
|
||||
<tr>
|
||||
<td
|
||||
colSpan={9}
|
||||
className="px-6 py-10 text-center text-xs text-fg-disabled font-korean"
|
||||
className="px-6 py-10 text-center text-caption text-fg-disabled font-korean"
|
||||
>
|
||||
조회된 사용자가 없습니다.
|
||||
</td>
|
||||
@ -897,7 +897,7 @@ function UsersPanel() {
|
||||
onChange={() => toggleRoleSelection(role.sn)}
|
||||
style={{ accentColor: color }}
|
||||
/>
|
||||
<span className="text-xs font-korean" style={{ color }}>
|
||||
<span className="text-caption font-korean" style={{ color }}>
|
||||
{role.name}
|
||||
</span>
|
||||
<span className="text-caption text-fg-disabled font-mono">
|
||||
|
||||
@ -90,7 +90,7 @@ function VesselMaterialsPanel() {
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h1 className="text-lg font-bold text-fg font-korean">방제선 보유자재 현황</h1>
|
||||
<p className="text-xs text-fg-disabled mt-1 font-korean">
|
||||
<p className="text-caption text-fg-disabled mt-1 font-korean">
|
||||
총 {filtered.length}개 기관 (방제선 보유)
|
||||
</p>
|
||||
</div>
|
||||
@ -98,7 +98,7 @@ function VesselMaterialsPanel() {
|
||||
<select
|
||||
value={regionFilter}
|
||||
onChange={handleFilterChange(setRegionFilter)}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="전체">전체 관할청</option>
|
||||
<option value="남해">남해청</option>
|
||||
@ -110,7 +110,7 @@ function VesselMaterialsPanel() {
|
||||
<select
|
||||
value={typeFilter}
|
||||
onChange={handleFilterChange(setTypeFilter)}
|
||||
className="px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
className="px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg focus:border-color-accent focus:outline-none font-korean"
|
||||
>
|
||||
<option value="전체">전체 유형</option>
|
||||
{typeOptions.map((t) => (
|
||||
@ -127,11 +127,11 @@ function VesselMaterialsPanel() {
|
||||
setSearchTerm(e.target.value);
|
||||
setCurrentPage(1);
|
||||
}}
|
||||
className="w-56 px-3 py-2 text-xs bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
className="w-56 px-3 py-2 text-caption bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none font-korean"
|
||||
/>
|
||||
<button
|
||||
onClick={load}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md bg-bg-elevated border border-stroke text-fg-sub hover:border-color-accent hover:text-color-accent transition-all font-korean"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md bg-bg-elevated border border-stroke text-fg-sub hover:border-color-accent hover:text-color-accent transition-all font-korean"
|
||||
>
|
||||
새로고침
|
||||
</button>
|
||||
@ -141,7 +141,7 @@ function VesselMaterialsPanel() {
|
||||
{/* 테이블 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-sm font-korean">
|
||||
<div className="flex items-center justify-center h-32 text-fg-disabled text-body-2 font-korean">
|
||||
불러오는 중...
|
||||
</div>
|
||||
) : (
|
||||
@ -188,7 +188,7 @@ function VesselMaterialsPanel() {
|
||||
<tr>
|
||||
<td
|
||||
colSpan={11}
|
||||
className="px-6 py-10 text-center text-xs text-fg-disabled font-korean"
|
||||
className="px-6 py-10 text-center text-caption text-fg-disabled font-korean"
|
||||
>
|
||||
조회된 기관이 없습니다.
|
||||
</td>
|
||||
|
||||
@ -142,17 +142,17 @@ export default function VesselSignalPanel() {
|
||||
<div className="flex flex-col h-full overflow-hidden">
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-6 py-3 border-b border-stroke-1">
|
||||
<h2 className="text-sm font-semibold text-fg">선박신호 수신 현황</h2>
|
||||
<h2 className="text-body-2 font-semibold text-fg">선박신호 수신 현황</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
<input
|
||||
type="date"
|
||||
value={date}
|
||||
onChange={(e) => setDate(e.target.value)}
|
||||
className="px-2 py-1 text-xs rounded bg-bg-elevated border border-stroke-1 text-fg"
|
||||
className="px-2 py-1 text-caption rounded bg-bg-elevated border border-stroke-1 text-fg"
|
||||
/>
|
||||
<button
|
||||
onClick={load}
|
||||
className="px-3 py-1 text-xs rounded bg-bg-elevated border border-stroke-1 text-fg-sub hover:bg-bg-card"
|
||||
className="px-3 py-1 text-caption rounded bg-bg-elevated border border-stroke-1 text-fg-sub hover:bg-bg-card"
|
||||
>
|
||||
새로고침
|
||||
</button>
|
||||
@ -163,7 +163,7 @@ export default function VesselSignalPanel() {
|
||||
<div className="flex-1 overflow-y-auto px-6 py-5">
|
||||
{loading ? (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<span className="text-xs text-fg-disabled">로딩 중...</span>
|
||||
<span className="text-caption text-fg-disabled">로딩 중...</span>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex gap-2">
|
||||
|
||||
@ -589,7 +589,7 @@ export function CctvView() {
|
||||
{/* 헤더 */}
|
||||
<div className="p-3 pb-2.5 border-b border-stroke shrink-0 bg-bg-elevated">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="text-xs font-bold text-fg font-korean flex items-center gap-1.5">
|
||||
<div className="text-caption font-bold text-fg font-korean flex items-center gap-1.5">
|
||||
<span
|
||||
className="w-[7px] h-[7px] rounded-full inline-block animate-pulse"
|
||||
style={{ background: 'var(--color-danger)' }}
|
||||
@ -776,7 +776,7 @@ export function CctvView() {
|
||||
{/* 뷰어 툴바 */}
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-stroke bg-bg-elevated shrink-0 gap-2.5">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<div className="text-xs font-bold text-fg font-korean whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<div className="text-caption font-bold text-fg font-korean whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{selectedCamera ? `📹 ${selectedCamera.cameraNm}` : '📹 카메라를 선택하세요'}
|
||||
</div>
|
||||
{selectedCamera?.sttsCd === 'LIVE' && (
|
||||
@ -966,7 +966,7 @@ export function CctvView() {
|
||||
<div key={srcKey} className="mb-5">
|
||||
{/* 출처 헤더 */}
|
||||
<div className="flex items-center gap-2 mb-2 pb-1.5 border-b border-stroke">
|
||||
<span className="text-sm">{group.icon}</span>
|
||||
<span className="text-body-2">{group.icon}</span>
|
||||
<span className="text-label-1 font-bold text-fg font-korean">
|
||||
{group.label}
|
||||
</span>
|
||||
|
||||
@ -421,7 +421,7 @@ export function MediaManagement() {
|
||||
<div className="fixed inset-0 z-[300] bg-black/60 backdrop-blur-sm flex items-center justify-center">
|
||||
<div className="bg-bg-surface border border-stroke rounded-md p-6 w-72 text-center">
|
||||
<div className="text-2xl mb-3">📥</div>
|
||||
<div className="text-sm font-bold font-korean mb-3">다운로드 완료</div>
|
||||
<div className="text-body-2 font-bold font-korean mb-3">다운로드 완료</div>
|
||||
<div className="text-title-4 font-korean text-fg-sub mb-1">
|
||||
총 <span className="text-color-accent font-bold">{downloadResult.total}</span>건 선택
|
||||
</div>
|
||||
@ -441,7 +441,7 @@ export function MediaManagement() {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setDownloadResult(null)}
|
||||
className="px-6 py-2 text-sm font-semibold rounded bg-[rgba(6,182,212,0.15)] text-color-accent border border-[rgba(6,182,212,0.3)] hover:bg-[rgba(6,182,212,0.25)] transition-colors font-korean"
|
||||
className="px-6 py-2 text-body-2 font-semibold rounded bg-[rgba(6,182,212,0.15)] text-color-accent border border-[rgba(6,182,212,0.3)] hover:bg-[rgba(6,182,212,0.25)] transition-colors font-korean"
|
||||
>
|
||||
확인
|
||||
</button>
|
||||
@ -475,7 +475,7 @@ export function MediaManagement() {
|
||||
</div>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
<label className="block text-caption font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
촬영 장비
|
||||
</label>
|
||||
<select className="prd-i w-full">
|
||||
@ -489,7 +489,7 @@ export function MediaManagement() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-3">
|
||||
<label className="block text-xs font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
<label className="block text-caption font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
연관 사고
|
||||
</label>
|
||||
<select className="prd-i w-full">
|
||||
@ -499,7 +499,7 @@ export function MediaManagement() {
|
||||
</select>
|
||||
</div>
|
||||
<div className="mb-4">
|
||||
<label className="block text-xs font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
<label className="block text-caption font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
메모
|
||||
</label>
|
||||
<textarea
|
||||
@ -508,7 +508,7 @@ export function MediaManagement() {
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
className="w-full py-3 rounded-sm text-sm font-bold font-korean cursor-pointer hover:brightness-125 transition-all"
|
||||
className="w-full py-3 rounded-sm text-body-2 font-bold font-korean cursor-pointer hover:brightness-125 transition-all"
|
||||
style={{
|
||||
background: 'rgba(6,182,212,0.15)',
|
||||
border: '1px solid rgba(6,182,212,0.3)',
|
||||
|
||||
@ -239,7 +239,7 @@ export function OilAreaAnalysis() {
|
||||
<div className="flex gap-5 h-full overflow-hidden">
|
||||
{/* ── Left Panel ── */}
|
||||
<div className="w-[280px] min-w-[280px] flex flex-col overflow-y-auto scrollbar-thin">
|
||||
<div className="text-sm font-bold mb-1 font-korean">🧩 영상사진합성</div>
|
||||
<div className="text-body-2 font-bold mb-1 font-korean">🧩 영상사진합성</div>
|
||||
<div className="text-label-2 text-fg-disabled mb-4 font-korean">
|
||||
드론 사진을 합성하여 유출유 확산 면적과 기름 양을 산정합니다.
|
||||
</div>
|
||||
@ -256,7 +256,7 @@ export function OilAreaAnalysis() {
|
||||
<button
|
||||
onClick={() => fileInputRef.current?.click()}
|
||||
disabled={selectedFiles.length >= MAX_IMAGES || isStitching || isAnalyzing}
|
||||
className="w-full py-2 mb-3 border border-dashed border-stroke rounded-sm text-xs font-korean text-fg-sub
|
||||
className="w-full py-2 mb-3 border border-dashed border-stroke rounded-sm text-caption font-korean text-fg-sub
|
||||
hover:border-color-accent hover:text-color-accent transition-colors cursor-pointer
|
||||
disabled:opacity-40 disabled:cursor-not-allowed"
|
||||
>
|
||||
|
||||
@ -177,7 +177,7 @@ export function RealtimeDrone() {
|
||||
{/* 헤더 */}
|
||||
<div className="p-3 pb-2.5 border-b border-stroke shrink-0 bg-bg-elevated">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="text-xs font-bold text-fg font-korean flex items-center gap-1.5">
|
||||
<div className="text-caption font-bold text-fg font-korean flex items-center gap-1.5">
|
||||
<span
|
||||
className="w-[7px] h-[7px] rounded-full inline-block"
|
||||
style={{
|
||||
@ -225,7 +225,7 @@ export function RealtimeDrone() {
|
||||
>
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="text-sm">🚁</div>
|
||||
<div className="text-body-2">🚁</div>
|
||||
<div>
|
||||
<div className="text-label-2 font-normal text-fg font-korean">
|
||||
{stream.shipName}{' '}
|
||||
@ -300,7 +300,7 @@ export function RealtimeDrone() {
|
||||
{/* 툴바 */}
|
||||
<div className="flex items-center justify-between px-4 py-2 border-b border-stroke bg-bg-elevated shrink-0 gap-2.5">
|
||||
<div className="flex items-center gap-2 min-w-0">
|
||||
<div className="text-xs font-bold text-fg font-korean whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
<div className="text-caption font-bold text-fg font-korean whitespace-nowrap overflow-hidden text-ellipsis">
|
||||
{selectedStream ? `🚁 ${selectedStream.shipName}` : '🚁 드론 스트림을 선택하세요'}
|
||||
</div>
|
||||
{selectedStream?.status === 'streaming' && (
|
||||
@ -566,7 +566,7 @@ export function RealtimeDrone() {
|
||||
style={{ minWidth: 170, background: 'var(--bg-card)', borderRadius: 6 }}
|
||||
>
|
||||
<div className="flex items-center gap-1.5 mb-1">
|
||||
<span className="text-sm">🚁</span>
|
||||
<span className="text-body-2">🚁</span>
|
||||
<div className="text-label-2 font-bold text-fg">{mapPopup.shipName}</div>
|
||||
</div>
|
||||
<div className="text-caption text-fg-disabled mb-0.5">
|
||||
@ -813,7 +813,7 @@ export function RealtimeDrone() {
|
||||
].map((item, i) => (
|
||||
<div key={i} className="px-2 py-1.5 bg-bg-base rounded text-center">
|
||||
<div className="text-caption text-fg-disabled font-korean">{item.label}</div>
|
||||
<div className="text-sm font-mono text-fg">{item.value}</div>
|
||||
<div className="text-body-2 font-mono text-fg">{item.value}</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
||||
@ -485,7 +485,7 @@ export function SatelliteRequest() {
|
||||
<div className="flex items-center gap-3 mb-2 h-9">
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<div
|
||||
className="w-7 h-7 rounded-md flex items-center justify-center text-sm"
|
||||
className="w-7 h-7 rounded-md flex items-center justify-center text-body-2"
|
||||
style={{
|
||||
background: 'rgba(6,182,212,0.15)',
|
||||
border: '1px solid rgba(6,182,212,0.3)',
|
||||
@ -624,7 +624,7 @@ export function SatelliteRequest() {
|
||||
>
|
||||
<div className="text-label-2 font-mono text-fg-sub">{r.id}</div>
|
||||
<div>
|
||||
<div className="text-xs font-semibold text-fg font-korean">{r.zone}</div>
|
||||
<div className="text-caption font-semibold text-fg font-korean">{r.zone}</div>
|
||||
<div className="text-caption text-fg-disabled font-mono mt-0.5">
|
||||
{r.zoneCoord} · {r.zoneArea}
|
||||
</div>
|
||||
@ -776,7 +776,9 @@ export function SatelliteRequest() {
|
||||
<div className="grid grid-cols-2 gap-3.5">
|
||||
{/* 가용 위성 현황 */}
|
||||
<div className="bg-bg-elevated border border-stroke rounded-md p-4">
|
||||
<div className="text-xs font-bold text-fg font-korean mb-3">🛰 가용 위성 현황</div>
|
||||
<div className="text-caption font-bold text-fg font-korean mb-3">
|
||||
🛰 가용 위성 현황
|
||||
</div>
|
||||
<div className="flex flex-col gap-2">
|
||||
{satellites.map((sat, i) => (
|
||||
<div
|
||||
@ -807,7 +809,7 @@ export function SatelliteRequest() {
|
||||
|
||||
{/* 오늘 촬영 가능 시간 */}
|
||||
<div className="bg-bg-elevated border border-stroke rounded-md p-4">
|
||||
<div className="text-xs font-bold text-fg font-korean mb-3">
|
||||
<div className="text-caption font-bold text-fg font-korean mb-3">
|
||||
⏰ 오늘 촬영 가능 시간 (KST)
|
||||
</div>
|
||||
<div className="flex flex-col gap-1.5">
|
||||
@ -1133,7 +1135,7 @@ export function SatelliteRequest() {
|
||||
)}
|
||||
<button
|
||||
onClick={() => setMapSelectedItem(null)}
|
||||
className="text-fg-disabled bg-transparent border-none cursor-pointer text-sm"
|
||||
className="text-fg-disabled bg-transparent border-none cursor-pointer text-body-2"
|
||||
>
|
||||
✕
|
||||
</button>
|
||||
@ -1213,7 +1215,7 @@ export function SatelliteRequest() {
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-bold text-fg font-korean">BlackSky</div>
|
||||
<div className="text-body-2 font-bold text-fg font-korean">BlackSky</div>
|
||||
<div className="text-caption text-fg-disabled font-korean mt-px">
|
||||
Maxar Electro-Optical API
|
||||
</div>
|
||||
@ -1274,7 +1276,7 @@ export function SatelliteRequest() {
|
||||
</span>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-sm font-bold text-fg font-korean">
|
||||
<div className="text-body-2 font-bold text-fg font-korean">
|
||||
UP42 — EO + SAR
|
||||
</div>
|
||||
<div className="text-caption text-fg-disabled font-korean mt-px">
|
||||
@ -1703,13 +1705,13 @@ export function SatelliteRequest() {
|
||||
</div>
|
||||
<button
|
||||
onClick={() => setModalPhase('provider')}
|
||||
className="px-5 py-2.5 rounded-lg border border-stroke text-xs font-semibold cursor-pointer font-korean text-fg-sub bg-bg-elevated"
|
||||
className="px-5 py-2.5 rounded-lg border border-stroke text-caption font-semibold cursor-pointer font-korean text-fg-sub bg-bg-elevated"
|
||||
>
|
||||
← 뒤로
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setModalPhase('none')}
|
||||
className="px-7 py-2.5 rounded-lg text-xs font-bold cursor-pointer font-korean text-color-accent"
|
||||
className="px-7 py-2.5 rounded-lg text-caption font-bold cursor-pointer font-korean text-color-accent"
|
||||
style={{
|
||||
background: 'rgba(6,182,212,0.08)',
|
||||
}}
|
||||
@ -2098,7 +2100,7 @@ export function SatelliteRequest() {
|
||||
{urgency}
|
||||
</span>
|
||||
{up42SelPass === pass.id && (
|
||||
<span className="text-xs text-fg">✓</span>
|
||||
<span className="text-caption text-fg">✓</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
|
||||
@ -391,7 +391,7 @@ function Vessel3DModel({ viewMode, status }: { viewMode: string; status: string
|
||||
{isProcessing && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="text-color-accent/40 text-xs font-mono animate-pulse">
|
||||
<div className="text-color-accent/40 text-caption font-mono animate-pulse">
|
||||
재구성 처리중...
|
||||
</div>
|
||||
<div className="w-24 h-0.5 bg-bg-card rounded-full mt-2 mx-auto overflow-hidden">
|
||||
@ -712,7 +712,7 @@ function Pollution3DModel({ viewMode, status }: { viewMode: string; status: stri
|
||||
{isProcessing && (
|
||||
<div className="absolute inset-0 flex items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="text-color-danger/40 text-xs font-mono animate-pulse">
|
||||
<div className="text-color-danger/40 text-caption font-mono animate-pulse">
|
||||
재구성 처리중...
|
||||
</div>
|
||||
<div className="w-24 h-0.5 bg-bg-card rounded-full mt-2 mx-auto overflow-hidden">
|
||||
@ -818,7 +818,7 @@ export function SensorAnalysis() {
|
||||
className="absolute inset-0 flex items-center justify-center"
|
||||
style={{ background: 'var(--bg-base)' }}
|
||||
>
|
||||
<div className="text-fg-disabled/10 text-xs font-mono">
|
||||
<div className="text-fg-disabled/10 text-caption font-mono">
|
||||
{src.label.split(' ')[0]}
|
||||
</div>
|
||||
</div>
|
||||
@ -916,7 +916,7 @@ export function SensorAnalysis() {
|
||||
{ value: '0.023m', label: 'RMS오차' },
|
||||
].map((s, i) => (
|
||||
<div key={i} className="text-center">
|
||||
<div className="font-mono font-bold text-sm text-color-accent">{s.value}</div>
|
||||
<div className="font-mono font-bold text-body-2 text-color-accent">{s.value}</div>
|
||||
<div className="text-caption text-fg-disabled mt-0.5 font-korean">{s.label}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -47,7 +47,7 @@ export function WingAI() {
|
||||
<div className="flex items-center gap-3 mb-4 h-9">
|
||||
<div className="flex items-center gap-2 shrink-0">
|
||||
<div
|
||||
className="w-7 h-7 rounded-md flex items-center justify-center text-sm"
|
||||
className="w-7 h-7 rounded-md flex items-center justify-center text-body-2"
|
||||
style={{
|
||||
background: 'rgba(6,182,212,0.15)',
|
||||
border: '1px solid rgba(6,182,212,0.3)',
|
||||
@ -1757,8 +1757,8 @@ function AoiPanel() {
|
||||
}
|
||||
}
|
||||
>
|
||||
<span className="text-sm shrink-0">{active ? '◉' : '○'}</span>
|
||||
<span className="text-sm shrink-0">{src.icon}</span>
|
||||
<span className="text-body-2 shrink-0">{active ? '◉' : '○'}</span>
|
||||
<span className="text-body-2 shrink-0">{src.icon}</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-caption font-bold font-korean">
|
||||
{src.label}
|
||||
|
||||
@ -83,7 +83,9 @@ function AssetManagement() {
|
||||
if (loading) {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-full">
|
||||
<div className="text-fg-disabled text-sm font-korean">방제자산 데이터를 불러오는 중...</div>
|
||||
<div className="text-fg-disabled text-body-2 font-korean">
|
||||
방제자산 데이터를 불러오는 중...
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@ -415,7 +417,7 @@ function AssetManagement() {
|
||||
<aside className="w-[340px] min-w-[340px] bg-bg-surface border-l border-stroke flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="p-4 border-b border-stroke">
|
||||
<div className="text-sm font-bold mb-1 font-korean">{selectedOrg.name}</div>
|
||||
<div className="text-body-2 font-bold mb-1 font-korean">{selectedOrg.name}</div>
|
||||
<div className="text-label-2 text-fg-sub font-semibold font-korean mb-1">
|
||||
{selectedOrg.type} · {regionShort(selectedOrg.jurisdiction)} · {selectedOrg.area}
|
||||
</div>
|
||||
@ -505,7 +507,7 @@ function AssetManagement() {
|
||||
);
|
||||
})
|
||||
) : (
|
||||
<div className="text-center text-fg-disabled text-xs py-8 font-korean">
|
||||
<div className="text-center text-fg-disabled text-caption py-8 font-korean">
|
||||
상세 장비 데이터가 없습니다.
|
||||
</div>
|
||||
)}
|
||||
@ -601,10 +603,10 @@ function AssetManagement() {
|
||||
|
||||
{/* Bottom Actions */}
|
||||
{/* <div className="p-3.5 border-t border-stroke flex gap-2">
|
||||
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean text-white bg-color-accent border-none cursor-pointer hover:opacity-90 transition-opacity">
|
||||
<button className="flex-1 py-2.5 rounded-sm text-caption font-semibold font-korean text-white bg-color-accent border-none cursor-pointer hover:opacity-90 transition-opacity">
|
||||
📥 다운로드
|
||||
</button>
|
||||
<button className="flex-1 py-2.5 rounded-sm text-xs font-semibold font-korean bg-bg-card border border-stroke text-fg-sub cursor-pointer hover:bg-bg-surface-hover transition-colors">
|
||||
<button className="flex-1 py-2.5 rounded-sm text-caption font-semibold font-korean bg-bg-card border border-stroke text-fg-sub cursor-pointer hover:bg-bg-surface-hover transition-colors">
|
||||
✏ 수정
|
||||
</button>
|
||||
</div> */}
|
||||
|
||||
@ -27,7 +27,7 @@ function AssetUpload() {
|
||||
{/* Drop Zone */}
|
||||
<div className="border-2 border-dashed border-stroke-light rounded-md py-10 px-5 text-center mb-5 cursor-pointer hover:border-[rgba(6,182,212,0.4)] transition-colors">
|
||||
<div className="text-4xl mb-2.5 opacity-50">📁</div>
|
||||
<div className="text-sm font-semibold mb-1.5 font-korean">
|
||||
<div className="text-body-2 font-semibold mb-1.5 font-korean">
|
||||
파일을 드래그하거나 클릭하여 업로드
|
||||
</div>
|
||||
<div className="text-label-2 text-fg-disabled mb-4 font-korean">
|
||||
@ -43,7 +43,7 @@ function AssetUpload() {
|
||||
|
||||
{/* Asset Classification */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-xs font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
<label className="block text-caption font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
자산 분류
|
||||
</label>
|
||||
<select className="prd-i w-full">
|
||||
@ -58,7 +58,7 @@ function AssetUpload() {
|
||||
|
||||
{/* Jurisdiction */}
|
||||
<div className="mb-4">
|
||||
<label className="block text-xs font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
<label className="block text-caption font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
업로드 대상 관할
|
||||
</label>
|
||||
<select className="prd-i w-full">
|
||||
@ -74,10 +74,10 @@ function AssetUpload() {
|
||||
|
||||
{/* Upload Mode */}
|
||||
<div className="mb-5">
|
||||
<label className="block text-xs font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
<label className="block text-caption font-semibold mb-1.5 text-fg-sub font-korean">
|
||||
업로드 방식
|
||||
</label>
|
||||
<div className="flex gap-4 text-xs text-fg-sub font-korean">
|
||||
<div className="flex gap-4 text-caption text-fg-sub font-korean">
|
||||
<label className="flex items-center gap-1.5 cursor-pointer">
|
||||
<input
|
||||
type="radio"
|
||||
@ -102,7 +102,7 @@ function AssetUpload() {
|
||||
{/* Upload Button */}
|
||||
<button
|
||||
onClick={handleUpload}
|
||||
className={`w-full py-3.5 rounded-sm text-sm font-bold font-korean border-none cursor-pointer transition-all ${
|
||||
className={`w-full py-3.5 rounded-sm text-body-2 font-bold font-korean border-none cursor-pointer transition-all ${
|
||||
uploaded
|
||||
? 'bg-[rgba(34,197,94,0.2)] text-color-success border border-status-green'
|
||||
: 'text-white'
|
||||
@ -163,7 +163,7 @@ function AssetUpload() {
|
||||
{p.icon}
|
||||
</div>
|
||||
<div>
|
||||
<div className={`text-xs font-bold font-korean ${p.color}`}>{p.role}</div>
|
||||
<div className={`text-caption font-bold font-korean ${p.color}`}>{p.role}</div>
|
||||
<div className="text-caption text-fg-disabled font-korean">{p.desc}</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -179,7 +179,7 @@ function AssetUpload() {
|
||||
className="flex justify-between items-center p-3.5 px-4 bg-bg-card border border-stroke rounded-sm"
|
||||
>
|
||||
<div>
|
||||
<div className="text-xs font-semibold font-korean">{h.fileNm}</div>
|
||||
<div className="text-caption font-semibold font-korean">{h.fileNm}</div>
|
||||
<div className="text-caption text-fg-disabled mt-0.5 font-korean">
|
||||
{new Date(h.regDtm).toLocaleString('ko-KR')} · {h.uploaderNm} · {h.uploadCnt}건
|
||||
</div>
|
||||
|
||||
@ -27,10 +27,8 @@ export function AssetsView() {
|
||||
<button
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id)}
|
||||
className={`px-5 py-3.5 text-xs font-semibold transition-all font-korean border-b-2 ${
|
||||
activeTab === tab.id
|
||||
? 'text-color-accent border-color-accent'
|
||||
: 'text-fg-disabled border-transparent hover:text-fg-sub'
|
||||
className={`px-4 py-2.5 text-title-4 font-medium transition-all duration-200 font-korean tracking-navigation ${
|
||||
activeTab === tab.id ? 'text-color-accent' : 'text-fg-sub hover:text-fg'
|
||||
}`}
|
||||
>
|
||||
{tab.label}
|
||||
|
||||
@ -167,7 +167,7 @@ function ShipInsurance() {
|
||||
{total > 0 ? `${total.toLocaleString()}건` : '데이터 없음'}
|
||||
</div>
|
||||
</div>
|
||||
<div className="text-xs text-fg-disabled">
|
||||
<div className="text-caption text-fg-disabled">
|
||||
해양수산부 해운항만물류정보 공공데이터 기반
|
||||
</div>
|
||||
</div>
|
||||
@ -200,7 +200,7 @@ function ShipInsurance() {
|
||||
onChange={(e) => setSearch(e.target.value)}
|
||||
onKeyDown={(e) => e.key === 'Enter' && handleSearch()}
|
||||
placeholder="선박명, 호출부호, IMO, 선주명"
|
||||
className="w-full px-3.5 py-2 bg-bg-base border border-stroke rounded-sm text-xs outline-none box-border"
|
||||
className="w-full px-3.5 py-2 bg-bg-base border border-stroke rounded-sm text-caption outline-none box-border"
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
@ -243,20 +243,20 @@ function ShipInsurance() {
|
||||
</div>
|
||||
<button
|
||||
onClick={handleSearch}
|
||||
className="px-4 py-2 bg-bg-base text-fg border border-stroke rounded-sm text-xs cursor-pointer"
|
||||
className="px-4 py-2 bg-bg-base text-fg border border-stroke rounded-sm text-caption cursor-pointer"
|
||||
>
|
||||
조회
|
||||
</button>
|
||||
<button
|
||||
onClick={handleReset}
|
||||
className="px-4 py-2 bg-bg-base text-fg border border-stroke rounded-sm text-xs cursor-pointer"
|
||||
className="px-4 py-2 bg-bg-base text-fg border border-stroke rounded-sm text-caption cursor-pointer"
|
||||
>
|
||||
초기화
|
||||
</button>
|
||||
<button
|
||||
onClick={handleDownload}
|
||||
disabled={total === 0}
|
||||
className="px-4 py-2 text-xs cursor-pointer rounded-sm disabled:opacity-30 disabled:cursor-default bg-bg-base border border-stroke"
|
||||
className="px-4 py-2 text-caption cursor-pointer rounded-sm disabled:opacity-30 disabled:cursor-default bg-bg-base border border-stroke"
|
||||
>
|
||||
엑셀 다운로드
|
||||
</button>
|
||||
@ -281,8 +281,8 @@ function ShipInsurance() {
|
||||
{/* 에러 */}
|
||||
{error && !isLoading && (
|
||||
<div className="flex flex-col items-center justify-center py-16 px-5 bg-bg-card border border-stroke rounded-md">
|
||||
<div className="text-sm font-bold text-color-danger mb-2">조회 실패</div>
|
||||
<div className="text-xs text-fg-disabled">{error}</div>
|
||||
<div className="text-body-2 font-bold text-color-danger mb-2">조회 실패</div>
|
||||
<div className="text-caption text-fg-disabled">{error}</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
@ -291,7 +291,7 @@ function ShipInsurance() {
|
||||
<>
|
||||
<div className="border border-stroke rounded-md overflow-hidden mb-3">
|
||||
<div className="flex items-center justify-between px-4 py-3 border-b border-stroke">
|
||||
<div className="text-xs font-bold">
|
||||
<div className="text-caption font-bold">
|
||||
조회 결과 <span className="text-color-accent">{total.toLocaleString()}</span>건
|
||||
{totalPages > 1 && (
|
||||
<span className="text-fg-disabled font-normal ml-2">
|
||||
|
||||
@ -799,15 +799,15 @@ export function HNSLeftPanel({
|
||||
<div className="flex flex-col gap-2">
|
||||
<div className="flex justify-between items-center p-2 bg-bg-base rounded">
|
||||
<span className="text-label-2 text-fg-disabled">전체 분석</span>
|
||||
<span className="text-sm font-bold text-color-accent font-mono">8건</span>
|
||||
<span className="text-body-2 font-bold text-color-accent font-mono">8건</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center p-2 bg-bg-base rounded">
|
||||
<span className="text-label-2 text-fg-disabled">고위험 (AEGL-3)</span>
|
||||
<span className="text-sm font-bold text-color-caution font-mono">3건</span>
|
||||
<span className="text-body-2 font-bold text-color-caution font-mono">3건</span>
|
||||
</div>
|
||||
<div className="flex justify-between items-center p-2 bg-bg-base rounded">
|
||||
<span className="text-label-2 text-fg-disabled">중위험 (AEGL-2)</span>
|
||||
<span className="text-sm font-bold text-color-accent font-mono">5건</span>
|
||||
<span className="text-body-2 font-bold text-color-accent font-mono">5건</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -792,7 +792,7 @@ ${styles}
|
||||
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card">
|
||||
<div className="flex items-center justify-between mb-[10px]">
|
||||
<div>
|
||||
<span className="text-sm font-mono font-extrabold text-color-accent">
|
||||
<span className="text-body-2 font-mono font-extrabold text-color-accent">
|
||||
NH₃
|
||||
</span>{' '}
|
||||
<span className="text-label-1 font-bold">암모니아</span>
|
||||
@ -891,7 +891,7 @@ ${styles}
|
||||
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card">
|
||||
<div className="flex items-center justify-between mb-[10px]">
|
||||
<div>
|
||||
<span className="text-sm font-mono font-extrabold text-color-accent">
|
||||
<span className="text-body-2 font-mono font-extrabold text-color-accent">
|
||||
CH₃OH
|
||||
</span>{' '}
|
||||
<span className="text-label-1 font-bold">메탄올</span>
|
||||
@ -990,7 +990,9 @@ ${styles}
|
||||
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card">
|
||||
<div className="flex items-center justify-between mb-[10px]">
|
||||
<div>
|
||||
<span className="text-sm font-mono font-extrabold text-color-accent">H₂</span>{' '}
|
||||
<span className="text-body-2 font-mono font-extrabold text-color-accent">
|
||||
H₂
|
||||
</span>{' '}
|
||||
<span className="text-label-1 font-bold">수소</span>
|
||||
</div>
|
||||
<div className="flex gap-1">
|
||||
@ -1048,7 +1050,7 @@ ${styles}
|
||||
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card">
|
||||
<div className="flex items-center justify-between mb-[10px]">
|
||||
<div>
|
||||
<span className="text-sm font-mono font-extrabold text-color-accent">
|
||||
<span className="text-body-2 font-mono font-extrabold text-color-accent">
|
||||
CH₄
|
||||
</span>{' '}
|
||||
<span className="text-label-1 font-bold">LNG (메탄)</span>
|
||||
@ -1106,7 +1108,7 @@ ${styles}
|
||||
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card">
|
||||
<div className="flex items-center justify-between mb-[10px]">
|
||||
<div>
|
||||
<span className="text-sm font-mono font-extrabold text-color-accent">
|
||||
<span className="text-body-2 font-mono font-extrabold text-color-accent">
|
||||
C₆H₅OH
|
||||
</span>{' '}
|
||||
<span className="text-label-1 font-bold">페놀</span>
|
||||
@ -1164,7 +1166,7 @@ ${styles}
|
||||
<div className="rounded-[10px] p-[14px] border border-stroke bg-bg-card">
|
||||
<div className="flex items-center justify-between mb-[10px]">
|
||||
<div>
|
||||
<span className="text-sm font-mono font-extrabold text-color-accent">
|
||||
<span className="text-body-2 font-mono font-extrabold text-color-accent">
|
||||
C₇H₈
|
||||
</span>{' '}
|
||||
<span className="text-label-1 font-bold">톨루엔</span>
|
||||
|
||||
@ -23,10 +23,10 @@ export function IncidentTable() {
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h1 className="text-xl font-bold text-fg">유출유 확산 예측 목록</h1>
|
||||
<p className="text-sm text-fg-disabled mt-1">총 {filteredIncidents.length}건</p>
|
||||
<p className="text-body-2 text-fg-disabled mt-1">총 {filteredIncidents.length}건</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<button className="px-4 py-2 text-sm font-semibold border border-stroke rounded-md bg-bg-card text-fg-sub hover:bg-bg-surface-hover hover:text-fg transition-all">
|
||||
<button className="px-4 py-2 text-body-2 font-semibold border border-stroke rounded-md bg-bg-card text-fg-sub hover:bg-bg-surface-hover hover:text-fg transition-all">
|
||||
시간별 검색
|
||||
</button>
|
||||
<div className="relative">
|
||||
@ -35,10 +35,10 @@ export function IncidentTable() {
|
||||
placeholder="검색..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-64 px-4 py-2 text-sm bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none"
|
||||
className="w-64 px-4 py-2 text-body-2 bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<button className="px-4 py-2 text-sm font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_16px_rgba(6,182,212,0.3)] transition-all">
|
||||
<button className="px-4 py-2 text-body-2 font-semibold rounded-md bg-color-accent text-bg-0 hover:shadow-[0_0_16px_rgba(6,182,212,0.3)] transition-all">
|
||||
+ 새 분석
|
||||
</button>
|
||||
</div>
|
||||
@ -49,31 +49,31 @@ export function IncidentTable() {
|
||||
<table className="w-full">
|
||||
<thead className="sticky top-0 bg-bg-surface border-b border-stroke z-10">
|
||||
<tr>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
번호
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
사고명
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
사고시각
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
선박유형
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
유종
|
||||
</th>
|
||||
<th className="px-4 py-3 text-right text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-right text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
유출량
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
사고유형
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
상태
|
||||
</th>
|
||||
<th className="px-4 py-3 text-left text-xs font-bold text-fg-disabled uppercase tracking-wider">
|
||||
<th className="px-4 py-3 text-left text-caption font-bold text-fg-disabled uppercase tracking-wider">
|
||||
분석자
|
||||
</th>
|
||||
</tr>
|
||||
@ -84,28 +84,28 @@ export function IncidentTable() {
|
||||
key={incident.acdntSn}
|
||||
className="hover:bg-bg-elevated transition-colors cursor-pointer group"
|
||||
>
|
||||
<td className="px-4 py-3 text-sm text-fg-sub font-mono">{incident.acdntSn}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub font-mono">{incident.acdntSn}</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-color-danger animate-pulse" />
|
||||
<span className="text-sm font-semibold text-fg group-hover:text-color-accent transition-colors">
|
||||
<span className="text-body-2 font-semibold text-fg group-hover:text-color-accent transition-colors">
|
||||
{incident.acdntNm}
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-fg-sub font-mono">{incident.occrnDtm}</td>
|
||||
<td className="px-4 py-3 text-sm text-fg-sub">{incident.vesselTp ?? '—'}</td>
|
||||
<td className="px-4 py-3 text-sm text-fg-sub">{incident.oilTpCd ?? '—'}</td>
|
||||
<td className="px-4 py-3 text-sm text-fg font-mono text-right font-semibold">
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub font-mono">{incident.occrnDtm}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{incident.vesselTp ?? '—'}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{incident.oilTpCd ?? '—'}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg font-mono text-right font-semibold">
|
||||
{incident.spilQty != null ? incident.spilQty.toFixed(2) : '—'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-fg-sub">{incident.acdntTpCd}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{incident.acdntTpCd}</td>
|
||||
<td className="px-4 py-3">
|
||||
<span className="px-2 py-1 text-xs font-semibold rounded-md bg-[rgba(168,85,247,0.15)] text-purple-400">
|
||||
<span className="px-2 py-1 text-caption font-semibold rounded-md bg-[rgba(168,85,247,0.15)] text-purple-400">
|
||||
{incident.phaseCd}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-sm text-fg-sub">{incident.analystNm ?? '—'}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{incident.analystNm ?? '—'}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
|
||||
@ -208,7 +208,7 @@ export function IncidentsLeftPanel({
|
||||
{/* Search */}
|
||||
<div className="px-4 py-3 border-b border-stroke shrink-0">
|
||||
<div className="relative">
|
||||
<span className="absolute left-[10px] top-1/2 -translate-y-1/2 text-xs">🔍</span>
|
||||
<span className="absolute left-[10px] top-1/2 -translate-y-1/2 text-caption">🔍</span>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="사고명, 선박명 검색..."
|
||||
@ -217,7 +217,7 @@ export function IncidentsLeftPanel({
|
||||
setSearchTerm(e.target.value);
|
||||
resetPage();
|
||||
}}
|
||||
className="w-full py-2 pr-3 pl-8 bg-bg-base border border-stroke text-xs outline-none"
|
||||
className="w-full py-2 pr-3 pl-8 bg-bg-base border border-stroke text-caption outline-none"
|
||||
style={{ borderRadius: 'var(--radius-sm)' }}
|
||||
/>
|
||||
</div>
|
||||
@ -435,7 +435,7 @@ export function IncidentsLeftPanel({
|
||||
>
|
||||
{/* Row 1: name + status */}
|
||||
<div className="flex items-center justify-between mb-[5px]">
|
||||
<div className="flex items-center gap-1.5 text-xs">
|
||||
<div className="flex items-center gap-1.5 text-caption">
|
||||
<span
|
||||
className="shrink-0"
|
||||
style={{
|
||||
@ -692,13 +692,13 @@ const WeatherPopup = forwardRef<
|
||||
}}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="text-sm">🌤</span>
|
||||
<span className="text-body-2">🌤</span>
|
||||
<div>
|
||||
<div className="text-label-2 font-bold">{data?.locNm || '기상정보 없음'}</div>
|
||||
<div className="text-fg-disabled font-mono text-caption">{data?.obsDtm || '-'}</div>
|
||||
</div>
|
||||
</div>
|
||||
<span onClick={onClose} className="cursor-pointer text-fg-disabled text-sm p-0.5">
|
||||
<span onClick={onClose} className="cursor-pointer text-fg-disabled text-body-2 p-0.5">
|
||||
✕
|
||||
</span>
|
||||
</div>
|
||||
@ -733,7 +733,7 @@ const WeatherPopup = forwardRef<
|
||||
border: '1px solid rgba(59,130,246,0.1)',
|
||||
}}
|
||||
>
|
||||
<span className="text-xs">⬆</span>
|
||||
<span className="text-caption">⬆</span>
|
||||
<div>
|
||||
<div className="text-fg-disabled text-caption">고조 (만조)</div>
|
||||
<div className="font-bold font-mono text-caption text-color-info">
|
||||
@ -745,7 +745,7 @@ const WeatherPopup = forwardRef<
|
||||
className="flex-1 flex items-center gap-1.5 px-2 py-1.5 rounded-md"
|
||||
style={{ background: 'rgba(6,182,212,0.06)', border: '1px solid rgba(6,182,212,0.1)' }}
|
||||
>
|
||||
<span className="text-xs">⬇</span>
|
||||
<span className="text-caption">⬇</span>
|
||||
<div>
|
||||
<div className="text-fg-disabled text-caption">저조 (간조)</div>
|
||||
<div className="text-color-accent font-bold font-mono text-caption">
|
||||
@ -763,7 +763,7 @@ const WeatherPopup = forwardRef<
|
||||
{forecast.map((f, i) => (
|
||||
<div key={i} className="text-center">
|
||||
<div>{f.hour}</div>
|
||||
<div className="text-xs my-0.5">{f.icon}</div>
|
||||
<div className="text-caption my-0.5">{f.icon}</div>
|
||||
<div className="font-semibold">{f.temp}</div>
|
||||
</div>
|
||||
))}
|
||||
|
||||
@ -314,7 +314,7 @@ export function IncidentsRightPanel({
|
||||
<div className="flex flex-col bg-bg-surface border-l border-stroke overflow-hidden h-full w-[280px] min-w-[280px]">
|
||||
{/* Header */}
|
||||
<div className="px-[14px] py-2.5 border-b border-stroke shrink-0">
|
||||
<div className="text-xs font-bold mb-0.5">🔬 통합분석 조회</div>
|
||||
<div className="text-caption font-bold mb-0.5">🔬 통합분석 조회</div>
|
||||
<div className="text-caption text-fg-disabled">
|
||||
선택: <span className="text-fg-disabled">{incident.name}</span>
|
||||
</div>
|
||||
@ -333,8 +333,8 @@ export function IncidentsRightPanel({
|
||||
<div className="bg-bg-elevated border border-stroke rounded-md p-2.5">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* <span className="text-sm">{sec.icon}</span> */}
|
||||
<span className="text-xs">{sec.title}</span>
|
||||
{/* <span className="text-body-2">{sec.icon}</span> */}
|
||||
<span className="text-caption">{sec.title}</span>
|
||||
</div>
|
||||
<button
|
||||
className="text-caption font-semibold cursor-pointer"
|
||||
@ -400,8 +400,8 @@ export function IncidentsRightPanel({
|
||||
<div key={sec.key} className="bg-bg-elevated border border-stroke rounded-md p-2.5">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<div className="flex items-center gap-1.5">
|
||||
{/* <span className="text-sm">{sec.icon}</span> */}
|
||||
<span className="text-xs">{sec.title}</span>
|
||||
{/* <span className="text-body-2">{sec.icon}</span> */}
|
||||
<span className="text-caption">{sec.title}</span>
|
||||
</div>
|
||||
<button
|
||||
className="text-caption font-semibold cursor-pointer"
|
||||
@ -425,8 +425,8 @@ export function IncidentsRightPanel({
|
||||
{/* 민감자원 */}
|
||||
<div className="bg-bg-elevated border border-stroke rounded-md p-2.5">
|
||||
<div className="flex items-center gap-1.5 mb-2">
|
||||
{/* <span className="text-sm">🐟</span> */}
|
||||
<span className="text-xs">민감자원</span>
|
||||
{/* <span className="text-body-2">🐟</span> */}
|
||||
<span className="text-caption">민감자원</span>
|
||||
</div>
|
||||
<div className="flex flex-col gap-[3px]">
|
||||
{sensCategories.length === 0 ? (
|
||||
@ -473,7 +473,7 @@ export function IncidentsRightPanel({
|
||||
{/* 근처 방제자원 */}
|
||||
<div className="bg-bg-elevated border border-stroke rounded-md p-2.5">
|
||||
<div className="flex items-center gap-1.5 mb-2">
|
||||
<span className="text-xs font-bold text-color-accent">근처 방제자원</span>
|
||||
<span className="text-caption font-bold text-color-accent">근처 방제자원</span>
|
||||
{nearbyOrgs.length > 0 && (
|
||||
<span className="ml-auto text-caption font-mono text-color-accent">
|
||||
{nearbyOrgs.length}개
|
||||
|
||||
@ -744,7 +744,7 @@ export function IncidentsView() {
|
||||
closeOnClick={false}
|
||||
className="incident-popup"
|
||||
>
|
||||
<div className="text-center min-w-[180px] text-xs">
|
||||
<div className="text-center min-w-[180px] text-caption">
|
||||
<div className="font-semibold" style={{ marginBottom: 6, color: '#1a1d21' }}>
|
||||
{incidentPopup.incident.name}
|
||||
</div>
|
||||
|
||||
@ -106,7 +106,7 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
<button
|
||||
key={page}
|
||||
onClick={() => setCurrentPage(page as number)}
|
||||
className={`px-3 py-1 text-title-3 font-medium rounded transition-colors ${
|
||||
className={`px-3 py-1 text-body-2 font-medium rounded transition-colors ${
|
||||
currentPage === page ? 'bg-color-accent text-bg-0' : 'text-fg-sub hover:bg-bg-elevated'
|
||||
}`}
|
||||
>
|
||||
@ -121,8 +121,8 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
{/* 헤더 */}
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-stroke">
|
||||
<div>
|
||||
<h1 className="text-heading-3 font-bold text-fg">유출유 확산 예측 목록</h1>
|
||||
<p className="text-title-3 text-fg-disabled mt-1">총 {analyses.length}건</p>
|
||||
<h1 className="text-heading-3 text-fg">유출유 확산 예측 목록</h1>
|
||||
<p className="text-body-2 text-fg-disabled mt-1">총 {analyses.length}건</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="relative">
|
||||
@ -131,12 +131,12 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
placeholder="검색..."
|
||||
value={searchTerm}
|
||||
onChange={(e) => setSearchTerm(e.target.value)}
|
||||
className="w-64 px-4 py-2 text-title-3 bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none"
|
||||
className="w-64 px-4 py-2 text-body-2 bg-bg-elevated border border-stroke rounded-md text-fg placeholder-fg-disabled focus:border-color-accent focus:outline-none"
|
||||
/>
|
||||
</div>
|
||||
<button
|
||||
onClick={() => onTabChange('analysis')}
|
||||
className="px-4 py-2 text-title-3 font-semibold rounded-sm cursor-pointer text-color-accent"
|
||||
className="px-4 py-2 text-body-2 font-semibold rounded-sm cursor-pointer text-color-accent"
|
||||
style={{
|
||||
border: '1px solid rgba(6,182,212,.3)',
|
||||
background: 'rgba(6,182,212,.08)',
|
||||
@ -150,7 +150,7 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
{/* 테이블 */}
|
||||
<div className="flex-1 overflow-auto">
|
||||
{loading ? (
|
||||
<div className="text-center py-20 text-fg-disabled text-title-3">로딩 중...</div>
|
||||
<div className="text-center py-20 text-fg-disabled text-body-2">로딩 중...</div>
|
||||
) : (
|
||||
<table className="w-full">
|
||||
<thead className="sticky top-0 bg-bg-surface border-b border-stroke z-10">
|
||||
@ -202,14 +202,14 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
key={analysis.predRunSn ?? analysis.acdntSn}
|
||||
className="hover:bg-bg-elevated transition-colors cursor-pointer group"
|
||||
>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub font-mono">
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub font-mono">
|
||||
{analysis.acdntSn}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="w-2 h-2 rounded-full bg-color-danger animate-pulse" />
|
||||
<span
|
||||
className="text-title-3 font-medium text-color-accent hover:underline transition-all cursor-pointer"
|
||||
className="text-body-2 font-medium text-color-accent hover:underline transition-all cursor-pointer"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
if (onSelectAnalysis) {
|
||||
@ -221,7 +221,7 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub font-mono">
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub font-mono">
|
||||
{analysis.occurredAt
|
||||
? new Date(analysis.occurredAt).toLocaleString('ko-KR', {
|
||||
month: '2-digit',
|
||||
@ -231,7 +231,7 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
})
|
||||
: '—'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub font-mono">
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub font-mono">
|
||||
{analysis.runDtm
|
||||
? new Date(analysis.runDtm).toLocaleString('ko-KR', {
|
||||
month: '2-digit',
|
||||
@ -241,11 +241,11 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
})
|
||||
: '—'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub font-mono">
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub font-mono">
|
||||
{analysis.duration}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub">{analysis.oilType}</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg font-mono text-right font-medium">
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{analysis.oilType}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg font-mono text-right font-medium">
|
||||
{analysis.volume != null ? analysis.volume.toFixed(2) : '—'}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-center">{getStatusBadge(analysis.kospsStatus)}</td>
|
||||
@ -258,8 +258,8 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
<td className="px-4 py-3 text-center">
|
||||
{getStatusBadge(analysis.backtrackStatus)}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub">{analysis.analyst}</td>
|
||||
<td className="px-4 py-3 text-title-3 text-fg-sub">{analysis.officeName}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{analysis.analyst}</td>
|
||||
<td className="px-4 py-3 text-body-2 text-fg-sub">{analysis.officeName}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
@ -267,7 +267,7 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
)}
|
||||
|
||||
{!loading && analyses.length === 0 && (
|
||||
<div className="text-center py-20 text-fg-disabled text-title-3">
|
||||
<div className="text-center py-20 text-fg-disabled text-body-2">
|
||||
분석 데이터가 없습니다.
|
||||
</div>
|
||||
)}
|
||||
@ -278,14 +278,14 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
<button
|
||||
onClick={() => setCurrentPage(1)}
|
||||
disabled={currentPage === 1}
|
||||
className="px-3 py-1 text-title-3 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 text-body-2 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
≪
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage((prev) => Math.max(1, prev - 1))}
|
||||
disabled={currentPage === 1}
|
||||
className="px-3 py-1 text-title-3 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 text-body-2 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
@ -295,14 +295,14 @@ export function AnalysisListTable({ onTabChange, onSelectAnalysis }: AnalysisLis
|
||||
<button
|
||||
onClick={() => setCurrentPage((prev) => Math.min(totalPages, prev + 1))}
|
||||
disabled={currentPage === totalPages}
|
||||
className="px-3 py-1 text-title-3 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 text-body-2 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setCurrentPage(totalPages)}
|
||||
disabled={currentPage === totalPages}
|
||||
className="px-3 py-1 text-title-3 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="px-3 py-1 text-body-2 font-medium text-fg-sub hover:bg-bg-elevated rounded transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
>
|
||||
≫
|
||||
</button>
|
||||
|
||||
@ -68,7 +68,7 @@ export function BacktrackModal({
|
||||
border: '1px solid var(--bd)',
|
||||
borderRadius: '6px',
|
||||
color: 'var(--t1)',
|
||||
fontSize: '11px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
fontFamily: 'var(--fM)',
|
||||
outline: 'none',
|
||||
opacity: inputDisabled ? 0.6 : 1,
|
||||
@ -108,9 +108,8 @@ export function BacktrackModal({
|
||||
borderRadius: '10px',
|
||||
background: 'linear-gradient(135deg, rgba(168,85,247,0.2), rgba(6,182,212,0.2))',
|
||||
border: '1px solid rgba(168,85,247,0.3)',
|
||||
fontSize: '18px',
|
||||
}}
|
||||
className="flex items-center justify-center"
|
||||
className="flex items-center justify-center text-title-1"
|
||||
>
|
||||
🔍
|
||||
</div>
|
||||
@ -127,7 +126,7 @@ export function BacktrackModal({
|
||||
height: '32px',
|
||||
borderRadius: '8px',
|
||||
background: 'var(--bg-card)',
|
||||
fontSize: '14px',
|
||||
fontSize: 'var(--font-size-body-2)',
|
||||
}}
|
||||
className="border border-stroke text-fg-disabled cursor-pointer flex items-center justify-center"
|
||||
>
|
||||
@ -245,7 +244,7 @@ export function BacktrackModal({
|
||||
}}
|
||||
>
|
||||
<div className="text-caption text-fg-disabled mb-1">분석 대상 선박</div>
|
||||
<div className="text-sm font-bold text-color-tertiary font-mono">
|
||||
<div className="text-body-2 font-bold text-color-tertiary font-mono">
|
||||
{conditions.totalVessels}척{' '}
|
||||
<span className="text-caption font-medium text-fg-disabled">(AIS 수신)</span>
|
||||
</div>
|
||||
@ -371,7 +370,7 @@ function VesselCard({ vessel }: { vessel: BacktrackVessel }) {
|
||||
borderRadius: '50%',
|
||||
background: `${vessel.color}20`,
|
||||
border: `2px solid ${vessel.color}`,
|
||||
fontSize: '12px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
fontWeight: 800,
|
||||
color: vessel.color,
|
||||
}}
|
||||
|
||||
@ -1222,7 +1222,7 @@ function FieldApplicationPanel() {
|
||||
style={{ background: step.bg, border: `1px solid ${step.bd}` }}
|
||||
>
|
||||
<div
|
||||
className="min-w-[36px] h-[36px] rounded-[9px] flex items-center justify-center font-extrabold text-sm flex-shrink-0"
|
||||
className="min-w-[36px] h-[36px] rounded-[9px] flex items-center justify-center font-extrabold text-body-2 flex-shrink-0"
|
||||
style={{
|
||||
background: step.numBg,
|
||||
border: `1px solid ${step.numBd}`,
|
||||
|
||||
@ -1378,7 +1378,7 @@ export function OilSpillView() {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
fontSize: 'var(--font-size-body-2)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
>
|
||||
@ -1404,7 +1404,7 @@ export function OilSpillView() {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
fontSize: '14px',
|
||||
fontSize: 'var(--font-size-body-2)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
>
|
||||
@ -1440,7 +1440,7 @@ export function OilSpillView() {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
fontSize: '12px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
transition: '0.2s',
|
||||
}}
|
||||
>
|
||||
@ -1461,7 +1461,7 @@ export function OilSpillView() {
|
||||
alignItems: 'center',
|
||||
justifyContent: 'center',
|
||||
cursor: 'pointer',
|
||||
fontSize: '11px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
fontWeight: 600,
|
||||
fontFamily: 'var(--font-mono)',
|
||||
transition: '0.2s',
|
||||
@ -1485,7 +1485,7 @@ export function OilSpillView() {
|
||||
position: 'absolute',
|
||||
left: `${pos}%`,
|
||||
transform: 'translateX(-50%)',
|
||||
fontSize: '10px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
fontFamily: 'var(--font-mono)',
|
||||
color: isActive ? 'var(--color-accent)' : 'var(--fg-disabled)',
|
||||
fontWeight: isActive ? 600 : 400,
|
||||
@ -1572,7 +1572,7 @@ export function OilSpillView() {
|
||||
top: '-18px',
|
||||
left: `${bm.pos}%`,
|
||||
transform: 'translateX(-50%)',
|
||||
fontSize: '12px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
cursor: 'pointer',
|
||||
filter: 'drop-shadow(0 0 4px rgba(245,158,11,0.5))',
|
||||
}}
|
||||
@ -1615,7 +1615,7 @@ export function OilSpillView() {
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
fontSize: '14px',
|
||||
fontSize: 'var(--font-size-body-2)',
|
||||
fontWeight: 600,
|
||||
color: 'var(--color-accent)',
|
||||
fontFamily: 'var(--font-mono)',
|
||||
@ -1656,7 +1656,7 @@ export function OilSpillView() {
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
gap: '5px',
|
||||
fontSize: '11px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
}}
|
||||
>
|
||||
<span className="text-fg-disabled">{s.label}</span>
|
||||
|
||||
@ -159,7 +159,7 @@ export function RecalcModal({
|
||||
borderRadius: '10px',
|
||||
background: 'linear-gradient(135deg, rgba(249,115,22,0.2), rgba(6,182,212,0.2))',
|
||||
border: '1px solid rgba(249,115,22,0.3)',
|
||||
fontSize: '16px',
|
||||
fontSize: 'var(--font-size-body-1)',
|
||||
}}
|
||||
className="flex items-center justify-center"
|
||||
>
|
||||
@ -178,7 +178,7 @@ export function RecalcModal({
|
||||
height: '28px',
|
||||
borderRadius: '6px',
|
||||
background: 'var(--bg-card)',
|
||||
fontSize: '12px',
|
||||
fontSize: 'var(--font-size-caption)',
|
||||
}}
|
||||
className="border border-stroke text-fg-disabled cursor-pointer flex items-center justify-center"
|
||||
>
|
||||
@ -321,7 +321,7 @@ export function RecalcModal({
|
||||
}
|
||||
toggleModel(model);
|
||||
}}
|
||||
style={{ fontSize: '11px', padding: '5px 10px' }}
|
||||
style={{ fontSize: 'var(--font-size-caption)', padding: '5px 10px' }}
|
||||
>
|
||||
<span className="prd-md" style={{ background: color }} />
|
||||
{model}
|
||||
|
||||
@ -1120,7 +1120,9 @@ function ReportGenerator({ onSave }: ReportGeneratorProps) {
|
||||
{activeSections.length === 0 && (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-fg-disabled">
|
||||
<div className="text-4xl mb-4">📋</div>
|
||||
<p className="text-sm font-korean">왼쪽에서 보고서에 포함할 섹션을 선택하세요</p>
|
||||
<p className="text-body-2 font-korean">
|
||||
왼쪽에서 보고서에 포함할 섹션을 선택하세요
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@ -171,13 +171,13 @@ export function ReportsView() {
|
||||
{filteredReports.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-20 text-fg-disabled">
|
||||
<div className="text-4xl mb-4">📄</div>
|
||||
<p className="text-sm font-korean mb-2">저장된 보고서가 없습니다</p>
|
||||
<p className="text-body-2 font-korean mb-2">저장된 보고서가 없습니다</p>
|
||||
<button
|
||||
onClick={() => {
|
||||
setView({ screen: 'templates' });
|
||||
setActiveSubTab('template');
|
||||
}}
|
||||
className="px-4 py-2 text-xs font-semibold rounded-md border border-color-accent text-color-accent hover:bg-[color-mix(in_srgb,var(--color-accent)_10%,transparent)] transition-all font-korean mt-4"
|
||||
className="px-4 py-2 text-caption font-semibold rounded-md border border-color-accent text-color-accent hover:bg-[color-mix(in_srgb,var(--color-accent)_10%,transparent)] transition-all font-korean mt-4"
|
||||
>
|
||||
템플릿에서 작성
|
||||
</button>
|
||||
@ -376,7 +376,7 @@ export function ReportsView() {
|
||||
<td className="px-3 py-3 text-center">
|
||||
<button
|
||||
onClick={() => handleDelete(report.id)}
|
||||
className="w-7 h-7 rounded flex items-center justify-center text-color-danger hover:bg-[color-mix(in_srgb,var(--color-danger)_10%,transparent)] transition-all text-sm"
|
||||
className="w-7 h-7 rounded flex items-center justify-center text-color-danger hover:bg-[color-mix(in_srgb,var(--color-danger)_10%,transparent)] transition-all text-body-2"
|
||||
>
|
||||
🗑
|
||||
</button>
|
||||
|
||||
@ -1048,14 +1048,14 @@ function NewScenarioModal({ ops, onClose }: { ops: RescueOpsItem[]; onClose: ()
|
||||
</div>
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-5 py-2.5 rounded-md border border-stroke bg-bg-card text-fg-sub text-xs font-semibold cursor-pointer"
|
||||
className="px-5 py-2.5 rounded-md border border-stroke bg-bg-card text-fg-sub text-caption font-semibold cursor-pointer"
|
||||
>
|
||||
취소
|
||||
</button>
|
||||
{done ? (
|
||||
<button
|
||||
onClick={onClose}
|
||||
className="px-7 py-2.5 rounded-md border-none text-static-white text-xs font-bold cursor-pointer bg-color-success"
|
||||
className="px-7 py-2.5 rounded-md border-none text-static-white text-caption font-bold cursor-pointer bg-color-success"
|
||||
>
|
||||
✅ 생성 완료 — 닫기
|
||||
</button>
|
||||
@ -1063,7 +1063,7 @@ function NewScenarioModal({ ops, onClose }: { ops: RescueOpsItem[]; onClose: ()
|
||||
<button
|
||||
onClick={handleSubmit}
|
||||
disabled={submitting}
|
||||
className={`px-7 py-2.5 rounded-md border-none text-xs font-bold ${
|
||||
className={`px-7 py-2.5 rounded-md border-none text-caption font-bold ${
|
||||
submitting
|
||||
? 'bg-bg-card text-fg-disabled cursor-wait'
|
||||
: 'bg-color-navy text-static-white cursor-pointer hover:bg-color-navy-hover'
|
||||
|
||||
@ -3,8 +3,8 @@ function DistributionView() {
|
||||
<div className="flex w-full h-full bg-bg-base items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="text-3xl opacity-20 mb-3">🗺</div>
|
||||
<div className="text-sm font-bold text-fg-sub font-korean mb-1">해양오염분포도</div>
|
||||
<div className="text-xs text-fg-disabled font-korean">
|
||||
<div className="text-body-2 font-bold text-fg-sub font-korean mb-1">해양오염분포도</div>
|
||||
<div className="text-caption text-fg-disabled font-korean">
|
||||
해양오염 분포도 기능이 준비 중입니다.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -177,13 +177,13 @@ export function PreScatView() {
|
||||
if (error) {
|
||||
return (
|
||||
<div className="flex w-full h-full bg-bg-base items-center justify-center flex-col gap-3">
|
||||
<div className="text-color-danger text-sm font-korean">{error}</div>
|
||||
<div className="text-color-danger text-body-2 font-korean">{error}</div>
|
||||
<button
|
||||
onClick={() => {
|
||||
setError(null);
|
||||
setLoading(true);
|
||||
}}
|
||||
className="px-4 py-1.5 bg-color-accent text-white text-xs rounded font-korean"
|
||||
className="px-4 py-1.5 bg-color-accent text-white text-caption rounded font-korean"
|
||||
>
|
||||
다시 시도
|
||||
</button>
|
||||
@ -194,7 +194,7 @@ export function PreScatView() {
|
||||
if (loading || !selectedSeg) {
|
||||
return (
|
||||
<div className="flex w-full h-full bg-bg-base items-center justify-center">
|
||||
<div className="text-fg-sub text-sm font-korean">SCAT 데이터 로딩 중...</div>
|
||||
<div className="text-fg-sub text-body-2 font-korean">SCAT 데이터 로딩 중...</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -182,7 +182,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
<button
|
||||
key={i}
|
||||
onClick={() => setPopTab(i)}
|
||||
className={`px-5 py-3 text-xs font-semibold font-korean border-b-2 transition-colors cursor-pointer ${
|
||||
className={`px-5 py-3 text-caption font-semibold font-korean border-b-2 transition-colors cursor-pointer ${
|
||||
popTab === i
|
||||
? 'text-color-success border-status-green'
|
||||
: 'text-fg-disabled border-transparent hover:text-fg-sub'
|
||||
@ -204,7 +204,9 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
{/* Skeleton */}
|
||||
{!imgLoaded && !imgError && (
|
||||
<div className="w-full aspect-video bg-bg-card animate-pulse flex items-center justify-center">
|
||||
<span className="text-fg-disabled text-xs font-korean">사진 로딩 중...</span>
|
||||
<span className="text-fg-disabled text-caption font-korean">
|
||||
사진 로딩 중...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<img
|
||||
@ -216,7 +218,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
onError={() => setImgError(true)}
|
||||
/>
|
||||
{imgError && (
|
||||
<div className="w-full aspect-video flex flex-col items-center justify-center text-fg-disabled text-xs font-korean">
|
||||
<div className="w-full aspect-video flex flex-col items-center justify-center text-fg-disabled text-caption font-korean">
|
||||
{/* <span className="text-[40px]">📷</span> */}
|
||||
<span>사진 없음</span>
|
||||
</div>
|
||||
@ -266,7 +268,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
].map(([k, v, cls], i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex justify-between py-1.5 text-xs border-b border-stroke last:border-b-0"
|
||||
className="flex justify-between py-1.5 text-caption border-b border-stroke last:border-b-0"
|
||||
>
|
||||
<span className="text-fg-sub font-korean">{k}</span>
|
||||
<span className={`text-fg font-korean ${cls}`}>{v}</span>
|
||||
@ -282,7 +284,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
{data.sensitive.map((s, i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex justify-between py-1.5 text-xs border-b border-stroke last:border-b-0"
|
||||
className="flex justify-between py-1.5 text-caption border-b border-stroke last:border-b-0"
|
||||
>
|
||||
<span className="text-fg-sub font-korean">{s.t}</span>
|
||||
<span className="text-fg font-korean">{s.v}</span>
|
||||
@ -344,7 +346,9 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
<div className="w-full aspect-[4/3] bg-bg-base border border-stroke rounded-md mb-4 overflow-hidden relative">
|
||||
{!mapLoaded && (
|
||||
<div className="absolute inset-0 bg-bg-card animate-pulse flex items-center justify-center z-10">
|
||||
<span className="text-fg-disabled text-xs font-korean">지도 로딩 중...</span>
|
||||
<span className="text-fg-disabled text-caption font-korean">
|
||||
지도 로딩 중...
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
<PopupMap
|
||||
@ -390,7 +394,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
].map(([k, v], i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex justify-between py-1.5 text-xs border-b border-stroke last:border-b-0"
|
||||
className="flex justify-between py-1.5 text-caption border-b border-stroke last:border-b-0"
|
||||
>
|
||||
<span className="text-fg-disabled font-korean">{k}</span>
|
||||
<span className="text-color-success font-mono font-medium">{v}</span>
|
||||
@ -413,7 +417,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
].map(([k, v], i) => (
|
||||
<div
|
||||
key={i}
|
||||
className="flex justify-between py-1.5 text-xs border-b border-stroke last:border-b-0"
|
||||
className="flex justify-between py-1.5 text-caption border-b border-stroke last:border-b-0"
|
||||
>
|
||||
<span className="text-fg-disabled font-korean">{k}</span>
|
||||
<span className="text-fg font-medium font-korean">{v}</span>
|
||||
@ -441,7 +445,7 @@ function ScatPopup({ data, segCode, onClose }: ScatPopupProps) {
|
||||
].map((h, i) => (
|
||||
<div key={i} className="bg-bg-card border border-stroke rounded-md p-4">
|
||||
<div className="flex items-center justify-between mb-2">
|
||||
<span className="text-xs font-bold font-mono">{h.date}</span>
|
||||
<span className="text-caption font-bold font-mono">{h.date}</span>
|
||||
<span
|
||||
className={`text-caption font-bold px-2 py-0.5 rounded-lg ${
|
||||
h.type === 'Pre-SCAT'
|
||||
|
||||
@ -49,12 +49,12 @@ export default function ScatRightPanel({
|
||||
{detail.esi}
|
||||
</span>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-xs font-bold truncate">{detail.name}</div>
|
||||
<div className="text-caption font-bold truncate">{detail.name}</div>
|
||||
<div className="text-caption text-fg-disabled">{detail.code}</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="text-xs text-fg-disabled">로딩 중...</div>
|
||||
<div className="text-caption text-fg-disabled">로딩 중...</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
|
||||
@ -67,38 +67,38 @@ function ScatTimeline({ segments, currentIdx, onSeek }: ScatTimelineProps) {
|
||||
<div className="flex gap-1 flex-shrink-0">
|
||||
<button
|
||||
onClick={() => onSeek(0)}
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-sm"
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-body-2"
|
||||
>
|
||||
⏮
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onSeek(Math.max(0, currentIdx - 1))}
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-sm"
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-body-2"
|
||||
>
|
||||
◀
|
||||
</button>
|
||||
<button
|
||||
onClick={togglePlay}
|
||||
className={`w-[34px] h-[34px] rounded-sm border flex items-center justify-center cursor-pointer text-sm ${playing ? 'bg-color-success text-black border-status-green' : 'border-stroke bg-bg-card text-fg-sub hover:bg-bg-surface-hover'}`}
|
||||
className={`w-[34px] h-[34px] rounded-sm border flex items-center justify-center cursor-pointer text-body-2 ${playing ? 'bg-color-success text-black border-status-green' : 'border-stroke bg-bg-card text-fg-sub hover:bg-bg-surface-hover'}`}
|
||||
>
|
||||
{playing ? '⏸' : '▶'}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onSeek(Math.min(total - 1, currentIdx + 1))}
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-sm"
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-body-2"
|
||||
>
|
||||
▶
|
||||
</button>
|
||||
<button
|
||||
onClick={() => onSeek(total - 1)}
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-sm"
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-body-2"
|
||||
>
|
||||
⏭
|
||||
</button>
|
||||
<div className="w-2" />
|
||||
<button
|
||||
onClick={cycleSpeed}
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-xs font-mono font-bold"
|
||||
className="w-[34px] h-[34px] rounded-sm border border-stroke bg-bg-card text-fg-sub flex items-center justify-center cursor-pointer hover:bg-bg-surface-hover text-caption font-mono font-bold"
|
||||
>
|
||||
{speed}×
|
||||
</button>
|
||||
@ -162,7 +162,7 @@ function ScatTimeline({ segments, currentIdx, onSeek }: ScatTimelineProps) {
|
||||
|
||||
{/* Info */}
|
||||
<div className="flex flex-col items-end gap-1 flex-shrink-0 min-w-[210px]">
|
||||
<span className="text-sm font-semibold text-color-success font-mono">
|
||||
<span className="text-body-2 font-semibold text-color-success font-mono">
|
||||
구간 {displaySegs[currentIdx]?.code || 'S-001'} / {total}개
|
||||
</span>
|
||||
<div className="flex gap-3.5">
|
||||
|
||||
@ -3,8 +3,8 @@ function SurveyView() {
|
||||
<div className="flex w-full h-full bg-bg-base items-center justify-center">
|
||||
<div className="text-center">
|
||||
<div className="text-3xl opacity-20 mb-3">📋</div>
|
||||
<div className="text-sm font-bold text-fg-sub font-korean mb-1">해안오염 조사 평가</div>
|
||||
<div className="text-xs text-fg-disabled font-korean">
|
||||
<div className="text-body-2 font-bold text-fg-sub font-korean mb-1">해안오염 조사 평가</div>
|
||||
<div className="text-caption text-fg-disabled font-korean">
|
||||
해안오염 조사 및 평가 기능이 준비 중입니다.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -37,7 +37,7 @@ export function WeatherMapControls({ center, zoom }: WeatherMapControlsProps) {
|
||||
>
|
||||
{label}
|
||||
</button>
|
||||
<div className="absolute right-full top-1/2 -translate-y-1/2 mr-2 px-2 py-1 text-xs bg-bg-base text-fg border border-stroke rounded whitespace-nowrap opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity z-20">
|
||||
<div className="absolute right-full top-1/2 -translate-y-1/2 mr-2 px-2 py-1 text-caption bg-bg-base text-fg border border-stroke rounded whitespace-nowrap opacity-0 group-hover:opacity-100 pointer-events-none transition-opacity z-20">
|
||||
{tooltip}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -100,7 +100,7 @@ export function WeatherMapOverlay({
|
||||
</svg>
|
||||
</div>
|
||||
<span
|
||||
className="text-xs font-bold leading-none px-1 py-px rounded-sm bg-bg-base/80"
|
||||
className="text-caption font-bold leading-none px-1 py-px rounded-sm bg-bg-base/80"
|
||||
style={{ color }}
|
||||
>
|
||||
{station.wind.speed.toFixed(1)}
|
||||
@ -140,7 +140,7 @@ export function WeatherMapOverlay({
|
||||
textShadow: '1px 1px 3px rgba(0,0,0,0.7)',
|
||||
borderBottom: `1px solid ${isSelected ? 'rgba(0,0,0,0.2)' : 'rgba(255,255,255,0.3)'}`,
|
||||
}}
|
||||
className="text-center text-xs font-bold pb-1 mb-0.5"
|
||||
className="text-center text-caption font-bold pb-1 mb-0.5"
|
||||
>
|
||||
{station.name}
|
||||
</div>
|
||||
@ -152,7 +152,7 @@ export function WeatherMapOverlay({
|
||||
🌡️
|
||||
</div>
|
||||
<div className="flex items-baseline gap-0.5">
|
||||
<span className="text-sm font-bold text-white" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>
|
||||
<span className="text-body-2 font-bold text-white" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>
|
||||
{station.temperature.current.toFixed(1)}
|
||||
</span>
|
||||
<span className="text-caption text-white opacity-90" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>°C</span>
|
||||
@ -166,7 +166,7 @@ export function WeatherMapOverlay({
|
||||
🌊
|
||||
</div>
|
||||
<div className="flex items-baseline gap-0.5">
|
||||
<span className="text-sm font-bold text-white" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>
|
||||
<span className="text-body-2 font-bold text-white" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>
|
||||
{station.wave.height.toFixed(1)}
|
||||
</span>
|
||||
<span className="text-caption text-white opacity-90" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>m</span>
|
||||
@ -180,7 +180,7 @@ export function WeatherMapOverlay({
|
||||
💨
|
||||
</div>
|
||||
<div className="flex items-baseline gap-0.5">
|
||||
<span className="text-sm font-bold text-white" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>
|
||||
<span className="text-body-2 font-bold text-white" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>
|
||||
{station.wind.speed.toFixed(1)}
|
||||
</span>
|
||||
<span className="text-caption text-white opacity-90" style={{ textShadow: '1px 1px 2px rgba(0,0,0,0.5)' }}>m/s</span>
|
||||
|
||||
@ -381,7 +381,7 @@ export function WeatherRightPanel({ weatherData }: WeatherRightPanelProps) {
|
||||
))}
|
||||
</div>
|
||||
<div className="flex items-center gap-2 mt-1.5 px-2 py-1 bg-bg-base border border-stroke rounded text-label-2">
|
||||
<span className="text-sm">🌓</span>
|
||||
<span className="text-body-2">🌓</span>
|
||||
<span className="text-fg-disabled">{astronomy.moonPhase}</span>
|
||||
<span className="ml-auto text-fg font-mono">조차 {astronomy.tidalRange}m</span>
|
||||
</div>
|
||||
|
||||
@ -300,7 +300,7 @@ export function WeatherView() {
|
||||
<button
|
||||
key={offset}
|
||||
onClick={() => setTimeOffset(offset)}
|
||||
className={`px-3 py-2 text-xs font-semibold rounded transition-all ${
|
||||
className={`px-3 py-2 text-caption font-semibold rounded transition-all ${
|
||||
timeOffset === offset
|
||||
? 'bg-color-accent text-bg-0'
|
||||
: 'bg-bg-card border border-stroke text-fg-sub hover:bg-bg-surface-hover'
|
||||
@ -311,7 +311,7 @@ export function WeatherView() {
|
||||
))}
|
||||
|
||||
<div className="flex items-center gap-2 ml-4">
|
||||
<span className="text-xs text-fg-disabled">
|
||||
<span className="text-caption text-fg-disabled">
|
||||
{lastUpdate
|
||||
? `마지막 업데이트: ${lastUpdate.toLocaleTimeString('ko-KR', {
|
||||
hour: '2-digit',
|
||||
@ -322,7 +322,7 @@ export function WeatherView() {
|
||||
{loading && (
|
||||
<div className="w-4 h-4 border-2 border-color-accent border-t-transparent rounded-full animate-spin" />
|
||||
)}
|
||||
{error && <span className="text-xs text-color-danger">⚠️ {error}</span>}
|
||||
{error && <span className="text-caption text-color-danger">⚠️ {error}</span>}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@ -77,29 +77,29 @@ export default {
|
||||
{ lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-navigation)' },
|
||||
],
|
||||
'title-3': [
|
||||
'0.875rem',
|
||||
'1rem',
|
||||
{ lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-navigation)' },
|
||||
],
|
||||
'title-4': [
|
||||
'0.8125rem',
|
||||
'0.875rem',
|
||||
{ lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-navigation)' },
|
||||
],
|
||||
'title-5': [
|
||||
'0.75rem',
|
||||
'0.8125rem',
|
||||
{ lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-navigation)' },
|
||||
],
|
||||
'title-6': [
|
||||
'0.6875rem',
|
||||
'0.75rem',
|
||||
{ lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-navigation)' },
|
||||
],
|
||||
'body-1': ['0.875rem', { lineHeight: '1.6', letterSpacing: 'var(--letter-spacing-body)' }],
|
||||
'body-2': ['0.8125rem', { lineHeight: '1.6', letterSpacing: 'var(--letter-spacing-body)' }],
|
||||
'label-1': ['0.75rem', { lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-label)' }],
|
||||
'label-2': [
|
||||
'0.6875rem',
|
||||
'body-1': ['1rem', { lineHeight: '1.6', letterSpacing: 'var(--letter-spacing-body)' }],
|
||||
'body-2': ['0.875rem', { lineHeight: '1.6', letterSpacing: 'var(--letter-spacing-body)' }],
|
||||
'label-1': [
|
||||
'0.8125rem',
|
||||
{ lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-label)' },
|
||||
],
|
||||
caption: ['0.6875rem', { lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-body)' }],
|
||||
'label-2': ['0.75rem', { lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-label)' }],
|
||||
caption: ['0.75rem', { lineHeight: '1.5', letterSpacing: 'var(--letter-spacing-body)' }],
|
||||
},
|
||||
letterSpacing: {
|
||||
display: 'var(--letter-spacing-display)',
|
||||
|
||||
불러오는 중...
Reference in New Issue
Block a user