# Sistema de Notificaciones

Sistema de notificaciones con historial persistente por usuario, implementado con localStorage y separación automática basada en el UID de Authentik.

## Tabla de Contenidos

- [Características Principales](#características-principales)
- [Arquitectura del Sistema](#arquitectura-del-sistema)
- [Componentes Principales](#componentes-principales)
- [Uso Básico](#uso-básico)
- [API del Backend](#api-del-backend)
- [Estructura de Datos](#estructura-de-datos)
- [Separación por Usuario](#separación-por-usuario)
- [Limpieza Automática](#limpieza-automática)
- [Sincronización entre Pestañas](#sincronización-entre-pestañas)

---

## Características Principales

✅ **Historial separado por usuario**: Cada usuario tiene su propio historial usando su UID de Authentik como clave

✅ **Persistencia local**: Las notificaciones se guardan en localStorage del navegador

✅ **Guardado automático de toasts**: Todos los toasts se guardan automáticamente en el historial

✅ **Auto-limpieza**: Notificaciones mayores a 30 días se eliminan automáticamente

✅ **Sincronización entre pestañas**: Los cambios se reflejan en tiempo real en todas las pestañas

✅ **Filtrado y búsqueda**: Filtrar por tipo (info, warning, success, error) y buscar por texto

✅ **Múltiples orígenes**: Soporta notificaciones desde toasts, backend y manuales

---

## Arquitectura del Sistema

```
┌─────────────────────────────────────────────────────────────┐
│                     Aplicación Nuxt                         │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  ┌──────────────────────────────────────────────────┐      │
│  │              useNotifications()                   │      │
│  │  - Gestión de notificaciones                     │      │
│  │  - Filtrado y búsqueda                          │      │
│  │  - Limpieza automática                          │      │
│  └──────────────────────────────────────────────────┘      │
│                        ▲                                    │
│                        │                                    │
│  ┌──────────────────────────────────────────────────┐      │
│  │               useToast()                         │      │
│  │  - Wrapper que intercepta toasts                │      │
│  │  - Guarda automáticamente en historial          │      │
│  └──────────────────────────────────────────────────┘      │
│                        ▲                                    │
│                        │                                    │
│  ┌──────────────────────────────────────────────────┐      │
│  │          API Endpoints (Backend)                 │      │
│  │  - /api/notifications/send                      │      │
│  │  - /api/notifications/list                      │      │
│  └──────────────────────────────────────────────────┘      │
│                                                             │
└─────────────────────────────────────────────────────────────┘
                        │
                        ▼
            ┌────────────────────────┐
            │   localStorage         │
            │  notifications-{uid}   │
            └────────────────────────┘
```

---

## Componentes Principales

### 1. Composable `useNotifications()`

**Ubicación:** `app/composables/useNotifications.ts`

El composable principal que gestiona todo el sistema de notificaciones.

**Funciones disponibles:**

```typescript
const {
  notifications,        // Ref<Notification[]> - Array reactivo de todas las notificaciones
  unreadCount,         // ComputedRef<number> - Contador de notificaciones no leídas
  addNotification,     // Agregar una notificación al historial
  markAsRead,          // Marcar una notificación como leída
  markAllAsRead,       // Marcar todas como leídas
  removeNotification,  // Eliminar una notificación específica
  clearAll,            // Eliminar todas las notificaciones
  cleanupOldNotifications, // Eliminar notificaciones > 30 días
  getFilteredNotifications, // Obtener notificaciones filtradas
  initialize           // Inicializar el sistema (se llama automáticamente)
} = useNotifications()
```

### 2. Wrapper `useToast()`

**Ubicación:** `app/composables/useToast.ts`

Wrapper transparente que intercepta todos los toasts de Nuxt UI y los guarda automáticamente en el historial.

**Uso (sin cambios en código existente):**

```typescript
const toast = useToast()

// Funciona exactamente igual que antes, pero ahora se guarda en el historial
toast.add({
  title: 'Operación exitosa',
  description: 'Los datos se guardaron correctamente',
  color: 'success'
})
```

### 3. API del Backend

**Ubicación:** `server/api/notifications/`

Endpoints para crear y obtener notificaciones desde el servidor.

---

## Uso Básico

### Agregar una notificación manualmente

```typescript
const { addNotification } = useNotifications()

addNotification({
  type: 'success',
  title: 'Proceso completado',
  message: 'El reporte se generó correctamente',
  origin: 'manual',
  metadata: {
    reportId: 123,
    timestamp: new Date().toISOString()
  }
})
```

### Marcar notificación como leída

```typescript
const { markAsRead } = useNotifications()

markAsRead('notification-id-123')
```

### Obtener notificaciones filtradas

```typescript
const { getFilteredNotifications } = useNotifications()

// Solo notificaciones de error no leídas
const errorNotifications = getFilteredNotifications({
  type: ['error'],
  read: false
})

// Buscar por texto
const searchResults = getFilteredNotifications({
  search: 'reporte'
})

// Limitar resultados
const latest = getFilteredNotifications({
  limit: 10
})
```

---

## API del Backend

### Enviar notificación desde el servidor

**Endpoint:** `POST /api/notifications/send`

```typescript
// Desde cualquier endpoint del servidor
const response = await $fetch('/api/notifications/send', {
  method: 'POST',
  body: {
    targetUserId: 'uid-del-usuario-destino',
    type: 'info',
    title: 'Nueva actualización',
    message: 'Hay una nueva versión disponible',
    metadata: {
      version: '2.0.0',
      changelog: 'https://...'
    }
  }
})
```

**Parámetros:**

- `targetUserId` (string, requerido): UID del usuario destino
- `type` (string, requerido): Tipo de notificación (`'info'`, `'warning'`, `'success'`, `'error'`)
- `title` (string, requerido): Título de la notificación
- `message` (string, opcional): Mensaje descriptivo
- `metadata` (object, opcional): Datos adicionales

### Obtener notificaciones pendientes

**Endpoint:** `GET /api/notifications/list`

```typescript
// Desde el cliente (automático)
const { data } = await $fetch('/api/notifications/list')

console.log(data.notifications) // Array de notificaciones pendientes
console.log(data.count)         // Número de notificaciones
```

> **Nota:** Este endpoint usa una cola en memoria. En producción, considera usar Redis o una base de datos para persistencia.

---

## Estructura de Datos

### Interfaz `Notification`

```typescript
interface Notification {
  id: string                    // ID único generado automáticamente
  type: 'info' | 'warning' | 'success' | 'error'
  title: string                 // Título de la notificación
  message: string               // Mensaje descriptivo
  timestamp: number             // Timestamp en milisegundos
  read: boolean                 // Estado de lectura
  origin: 'toast' | 'backend' | 'manual' // Origen de la notificación
  metadata?: Record<string, any> // Datos adicionales opcionales
}
```

### Ejemplo de notificación guardada

```json
{
  "id": "1730328465123-a3f8d9e2b",
  "type": "success",
  "title": "Tema guardado",
  "message": "Los cambios se aplicaron correctamente",
  "timestamp": 1730328465123,
  "read": false,
  "origin": "toast",
  "metadata": {
    "icon": "i-lucide-check",
    "color": "green",
    "hasActions": false
  }
}
```

---

## Separación por Usuario

El sistema utiliza el **UID de Authentik** como clave única para separar las notificaciones de cada usuario.

### Cómo funciona

1. **Autenticación con Authentik:**
   - El usuario se autentica mediante Authentik Proxy Outpost
   - El header `x-authentik-uid` contiene el UID único del usuario

2. **Clave de localStorage:**
   ```typescript
   const storageKey = `notifications-${user.uid}`
   // Ejemplo: "notifications-a3f8d9e2b4c1a5d6"
   ```

3. **Cambio de usuario:**
   - Al detectar un cambio en el UID, el sistema automáticamente:
     - Guarda las notificaciones del usuario anterior
     - Carga las notificaciones del nuevo usuario
     - Limpia el estado en memoria

### Ejemplo práctico

```
Usuario A (UID: abc123):
  localStorage["notifications-abc123"] = [notif1, notif2, notif3]

Usuario B (UID: xyz789):
  localStorage["notifications-xyz789"] = [notif4, notif5]

// Cada usuario solo ve sus propias notificaciones
// Incluso si usan el mismo navegador en la misma máquina
```

---

## Limpieza Automática

El sistema elimina automáticamente las notificaciones antiguas para evitar acumulación excesiva.

### Configuración

```typescript
// En useNotifications.ts
const RETENTION_DAYS = 30 // Días de retención
```

### Cuándo se ejecuta

1. **Al iniciar la aplicación:**
   - Se ejecuta `cleanupOldNotifications()` en `app.vue`
   - Elimina notificaciones mayores a 30 días

2. **Al cargar notificaciones:**
   - Las notificaciones antiguas se filtran automáticamente
   - Solo se cargan las recientes

### Modificar el período de retención

Si necesitas cambiar el período de retención, modifica la constante:

```typescript
// app/composables/useNotifications.ts
const RETENTION_DAYS = 60 // Cambiar a 60 días
```

---

## Sincronización entre Pestañas

El sistema mantiene sincronizadas todas las pestañas del navegador usando el evento `storage`.

### Cómo funciona

```typescript
// Cuando cambia el localStorage en una pestaña...
window.addEventListener('storage', (event) => {
  if (event.key === storageKey && event.newValue) {
    // ...todas las demás pestañas se actualizan automáticamente
    notifications.value = JSON.parse(event.newValue)
  }
})
```

### Casos de uso

1. **Usuario agrega notificación en Pestaña A:**
   - Se guarda en localStorage
   - Pestaña B y C reciben el evento `storage`
   - Todas las pestañas muestran la nueva notificación

2. **Usuario elimina notificación en Pestaña B:**
   - Se actualiza localStorage
   - Pestaña A y C se sincronizan automáticamente

---

## Página de Notificaciones

**Ubicación:** `app/pages/notifications.vue`

### Características

- ✅ Lista completa de notificaciones del usuario actual
- ✅ Búsqueda por texto (título y mensaje)
- ✅ Filtros por tipo (info, warning, success, error)
- ✅ Marcar como leídas (individual o todas)
- ✅ Eliminar (individual o todas)
- ✅ Indicadores de tiempo relativo ("hace 2 horas", "hace 3 días")
- ✅ Badges de origen (toast, backend, manual)
- ✅ Estados vacíos informativos

### Badge en Sidebar

**Ubicación:** `app/components/app/AppSidebar.vue`

- Muestra el número de notificaciones no leídas
- Se oculta automáticamente cuando no hay notificaciones
- Indicador visual (punto rojo) cuando hay notificaciones pendientes

---

## Inicialización del Sistema

**Ubicación:** `app/app.vue`

```typescript
// Se inicializa automáticamente al montar la aplicación
const { initialize, cleanupOldNotifications } = useNotifications()

onMounted(() => {
  initialize()                  // Inicializar el sistema
  cleanupOldNotifications()     // Limpiar notificaciones antiguas
})
```

### Flujo de inicialización

1. **App se monta** → `onMounted()` se ejecuta
2. **Usuario autenticado** → Se obtiene el UID de Authentik
3. **Carga desde localStorage** → Se cargan notificaciones del usuario
4. **Limpieza automática** → Se eliminan notificaciones > 30 días
5. **Configurar sincronización** → Se activa el listener de `storage`
6. **Sistema listo** → El usuario puede ver su historial

---

## Casos de Uso

### 1. Notificación de operación exitosa

```typescript
// Desde cualquier componente
const toast = useToast()

async function saveData() {
  try {
    await $fetch('/api/save', { method: 'POST', body: data })

    // Se guarda automáticamente en el historial
    toast.add({
      title: 'Datos guardados',
      description: 'Los cambios se guardaron correctamente',
      color: 'success'
    })
  } catch (error) {
    toast.add({
      title: 'Error al guardar',
      description: error.message,
      color: 'error'
    })
  }
}
```

### 2. Notificación desde el backend

```typescript
// server/api/generate-report.post.ts
export default defineEventHandler(async (event) => {
  const headers = getHeaders(event)
  const userId = headers['x-authentik-uid']

  // Generar el reporte...
  const report = await generateReport()

  // Notificar al usuario
  await $fetch('/api/notifications/send', {
    method: 'POST',
    body: {
      targetUserId: userId,
      type: 'success',
      title: 'Reporte generado',
      message: `El reporte ${report.name} está listo para descargar`,
      metadata: {
        reportId: report.id,
        downloadUrl: report.url
      }
    }
  })

  return { success: true, report }
})
```

### 3. Consultar historial programáticamente

```typescript
const { getFilteredNotifications } = useNotifications()

// Obtener últimas 5 notificaciones de error
const recentErrors = getFilteredNotifications({
  type: ['error'],
  limit: 5
})

// Mostrar en consola
recentErrors.forEach(notif => {
  console.error(`[${notif.timestamp}] ${notif.title}: ${notif.message}`)
})
```

---

## Mejoras Futuras

### Posibles extensiones del sistema:

1. **Persistencia en base de datos:**
   - Migrar de localStorage a Supabase
   - Acceso desde cualquier dispositivo
   - Historial ilimitado

2. **Notificaciones push:**
   - Integración con Web Push API
   - Notificaciones incluso cuando la app está cerrada

3. **Categorías personalizadas:**
   - Permitir al usuario crear categorías
   - Filtros más granulares

4. **Acciones en notificaciones:**
   - Botones de acción (Descargar, Ver más, Abrir)
   - Ejecutar código al hacer clic

5. **Notificaciones programadas:**
   - Enviar notificaciones en fecha/hora específica
   - Recordatorios recurrentes

6. **Sistema de preferencias:**
   - Configurar qué notificaciones recibir
   - Silenciar notificaciones por tipo
   - Modo "No molestar"

---

## Preguntas Frecuentes

### ¿Las notificaciones se pierden al cerrar el navegador?

No, las notificaciones se guardan en localStorage y persisten entre sesiones.

### ¿Qué pasa si dos usuarios usan el mismo navegador?

Cada usuario tiene su propio historial separado por su UID de Authentik. Al cambiar de usuario (logout/login), el sistema carga automáticamente las notificaciones del nuevo usuario.

### ¿Cuántas notificaciones se pueden guardar?

El límite lo impone localStorage (aproximadamente 5-10 MB dependiendo del navegador). Con la limpieza automática de 30 días, es poco probable alcanzar este límite.

### ¿Se pueden recuperar notificaciones eliminadas?

No, una vez eliminadas del localStorage, no se pueden recuperar. Considera implementar persistencia en base de datos si necesitas esta funcionalidad.

### ¿Las notificaciones son privadas?

Sí, están almacenadas localmente en el navegador del usuario. Solo son accesibles por el usuario que las recibió y en el dispositivo donde se guardaron.

---

## Soporte

Para reportar problemas o sugerencias sobre el sistema de notificaciones:

1. Revisar los logs en la consola del navegador
2. Verificar que el UID de Authentik esté presente
3. Comprobar el localStorage en DevTools: `localStorage.getItem('notifications-{uid}')`
4. Crear un issue en el repositorio con los detalles

---

**Última actualización:** 2025-10-30
**Versión:** 1.0.0
