"use client"; // 종목 페이지의 사이드 메모 패널. // Toss "투자 메모" 패턴 — 진입 이유, 관전 포인트를 짧게 적는 자리. // localStorage 만 사용, 서버 전송 없음. // Collapsible 본문은 hidden 으로만 가리므로 접어도 textarea dirty 텍스트 유지. import { useEffect, useMemo, useState } from "react"; import { NOTE_MAX_LEN, readNote, writeNote, type Note } from "../lib/notes"; import { Collapsible } from "./Collapsible"; const FEEDBACK_TEXT: Record<"saved" | "deleted" | "failed", string> = { saved: "저장됨", deleted: "삭제됨", failed: "저장 안됨", }; type Props = { code: string; }; // '방금 전', '5분 전', '오늘 14:30', 'YYYY-MM-DD' 단계로 압축. function fmtUpdatedAt(iso: string): string { const t = Date.parse(iso); if (!Number.isFinite(t)) return ""; const now = Date.now(); const diffSec = Math.max(0, Math.floor((now - t) / 1000)); if (diffSec < 60) return "방금 전"; const diffMin = Math.floor(diffSec / 60); if (diffMin < 60) return `${diffMin}분 전`; const d = new Date(t); const pad = (n: number) => String(n).padStart(2, "0"); const ymd = `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}`; const hm = `${pad(d.getHours())}:${pad(d.getMinutes())}`; // 같은 날짜면 '오늘 HH:MM', 아니면 'YYYY-MM-DD' const today = new Date(); if ( today.getFullYear() === d.getFullYear() && today.getMonth() === d.getMonth() && today.getDate() === d.getDate() ) { return `오늘 ${hm}`; } return ymd; } export function InvestmentNote({ code }: Props) { const [text, setText] = useState(""); const [saved, setSaved] = useState(null); const [feedback, setFeedback] = useState(null); useEffect(() => { const n = readNote(code); setSaved(n); setText(n?.text ?? ""); setFeedback(null); }, [code]); const dirty = (saved?.text ?? "") !== text; // 저장 직후 잠깐 띄우는 toast 같은 안내. dirty 또는 종목 전환 시 자동 해제. useEffect(() => { if (!feedback) return; const h = window.setTimeout(() => setFeedback(null), 2500); return () => window.clearTimeout(h); }, [feedback]); const remaining = useMemo(() => NOTE_MAX_LEN - text.length, [text]); const save = () => { const res = writeNote(code, text); if (res.status === "saved") { setSaved(res.note); setText(res.note.text); } else if (res.status === "deleted") { setSaved(null); setText(""); } // failed: 입력값 유지 — 사용자가 재시도하거나 일부를 줄일 수 있도록. setFeedback(FEEDBACK_TEXT[res.status]); }; const subtitle = saved ? fmtUpdatedAt(saved.updatedAt) : "비공개 · 브라우저 저장"; return (