"use client"; // 종목 페이지의 사이드 메모 패널. // Toss "투자 메모" 패턴 — 진입 이유, 관전 포인트를 짧게 적는 자리. // localStorage 만 사용, 서버 전송 없음. import { useEffect, useMemo, useState } from "react"; import { NOTE_MAX_LEN, readNote, writeNote, type Note } from "../lib/notes"; 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 next = writeNote(code, text); setSaved(next); setText(next?.text ?? ""); setFeedback(next ? "저장됨" : "저장 안됨"); }; return (

투자 메모

{saved ? fmtUpdatedAt(saved.updatedAt) : "비공개 · 브라우저 저장"}