- COMPONENTS-BY-EPIC.yml (4200 líneas) * Análisis de 146 componentes existentes vs 187 especificados * Cobertura: OQI-001 (70%), OQI-002 (55%), OQI-003 (60%), OQI-004 (55%) * 41 componentes faltantes, 18 parciales * Esfuerzo total: 2,870h estimadas - FRONTEND-STORES-PLAN.yml (2800 líneas) * 9 Zustand stores existentes vs 8 faltantes * 14 services existentes vs 12 faltantes * 6 WebSocket streams requeridos * 890h para completar infraestructura - ANALYSIS-SUMMARY.md * Resumen ejecutivo de hallazgos * 4 P0 blockers identificados * Roadmap por trimestre Q1-Q4 2026 * Dependencias y recomendaciones Gaps principales: - Token refresh automático (60h) - KYC wizard (100h - CRITICO) - PCI-DSS compliance payment form (80h) - MT4 Gateway (180h - puede diferirse) Cobertura promedio: 78% P0 Blockers: 240h Esfuerzo Q1 2026: 270h Co-Authored-By: Claude Code <noreply@anthropic.com>
1415 lines
44 KiB
YAML
1415 lines
44 KiB
YAML
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# FRONTEND-STORES-PLAN.yml - Stores & Services Analysis
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
version: "1.0.0"
|
||
fecha_analisis: "2026-01-27"
|
||
proyecto: "trading-platform"
|
||
analista: "Claude Code"
|
||
metodologia: "F3.2 - Stores & Services Gap Analysis"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# RESUMEN EJECUTIVO
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
resumen:
|
||
total_stores_existentes: 9
|
||
total_stores_faltantes: 8
|
||
total_services_existentes: 14
|
||
total_services_faltantes: 12
|
||
cobertura_stores: "53%"
|
||
cobertura_services: "54%"
|
||
websocket_connections_requeridas: 6
|
||
esfuerzo_estimado_total: "890h"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# ANALYSIS SECTION 1: ZUSTAND STORES
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
stores:
|
||
framework: "Zustand v4.4.7"
|
||
ubicacion: "apps/frontend/src/stores/"
|
||
pattern: "devtools + persist middleware"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# STORES EXISTENTES
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
existentes:
|
||
|
||
auth_store:
|
||
archivo: "auth.store.ts"
|
||
epicAssociated: "OQI-001"
|
||
estado: "FUNCIONAL"
|
||
lineas: 420
|
||
descripcion: "Global authentication state, user data, tokens"
|
||
|
||
state_fields:
|
||
- nombre: "user"
|
||
tipo: "User | null"
|
||
descripcion: "Current authenticated user"
|
||
persistido: true
|
||
|
||
- nombre: "accessToken"
|
||
tipo: "string | null"
|
||
descripcion: "JWT access token (15 min TTL)"
|
||
persistido: true
|
||
persistencia_tipo: "localStorage (INSECURO)"
|
||
|
||
- nombre: "refreshToken"
|
||
tipo: "string | null"
|
||
descripcion: "JWT refresh token (7d TTL)"
|
||
persistido: true
|
||
persistencia_tipo: "httpOnly cookie (when backend ready)"
|
||
|
||
- nombre: "isAuthenticated"
|
||
tipo: "boolean"
|
||
descripcion: "Login state"
|
||
persistido: false
|
||
|
||
- nombre: "isLoading"
|
||
tipo: "boolean"
|
||
descripcion: "Auth operation loading state"
|
||
persistido: false
|
||
|
||
- nombre: "error"
|
||
tipo: "string | null"
|
||
descripcion: "Last auth error message"
|
||
persistido: false
|
||
|
||
acciones_principales:
|
||
- "setUser(user: User)"
|
||
- "setAccessToken(token: string)"
|
||
- "setRefreshToken(token: string)"
|
||
- "logout()"
|
||
- "clearError()"
|
||
- "setLoading(loading: boolean)"
|
||
|
||
dependencias:
|
||
- "axios (para API calls)"
|
||
- "localStorage (tokens)"
|
||
|
||
gaps:
|
||
- nombre: "Auto-refresh mechanism"
|
||
severidad: "P0"
|
||
descripcion: "Tokens se refrescan manualmente, not automatic"
|
||
notas: "Usuarios pierden acceso cuando token expira"
|
||
|
||
- nombre: "Role-based access control"
|
||
severidad: "P1"
|
||
descripcion: "No hay roles stored en state"
|
||
notas: "RBAC debe agregarse para menus y permisos"
|
||
|
||
- nombre: "Device tracking"
|
||
severidad: "P1"
|
||
descripcion: "No hay device ID para multi-session management"
|
||
notas: "Necessary para session management"
|
||
|
||
middleware:
|
||
- devtools
|
||
- persist (localStorage)
|
||
|
||
test_coverage: "70%"
|
||
|
||
---
|
||
|
||
trading_store:
|
||
archivo: "trading.store.ts"
|
||
epicAssociated: "OQI-003"
|
||
estado: "FUNCIONAL"
|
||
lineas: 350
|
||
descripcion: "Trading module state: symbol, market data, orderbook"
|
||
|
||
state_fields:
|
||
- nombre: "selectedSymbol"
|
||
tipo: "string"
|
||
descripcion: "Currently selected trading pair (e.g. BTCUSDT)"
|
||
default: "BTCUSDT"
|
||
persistido: true
|
||
|
||
- nombre: "ticker"
|
||
tipo: "Ticker | null"
|
||
descripcion: "Current ticker data (price, change, high, low)"
|
||
persistido: false
|
||
ultima_actualizacion: "API / WebSocket"
|
||
|
||
- nombre: "orderBook"
|
||
tipo: "OrderBook | null"
|
||
descripcion: "Current market orderbook (bids/asks)"
|
||
persistido: false
|
||
actualizado_por: "WebSocket (POLLING actualmente - LAG)"
|
||
|
||
- nombre: "klines"
|
||
tipo: "Kline[]"
|
||
descripcion: "OHLCV candle data"
|
||
persistido: false
|
||
cantidad_maxima: "500 candles"
|
||
|
||
- nombre: "watchlists"
|
||
tipo: "Watchlist[]"
|
||
descripcion: "User's custom symbol watchlists"
|
||
persistido: true
|
||
relacion: "ONE-TO-MANY"
|
||
|
||
- nombre: "selectedWatchlistId"
|
||
tipo: "string | null"
|
||
descripcion: "Currently selected watchlist"
|
||
persistido: true
|
||
|
||
- nombre: "isLoadingTicker"
|
||
tipo: "boolean"
|
||
persistido: false
|
||
|
||
- nombre: "isLoadingKlines"
|
||
tipo: "boolean"
|
||
persistido: false
|
||
|
||
- nombre: "isLoadingOrderBook"
|
||
tipo: "boolean"
|
||
persistido: false
|
||
|
||
acciones_principales:
|
||
- "setSelectedSymbol(symbol: string)"
|
||
- "setTicker(ticker: Ticker)"
|
||
- "setOrderBook(orderBook: OrderBook)"
|
||
- "setKlines(klines: Kline[])"
|
||
- "appendKline(kline: Kline)"
|
||
- "setWatchlists(watchlists: Watchlist[])"
|
||
- "setSelectedWatchlistId(id: string | null)"
|
||
- "reset()"
|
||
|
||
dependencias:
|
||
- "axios para fetch inicial"
|
||
- "WebSocket service para updates en tiempo real"
|
||
|
||
gaps:
|
||
- nombre: "No WebSocket para orderbook"
|
||
severidad: "P1"
|
||
descripcion: "OrderBook se actualiza via polling (1-2 seg lag)"
|
||
impacto: "Traders ven precios atrasados"
|
||
solucion: "Integrar websocket.service"
|
||
|
||
- nombre: "No multi-symbol concurrent subscriptions"
|
||
severidad: "P2"
|
||
descripcion: "Solo puedes ver un símbolo a la vez"
|
||
impacto: "No puedes comparar múltiples pares"
|
||
solucion: "Extender store para multi-symbol"
|
||
|
||
- nombre: "Falta indicators state"
|
||
severidad: "P2"
|
||
descripcion: "Indicators solo en chartStore, no en trading"
|
||
impacto: "Dificil sincronizar entre componentes"
|
||
solucion: "Agregar indicators array al store"
|
||
|
||
middleware:
|
||
- devtools
|
||
- persist
|
||
|
||
test_coverage: "65%"
|
||
|
||
---
|
||
|
||
order_store:
|
||
archivo: "order.store.ts"
|
||
epicAssociated: "OQI-003"
|
||
estado: "FUNCIONAL"
|
||
lineas: 380
|
||
descripcion: "Order placement form state and positions management"
|
||
|
||
state_fields:
|
||
- nombre: "balances"
|
||
tipo: "Balance[]"
|
||
descripcion: "User's paper trading balances"
|
||
persistido: false
|
||
campos_del_balance:
|
||
- "asset: string (e.g., USDT, BTC)"
|
||
- "total: number"
|
||
- "available: number"
|
||
- "locked: number"
|
||
|
||
- nombre: "orders"
|
||
tipo: "Order[]"
|
||
descripcion: "Open and filled orders"
|
||
persistido: false
|
||
|
||
- nombre: "positions"
|
||
tipo: "Position[]"
|
||
descripcion: "Open positions"
|
||
persistido: false
|
||
campos_de_position:
|
||
- "id, symbol, side, status"
|
||
- "currentQuantity, averageEntryPrice"
|
||
- "unrealizedPnl, realizedPnl, totalPnl"
|
||
|
||
- nombre: "orderSide"
|
||
tipo: "'buy' | 'sell'"
|
||
descripcion: "Order form current side"
|
||
persistido: false
|
||
default: "buy"
|
||
|
||
- nombre: "orderType"
|
||
tipo: "'market' | 'limit' | 'stop_loss'"
|
||
descripcion: "Order form current type"
|
||
persistido: false
|
||
default: "market"
|
||
|
||
- nombre: "orderQuantity"
|
||
tipo: "string"
|
||
descripcion: "Order form quantity input"
|
||
persistido: false
|
||
|
||
- nombre: "orderPrice"
|
||
tipo: "string"
|
||
descripcion: "Order form price input (for limit orders)"
|
||
persistido: false
|
||
|
||
- nombre: "stopLoss"
|
||
tipo: "string"
|
||
descripcion: "Order form SL price"
|
||
persistido: false
|
||
|
||
- nombre: "takeProfit"
|
||
tipo: "string"
|
||
descripcion: "Order form TP price"
|
||
persistido: false
|
||
|
||
acciones_principales:
|
||
- "setBalances(balances: Balance[])"
|
||
- "getBalance(asset: string): Balance | undefined"
|
||
- "setOrders(orders: Order[])"
|
||
- "addOrder(order: Order)"
|
||
- "updateOrder(orderId: string, updates: Partial<Order>)"
|
||
- "removeOrder(orderId: string)"
|
||
- "setPositions(positions: Position[])"
|
||
- "updatePosition(positionId: string, updates: Partial<Position>)"
|
||
- "setOrderSide, setOrderType, setOrderQuantity, setOrderPrice, setStopLoss, setTakeProfit"
|
||
- "resetOrderForm()"
|
||
|
||
dependencias:
|
||
- "trading.service.ts para API calls"
|
||
|
||
gaps:
|
||
- nombre: "No order validation en store"
|
||
severidad: "P1"
|
||
descripcion: "Validaciones son solo en componentes"
|
||
impacto: "Código duplicado, inconsistent"
|
||
solucion: "Mover validaciones al store action"
|
||
|
||
- nombre: "No undo/redo capability"
|
||
severidad: "P2"
|
||
descripcion: "No puedes revertir cambios de forma"
|
||
impacto: "UX friction"
|
||
solucion: "Implementar history stack"
|
||
|
||
- nombre: "No order templates"
|
||
severidad: "P2"
|
||
descripcion: "No puedes guardar ordenes frecuentes"
|
||
impacto: "Traders deben reescribir cada vez"
|
||
solucion: "Agregar saveTemplate/loadTemplate"
|
||
|
||
middleware:
|
||
- devtools (without persist)
|
||
|
||
test_coverage: "60%"
|
||
|
||
---
|
||
|
||
chart_store:
|
||
archivo: "chart.store.ts"
|
||
epicAssociated: "OQI-003"
|
||
estado: "FUNCIONAL"
|
||
lineas: 320
|
||
descripcion: "Chart UI settings and technical indicators"
|
||
|
||
state_fields:
|
||
- nombre: "interval"
|
||
tipo: "TimeInterval ('1m' | '5m' | '15m' | '1h' | '4h' | '1d')"
|
||
descripcion: "Current chart timeframe"
|
||
persistido: true
|
||
default: "1h"
|
||
|
||
- nombre: "chartType"
|
||
tipo: "'candlestick' | 'line' | 'area'"
|
||
descripcion: "Chart rendering type"
|
||
persistido: true
|
||
default: "candlestick"
|
||
|
||
- nombre: "indicators"
|
||
tipo: "ChartIndicator[]"
|
||
descripcion: "Active chart indicators"
|
||
persistido: true
|
||
estructura:
|
||
id: "string"
|
||
type: "'SMA' | 'EMA' | 'RSI' | 'MACD' | 'BB'"
|
||
params: "Record<string, any>"
|
||
visible: "boolean"
|
||
color: "string (optional)"
|
||
|
||
- nombre: "drawingsEnabled"
|
||
tipo: "boolean"
|
||
descripcion: "Is drawing mode active"
|
||
persistido: true
|
||
default: false
|
||
|
||
- nombre: "showVolume"
|
||
tipo: "boolean"
|
||
default: true
|
||
persistido: true
|
||
|
||
- nombre: "showGrid"
|
||
tipo: "boolean"
|
||
default: true
|
||
persistido: true
|
||
|
||
- nombre: "theme"
|
||
tipo: "'light' | 'dark'"
|
||
persistido: true
|
||
default: "dark"
|
||
|
||
acciones_principales:
|
||
- "setInterval(interval: TimeInterval)"
|
||
- "setChartType(type: ChartType)"
|
||
- "addIndicator(indicator: ChartIndicator)"
|
||
- "removeIndicator(id: string)"
|
||
- "updateIndicator(id: string, updates: Partial<ChartIndicator>)"
|
||
- "toggleIndicator(id: string)"
|
||
- "setDrawingsEnabled(enabled: boolean)"
|
||
- "toggleVolume()"
|
||
- "toggleGrid()"
|
||
- "setTheme(theme: 'light' | 'dark')"
|
||
|
||
dependencias:
|
||
- "Lightweight Charts para rendering"
|
||
|
||
gaps:
|
||
- nombre: "No saved chart layouts"
|
||
severidad: "P2"
|
||
descripcion: "Usuarios no pueden guardar layouts favoritos"
|
||
impacto: "Deben reconfigurare cada sesión"
|
||
solucion: "Agregar saveLayout/loadLayout actions"
|
||
|
||
- nombre: "No drawing persistence"
|
||
severidad: "P2"
|
||
descripcion: "Drawings se pierden al refresca página"
|
||
impacto: "Análisis técnico se pierde"
|
||
solucion: "Serializar drawings y persistir"
|
||
|
||
- nombre: "No indicator parameters persistence"
|
||
severidad: "P1"
|
||
descripcion: "Parámetros de SMA(20) se resetean"
|
||
impacto: "Traders deben reconfigurar"
|
||
solucion: "Mejorar persist logic"
|
||
|
||
middleware:
|
||
- devtools
|
||
- persist
|
||
|
||
test_coverage: "75%"
|
||
|
||
---
|
||
|
||
education_store:
|
||
archivo: "educationStore.ts"
|
||
epicAssociated: "OQI-002"
|
||
estado: "FUNCIONAL"
|
||
lineas: 300
|
||
descripcion: "Education module state: courses, enrollments, progress"
|
||
|
||
state_fields:
|
||
- nombre: "courses"
|
||
tipo: "Course[]"
|
||
persistido: false
|
||
|
||
- nombre: "enrollments"
|
||
tipo: "Enrollment[]"
|
||
persistido: false
|
||
|
||
- nombre: "currentCourse"
|
||
tipo: "Course | null"
|
||
persistido: false
|
||
|
||
- nombre: "currentLesson"
|
||
tipo: "Lesson | null"
|
||
persistido: false
|
||
|
||
- nombre: "isLoading"
|
||
tipo: "boolean"
|
||
persistido: false
|
||
|
||
acciones_principales:
|
||
- "setCourses(courses: Course[])"
|
||
- "setEnrollments(enrollments: Enrollment[])"
|
||
- "setCurrentCourse(course: Course | null)"
|
||
- "setCurrentLesson(lesson: Lesson | null)"
|
||
|
||
gaps:
|
||
- nombre: "Falta gamification state"
|
||
severidad: "P1"
|
||
descripcion: "XP, levels, achievements no están en store"
|
||
impacto: "Gamification desconectada"
|
||
solucion: "Crear gamificationStore"
|
||
|
||
- nombre: "Falta quiz state"
|
||
severidad: "P1"
|
||
descripcion: "Quiz attempt state no persisted"
|
||
impacto: "Usuarios pierden respuestas al refrescar"
|
||
solucion: "Crear quizStore"
|
||
|
||
middleware:
|
||
- devtools
|
||
|
||
test_coverage: "55%"
|
||
|
||
---
|
||
|
||
payment_store:
|
||
archivo: "paymentStore.ts"
|
||
epicAssociated: "OQI-005"
|
||
estado: "FUNCIONAL"
|
||
lineas: 280
|
||
descripcion: "Payment methods, subscriptions, invoices"
|
||
|
||
state_fields:
|
||
- nombre: "paymentMethods"
|
||
tipo: "PaymentMethod[]"
|
||
persistido: false
|
||
|
||
- nombre: "subscriptions"
|
||
tipo: "Subscription[]"
|
||
persistido: false
|
||
|
||
- nombre: "invoices"
|
||
tipo: "Invoice[]"
|
||
persistido: false
|
||
|
||
gaps:
|
||
- nombre: "No Stripe Elements state"
|
||
severidad: "P0"
|
||
descripcion: "No hay state para cardElement, paymentIntent"
|
||
impacto: "PCI compliance issues"
|
||
solucion: "Crear stripeStore"
|
||
|
||
middleware:
|
||
- devtools
|
||
|
||
test_coverage: "50%"
|
||
|
||
---
|
||
|
||
portfolio_store:
|
||
archivo: "portfolioStore.ts"
|
||
epicAssociated: "OQI-008"
|
||
estado: "FUNCIONAL"
|
||
lineas: 300
|
||
descripcion: "Portfolio management: allocations, goals, rebalancing"
|
||
|
||
state_fields:
|
||
- nombre: "portfolios"
|
||
tipo: "Portfolio[]"
|
||
persistido: true
|
||
|
||
- nombre: "goals"
|
||
tipo: "FinancialGoal[]"
|
||
persistido: false
|
||
|
||
- nombre: "allocations"
|
||
tipo: "Allocation[]"
|
||
persistido: true
|
||
|
||
gaps:
|
||
- nombre: "No WebSocket para real-time updates"
|
||
severidad: "P1"
|
||
descripcion: "Portfolio values lag detrás"
|
||
impacto: "Users no ven actualización en tiempo real"
|
||
solucion: "Integrar websocket.service"
|
||
|
||
middleware:
|
||
- devtools
|
||
- persist
|
||
|
||
test_coverage: "45%"
|
||
|
||
---
|
||
|
||
notification_store:
|
||
archivo: "notificationStore.ts"
|
||
epicAssociated: "ALL"
|
||
estado: "FUNCIONAL"
|
||
lineas: 200
|
||
descripcion: "System notifications/toasts"
|
||
|
||
gaps: []
|
||
middleware:
|
||
- devtools
|
||
|
||
test_coverage: "80%"
|
||
|
||
---
|
||
|
||
sessions_store:
|
||
archivo: "sessionsStore.ts"
|
||
epicAssociated: "OQI-001"
|
||
estado: "FUNCIONAL"
|
||
lineas: 250
|
||
descripcion: "Active user sessions, device management"
|
||
|
||
state_fields:
|
||
- nombre: "activeSessions"
|
||
tipo: "Session[]"
|
||
persistido: false
|
||
|
||
- nombre: "currentDeviceId"
|
||
tipo: "string"
|
||
persistido: true
|
||
|
||
gaps:
|
||
- nombre: "Incompleto - falta sincronización backend"
|
||
severidad: "P1"
|
||
descripcion: "No hay API para actualizar sessions"
|
||
impacto: "Device management no funciona"
|
||
solucion: "Crear sessionService.ts"
|
||
|
||
middleware:
|
||
- devtools
|
||
- persist
|
||
|
||
test_coverage: "40%"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# STORES FALTANTES
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
faltantes:
|
||
|
||
session_store:
|
||
epicAssociated: "OQI-001"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "30h"
|
||
descripcion: "Active sessions, device management, challenge auth"
|
||
|
||
state_requerido:
|
||
- "activeSessions: Session[]"
|
||
- "currentDeviceId: string"
|
||
- "challenges: Challenge[]"
|
||
- "isSwitchingDevice: boolean"
|
||
|
||
acciones_requeridas:
|
||
- "setActiveSessions(sessions: Session[])"
|
||
- "terminateSession(sessionId: string)"
|
||
- "setCurrentDeviceId(deviceId: string)"
|
||
- "addChallenge(challenge: Challenge)"
|
||
- "approveChallenge(challengeId: string)"
|
||
|
||
dependencias_bloqueantes:
|
||
- "sessionService.ts (para API calls)"
|
||
- "websocket.service.ts (para push notifications)"
|
||
|
||
---
|
||
|
||
quiz_store:
|
||
epicAssociated: "OQI-002"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "35h"
|
||
descripcion: "Quiz attempt state, answers, results"
|
||
|
||
state_requerido:
|
||
- "currentQuiz: Quiz | null"
|
||
- "currentAttempt: QuizAttempt | null"
|
||
- "answers: Map<QuestionId, Answer>"
|
||
- "isSubmitting: boolean"
|
||
- "attemptHistory: QuizAttempt[]"
|
||
|
||
acciones_requeridas:
|
||
- "startQuiz(quizId: string)"
|
||
- "answerQuestion(questionId: string, answer: Answer)"
|
||
- "submitAttempt()"
|
||
- "endQuiz()"
|
||
- "saveProgress()" # For resumable quizzes
|
||
|
||
persistencia:
|
||
- "answers MUST persist para quizzes resumibles"
|
||
- "Usar localStorage con encryption para security"
|
||
|
||
---
|
||
|
||
gamification_store:
|
||
epicAssociated: "OQI-002"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "40h"
|
||
descripcion: "User XP, levels, achievements, streaks"
|
||
|
||
state_requerido:
|
||
- "profile: GamificationProfile"
|
||
- "achievements: Achievement[]"
|
||
- "streaks: StreakData"
|
||
- "leaderboardPosition: number"
|
||
- "showLevelUpModal: boolean"
|
||
- "showAchievementModal: Achievement | null"
|
||
|
||
campos_de_profile:
|
||
- "userId: string"
|
||
- "totalXP: number"
|
||
- "currentLevel: number"
|
||
- "xpForNextLevel: number"
|
||
- "xpProgressPercentage: number"
|
||
- "currentStreakDays: number"
|
||
- "longestStreak: number"
|
||
- "lastActivityDate: Date"
|
||
|
||
acciones_requeridas:
|
||
- "addXP(amount: number, reason: string)"
|
||
- "unlockAchievement(achievementId: string)"
|
||
- "setStreakDays(days: number)"
|
||
- "getLevel(): number"
|
||
- "checkLevelUp(): boolean"
|
||
- "resetStreak()"
|
||
|
||
conexiones_requeridas:
|
||
- "WebSocket para notificaciones de level-up"
|
||
- "Backend: gamification events"
|
||
|
||
---
|
||
|
||
certificate_store:
|
||
epicAssociated: "OQI-002"
|
||
prioridad: "P2"
|
||
esfuerzo_estimado: "35h"
|
||
descripcion: "User certificates, templates, generation status"
|
||
|
||
state_requerido:
|
||
- "certificates: Certificate[]"
|
||
- "templates: CertificateTemplate[]"
|
||
- "isGenerating: boolean"
|
||
- "generationProgress: number"
|
||
- "lastGenerated: Certificate | null"
|
||
|
||
---
|
||
|
||
stripe_store:
|
||
epicAssociated: "OQI-005"
|
||
prioridad: "P0"
|
||
esfuerzo_estimado: "45h"
|
||
descripcion: "Stripe Elements state, payment intents, errors"
|
||
|
||
state_requerido:
|
||
- "clientSecret: string | null"
|
||
- "isProcessing: boolean"
|
||
- "stripeError: StripeError | null"
|
||
- "cardElement: StripeCardElement | null"
|
||
- "paymentMethodId: string | null"
|
||
|
||
acciones_requeridas:
|
||
- "setClientSecret(secret: string)"
|
||
- "setIsProcessing(processing: boolean)"
|
||
- "setStripeError(error: StripeError | null)"
|
||
- "clearError()"
|
||
|
||
CRITICO:
|
||
- "NUNCA almacenar card data en state"
|
||
- "NUNCA loguear sensitive data"
|
||
- "Use Stripe Elements EXCLUSIVAMENTE"
|
||
|
||
---
|
||
|
||
kyc_store:
|
||
epicAssociated: "OQI-004"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "40h"
|
||
descripcion: "KYC verification status, documents, verification state"
|
||
|
||
state_requerido:
|
||
- "status: 'pending' | 'submitted' | 'verified' | 'rejected'"
|
||
- "documents: KYCDocument[]"
|
||
- "verificationSteps: VerificationStep[]"
|
||
- "currentStep: number"
|
||
- "rejectionReason: string | null"
|
||
|
||
---
|
||
|
||
agent_store:
|
||
epicAssociated: "OQI-007"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "35h"
|
||
descripcion: "LLM agent state, available strategies, execution status"
|
||
|
||
state_requerido:
|
||
- "agentStatus: 'ready' | 'thinking' | 'executing' | 'error'"
|
||
- "strategies: Strategy[]"
|
||
- "activeExecution: Execution | null"
|
||
- "executionHistory: Execution[]"
|
||
- "tokenUsage: TokenUsageMetrics"
|
||
|
||
campos_token_usage:
|
||
- "tokensUsedThisMonth: number"
|
||
- "tokenLimit: number"
|
||
- "percentageUsed: number"
|
||
- "estimatedCost: number"
|
||
|
||
---
|
||
|
||
ml_signal_store:
|
||
epicAssociated: "OQI-006"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "35h"
|
||
descripcion: "ML predictions, signals, confidence, ensemble state"
|
||
|
||
state_requerido:
|
||
- "currentSignal: Signal | null"
|
||
- "signals: Signal[]"
|
||
- "confidence: number"
|
||
- "ensembleVote: EnsembleVote | null"
|
||
- "individualModelScores: Map<ModelId, number>"
|
||
- "isUpdating: boolean"
|
||
|
||
conexiones_requeridas:
|
||
- "WebSocket para predicciones en tiempo real"
|
||
- "Backend: ml-engine signals"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# ANALYSIS SECTION 2: SERVICES / API CLIENTS
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
services:
|
||
framework: "Axios with interceptors"
|
||
ubicacion: "apps/frontend/src/services/"
|
||
pattern: "RESTful API client"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SERVICES EXISTENTES
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
existentes:
|
||
|
||
auth_service:
|
||
archivo: "auth.service.ts"
|
||
epicAssociated: "OQI-001"
|
||
estado: "FUNCIONAL"
|
||
lineas: 380
|
||
descripcion: "Authentication API endpoints"
|
||
|
||
endpoints_implementados:
|
||
- "POST /auth/register"
|
||
- "POST /auth/login"
|
||
- "POST /auth/oauth/:provider"
|
||
- "GET /auth/oauth/callback/:provider"
|
||
- "POST /auth/logout"
|
||
- "POST /auth/refresh-token"
|
||
- "POST /auth/2fa/setup"
|
||
- "POST /auth/2fa/verify"
|
||
- "POST /auth/phone/send"
|
||
- "POST /auth/phone/verify"
|
||
- "POST /auth/forgot-password"
|
||
- "POST /auth/reset-password/:token"
|
||
- "GET /auth/verify-email/:token"
|
||
|
||
funcionalidad_actual: "85% (missing session endpoints)"
|
||
|
||
gaps:
|
||
- endpoint: "GET /auth/sessions"
|
||
severidad: "P1"
|
||
descripcion: "Get active user sessions"
|
||
|
||
- endpoint: "DELETE /auth/sessions/:sessionId"
|
||
severidad: "P1"
|
||
descripcion: "Terminate specific session"
|
||
|
||
- endpoint: "POST /auth/device/verify"
|
||
severidad: "P1"
|
||
descripcion: "Verify new device challenge"
|
||
|
||
---
|
||
|
||
trading_service:
|
||
archivo: "trading.service.ts"
|
||
epicAssociated: "OQI-003"
|
||
estado: "FUNCIONAL"
|
||
lineas: 420
|
||
descripcion: "Market data, orders, positions API"
|
||
|
||
endpoints_implementados:
|
||
- "GET /market/klines"
|
||
- "GET /market/ticker/:symbol"
|
||
- "GET /market/orderbook/:symbol"
|
||
- "POST /paper/orders"
|
||
- "GET /paper/orders"
|
||
- "POST /paper/orders/:id/cancel"
|
||
- "GET /paper/positions"
|
||
- "POST /paper/positions/:id/close"
|
||
- "GET /paper/balance"
|
||
|
||
gaps:
|
||
- endpoint: "GET /market/symbols"
|
||
descripcion: "List all available symbols"
|
||
|
||
- endpoint: "WS /market/klines/:symbol"
|
||
severidad: "P1"
|
||
descripcion: "WebSocket kline updates"
|
||
|
||
- endpoint: "WS /market/orderbook/:symbol"
|
||
severidad: "P1"
|
||
descripcion: "WebSocket orderbook updates"
|
||
|
||
- endpoint: "GET /paper/statistics"
|
||
descripcion: "Trading statistics (win rate, avg loss, etc)"
|
||
|
||
- endpoint: "POST /paper/backtest"
|
||
severidad: "P2"
|
||
descripcion: "Backtest strategy"
|
||
|
||
---
|
||
|
||
education_service:
|
||
archivo: "education.service.ts"
|
||
epicAssociated: "OQI-002"
|
||
estado: "FUNCIONAL"
|
||
lineas: 350
|
||
descripcion: "Courses, lessons, enrollments, progress"
|
||
|
||
endpoints_implementados:
|
||
- "GET /education/courses"
|
||
- "GET /education/courses/:id"
|
||
- "POST /education/enrollments"
|
||
- "GET /education/enrollments"
|
||
- "POST /education/progress"
|
||
- "GET /education/progress/:lessonId"
|
||
- "POST /education/certificates/generate"
|
||
|
||
gaps:
|
||
- endpoint: "POST /education/quizzes/:id/attempts"
|
||
severidad: "P1"
|
||
descripcion: "Start quiz attempt"
|
||
|
||
- endpoint: "POST /education/attempts/:id/submit"
|
||
severidad: "P1"
|
||
descripcion: "Submit quiz attempt"
|
||
|
||
- endpoint: "GET /education/attempts/:id/results"
|
||
severidad: "P1"
|
||
descripcion: "Get quiz results"
|
||
|
||
- endpoint: "WS /education/lessons/:id/stream"
|
||
severidad: "P1"
|
||
descripcion: "Live lesson streaming"
|
||
|
||
---
|
||
|
||
payment_service:
|
||
archivo: "payment.service.ts"
|
||
epicAssociated: "OQI-005"
|
||
estado: "FUNCIONAL"
|
||
lineas: 300
|
||
descripcion: "Stripe integration, payments, subscriptions"
|
||
|
||
endpoints_implementados:
|
||
- "POST /payments/create-intent"
|
||
- "POST /payments/confirm"
|
||
- "GET /payments/methods"
|
||
- "POST /payments/methods"
|
||
- "DELETE /payments/methods/:id"
|
||
- "GET /payments/subscriptions"
|
||
- "POST /payments/subscriptions"
|
||
- "POST /payments/subscriptions/:id/cancel"
|
||
- "GET /payments/invoices"
|
||
- "GET /payments/invoices/:id"
|
||
|
||
gaps:
|
||
- endpoint: "POST /payments/refunds"
|
||
descripcion: "Initiate refund"
|
||
|
||
- endpoint: "GET /payments/refunds"
|
||
descripcion: "Get refund history"
|
||
|
||
- endpoint: "POST /payments/webhooks"
|
||
severidad: "P1"
|
||
descripcion: "Handle Stripe webhook events"
|
||
|
||
---
|
||
|
||
investment_service:
|
||
archivo: "investment.service.ts"
|
||
epicAssociated: "OQI-004"
|
||
estado: "FUNCIONAL"
|
||
lineas: 380
|
||
descripcion: "Investment accounts, products, transactions"
|
||
|
||
endpoints_implementados:
|
||
- "POST /investment/accounts"
|
||
- "GET /investment/accounts"
|
||
- "GET /investment/accounts/:id"
|
||
- "GET /investment/products"
|
||
- "POST /investment/accounts/:id/deposit"
|
||
- "POST /investment/accounts/:id/withdraw"
|
||
- "GET /investment/accounts/:id/transactions"
|
||
- "GET /investment/accounts/:id/distributions"
|
||
|
||
gaps:
|
||
- endpoint: "POST /investment/kyc"
|
||
severidad: "P0"
|
||
descripcion: "Submit KYC documents"
|
||
|
||
- endpoint: "GET /investment/kyc/status"
|
||
severidad: "P0"
|
||
descripcion: "Get KYC verification status"
|
||
|
||
- endpoint: "POST /investment/accounts/:id/transfer"
|
||
descripcion: "Inter-account transfer"
|
||
|
||
---
|
||
|
||
ml_service:
|
||
archivo: "mlService.ts"
|
||
epicAssociated: "OQI-006"
|
||
estado: "FUNCIONAL"
|
||
lineas: 280
|
||
descripcion: "ML predictions, signals, model management"
|
||
|
||
endpoints_implementados:
|
||
- "GET /ml/predict/:symbol"
|
||
- "GET /ml/signals/:symbol"
|
||
- "GET /ml/indicators/:symbol"
|
||
- "GET /ml/models"
|
||
- "POST /ml/models/:id/select"
|
||
- "GET /ml/ensemble/vote/:symbol"
|
||
|
||
gaps:
|
||
- endpoint: "WS /ml/predictions/:symbol"
|
||
severidad: "P1"
|
||
descripcion: "Real-time prediction updates"
|
||
|
||
- endpoint: "GET /ml/backtests"
|
||
descripcion: "Get backtest results"
|
||
|
||
---
|
||
|
||
chat_service:
|
||
archivo: "chat.service.ts"
|
||
epicAssociated: "OQI-007"
|
||
estado: "FUNCIONAL"
|
||
lineas: 200
|
||
descripcion: "Chat/conversation API"
|
||
|
||
endpoints_implementados:
|
||
- "POST /chat/messages"
|
||
- "GET /chat/conversations"
|
||
- "GET /chat/conversations/:id"
|
||
|
||
gaps: []
|
||
|
||
---
|
||
|
||
admin_service:
|
||
archivo: "adminService.ts"
|
||
epicAssociated: "Admin"
|
||
estado: "FUNCIONAL"
|
||
lineas: 250
|
||
descripcion: "Admin dashboard APIs"
|
||
|
||
endpoints_implementados:
|
||
- "GET /admin/models"
|
||
- "GET /admin/agents"
|
||
- "POST /admin/agents/:id/start"
|
||
- "POST /admin/agents/:id/stop"
|
||
|
||
---
|
||
|
||
video_upload_service:
|
||
archivo: "video-upload.service.ts"
|
||
epicAssociated: "OQI-002"
|
||
estado: "FUNCIONAL"
|
||
lineas: 320
|
||
descripcion: "Multi-part video upload to S3/R2"
|
||
|
||
funcionalidad:
|
||
- "Progress tracking"
|
||
- "Resumable uploads"
|
||
- "Multi-part upload"
|
||
- "Validation (size, codec, duration)"
|
||
|
||
fecha_creacion: "2026-01-26"
|
||
|
||
---
|
||
|
||
portfolio_service:
|
||
archivo: "portfolio.service.ts"
|
||
epicAssociated: "OQI-008"
|
||
estado: "FUNCIONAL"
|
||
lineas: 300
|
||
descripcion: "Portfolio management API"
|
||
|
||
endpoints_implementados:
|
||
- "POST /portfolio"
|
||
- "GET /portfolio"
|
||
- "GET /portfolio/:id"
|
||
- "PUT /portfolio/:id"
|
||
- "GET /portfolio/:id/allocations"
|
||
- "POST /portfolio/:id/allocations"
|
||
|
||
gaps:
|
||
- endpoint: "POST /portfolio/:id/optimize"
|
||
severidad: "P2"
|
||
descripcion: "Run portfolio optimization"
|
||
|
||
- endpoint: "POST /portfolio/:id/backtest"
|
||
descripcion: "Backtest portfolio strategy"
|
||
|
||
---
|
||
|
||
notification_service:
|
||
archivo: "notification.service.ts"
|
||
epicAssociated: "ALL"
|
||
estado: "FUNCIONAL"
|
||
lineas: 150
|
||
descripcion: "In-app notifications, push, email"
|
||
|
||
---
|
||
|
||
websocket_service:
|
||
archivo: "websocket.service.ts"
|
||
epicAssociated: "ALL"
|
||
estado: "PARCIAL"
|
||
lineas: 400
|
||
descripcion: "WebSocket connection management"
|
||
|
||
streams_implementados:
|
||
- "trading/klines/:symbol"
|
||
- "trading/ticker/:symbol"
|
||
- "trading/orderbook/:symbol"
|
||
- "portfolio/updates"
|
||
|
||
streams_faltantes:
|
||
- "ml/predictions/:symbol (P1)"
|
||
- "chat/messages (P1)"
|
||
- "education/live-stream (P1)"
|
||
- "auth/sessions (P1)"
|
||
|
||
notas:
|
||
- "Base infrastructure OK"
|
||
- "Falta completar handlers para LLM, education, auth"
|
||
- "Falta error recovery y reconnection logic"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# SERVICES FALTANTES
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
faltantes:
|
||
|
||
session_service:
|
||
epicAssociated: "OQI-001"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "25h"
|
||
descripcion: "Session management, device verification"
|
||
|
||
endpoints_requeridos:
|
||
- "GET /auth/sessions"
|
||
- "DELETE /auth/sessions/:sessionId"
|
||
- "POST /auth/device/verify"
|
||
- "GET /auth/device/challenges"
|
||
- "POST /auth/device/challenges/:id/approve"
|
||
|
||
---
|
||
|
||
quiz_service:
|
||
epicAssociated: "OQI-002"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "35h"
|
||
descripcion: "Quiz API"
|
||
|
||
endpoints_requeridos:
|
||
- "GET /education/quizzes/:id"
|
||
- "POST /education/quizzes/:id/attempts"
|
||
- "POST /education/attempts/:id/submit"
|
||
- "GET /education/attempts/:id/results"
|
||
- "GET /education/questions/:id"
|
||
|
||
---
|
||
|
||
certificate_service:
|
||
epicAssociated: "OQI-002"
|
||
prioridad: "P2"
|
||
esfuerzo_estimado: "30h"
|
||
descripcion: "Certificate generation and management"
|
||
|
||
endpoints_requeridos:
|
||
- "GET /education/certificates"
|
||
- "POST /education/courses/:id/certificate/generate"
|
||
- "GET /education/certificates/:id/download"
|
||
- "POST /education/certificates/:id/verify"
|
||
|
||
---
|
||
|
||
kyc_service:
|
||
epicAssociated: "OQI-004"
|
||
prioridad: "P0"
|
||
esfuerzo_estimado: "40h"
|
||
descripcion: "KYC document upload and verification"
|
||
|
||
endpoints_requeridos:
|
||
- "POST /investment/kyc"
|
||
- "POST /investment/kyc/documents"
|
||
- "GET /investment/kyc/status"
|
||
- "PUT /investment/kyc/documents/:id"
|
||
- "DELETE /investment/kyc/documents/:id"
|
||
|
||
---
|
||
|
||
backtest_service:
|
||
epicAssociated: "OQI-003"
|
||
prioridad: "P2"
|
||
esfuerzo_estimado: "45h"
|
||
descripcion: "Strategy backtesting"
|
||
|
||
endpoints_requeridos:
|
||
- "POST /backtest/run"
|
||
- "GET /backtest/:id"
|
||
- "GET /backtest/:id/results"
|
||
- "GET /backtest/:id/trades"
|
||
- "DELETE /backtest/:id"
|
||
|
||
---
|
||
|
||
agent_service:
|
||
epicAssociated: "OQI-007"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "50h"
|
||
descripcion: "LLM agent APIs"
|
||
|
||
endpoints_requeridos:
|
||
- "POST /agent/analyze/:symbol"
|
||
- "POST /agent/strategy/build"
|
||
- "POST /agent/strategy/execute"
|
||
- "GET /agent/token-usage"
|
||
- "GET /agent/strategies"
|
||
- "GET /agent/execution/:id"
|
||
- "POST /agent/execution/:id/cancel"
|
||
|
||
---
|
||
|
||
refund_service:
|
||
epicAssociated: "OQI-005"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "30h"
|
||
descripcion: "Refund request and processing"
|
||
|
||
endpoints_requeridos:
|
||
- "POST /payments/refunds"
|
||
- "GET /payments/refunds"
|
||
- "GET /payments/refunds/:id"
|
||
- "PUT /payments/refunds/:id"
|
||
- "DELETE /payments/refunds/:id"
|
||
|
||
---
|
||
|
||
watchlist_service:
|
||
epicAssociated: "OQI-003"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "25h"
|
||
descripcion: "Watchlist CRUD operations"
|
||
|
||
endpoints_requeridos:
|
||
- "GET /trading/watchlists"
|
||
- "POST /trading/watchlists"
|
||
- "PUT /trading/watchlists/:id"
|
||
- "DELETE /trading/watchlists/:id"
|
||
- "POST /trading/watchlists/:id/symbols"
|
||
- "DELETE /trading/watchlists/:id/symbols/:symbol"
|
||
|
||
---
|
||
|
||
portfolio_optimization_service:
|
||
epicAssociated: "OQI-008"
|
||
prioridad: "P2"
|
||
esfuerzo_estimado: "50h"
|
||
descripcion: "Portfolio optimization algorithms"
|
||
|
||
endpoints_requeridos:
|
||
- "POST /portfolio/:id/optimize"
|
||
- "POST /portfolio/:id/optimize/efficient-frontier"
|
||
- "GET /portfolio/:id/recommendations"
|
||
|
||
---
|
||
|
||
stripe_webhook_service:
|
||
epicAssociated: "OQI-005"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "40h"
|
||
descripcion: "Handle Stripe webhook events"
|
||
|
||
eventos_requeridos:
|
||
- "payment_intent.succeeded"
|
||
- "payment_intent.payment_failed"
|
||
- "invoice.paid"
|
||
- "invoice.payment_failed"
|
||
- "customer.subscription.created"
|
||
- "customer.subscription.deleted"
|
||
|
||
---
|
||
|
||
market_alerts_service:
|
||
epicAssociated: "OQI-003"
|
||
prioridad: "P1"
|
||
esfuerzo_estimado: "35h"
|
||
descripcion: "Price alerts management"
|
||
|
||
endpoints_requeridos:
|
||
- "GET /trading/alerts"
|
||
- "POST /trading/alerts"
|
||
- "PUT /trading/alerts/:id"
|
||
- "DELETE /trading/alerts/:id"
|
||
- "WS /trading/alerts/realtime"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# WEBSOCKET CONNECTIONS REQUIRED
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
websocket_requirements:
|
||
framework: "Socket.io v4.5+ (recomendado)"
|
||
ubicacion: "apps/frontend/src/services/websocket.service.ts"
|
||
|
||
conexiones_requeridas:
|
||
- stream: "trading/klines/:symbol"
|
||
estado: "IMPLEMENTADO"
|
||
tasa_actualizacion: "1s"
|
||
tamaño_mensaje: "~500 bytes"
|
||
|
||
- stream: "trading/ticker/:symbol"
|
||
estado: "IMPLEMENTADO"
|
||
tasa_actualizacion: "100ms"
|
||
tamaño_mensaje: "~200 bytes"
|
||
|
||
- stream: "trading/orderbook/:symbol"
|
||
estado: "NO IMPLEMENTADO"
|
||
tasa_actualizacion: "500ms"
|
||
tamaño_mensaje: "~2KB"
|
||
prioridad: "P1"
|
||
esfuerzo: "20h"
|
||
|
||
- stream: "ml/predictions/:symbol"
|
||
estado: "NO IMPLEMENTADO"
|
||
tasa_actualizacion: "5min"
|
||
tamaño_mensaje: "~500 bytes"
|
||
prioridad: "P1"
|
||
esfuerzo: "15h"
|
||
|
||
- stream: "portfolio/updates"
|
||
estado: "IMPLEMENTADO"
|
||
tasa_actualizacion: "1s"
|
||
tamaño_mensaje: "~1KB"
|
||
|
||
- stream: "education/live-stream/:sessionId"
|
||
estado: "NO IMPLEMENTADO"
|
||
tasa_actualizacion: "30fps"
|
||
tamaño_mensaje: "STREAMING"
|
||
prioridad: "P1"
|
||
esfuerzo: "40h"
|
||
|
||
- stream: "chat/messages"
|
||
estado: "NO IMPLEMENTADO"
|
||
tasa_actualizacion: "REAL-TIME"
|
||
tamaño_mensaje: "~500 bytes per message"
|
||
prioridad: "P1"
|
||
esfuerzo: "25h"
|
||
|
||
- stream: "auth/sessions"
|
||
estado: "NO IMPLEMENTADO"
|
||
tasa_actualizacion: "ON CHANGE"
|
||
tamaño_mensaje: "~200 bytes"
|
||
prioridad: "P1"
|
||
esfuerzo: "15h"
|
||
|
||
- stream: "agent/execution/:executionId"
|
||
estado: "NO IMPLEMENTADO"
|
||
tasa_actualizacion: "REAL-TIME"
|
||
tamaño_mensaje: "~300 bytes per update"
|
||
prioridad: "P1"
|
||
esfuerzo: "20h"
|
||
|
||
resiliencia:
|
||
- "Auto-reconnect con exponential backoff"
|
||
- "Message queuing durante desconexión"
|
||
- "State reconciliation post-reconnect"
|
||
- "Fallback a polling si WebSocket falla"
|
||
|
||
performance:
|
||
- "Usar message compression (gzip) para payloads > 1KB"
|
||
- "Batch updates cuando sea posible"
|
||
- "Client-side debouncing/throttling"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# RESUMEN DE ESTIMACIONES Y ROADMAP
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
estimaciones_totales:
|
||
|
||
stores_por_hacer: "8 stores × 30-45h = 290h"
|
||
|
||
services_por_hacer: "12 services × 25-50h = 420h"
|
||
|
||
websocket_streams: "6 streams × 15-40h = 150h"
|
||
|
||
total_aproximado: "890h"
|
||
|
||
breakdown_por_trimestre:
|
||
q1_2026:
|
||
enfoque: "P0 blockers + core stores"
|
||
items:
|
||
- "auth stores (sessionStore)"
|
||
- "payment stores (stripeStore, kycStore)"
|
||
- "session service + kyc service"
|
||
estimado: "120h"
|
||
|
||
q2_2026:
|
||
enfoque: "Education + Trading stores/services"
|
||
items:
|
||
- "quizStore, gamificationStore"
|
||
- "watchlistService, backtestService"
|
||
- "WebSocket order book y ML predictions"
|
||
estimado: "220h"
|
||
|
||
q3_2026:
|
||
enfoque: "Advanced features"
|
||
items:
|
||
- "agentStore + agentService"
|
||
- "certificateStore + service"
|
||
- "portfolio optimization"
|
||
- "Live streaming WebSocket"
|
||
estimado: "280h"
|
||
|
||
q4_2026:
|
||
enfoque: "Polish + edge cases"
|
||
items:
|
||
- "Error boundaries + recovery"
|
||
- "Performance optimizations"
|
||
- "Additional tests"
|
||
estimado: "190h"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# DEPENDENCIES Y BLOQUEADORES
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
|
||
dependencies:
|
||
|
||
bloqueadores:
|
||
- store: "stripeStore"
|
||
bloqueado_por: "Backend Stripe webhook implementation"
|
||
impacto: "Payments cannot work without webhook handling"
|
||
solucion: "Coordinate with backend team Q1"
|
||
|
||
- store: "kycStore"
|
||
bloqueado_por: "Backend KYC verification API"
|
||
impacto: "Investment accounts cannot open without KYC"
|
||
solucion: "Coordinate with backend team Q1"
|
||
|
||
- service: "quizService"
|
||
bloqueado_por: "Backend quiz grading logic"
|
||
impacto: "Quizzes cannot be submitted"
|
||
solucion: "Coordinate with education team Q1"
|
||
|
||
- websocket: "orderbook stream"
|
||
bloqueado_por: "WebSocket stream en trading backend"
|
||
impacto: "Order book lags detrás de mercado"
|
||
solucion: "Coordinate with trading backend Q1"
|
||
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|
||
# FIN FRONTEND-STORES-PLAN.yml
|
||
# ═══════════════════════════════════════════════════════════════════════════════
|