# 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)
