# Sprint 2 — Etapa 4: Edición inline de nombre de proceso y cliente

> **Tipo:** UI — un solo componente. Sin cambios en dominio, persistencia ni reporte.
> **Objetivo:** hacer editable el nombre del proceso y el cliente directamente desde el header del workspace. Hoy son campos estáticos (`<p>`); deben convertirse en inputs inline que guardan en IndexedDB al confirmar.
> **Resultado esperado:** click sobre nombre o cliente → input inline → Enter o blur guarda → checkmark naranja breve confirma. Validación visual del director requerida antes del OK formal.

---

## Archivo a modificar

**Un único archivo:** `src/features/workspace/WorkspacePage.tsx`

Si durante la implementación identificás que otro archivo necesita cambio, **detenete y reportá el conflicto antes de tocarlo**. No procedas sin confirmación.

---

## Contexto: estado actual del header

El header del workspace (líneas ~139–144) tiene hoy:

```tsx
<div className="flex-1 min-w-0">
  <p className="text-sm font-semibold text-slate-800 truncate">{currentProcess.name}</p>
  {currentProcess.clientName && (
    <p className="text-xs text-slate-400 truncate">{currentProcess.clientName}</p>
  )}
</div>
```

Ambos son texto estático. La función `updateProcess` ya existe en el store
(`useProcessStore`) y persiste en IndexedDB — no requiere ningún cambio fuera de este archivo.

---

## Cambios requeridos

### 1. Agregar `updateProcess` al destructuring del store

```tsx
// ANTES (línea ~26)
const { currentProcess, activities, isLoading, error, loadProcess, gateways } = useProcessStore()

// DESPUÉS
const { currentProcess, activities, isLoading, error, loadProcess, gateways, updateProcess } = useProcessStore()
```

### 2. Agregar iconos al import de lucide-react

Agregar `Check` y `Pencil` al import existente:

```tsx
import {
  Loader2, BarChart3, Play, AlertTriangle,
  PanelRightClose, PanelRightOpen, Home, GitMerge, MousePointer2,
  Check, Pencil
} from 'lucide-react'
```

### 3. Agregar estado local para edición inline

Insertar antes del `return`, junto a los otros estados del componente:

```tsx
// Estado para edición inline del header
const [editingField, setEditingField] = useState<'name' | 'client' | null>(null)
const [editValue, setEditValue] = useState('')
const [savedField, setSavedField] = useState<'name' | 'client' | null>(null)
const savedTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null)
```

`useRef` ya está importado desde React — no requiere cambio en imports de React.

### 4. Agregar función `saveField`

Insertar junto a las otras funciones del componente (cerca de `handleSimulate`):

```tsx
async function saveField(field: 'name' | 'client') {
  if (field === 'name') {
    const trimmed = editValue.trim()
    if (trimmed && trimmed !== currentProcess?.name) {
      await updateProcess({ name: trimmed })
    }
  } else {
    const trimmed = editValue.trim()
    if (trimmed !== (currentProcess?.clientName ?? '')) {
      await updateProcess({ clientName: trimmed })
    }
  }
  setEditingField(null)
  // Wow: checkmark naranja breve
  setSavedField(field)
  if (savedTimerRef.current) clearTimeout(savedTimerRef.current)
  savedTimerRef.current = setTimeout(() => setSavedField(null), 1500)
}
```

### 5. Reemplazar el bloque estático del header

Reemplazar las líneas ~139–144 (el `<div className="flex-1 min-w-0">` completo) con:

```tsx
<div className="flex-1 min-w-0">

  {/* Nombre del proceso — editable inline */}
  {editingField === 'name' ? (
    <input
      autoFocus
      className="text-sm font-semibold text-slate-800 bg-transparent border-b border-[#F59845] outline-none w-full"
      value={editValue}
      onChange={(e) => setEditValue(e.target.value)}
      onBlur={() => saveField('name')}
      onKeyDown={(e) => {
        if (e.key === 'Enter') { e.preventDefault(); saveField('name') }
        if (e.key === 'Escape') setEditingField(null)
      }}
    />
  ) : (
    <div
      className="flex items-center gap-1 group cursor-text"
      onClick={() => { setEditValue(currentProcess.name); setEditingField('name') }}
    >
      <p className="text-sm font-semibold text-slate-800 truncate">{currentProcess.name}</p>
      {savedField === 'name'
        ? <Check className="h-3.5 w-3.5 text-[#F59845] shrink-0" />
        : <Pencil className="h-3 w-3 text-slate-300 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity" />
      }
    </div>
  )}

  {/* Cliente — editable inline */}
  {editingField === 'client' ? (
    <input
      autoFocus
      className="text-xs text-slate-400 bg-transparent border-b border-[#F59845] outline-none w-full"
      value={editValue}
      onChange={(e) => setEditValue(e.target.value)}
      onBlur={() => saveField('client')}
      onKeyDown={(e) => {
        if (e.key === 'Enter') { e.preventDefault(); saveField('client') }
        if (e.key === 'Escape') setEditingField(null)
      }}
      placeholder="Agregar cliente…"
    />
  ) : (
    <div
      className="flex items-center gap-1 group cursor-text"
      onClick={() => { setEditValue(currentProcess.clientName ?? ''); setEditingField('client') }}
    >
      {currentProcess.clientName
        ? <p className="text-xs text-slate-400 truncate">{currentProcess.clientName}</p>
        : <p className="text-xs text-slate-300 truncate italic">Agregar cliente…</p>
      }
      {savedField === 'client'
        ? <Check className="h-3 w-3 text-[#F59845] shrink-0" />
        : <Pencil className="h-2.5 w-2.5 text-slate-300 shrink-0 opacity-0 group-hover:opacity-100 transition-opacity" />
      }
    </div>
  )}

</div>
```

