# 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>) => Promise 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() 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)