"use client"; import { createContext, useCallback, useContext, useMemo, useRef, useState, type ReactNode } from "react"; import { isApiError } from "./api"; export type ToastKind = "info" | "success" | "warning" | "error"; export interface ToastItem { id: number; kind: ToastKind; title: string; detail?: string; requestId?: string | null; } interface ToastCtx { toasts: ToastItem[]; push: (t: Omit) => void; dismiss: (id: number) => void; error: (e: unknown, fallback?: string) => void; success: (title: string, detail?: string) => void; info: (title: string, detail?: string) => void; } const Ctx = createContext(null); export function ToastProvider({ children }: { children: ReactNode }) { const [toasts, setToasts] = useState([]); const seq = useRef(0); const dismiss = useCallback((id: number) => setToasts((t) => t.filter((x) => x.id !== id)), []); const push = useCallback( (t: Omit) => { const id = ++seq.current; setToasts((prev) => [...prev.slice(-4), { ...t, id }]); const ttl = t.kind === "error" ? 9000 : 4500; window.setTimeout(() => dismiss(id), ttl); }, [dismiss], ); const error = useCallback( (e: unknown, fallback = "Request failed") => { if (isApiError(e)) { push({ kind: "error", title: `${e.message}`, detail: `${e.code} ยท HTTP ${e.status}`, requestId: e.request_id }); } else if (e instanceof Error) { push({ kind: "error", title: fallback, detail: e.message }); } else { push({ kind: "error", title: fallback }); } }, [push], ); const success = useCallback((title: string, detail?: string) => push({ kind: "success", title, detail }), [push]); const info = useCallback((title: string, detail?: string) => push({ kind: "info", title, detail }), [push]); const value = useMemo(() => ({ toasts, push, dismiss, error, success, info }), [toasts, push, dismiss, error, success, info]); return {children}; } export function useToast(): ToastCtx { const c = useContext(Ctx); if (!c) throw new Error("useToast outside ToastProvider"); return c; }