docs(E1): triage de débitos aplicada — refs .spec.tsx residuais no impl plan

This commit is contained in:
Francisco Solla 2026-08-23 12:56:49 -03:00
parent 1c865bd822
commit feb8e6cab8

View file

@ -66,7 +66,7 @@ Alternativas rejeitadas: B — a mecânica requestId/gate/debounce é a parte ma
- **`ResponsibleMultiSelect`** (`src/components/campaign/shared/ResponsibleMultiSelect.tsx`): remove L67-76 e o efeito L78-107; hook com `isQueryReady={isContactSearchQueryReady}`. Mantém: `groups` por `typeLabel`, `atCapacity`, mensagem "Digite ao menos dois caracteres…" (o import de `isContactSearchQueryReady` permanece — usado no render, L224), hidden JSON, chips com `typeLabel`.
- **`AsyncSearchCombobox`** (`src/components/campaign/shared/AsyncSearchCombobox.tsx`): remove estados/refs L49-60 e o efeito de busca L66-95; hook com `isQueryReady` (a prop continua no componente — usada no render do vazio, L189). **O efeito sync `selected←value` (L62-64) FICA no componente** (é contrato do combobox, não da busca). Mantém: `pinnedOptions`/`pinnedGroupHeading`, grupos "Seleção"/"Selecionado"/"Resultados", `choose()`, `emptyOptionLabel`, `queryTooShortMessage`.
- **Consumers de `AsyncSearchCombobox`** (`DemandFields.tsx:85`, `ContactCombobox.tsx:38`, `ActivityTaskFields.tsx:98`): intocados (o gate é resolvido dentro do combobox).
- **`tests/unit/useAsyncSearchOptions.unit.spec.tsx`** (novo): ~5 `it` — debounce só dispara após `ASYNC_SEARCH_DEBOUNCE_MS`; query rápida em sequência cancela o timer anterior (só a última busca); gate não-pronto → reset sem chamar `search`; resposta fora de ordem descartada (requestId) e `catch` → `failed`, `finally` → `loading=false`; `open=false` não busca.
- **`tests/unit/useAsyncSearchOptions.unit.spec.ts`** (novo): ~5 `it` — debounce só dispara após `ASYNC_SEARCH_DEBOUNCE_MS`; query rápida em sequência cancela o timer anterior (só a última busca); gate não-pronto → reset sem chamar `search`; resposta fora de ordem descartada (requestId) e `catch` → `failed`, `finally` → `loading=false`; `open=false` não busca.
- **Migration:** nenhuma (refactor client, sem schema). **Access / Consent:** nenhum (server actions intocadas; sem escrita nova, PII ou chave). **UI:** Impeccable A — refactor estrutural sem superfície nova.
### Dados → forma
@ -75,7 +75,7 @@ N/A — refactor de estado de UI; nenhuma métrica, série, ranking ou mapa novo
## Fases verificáveis
1. **Tracer / schema+server** — nada de schema/server (as server actions já são props dos 3 componentes e ficam intocadas; sem migration; sem access/Consent). O tracer é o próprio hook: criar `src/hooks/useAsyncSearchOptions.ts` + `tests/unit/useAsyncSearchOptions.unit.spec.tsx` e rodar `pnpm gate:fast` — a mecânica requestId/debounce/gate fica provada isolada (renderHook + fake timers) antes de tocar os 3 componentes.
1. **Tracer / schema+server** — nada de schema/server (as server actions já são props dos 3 componentes e ficam intocadas; sem migration; sem access/Consent). O tracer é o próprio hook: criar `src/hooks/useAsyncSearchOptions.ts` + `tests/unit/useAsyncSearchOptions.unit.spec.ts` e rodar `pnpm gate:fast` — a mecânica requestId/debounce/gate fica provada isolada (renderHook + fake timers) antes de tocar os 3 componentes.
2. **UI** — migrar os 3 componentes (ordem: `AsyncSearchCombobox` e `ResponsibleMultiSelect` em `shared/` primeiro, depois `DemandResponsibleMultiSelect` em `demand/`), mantendo em cada um só o que é seu (D4); registrar o hook com seu type público `useAsyncSearchOptions` (assinatura + `UseAsyncSearchOptionsResult<T>` exportados — sem pin a atualizar: `src/hooks/` não é pinado; knip valida o consumo). Rodar os e2e afetados da superfície antes do push: `pnpm test:e2e:affected` (activity + demand).
3. **Gates** — `pnpm gate:fast`; `pnpm check:cycles` (hook é folha — sem ciclo), `pnpm format:check`, `pnpm exec knip`, `pnpm test` (unit + int — incl. `campaignDemandWorkflow.int.spec.ts`, que cobre o record de responsáveis C143), `pnpm build`; push via `pnpm push`.