diff --git a/frontend/src/routes/memos/+page.svelte b/frontend/src/routes/memos/+page.svelte index 249f335..c17a0c5 100644 --- a/frontend/src/routes/memos/+page.svelte +++ b/frontend/src/routes/memos/+page.svelte @@ -3,7 +3,8 @@ import { api } from '$lib/api'; import { addToast } from '$lib/stores/toast'; import { renderMemoHtml, todayIso, countHiddenTasks, DEFAULT_HIDE_AFTER_MS } from '$lib/utils/memoRenderer'; - import { Pin, PinOff, Pencil, Trash2, Eye, EyeOff, X, Check, Archive, ArchiveRestore, ListChecks, Bold, Heading, CalendarDays } from 'lucide-svelte'; + import { Pin, PinOff, Pencil, Trash2, Eye, EyeOff, X, Check, Archive, ArchiveRestore, ListChecks, Bold, Heading, CalendarDays, Mic, Calendar, Activity, ArrowRight, FileText, BookOpen } from 'lucide-svelte'; + import { getAccessToken } from '$lib/api'; import Button from '$lib/components/ui/Button.svelte'; import Card from '$lib/components/ui/Card.svelte'; import EmptyState from '$lib/components/ui/EmptyState.svelte'; @@ -249,6 +250,54 @@ } } + // ─── PR-2B: 메모 → events 1-click promote ─── + + async function promoteMemo(memoId, kind) { + const labels = { task: '할 일', calendar_event: '일정', activity_log: '활동 기록' }; + try { + const res = await api(`/memos/${memoId}/promote-to-event`, { + method: 'POST', + body: JSON.stringify({ kind }), + }); + addToast('success', `${labels[kind]} 로 승급 (events #${res.event_id})`); + // 로컬 상태 갱신 — promoted 표시를 위해 메모에 임시 마킹 (서버 미반영, UX 힌트만) + memos = memos.map((m) => m.id === memoId ? { ...m, _last_promoted: { event_id: res.event_id, kind } } : m); + } catch (err) { + addToast('error', err?.detail || '승급 실패'); + } + } + + async function dismissEventSuggestion(memoId) { + try { + const updated = await api(`/memos/${memoId}/dismiss-event-suggestion`, { method: 'POST' }); + memos = memos.map((m) => (m.id === memoId ? updated : m)); + } catch (err) { + addToast('error', '처리 실패'); + } + } + + // voice 메모 audio URL — /api/documents/{id}/file?token= 패턴 재사용 + function voiceAudioUrl(memoId) { + const token = getAccessToken(); + return `/api/documents/${memoId}/file?token=${encodeURIComponent(token ?? '')}`; + } + + // ai_event_kind 별 라벨 / 색상 + const KIND_LABELS = { + note: '메모', + task: '할 일', + calendar_event: '일정', + activity_log: '활동', + reference: '참조', + }; + const KIND_BADGE_CLASS = { + note: 'bg-surface text-dim', + task: 'bg-indigo-100 text-indigo-700', + calendar_event: 'bg-blue-100 text-blue-700', + activity_log: 'bg-emerald-100 text-emerald-700', + reference: 'bg-amber-100 text-amber-700', + }; + async function handleCheckboxClick(e, memo) { const target = e.target; if (target.tagName !== 'INPUT' || target.type !== 'checkbox') return; @@ -473,9 +522,36 @@ {:else} + + {#if memo.source_channel === 'voice' || memo.ai_event_kind || memo._last_promoted} +
{memo.title}
{/if} + + + {#if memo.source_channel === 'voice' && memo.file_path} + + {/if} +음성 → 텍스트 변환 대기 중…
+ {/if}