617
FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md
Normal file
617
FRONTEND_NOTIFICATIONS_IMPLEMENTATION.md
Normal file
@@ -0,0 +1,617 @@
|
||||
# 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 (
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setIsOpen(!isOpen)}
|
||||
className="relative p-2 rounded-full hover:bg-gray-100"
|
||||
>
|
||||
🔔
|
||||
{unreadCount > 0 && (
|
||||
<span className="absolute top-0 right-0 bg-red-500 text-white
|
||||
text-xs rounded-full w-5 h-5 flex items-center
|
||||
justify-center animate-pulse">
|
||||
{unreadCount}
|
||||
</span>
|
||||
)}
|
||||
</button>
|
||||
|
||||
{isOpen && (
|
||||
<div className="absolute right-0 mt-2 w-80 bg-white rounded-lg
|
||||
shadow-lg border border-gray-200">
|
||||
<div className="p-4 border-b border-gray-200 font-semibold">
|
||||
Notificaciones
|
||||
</div>
|
||||
<div className="max-h-96 overflow-y-auto">
|
||||
{notifications.length === 0 ? (
|
||||
<div className="p-4 text-center text-gray-500">
|
||||
No hay notificaciones
|
||||
</div>
|
||||
) : (
|
||||
notifications.map((n) => (
|
||||
<div
|
||||
key={n.id_notificacion}
|
||||
className={`p-4 border-b hover:bg-gray-50 ${
|
||||
!n.read ? 'bg-blue-50' : ''
|
||||
}`}
|
||||
>
|
||||
<p className={!n.read ? 'font-semibold' : ''}>
|
||||
{n.message}
|
||||
</p>
|
||||
<p className="text-xs text-gray-500 mt-1">
|
||||
Reporte: {n.id_reporte}
|
||||
</p>
|
||||
<p className="text-xs text-gray-400">
|
||||
{formatDistanceToNow(new Date(n.fecha), {
|
||||
locale: es,
|
||||
addSuffix: true
|
||||
})}
|
||||
</p>
|
||||
<div className="flex gap-2 mt-2">
|
||||
{!n.read && (
|
||||
<button
|
||||
onClick={() => markAsRead(n.id_notificacion)}
|
||||
className="text-xs text-blue-600 hover:underline"
|
||||
>
|
||||
Marcar como leída
|
||||
</button>
|
||||
)}
|
||||
<button
|
||||
onClick={() => deleteNotification(n.id_notificacion)}
|
||||
className="text-xs text-red-600 hover:underline ml-auto"
|
||||
>
|
||||
Eliminar
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
))
|
||||
)}
|
||||
</div>
|
||||
<div className="p-3 border-t border-gray-200 text-center">
|
||||
<a href="/notifications" className="text-blue-600 text-sm hover:underline">
|
||||
Ver todas las notificaciones
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 13. Preguntas Frecuentes
|
||||
|
||||
**P: ¿Debo implementar WebSocket o polling está bien?**
|
||||
R: Polling cada 30s está bien para MVP. WebSocket es mejor para experiencia en tiempo real pero requiere implementación en backend.
|
||||
|
||||
**P: ¿Cómo manejo notificaciones si el usuario está offline?**
|
||||
R: Todas las notificaciones se guardan en BD. Cuando reconecte, vera el histórico completo.
|
||||
|
||||
**P: ¿Debo eliminar notificaciones antiguas?**
|
||||
R: Por ahora, guardarlas todas. Puedes agregar lógica de archivado después de 30 días.
|
||||
|
||||
**P: ¿Puedo mostrar solo las últimas 10 notificaciones?**
|
||||
R: Sí, el endpoint soporta `limit=10`. Usa scroll infinito para cargar más.
|
||||
|
||||
---
|
||||
|
||||
## 14. Checklist de Implementación
|
||||
|
||||
- [ ] Servicio HTTP creado para llamadas a `/notifications/{user_id}`
|
||||
- [ ] Hook `useNotifications` implementado
|
||||
- [ ] Componente de icono de campana con badge
|
||||
- [ ] Dropdown de últimas notificaciones
|
||||
- [ ] Página dedicada `/notifications`
|
||||
- [ ] Toast notifications en tiempo real
|
||||
- [ ] Funcionalidad de marcar como leída
|
||||
- [ ] Funcionalidad de eliminar notificaciones
|
||||
- [ ] Polling o WebSocket implementado
|
||||
- [ ] Tests unitarios para hooks
|
||||
- [ ] Tests de integración end-to-end
|
||||
- [ ] Documentación de componentes
|
||||
- [ ] Respuesta mobile optimizada
|
||||
|
||||
---
|
||||
|
||||
**Última actualización:** 29 de Abril de 2024
|
||||
**Versión API:** 1.0.0
|
||||
**Autor:** VoxPopuli Development Team
|
||||
Reference in New Issue
Block a user