Commit graph

920 commits

Author SHA1 Message Date
Francisco Solla
7fabb72df8 feat(C125): facet de ausência completo (qualquer_ausencia) e descoberta de ordenação no mobile
- PEOPLE_ABSENCES ganha o 4º valor qualquer_ausencia ("Qualquer ausência"),
  a união exata das três ausências em UM chip — o 3º clique dos específicos
  deixa de colapsar o facet (3 de 4 membros) e o parse absorve o guarda-chuva
  sobre os específicos, sem tocar no B18 genérico de capacity/status
- peopleListSortPrimaryOptions: seeds do grupo Ordenação viram as 7 direções
  primárias com emptyQueryVisible, então o mobile descobre as 7 chaves ao
  tocar no bar (e "ordenar" não corta aliada/assessorado/base no cap 8)
- CampaignListOmnibox reabre o popover no mousedown: clicar num input já
  focado (logo após commitar busca com Enter) não disparava focus, deixando
  a descoberta morta no mobile até digitar
- tests: contrato de URL (absorção + não-colapso), predicado + AND, novo spec
  de omnibox, e2e do 3º clique e da descoberta mobile (390×844)
2026-08-11 20:34:51 -03:00
Francisco Solla
b604157fc7
Merge pull request #685 from fsolla/OPS39-prewarm-e2e-dev-adicionar-campanha-home-search-post-e-
OPS39 — Prewarm e2e: home-search POST e mudar-tendencia GET no setup spec
2026-08-11 20:14:51 +00:00
Francisco Solla
4043573152 fix(C118): fecha describe do e2e campaignPeople (merge com C117) e formata changelog 2026-08-11 16:32:49 -03:00
Francisco Solla
c0ffc22026 feat(C118): página de detalhe da pessoa — seções montadas pelas capacidades (Closes #657) 2026-08-11 16:32:21 -03:00
Francisco Solla
94c89a57a1 feat(C124): tarefa de atividade só com texto, responsável e concluída (sem prazo) 2026-08-11 16:05:07 -03:00
Francisco Solla
15461d7abf
Merge pull request #679 from fsolla/B197+-assessores-menu-do-picker-de-colunas-derivado-da-mesma
B197+: picker de colunas de assessores derivado da mesma fonte do AdvisorsTable
2026-08-11 18:53:09 +00:00
Francisco Solla
fdca8ff9c4 format: prettier no impl plan OPS39 2026-08-11 15:47:12 -03:00
Francisco Solla
21b4b4d0c6 fix(gate): seleção e2e contendo apenas o spec setup vira none (setup é dev-only; OPS39) 2026-08-11 15:47:12 -03:00
Francisco Solla
2fec6a407e test(OPS39): prewarm POST /campanha/home-search e GET /campanha/acoes/mudar-tendencia no setup e2e (Closes #645) 2026-08-11 15:47:12 -03:00
Francisco Solla
a8414a49f1 Merge origin/main into C117 (C116/C119/OPS40) — sortable heads sobre células editáveis 2026-08-11 15:43:23 -03:00
Francisco Solla
421a680402
Merge pull request #683 from fsolla/plans/plan-issue-40
plans(OPS40): worktree dev — PORT do .env.local não honrado pelo next dev
2026-08-11 18:27:28 +00:00
Francisco Solla
f1405b04a5
Merge pull request #680 from fsolla/C119-status-de-apoio-lembranca-em-liderancas
C119 — Status de apoio 'Lembrança' em lideranças
2026-08-11 18:25:18 +00:00
Francisco Solla
7768b366f9 merge: main into B197+ (CHANGELOG C116) 2026-08-11 15:24:56 -03:00
Francisco Solla
2ef0d38dd9 plans(OPS40): PORT do .env.local não é honrado pelo next dev no worktree 2026-08-11 15:24:33 -03:00
Francisco Solla
9e5637fa7c plans(C125): débitos UX pós-C117 — descoberta de ordenação mobile e facet de ausência completo (depends 656) 2026-08-11 15:23:45 -03:00
Francisco Solla
a83bedd7a8 feat(C116): pessoas — edição onde você vê na lista desktop (células sempre input, chips territoriais internos, nome-link, tooltips nas ações) 2026-08-11 15:09:56 -03:00
Francisco Solla
45e687bf32 merge: main into B197+ (CHANGELOG B198) 2026-08-11 15:05:06 -03:00
Francisco Solla
6e5a293781 merge: main into B197+ (CHANGELOG) 2026-08-11 14:54:36 -03:00
Francisco Solla
5fa4e22c9d feat(C119): status de apoio 'Lembrança' em lideranças — enum, badge e exclusão de pendências 2026-08-11 14:52:49 -03:00
Francisco Solla
6011efcee7 chore(B197+): picker de colunas de assessores derivado da mesma fonte do AdvisorsTable (#642) 2026-08-11 14:40:24 -03:00
Francisco Solla
aa8fe582c0 feat(B198): drawer mobile do Sollinha fecha ao navegar por link interno
O renderer de links do chat decide interno vs externo (B187/B188); a decisão
pura de fechamento mora no owner src/lib/ai/markdownLinks.ts
(shouldCloseDrawerOnLinkClick: interno && mobile && clique simples) e o
markdownComponents estatico virou useMemo no corpo do CampaignAIChat para
alcancar o contexto. Desktop inalterado; links externos/modos de nova aba
mantem o drawer da aba de origem; sessao e restore intactos (close persiste
open:false).

- unit: 12 testes da decisao pura
- e2e (modo CI, E2E_PROD=1): interno fecha + navega sem load + reload nao
  reabre; externo abre popup e mantem o drawer
- debito B199/#672 (depends #660): race de persistencia ao fechar mid-stream
2026-08-11 14:29:29 -03:00
Francisco Solla
9789bf36e6
Merge pull request #673 from fsolla/C114-google-calendar-agenda-da-campanha-espelhada-com-notifi
feat(C114): agenda da campanha espelhada no Google Calendar com notificação (push Teqo → Google)
2026-08-11 17:17:17 +00:00
Francisco Solla
d56f2250c6
Merge pull request #675 from fsolla/D12-flake-e2e-getbylabel-atividade-relacionada-resolve-2-ele
fix(e2e): first() no getByLabel('Atividade relacionada') sob duplicação transitória (D12 #641)
2026-08-11 17:11:46 +00:00
Francisco Solla
70070db8dc feat(C114): agenda da campanha espelhada no Google Calendar com notificação (push Teqo → Google)
Espelho oficial da agenda em calendário Google compartilhado via API
(service account, jose + fetch): reconciliação por passada com id
determinístico teqo+base32hex (upsert idempotente, sem estado por evento),
hooks afterChange/afterDelete na activity (só campos espelhados, nunca
lança), auto-resync quando calendarId muda, retry na view da agenda,
pill de estado no header + FAB + diálogo (4 estados). Cancelado no Teqo
deleta o evento no Google. Sem participantes no v1 (decisão de produto).
Sem migration nova de tabela (hand-written). Closes #635
2026-08-11 13:53:51 -03:00
Francisco Solla
b42de3e7ec feat(C117): ordenação por coluna (header + omnibox) e filtros de ausência em Pessoas
- sort/dir/ausencia no contrato de URL de /campanha/pessoas (molde territórios/B15)
- sortPeopleRows sobre o conjunto filtrado, nulls no fim, tiebreak nome→contactID
- PeopleSortableHead (CampaignSortableHead) com aria-sort nas 7 colunas visíveis
- omnibox: grupo Ordenação (chave×direção) e facet Ausência (Sem assessor/base/contato)
- clear preserva o sort; tests unit + e2e; sem migration/collection
2026-08-11 13:33:21 -03:00
Francisco Solla
a740f1eb16 fix(e2e): first() no getByLabel('Atividade relacionada') sob duplicação transitória (flake pré-existente, precedente cd469857)
D12 (#641): strict-mode violation (2 triggers idênticos) só em runs
completos sob carga. Estado estável tem 1 match (verificado em browser);
o 2º match é duplicação transitória RSC-pending, mesma classe de cd469857.
Mesmo alvo em campaignActivity:52 (único outro uso do label em e2e).
2026-08-11 13:21:21 -03:00
Francisco Solla
be4bb49da9 plans(C123,C124): agenda — modal central como única superfície de criar/editar e tarefa sem data/hora 2026-08-11 13:17:05 -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
b025361959
Merge pull request #650 from fsolla/D10-d10-auto-cura-de-leftovers-de-consent-supporter-abba-do-
D10 — Auto-cura de leftovers de consent/supporter + ABBA do shared-write do lease de consent (pós-D9)
2026-08-11 13:59:54 +00:00
Francisco Solla
442378d7c8
Merge pull request #654 from fsolla/OPS36-e2e-journeys-sessao-compartilhada-arquivos-pesados
OPS36 — E2E: journeys com sessão compartilhada nos arquivos pesados (amortizar overhead por teste)
2026-08-11 13:48:29 +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
d23433a39a plans(C119,B198): status Lembrança em lideranças e Sollinha fecha o drawer ao navegar em mobile 2026-08-11 10:44:53 -03:00
Francisco Solla
29ce38bd29 Merge remote-tracking branch 'origin/main' into OPS36-e2e-journeys-sessao-compartilhada-arquivos-pesados
# Conflicts:
#	docs/CHANGELOG-AGENTS.md
2026-08-11 10:31:04 -03:00
Francisco Solla
62335084a1
Merge pull request #658 from fsolla/plans/plan-issue-35
plans(C116,C117,C118): lista de pessoas — edição onde você vê, ordenação/filtros de ausência e detalhe por capacidades
2026-08-11 13:25:14 +00:00
Francisco Solla
3af97e960f plans(C116,C117,C118): lista de pessoas — edição onde você vê, ordenação/filtros de ausência e detalhe por capacidades 2026-08-11 10:23:10 -03:00
Francisco Solla
3df0b16a66 docs(OPS39): plano do prewarm registrado como Issue #645 (depends OPS30) 2026-08-11 09:40:14 -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
c81a439d36 merge: origin/main (C113/OPS38 changelog, wizard chrome) 2026-08-11 09:01:13 -03:00
Francisco Solla
5577d31c38 fix(C113): feed iCal da agenda vivo — contrato de frescor no lugar do snapshot congelado
Spike no build de produção provou que o Next 15 não congela route handlers GET
(rota dinâmica por default); o freeze era o contrato de resposta
(public, max-age=3600 sem validadores) permitindo caches compartilhados
reterem o snapshot do primeiro fetch. Fix: force-dynamic explícito +
buildICalFeedResponse com public no-cache, ETag forte sha256, Last-Modified,
304 só por If-None-Match (nunca por data — blind spot de exclusão), dica
X-PUBLISHED-TTL no corpo do VCALENDAR (RFC 7986) e 404 no-store para
revogação fail-closed no CDN. Testes unit/int/e2e (dev + prod build).
2026-08-11 08:42:42 -03:00
Francisco Solla
e4c73803cd
Merge pull request #648 from fsolla/B196-municipios-mobile-omnibox-colado-ao-header-e-card-mais-
B196 — Municípios mobile: omnibox colado ao header e card mais denso
2026-08-11 11:24:10 +00:00
Francisco Solla
5817b3a74b merge: origin/main (CHANGELOG header + B197/OPS35+ entries) 2026-08-11 08:06:34 -03:00
Francisco Solla
643338888d
docs(OPS38): corrige header e ordem do topo do changelog — squash do #646 sobre main avançado re-clobberou (#649)
- PR #646 squash-merged sobre main com 4 merges em voo que tocavam o CHANGELOG (push #634, OPS37 #624, OPS35+ #638, B197 #639) — resolução do GitHub reproduziu o defeito da Issue: header perdido + topo fora da ordem canônica
- Reconstrução: header da base boa (0b520fdb) + 8 entradas pós-base na ordem de merge (B197, OPS35+, Push, OPS37, D9, B195, OPS33, OPS35) + base intacta
- Verificação: diff vs 0b520fdb = +8/−0; 128 entradas; md5 das 8 vs fontes OK; format:check + gate:fast OK
- Impl: docs/plans/changelog-agents-truncado-ops35-ops33-impl.md (segunda passada)
2026-08-11 11:04:26 +00:00
Francisco Solla
0a9da04c92
Merge pull request #647 from fsolla/D11-escala-dry-chrome-comum-dos-passos-finais-do-wizard-pos-
D11 — Escala/DRY: chrome comum dos passos finais do wizard (pós-B195)
2026-08-11 10:59:13 +00: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
1f68e543c5
docs(OPS38): restaura CHANGELOG-AGENTS truncado — 120 entradas de volta + re-anexa OPS35/OPS33/B195/D9 (#646)
- Merges #621 (OPS35) e #623 (OPS33) clobberaram docs/CHANGELOG-AGENTS.md de 246 para 12 linhas (resolução de conflito pegou cópia velha)
- Base boa: 0b520fdb (merge OPS28, PR #614) — 246 linhas, 120 entradas, byte-idêntica a 0047d263 e ao pai do merge OPS35
- Re-anexadas verbatim no topo (ordem de merge): D9 (#630), B195 (#622), OPS33 (#623), OPS35 (#621)
- OPS34/OPS28/B193 já estavam na base boa (md5 idêntico) — não duplicadas
- Diff vs base boa: +4 entradas, 0 remoções; grep: 124 entradas, cada código 1x
- Impl: docs/plans/changelog-agents-truncado-ops35-ops33-impl.md
2026-08-11 10:47:27 +00: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
8d007285d5 chore(D10): auto-cura de leftovers de consent/supporter + janela missing-consent exclusiva (12/12 sob 6 workers)
F1: purgeOrphanedConsentRenames sob lease exclusivo (autocommit) nos 3
pontos que tocam a key canônica; renames de teste migrados para chaves
privadas; F1b: purge de contatos órfãos no purgeMunicipalityResidue.
F2 (medição): shared-writer ABBA refutado; janela do missing-consent
exclusiva + Onda0 atômico (down transacional) + provision exclusivo.
Contraprova: 7 arquivos da key x 6 workers = 12/12 verdes (baseline 1/4).
2026-08-11 07:41:04 -03:00
Francisco Solla
0c952e5824 plans(B197+): escala-dry-pos — picker de assessores derivado do AdvisorsTable (débito pós-B197) 2026-08-11 07:39:17 -03:00
Francisco Solla
06bf1cad0c chore(D11): chrome comum dos passos finais do wizard — WizardStepFormChrome + DemandFields 2026-08-11 07:37:44 -03:00
Francisco Solla
170ce59ef4
Merge pull request #640 from fsolla/C111-telefone-nao-unico-duas-pessoas-podem-compartilhar-o-me
C111 — Telefone não único: duas pessoas podem compartilhar o mesmo celular
2026-08-11 03:58:55 +00:00