teqo-backup/docs/changelog/2026-08-23-e1.md

1.8 KiB

E1 — extração do hook compartilhado de busca assíncrona com debounce (2026-08-23): a 3ª cópia do efeito de busca async (requestId + setTimeout(250) + loading/failed) — introduzida pela C143 em DemandResponsibleMultiSelect (demand), e presente em ResponsibleMultiSelect (C90) e AsyncSearchCombobox — vira o hook useAsyncSearchOptions({ open, query, search, isQueryReady? }) → { options, loading, failed } (src/hooks/useAsyncSearchOptions.ts, novo; constante ASYNC_SEARCH_DEBOUNCE_MS exportada). Os 3 componentes consomem o hook e mantêm só o que é seu (grupos por typeLabel, avatares, headings de sugestões do município, pinnedOptions, filtro de selecionados, mensagens de vazio); semântica de query vazia preservada por consumidor (Demand busca sempre → sugestões; ResponsibleMultiSelect reseta via isContactSearchQueryReady; combobox com gate configurável — o efeito sync selected←value fica no componente). Fix pontual: server actions intocadas (continuam props — hook client-safe por injeção), sem schema/migration/access/Consent. Testes: spec unit novo tests/unit/useAsyncSearchOptions.unit.spec.ts (renderHook + fake timers, 9 casos: debounce/trim, coalesce, gate reset, closed, failed + retry, in-flight loading, out-of-order, quirk gate→not-ready com resposta stale pinado — comportamento herdado dos 3 originais, registrado na decisão D3 do impl plan). Impl plan docs/plans/extrair-hook-busca-assincrona-debounce-impl.md. Verificação: unit 2428 + int 815 verdes, e2e activity/demand (campaignActivity, campaignRegisterDemand, campaignDemandVisibility) verdes em re-run (1 flake de cold-compile pré-existente isolado), tsc/lint/format/cycles/build verdes; knip com o erro de load pré-existente (exit 0); fora de escopo: WizardMunicipalitySearchStep (4ª variante, AbortController — defer com gatilho no plano).