- Create TASK-2026-01-25-NOTIFICACIONES-COMPLETAS with full CAPVED docs - Update DATABASE_INVENTORY with auth.notifications, auth.user_push_tokens, investment.distribution_history, investment.distribution_runs tables - Update BACKEND_INVENTORY with push-token endpoints, firebase.client, and unit tests - Update FRONTEND_INVENTORY with notification components, store, service - Update MASTER_INVENTORY with updated totals - Update _INDEX.yml with new task entry Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
192 lines
5.0 KiB
Markdown
192 lines
5.0 KiB
Markdown
# 05-EJECUCION - Sistema de Notificaciones Completo
|
|
|
|
## Resumen de Ejecucion
|
|
|
|
**Fecha**: 2026-01-25
|
|
**Agente**: claude-opus-4.5
|
|
**Duracion**: ~45 minutos
|
|
|
|
## 1. DDL Creados
|
|
|
|
### auth/tables/11-notifications.sql
|
|
```sql
|
|
CREATE TABLE auth.notifications (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
|
type VARCHAR(50) NOT NULL,
|
|
title VARCHAR(255) NOT NULL,
|
|
message TEXT NOT NULL,
|
|
priority VARCHAR(20) DEFAULT 'normal',
|
|
data JSONB,
|
|
action_url VARCHAR(500),
|
|
icon_type VARCHAR(20) DEFAULT 'info',
|
|
channels TEXT[] DEFAULT '{}',
|
|
is_read BOOLEAN DEFAULT FALSE,
|
|
read_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
### auth/tables/12-user_push_tokens.sql
|
|
```sql
|
|
CREATE TABLE auth.user_push_tokens (
|
|
id UUID PRIMARY KEY,
|
|
user_id UUID NOT NULL REFERENCES auth.users(id),
|
|
token TEXT NOT NULL UNIQUE,
|
|
platform VARCHAR(20) NOT NULL, -- web, ios, android
|
|
device_info JSONB,
|
|
is_active BOOLEAN DEFAULT TRUE,
|
|
last_used_at TIMESTAMPTZ,
|
|
created_at TIMESTAMPTZ,
|
|
updated_at TIMESTAMPTZ
|
|
);
|
|
```
|
|
|
|
### investment/tables/08-distribution_history.sql
|
|
```sql
|
|
CREATE TABLE investment.distribution_history (
|
|
id UUID PRIMARY KEY,
|
|
account_id UUID NOT NULL REFERENCES investment.accounts(id),
|
|
product_id UUID NOT NULL REFERENCES investment.products(id),
|
|
distribution_date DATE NOT NULL,
|
|
gross_amount DECIMAL(18,2) NOT NULL,
|
|
fee_amount DECIMAL(18,2) DEFAULT 0,
|
|
net_amount DECIMAL(18,2) NOT NULL,
|
|
balance_before DECIMAL(18,2) NOT NULL,
|
|
balance_after DECIMAL(18,2) NOT NULL,
|
|
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
CONSTRAINT unique_daily_distribution UNIQUE (account_id, distribution_date)
|
|
);
|
|
```
|
|
|
|
### investment/tables/09-distribution_runs.sql
|
|
```sql
|
|
CREATE TABLE investment.distribution_runs (
|
|
id UUID PRIMARY KEY,
|
|
run_date DATE NOT NULL UNIQUE,
|
|
total_accounts INTEGER DEFAULT 0,
|
|
successful_count INTEGER DEFAULT 0,
|
|
failed_count INTEGER DEFAULT 0,
|
|
total_gross_amount DECIMAL(18,2) DEFAULT 0,
|
|
total_fee_amount DECIMAL(18,2) DEFAULT 0,
|
|
total_net_amount DECIMAL(18,2) DEFAULT 0,
|
|
started_at TIMESTAMPTZ NOT NULL,
|
|
completed_at TIMESTAMPTZ,
|
|
duration_ms INTEGER,
|
|
error_details JSONB,
|
|
created_at TIMESTAMPTZ DEFAULT NOW()
|
|
);
|
|
```
|
|
|
|
## 2. Backend - Firebase Client
|
|
|
|
### firebase.client.ts (~300 lineas)
|
|
```typescript
|
|
class FirebaseClient {
|
|
private app: admin.app.App | null = null;
|
|
|
|
initialize(): void // Init con service account
|
|
|
|
async sendToDevice(token: string, payload: NotificationPayload): Promise<boolean>
|
|
|
|
async sendToMultiple(tokens: string[], payload: NotificationPayload): Promise<SendResult>
|
|
|
|
async deactivateInvalidTokens(tokens: string[]): Promise<void>
|
|
}
|
|
|
|
export const firebaseClient = new FirebaseClient();
|
|
```
|
|
|
|
### config/index.ts (modificado)
|
|
```typescript
|
|
firebase: {
|
|
serviceAccountKey: process.env.FIREBASE_SERVICE_ACCOUNT_KEY || '',
|
|
projectId: process.env.FIREBASE_PROJECT_ID || '',
|
|
},
|
|
webPush: {
|
|
publicKey: process.env.VAPID_PUBLIC_KEY || '',
|
|
privateKey: process.env.VAPID_PRIVATE_KEY || '',
|
|
subject: process.env.VAPID_SUBJECT || 'mailto:admin@orbiquant.io',
|
|
},
|
|
```
|
|
|
|
### notification.controller.ts (modificado)
|
|
```typescript
|
|
// Nuevos endpoints
|
|
export async function registerPushToken(req: AuthenticatedRequest, res: Response)
|
|
export async function removePushToken(req: AuthenticatedRequest, res: Response)
|
|
```
|
|
|
|
### notification.routes.ts (modificado)
|
|
```typescript
|
|
router.post('/push-token', authHandler(registerPushToken));
|
|
router.delete('/push-token', authHandler(removePushToken));
|
|
```
|
|
|
|
## 3. Tests Unitarios
|
|
|
|
### notification.service.spec.ts (~450 lineas)
|
|
- Mocks: db, wsManager, nodemailer, firebaseClient
|
|
- Tests: sendNotification, getUserNotifications, markAsRead, etc.
|
|
|
|
### distribution.job.spec.ts (~380 lineas)
|
|
- Mocks: db, notificationService
|
|
- Tests: run, getActiveAccounts, distributeReturns, etc.
|
|
|
|
## 4. Frontend
|
|
|
|
### notificationStore.ts
|
|
```typescript
|
|
interface NotificationState {
|
|
notifications: Notification[];
|
|
unreadCount: number;
|
|
preferences: NotificationPreferences | null;
|
|
loading: boolean;
|
|
error: string | null;
|
|
|
|
fetchNotifications: (unreadOnly?: boolean) => Promise<void>;
|
|
fetchUnreadCount: () => Promise<void>;
|
|
markAsRead: (id: string) => Promise<void>;
|
|
markAllAsRead: () => Promise<void>;
|
|
addNotification: (notification: Notification) => void;
|
|
initializeWebSocket: () => () => void;
|
|
}
|
|
```
|
|
|
|
### NotificationBell.tsx
|
|
- Badge con unreadCount
|
|
- Click toggle dropdown
|
|
- useEffect para fetch inicial y WebSocket
|
|
|
|
### NotificationDropdown.tsx
|
|
- Lista de ultimas 10 notificaciones
|
|
- Mark all as read button
|
|
- View all link
|
|
|
|
### NotificationItem.tsx
|
|
- Iconos por tipo (success, warning, error, info)
|
|
- Tiempo relativo (hace X minutos)
|
|
- Click marca como leida y navega
|
|
|
|
### NotificationsPage.tsx
|
|
- Lista completa con paginacion
|
|
- Filtros por tipo y estado
|
|
- Seccion de preferencias
|
|
|
|
### MainLayout.tsx (modificado)
|
|
```typescript
|
|
// Antes: Bell icon estatico
|
|
// Despues: <NotificationBell />
|
|
```
|
|
|
|
## Dependencias Instaladas
|
|
|
|
```bash
|
|
npm install firebase-admin web-push
|
|
npm install -D @types/web-push
|
|
```
|
|
|
|
## Estado Final
|
|
|
|
Todos los archivos creados, tests estructurados, commits pusheados.
|