feat(ui): Phase C — 대시보드 위젯 그리드 + dashboardSummary 공유 fetch

- dashboardSummary store 구독으로 SystemStatusDot과 fetch 1회 공유 (60초 폴링)
- Svelte 5 runes + Card/EmptyState/Skeleton/FormatIcon 프리미티브
- 12-col 그리드 (sm 1열 / md 2열 / lg 표 그대로):
  * 행1: stat 4장 (전체/Inbox/법령/시스템 상태)
  * 행2: 파이프라인 가로 막대 차트(8) + 오늘 도메인 누적바(4)
  * 행3: 최근 문서(8) + CalDAV stub(4)
- 신규 util: domainSlug.ts — ai_domain → bg-domain-{slug} + 라벨 매핑
- 새 코드에 bg-[var(--*)] 0건 (lint:tokens 통과)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-08 12:13:36 +09:00
parent 0c63c0b6ab
commit b3124928a6
2 changed files with 448 additions and 74 deletions

View File

@@ -0,0 +1,66 @@
// ai_domain 값을 @theme의 bg-domain-{slug} 토큰 슬러그로 매핑.
// 백엔드는 'Knowledge/Philosophy', 'Knowledge/Industrial_Safety', 'Reference', null
// 형태를 모두 보낼 수 있으므로 leaf만 잘라 lowercase하고 industrial_safety→safety로 재매핑.
//
// Phase C: 대시보드 today_by_domain 누적바, recent_documents accent에서 사용.
// 추후 phase에서도 도메인 색상 칩이 필요해지면 여기로 일원화.
export type DomainSlug =
| 'philosophy'
| 'language'
| 'engineering'
| 'safety'
| 'programming'
| 'general'
| 'reference';
const LEAF_TO_SLUG: Record<string, DomainSlug> = {
philosophy: 'philosophy',
language: 'language',
engineering: 'engineering',
industrial_safety: 'safety',
safety: 'safety',
programming: 'programming',
general: 'general',
reference: 'reference',
};
/** 'Knowledge/Philosophy' → 'philosophy', 'Reference' → 'reference', null → null */
export function domainSlug(domain: string | null | undefined): DomainSlug | null {
if (!domain) return null;
const leaf = domain.includes('/') ? domain.split('/').pop()! : domain;
return LEAF_TO_SLUG[leaf.toLowerCase()] ?? null;
}
const SLUG_BG_CLASS: Record<DomainSlug, string> = {
philosophy: 'bg-domain-philosophy',
language: 'bg-domain-language',
engineering: 'bg-domain-engineering',
safety: 'bg-domain-safety',
programming: 'bg-domain-programming',
general: 'bg-domain-general',
reference: 'bg-domain-reference',
};
/** bg-domain-{slug} 클래스. 미지정/미매핑은 bg-default. */
export function domainBgClass(domain: string | null | undefined): string {
const slug = domainSlug(domain);
return slug ? SLUG_BG_CLASS[slug] : 'bg-default';
}
const SLUG_LABEL: Record<DomainSlug, string> = {
philosophy: 'Philosophy',
language: 'Language',
engineering: 'Engineering',
safety: 'Industrial Safety',
programming: 'Programming',
general: 'General',
reference: 'Reference',
};
/** 표시용 라벨. domain이 null이면 '미분류'. */
export function domainLabel(domain: string | null | undefined): string {
const slug = domainSlug(domain);
if (!slug) return domain ?? '미분류';
return SLUG_LABEL[slug];
}