# Prompt de Implementación: Integración de Notificaciones en el Frontend ## Resumen Ejecutivo Se ha implementado una nueva **API de Notificaciones** en el backend que se integra automáticamente cuando el estado de los reportes cambia. Tu tarea es implementar la interfaz de usuario para que los usuarios puedan ver, gestionar y ser notificados de los cambios en sus reportes. --- ## 1. Arquitectura Backend - Información de Referencia ### Endpoints de la API de Notificaciones (Puerto 8002) #### 1.1 Crear Notificación (Interno) ``` POST /notifications/ Content-Type: application/json { "id_usuario": 1, "id_reporte": "uuid-del-reporte", "message": "¡Tu reporte #uuid ha sido resuelto!" } Response: 200 OK { "id_notificacion": "ObjectId", "id_usuario": 1, "id_reporte": "uuid-del-reporte", "message": "¡Tu reporte #uuid ha sido resuelto!", "fecha": "2024-04-29T15:30:00Z", "read": false } ``` #### 1.2 Obtener Notificaciones de un Usuario (IMPORTANTE) ``` GET /notifications/{user_id}?limit=50&offset=0 Authorization: Bearer {jwt_token} Query Parameters: - limit: int (1-100, default: 50) - Número máximo de notificaciones - offset: int (default: 0) - Paginación Response: 200 OK { "total": 150, "unread_count": 5, "notifications": [ { "id_notificacion": "ObjectId_1", "id_usuario": 1, "id_reporte": "uuid-reporte-1", "message": "¡Tu reporte ha sido resuelto!", "fecha": "2024-04-29T15:30:00Z", "read": false }, { "id_notificacion": "ObjectId_2", "id_usuario": 1, "id_reporte": "uuid-reporte-2", "message": "Tu reporte fue marcado como no resuelto.", "fecha": "2024-04-29T14:20:00Z", "read": true } ] } ``` #### 1.3 Obtener Conteo de No Leídas ``` GET /notifications/{user_id}/unread-count Authorization: Bearer {jwt_token} Response: 200 OK { "unread_count": 5 } ``` #### 1.4 Marcar como Leída ``` PUT /notifications/{notification_id}/read Authorization: Bearer {jwt_token} Response: 200 OK { "id_notificacion": "ObjectId", "id_usuario": 1, "id_reporte": "uuid", "message": "¡Tu reporte ha sido resuelto!", "fecha": "2024-04-29T15:30:00Z", "read": true } ``` #### 1.5 Marcar Todas como Leídas ``` PUT /notifications/{user_id}/read-all Authorization: Bearer {jwt_token} Response: 200 OK { "message": "All notifications marked as read", "updated_count": 5 } ``` #### 1.6 Eliminar Notificación ``` DELETE /notifications/{notification_id} Authorization: Bearer {jwt_token} Response: 200 OK { "message": "Notification deleted successfully" } ``` #### 1.7 Health Check ``` GET / Response: 200 OK { "status": "ok", "service": "Notificaciones Microservice" } ``` --- ## 2. Requerimientos Funcionales del Frontend ### 2.1 Panel de Notificaciones **Ubicación:** Icono de campana (🔔) en la barra de navegación/header **Funcionalidades:** - Mostrar un badge con el número de notificaciones no leídas - Mostrar un dropdown/modal al hacer click en el icono - Listar las últimas 10-15 notificaciones con scroll infinito - Mostrar fecha relativa (ej: "hace 2 minutos", "hace 1 hora") - Indicar visualmente cuáles están leídas y cuáles no - Cada notificación debe tener: - Mensaje de estado - ID del reporte (clickeable para ir al detalle del reporte) - Fecha/hora - Botón para marcar como leída/no leída - Botón para eliminar ### 2.2 Sincronización en Tiempo Real **Opciones (elige una o combina):** **Opción A: Polling (Más Simple)** - Hacer request a `/notifications/{user_id}` cada 30 segundos - Comparar con estado anterior - Mostrar toast/notificación visual si hay nuevas **Opción B: WebSocket (Recomendado)** - Conectar WebSocket a backend (requiere implementar en backend) - Recibir eventos en tiempo real - Badge se actualiza instantáneamente **Opción C: Híbrido (Recomendado)** - WebSocket para actualizaciones en tiempo real - Polling cada 5 minutos como fallback - Sincronización al abrir la app ### 2.3 Notificaciones Visuales **Toast Notifications:** - Mostrar toast en la esquina superior derecha cuando llega una notificación - Ejemplo: ``` ✓ ¡Tu reporte ha sido resuelto! [Ver Reporte] [Cerrar] ``` - Auto-cerrar después de 5 segundos - Permitir cerrar manualmente - Solo mostrar en tiempo real (no históricos) **Sound Notification (Opcional):** - Reproducir sonido discreto cuando llega notificación nueva - Respetar configuración de silencio del dispositivo ### 2.4 Página Dedicada de Notificaciones **Ruta:** `/notifications` o similar **Características:** - Vista completa de todas las notificaciones del usuario - Filtros: - Todas - No leídas - Resueltas (estado: resuelto) - No resueltas (estado: no resuelto) - En proceso - Sorting: - Por fecha (descendente por defecto) - Más antiguas - Bulk actions: - Marcar todas como leídas - Eliminar seleccionadas - Marcar seleccionadas como leídas - Paginación (implementar scroll infinito o paginación tradicional) - Búsqueda por ID de reporte o mensaje --- ## 3. Integración con Componentes Existentes ### 3.1 Detalle del Reporte Cuando el usuario ve un reporte, mostrar un histórico/timeline de cambios: ``` Timeline de Cambios: ├── 2024-04-29 15:30 - Reporte resuelto ├── 2024-04-29 14:20 - Reporte marcado como no resuelto └── 2024-04-29 10:00 - Reporte creado (estado: en proceso) ``` **Nota:** Esto requeriría agregar un endpoint en backend para obtener el histórico de cambios por reporte. Alternativa: mostrar las notificaciones del usuario filtradas por `id_reporte`. ### 3.2 Perfil del Usuario En la página de perfil/configuración, agregar sección de "Preferencias de Notificaciones": - [ ] Recibir notificaciones de reportes - [ ] Notificación de sonido - [ ] Notificaciones al escribir - [ ] Resumen diario/semanal (opcional) --- ## 4. Especificaciones Técnicas ### 4.1 Configuración Base ```javascript // config.ts o similar const API_BASE_URL = 'http://localhost:8002'; // Notificaciones API const NOTIFICATIONS_POLL_INTERVAL = 30000; // 30 segundos const NOTIFICATIONS_TOAST_DURATION = 5000; // 5 segundos ``` ### 4.2 Service/Hook para Notificaciones ```typescript // Ejemplo con React interface Notification { id_notificacion: string; id_usuario: number; id_reporte: string; message: string; fecha: Date; read: boolean; } interface NotificationsState { notifications: Notification[]; unreadCount: number; loading: boolean; error: string | null; } // Hook personalizado recomendado useNotifications(userId: number) // Métodos del hook: - fetchNotifications(limit?: number, offset?: number) - getUnreadCount() - markAsRead(notificationId: string) - markAllAsRead(userId: number) - deleteNotification(notificationId: string) - subscribeToUpdates() // Para WebSocket ``` ### 4.3 Estados y Transiciones ``` Estados del Reporte → Mensaje de Notificación: ───────────────────────────────────────── en proceso → resuelto → "¡Tu reporte #xxx ha sido resuelto!" en proceso → no resuelto → "Tu reporte #xxx fue marcado como no resuelto." no resuelto → resuelto → "¡Tu reporte #xxx ha sido resuelto!" resuelto → en proceso → "Tu reporte #xxx ha sido reabierto." resuelto → no resuelto → "Tu reporte #xxx fue reabierto como no resuelto." ``` --- ## 5. UI/UX Recomendaciones ### 5.1 Estilos y Componentes - **Badge de no leídas:** Rojo/Naranja, número en blanco, esquina superior derecha del icono - **Notificación no leída:** Fondo ligeramente coloreado, indicador visual (punto azul, negrita) - **Notificación leída:** Fondo normal, texto gris - **Hover state:** Fondo gris claro, mostrar botones de acción - **Empty state:** "No tienes notificaciones" con icono de campana vacía ### 5.2 Animaciones (Opcional pero Recomendado) - Badge pulsante cuando hay nuevas notificaciones - Slide-in del toast desde la esquina - Fade cuando se marca como leída - Transición suave del dropdown ### 5.3 Diseño Responsivo - Mobile: Dropdown debe ser full-width o modal - Tablet: Dropdown con ancho fijo - Desktop: Dropdown normal --- ## 6. Flujo Completo de Usuario 1. **Usuario inicia sesión** → Cargar notificaciones existentes 2. **Usuario navega por la app** → Polling/WebSocket actualiza notificaciones 3. **Un reporte cambia de estado en backend** → - Evento enviado a RabbitMQ - Consumidor de notificaciones lo procesa - Notificación almacenada en BD - WebSocket notifica al cliente (o lo ve en siguiente polling) 4. **Cliente recibe actualización** → - Badge se actualiza con nuevo conteo - Toast se muestra (opcional) - Usuario puede ver en el dropdown/página de notificaciones 5. **Usuario hace click en notificación** → - Va al detalle del reporte - Marca como leída automáticamente (opcional) 6. **Usuario gestiona notificaciones** → - Puede marcar como leída - Puede eliminar - Puede marcar todas como leídas --- ## 7. Consideraciones de Seguridad - **Autenticación:** Todas las requests deben incluir JWT token - **Autorización:** Un usuario solo puede ver SUS propias notificaciones - Backend valida que `user_id` en token === `user_id` en path - **Rate Limiting:** Considerar rate limiting en polling (máx 1 request cada 10s) - **Validación:** Validar que `notification_id` pertenece al usuario antes de actualizar --- ## 8. Testing Recomendado ### 8.1 Unit Tests - [ ] Hook `useNotifications` retorna estado correcto - [ ] Funciones de formateo de fecha relativa - [ ] Lógica de filtrado y sorting ### 8.2 Integration Tests - [ ] Polling se ejecuta correctamente cada 30s - [ ] Marcar como leída actualiza UI - [ ] Eliminar notificación la remueve de la lista - [ ] Badge se actualiza cuando llega notificación ### 8.3 E2E Tests - [ ] Usuario ve notificación cuando reporte cambia de estado - [ ] Usuario puede marcar todas como leídas - [ ] Página de notificaciones carga correctamente --- ## 9. Dependencias Frontend Recomendadas ```json { "dependencies": { "axios": "^1.x.x", // Para requests HTTP "framer-motion": "^10.x.x", // Para animaciones "react-query": "^3.x.x", // Para caching y sincronización "react-hot-toast": "^2.x.x", // Para toast notifications "date-fns": "^2.x.x", // Para formateo de fechas "react-infinite-scroll-component": "^6.x.x" // Para scroll infinito (opcional) } } ``` --- ## 10. Roadmap Futuro (Phase 2) - [ ] Notificaciones push en navegador (Service Workers) - [ ] Notificaciones por email - [ ] Histórico completo de cambios de estado por reporte - [ ] Suscripción a notificaciones de otros reportes - [ ] Sistema de preferencias granular de notificaciones - [ ] Archivado de notificaciones (en lugar de eliminar) - [ ] Notificaciones de actividad comunitaria --- ## 11. Endpoints de Referencia Backend Completos ### Base URL ``` Desarrollo: http://localhost:8002 Producción: https://api.voxpopuli.com/notifications ``` ### Headers Requeridos ``` Authorization: Bearer {jwt_token} Content-Type: application/json ``` ### Códigos de Respuesta HTTP - `200 OK` - Operación exitosa - `202 Accepted` - Solicitud aceptada pero en proceso - `400 Bad Request` - Parámetros inválidos - `401 Unauthorized` - Token inválido/expirado - `404 Not Found` - Recurso no encontrado - `500 Internal Server Error` - Error del servidor --- ## 12. Ejemplo de Implementación React ```typescript // hooks/useNotifications.ts import { useEffect, useState, useCallback } from 'react'; import { useQuery, useMutation } from 'react-query'; import axios from 'axios'; interface Notification { id_notificacion: string; id_usuario: number; id_reporte: string; message: string; fecha: Date; read: boolean; } const API_BASE = 'http://localhost:8002'; export function useNotifications(userId: number) { const [unreadCount, setUnreadCount] = useState(0); // Fetch notificaciones const { data: response, refetch } = useQuery( ['notifications', userId], () => axios.get( `${API_BASE}/notifications/${userId}?limit=50&offset=0`, { headers: { Authorization: `Bearer ${getToken()}` } } ), { refetchInterval: 30000 } // Poll cada 30s ); // Marcar como leída const markAsReadMutation = useMutation( (notificationId: string) => axios.put( `${API_BASE}/notifications/${notificationId}/read`, {}, { headers: { Authorization: `Bearer ${getToken()}` } } ), { onSuccess: () => refetch() } ); // Marcar todas como leídas const markAllAsReadMutation = useMutation( () => axios.put( `${API_BASE}/notifications/${userId}/read-all`, {}, { headers: { Authorization: `Bearer ${getToken()}` } } ), { onSuccess: () => refetch() } ); // Eliminar notificación const deleteNotificationMutation = useMutation( (notificationId: string) => axios.delete( `${API_BASE}/notifications/${notificationId}`, { headers: { Authorization: `Bearer ${getToken()}` } } ), { onSuccess: () => refetch() } ); useEffect(() => { if (response?.data?.unread_count !== undefined) { setUnreadCount(response.data.unread_count); } }, [response]); return { notifications: response?.data?.notifications || [], unreadCount, loading: false, markAsRead: (id: string) => markAsReadMutation.mutate(id), markAllAsRead: () => markAllAsReadMutation.mutate(), deleteNotification: (id: string) => deleteNotificationMutation.mutate(id), }; } // components/NotificationBell.tsx import { useNotifications } from '@/hooks/useNotifications'; import { formatDistanceToNow } from 'date-fns'; import { es } from 'date-fns/locale'; export function NotificationBell({ userId }: { userId: number }) { const { notifications, unreadCount, markAsRead, deleteNotification } = useNotifications(userId); const [isOpen, setIsOpen] = useState(false); return (
{n.message}
Reporte: {n.id_reporte}
{formatDistanceToNow(new Date(n.fecha), { locale: es, addSuffix: true })}