---

## Comportamientos esperados

- **Click sobre el nombre** → input inline con el valor actual, borde inferior naranja
- **Enter o blur** → guarda, vuelve a modo lectura, aparece ✓ naranja 1.5 segundos
- **Escape** → cancela sin guardar, vuelve al valor anterior
- **Nombre vacío al guardar** → no guarda (el `if (trimmed && ...)` lo previene), vuelve al valor anterior
- **Cliente vacío al guardar** → guarda string vacío (limpiar el cliente es una acción válida)
- **Sin cliente asignado** → muestra "Agregar cliente…" en gris itálico; al hacer click se abre el input vacío
- El ícono ✏️ es invisible hasta hacer hover sobre la fila — indica que es editable sin ocupar espacio visualmente

---

## Verificaciones obligatorias antes de reportar

### 1. Verificación de scope con git diff — OBLIGATORIO
```bash
git diff --name-only HEAD
```

El resultado debe contener únicamente:
```
src/features/workspace/WorkspacePage.tsx
```

Si aparece cualquier archivo no listado:
1. Revertilo: `git checkout HEAD -- <archivo>`
2. Listarlo bajo "Conflictos de scope" en el reporte
3. No incluirlo en el entregable

### 2. Build limpio
```bash
npm run build
```
Sin errores TypeScript.

### 3. Tests
```bash
npm run test
```
Todos los tests existentes deben seguir verdes. Esta etapa no agrega tests nuevos (UI pura, sin lógica de dominio nueva).

### 4. Smoke test manual
- [ ] Click sobre el nombre del proceso → se abre input con valor actual
- [ ] Tipear un nombre nuevo → Enter → se cierra, aparece ✓ naranja breve, el nombre cambió
- [ ] Recargar la página → el nombre nuevo persiste (IndexedDB)
- [ ] Click sobre el cliente → input editable
- [ ] Borrar el cliente completamente → guardar → el campo queda vacío mostrando "Agregar cliente…"
- [ ] Escape cancela sin guardar

---

## Alcance estricto — lo que NO entra en esta etapa

- ❌ `GlobalSettingsPanel.tsx` — no tocar (edición desde settings sigue funcionando igual)
- ❌ Ningún archivo de reporte (`src/features/report/`) — Etapa 3 ya cerrada
- ❌ Ningún archivo de dominio (`src/domain/`) — Etapas 1–3 ya cerraron dominio
- ❌ Ningún archivo de persistencia (`src/persistence/`) — `updateProcess` del store es suficiente
- ❌ PDFs — Etapa 5
- ❌ Edición de campos adicionales del proceso (frecuencia anual, overhead) — BACKLOG

---

## Reporte esperado al terminar

```
## Etapa 4 completada

### Archivos modificados
- src/features/workspace/WorkspacePage.tsx

### Cambios realizados
- Edición inline de nombre de proceso: click → input → Enter/blur guarda
- Edición inline de cliente: click → input → Enter/blur guarda
- Checkmark naranja (#F59845) confirmatorio con timeout 1.5s
- Ícono ✏️ visible al hover, oculto en reposo
- Escape cancela sin guardar en ambos campos

### Verificaciones
- git diff --name-only HEAD: solo WorkspacePage.tsx ✅
- npm run build: ✅
- npm run test: X/X tests verdes ✅
- Smoke test: [descripción breve]

### Conflictos de scope
[ninguno / lista de archivos revertidos y razón]

### Notas para Etapas siguientes
[hallazgos relevantes para Etapas 5+]
```
