Commit graph

1622 commits

Author SHA1 Message Date
Francisco Solla
3a4e44f422 chore(C119): regenera migration sobre main rebaseada (C112 phones) e ajusta teste int para schema phones 2026-08-11 14:52:49 -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
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
69b9d4fe5b
Merge pull request #671 from fsolla/work/4
ops(work-issue): capture-review-debts autônomo — agente decide o que registrar e descartar
2026-08-11 16:35:13 +00:00
Francisco Solla
95ac668c0b ops(work-issue): capture-review-debts autônomo — agente decide o que registrar e descartar 2026-08-11 13:32:08 -03:00
Francisco Solla
7ae2e80470
Merge pull request #670 from fsolla/plans/plan-issue-37
plans(C123,C124): agenda — modal central única superfície de criar/editar; tarefa sem data/hora
2026-08-11 16:29:19 +00: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
02dce581cf
Merge pull request #662 from fsolla/plans/plan-issue-36
plans(C119,B198): status Lembrança em lideranças + Sollinha fecha drawer mobile ao navegar
2026-08-11 13:47:18 +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
7677cc3696
Merge pull request #652 from fsolla/C113-feed-ical-da-agenda-congela-na-criacao-do-link-eventos-
fix(C113): feed iCal da agenda vivo — eventos criados depois do link aparecem
2026-08-11 11:57:30 +00: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
f619658bd2
Merge pull request #644 from fsolla/plans/plan-issue-34
plans(B197+): escala-dry-pos — picker de assessores derivado do AdvisorsTable
2026-08-11 10:41:56 +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
Francisco Solla
162a518416 feat(C111): telefone não único — duas pessoas podem compartilhar o mesmo celular
Contact.phone deixa de ser único em enforcement (nunca houve constraint DB):
- remove o hook enforceUniqueContactPhone e os asserts assertContactPhone*
  (conflicts/ambiguity) — os 5 fluxos de escrita de pessoa salvam com número
  compartilhado; rename contactPhoneInvariant.ts -> contactPhoneLocks.ts
  (locks passam a significar serialização, não unicidade)
- findOrCreateContactByPhone: 0 fichas cria, 1 reusa (dedupe C6/C99),
  2+ cria ficha nova (a identidade da pessoa em criação é a ficha nova)
- convite autofill/login: ficha ancorada pode compartilhar o phone; o lado
  conta permanece fail-closed (username DB-unique; check de 2+ contas)
- import CSV: preview sinaliza telefone_compartilhado (resolução manual);
  confirm/bulk abortam fail-closed se um phone ganhar 2+ fichas (race)
- fix de achado de execução: supporter_import_batch.actor_id NOT NULL com
  FK ON DELETE set null derrubava o delete de campaignUser com batch
  órfão (23502) — CampaignUser.beforeDelete limpa batches + fixture
  cleanup descobre/deleta batches dos accounts owned
2026-08-10 23:03:05 -03:00
Francisco Solla
419ba39ecc
Merge pull request #639 from fsolla/B197-ocultar-a-coluna-de-e-mail-por-padrao-nas-listas-de-pes
B197 — Ocultar a coluna de e-mail por padrão nas listas de pessoas
2026-08-11 01:39:57 +00:00
Francisco Solla
02a33f1bc0
Merge pull request #638 from fsolla/OPS35+-restaurar-pin-de-visibilidade-css-das-colunas-de-rede
test(OPS35+): restaurar pin de browser da visibilidade CSS das colunas de rede em Territórios
2026-08-11 01:23:44 +00:00
Francisco Solla
e75148f3c4
Merge pull request #634 from fsolla/push-optin-automatico-fix-prompt-nativo
feat(push): opt-in automático e fix do prompt nativo no /campanha
2026-08-11 01:07:15 +00:00
Francisco Solla
3e9390230b feat(B197): e-mail oculto por padrão nas listas de pessoas — default B17 nas 5 listas + coluna própria em pessoas/apoiadores + picker em assessores 2026-08-10 21:57:52 -03:00
Francisco Solla
e86331fe4d
Merge pull request #624 from fsolla/OPS37-container-postgres-compartilhado-e-recriado-por-outro-
fix(OPS37): db:start idempotente — não recria o container Postgres compartilhado
2026-08-11 00:54:38 +00:00
Francisco Solla
bfe3bf6b19
Merge pull request #637 from fsolla/plans/plan-issue-33
plans(C114,C115): Google Calendar na agenda — espelho com notificação + edição bidirecional
2026-08-11 00:39:32 +00:00
Francisco Solla
21463f52c4 format: prettier nos planos C114/C115 2026-08-10 21:36:35 -03:00
Francisco Solla
80d8b67551 plans(C114,C115): integração Google Calendar da agenda — espelho com notificação e edição bidirecional 2026-08-10 21:30:40 -03:00
Francisco Solla
91ae6b5408 test(OPS35+): restaurar pin de browser da visibilidade CSS das colunas de rede em Territórios (Closes #616) 2026-08-10 21:28:52 -03:00
Francisco Solla
c4212b3e03 feat(push): opt-in automático e fix do prompt nativo no /campanha
- requestPermission agora é chamado sincronamente no clique: dentro do
  startTransition(async ...) o navegador perdia a user activation e o Chrome
  auto-negava silenciosamente ("Permissão de notificação negada" sem prompt)
- CampaignPushOptInToast no shell (app): staff com permission 'default', sem
  inscrição ativa, consentimento publicado e VAPID configurado; dismiss em
  localStorage; iOS só com app instalado
- cerimônia de ativação/desativação extraída para
  src/utilities/campaignPushClient.ts, compartilhada com o card de perfil
- mensagens distintas para denied (reativar nas configurações do site) vs
  default (prompt não exibido); pin de top-level registrado
- CHANGELOG-AGENTS.md: entrada única
2026-08-10 21:14:56 -03:00
Francisco Solla
c4865135b4
Merge pull request #633 from fsolla/C110+-ux-pos-critique-refinamentos-do-preview-do-arrasto-da-
C110+ — UX pós-critique: preview do arrasto — indicadores ancorados ao frame, gradiente deduplicado, e2e prev
2026-08-11 00:14:04 +00:00
Francisco Solla
7ce0219ec2
Merge pull request #632 from fsolla/plans/plan-issue-32
plans(C113): feed iCal da agenda congela na criação do link
2026-08-11 00:05:14 +00:00
Francisco Solla
6ef008b92f plans(C113): feed iCal da agenda congela na criação do link — plano de intenção 2026-08-10 21:02:11 -03:00