teqo-backup/docs/plans/wizard-registro-sinal.md
2026-07-30 04:02:30 -03:00

7.3 KiB
Raw Permalink Blame History

Wizard registrar sinal — escolher tipo + detalhar texto

Status: entregue (2026-07-30) Atualizado em: 2026-07-30 Item do roadmap: docs/roadmap.md (Trilha B, item B63 — UX-1 wizards) Impeccable: C — UI nova (grid de tipos + drawer de info + passo de texto) Appetite: ~1–1,25 dia eng; 2 etapas no B59; write via action createMunicipalityUpdate existente Responsável: —

Contrato de URL (as-built)

/campanha/acoes/registrar-sinal
  → B60 WizardMunicipalitySearchStep

/campanha/acoes/registrar-sinal?municipio=<slug>
  → WizardSignalTypeStep (grid)

/campanha/acoes/registrar-sinal?municipio=<slug>&signalType=<enum>
  → WizardSignalBodyStep (textarea + Salvar)

Query opcional (propagar entre passos):
  ?entry=register-signal|update-votes|…

Helpers em src/lib/campaignActionRoutes.ts: WIZARD_SIGNAL_TYPE_QUERY_KEY, WIZARD_ENTRY_ACTION_QUERY_KEY, parseWizardEntryActionParam, resolveWizardSignalTypeParam, wizardSignalHref. Skip: shouldShowWizardSignalSkip em wizardSignalUi.ts — oculto quando entryAction ausente ou register-signal; visível caso contrário (href → /campanha na v1).

Design (Impeccable)

Âncoras: PRODUCT.md / DESIGN.md · Drawer shadcn · tema campaign · labels de B62.

Na implementação: shape → craft → critique → polish.

Brief compacto:

  • Persona: CG/assessor no polegar; acabou de ouvir no Zap e precisa classificar o fato sem jargão.
  • Job principal: escolher o tipo de sinal em 1 toque (com info sob demanda) e, se quiser, detalhar em texto; ou pular quando o sinal não era o motivo do wizard.
  • Estratégia de cor: Restrained — fundo transparente, borda cinza clara (border-border), título/ícone foreground; sem fill primary nos tiles.
  • Edit where you see: não — fluxo com Salvar explícito no passo de texto.
  • Anti-goals: select nativo escondido; “broker”; obrigar sinal quando o fluxo veio de votos; cards com sombra/elevação.

Dados → decisão → apresentação

Dados: N/A como métrica — tipo categórico + texto livre. Decisão = classificar o fato e se registrar agora ou pular.

Contexto

Após B62, o modelo é signalType + body. Pedido (2026-07-29, gate confirmado):

Etapa 1 — tipo

  • Cada tipo = botão quadrado grande, cantos arredondados; ícone canto superior esquerdo; título + descrição curta alinhados ao inferior esquerdo; fundo transparente + borda cinza clara; título/ícone pretos (foreground).
  • Ícone de informação no tile → bottom Drawer com: o que é, consequências, quando usar (copy longa; provisória ok).
  • Grid; alinhamento do bloco: bottom no mobile, top em tablet/desktop (prop contentAlign do B59).
  • Topo direito: “Pular registro de sinal” — exceto quando a ação que abriu o wizard foi register-signal (B45).

Etapa 2 — texto

  • Texto livre para detalhar.
  • “Pular registro de sinal” ainda no topo direito (mesma regra de origem).
  • Inferior direito: “Salvar” → cria municipalityUpdate kind: 'sinal'.

Município: já escolhido (B60) ou pré-preenchido se veio embutido de A1.

