feat(sprint-3): biblioteca, recursos TO-BE, ROI mejorado e identidad de marca

Sprint 3 completo — 7 etapas:

- Etapa 1-2: biblioteca de procesos con agrupación por cliente (Dexie v4,
  LibraryPage, library-store, groupId/tags en Process)
- Etapa 3: modal de configuración global por proceso (horizonte, frecuencia,
  inversión, moneda) con validación y persistencia
- Etapa 4: panel de recursos rediseñado con soporte de unidades y minutos
  de utilización (utilizationMinutes + units)
- Etapa 5: recursos en escenario automatizado — motor de simulación,
  UI de edición en ActivityPanel, comparación AS-IS/TO-BE en reporte
- Etapa 6: PDF recursos automatizados, automatedAssignedResources requerido,
  561 tests pasando
- Etapa 7: identidad de marca — AppHeader gradiente naranja/magenta con logo
  InQuality en 5 páginas, favicon con logo real, logo/marca navegan a home

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-05-27 11:49:43 -03:00
parent d2746f07d1
commit 67e259e6ed
57 changed files with 4372 additions and 82 deletions

View File

@@ -0,0 +1,242 @@
# Sprint 3 — Etapa 3: Tags + búsqueda + configuración de groupLabel
## Contexto
Sos Claude Code trabajando en **InQ ROI**. Las reglas de implementación están en `simulador-web/CLAUDE.md`. El brief del sprint está en `sprints/sprint-3/BRIEF.md`.
**Estado al iniciar esta etapa:**
- Etapa 1 completada: Dexie v4 en producción. `Process.groupId` y `Process.tags` requeridos. `Activity.automatedAssignedResources?` opcional.
- Etapa 2 completada: `LibraryPage` es la home (`/`). `ImportPage` en `/import`. `library-store.ts` con `groups`, `processes`, `settings`. `groupRepo` y `settingsRepo` en `repositories.ts`. La biblioteca muestra grupos, "Sin clasificar", búsqueda global y filtro por tags en modo AND.
**Esta etapa agrega la edición de tags desde el workspace, la configuración de `groupLabel`, y una SettingsPage accesible desde la biblioteca.**
---
## ⚠️ Tarea preflight — verificar antes de cualquier trabajo nuevo
Antes de escribir código nuevo, verificar y corregir el siguiente bug en `src/features/library/LibraryPage.tsx`:
**Bug:** El contador de clientes en el header (`"N procesos · N clientes"`) usa un campo heredado:
```typescript
// LÍNEA ~524 — código actual (INCORRECTO):
const totalClients = new Set(processes.map((p) => p.clientName).filter(Boolean)).size
```
`clientName` es un campo del modelo anterior que ya no refleja la agrupación por grupos. El contador no se actualiza cuando se crea un nuevo grupo porque ningún proceso cambia de `clientName`.
**Fix requerido:**
```typescript
// CORRECTO:
const { load, isLoading, processes, groups } = useLibraryStore()
// ...
const totalClients = groups.length
```
Verificar también que el destructuring en la línea `const { load, isLoading, processes } = useLibraryStore()` (dentro de `LibraryPage`) incluya `groups`.
**Clasificación de iniciativa:** Nivel 2 — cambio visible al usuario (el contador ahora refleja grupos reales). Reportar en el reporte final.
---
## Objetivo
1. **Tags en WorkspacePage:** chip input para agregar y quitar tags al proceso actual. Con autocompletado de tags existentes (case-insensitive). Persiste via `updateProcess({ tags: [...] })`.
2. **Configuración de `groupLabel`:** nueva `SettingsPage` en ruta `/settings`. Permite cambiar el label del nivel de agrupación. Persiste en `AppSettings`. Reactivo: la UI entera refleja el cambio sin recargar.
3. **Acceso a settings desde biblioteca:** ícono ⚙ en el header de `LibraryPage` que navega a `/settings`.
4. **`updateSettings` en `library-store.ts`:** nueva acción que actualiza `AppSettings` en Dexie y en el estado Zustand.
5. **Verificar filtro de tags en biblioteca:** el filtro AND ya existe desde Etapa 2 — confirmar que funciona correctamente con tags reales (test o validación manual).
---
## Cambios autorizados y arquitectura
### `src/store/library-store.ts`
Agregar la acción `updateSettings` a la interfaz y al store:
```typescript
interface LibraryState {
// ... existentes ...
updateSettings: (updates: Partial<Omit<AppSettings, 'id'>>) => Promise<void>
getAllTags: () => string[] // computed — retorna tags únicos de todos los procesos (sin async)
}
```
Implementación:
```typescript
updateSettings: async (updates) => {
await settingsRepo.update(updates)
set((state) => ({ settings: { ...state.settings, ...updates } }))
},
getAllTags: () => {
const set = new Set<string>()
get().processes.forEach((p) => p.tags.forEach((t) => set.add(t)))
return [...set].sort()
},
```
`getAllTags()` es sincrónico porque `processes` ya está cargado en el store desde `load()`. Si el store no está cargado aún, retorna `[]` — el componente maneja este caso.
### `src/features/workspace/WorkspacePage.tsx`
Agregar chip input de tags justo **después del campo `clientName`** (línea ~220 en el archivo actual, después del bloque `{/* Cliente — editable inline */}`).
**Comportamiento:**
- Tags existentes del proceso aparecen como chips removibles con ícono `×`.
- Input inline para agregar nuevos tags: placeholder `"Agregar tag..."`.
- Al escribir, mostrar dropdown con sugerencias de tags existentes (filtradas por lo que se escribe, case-insensitive). Máximo 6 sugerencias.
- Confirmar tag: `Enter` o click en sugerencia.
- Si el tag ingresado ya existe en el proceso (case-insensitive): ignorar silenciosamente.
- Normalización: guardar tags en lowercase para evitar fragmentación (`"BPMN"``"bpmn"`).
- Quitar tag: click en el `×` del chip.
- Guardar: llamar `updateProcess({ tags: newTags })` después de cada cambio (add o remove).
**Fuente de tags para autocompletado:** `useLibraryStore().getAllTags()`. Si `library-store` no está inicializado aún (puede ocurrir si el usuario navega directo a `/workspace/...`), hacer `load()` perezoso o simplemente mostrar sugerencias vacías — no bloquear la UI.
**Nota de arquitectura:** `WorkspacePage` importa de `useProcessStore` para mutaciones y puede importar de `useLibraryStore` para leer la lista de tags globales. Esto es un import de lectura entre stores — aceptable. **No** importar directamente de `repositories.ts`.
**Estilo de chips:**
- Chip de tag: `text-[10px] px-2 py-0.5 rounded-full bg-secondary border border-border text-secondary-foreground`. Mismo estilo que en `ProcessCard` (ya existe en `LibraryPage.tsx`).
- Input de agregar: `text-xs bg-transparent border-b border-muted outline-none w-24` — línea subtil, sin box, para no romper el layout del sidebar.
- Dropdown de sugerencias: posición absolute, `bg-background border border-border rounded-md shadow-sm`, ancho mínimo del input. Z-index 50.
### `src/features/settings/SettingsPage.tsx` (NUEVO)
Nueva ruta `/settings`. Componente simple, sin tabs en esta etapa.
**Layout:**
- Header: título `"Configuración"` (18px, weight 500), subtítulo `"Personalización del workspace"` (12px, color tertiary).
- Botón `"← Volver"` que navega a `/`. Color `#F59845`.
- Sección `"Biblioteca"`:
- Label: `"Etiqueta de grupos"` (13px, weight 500).
- Descripción: `"Cómo se llama el nivel de agrupación en la biblioteca. Por defecto: 'Cliente'."` (12px, color tertiary).
- Input de texto con el valor actual de `settings.groupLabel`. Al blur o al hacer Enter: guardar via `updateSettings({ groupLabel: value.trim() || 'Cliente' })`.
- Preview inline: `"Los procesos se organizarán por [valor]."` que se actualiza mientras se escribe (sin guardar hasta blur/Enter).
- Si el input está vacío o en blanco al salir del foco: revertir a `'Cliente'` (valor default).
**Reactividad:** Como `settings` vive en `useLibraryStore`, cualquier componente que lea `settings.groupLabel` del store se actualiza automáticamente al hacer `updateSettings`. Verificar que `LibraryPage`, `HomeView`, `GroupView` y `GroupCard` usan `settings.groupLabel` del store (ya deberían desde Etapa 2).
### `src/router.tsx`
Agregar ruta `/settings`:
```typescript
import { SettingsPage } from '@/features/settings/SettingsPage'
const settingsRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/settings',
component: SettingsPage,
})
// Agregar settingsRoute al routeTree
```
### `src/features/library/LibraryPage.tsx`
Dos cambios:
1. Fix del bug del contador (ver sección preflight).
2. Agregar ícono de settings en el header: botón con ícono `Settings` (Lucide) que navega a `/settings`. Posicionarlo a la izquierda del botón "Importar BPMN", con estilo ghost (sin fondo, color `text-muted-foreground`, hover `text-foreground`).
---
## Versión con wow (recomendada)
Si el costo de implementación es < 40% sobre la versión mínima, agregar:
**Tag cloud en home de biblioteca:**
- Visible cuando hay al menos 3 tags distintos en la base y no hay búsqueda activa.
- Ubicación: entre la barra de búsqueda y la sección de grupos (igual que la fila de tags actual).
- Los chips de tags muestran tamaño de fuente proporcional a la frecuencia de uso: tags con más procesos → ligeramente más grandes (rango: `text-[10px]` a `text-[13px]`). Máximo 12 tags.
- Click en un chip activa el filtro AND (comportamiento idéntico al existente).
- Si ya hay una fila de tags (línea `{tags.length > 0 && ...}`), reemplazarla con el tag cloud. No duplicar.
**Autocompletado fuzzy en WorkspacePage:**
- En lugar de filtrar por `startsWith`, usar `includes` (substring match). Ejemplo: escribir `"proc"` sugiere `"proceso"` y `"subprocess"`.
---
## ❌ Lo que NO entra en esta etapa
- Renombrar o eliminar grupos desde la biblioteca — pendiente
- Eliminar procesos desde la biblioteca — backlog
- Múltiples niveles de etiquetas configurables — Sprint 4+
- Indicador de simulación desactualizada — Etapa 4
- Zoom/fit canvas BPMN — Etapa 4
- Recursos en escenario automatizado — Etapa 5
- Cambios en el motor de simulación, export PDF/CSV
- Cambios en `process-store.ts` o `simulation-store.ts`
- `Activity.automatedAssignedResources` sigue siendo opcional — no promover a requerido
---
## Verificación antes de entregar
```bash
# 1. Build limpio
npm run build
# 2. Tests (todos los previos deben seguir verdes)
npm run test
# 3. Control de scope
git diff --name-only HEAD
```
El diff debe mostrar únicamente archivos de:
- `src/features/library/LibraryPage.tsx` (fix bug contador + botón settings)
- `src/store/library-store.ts` (nuevas acciones `updateSettings`, `getAllTags`)
- `src/features/workspace/WorkspacePage.tsx` (chip input de tags)
- `src/features/settings/SettingsPage.tsx` (nuevo)
- `src/router.tsx` (nueva ruta `/settings`)
Si aparece cualquier archivo de `src/features/report/`, `src/lib/export/`, `src/domain/simulation.ts`, `src/persistence/db.ts`, `src/store/process-store.ts`, o `src/store/simulation-store.ts`: **revertir y reportar**.
---
## Criterios de aceptación
- [ ] Bug preflight resuelto: el contador `"N clientes"` refleja `groups.length` y se actualiza al crear un grupo.
- [ ] Tags del proceso se muestran como chips removibles en el sidebar de WorkspacePage.
- [ ] Agregar tag funciona: Enter confirma, el tag se guarda en el proceso (persiste al recargar).
- [ ] Autocompletado muestra tags existentes (case-insensitive, substring match en versión wow).
- [ ] Tags normalizados a lowercase al guardar.
- [ ] SettingsPage en `/settings` permite cambiar `groupLabel`.
- [ ] El nuevo label se refleja en toda la UI sin recargar (reactivo via Zustand).
- [ ] Botón ⚙ en LibraryPage navega a `/settings`.
- [ ] Build sin errores TypeScript.
- [ ] Todos los tests previos siguen verdes.
- [ ] Validación visual del director aprobada.
---
## Criterio de wow
Un chip input bien ejecutado es la diferencia entre "edito tags" y "organizo mis procesos naturalmente". El autocomplete que aparece mientras escribís y desaparece sin molestar al salir del foco marca esa diferencia. Apuntar a esa experiencia.
---
## Reporte esperado al terminar
1. Confirmación del fix del bug preflight (línea exacta modificada).
2. Resultado de `git diff --name-only HEAD` con la lista de archivos modificados.
3. Resultado de `npm run build` y `npm run test`.
4. Si se implementó versión wow (tag cloud, fuzzy): declararlo.
5. Cualquier decisión de iniciativa tomada (Nivel 1/2/3).
6. Si hubo algún archivo fuera del scope que requirió modificación: declararlo explícitamente con justificación.
---
## Referencias
- `sprints/sprint-3/BRIEF.md` — sección 4, Feature C
- `src/features/library/LibraryPage.tsx` — estructura actual de la home (chip de tags ya existe en `ProcessCard`)
- `src/features/workspace/WorkspacePage.tsx` — bloque `clientName` editable (~línea 192-220) — el chip input de tags va después
- `src/store/library-store.ts` — store a extender con `updateSettings` y `getAllTags`
- `src/persistence/repositories.ts``settingsRepo.update` ya existe
- `methodology/ANTI_PATTERNS.md` — AP.8 (adelanto silencioso), AP.9 (restricciones contradictorias)
- `methodology/PATTERNS.md` — C.7 (git diff como control preventivo)