diff --git a/src/renderer/inbox/components/NoteCard.tsx b/src/renderer/inbox/components/NoteCard.tsx index 35618dc..c3277ba 100644 --- a/src/renderer/inbox/components/NoteCard.tsx +++ b/src/renderer/inbox/components/NoteCard.tsx @@ -1,5 +1,163 @@ -import React from 'react'; +import React, { useState } from 'react'; import type { Note } from '@shared/types'; -export function NoteCard({ note }: { note: Note; onDeleted: () => void; onUpdated: (n: Note) => void }) { - return
{note.rawText}
; +import { inboxApi } from '../api.js'; +import { EditableField } from './EditableField.js'; +import { IntentBanner } from './IntentBanner.js'; + +interface Props { + note: Note; + onDeleted: () => void; + onUpdated: (n: Note) => void; +} + +const aiBadgeStyle: React.CSSProperties = { + display: 'inline-block', marginLeft: 6, padding: '1px 5px', + background: '#eee', color: '#666', fontSize: 10, borderRadius: 3, verticalAlign: 'middle' +}; + +export function NoteCard({ note, onDeleted, onUpdated }: Props): React.ReactElement { + const [rawOpen, setRawOpen] = useState(note.aiStatus !== 'done'); + const [local, setLocal] = useState(note); + + React.useEffect(() => { setLocal(note); }, [note]); + + const formatted = new Date(note.createdAt).toLocaleString('ko-KR'); + + async function handleDelete() { + if (!window.confirm('이 기억을 버릴까요? 되돌릴 수 없습니다.')) return; + await inboxApi.deleteNote(note.id); + onDeleted(); + } + + async function saveTitle(next: string) { + await inboxApi.updateAiFields(note.id, { title: next }); + const updated = { ...local, aiTitle: next, titleEditedByUser: true }; + setLocal(updated); onUpdated(updated); + } + + async function saveSummary(next: string) { + await inboxApi.updateAiFields(note.id, { summary: next }); + const updated = { ...local, aiSummary: next, summaryEditedByUser: true }; + setLocal(updated); onUpdated(updated); + } + + async function removeTag(tagName: string) { + const next = local.tags.filter((t) => t.name !== tagName).map((t) => t.name); + await inboxApi.updateAiFields(note.id, { tags: next }); + const updated = { ...local, tags: local.tags.filter((t) => t.name !== tagName) }; + setLocal(updated); onUpdated(updated); + } + + async function saveIntent(next: string) { + await inboxApi.setIntent(note.id, next); + const now = new Date().toISOString(); + const updated = { ...local, userIntent: next, intentPromptedAt: local.intentPromptedAt ?? now }; + setLocal(updated); onUpdated(updated); + } + + const showIntentBanner = local.aiStatus === 'done' && local.intentPromptedAt === null; + + return ( +
+
{formatted}
+ + {showIntentBanner && ( + { + const now = new Date().toISOString(); + const updated = { ...local, userIntent: intentText ?? null, intentPromptedAt: now }; + setLocal(updated); onUpdated(updated); + }} + /> + )} + + {local.aiStatus === 'pending' && ( +
+ Inkling이 정리하는 중… +
+ )} + {local.aiStatus === 'failed' && ( +
+ 정리 보류 — 원문은 안전합니다 +
+ )} + {local.aiStatus === 'done' && ( + <> +
+ + {!local.titleEditedByUser && AI} +
+
+ + {!local.summaryEditedByUser && AI} +
+ {local.tags.length > 0 && ( +
+ {local.tags.map((t) => ( + void removeTag(t.name)} + style={{ + background: t.source === 'ai' ? '#eaf3ff' : '#e9f9e4', + color: t.source === 'ai' ? '#0a4b80' : '#236b1a', + padding: '2px 8px', borderRadius: 12, fontSize: 12, cursor: 'pointer' + }} + title={t.source === 'ai' ? 'AI 제안 — 클릭으로 제거' : '내가 추가 — 클릭으로 제거'} + > + {t.name}{t.source === 'ai' && AI} + + ))} +
+ )} + {local.userIntent !== null && ( +
+ 💡 + +
+ )} + + )} + + {local.media.length > 0 && ( +
+ {local.media.map((m) => ( +
+ ))} +
+ )} + +
+ + {rawOpen && ( +
+            {local.rawText}
+          
+ )} +
+ +
+ +
+
+ ); }