Commit graph

81 commits

Author SHA1 Message Date
Francisco Solla
0c0725835b feat(B202): totais do recorte no rodapé da lista de municípios (Closes #1005) 2026-09-15 07:37:45 -03:00
Francisco Solla
b4132b72c6 fix(e2e): B196 atividades deriva a data da fixture de hoje — desbloqueia deploy verify
A fixture B196 'chassis elsewhere' criava 5 atividades com startAt fixo em 2026-09-01. A aba default de /campanha/atividades (proximos) filtra status=confirmado e startAt >= now, então a partir de 02/09 a lista virou empty state, o scrollport perdeu overflow e a assert scrollHeight - clientHeight > 0 falhou nas 3 retries do verify do deploy de 2026-09-11 (run 34652323456), bloqueando o job de deploy.

Deriva o startAt de hoje (+7 dias, tempo civil da Bahia) como os specs de agenda já fazem, dá ao poll do scrollport o orçamento de 30s da classe de latência B196 documentada no arquivo, e adiciona guard ESLint (no-restricted-syntax escopado a tests/e2e) que bane ISO literal em propriedades start*/end*/date* de fixture — pinado em eslintConventions.unit.spec.ts.
2026-09-12 02:17:13 -03:00
Francisco Solla
d29b790306 chore(e2e): E2E-DEBT-S-GATE — extract waitForStreamSettled helper and migrate 21 sites across 5 specs (Closes #827) 2026-08-24 16:01:08 -03:00
Francisco Solla
3b32dbc814 fix(OPS83-recurr): cast rotativa do verify #16 — serializar admin atrás do frontend (stub IG compartilhado) + notificação pula recipient sumido em vez de abortar transação 2026-08-24 15:11:39 -03:00
Francisco Solla
10e0c17078 fix(e2e): OPS83 — polish after review: rename expectSectionCount, toBeOK, settle helper reuse 2026-08-24 07:27:49 -03:00
Francisco Solla
e4cb8d1006 fix(e2e): OPS83 — estabilizar os e2e do verify sem reduzir cobertura; AgendaFeed roda em prod (Closes #824) 2026-08-24 07:27:49 -03:00
Francisco Solla
7de6c01b10 fix(e2e): C142 — settle dossiê tab before asserting leadership link in advisor-scopes journey
The 'advisor scopes municipalities' journey casts many steps; the dossiê
leadership-list chunk can land after goto settles under 4-worker load,
tripping the leadership-link visibility assert. Adds the C106 S: settle
before the assert and scopes with .first() against transient duplicates.
2026-08-23 22:13:52 -03:00
Francisco Solla
2a101c87ab fix(e2e): C142 — harden B196 completion-footer assert (30s poll) for deploy verify
The B196 'relation groups' spec asserted the '1 município encontrado'
completion footer with the 10s expect default; under 4-worker CI load the
RSC list chunk can land late ('element(s) not found'), the same
stream-latency class as the issue's three deploy-blocking assertions.

Polls the footer with the same 30s budget the sibling geometry asserts
use, scoped to .first() so a transient streaming duplicate cannot trip
strict mode.
2026-08-23 22:13:52 -03:00
Francisco Solla
9039b1bbd3 fix(e2e): C142 — add C106 shell-settle before write-chrome assertions to unblock deploy
The FAB in campaignPermissionProfile:85/:101 (carteira/tudo write present)
and the advisor checkbox in campaignMunicipalities:115 (editar page) failed
consistently under 4-worker CI load because the RSC streaming shell's
S:* copies hadn't settled when networkidle resolved, and the default
expect/action timeout wasn't sufficient on the constrained hosted runner.

A probe proved the FAB SSR-renders correctly for carteira/tudo advisors
(profile scope is honored; no seed false-negative). The fix adds the
C106 shell-settle (document.querySelectorAll('div[id^=S:]').length === 0)
before the presence assertions, matching the established idiom used by
the sibling campaign spec tests.

21/21 passed at E2E_PROD=1 PLAYWRIGHT_WORKERS=4 repeat-each=3.
gate:fast (2439 unit tests) all green.
2026-08-23 22:13:52 -03:00
Francisco Solla
1daaaf9f4e feat(C139): domínio de contatos — URL state, loader com vínculos e invariante de nome
Fase 2 do plano aprovado. Três módulos em src/utilities/contacts/:
- contactListUrl: contrato de params (q|gender|state|city|ausencia|vinculo|sort|dir|page),
  canonicalize, hrefs de sort e where de fonte com 'phones.value' like (ILIKE verificado
  no Postgres local).
- contactListData: loader com UM find em contact (user threadado, overrideAccess false),
  vínculos (liderancas/dobradinhas/assessores/equipe) como facets sobre o conjunto
  legível, filtro/sort/paginação em memória, nulos no fim (precedente B15).
- contactNameInvariant: lock contact-name:{normalized} + prefilter ILIKE + veredito
  exato em memória; mensagem de conflito pt-BR.

38 testes unit novos; format + tsc limpos.
2026-08-13 14:05:24 -03:00
Francisco Solla
3ee2d83350 feat(B200): encaixes dos cards mobile de municípios — densidade, alinhamento de labels e pilha de avatares (pós-B196) 2026-08-12 15:40:02 -03:00
Francisco Solla
48088d8162 refactor(OPS30): fixes do /simplify — pessoas no prewarm, login com sub-budgets, retry da demanda via existência, B24 no helper do arquivo
Review 3× read-only (0 P1):
- setup: /campanha/pessoas faltava (campaignPeople); log do 1º hit
  quando o retry salva; comentário do budget ancorado no medido.
- login: sub-budgets 15+5+30 (antes 30+5+30 estourava o test timeout
  e credencial errada morria de timeout genérico); retry bifurca pela
  URL real (dashboard/fora do login/re-submit) — re-submit cego
  contra página que saiu do login falhava com fill na dashboard.
- :713: retry cego criaria duplicata que o UNIQUE do slug refuta —
  o retry consulta por título e navega ao detail quando o 1º POST
  criou mas o redirect se perdeu.
- B24: locator reusa visibleMunicipalityButton (escapeRegExp +
  fronteira de nome, 23/435 com prefixo) e ensureWideMunicipalityList
  re-rodado pós-reload (padrão do irmão dobradinhas).
- :1063: waitForResponse com timeout 5s para o toPass decidir.

Re-prova: campaignMunicipalities 22/22 (arquivo atual, B193+OPS29 do
main) ×3 consecutivos em dev a 2 workers (load 5-17).
2026-08-12 13:34:42 -03:00
Francisco Solla
02beea808b chore(OPS30): hardening e2e em dev — prewarm completo, login resiliente a compile-frio, fechamento do P4-L
- setup.e2e.spec.ts: prewarm GET 13→26 rotas (rota /editar, alvos de
  redirect, wizards, PWA artifacts, rotas do chat Sollinha) + POST
  ganhou next-steps, home-search, ai-chat, ai-transcribe; prewarmGet
  com retry-único (abort do compile aborta a conexão do próprio
  prewarm); budget explícito 420s no lugar do test.slow().
- campaignE2EFixtures.ts: campaign.login com retry-único do submit
  (re-fill+re-click com grace-wait do destino exato) — submit abortado
  pelo full-page reload do compile frio nunca navega.
- campaignMunicipalities.e2e.spec.ts: refactor mínimo por evidência —
  B24 (estágio largo B158 + toggle do trigger pós-reload), :713 (retry
  do server action de criação), :1063 (seed parity via db:seed:minimal
  — achado do OPS28 — + retry do suggest).
- P4-L fechado com evidência: campaignLeaderships ×3 dev + ×1
  E2E_PROD=1 (TECH-DEBT row 59 + TESTING.md caveat).
- Aceite: campaignMunicipalities 17/17 ×3 consecutivos em dev a 2
  workers (load 5-18).

Sem migration/access/Consent/UI.
2026-08-12 13:34:42 -03:00
Francisco Solla
c83bd779d2
Merge pull request #665 from fsolla/C112-multiplos-telefones-por-pessoa
C112 — Múltiplos telefones por pessoa
2026-08-11 14:27:47 +00:00
Francisco Solla
6de8fad8f6 feat(C112): múltiplos telefones por pessoa — Contact.phones array (ordem = prioridade)
- Contact.phone (text único) → phones (array field Payload, subcampo value):
  fonte única; principal = phones[0]; beforeValidate normaliza, filtra vazios,
  rejeita inválido e duplicado DENTRO da ficha (C111 entre fichas preservado)
- migration add_contact_phones_array: backfill phone → phones[0] (gen_random_uuid)
  antes do drop da coluna + índice em value (hand-edit documentado; snapshot .json
  gerado mantém as colunas C99/C100 já existentes)
- primaryPhoneOf / phoneValuesOf / reorderWithPrimaryPhone em src/lib/phone.ts
  (ordem = prioridade; inline edit = seta o principal preservando o resto)
- findOrCreateContactByPhone aceita phones[] (dedupe pelo primário; reuse faz
  append dos faltantes; locks em todos os números digitados)
- sync conta→ficha (CampaignUser): phone da conta faz APPEND quando a ficha não
  tem (nunca reordena nem remove); tombstone LGPD substitui TODOS os telefones
- convite redemption: telefone digitado vira principal, demais preservados
- import CSV: preview sinaliza compartilhado também por número secundário;
  bulk insere contact_phones (JOIN nas leituras, uuid no insert)
- busca por telefone (apoiadores, pickers, combobox) casa qualquer número
- UI: PhonesFieldEditor compartilhado (repetível mascarado, remover, ↑/↓,
  modo inline com Salvar) nas 5 superfícies — liderança (form/ficha/lista),
  apoiador (form/ficha), dobradinha (ficha), assessor (ficha), wizard
- UI pública (petição/WhatsApp) intocada: zod e forms ficam com phone;
  as ações mapeiam phone → phones[0]
- testes: spec novo campaignMultiPhones (aceite completo), specs C111/C99
  atualizados, e2e wizard/apoiador ajustados
2026-08-11 10:47:02 -03:00
Francisco Solla
a0fe13c3d1 chore(OPS36): journeys com sessão compartilhada nos e2e pesados — seed do campaign-token via payload.login sem login de browser
- Sessão por arquivo: coordinator/leader compartilhados por worker (ownership
  própria, cleanup no afterAll); beforeEach semeia o cookie campaign-token no
  context fresh de cada teste; advisors permanecem por teste (OPS29/B176)
- Premissa da intenção corrigida por medição: getPayload é memoizado por
  worker (364 ms cold, 0 ms warm); o overhead real era login de browser
  (3.6 s quente / 30.3 s 1º hit vs 112-134 ms de seed) + criação de usuário
- Sem mode: 'serial' (divergência declarada): seed por cookie dispensa o
  reuso de contexto e evita skips em cascata no relatório
- Endurece 7 interações client-side com toPass (classe B13/B17 exposta pela
  remoção do warm-up do login) e 4 lookups do strip de ações com .first()
  (duplicado transitório RSC)
- Medição antes/depois registrada no CHANGELOG; débito OPS39 (prewarm de
  home-search/mudar-tendencia) registrado com depends no OPS30
2026-08-11 09:35:14 -03:00
Francisco Solla
02abd7de89 feat(B196): omnibox colado ao header e card mobile mais denso (Closes #607)
Chassis de lista (vale para todas as listas, gate A):
- scrollport: max-md:has-[.campaign-list-omnibox-form]:pt-0 — o containing
  block do sticky passa a começar no topo; a barra cola no header (sem vão
  de 16px, nada passa por cima no scroll)
- barra densa: campo min-h-8 py-0 + X size-8 → ~40px no total; anel de
  foco escopado a md (mobile sem anel de cor; caret é o indicador)
- trailing cluster desktop-only por padrão (edgeToEdge removido)
- pt-4 md:pt-0 nas 6 listas com linha de ações antes do form (incl. organizacoes)

Card mobile (B193 densificado):
- nome 18px leading-tight com território a ~2px
- chips como blocos label-acima (Classe/Tendência/Nível, gate B) via ChipBlock
- avatares numa linha por grupo com overlap proporcional (flex-1 cells,
  sem cap/wrap/…, overflow-hidden nas bordas) — prop wrap → overlapRow
- MissingAdvisorBadge com wrap para não clipar na célula do grupo
- densidade vertical gap-2.5/py-3; toques min-h-11 preservados

e2e B196 (6 testes) + regressão B184/B183/B193 verde.
2026-08-11 07:55:50 -03:00
Francisco Solla
07dd68511f chore(OPS36): journeys com sessão compartilhada nos e2e pesados — seed do campaign-token via payload.login sem login de browser
- Sessão por arquivo: coordinator/leader compartilhados por worker (ownership
  própria, cleanup no afterAll); beforeEach semeia o cookie campaign-token no
  context fresh de cada teste; advisors permanecem por teste (OPS29/B176)
- Premissa da intenção corrigida por medição: getPayload é memoizado por
  worker (364 ms cold, 0 ms warm); o overhead real era login de browser
  (3.6 s quente / 30.3 s 1º hit vs 112-134 ms de seed) + criação de usuário
- Sem mode: 'serial' (divergência declarada): seed por cookie dispensa o
  reuso de contexto e evita skips em cascata no relatório
- Endurece 7 interações client-side com toPass (classe B13/B17 exposta pela
  remoção do warm-up do login)
- Medição antes/depois registrada no CHANGELOG; débito OPS39 (prewarm de
  home-search/mudar-tendencia) registrado com depends no OPS30
2026-08-11 07:41:14 -03:00
Francisco Solla
6fe38b332c test(B195): migrar e2e B85 do cadastro de demanda ao campo único 2026-08-10 20:15:05 -03:00
Francisco Solla
0fefbd0490
Merge pull request #604 from fsolla/OPS29-busca-global-sem-sugestoes-mostra-regiao-em-branco-est
fix(OPS29): estado vazio honesto na busca global sem sugestões curadas
2026-08-10 13:31:03 -03:00
Francisco Solla
fb7f85cd59 fix(OPS29): estado vazio honesto na busca global sem sugestões curadas (Closes #585)
- HomeSearchResultsShell renderiza 'Nenhuma sugestão ainda — priorize
  municípios na planilha de projeção' quando o suggest responde success
  sem hits (Início e FAB overlay compartilham o shell; SSR initialSuggest
  e POST cobertos); gateada por !isFetching para não falar sob o skeleton
- B126 e2e determinístico: pin um município priority alta claimado;
  testes OPS29 novos usam advisor sem carteira (scoped, imune a alta
  global de workers paralelos); settle de animação do overlay no B126
- unit: 3 casos novos no shell + overlap skeleton/mensagem
2026-08-10 12:54:16 -03:00
Francisco Solla
2b8213eff6 test(B193): adapta o e2e C109 do main ao contrato do card novo — rodapé expandível após registrar
O teste C109 (merge do main via rebase) esperava o CTA 'Registrar atualização'
sempre visível no card; o card B193 troca o rodapé para a linha expandível
'Última atualização' após o primeiro registro (CTA dentro da expansão). A
cobertura da C109 (abrir/registrar/fechar/reabrir + sheet de nível) é mantida.
2026-08-10 11:23:49 -03:00
Francisco Solla
5d38bed12f test(B193): e2e robustos — cards ancorados pelo link exato do nome e C106 com timestamps determinísticos
- '?q=' casa prefixos de nome ('Coribe' também retorna 'São Félix do Coribe'):
  os cards B193 são ancorados por has:{ link do nome exato }.
- C106: os 8 updates seedados podiam compartilhar o mesmo now() de transação,
  tornando o sort -createdAt instável (flake '#8 no topo'); createdAt
  explícito e crescente.
2026-08-10 11:23:49 -03:00
Francisco Solla
b210939226 refactor(B193): simplify fixes pós-revisão — mapper único de updates, API de trigger uniforme, grupos de relação extraídos, hardening do sheet host
- Extrai toMunicipalityUpdateViewModel/loadUpdateAuthorNames compartilhados
  (feed + batch da lista); remove avatarClassName morto.
- MunicipalityRelationTriggerProps compartilhado nos 3 wrappers; triggerClassName
  no ExpectedVotesControl; ChipLabel/RelationGroup/CityDash no card.
- Sheet host: cleanup de unmount no overlay (sem drawer órfão), registerFooter
  valida open, branch nulo do handleOpenChange limpa footer; chrome freeze
  documentado.
- A11y/UX: ícone de alerta no rodapé frio (WCAG 1.4.1), iniciais de avatar
  aria-hidden, chip sem_base sem target morto, label da cidade com Tendência,
  aria-label da barra anuncia os 3 cenários, card layout 'colocação · votos'.
- E2E: settleStream no B184 (flake da cópia transitória do streaming), cards
  ancorados por hasText (q= casa prefixos), cor da borda via classe do token.
2026-08-10 11:23:49 -03:00
Francisco Solla
3d8dbae61f feat(B193): card de município mobile denso com edit-where-you-see e rodapé de atualização expandível
- Card denso (wireframe): posição de voto à direita, barra de cenários com
  marcador do ativo, chips classe/tendência/nível (label à esquerda), 3 grupos
  de avatares com wrap (um por entidade), rodapé Última atualização + chevron
  que expande revelando o último card de atualização + CTA registrar; sem
  atualização → CTA direto; prioridade = borda lateral direita 6px primary.
- Controles ganham prop trigger/triggerClassName (precedente RelationChipCell);
  MunicipalityRelationAvatarStack ganha modo wrap.
- C109 (sheet quebra ao abrir) corrigida: sessões do sheet host keyed por id
  (imune à identidade dos callbacks recriados por render) e footer customizado
  renderizado pelo host dentro da árvore do Drawer (portal perdia o Dialog
  context — reproduzido digitando no sheet).
- Dado: MunicipalityListViewModel.lastUpdate (batch por página, scoped).
- E2E novos (barra/votos, chip nível, borda prioridade, expansão/CTA vazio).
Sem migration/access/Consent.
2026-08-10 11:23:49 -03:00
Francisco Solla
5ad2f7c722 fix(campanha): quick-edit sheet mobile com footer custom quebrava ao abrir (C109)
O footer custom ('Registrar atualização' B42, 'Registrar movimento' E14) era
portalado da célula para o DrawerFooter do shared-sheet: createPortal preserva
o contexto React da origem (sem Drawer.Root), então o DrawerCloseButton
(base-ui DialogClose) crashava em useDialogRootContext() e o sheet nunca
renderizava.

O footer agora viaja no chrome do CampaignListSheetHost (mesmo mecanismo de
title/description) e é renderizado inline no DrawerFooter — contexto correto
por construção. O elemento continua criado na célula (estado do form e
associação form attribute preservados). footerPortalRef/attachFooterPortal/
hasCustomFooter removidos; fallback sem provider e popover intactos.

Cobertura: unit do shared path (abre/fecha/reabre + submit via form attribute)
e e2e mobile no describe B42 (abre, registra, fecha — incluindo o sheet do
nível E14).
2026-08-10 08:11:05 -03:00
Francisco Solla
3ca200e7b0
Merge pull request #549 from fsolla/B184-municipios-mobile-lista-sem-moldura-omnibox-sticky-x-de
B184 — Municípios mobile: lista sem moldura (omnibox sticky, X de limpar no input, salvar filtro no header, cards edge-to-edge)
2026-08-10 03:30:25 -03:00
Francisco Solla
324e1c3c51 feat(B184): omnibox mobile sem moldura como padrão das listas (sticky no form, X de limpar no input, salvar filtro no header, cards edge-to-edge)
- Chassis CampaignListOmnibox (B127): abaixo de md a label some (input carrega
  aria-label), o campo perde borda/sombra (md: restaura), o form do filtro vira
  barra sticky com linha separadora (sticky no form porque não escapa do
  containing block — a coluna do input dentro do wrapper curto não grudava),
  o texto Limpar vira X circular dentro do input (chips OU busca; slot sempre
  renderizado com invisible; hit target 44px; live region de limpeza), botão
  texto Limpar desktop-only. Padrão para as 11 listas via
  campaignListOmniboxFormClassName.
- Municípios: Salvar filtro vira icon no header mobile (C94/C95 slot,
  presentation='icon', nome de rename = contrato 2.5.3); cards edge-to-edge
  com separador de linha (border-b last:border-b-0, -mx-4, gate md:).
- Atualizações: Nova atualização vai para o header mobile (único trailing
  mobile-visible que grudaria na barra).
- campaignMobileHeaderIconClassName compartilhada (3º consumidor).
- E2E mobile novo (390x844) com pins: foco/aria-expanded após limpar, salvar
  -> renomear no header, cards, desktop invertido.
- D7 registrado no caminho: sticky dos headers da tabela desktop morto.
2026-08-10 03:11:43 -03:00
Francisco Solla
4e920ee064 feat(C107): sheet de criação de atualização usável no mobile — altura auto até o topo, form sem labels com divisórias full-bleed, placeholders, polaridade em toggle, checks à direita
- MunicipalityUpdateFields ganha layout 'labeled'|'list'; polaridade vira ToggleGroup Ruim|Neutra|Boa nas duas resoluções (mesmo dado, hidden input)
- layout 'list': labels sr-only, textarea sem borda, divisórias divide-y full-bleed, Urgente/Sinalizar adversário como linha tocável com check à direita
- StrictCombobox: prop placeholder (backward-compatible)
- Sheet do feed: max-h-dvh (teto no topo, rola por dentro), título sr-only, form lista com placeholders descritivos; desktop preservado
- Quick-edit B42 herda o visual no sheet mobile via layout
- unit/e2e atualizados para o toggle (radio)
2026-08-10 01:11:24 -03:00
Francisco Solla
cd4698571a fix(e2e): first() em asserts com duplicação transitória de RSC pending (flake pré-existente, base reproduz) 2026-08-09 04:37:27 -03:00
Francisco Solla
97dd13443e test(campaign): endurece locators de texto contra duplicata transitória de hidratação
Mesmo padrão já pinado em 'Otimista: 5.000': o slot server-composed
renderiza duas cópias por um instante durante a hidratação — .nth(0)
nas asserções de perfil (webauthn) e dossiê (municípios). Não relacionado
ao B176; observado deterministicamente no servidor de produção local.
2026-08-09 03:19:54 -03:00
Francisco Solla
14bbee6e00 feat(B176): filtrar municípios por Dobradinha/Liderança/Partido + coluna Dobradinhas ao assessor
Omnibox de /campanha/municipios ganha as dimensões Dobradinha (nome do
contato), Liderança (nome do contato) e Partido (sigla), com recortes
"Sem dobradinha"/"Sem liderança"/"Sem partido" no precedente de
"Sem nível". Params aditivos stateDeputy/leadership/party na URL
congelada; buildMunicipalityListWhere ganha catálogo request-scoped
fail-closed (municipalityRelationSets) que vira as leituras reversa e
cruzada em where — sem migration, paginação nativa mantida; facets
estendidas e o mapa compartilha o catálogo. Coluna Dobradinhas liberada
ao staff (servidor já scoped B37/B157; só o gate de UI mudou).
Extrato do par sentinela+exists:false em campaignListUrl (5 call sites).
2026-08-09 03:19:54 -03:00
Francisco Solla
c1bedcf838 test(campaignMunicipalities): torna o assert Otimista: 5.000 determinístico
O getByText sem índice também casa com o span sr-only do hover-preview quando
uma linha está hovered — strict-mode violation flaky no CI. .nth(0) fixa no
resumo da célula.
2026-08-08 18:29:42 -03:00
Francisco Solla
11a9f45d69 test(e2e): fix C87 unified-update specs (polarity + adversary signal) 2026-08-07 06:56:41 -03:00
Francisco Solla
212a1f2423 feat(campaign): C87 — atualização unificada (texto + polaridade + urgente)
Substitui o modelo de MunicipalityUpdate (kind/semanal/urgente/nota/sinal + signalType)
por um modelo unificado: body obrigatório + polarity (boa/neutra/ruim) + urgent + adversarySignal.

- Schema: polarity enum obrigatório, urgent boolean default false, adversarySignal boolean
- Wizard: novo WizardUpdateBodyStep com campo de texto + polaridade + urgente
- Lista: controle unificado de atualização com polaridade inline
- Feed: exibe polaridade como badge colorido + indicador de urgência
- V2 status strip: atualização rápida de polaridade
- E11: queries atualizadas para polarity + adversarySignal
- Migração: 20260807_030000_unify_municipality_update (backfill polarity=neutra, urgent de kind, adversarySignal de kind+signalType)
- Testes: 18/18 integração C87, 593/593 total, 1399/1399 unitários
- Fix: testes de state deputy usavam nomes fixos que colidiam entre execuções
2026-08-07 05:04:41 -03:00
Francisco Ignácio Fontoura Solla
c1fb8c03c4 test(e2e): document municipality locator boundary 2026-08-06 19:12:32 -03:00
Francisco Ignácio Fontoura Solla
9c99469c36 test(e2e): stabilize municipality campaign journeys 2026-08-06 19:00:13 -03:00
Francisco Solla
8abf18ecf9 test: stabilize responsive municipality journeys 2026-08-05 15:00:33 -03:00
Francisco Solla
2f8d16881d test: align municipality e2e journeys with responsive columns 2026-08-05 14:24:47 -03:00
Francisco Ignácio Fontoura Solla
086a9c8ee0 B157 — coluna de dobradinhas na lista de municípios (+ criação inline) 2026-08-04 02:53:43 -03:00
Francisco Ignácio Fontoura Solla
26b2205c3a B154 — criar assessor inline no popover da lista de municípios
Closes #352

- POST /campanha/municipios/advisors aceita body de criação (name) na union;
  createMunicipalityAdvisorRecord cria campaignUser advisor (stub
  <slug>@criado.invalid, senha aleatória) e atribui ao município na mesma
  transação (cap 10 com rollback, lock municipality-advisors:{id})
- MunicipalityAdvisorCreateProvider compartilha o assessor criado entre linhas;
  opção "+ Criar assessor" e chip otimista com id temporário no control
- advisorNameSchema unificado em primitives.ts; predicado de placeholder cobre
  @criado.invalid (reset de senha e célula de e-mail tratam stubs inline)
- Destrava o spec e2e de municipios (falhas pré-existentes em main): key
  warning do fragment trailing de MunicipalityFilters; settle-wait da animação
  do Drawer no teste B136
2026-08-03 22:46:06 -03:00
Cursor Agent
ffe7eca629
test(e2e): assert assessoria on dossiê tab after B145 hero removal 2026-08-03 00:29:48 +00:00
Cursor Agent
c62fe67809
test(e2e): assert entity identity in shell chrome (B145) 2026-08-03 00:29:48 +00:00
Cursor Agent
18c1317884
chore(B136): keep groupActionsByRow internal; drop unused import 2026-08-02 23:36:28 +00:00
Cursor Agent
4cbb65963b
fix(B136): pass raw boxes to grid geometry assert 2026-08-02 23:36:28 +00:00
Cursor Agent
3ac5d303e8
test(B136): assert 3×2 grid by column clusters (tolerant row heights) 2026-08-02 23:36:28 +00:00
Cursor Agent
993b9af69b
style: format B136 files with Prettier 2026-08-02 23:36:28 +00:00
Cursor Agent
2f2c8563a5
B136 — corrigir grade 3×2 no Início e FAB overlay
- layout=grid no botão com w-full/min-w-0 sem box-content no mobile
- li min-w-0, gap-2, data-layout=grid-3 no strip
- e2e com geometria (boundingBox) em viewport 390
- impl plan docs/plans/inicio-acoes-grade-3cols-2rows-impl.md

Co-authored-by: Francisco Solla <fsolla@users.noreply.github.com>
2026-08-02 23:36:28 +00:00
Francisco Ignácio Fontoura Solla
0baeda22a0 Fix e2e failures from FAB chrome waits and B27 advisor strict-mode.
B126 mobile specs looked for a page heading that shell chrome replaced;
wait on campaignPageChrome with exact title text, and wrap the desktop
title so subtitle no longer collides. Pin B27 to the município-specific
assessores trigger and map home-search libs to campaignMunicipalities.
2026-08-02 17:49:08 -03:00
Cursor Agent
62673367c9
test(e2e): harden município list selectors after B127 omnibox
Use campaignPageChrome for FAB specs and .first() on Editar assessores
when mobile+desktop cells both mount.
2026-08-02 20:30:55 +00:00