This commit is contained in:
14
BACKLOG.md
14
BACKLOG.md
@@ -128,4 +128,16 @@ Los assets SÍ existen en `assets/` (4 PNGs documentados en `simulador-web/CLAUD
|
|||||||
|
|
||||||
**Trabajo pendiente:** cargar el PNG vía `html2canvas` o convertirlo a base64 e inyectarlo en el header de los 3 PDFs (Actual, Automatizado, ROI). Ya existe la regla en CLAUDE.md de usar el PNG en lugar de texto "InQ"/"InQuality".
|
**Trabajo pendiente:** cargar el PNG vía `html2canvas` o convertirlo a base64 e inyectarlo en el header de los 3 PDFs (Actual, Automatizado, ROI). Ya existe la regla en CLAUDE.md de usar el PNG en lugar de texto "InQ"/"InQuality".
|
||||||
|
|
||||||
**Estimación:** Sprint 6 o posterior. Baja prioridad — los PDFs ya tienen "Powered by InQuality" en footer.
|
**Estimación:** Sprint 6 o posterior. Baja prioridad — los PDFs ya tienen "Powered by InQuality" en footer.
|
||||||
|
|
||||||
|
### [BACKLOG] Ocultar Settings y ruta /settings para roles de cliente
|
||||||
|
|
||||||
|
El ícono de tuerca (⚙) en LibraryPage es visible y navegable para `client_editor` y `client_viewer`. La página de configuración ("Etiqueta de grupos") es una feature interna de InQuality — un cliente no debería ver ni editar esta preferencia.
|
||||||
|
|
||||||
|
**Trabajo pendiente:**
|
||||||
|
- Ocultar el botón de Settings en LibraryPage para `client_editor` y `client_viewer`
|
||||||
|
- Agregar guard en la ruta `/settings` (componente-level, mismo patrón que `/admin/*` y `/import`)
|
||||||
|
- Verificar si `/recursos` también debe ser inaccesible para roles de cliente
|
||||||
|
|
||||||
|
**No es riesgo de seguridad** (es solo preferencia de UI local, sin datos sensibles).
|
||||||
|
**Estimación:** 1h — agregar al sprint siguiente junto con otros ajustes de UX de cliente.
|
||||||
|
|||||||
@@ -3,6 +3,11 @@ import type { AppSettings } from '@/domain/types'
|
|||||||
|
|
||||||
const DEFAULT_SETTINGS: AppSettings = { id: 'singleton', groupLabel: 'Cliente' }
|
const DEFAULT_SETTINGS: AppSettings = { id: 'singleton', groupLabel: 'Cliente' }
|
||||||
|
|
||||||
|
// Elimina artículos iniciales que el usuario pudo haber ingresado por error (ej. "un Cliente" → "Cliente")
|
||||||
|
function normalizeGroupLabel(raw: string): string {
|
||||||
|
return raw.replace(/^(un|una|el|la)\s+/i, '').trim() || 'Cliente'
|
||||||
|
}
|
||||||
|
|
||||||
export const supabaseSettingsRepo = {
|
export const supabaseSettingsRepo = {
|
||||||
async get(): Promise<AppSettings> {
|
async get(): Promise<AppSettings> {
|
||||||
const { data, error } = await supabase
|
const { data, error } = await supabase
|
||||||
@@ -20,12 +25,12 @@ export const supabaseSettingsRepo = {
|
|||||||
return DEFAULT_SETTINGS
|
return DEFAULT_SETTINGS
|
||||||
}
|
}
|
||||||
|
|
||||||
return { id: 'singleton', groupLabel: (data.group_label as string) ?? 'Cliente' }
|
return { id: 'singleton', groupLabel: normalizeGroupLabel((data.group_label as string) ?? 'Cliente') }
|
||||||
},
|
},
|
||||||
|
|
||||||
async update(updates: Partial<Omit<AppSettings, 'id'>>): Promise<void> {
|
async update(updates: Partial<Omit<AppSettings, 'id'>>): Promise<void> {
|
||||||
const patch: Record<string, unknown> = { updated_at: new Date().toISOString() }
|
const patch: Record<string, unknown> = { updated_at: new Date().toISOString() }
|
||||||
if (updates.groupLabel !== undefined) patch.group_label = updates.groupLabel
|
if (updates.groupLabel !== undefined) patch.group_label = normalizeGroupLabel(updates.groupLabel)
|
||||||
|
|
||||||
const { error } = await supabase
|
const { error } = await supabase
|
||||||
.from('app_settings')
|
.from('app_settings')
|
||||||
|
|||||||
Reference in New Issue
Block a user