"use client"; // /alerts — 모든 가격 알람을 한 화면에서 관리. // 종목별로 그룹핑, 활성/발사됨 분리, 재무장·삭제·일괄 지우기. // AlertsToaster 가 글로벌로 폴링하므로 이 페이지는 표시/조작만 담당. import Link from "next/link"; import { useEffect, useMemo, useState } from "react"; import { alerts, type Alert } from "../../lib/alerts"; export default function AlertsPage() { const [items, setItems] = useState([]); const [hydrated, setHydrated] = useState(false); useEffect(() => { setHydrated(true); const refresh = () => setItems(alerts.list()); refresh(); return alerts.subscribe(refresh); }, []); // 종목별 그룹. 표시는 최신 등록순(prepend 된 순서) 유지. const grouped = useMemo(() => { const m = new Map(); for (const a of items) { const g = m.get(a.code) ?? { name: a.name, rows: [] }; if (!g.name && a.name) g.name = a.name; g.rows.push(a); m.set(a.code, g); } return Array.from(m.entries()).map(([code, g]) => ({ code, name: g.name, rows: g.rows, })); }, [items]); const activeCount = items.filter((a) => !a.triggered).length; const firedCount = items.length - activeCount; return (
← 홈 활성 {activeCount} · 발사됨 {firedCount}

가격 알람

브라우저에 저장됩니다. 활성 알람은 60초마다 체크되어 임계값 도달 시 토스트 + (권한 허용 시) 시스템 알림.

{items.length > 0 && ( )}
{!hydrated ? (
로딩 중…
) : items.length === 0 ? (
아직 설정된 알람이 없습니다.
종목 페이지의 가격 알람 카드에서 추가하세요.
) : ( )} {hydrated && grouped.length > 0 && (
{grouped.map((g) => ( ))}
)}
); } // 알림 권한이 default 면 안내 + 요청 버튼. function NotificationHint() { const [perm, setPerm] = useState( "unsupported", ); useEffect(() => { if (typeof window === "undefined") return; if (!("Notification" in window)) { setPerm("unsupported"); return; } setPerm(Notification.permission); }, []); if (perm === "granted" || perm === "denied" || perm === "unsupported") return null; return (
시스템 알림 권한이 없습니다. 허용하면 브라우저 탭이 닫혀 있어도 알람을 받을 수 있습니다.
); } function Group({ code, name, rows, }: { code: string; name?: string; rows: Alert[]; }) { return (
{name ?? code} {code} {rows.length}개
    {rows.map((a) => (
  • {a.op === "gte" ? "≥" : "≤"} {a.target.toLocaleString()} {new Date(a.createdAt).toLocaleString("ko-KR", { month: "2-digit", day: "2-digit", hour: "2-digit", minute: "2-digit", })} {a.triggered && ( 발사됨 )}
    {a.triggered && ( )}
  • ))}
); }