From b19c567b4144ada53004dc9cc93886acb31c5292 Mon Sep 17 00:00:00 2001 From: Francisco Solla Date: Mon, 10 Aug 2026 20:26:13 -0300 Subject: [PATCH] =?UTF-8?q?plans(C111,C112,B197):=20telefone=20n=C3=A3o=20?= =?UTF-8?q?=C3=BAnico,=20m=C3=BAltiplos=20telefones=20por=20pessoa,=20e-ma?= =?UTF-8?q?il=20oculto=20por=20padr=C3=A3o=20nas=20listas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- docs/plans/multiplos-telefones-por-pessoa.md | 118 ++++++++++++++++ docs/plans/ocultar-email-por-padrao-listas.md | 126 ++++++++++++++++++ docs/plans/telefone-nao-unico.md | 120 +++++++++++++++++ 3 files changed, 364 insertions(+) create mode 100644 docs/plans/multiplos-telefones-por-pessoa.md create mode 100644 docs/plans/ocultar-email-por-padrao-listas.md create mode 100644 docs/plans/telefone-nao-unico.md diff --git a/docs/plans/multiplos-telefones-por-pessoa.md b/docs/plans/multiplos-telefones-por-pessoa.md new file mode 100644 index 00000000..45ca0afd --- /dev/null +++ b/docs/plans/multiplos-telefones-por-pessoa.md @@ -0,0 +1,118 @@ +# Múltiplos telefones por pessoa + +Status: registrado (blocked até plano em main) +Atualizado em: 2026-08-10 +Issue: #626 +Priority: P2 +Model: composer-2.5 +Impeccable: B — encaixe: formulários de criação/edição de pessoa + ficha (detalhe) + listas existentes +Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-30/canvases/plan-c112-ui-draft.canvas.tsx +Appetite: ~1–2 dias eng; encaixe no modelo de pessoa existente +Responsável: — + +## Intenção + +Uma pessoa pode ter mais de um celular — o pessoal e o do comitê, o número que +troca e o antigo que ainda recebe WhatsApp. Hoje a ficha aceita um único número +e a mesa tem de escolher qual guardar. Queremos cadastrar **N telefones por +pessoa**, com as listas continuando compactas (mostram o principal) e a ficha +mostrando todos. Pré-requisito: telefones deixam de ser únicos (item **C111**). + +## Persona e fluxo + +- **Persona / contexto:** staff da mesa (coordenação e assessores) editando a + ficha de uma pessoa — liderança, apoiador, dobradinha, assessor, pessoa. +- **Job principal:** registrar todos os números de contato de uma pessoa, sem + perder informação e sem poluir as listas. +- **Fluxo desejado:** abre a ficha da liderança → vê os dois telefones dela → + adiciona um terceiro → salva → a lista mostra o principal; a ficha mostra + todos; WhatsApp e convite usam o principal. +- **Anti-goals de produto:** não é CRUD de agenda telefônica (sem rótulos + "casa/trabalho", sem tipo fixo/móvel, sem reordenação por arrasto); o import + CSV continua com uma coluna de telefone (mapeia no principal). + +### Esboço de fluxo (B — ver canvas) + +```text +[ficha da pessoa] → vê telefones (principal primeiro) → "+ Adicionar telefone" +→ salva → lista: mostra o principal · ficha: mostra todos · WhatsApp/convite: principal +``` + +## Objetivo e aceite + +- Uma pessoa pode ter **2+ telefones** cadastrados e editáveis nas cinco + superfícies de pessoa: ficha/detalhe, criação/edição de liderança, apoiador, + dobradinha e assessor. +- As listas de pessoas continuam mostrando **um** telefone por linha (o + principal); a ficha mostra todos. +- WhatsApp (`wa.me`), convite e o import CSV usam o **principal** (o import só + tem uma coluna). +- A busca por telefone encontra a pessoa por **qualquer** um dos números. +- Guardrail: os telefones continuam **não únicos** entre pessoas (C111) — duas + pessoas podem compartilhar um número, e cada uma mantém seus demais números. + +## Dados (intenção) + +Dados: N/A — nenhuma métrica; a mudança é de cadastro e exibição de contato. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/collections/Contact.ts` (campo de telefones), + formulários de pessoa (`src/app/(campaign)/campanha/(app)/{liderancas,apoiadores,dobradinhas,assessores,pessoas}/`), + fichas/detalhes, view models das listas (`src/utilities/*ViewModels`), + `src/app/(campaign)/campanha/actions/…` (escrita), import CSV + (`supporterImport`), `src/lib/phone.ts` (normalização). +- **Precedente a olhar:** C99/C100 (ficha `Contact` como identidade), B163 + (edição inline de contato em dobradinhas), C6 (import de apoiadores), + C111 (telefone não único — dependência). +- **Risco de acoplamento:** o telefone é chave em fluxos automáticos (import, + convite, login de liderança) — o "principal" precisa ser estável para não + mudar de número a cada edição; e a mudança toca as mesmas páginas de lista do + item **B197** (colunas) — não rodar em paralelo. + +## Dependências + +- **C111** (telefone não único) — dura: múltiplos telefones pressupõem que + números compartilhados entre pessoas são permitidos. +- Serializa com **B197** (mesmas páginas de lista). + +## Fora de escopo + +- Rótulos de telefone (casa/trabalho/whatsapp) e tipo (fixo/móvel) — sem label. +- Reordenação visual por arrasto; "definir principal" por controle explícito + (a ordem da lista é a prioridade). +- Múltiplos telefones no import CSV (a planilha continua com uma coluna). +- Trocar a chave de login da liderança (continua o principal). + +## Rabbit holes de produto + +- **"Telefone com rótulo + tipo + principal explícito".** Vira CRUD de agenda + telefônica com controle extra em todo form. **Corte neste item:** ordem = + prioridade, sem labels, um input repetível. +- **"Mostrar todos os telefones nas listas".** Polui a linha e fura o pedido + implícito de lista compacta. **Corte:** lista mostra o principal; a ficha é o + lugar de todos. + +## Questões em aberto (produto) + +- **O que define o telefone "principal"?** **Opções:** A) o primeiro da lista — + ordem = prioridade, sem controle extra na UI; B) flag explícita "principal" + com controle dedicado. **Recomendação: A** — menos UI, e trocar o principal é + editar a ordem; se a mesa pedir reordenação, vira item futuro. _(assumido — + validar com produto)_ +- **Onde se adiciona telefone?** **Opções:** A) nos formulários de + criação/edição ("+ Adicionar telefone", repetível) e na ficha; B) só na ficha. + **Recomendação: A** — quem cria a pessoa já informa os números que tem; sem + volta à ficha para completar. _(assumido — validar com produto)_ +- **Telefones com número repetido na MESMA pessoa (dedupe interno)?** + **Opções:** A) impede salvar o mesmo número duas vezes na mesma ficha; B) + permite (deixar para a mesa limpar). **Recomendação: A** — custo baixo, + evita lista suja; o bloqueio é só dentro da própria ficha, nunca entre + pessoas. _(assumido — validar com produto)_ + +## Referências + +- GitHub Issue (após registro) +- Canvas UI (gate): `plan-c112-ui-draft.canvas.tsx` +- C111 `docs/plans/telefone-nao-unico.md` (dependência) · C99/C100 · B163 +- `src/collections/Contact.ts` · `src/lib/phone.ts` diff --git a/docs/plans/ocultar-email-por-padrao-listas.md b/docs/plans/ocultar-email-por-padrao-listas.md new file mode 100644 index 00000000..70ef33ba --- /dev/null +++ b/docs/plans/ocultar-email-por-padrao-listas.md @@ -0,0 +1,126 @@ +# Ocultar a coluna de e-mail por padrão nas listas de pessoas + +Status: registrado (blocked até plano em main) +Atualizado em: 2026-08-10 +Issue: #627 +Priority: P2 +Model: composer-2.5 +Impeccable: B — encaixe: seletor de colunas existente (B17) + defaults por lista; sem rota nova +Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-cursor-worktrees-teqo-plans-plan-issue-30/canvases/plan-b197-ui-draft.canvas.tsx +Appetite: ~0,5–1 dia eng; defaults + um split de coluna (pessoas) + um encaixe (assessores) +Responsável: — + +## Intenção + +As listas de pessoas — pessoas, lideranças, dobradinhas, assessores e apoiadores +— mostram o e-mail como coluna sempre visível. No dia a dia da mesa o canal real +é telefone/WhatsApp; o e-mail é o dado menos consultado e ocupa largura em cinco +telas. Queremos que a coluna de e-mail **comece oculta por padrão** nessas +listas, com o usuário podendo **ligá-la manualmente** pelo seletor de colunas — +mecânica que já existe em `/campanha` (usada em Municípios, Territórios etc.). + +## Persona e fluxo + +- **Persona / contexto:** staff da mesa (coordenação e assessores) navegando as + listas de pessoas para gestão diária. +- **Job principal:** ler a lista sem ruído (nome + telefone), e ter e-mail à mão + quando precisar, sem pedir para ninguém. +- **Fluxo desejado:** abre `/campanha/liderancas` → sem coluna de e-mail → quer + ver → abre o seletor de colunas → liga "E-mail" → a escolha fica salva naquele + dispositivo, como já acontece hoje. +- **Anti-goals de produto:** não reordenar colunas, não redesenhar as tabelas, + não mexer nas fichas/detalhes, não mudar o e-mail no card mobile (não há + colunas lá). + +### Esboço de fluxo (B — ver canvas) + +```text +[lista de pessoas] → e-mail oculto por padrão → [seletor de colunas] → liga E-mail +→ coluna aparece e fica lembrada no dispositivo → (desliga de novo quando quiser) +``` + +## Objetivo e aceite + +- Nas **cinco listas** (pessoas, lideranças, dobradinhas, assessores, apoiadores) + a coluna de e-mail começa oculta para quem nunca mexeu no seletor de colunas. +- Em todas, o usuário pode **religar** o e-mail manualmente (seletor de colunas + existente), com a escolha lembrada por dispositivo — mesma mecânica atual. +- Nas listas que já têm o seletor (lideranças, dobradinhas, pessoas, apoiadores), + só muda o **default**; nenhuma outra coluna muda de ordem ou visibilidade. +- Na lista de assessores, o e-mail passa a ser ocultável (hoje não há seletor); + default oculto, religável. +- Na lista de pessoas, o e-mail deixa de ficar embutido na coluna "Contato" e + vira coluna própria, oculta por padrão — a coluna "Contato" fica só com o + telefone. (Na ficha/mobile nada muda.) + +## Dados (intenção) + +Dados: N/A — nenhuma métrica; a mudança é de apresentação de colunas. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/lib/campaignColumnVisibility.ts` (defaults por + lista — mecanismo B17 já existente), páginas + `src/app/(campaign)/campanha/(app)/{liderancas,dobradinhas,pessoas,apoiadores,assessores}/` + e os componentes de lista (`src/components/campaign/{leadership,stateDeputy,people,supporter,advisor}/`). +- **Precedente a olhar:** B17 (`docs/plans/seletor-colunas-lista-municipios.md` — + mecanismo de ocultar colunas), B28 (`email-celular-lista-liderancas.md` — + coluna de e-mail em lideranças), B163 (dobradinhas), C100 + (`pessoas-lista-unificada.md` — coluna "Contato" com telefone+e-mail na mesma + célula, decisão que este item reabre). +- **Risco de acoplamento:** a lista de assessores usa tabela própria (sem o + seletor); o item deve trazer a mesma mecânica de colunas para lá — sem criar + um segundo padrão. Mesmas páginas que o item **C112** (telefones) toca — não + rodar em paralelo. + +## Dependências + +- Nenhuma dura. Serializa com **C112** (múltiplos telefones — mesmas páginas de + lista). + +## Fora de escopo + +- Reordenação de colunas (por arrasto ou não). +- Redesenho das tabelas (cards mobile, larguras, densidade). +- Ocultar e-mail nas **fichas/detalhes** ou nos cards mobile — o pedido é de + colunas de lista. +- Ocultar qualquer outra coluna (telefone, municípios etc.). + +## Rabbit holes de produto + +- **"Já que vamos mexer, esconder também telefone/municípios".** Escopo + explode e mexe em leitura de gestão. **Corte neste item:** só e-mail. +- **"Redesenhar a tabela de assessores para caber no padrão".** O pedido é + visibilidade de coluna, não refactor da superfície. **Corte:** usar a + mecânica de ocultar/religar sem reescrever a tabela. + +## Questões em aberto (produto) + +- **Pessoas (C100) hoje mostra o e-mail dentro da coluna "Contato" (telefone em + destaque, e-mail embaixo). Ocultar "a coluna de e-mail" lá = separar o e-mail + numa coluna própria?** **Opções:** A) sim — coluna "E-mail" própria (como + lideranças/dobradinhas), oculta por padrão; B) esconder só a linha de e-mail + dentro da célula (fora da mecânica de colunas). **Recomendação: A** — + consistência entre as listas; quem religa ganha coluna igual às outras. + _(assumido — validar com produto)_ +- **Apoiadores hoje não tem coluna de e-mail na lista (só na ficha).** **Opções:** + A) adicionar a coluna "E-mail" (oculta por padrão) — completa o pedido; + B) nada a esconder — não mexer. **Recomendação: A** — o pedido nomeia + apoiadores; a coluna nasce oculta e quem quiser liga. _(assumido — validar + com produto)_ +- **Assessores não tem o seletor de colunas.** **Opções:** A) trazer a mesma + mecânica B17 para a tabela de assessores (ocultar por padrão + religar); + B) ocultar o e-mail fixo, sem como religar. **Recomendação: A** — sem religar, + o "ativar manualmente se preferir" não existe nessa lista. _(assumido — + validar com produto)_ +- **Quem já tem escolha salva no dispositivo (cookie do seletor) continua com o + e-mail visível?** **Recomendação: sim** — o default vale para quem nunca + tocou no seletor; a escolha existente é respeitada (contrato atual do + mecanismo). _(assumido — validar com produto)_ + +## Referências + +- GitHub Issue (após registro) +- Canvas UI (gate): `plan-b197-ui-draft.canvas.tsx` +- B17 `docs/plans/seletor-colunas-lista-municipios.md` · B28 `docs/plans/email-celular-lista-liderancas.md` · C100 `docs/plans/pessoas-lista-unificada.md` +- `src/lib/campaignColumnVisibility.ts` · `src/components/campaign/advisor/AdvisorsTable.tsx` diff --git a/docs/plans/telefone-nao-unico.md b/docs/plans/telefone-nao-unico.md new file mode 100644 index 00000000..6c7e2f21 --- /dev/null +++ b/docs/plans/telefone-nao-unico.md @@ -0,0 +1,120 @@ +# Telefone não único: duas pessoas podem compartilhar o mesmo celular + +Status: registrado (blocked até plano em main) +Atualizado em: 2026-08-10 +Issue: #625 +Priority: P1 +Model: cursor-grok-4.5-medium +Impeccable: A — N/A (nenhuma superfície nova; comportamento de escrita, sem UI) +Canvas UI: N/A — sem UI +Appetite: ~0,5–1 dia eng; um outcome verificável +Responsável: — + +## Intenção + +Hoje o sistema recusa salvar uma pessoa cujo celular já pertence a outro contato +("Já existe outro contato com este celular"). Na prática, números compartilhados +são comuns — aparelho de família, número do comitê, número que passou de mão — e +a recusa trava o fluxo de gestão da mesa: a ficha não salva, o assessor fica sem +saída e inventa um número errado ou deixa a pessoa sem telefone. O telefone é um +**canal de contato**, não a identidade da pessoa — a identidade é a ficha `Contact` +(entrega C99). Queremos que duas pessoas possam ter o mesmo número, sem bloquear +nenhum fluxo de cadastro ou edição. + +## Persona e fluxo + +- **Persona / contexto:** staff da mesa (coordenação e assessores) cadastrando e + editando pessoas — lideranças, apoiadores, dobradinhas, assessores — além do + fluxo de importação de apoiadores por CSV. +- **Job principal:** salvar a ficha de uma pessoa com o número real dela, mesmo + que outra pessoa já use o mesmo número. +- **Fluxo desejado:** a assessora salva a liderança Ana com o celular que já é do + comitê → **salva sem erro** → a ficha fica completa com o número real. A busca + por telefone continua encontrando as duas pessoas. Fluxos automáticos (import, + convite) tratam números compartilhados **sem adivinhar** de quem são. +- **Anti-goals de produto:** esta entrega não reconcilia fichas duplicadas do + histórico (fora de escopo, pós-eleição); não muda o que as listas exibem; não + cria um "identificador" novo para a pessoa. + +### Esboço de fluxo (A — omitido; sem UI nova) + +## Objetivo e aceite + +- Cadastrar ou editar uma pessoa com telefone igual ao de outra pessoa **salva + sem erro**, em qualquer um dos cinco fluxos de escrita de pessoa (liderança, + apoiador, dobradinha, assessor, pessoa). +- As mensagens de erro de conflito de celular ("já existe outro contato…") + deixam de aparecer nos fluxos de gestão. +- Fluxos automáticos que usam o telefone como chave (import CSV, convite por + telefone, login de liderança) continuam funcionando; quando o número é + compartilhado, eles **falham fechado com mensagem clara** ou usam a chave que + não é ambígua — nunca "chutam" qual pessoa. +- Nenhum outro campo de `Contact` muda; a busca por telefone continua achando + todas as pessoas com aquele número. + +## Dados (intenção) + +Dados: N/A — nenhuma métrica nova; a mudança é de escrita e matching de contato. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** `src/utilities/contactPhoneInvariant.ts` (regra de + unicidade e locks), `src/collections/Contact.ts` (hook de validação), + `src/collections/CampaignUser.ts` (sincronização conta↔ficha), + `src/utilities/campaignInviteRedemption.ts` (login/convite por telefone), + `src/app/(campaign)/campanha/actions/supporterImport.ts` (dedupe do CSV), + forms de liderança/apoiador/dobradinha/assessor/pessoa. +- **Precedente a olhar:** C99 (`docs/plans/pessoas-identidade-contact.md`) — + declarou o telefone como chave de dedupe da ficha; esta entrega reabre essa + política por pedido do produto; C100 (lista de pessoas). +- **Risco de acoplamento:** o telefone é hoje a chave "pessoa ↔ ficha" em vários + fluxos (import, convite, resolução de conta). Cada fluxo precisa de um + comportamento definido para números compartilhados — sem criar identidade + ambígua nem bloquear a gestão. + +## Dependências + +- Nenhuma dura. Não deve rodar em paralelo com trabalho que edite os mesmos + arquivos de escrita de pessoa (ver `serializes` na Issue). + +## Fora de escopo + +- Múltiplos telefones por pessoa → item **C112** (dependente deste). +- Reconciliação de fichas duplicadas existentes (mesma pessoa em duas fichas) — + aceita e visível; reconciliação segue pós-eleição. +- Mudar o que as listas mostram (telefone principal, colunas) → fora. + +## Rabbit holes de produto + +- **"Já que vamos desbloquear, deduplicar as fichas existentes".** Explosão de + escopo e risco em janela eleitoral. **Corte neste item:** só o comportamento de + escrita; reconciliação segue pós-eleição. +- **"Telefone vira chave solta: import passa a criar ficha nova sempre".** + Perde o casamento pessoa↔ficha do CSV. **Corte:** matching por telefone + continua onde existe, com falha fechada na ambiguidade. + +## Questões em aberto (produto) + +- **Import CSV casa linhas por telefone; com números compartilhados, o que o + import faz?** **Opções:** A) mantém o casamento por telefone e falha fechado + quando o número pertence a mais de um contato — a linha fica sinalizada para + resolução manual; B) para de casar por telefone e cria fichas novas. **Recomendação: + A** — o casamento por telefone é o jeito mais confiável de reencontrar a + pessoa no re-import; ambiguidade pede decisão humana, nunca chute. _(assumido — + validar com produto)_ +- **Login de liderança é pelo celular (nome de usuário da conta). Duas + lideranças com o mesmo número: a segunda entra como?** **Opções:** A) a conta + continua única — quem não tem número exclusivo usa e-mail para entrar; + B) bloqueia criar a segunda conta com o mesmo número. **Recomendação: A** — + o login é de conta, não de pessoa; o bloqueio de conta recriaria o problema + que esta entrega resolve. _(assumido — validar com produto)_ +- **Convite por telefone para número compartilhado:** a quem o convite vincula? + **Opções:** A) falha fechado pedindo escolher a ficha; B) vincula à ficha de + quem gerou o convite (o convite nasce de uma ficha). **Recomendação: B** — o + convite já é gerado a partir de uma pessoa; o número é só o canal de envio. + +## Referências + +- C99 `docs/plans/pessoas-identidade-contact.md` · C100 `docs/plans/pessoas-lista-unificada.md` +- C6 `docs/plans/escala-dry-pos-c6.md` (import com dedupe por telefone) +- `src/utilities/contactPhoneInvariant.ts` (regra atual a reavaliar)