borsa / nextjs-app /src /contexts /ToastContext.tsx
veteroner's picture
feat: sync US market support β€” bootstrap fix, symbol validation, ghost cleanup, correct dir paths
cb11e81
'use client'
import { createContext, useContext, useState, useCallback, ReactNode } from 'react'
type ToastType = 'success' | 'error' | 'warning' | 'info'
interface Toast {
id: number
type: ToastType
message: string
}
interface ToastContextValue {
toast: (message: string, type?: ToastType) => void
success: (message: string) => void
error: (message: string) => void
warning: (message: string) => void
info: (message: string) => void
}
const ToastContext = createContext<ToastContextValue | null>(null)
let nextId = 0
export function ToastProvider({ children }: { children: ReactNode }) {
const [toasts, setToasts] = useState<Toast[]>([])
const removeToast = useCallback((id: number) => {
setToasts((prev) => prev.filter((t) => t.id !== id))
}, [])
const addToast = useCallback(
(message: string, type: ToastType = 'info') => {
const id = ++nextId
setToasts((prev) => [...prev, { id, type, message }])
setTimeout(() => removeToast(id), 4000)
},
[removeToast]
)
const value: ToastContextValue = {
toast: addToast,
success: (msg) => addToast(msg, 'success'),
error: (msg) => addToast(msg, 'error'),
warning: (msg) => addToast(msg, 'warning'),
info: (msg) => addToast(msg, 'info'),
}
return (
<ToastContext.Provider value={value}>
{children}
{/* Toast container */}
<div
className="fixed bottom-4 right-4 z-[100] flex flex-col gap-2 pointer-events-none"
aria-live="polite"
aria-label="Bildirimler"
>
{toasts.map((t) => (
<div
key={t.id}
className={`pointer-events-auto flex items-center gap-3 px-4 py-3 rounded-lg shadow-lg text-sm font-medium animate-slide-in-right ${typeStyles[t.type]}`}
role="alert"
>
<span className="shrink-0">{typeIcons[t.type]}</span>
<span className="flex-1">{t.message}</span>
<button
onClick={() => removeToast(t.id)}
className="shrink-0 opacity-60 hover:opacity-100 transition-opacity"
aria-label="Kapat"
>
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
))}
</div>
</ToastContext.Provider>
)
}
export function useToast(): ToastContextValue {
const ctx = useContext(ToastContext)
if (!ctx) throw new Error('useToast must be used within ToastProvider')
return ctx
}
/* ─── Styles ─── */
const typeStyles: Record<ToastType, string> = {
success: 'bg-green-600 text-white',
error: 'bg-red-600 text-white',
warning: 'bg-yellow-500 text-white',
info: 'bg-blue-600 text-white',
}
const typeIcons: Record<ToastType, string> = {
success: 'βœ“',
error: 'βœ•',
warning: '⚠',
info: 'β„Ή',
}