# Sprint 4 — Etapa 7: ResourcesPanel — UX de catálogo global

## Contexto

InQ ROI — React 18 + Vite + TypeScript + TanStack Router + Zustand + shadcn/ui + Tailwind.
Etapas 1–6 completadas. El backend de recursos ya es global desde Etapa 3: `resource-repo.ts` hace `getAll()` sin filtro, el upsert no envía `processId`, y la tabla `resources` en Supabase no tiene columna `process_id`.

Lo que falta es UX: el `ResourcesPanel` todavía muestra todos los recursos sin filtrar (con el header "Recursos del proceso" que ya no corresponde) y no existe página de catálogo.

**Este prompt tiene un mockup aprobado como contrato visual. Claude Code lo replica sin interpretar libremente.**

---

## Mockup aprobado — descripción exacta

### Vista 1: ResourcesPanel en workspace (panel lateral derecho)

**Header del panel:**
- Título: "Recursos" (no "del proceso")
- Botón naranja `#F59845` con ícono `+` y texto "Nuevo" — abre un Sheet/Dialog para crear recurso en el catálogo global

**Cuerpo del panel:**
- Subheader: "Usados en este proceso" + cuenta `(N)`
- Lista de recursos que tienen al menos una asignación en las actividades del proceso actual
  - Cada ítem: avatar circular con ícono del tipo (ver iconos y colores más abajo) + nombre del recurso + costo/hora a la derecha
  - Segunda línea: tipo en texto + "N actividades" (contar cuántas actividades de este proceso usan este recurso)
- Si no hay recursos asignados: estado vacío con mensaje "Ningún recurso asignado a actividades aún"

**Footer del panel:**
- Texto de contexto: "Recursos del catálogo global del equipo"
- Botón secundario ancho completo: ícono `external-link` + "Ver catálogo completo" → navega a `/recursos`

**La lista de recursos "usados en este proceso" se deriva así:**
```typescript
const usedResourceIds = new Set(
  currentProcess.activities
    .flatMap(a => a.assignedResources ?? [])
    .map(ar => ar.resourceId)
)
const usedResources = resources.filter(r => usedResourceIds.has(r.id))
const usageCount = (resourceId: string) =>
  currentProcess.activities.filter(
    a => (a.assignedResources ?? []).some(ar => ar.resourceId === resourceId)
  ).length
```

---

### Vista 2: Página catálogo `/recursos`

**Header de la página:**
- Título: "Catálogo de recursos"
- Subtítulo con ícono de equipo: "Compartido con todo el equipo · N recursos"
- Botón naranja `#F59845`: "+ Nuevo recurso" → abre Sheet/Dialog de creación

**Barra de herramientas:**
- Input de búsqueda (filtra por nombre en tiempo real, client-side)
- Select de tipo (filtra por tipo: "Todos los tipos" + los 5 tipos)

**Tabla:**
| Recurso | Tipo | Costo/h | Creado por | Acciones |
|---------|------|---------|-----------|---------|
| [avatar + nombre] | [badge] | [mono] | [nombre] | [edit] [delete] |

- Botones de acción: editar (abre Sheet con form pre-poblado) y eliminar (con confirmación)
- "Creado por" se resuelve desde el JOIN con `users` (igual que en LibraryPage — mismo patrón de Etapa 6)

---

## Tipos de recurso — valores definitivos

**Los únicos valores válidos son:** `person | role | system | equipment | supply`

Estos son los valores del schema real (migración 005). Actualizar `ResourceType` en `domain/types.ts`:

```typescript
export type ResourceType = 'person' | 'role' | 'system' | 'equipment' | 'supply'
```

**Labels en español para la UI:**
```typescript
const RESOURCE_TYPE_LABELS: Record<ResourceType, string> = {
  person:    'Persona',
  role:      'Rol',
  system:    'Sistema',
  equipment: 'Equipo',
  supply:    'Insumo',
}
```

**Íconos y colores por tipo (Lucide React / Tabler):**
```typescript
// Usando Lucide React (ya en el stack):
person:    User     — fondo #FEF3E7, ícono #F59845
role:      Briefcase — fondo #FEF3E7, ícono #D97706
system:    Server   — fondo #EEF2FF, ícono #6366F1
equipment: Wrench   — fondo #F0FDF4, ícono #16A34A
supply:    Package  — fondo #F1F5F9, ícono #64748B
```

---

## Alcance estricto — qué entra en esta etapa

### 1. Limpieza de tipos

En `src/domain/types.ts`:
- Actualizar `ResourceType` a los 5 valores definitivos (ver arriba)
- Eliminar `processId?: UUID` de la interfaz `Resource` — es un campo vestigial sin uso. Ya no se mapea ni se envía

En `src/persistence/supabase/resource-repo.ts`:
- Eliminar cualquier referencia a `processId` si existe en `fromRow` o `toRow`

