371 lines
9.5 KiB
Markdown
371 lines
9.5 KiB
Markdown
# 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<UserPreferences>,
|
|
): Promise<UserPreferences> {
|
|
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<UserPreferences | null>(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 (
|
|
<PreferencesContext.Provider value={{ preferences, updatePreferences }}>
|
|
{children}
|
|
</PreferencesContext.Provider>
|
|
);
|
|
};
|
|
```
|
|
|
|
---
|
|
|
|
## 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 | - | - | [ ] |
|