# RF-USER-005: Preferencias de Usuario ## Identificacion | Campo | Valor | |-------|-------| | **ID** | RF-USER-005 | | **Modulo** | MGN-002 | | **Nombre Modulo** | Users - Gestion de Usuarios | | **Prioridad** | P2 | | **Complejidad** | Baja | | **Estado** | Aprobado | | **Autor** | System | | **Fecha** | 2025-12-05 | --- ## Descripcion El sistema debe permitir a cada usuario configurar sus preferencias personales, incluyendo idioma, zona horaria, formato de fecha/hora, tema visual y preferencias de notificaciones. Estas configuraciones personalizan la experiencia del usuario sin afectar a otros usuarios del tenant. ### Contexto de Negocio Las preferencias de usuario permiten: - Experiencia personalizada por usuario - Soporte multi-idioma - Adaptacion a zonas horarias locales - Control sobre notificaciones recibidas - Accesibilidad (tema oscuro, tamano de fuente) --- ## Criterios de Aceptacion - [x] **CA-001:** El usuario debe poder seleccionar idioma preferido - [x] **CA-002:** El usuario debe poder configurar zona horaria - [x] **CA-003:** El usuario debe poder elegir formato de fecha (DD/MM/YYYY, MM/DD/YYYY, etc.) - [x] **CA-004:** El usuario debe poder elegir formato de hora (12h, 24h) - [x] **CA-005:** El usuario debe poder elegir tema (claro, oscuro, sistema) - [x] **CA-006:** El usuario debe poder configurar preferencias de notificaciones - [x] **CA-007:** Las preferencias deben aplicarse inmediatamente - [x] **CA-008:** Las preferencias deben persistir entre sesiones ### Ejemplos de Verificacion ```gherkin Scenario: Cambiar idioma Given un usuario con idioma "es" (espaƱol) When cambia su preferencia a "en" (ingles) Then el sistema guarda la preferencia And la interfaz cambia a ingles inmediatamente And las fechas se formatean en ingles Scenario: Configurar zona horaria Given un usuario en Mexico (America/Mexico_City) When configura su zona horaria Then todas las fechas/horas se muestran en esa zona And los eventos del calendario se ajustan Scenario: Preferencias de notificaciones Given un usuario con notificaciones por email activadas When desactiva "notificaciones de marketing" Then deja de recibir ese tipo de emails And mantiene otras notificaciones activas Scenario: Tema oscuro Given un usuario con tema claro When activa tema oscuro Then la interfaz cambia a colores oscuros And la preferencia se mantiene al recargar ``` --- ## Reglas de Negocio | ID | Regla | Validacion | |----|-------|------------| | RN-001 | Idiomas soportados: es, en, pt | Enum validation | | RN-002 | Zona horaria debe ser valida IANA | Timezone validation | | RN-003 | Preferencias son por usuario, no por tenant | user_id scope | | RN-004 | Valores por defecto del tenant si no hay preferencia | Fallback logic | | RN-005 | Tema "sistema" sigue preferencia del OS | CSS media query | ### Preferencias Disponibles ```typescript interface UserPreferences { // Localizacion language: 'es' | 'en' | 'pt'; timezone: string; // IANA timezone dateFormat: 'DD/MM/YYYY' | 'MM/DD/YYYY' | 'YYYY-MM-DD'; timeFormat: '12h' | '24h'; currency: string; // ISO 4217 numberFormat: 'es-MX' | 'en-US' | 'pt-BR'; // Apariencia theme: 'light' | 'dark' | 'system'; sidebarCollapsed: boolean; compactMode: boolean; fontSize: 'small' | 'medium' | 'large'; // Notificaciones notifications: { email: { enabled: boolean; digest: 'instant' | 'daily' | 'weekly'; marketing: boolean; security: boolean; updates: boolean; }; push: { enabled: boolean; sound: boolean; }; inApp: { enabled: boolean; desktop: boolean; }; }; // Dashboard dashboard: { defaultView: string; widgets: string[]; }; } ``` --- ## Impacto en Capas ### Database | Elemento | Accion | Descripcion | |----------|--------|-------------| | Tabla | crear | `user_preferences` | | Columna | - | `id` UUID PK | | Columna | - | `user_id` UUID FK UNIQUE | | Columna | - | `tenant_id` UUID FK | | Columna | - | `language` VARCHAR(5) | | Columna | - | `timezone` VARCHAR(50) | | Columna | - | `date_format` VARCHAR(20) | | Columna | - | `time_format` VARCHAR(5) | | Columna | - | `theme` VARCHAR(10) | | Columna | - | `notifications` JSONB | | Columna | - | `dashboard` JSONB | | Columna | - | `metadata` JSONB | | Columna | - | `updated_at` TIMESTAMPTZ | ### Backend | Elemento | Accion | Descripcion | |----------|--------|-------------| | Controller | crear | `PreferencesController` | | Service | crear | `PreferencesService` | | Method | crear | `getPreferences()` | | Method | crear | `updatePreferences()` | | Method | crear | `resetPreferences()` | | DTO | crear | `UpdatePreferencesDto` | | DTO | crear | `PreferencesResponseDto` | | Entity | crear | `UserPreferences` | | Endpoint | crear | `GET /api/v1/users/me/preferences` | | Endpoint | crear | `PATCH /api/v1/users/me/preferences` | | Endpoint | crear | `POST /api/v1/users/me/preferences/reset` | ### Frontend | Elemento | Accion | Descripcion | |----------|--------|-------------| | Pagina | crear | `PreferencesPage` | | Componente | crear | `LanguageSelector` | | Componente | crear | `TimezoneSelector` | | Componente | crear | `ThemeToggle` | | Componente | crear | `NotificationSettings` | | Store | crear | `preferencesStore` | | Hook | crear | `usePreferences` | | Context | crear | `PreferencesContext` | --- ## Dependencias ### Depende de (Bloqueantes) | ID | Requerimiento | Estado | |----|---------------|--------| | RF-USER-001 | CRUD Usuarios | Tabla users | ### Dependencias Relacionadas | ID | Requerimiento | Relacion | |----|---------------|----------| | RF-SETTINGS-001 | Tenant Settings | Valores por defecto | --- ## Especificaciones Tecnicas ### Endpoint GET /api/v1/users/me/preferences ```typescript // Response 200 { "language": "es", "timezone": "America/Mexico_City", "dateFormat": "DD/MM/YYYY", "timeFormat": "24h", "currency": "MXN", "numberFormat": "es-MX", "theme": "dark", "sidebarCollapsed": false, "compactMode": false, "fontSize": "medium", "notifications": { "email": { "enabled": true, "digest": "daily", "marketing": false, "security": true, "updates": true }, "push": { "enabled": true, "sound": true }, "inApp": { "enabled": true, "desktop": false } }, "dashboard": { "defaultView": "overview", "widgets": ["sales", "inventory", "tasks"] } } ``` ### Endpoint PATCH /api/v1/users/me/preferences ```typescript // Request - Actualizacion parcial { "theme": "light", "notifications": { "email": { "marketing": false } } } // Response 200 { // PreferencesResponseDto completo actualizado } ``` ### Merge de Preferencias ```typescript // preferences.service.ts async updatePreferences( userId: string, updates: Partial, ): Promise { let preferences = await this.preferencesRepository.findOne({ where: { userId }, }); if (!preferences) { // Crear con defaults del tenant const tenantDefaults = await this.getTenantDefaults(userId); preferences = this.preferencesRepository.create({ userId, ...tenantDefaults, }); } // Deep merge para objetos anidados (notifications, dashboard) const merged = deepMerge(preferences, updates); return this.preferencesRepository.save(merged); } ``` ### Aplicacion en Frontend ```typescript // PreferencesContext.tsx export const PreferencesProvider: React.FC = ({ children }) => { const [preferences, setPreferences] = useState(null); useEffect(() => { loadPreferences(); }, []); useEffect(() => { if (preferences) { // Aplicar tema document.documentElement.setAttribute('data-theme', preferences.theme); // Aplicar idioma i18n.changeLanguage(preferences.language); // Configurar moment/dayjs timezone dayjs.tz.setDefault(preferences.timezone); } }, [preferences]); return ( {children} ); }; ``` --- ## Datos de Prueba | Escenario | Entrada | Resultado | |-----------|---------|-----------| | Obtener preferencias | GET /preferences | 200, preferencias o defaults | | Cambiar idioma | language: "en" | 200, idioma actualizado | | Zona horaria invalida | timezone: "Invalid/Zone" | 400, "Zona horaria invalida" | | Tema valido | theme: "dark" | 200, tema actualizado | | Tema invalido | theme: "purple" | 400, "Valor no permitido" | | Desactivar notificaciones | notifications.email.enabled: false | 200, actualizado | | Reset preferencias | POST /reset | 200, defaults del tenant | --- ## Estimacion | Capa | Story Points | Notas | |------|--------------|-------| | Database | 1 | Tabla user_preferences | | Backend | 2 | CRUD preferencias | | Frontend | 4 | UI de configuracion + contexto | | **Total** | **7** | | --- ## Notas Adicionales - Las preferencias se cargan al inicio de sesion y se cachean - Cambios de tema deben ser instantaneos (sin reload) - Considerar preferencias sincronizadas entre dispositivos - Exportar/importar preferencias para usuarios - Preferencias de accesibilidad (alto contraste, reducir animaciones) --- ## Historial de Cambios | Version | Fecha | Autor | Cambios | |---------|-------|-------|---------| | 1.0 | 2025-12-05 | System | Creacion inicial | --- ## Aprobaciones | Rol | Nombre | Fecha | Firma | |-----|--------|-------|-------| | Analista | System | 2025-12-05 | [x] | | Tech Lead | - | - | [ ] | | Product Owner | - | - | [ ] |