import { ref, computed, watch } from 'vue' import type { Ref } from 'vue' export interface Notification { id: string type: 'info' | 'warning' | 'success' | 'error' title: string message: string timestamp: number read: boolean origin: 'toast' | 'backend' | 'manual' metadata?: Record } const notifications: Ref = ref([]) const unreadCount = computed(() => notifications.value.filter(n => !n.read).length) // Días para retención de notificaciones const RETENTION_DAYS = 30 const STORAGE_KEY_PREFIX = 'notifications-' let currentUserId: string | null = null let isInitialized = false /** * Obtiene la clave de localStorage para el usuario actual */ function getStorageKey(userId: string): string { return `${STORAGE_KEY_PREFIX}${userId}` } /** * Carga las notificaciones desde localStorage */ function loadNotifications(userId: string): Notification[] { if (process.client) { const key = getStorageKey(userId) const stored = localStorage.getItem(key) if (stored) { try { const parsed = JSON.parse(stored) as Notification[] // Limpiar notificaciones antiguas (> RETENTION_DAYS días) const cutoffTime = Date.now() - (RETENTION_DAYS * 24 * 60 * 60 * 1000) return parsed.filter(n => n.timestamp > cutoffTime) } catch (error) { console.error('Error al parsear notificaciones:', error) return [] } } } return [] } /** * Guarda las notificaciones en localStorage */ function saveNotifications(userId: string, notifs: Notification[]): void { if (process.client) { const key = getStorageKey(userId) localStorage.setItem(key, JSON.stringify(notifs)) } } /** * Limpia las notificaciones del usuario actual del localStorage */ function clearStorageForUser(userId: string): void { if (process.client) { const key = getStorageKey(userId) localStorage.removeItem(key) } } /** * Sincronización entre pestañas */ function setupStorageSync(userId: string): void { if (process.client) { const key = getStorageKey(userId) const handleStorageChange = (event: StorageEvent) => { if (event.key === key && event.newValue) { try { notifications.value = JSON.parse(event.newValue) } catch (error) { console.error('Error al sincronizar notificaciones:', error) } } } window.addEventListener('storage', handleStorageChange) // Cleanup return () => { window.removeEventListener('storage', handleStorageChange) } } } /** * Composable principal para gestionar notificaciones */ export function useNotifications() { const { user } = useAuthentik() /** * Inicializa el sistema de notificaciones para el usuario actual */ function initialize(): void { if (!user.value || !user.value.uid) { console.warn('No se puede inicializar notificaciones sin usuario autenticado') notifications.value = [] currentUserId = null isInitialized = false return } const userId = user.value.uid // Si cambia el usuario, limpiar y recargar if (currentUserId !== userId) { currentUserId = userId notifications.value = loadNotifications(userId) // Guardar automáticamente cuando cambien las notificaciones watch(notifications, (newNotifications) => { if (currentUserId) { saveNotifications(currentUserId, newNotifications) } }, { deep: true }) // Configurar sincronización entre pestañas setupStorageSync(userId) isInitialized = true } } /** * Agrega una nueva notificación */ function addNotification( notification: Omit ): void { if (!isInitialized) { initialize() } const newNotification: Notification = { ...notification, id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`, timestamp: Date.now(), read: false } notifications.value.unshift(newNotification) } /** * Marca una notificación como leída */ function markAsRead(notificationId: string): void { const notification = notifications.value.find(n => n.id === notificationId) if (notification) { notification.read = true } } /** * Marca todas las notificaciones como leídas */ function markAllAsRead(): void { notifications.value.forEach(n => n.read = true) } /** * Elimina una notificación específica */ function removeNotification(notificationId: string): void { const index = notifications.value.findIndex(n => n.id === notificationId) if (index !== -1) { notifications.value.splice(index, 1) } } /** * Elimina todas las notificaciones */ function clearAll(): void { notifications.value = [] if (currentUserId) { clearStorageForUser(currentUserId) } } /** * Elimina notificaciones antiguas (> RETENTION_DAYS días) */ function cleanupOldNotifications(): void { const cutoffTime = Date.now() - (RETENTION_DAYS * 24 * 60 * 60 * 1000) notifications.value = notifications.value.filter(n => n.timestamp > cutoffTime) } /** * Obtiene notificaciones filtradas */ function getFilteredNotifications(options?: { type?: Notification['type'][] read?: boolean search?: string limit?: number }): Notification[] { let filtered = [...notifications.value] if (options?.type && options.type.length > 0) { filtered = filtered.filter(n => options.type!.includes(n.type)) } if (options?.read !== undefined) { filtered = filtered.filter(n => n.read === options.read) } if (options?.search) { const searchLower = options.search.toLowerCase() filtered = filtered.filter(n => n.title.toLowerCase().includes(searchLower) || n.message.toLowerCase().includes(searchLower) ) } if (options?.limit) { filtered = filtered.slice(0, options.limit) } return filtered } // Auto-inicializar cuando haya usuario if (user.value && user.value.uid) { initialize() } // Re-inicializar si cambia el usuario watch(() => user.value?.uid, (newUid) => { if (newUid) { initialize() } else { notifications.value = [] currentUserId = null isInitialized = false } }) return { notifications: computed(() => notifications.value), unreadCount, addNotification, markAsRead, markAllAsRead, removeNotification, clearAll, cleanupOldNotifications, getFilteredNotifications, initialize } }