Objetivos

  • Duas telas/passos no shell B59: WizardSignalTypeStep + WizardSignalBodyStep.
  • Catálogo de tiles: mapear os 5 municipalitySignalTypes → ícone Lucide + label/description curta (B62) + infoContent (markdown/plain).
  • Info: botão Info com stopPropagation para não selecionar o tipo ao abrir o Drawer.
  • Select de tipo = avança para o passo de texto (sem CTA intermediário).
  • Skip: Link/button → próximo bloco do fluxo pai ou Início; hidden se entryAction === 'register-signal'.
  • Salvar: createMunicipalityUpdate / formAction existente com overrideAccess: false; pending no botão (Feel the action).
  • Atualizar B26 list control não é obrigatório a adotar o grid (pode permanecer select pós-B62); opcional Adiado.
  • Sem migration (B62 já migrou). Sem Consent.
  • Unit/e2e smoke: skip oculto no entry signal; info não seleciona tipo; salvar exige tipo+body.

Decisões travadas

  • Duas etapas (tipo → texto), um item de roadmap. Rejeitado: IDs separados (texto é fino demais sozinho).
  • Skip visível só se a origem ≠ register-signal. Rejeitado: sempre mostrar (permitiria “registrar sinal” sem sinal); nunca mostrar (bloqueia “quer também?” após votos).
  • Tiles transparentes + borda, não filled primary. Rejeitado: cards elevados / Signal Red no idle.
  • Info = Drawer bottom (pedido). Rejeitado: Tooltip only (touch); Dialog centrado.
  • i18n: signalType, entryAction, skipSignal; copy “Pular registro de sinal” / “Salvar”.

Questões em aberto

  • Após Salvar no fluxo standalone A2: ir ao Início ou oferecer “ajustar votos”? Opções: A Início + toast | B “Quer atualizar votos?”. Recomendação: A no v1; B no wiring A1/A2 contínuo. (assumido)

Abordagem proposta

flowchart TB
  Entry["origem: register-signal | embutido"] --> Type["grid tipos"]
  Type -->|info| Drawer["Drawer detalhes"]
  Type -->|select| Body["textarea"]
  Type & Body -->|skip se permitido| Skip["próximo / Início"]
  Body -->|Salvar| Action["createMunicipalityUpdate"]

Componentes:

  • WizardSignalTypeStep + WizardSignalBodyStep em components/campaign/municipality/ (ou shared/ se 2º call site).
  • municipalitySignalTypeMeta em lib/ (ícone id, short description, long info) — client-safe; labels de B62.
  • Reuso: Drawer, Button, B59 trailing slot + contentAlign="end" no mobile da etapa de tipo.
  • Migration: Sem migration (depende de B62).

Dependências

  • Dura: B59, B62. Soft: B60 (município no standalone). Soft: B45 ✓ (register-signal id).

Não escopo

  • Dropar fonte/triangulado / labels → B62 (pré-requisito).
  • Wiring “quer também?” dentro de A1 → fatia A1.
  • Trocar B26 para o grid de tiles → Adiado.

Rabbit holes

  • CMS dos textos de info. Mitigação: constantes em lib/ até R6/assessoria.
  • Mais de 5 tipos / multi-select. Mitigação: um tipo por sinal (C12).

Adiado com gatilho

  • Grid de tipos no Popover B26. Revisitar se o select pós-B62 ainda gerar dúvida de classificação em campo.
  • Copy longa revisada pela assessoria. Revisitar no R6.
  • Fundir wizardSignalHref em wizardActionHref. Revisitar quando B64+ compartilhar o mesmo builder de query (hoje são contratos distintos: signalType vs opções genéricas).

Já resolvido no simplify (não reabrir)

  • B75 chrome: flowTitle, isEntryStep, skip no CampaignWizardShell + WizardSignalSkipTrailing no desktop.
  • Skip DRY: resolveWizardSignalSkip em wizardSignalUi.ts (substitui wizardSignalSkipHref).
  • Lookup O(1): municipalitySignalTypeMetaByType; tiles com <Link> (prefetch) em vez de router.push.
  • URL ?entry= alinhada a B70/B77 (não ?entryAction=).

Referências