### 2. Página de catálogo — nueva ruta `/recursos`

Crear `src/features/resources/ResourceCatalogPage.tsx` con:
- Header + búsqueda + filtro por tipo + tabla (ver mockup)
- Sheet/Dialog compartido para crear y editar recursos (reutilizar el mismo componente para ambas acciones)
- Confirmación antes de eliminar (no eliminar recursos que están asignados a actividades — mostrar error explicativo si hay asignaciones activas)

**JOIN con users para "Creado por"** (mismo patrón que Etapa 6):
```typescript
// En resource-repo.ts, método getAll():
const { data } = await supabase
  .from('resources')
  .select('*, creator:users!created_by(id, name)')
  .order('name', { ascending: true })
```

Agregar tipo extendido `ResourceWithCreator` en `resource-repo.ts` (igual que `ProcessWithUserNames` en Etapa 6).

**Ruta nueva en TanStack Router:** `src/router.tsx` o donde estén definidas las rutas.

### 3. Navegación al catálogo

Agregar acceso a `/recursos` desde la app. Opciones (elegir la más limpia según la estructura de nav existente):
- Link en AppHeader junto al logo/título
- O link desde el botón "Ver catálogo completo" del ResourcesPanel (este ya está en el mockup)
- No agregar ítem de navegación lateral si no existe nav lateral — el acceso desde el panel es suficiente para Sprint 4

### 4. ResourcesPanel — redesign

Reemplazar el cuerpo actual de `src/features/workspace/ResourcesPanel.tsx` según el mockup aprobado:
- Lista filtrada "usados en este proceso" (ver derivación arriba)
- Botón "Nuevo" → Sheet/Dialog de creación (el mismo componente de la página de catálogo)
- Link "Ver catálogo completo" → navega a `/recursos`
- El CRUD completo (editar, eliminar, listar todos) vive en `/recursos`, no en el panel del workspace

### 5. ActivityPanel — sin cambios

El dropdown de asignación de recursos ya funciona correctamente con el catálogo global. No tocar.

---

## Qué NO entra en esta etapa

- Gestión de grupos de usuarios
- Permisos por recurso (los recursos son siempre visibles para todos los autenticados)
- Import/export de catálogo
- Historial de uso de un recurso (cuántos procesos lo usan)
- Cambios en PDF, simulación o workspace salvo ResourcesPanel
- Tests de integración de auth → Etapa 8

---

## Orden de ejecución

1. Actualizar `ResourceType` en `domain/types.ts` + eliminar `processId?` de `Resource`
2. Limpiar `resource-repo.ts` (eliminar processId, agregar JOIN con users)
3. Crear `src/features/resources/ResourceCatalogPage.tsx`
4. Registrar la ruta `/recursos` en el router
5. Redesign de `ResourcesPanel.tsx` (mockup como ground truth)
6. `npm run build` — debe pasar sin errores TypeScript
7. `npm run test` — debe mantener 521+ tests verdes
8. Commit: `feat(sprint-4/etapa-7): catálogo global de recursos + redesign ResourcesPanel`

---

## Criterios de validación

- [ ] `ResourceType` tiene exactamente `'person' | 'role' | 'system' | 'equipment' | 'supply'`
- [ ] `Resource` no tiene campo `processId`
- [ ] Ruta `/recursos` existe y carga la página de catálogo
- [ ] Tabla del catálogo muestra nombre, tipo (badge con color), costo/h, creado por
- [ ] Búsqueda client-side funciona por nombre
- [ ] Filtro por tipo funciona
- [ ] Crear recurso desde catálogo funciona (guarda en Supabase, aparece en la lista)
- [ ] Editar recurso funciona
- [ ] Eliminar recurso funciona (con confirmación)
- [ ] ResourcesPanel en workspace muestra SOLO recursos usados en el proceso actual
- [ ] ResourcesPanel "Ver catálogo completo" navega a `/recursos`
- [ ] ResourcesPanel "Nuevo" crea en el catálogo global
- [ ] ActivityPanel sigue funcionando (asignación de recursos sin cambios)
- [ ] Build limpio sin errores TypeScript
- [ ] 521+ tests verdes
- [ ] **Validación visual del director** — ver ResourcesPanel con lista filtrada y página `/recursos` con tabla antes del OK formal

---

## Nota: Sheet vs Modal para el form de recurso

Usar `Sheet` de shadcn/ui (panel lateral deslizante) en lugar de `Dialog` (modal centrado) — es más consistente con el contexto del workspace donde el espacio es lateral. El Sheet se abre desde ambos lugares: botón "Nuevo" del ResourcesPanel y botón "+ Nuevo recurso" de la página de catálogo. Reutilizar el mismo componente `ResourceFormSheet.tsx` en ambos contextos.
