import { create } from 'zustand' export type NotificationType = 'info' | 'warning' | 'success' | 'error' export interface Notification { id: string tag?: string message: string type: NotificationType durationMs?: number } interface NotificationState { notifications: Notification[] timers: Record> add: (msg: string, type?: NotificationType, durationMs?: number, tag?: string) => void remove: (id: string) => void } export const useNotificationStore = create((set, get) => ({ notifications: [], timers: {}, add(msg, type = 'info', durationMs = 5000, tag) { const { timers } = get() // Se tem tag, remove a notificação anterior com a mesma tag (sem animação de saída) // e cancela o timer dela, para não empilhar if (tag) { const existing = get().notifications.find((n) => n.tag === tag) if (existing) { if (timers[existing.id]) { clearTimeout(timers[existing.id]) set((s) => { const { [existing.id]: _, ...rest } = s.timers return { timers: rest } }) } set((s) => ({ notifications: s.notifications.filter((n) => n.tag !== tag) })) } } const id = tag ?? `${Date.now()}-${Math.random().toString(36).slice(2, 7)}` set((s) => ({ notifications: [...s.notifications, { id, tag, message: msg, type, durationMs }] })) if (durationMs > 0) { const timer = setTimeout(() => { set((s) => ({ notifications: s.notifications.filter((n) => n.id !== id), timers: Object.fromEntries(Object.entries(s.timers).filter(([k]) => k !== id)), })) }, durationMs) set((s) => ({ timers: { ...s.timers, [id]: timer } })) } }, remove(id) { const { timers } = get() if (timers[id]) { clearTimeout(timers[id]) set((s) => { const { [id]: _, ...rest } = s.timers return { timers: rest } }) } set((s) => ({ notifications: s.notifications.filter((n) => n.id !== id) })) }, })) /** Atalho para usar fora de componentes React (stores, services, etc.) */ export const notify = (msg: string, type?: NotificationType, durationMs?: number, tag?: string) => useNotificationStore.getState().add(msg, type, durationMs, tag)