diff --git a/frontend/src/routes/+page.svelte b/frontend/src/routes/+page.svelte index 2b8d6a1..66abf66 100644 --- a/frontend/src/routes/+page.svelte +++ b/frontend/src/routes/+page.svelte @@ -1,76 +1,124 @@ -
-
+
+
- -
-

대시보드

- {#if systemView} - - {systemView.label} - - - {/if} + +
+

안녕하세요, {greetingName}

+ 오늘도 지식 쌓는 날.
+
{todayLabel}
{#if loading} -
- {#each Array(4) as _} - - {/each} +
+ + + +
+
+
+
+
+
+
+
+
+
- - {:else if summary} - - {#if pinnedMemos.length > 0} -
- {#each pinnedMemos as memo (memo.id)} -
- - - - {memo.title && memo.title !== memo.content?.split('\n')[0]?.replace(/^#+\s*/, '').slice(0, 80) - ? memo.title - : memo.content?.split('\n')[0] || '메모'} - - - - - -
handlePinCheckbox(e, memo)} - > -
- {@html renderMemoHtml(memo.content || '', { - compact: true, - interactive: true, - taskStates: memo.memo_task_state ?? {}, - now: nowTick, - })} -
-
- {#if countHiddenTasks(memo.memo_task_state, nowTick, DEFAULT_HIDE_AFTER_MS) > 0 || showHiddenByMemo[memo.id]} - - {/if} - 메모함에서 보기 → -
+ +
+ +
+ +
+ + {summary.inbox_count.toLocaleString()} + + 검토 대기 문서 + {#if summary.inbox_count > 0} + 검토 시작 → + {:else} + 미분류 없음 + {/if} +
+ + + {#if digestLead} + +
+ 속보 + {digestLead.date} 브리핑
-
- {/each} - {#if pinnedMemos.length >= 3} - 더보기 → +
+ {digestLead.topic_label} +
+
+ 관련 기사 {digestLead.article_count}건 + · 중요도 {digestLead.importance_score.toFixed(2)} + · {countryKo(digestLead.country)} +
+ + {:else} + + 오늘의 뉴스 브리핑 보기 → + {/if}
- {/if} - -
- - - -
-

문서함

- -
-

{(summary.documents_count ?? 0).toLocaleString()}

-

- {#if summary.today_added > 0} - +{summary.today_added} 오늘 - {:else} - 일반 문서 - {/if} -

-
-
- - - - -
-

메모

- -
-

{(summary.memos_count ?? 0).toLocaleString()}

-

직접 작성

-
-
- - - - -
-

뉴스

- -
-

{(summary.news_count ?? 0).toLocaleString()}

-

수집 기사

-
-
- - - - -
-

승인 대기

- -
-

- {summary.inbox_count} -

- {#if summary.inbox_count > 0} -

검토하기 →

- {:else} -

미분류 없음

- {/if} -
-
+ +
+ {@render stat((summary.documents_count ?? 0).toLocaleString(), '문서', 'text-accent')} + {@render stat((summary.news_count ?? 0).toLocaleString(), '뉴스')} + {#if domainTotal > 0} + {@render stat(domainCount('Industrial_Safety').toLocaleString(), '산업안전', 'text-domain-safety')} + {@render stat(domainCount('Engineering').toLocaleString(), '엔지니어링', 'text-domain-engineering')} + {/if} + {#if summary.category_counts?.library} + {@render stat(summary.category_counts.library.toLocaleString(), '자료실')} + {/if} + {@render stat((summary.memos_count ?? 0).toLocaleString(), '메모')} +
- - {#if summary.category_counts && (Object.keys(summary.category_counts).length > 0 || summary.library_pending_suggestions > 0)} -
- {#each CATEGORY_CARDS as cat} - {@const count = summary.category_counts?.[cat.key] ?? 0} - - -
-

{cat.label}

- -
-

{count.toLocaleString()}

-

카테고리

-
-
- {/each} + + - {/if} + +
- - {#if summary.tier_health && summary.tier_health.triage_total > 0} - {@const th = summary.tier_health} - {@const esc_rate = th.triage_total > 0 ? th.escalated_total / th.triage_total : 0} - {@const json_rate = th.triage_total > 0 ? th.triage_json_invalid / th.triage_total : 0} - {@const sup_rate = th.triage_total > 0 ? th.suppressed_total / th.triage_total : 0} - {@const deep_total = th.deep_total ?? 0} - {@const deep_err_rate = deep_total > 0 ? (th.deep_err_total ?? 0) / deep_total : 0} - - {@const esc_tone = esc_rate < 0.80 ? 'text-error' : 'text-text'} - {@const json_tone = json_rate > 0.05 ? 'text-error' : 'text-text'} - {@const sup_tone = sup_rate > 0.10 ? 'text-warning' : 'text-text'} - {@const deep_tone = deep_err_rate > 0.05 ? 'text-error' : 'text-text'} -
- - -
-

에스컬레이션 비율 (24h)

- + +
+
빠른 캡처
+
+ +
-

- {(esc_rate * 100).toFixed(1)}% -

-

- {th.escalated_total} / {th.triage_total} - {#if esc_rate < 0.80}(매칭 실패 증가){/if} -

-

safety 정책상 95~100% 가 정상

- {#if Object.keys(th.escalation_by_reason).length > 0} -
- {#each Object.entries(th.escalation_by_reason).slice(0, 4) as [reason, n]} - - {reason} {n} - + +
+ + +
+
+ 최근 활동 +
+ {#if summary.law_alerts > 0} + + 법령 {summary.law_alerts} + + {/if} + 전체 보기 → +
+
+ + {#if summary.recent_documents.length > 0} + - {/if} - - - - -
-

triage JSON 건강도 (24h)

- -
-

- {(json_rate * 100).toFixed(1)}% -

-

- 깨짐 {th.triage_json_invalid} 건 - {#if json_rate > 0.05}(프롬프트 이슈 의심){/if} -

-

5% 초과 시 4B 프롬프트·모델 재검토

-
- - - -
-

Backlog Suppression (24h)

- -
-

- {(sup_rate * 100).toFixed(1)}% -

-

- 억제 {th.suppressed_total} 건 - {#if sup_rate > 0.10}(임계치 재조정 신호){/if} -

-

10% 초과 시 ratio/pending threshold 조정

-
- - - -
-

Deep summary 안정성 (24h)

- -
-

- {(deep_err_rate * 100).toFixed(1)}% -

-

- 실패 {th.deep_err_total ?? 0} / {deep_total} - {#if deep_err_rate > 0.05}(MLX 안정성 점검){/if} -

-

call_failed / parse:* 합계, 5% 초과 시 점검

-
-
- {/if} - - - -
-

최근 활동

-
- {#if summary.law_alerts > 0} - - 법령 {summary.law_alerts} - + {:else} + {/if}
- {#if summary.recent_documents.length > 0} - - {:else} - - {/if} -
+ +
- + + +
+ 학습 + +
+
암기 노트 학습 시작 →
+
검수함 · 복습함 · 암기카드
+
+ + + {#if domainDist.length > 0} +
+
도메인 분포
+
전체 {domainTotal.toLocaleString()}
+ + +
+ {#each domainDist as d (d.name)} +
+ {/each} +
+ +
+ {#each domainDist.slice(0, 6) as d (d.name)} + + + {domainLabel(d.name)} + {d.count.toLocaleString()} + + {/each} +
+ + +
+ 파이프라인 + {#if totalFailed > 0}실패 {totalFailed}{/if} + {#if totalPending > 0}대기 {totalPending}{/if} + {#if totalProcessing > 0}처리중 {totalProcessing}{/if} + {#if totalFailed === 0 && totalPending === 0 && totalProcessing === 0}정상{/if} +
+
+ {/if} + + + {#if pinnedMemos.length > 0} +
+
+ 고정 항목 + 관리 → +
+
+ {#each pinnedMemos as memo (memo.id)} + + 메모 + {pinTitle(memo)} + + + {/each} +
+
+ {/if} +
+
+ +
{ if (!e.currentTarget.open) pipelineManualClosed = true; }} > - + - 파이프라인 + 파이프라인 상세 - {#if totalFailed > 0} - 실패 {totalFailed} - {/if} - {#if totalPending > 0} - 대기 {totalPending} - {/if} - {#if totalFailed === 0 && totalPending === 0} - 처리 완료 - {/if} + {#if totalFailed > 0}실패 {totalFailed}{/if} + {#if totalPending > 0}대기 {totalPending}{/if} + {#if totalFailed === 0 && totalPending === 0}처리 완료{/if} -
+

최근 24시간

{#if pipelineRows.length > 0}
@@ -522,9 +448,7 @@ {row.label} {#if row.oldestPendingAgeSec && row.oldestPendingAgeSec > 600} - - ({formatAge(row.oldestPendingAgeSec)}) - + ({formatAge(row.oldestPendingAgeSec)}) {/if} @@ -551,22 +475,14 @@
+{#snippet stat(value: string, label: string, colorClass = 'text-text')} +
+ {value} + {label} +
+{/snippet} + diff --git a/frontend/src/routes/digest/+page.svelte b/frontend/src/routes/digest/+page.svelte index c150f91..bf282af 100644 --- a/frontend/src/routes/digest/+page.svelte +++ b/frontend/src/routes/digest/+page.svelte @@ -410,20 +410,20 @@