mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 06:43:06 +00:00
docs(B68): mark delivered and register B68+ post-simplify perf debt.
Close roadmap/plan status after rebase conflict merge and capture simplify deferrals (RSC embed suggest, shared deficit comparator).
This commit is contained in:
parent
e733efbe42
commit
f243c8a88a
3 changed files with 61 additions and 0 deletions
58
docs/plans/escala-dry-pos-b68.md
Normal file
58
docs/plans/escala-dry-pos-b68.md
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
# Escala e DRY pós-B68 (sugestões na busca aberta)
|
||||
|
||||
Status: rascunho (débito pós-`/simplify` 2026-07-29)
|
||||
Atualizado em: 2026-07-29
|
||||
Item do roadmap: [docs/roadmap.md](../roadmap.md) (Fill-ins abertos, **B68+**)
|
||||
Impeccable: A — perf/estrutura de loader; sem pixel novo
|
||||
Appetite: ~0,5–0,75 dia eng (F1); F2 cortável
|
||||
Responsável: —
|
||||
|
||||
## Dados → decisão → apresentação
|
||||
|
||||
- **Vou apresentar dados?** Não (N/A) — mesmos ~8 hits já mostrados; só muda de onde vêm no primeiro focus.
|
||||
|
||||
## Contexto
|
||||
|
||||
**B68 ✓ (2026-07-29)** entrega sugestões de municípios no empty state da busca do Início (`mode: 'suggest'` em `POST /campanha/home-search`). O `/simplify` do fechamento mediu que, para **coordenador/candidato**, cada focus dispara um POST que repete `loadMunicipalityScope` + `loadMunicipalityGoalCoverageBundle` — trabalho que a mesma página já pagou em `loadCampaignHomeSummary` no RSC. `React cache()` não cruza a fronteira RSC ↔ route handler.
|
||||
|
||||
O simplify da sessão já aplicou mitigação parcial: o bundle E8 roda só em municípios `priority === 'alta'` (não nas 435 linhas) e `toHomeSearchMunicipalityHit` unificou o shaping dos hits.
|
||||
|
||||
## Objetivos
|
||||
|
||||
- **F1:** primeiro focus no Início staff **não** refaz scope+coverage no POST quando o RSC da página já carregou os insumos — embed `initialSuggest` (ou loader irmão no mesmo `Promise.all` da página) e hidratar `useHomeSearchResultsState` com TTL curto / invalidação ao navegar.
|
||||
- Sem mudar a política de ranking (assessor = frescor; CG = déficit + frescor).
|
||||
- Sem migration/collection/Consent.
|
||||
|
||||
## Já resolvido no simplify/critique (não reabrir)
|
||||
|
||||
- `aria-busy` duplicado (`resultsBusy ?? isDebouncing`).
|
||||
- Shell de erro sem `errorMessage` nullable intermediário.
|
||||
- `id` removido do tipo puro do ranker.
|
||||
- Factory `toHomeSearchMunicipalityHit` nos dois loaders.
|
||||
- Bundle de cobertura restrito a `priority === 'alta'`.
|
||||
|
||||
## Fases
|
||||
|
||||
### F1 — RSC embed do suggest (P1 perf)
|
||||
|
||||
- Estender `loadCampaignHomeSummary` ou extrair `loadHomeSearchSuggestHits` reutilizável no mesmo request da página (`page.tsx`).
|
||||
- Passar `initialSuggest` para `CampaignHomeStaffChrome` → `useHomeSearchResultsState({ initialSuggest })`.
|
||||
- Client: usar payload inicial quando `uiFocused && !query.isActive` e não houver stale/error; POST só para revalidação explícita ou após TTL (opcional v1: só primeiro paint).
|
||||
|
||||
### F2 — Comparator compartilhado de déficit (cortável)
|
||||
|
||||
- Extrair `compareNullableNumberDesc` + tiebreak hook para `lib/municipalitySort.ts` quando houver 3º call site com política diferente de tiebreak (hoje: B20 nome, B68 frescor, E9 lista).
|
||||
|
||||
## Explicitamente fora / Adiado com gatilho
|
||||
|
||||
- **Cache client-side / stale-while-revalidate no refocus** — aceito no plano B68 (fetch ao focus); revisitar se sessão reclamar de flash de loading.
|
||||
- **Unificar constante `8`** (`HOME_SEARCH_SUGGEST_LIMIT` vs `DASHBOARD_PRIORITY_SAMPLE_LIMIT`) — cosmético; mover para `lib/` só se um dos dois mudar.
|
||||
- **`loadTerritoryOverview` em todo keystroke da busca** — débito adjacente B48 (search path), não B68; gatilho: latência perceptível ao digitar após B49+.
|
||||
- **Mesclar `loadHomeSearchSuggestions` com `searchHomeMunicipalities`** — dois call sites; extrair só o mapper (já feito).
|
||||
|
||||
## Referências
|
||||
|
||||
- [sugestoes-busca-vazia-inicio.md](sugestoes-busca-vazia-inicio.md) (B68)
|
||||
- [`loadHomeSearchSuggestions.ts`](../src/utilities/homeSearch/loadHomeSearchSuggestions.ts)
|
||||
- [`campaignDashboardData.ts`](../src/utilities/campaignDashboardData.ts) (`loadCampaignHomeSummary`)
|
||||
- [`HomeSearchResultsContext.tsx`](../src/components/campaign/dashboard/HomeSearchResultsContext.tsx)
|
||||
|
|
@ -111,6 +111,8 @@ Componentes:
|
|||
- **Grupos Lideranças / Demandas / Atividades / Assessores no empty.** Revisitar quando **B49–B53** landarem **e** o empty de municípios já estiver em uso (evidência de “falta X”).
|
||||
- **Visitados recentes no topo do suggest.** Revisitar se sessão observada pedir continuidade de sessão no Início (não só no Quadro).
|
||||
- **Ranking “mais inteligente”** (déficit × classe E10 × nível E14). Revisitar após 2 semanas de uso real do v1 simples.
|
||||
- **RSC embed do suggest (evitar POST duplicado no focus).** Registrado como **B68+** F1 — revisitar se latência no focus for P1 em produção.
|
||||
- **Comparator compartilhado de déficit** (B20 vs B68 vs E9). Revisitar no 3º call site — ver **B68+** F2.
|
||||
- **Soft deadline UX-1 03/08:** se folga apertar, este item é cortável depois de B66 (busca ainda funciona digitando).
|
||||
|
||||
## Referências
|
||||
|
|
|
|||
|
|
@ -183,6 +183,7 @@ O discovery literatura→persona→entrevista ([relatório aprovado](research/re
|
|||
- ~~**B33+**~~ ✓ escala/DRY pós-B33 (entregue 2026-07-27) — `useCampaignListFilterNavigation` em `shared/` absorve o scaffold de navegação/debounce dos três filtros (**325 linhas removidas contra 228 em `src`**, das quais 138 são o hook novo; JSX só se moveu onde era bug); a auditoria derrubou o racional das "assinaturas diferentes" (os três `buildXListHref` já canonicalizam, então um só callback `toHref` basta) e achou **dois** bugs de comportamento: dobradinhas e territórios descartavam a busca digitada ao tocar um filtro (unificado na semântica de municípios via `navigateWithSearch`) e o timer do debounce capturava o `state` do render que o agendou, revertendo um sort clicado dentro da janela de 1s — municípios escapava pelo `key=` da página, que remonta e mata o timer (corrigido com `stateRef`, com caso de spec verificado vermelho nas três listas sem o fix). O `/simplify` somou o fallback de transition copiado em 5 lugares (o hook era o 6º) virando `useCampaignListTransition`, e um `aria-live` a menos; a 2ª rodada fechou a ressincronização da caixa de busca com o `q` commitado (o que permitiu **remover** o `key=` de municípios e unificar a política de montagem das três listas — `buildMunicipalityFiltersKey` orfanou e morreu, junto com `shouldUpdateMunicipalitySearchUrl`), colapsou `setSearch`+`scheduleSearch` num `onSearchChange` (superfície de 8 → 6 nomes), passou a reusar o `buildMunicipalityFilterHref` que já existia + criou o irmão `buildStateDeputyFilterHref` (4 sítios pinavam página 1 à mão), e deu a municípios o resumo de filtros ativos que faltava no bloco mobile; `campaignListFilterNavigation.unit.spec.ts` (10 casos × 3 listas) é a primeira cobertura desses três componentes. **Dívidas deferidas com gatilho** (no plano, § "Adiado com gatilho"): shell de `<form>` compartilhado (gatilho B29), `CampaignListSortSelect` (bloqueado numa decisão de altura de select, já divergida entre as três), `serialize`/`parseXSortValue` triplicado → `campaignListUrl`, `SupporterFilters`/`ActivityFilters` reconstruírem href à mão tendo builders pinados, ~10 kB gzip do catálogo no bundle cliente de `/campanha/municipios` (pré-existente, achado da review de performance — **gatilho já disparado duas vezes**: B41 ✓ e B42 ✓ entregaram na rota sem pagar, então deixou de ser condicional e é a dívida de bundle corrente da rota) e o 3º idioma de mock de router nos specs · [plano](plans/escala-dry-pos-b33.md)
|
||||
- ~~**B32+**~~ ✓ escala/DRY pós-B32 (entregue 2026-07-28) — **+564 / −802 linhas** em 15 arquivos mais quatro novos. A auditoria mudou três premissas antes do código: **`display` vs `draft` nunca divergiu** (os dois eram setados juntos em todos os caminhos — um valor com dois nomes; só o `noteDraft` do Trend, texto cru vs. nota normalizada, ficou no chamador), **`reportFailure` não nascera nos quatro controles** (o de assessores não importava `sonner`, então falha depois do fechamento era muda — furo do B42, fechado aqui) e **o helper do F2 não resolvia o motivo do F2**. Por isso F2 virou **wrapper de rota**: `campaignJsonMutationRoute(config, handler)` exporta o `POST` já com origem verificada, corpo parseado e `catch` mapeado, com o `bodySchema.parse` **dentro do mesmo `try`** para preservar a mensagem de hoje em corpo inválido; `parseCampaignJsonRequestBody`/`campaignJsonMutationErrorResponse` deixaram de ser exportados, `engagement-level` mantém o 409 dentro do handler, e um **guard de convenção** (molde do de `formActions`, verificado vermelho com um `route.ts` probe) impede a 6ª rota de nascer fora — é o que troca a política de origem de disciplina de cópia por estrutura. F3 é `postCampaignJson` (25 linhas, cinco call sites). F1 é `useCampaignCellAutosave`: **um** valor, `flush()` idempotente como caminho único de save (o `inFlightRef` do B24 entrou no hook e passou a proteger os três), o hook **dono do `open`** porque fechar É o commit, `valueRef` avançado sincronamente para não reler o render que agendou a mudança (bug que B33+ e B34 já pagaram) e `reportFailure` embutido; spec novo com fake timers cobre debounce, coalescência, resposta superada, flush no fechamento e revert com toast só depois de fechado — antes essa máquina só tinha rede e2e. F4 moveu a região `aria-live` para **fora** do Popover/Drawer (prop `statusMessage` na casca, que permanece montada). F5 deu `footer` ao `variant="sheet"` e **o Sinal migrou**: o `formId` que o plano queria na casca é desnecessário — `<form id>` no chamador + `<button form={id}>` no footer é HTML padrão, e com isso `MunicipalityListSignalControl` perdeu o Popover/Drawer próprios e `campaignCellEditTriggerClassName` virou privada. Gate: 661 unit / 444 int, e2e de tendência e de sinal reexecutados isoladamente (as falhas da suíte cheia foram compilação a frio sob duas outras sessões `next dev` no mesmo host, o padrão já fichado em B27 ✓/B32 ✓/B42 ✓) · [plano](plans/escala-dry-pos-b32.md)
|
||||
- ~~**B17+**~~ ✓ escala/DRY pós-B17 (entregue 2026-07-29) — duas fases independentes do `/simplify` do seletor de colunas, ambas de estrutura e nenhuma de cálculo (o corte server-side do B17 já economiza as 25 chamadas de `cell()` e o payload RSC da coluna oculta): **F1** esconder a coluna **não** desliga a query que existe só para ela — `loadLeadershipOptions` (dobradinhas) e `loadStateDeputyOptions` (lideranças) são `limit: 0, pagination: false` dentro do `Promise.all`, e o cookie é lido **depois**; como `cookies()` não faz I/O, subir a leitura e condicionar a promise é ordem de statements, e a rota que mais paga é `/campanha/liderancas` (~229 ms / 371 statements, P1 no TECH-DEBT); **F2** o botão "Colunas" some junto com a tabela no estado vazio em demandas/organizações/apoiadores, que trocam a tabela inteira por um bloco (`rows.length ? … : …`) em vez de usar a prop `empty` que a `CampaignTable` expõe e que municípios/lideranças/territórios já adotam — filtrar até zero linhas tira um controle que não tem relação com o filtro · ~0,5d, F2 cortável · Impeccable A (F1) · [plano](plans/escala-dry-pos-b17.md)
|
||||
- **B68+** escala/DRY pós-B68 — **F1** embed RSC de suggest no Início (evitar POST duplicado scope+coverage no focus CG/candidato; `initialSuggest` hidratando `useHomeSearchResultsState`); **F2** cortável: comparator compartilhado de déficit central quando 3º call site · gatilho: latência observada no focus ou próximo touch em ranking · ~0,5–0,75d eng · Impeccable A · [plano](plans/escala-dry-pos-b68.md)
|
||||
- Higiene PascalCase de componentes legados
|
||||
|
||||
### A validar (assumptions)
|
||||
|
|
|
|||
Loading…
Reference in a new issue