diff --git a/frontend/src/routes/+layout.svelte b/frontend/src/routes/+layout.svelte index 169b508..6393d8c 100644 --- a/frontend/src/routes/+layout.svelte +++ b/frontend/src/routes/+layout.svelte @@ -99,6 +99,7 @@ +
+ import { onMount } from 'svelte'; + import { api, type ApiError } from '$lib/api'; + import Card from '$lib/components/ui/Card.svelte'; + import Skeleton from '$lib/components/ui/Skeleton.svelte'; + import EmptyState from '$lib/components/ui/EmptyState.svelte'; + import Tabs from '$lib/components/ui/Tabs.svelte'; + import Badge from '$lib/components/ui/Badge.svelte'; + import Button from '$lib/components/ui/Button.svelte'; + + type Topic = { + topic_rank: number; + topic_label: string; + summary: string; + article_ids: number[]; + article_count: number; + importance_score: number; + llm_fallback_used: boolean; + }; + type Country = { country: string; topics: Topic[] }; + type Digest = { + digest_date: string; + total_articles: number; + total_countries: number; + total_topics: number; + llm_calls: number; + llm_failures: number; + status: string; + countries: Country[]; + }; + + let digest = $state(null); + let loading = $state(true); + let error = $state(null); + let country = $state(''); + + async function load() { + loading = true; + error = null; + try { + digest = await api('/digest/latest'); + const countries = digest?.countries ?? []; + if (!countries.some((c) => c.country === country)) { + country = countries[0]?.country ?? ''; + } + } catch (e) { + const err = e as ApiError; + if (err && err.status === 404) { + digest = null; + error = null; + return; + } + error = err?.detail ?? (e as Error)?.message ?? '알 수 없는 오류'; + } finally { + loading = false; + } + } + + onMount(load); + + let tabs = $derived( + (digest?.countries ?? []).map((c) => ({ id: c.country, label: c.country })), + ); + let topics = $derived( + digest?.countries?.find((c) => c.country === country)?.topics ?? [], + ); + + +
+
+

뉴스 다이제스트

+ {#if digest} + + {digest.digest_date} · {digest.countries.length}국 · {digest.total_topics}주제 + + {/if} +
+ + {#if loading} + + + + {:else if error} + + + + {:else if !digest || digest.countries.length === 0} + + {:else} + + {#snippet children(_activeId)} + {#if topics.length === 0} + + {:else} +
+ {#each topics as t (t.topic_rank)} + +
+

+ {t.topic_rank}. {t.topic_label} +

+ {#if t.llm_fallback_used} + fallback + {/if} +
+

{t.summary}

+
+ {t.article_count} articles · importance {t.importance_score.toFixed(2)} +
+
+ {/each} +
+ {/if} + {/snippet} +
+ {/if} +