diff --git a/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-desktop-novo.png b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-desktop-novo.png new file mode 100644 index 00000000..036f93fa Binary files /dev/null and b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-desktop-novo.png differ diff --git a/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-desktop.png b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-desktop.png new file mode 100644 index 00000000..77fc178b Binary files /dev/null and b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-desktop.png differ diff --git a/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-mobile-edicao.png b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-mobile-edicao.png new file mode 100644 index 00000000..68405db9 Binary files /dev/null and b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-mobile-edicao.png differ diff --git a/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-mobile.png b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-mobile.png new file mode 100644 index 00000000..2c3886ec Binary files /dev/null and b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft-mobile.png differ diff --git a/docs/plans/contatos-pagina-da-entidade-contact-ui-draft.html b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft.html new file mode 100644 index 00000000..07bf08f9 --- /dev/null +++ b/docs/plans/contatos-pagina-da-entidade-contact-ui-draft.html @@ -0,0 +1,611 @@ + + + + + + + UI draft — Contatos: página da entidade Contact + + +
+

+ Mesa (coordenação/assessores) na lista de Contatos — atualizar ficha e contatar pessoas. + Cenas: desktop 1280px (lista + linha nova) e mobile 390px (cards + sheet de edição). Acento + único = CTA primário (vermelho do app). Chips dentro do input; células = inputs. +

+ + +

+ Desktop — /campanha/contatos (1280px) +

+
+ +
+
+

Contatos

+

+ Fichas da campanha — atualize dados e fale com as pessoas +

+
+ SR +
+ + +
+
+
+ ⌕ + + Gênero: Feminino ✕ + + + Estado: BA ✕ + + + Sem e-mail ✕ + + + ✕ +
+ + +
+ +
+

+ ⌕ Buscar: “maria” +

+

Lideranças: Sim

+

+ Dobradinhas: Sim +

+

Assessores: Sim

+

Equipe: Sim

+

+ Gênero: Masculino +

+

+ Gênero: Não binário +

+

+ Gênero: Não informado +

+

+ Cidade: Salvador +

+

Sem telefone

+

Ordenar: Cidade

+
+
+ + +
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NomeGêneroTelefoneE-mailCidadeEstadoAções
+ + + + + + + + + + + + +
+ ✆ + ✉ + 🗑 +
+
+ + + + +
+ + +
+
+ + + + + + +
+ ✆ + ✉ + 🗑 +
+
+ + + + + + + + + + + + +
+ ✆ + ✉ + 🗑 +
+
+
+ Mostrando 3 de 435 contatos + ‹ 1 · 2 · 3 › +
+
+
+ + +

+ Desktop — linha nova (após “+ Criar contato”) +

+
+
+

Contatos

+ SR +
+
+
+ ⌕ + +
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NomeGêneroTelefoneE-mailCidadeEstadoAções
+ + + + + + + + + + + + +
+ + +
+
Maria dos SantosFeminino(71) 99999-0001maria@email.comSalvadorBA +
+ ✆ + ✉ + 🗑 +
+
+
+
+ + +

+ Mobile — cards (390px, omnibox edge-to-edge sem borda) +

+
+ +
+

Contatos

+ SR +
+ +
+
+ ⌕ + + Gênero: Feminino ✕ + + +
+
+ + + +
+
+ + +
+
+ IA +
+
+
+ + +

+ Mobile — bottom sheet de edição (toque no card) +

+
+ +
+
+

Contatos

+
+
+
+
+
+
+ +
+
+

Editar contato

+
+ + + +

+ Adicionar telefone

+ +
+ + +
+ +
+
+ + +
+
+
+
+ + diff --git a/docs/plans/contatos-pagina-da-entidade-contact.md b/docs/plans/contatos-pagina-da-entidade-contact.md new file mode 100644 index 00000000..3a517ccc --- /dev/null +++ b/docs/plans/contatos-pagina-da-entidade-contact.md @@ -0,0 +1,110 @@ +# Contatos — página da entidade Contact (fichas da campanha) + +Status: rascunho +Atualizado em: 2026-08-13 +Issue: #728 +Priority: P1 +Model: composer-2.5 +Impeccable: C — fluxo novo: rota nova + tabela desktop + cards mobile + omnibox +Rascunho UI: docs/plans/contatos-pagina-da-entidade-contact-ui-draft.html +Appetite: ~2–3 dias eng; uma rota nova com chassis existentes +Responsável: — + +## Intenção + +A página de Pessoas (`/campanha/pessoas`) nasceu de uma necessidade real — editar o contato de todo mundo rápido num lugar só — mas o que ela virou (lista all-featuring, com colunas de capacidade por território) não tem caso de uso: a mesa não navega por capacidades nela, e cada lista especializada já faz isso melhor. O que a mesa realmente precisa é **uma lista de fichas de contato**: ver os dados de contato de qualquer pessoa da campanha num lugar só, atualizar ficha (nome, e-mail, telefones, gênero, estado, cidade, CEP) e **contatar pessoas específicas** (WhatsApp/e-mail) sem saltar entre listas. + +O padrão que deu certo — **toda entidade de pessoa tem uma ficha `Contact`** — é exatamente a fundação do caso de uso: a ficha é a fonte única de dados de contato. Esta entrega constrói a página da ficha. A página de Pessoas **fica no ar como referência** enquanto esta nasce (e cai depois, em decisão futura — ver Fora de escopo). + +## Persona e fluxo + +- **Persona / contexto:** coordenação e assessores na mesa, com um celular na mão ("preciso falar com fulana") ou editando a ficha de quem entrou na campanha. +- **Job principal:** achar a pessoa por qualquer dado de contato (nome, e-mail, telefone, cidade…), ver a ficha completa e atualizar o que estiver errado, e partir dela para o contato (WhatsApp / e-mail). +- **Fluxo desejado:** abre `/campanha/contatos` → vê a lista de fichas (tabela no desktop, cards no mobile) → filtra na omnibox por cada propriedade da ficha (gênero, estado, cidade, ausências como "sem telefone") e/ou busca geral → ordena → edita a ficha onde vê (nome, e-mail, telefones, gênero, estado, cidade, CEP) → contata pela ação da linha (WhatsApp / e-mail / copiar telefone). +- **Anti-goals de produto:** **não** vira a página de Pessoas de novo — sem colunas de capacidade (assessora/lidera/dobra/apoiador), sem territórios, sem papel; **não** é segundo cadastro de pessoa (a ficha continua sendo `Contact`); **não** substitui as listas especializadas (`/liderancas`, `/dobradinhas`, `/apoiadores`, `/assessores` seguem intocadas); **não** é edição em massa / spreadsheet. + +## Decisões de produto (travadas com o humano em 2026-08-13) + +- **A página é da ficha `Contact`, pura.** Colunas desktop, nesta ordem: **Nome, Gênero, Telefone, E-mail, Cidade, Estado** (CEP fica dado da ficha, editável no mobile sheet, sem coluna). Sem coluna de vínculos — foi o que matou a página de Pessoas. +- **Gênero (vocabulário de produto):** Masculino, Feminino, **Não binário**, Não informado — facet e edição usam exatamente esses 4 valores. +- **Busca geral cobre telefone.** O `q` da omnibox casa nome, e-mail e **qualquer** telefone da ficha (multi-telefone, C112) — absorve o gatilho de C121 ("tenho um número na mão, acho a pessoa"). +- **Filtros por propriedade da ficha:** gênero, estado, cidade, ausências ("sem telefone", "sem e-mail"). **Filtros de vínculo** (só facet, nunca coluna): **Lideranças, Dobradinhas, Assessores, Equipe** (nome pt-BR que cobre assessores/coordenadores/candidato — "Equipe" assumido; facet booleano "Sim" para cada vínculo). +- **Ordenação:** pelas colunas relevantes (nome default, cidade, estado, e-mail) — padrão C117 (header sortable + omnibox). Sem ordenação por telefone. +- **Edição onde se vê (desktop):** **as células de dados são inputs** — salva no blur ou Enter; multi-telefones empilhados um sobre o outro na célula. Mobile: toque no card abre **bottom sheet com todos os campos** editáveis (nome, gênero, telefones + adicionar, e-mail, cidade, estado, CEP). Mesmo padrão de escopo do `updatePersonContact` (assessor só edita ficha da carteira dele). +- **Criação de contato:** desktop botão **"+ Criar contato" ao lado de "Colunas"** → linha vazia no topo com ações **Descartar/Salvar**; mobile **FAB de criação** (substitui o FAB de ações rápidas nesta rota; FAB de IA permanece) → bottom sheet de criação. +- **Ações por linha:** **Mensagem no WhatsApp** (`wa.me`, só com telefone — `whatsAppHrefForPhone`), **Enviar e-mail** (`mailto`, só com e-mail), **Apagar contato** — com **alerta de confirmação que lista o contato e todas as entidades linkadas a ele** (liderança, dobradinha, assessor, apoiador, conta de acesso…) que serão removidas. +- **Escopo de acesso = `canReadContacts` existente:** assessor vê só fichas da carteira (via lideranças/dobradinhas/apoiadores do escopo); coordenador/candidato veem tudo. **Leader não acessa** (lockdown — a ferramenta "Meus contatos" do leader fica intocada). +- **Nenhuma migration.** `Contact` já existe; nada no schema muda. +- **Mobile: cards mostram só o telefone principal; "Cidade · Estado"** (nessa ordem); **sem rótulo "Sem e-mail"** — o que não existe simplesmente não aparece. **Omnibox sem bordas, edge-to-edge.** + +## Objetivo e aceite + +- `/campanha/contatos` lista as fichas que o ator pode ler, com **tabela no desktop e cards no mobile** (mesmo chassis das listas atuais). +- Omnibox com facet por propriedade (gênero com os 4 valores, estado, cidade, ausências) + vínculos (Lideranças/Dobradinhas/Assessores/Equipe) + busca geral por nome/e-mail/qualquer telefone + ordenação (nome/cidade/estado/e-mail). **Chips do filtro dentro do input.** +- Edição where-you-see: desktop células são inputs (salva no blur/Enter), telefones múltiplos empilhados; mobile sheet com todos os campos, CEP incluso. Escopo do ator respeitado. +- Criação: desktop "+ Criar contato" ao lado de "Colunas" (linha vazia + Descartar/Salvar); mobile FAB (no lugar do de ações rápidas, IA permanece) → bottom sheet. +- Ações por linha: WhatsApp (só com telefone), e-mail (só com e-mail), Apagar contato com alerta listando as entidades linkadas. +- Mobile: card mostra só telefone principal; "Cidade · Estado"; sem "Sem e-mail"; omnibox edge-to-edge sem bordas. +- Leader não acessa a rota; "Meus contatos" do leader intacta. +- Nenhuma rota pública muda; nenhuma migration; `/campanha/pessoas` continua no ar como referência. + +## Dados (intenção) + +- **Vou apresentar dados?** Não — as colunas expressam a ficha, não métricas. Nenhum agregado entra na superfície. + +## Direção no codebase (hipótese) + +- **Áreas prováveis:** rota nova `src/app/(campaign)/campanha/(app)/contatos/` (ver Questões — decidido: A, mover o leader tool); shells do Pass 2 W1 (URL state, omnibox, tabela, cards, paginação); utilitários de domínio já existentes (`canReadContacts` em `src/utilities/access/contacts.ts`, `contactIdentity.ts`, `contactPhoneLocks.ts`, `src/lib/phone.ts` com `whatsAppHrefForPhone`/`formatBrazilianPhoneInput`). +- **Precedente a olhar:** C116/C130 (células editáveis in-place + célula de contato), C117 (ordenação header + omnibox), C125 (facet de ausência), C112 (multi-telefone), C100 (lista de pessoas como referência estrutural), B18 (filtros salvos — padrão disponível, não prometido no v1), `updatePersonContact` (`src/app/(campaign)/campanha/actions/person.ts` — padrão de escopo de edição de ficha), **apagar com manifest de vínculos** (precedente do `personDelete`/delete de pessoa de C100 — reusar o contrato "o que a remoção leva junto" para o alerta). +- **Risco de acoplamento:** a edição da ficha deve preservar o escopo (assessor só a carteira) e os invariantes do `Contact` (normalização de telefone, nome de dobradinha vinculada); a rota `contatos` hoje pertence ao leader tool — **mover para `/campanha/meus-contatos`** exige atualizar `LEADER_CONTACTS_HOME` (`src/lib/campaignPaths.ts`) e o catálogo `campaignPageChrome`; leader lockdown é o limite da rota; facets de vínculo precisam dos joins existentes (leadership/stateDeputy/campaignUser por ficha) — não podem vazar escopo de assessor. + +## Dependências + +- Nenhuma dura. Soft: C111/C112 (telefone não único / múltiplos telefones — entregues) e C120 (race de telefone em `findOrCreateContactByPhone`, aberta — não bloqueia). + +## Fora de escopo + +- **Derrubar a página de Pessoas** — fica no ar como referência durante esta entrega; a remoção é decisão futura do produto (a rota de Pessoas e seus planos abertos morrem juntos — planos cancelados neste lote). +- Substituir as listas especializadas (lideranças/dobradinhas/apoiadores/assessores). +- Colunas de capacidade/vínculos na lista de contatos (anti-goal explícito — vínculos entram **só** como facet). +- Filtros salvos (padrão B18 existe e pode ser reusado depois se a mesa pedir). +- Dedupe/merge de fichas duplicadas (pré-existente, fora do v1). + +## Rabbit holes de produto + +- **"Só completar" com colunas de capacidade.** Se alguém adicionar "Lidera/Assessora/Dobra" como coluna, vira a página de Pessoas de novo. **Corte:** ficha pura; vínculos só como facet da omnibox. +- **"Só completar" com edição em massa.** Editar várias fichas de uma vez = spreadsheet mode sem pedido. **Corte:** edição in-place de uma ficha por vez. +- **"Só completar" com a busca virando filtro por município.** Município não é propriedade da ficha `Contact` (é do vínculo). **Corte:** facet de cidade/estado (texto da ficha), não de município de capacidade. +- **"Só completar" com copiar telefone nas ações.** A mesa não pediu; WhatsApp + e-mail + apagar cobrem o job. **Corte:** sem ação extra no v1. + +## Questões em aberto (produto) + +_Resolvidas no gate 2026-08-13:_ rota = **A** (leader tool move para `/campanha/meus-contatos`; nova página toma `/campanha/contatos`); CEP editável = **A** (sim, no sheet mobile); ordenação por telefone = **não**; kill list = **confirmado**. + +- **Nome do facet de staff?** **Opções:** A) "Equipe" | B) "Staff" | C) "Pessoal da campanha". **Recomendação:** A — pt-BR curto que cobre assessores, coordenadores e candidato. _(assumido — validar)_ + +## Referências + +- GitHub Issue #728 +- Rascunho UI (gate): docs/plans/contatos-pagina-da-entidade-contact-ui-draft.html + PNGs embutidos abaixo +- Planos cancelados neste lote (decisão "Pessoas vira referência"): C130+ (#717), C132 (#699), C135 (#714), C136 (#722), C137 (#724), C138 (#725), C121 (#664) +- Abrir primeiro: `src/collections/Contact.ts`, `src/utilities/access/contacts.ts`, `src/app/(campaign)/campanha/(app)/pessoas/page.tsx`, `src/utilities/people/peopleData.ts`, `src/components/campaign/shared/CampaignListOmnibox.tsx` +- `AGENTS.md` — `Contact` é a ficha de pessoa (convenção travada); leader lockdown; escopo de assessor por carteira + +## Rascunho UI (gate) + +Desktop — tabela (Nome, Gênero, Telefone, E-mail, Cidade, Estado), células = inputs, chips dentro do input, "+ Criar contato" ao lado de "Colunas": + +![Rascunho UI — desktop](contatos-pagina-da-entidade-contact-ui-draft-desktop.png) + +Desktop — linha nova após "+ Criar contato" (Descartar/Salvar): + +![Rascunho UI — desktop, linha nova](contatos-pagina-da-entidade-contact-ui-draft-desktop-novo.png) + +Mobile — cards edge-to-edge, omnibox sem borda, só telefone principal, "Cidade · Estado", FAB de criação (IA permanece): + +![Rascunho UI — mobile](contatos-pagina-da-entidade-contact-ui-draft-mobile.png) + +Mobile — bottom sheet de edição (toque no card): + +![Rascunho UI — mobile, sheet de edição](contatos-pagina-da-entidade-contact-ui-draft-mobile-edicao.png) diff --git a/docs/plans/escala-dry-pos-c130.md b/docs/plans/escala-dry-pos-c130.md deleted file mode 100644 index 75c297ca..00000000 --- a/docs/plans/escala-dry-pos-c130.md +++ /dev/null @@ -1,76 +0,0 @@ -# Escala e DRY pós-C130 (registro de facets da omnibox) - -Status: rascunho -Atualizado em: 2026-08-11 -Item do roadmap: fill-in **C130+** pós-[#697](https://github.com/fsolla/teqo/issues/697) -Impeccable: A — sem superfície UI nova (DRY/estrutura) -Appetite: ~0,5–1 dia eng -Responsável: — - -## Contexto - -O C130 adicionou o **5º facet** da lista de pessoas (`party`), cada um com o mesmo -bloco em `peopleOmnibox.ts` (seeds com `createOmniboxSuggestionSeed`, chips, -branches de prefixo em `applyPeopleOmniboxSuggestion`/`removePeopleOmniboxChip`) -mais o toggle correspondente em `peopleListFilters.ts` e o param no -`peopleListUrl.ts`. O padrão já era repetido para capacity/municipality/status/ -ausência — e a mesma forma existe nas outras listas (lideranças, municípios, -apoiadores). A cada facet novo, o custo de adição é ~5 edições em 3 módulos, com -risco de esquecer um branch (facet que aparece nas sugestões mas não filtra). - -## Objetivos - -- **Registro declarativo de facet** (um lugar por facet: prefixo do id, grupo, - label, keywords, toggle/parse): `peopleOmnibox.ts` e `peopleListFilters.ts` - derivam seeds/chips/branches do registro; a adição de um facet novo vira uma - entrada, não cinco edições. -- Sem mudança de URL/contrato (params, chaves e labels pt-BR iguais). -- Escopo mínimo: **pessoas** (os 5 facets). Generalizar para as outras listas - só quando a segunda lista adotar (2º consumidor) — mesmo corte do C130. -- **Hazard documentado no registro:** valores de facet de texto livre (partido) - não podem conter `:` (separador do id de sugestão) — validação no registro. - -## Já resolvido no simplify/critique (não reabrir) - -- Parse do `party` simplificado para `allParamValues(...).filter(≤32)` (o - helper já trima/deduplica) — redundância removida. -- Comentários de sort key (`peopleListUrl`/`peopleData`) reconciliados com a - exceção C130 (base/party invisíveis vs `email` oculta-por-toggle). -- Comentários "Aliada em" → "Dobra em" (3 pontos). -- Orfandade `field: 'city'` removida (type/schema/record/formAction). -- Tooltip span-wrappers documentados por site (disabled vs ref-forwarding). - -## Explicitamente fora - -- **Flake `googleCalendarSync.int.spec.ts`** (falha intermitente, reproduz no - main limpo) — classe de teste instável pré-existente, fora deste lote. -- **Flakes e2e sob carga da máquina** (setup prewarm socket hang-up; picker - "2022"/B34 em runs combinados com load ~60+; passam isolados e no main) — - classe já documentada no changelog B197+; reavaliar com harness dev - estabilizado. -- **Tooltip `openOnTouch` nas ações da tabela de pessoas** — a tabela é - desktop-only (`md:block`); touch não a alcança. Gatilho: se a tabela ganhar - cobertura coarse, seguir o precedente B23 (`openOnTouch={false}` em - triggers que abrem Popover/Dialog). -- **Label mobile "Base:" vs 2ª linha desktop sem label** — decisão de gate - (mobile muda só o nome da coluna; C130 anti-goal). Gatilho: redesign dos - cards mobile. -- **Janela do save já-despachado no Escape** (draft descartado localmente mas - o save em voo persiste) — mitigada com `requestId` bump; janela - completada-save irrecuperável client-side, documentada no código. Gatilho: - se o autosave da célula virar transacional. - -## Fases - -1. **Registro de facets de pessoas** — declarar os 5 facets existentes; derivar - seeds/chips/apply/remove/toggles do registro; unit tests do - `peopleOmnibox`/`peopleListFilters` inalterados em comportamento. -2. **Gates** — `pnpm gate:fast`, e2e `campaignPeople`. - -## Guardrails - -- Identificadores em inglês; labels pt-BR intactos (grupos "Capacidade", - "Município", "Apoio", "Ausência", "Partido", "Ordenação"). -- Sem migration/access/Consent/URL nova. -- Precedentes: `escala-dry-pos-c6.md` (F3 — DRY de filtros/forms), - `escala-dry-pos-c8.md` (facet de município de apoiadores). diff --git a/docs/plans/pessoas-criacao-rapida-na-lista.md b/docs/plans/pessoas-criacao-rapida-na-lista.md deleted file mode 100644 index 245778a5..00000000 --- a/docs/plans/pessoas-criacao-rapida-na-lista.md +++ /dev/null @@ -1,69 +0,0 @@ -# Pessoas: criação rápida de pessoa na lista (linha nova no topo com botão Salvar) - -Status: rascunho -Atualizado em: 2026-08-11 -Issue: #699 -Priority: P1 -Model: composer-2.5 -Impeccable: B — encaixe na tabela/omnibox de `/campanha/pessoas` (botão ao lado do seletor de colunas + linha-draft) -Canvas UI: /home/fsolla/.cursor/projects/home-fsolla-Code-teqo/canvases/plan-pessoas-ajustes-ui-draft.canvas.tsx (seção "Criação rápida de pessoa") -Appetite: ~0,5–1 dia eng; uma linha-draft + ação de criação -Responsável: — - -## Intenção - -Cadastrar uma pessoa nova exige hoje sair da lista (fluxo de criação das listas especializadas). A mesa quer criar direto em `/campanha/pessoas`: um botão "Nova pessoa" ao lado do seletor de colunas abre uma **linha em branco no topo da lista**, onde nome/telefone/capacidades são preenchidos inline e um botão de **Salvar** (na coluna de ações) confirma a criação. - -## Persona e fluxo - -- **Persona / contexto:** coordenador/candidato (e assessor na carteira dele) cadastrando contato novo durante atendimento. -- **Job principal:** criar a ficha da pessoa e já amarrar a primeira capacidade (cidade assessorada/liderada/onde dobra) sem trocar de tela. -- **Fluxo desejado:** clico em "Nova pessoa" → uma linha vazia aparece no topo da lista (nome e telefone em branco, capacidades vazias) → digito nome e telefone → adiciono a primeira cidade de uma capacidade (com C128, a pessoa nasce naquela tabela) → clico em "Salvar" → a linha vira uma pessoa de verdade na lista. "Descartar" (X) remove a linha sem salvar. -- **Anti-goals de produto:** não é modal/formulário novo (a linha é a superfície); não é segundo cadastro paralelo de pessoa (ficha continua `Contact`); não substitui os fluxos ricos das listas especializadas. - -## Objetivo e aceite - -- Botão **"Nova pessoa"** visível ao lado do seletor de colunas (barra de filtros) para quem pode criar (staff; escopo de carteira preservado para assessor). -- Clicar abre **uma linha-draft no topo da lista**, destacada, com: Nome e Contato (telefone) editáveis inline, as três colunas de capacidade vazias, e na coluna de ações um botão **Salvar** (+ descartar). -- **Salvar** cria a ficha (e, com C128, os vínculos de capacidade já adicionados na linha) e a pessoa aparece na lista; **Descartar** fecha a linha sem escrever nada. -- Salvar sem nome (ou só com nome, sem capacidade) não quebra a lista: pessoa sem capacidade continua fora da lista (regra C100) — a linha avisa quando isso acontecer em vez de "sumir" com a pessoa. -- A linha-draft respeita os mesmos limites das células normais (escopo de acesso, chips de território, "Salvador" agregado de C131). -- Sem regressão nas linhas normais: a linha-draft nunca conta como linha real (ordenação, filtros, paginação, facetas não a incluem). - -## Dados (intenção) - -- **Vou apresentar dados?** Não — cadastro, sem métrica. - -## Direção no codebase (hipótese) - -- **Áreas prováveis:** `src/app/(campaign)/campanha/(app)/pessoas/page.tsx` (botão na barra de filtros, linha-draft no topo da tabela), células de capacidade em modo `draft` (as células de município já têm contrato de draft local), ação de criação de pessoa no estilo das ações de domínio existentes (`src/app/(campaign)/campanha/actions/…`), escrita transacional Contact + capacidades. -- **Precedente a olhar:** C128 (criação de entidade pela primeira cidade), criação de liderança/dobradinha/contato nas listas especializadas (findOrCreate de Contact + entidade, transação), células `draft` (`MunicipalityPortfolioCell.draft`), C116 (células sempre-input). -- **Risco de acoplamento:** a linha-draft lida com "pessoa que ainda não existe" — toda célula que hoje parte de `ownerId` existente precisa do caminho de criação; a regra C100 (sem capacidade não há linha) determina o que acontece ao salvar. - -## Dependências - -- **C128** (ordem/forte) — sem o ciclo de vida "primeira cidade cria a entidade", a linha-draft não consegue amarrar capacidades no momento da criação. -- Suave com C130 (a linha-draft usa as mesmas colunas/larguras da tabela ajustada). - -## Fora de escopo - -- Wizard rico de criação (telefones múltiplos, partido, organizações, convite) — a linha é o cadastro mínimo; o resto segue nas fichas. -- Edição de dados da linha-draft além de nome/telefone/capacidades (e-mail fica para depois da criação). -- Criação a partir do mobile (cards) no v1 — a pedido é o botão ao lado de Colunas (desktop). - -## Rabbit holes de produto - -- **Linha-draft vira mini-wizard**: se cada capacidade pedir seu próprio fluxo de criação na linha, a superfície explode. **Corte:** a linha só repete o comportamento das células normais (com C128 criando as entidades); sem campos extras além de nome/telefone. -- **"Salvar sem capacidade some a pessoa"**: a regra C100 confunde se a linha apenas "desaparece". **Corte:** aviso claro na linha ao salvar sem capacidade ("a pessoa não aparece na lista enquanto não tiver uma capacidade") + manter o cadastro feito (a ficha existe para o admin). -- **Duas linhas-draft simultâneas**: só uma linha de criação por vez (abrir outra descarta/fecha a atual sem salvar). - -## Questões em aberto (produto) - -- **Salvar pessoa sem nenhuma capacidade?** **Opções:** (a) salva a ficha e avisa que ela não aparece na lista (regra C100 mantida); (b) bloqueia o Salvar até ter uma capacidade. **Recomendação:** (a) — cadastro não deve ser impedido, mas o destino fica explícito no aviso. _(assumido — validar no gate)_ -- **Quem pode usar "Nova pessoa"?** **Opções:** (a) todo staff da página (assessor cria na carteira dele); (b) só coordenação/candidato. **Recomendação:** (a) — com escopo de carteira igual às células; coordenação continua vendo tudo. _(assumido — validar no gate)_ - -## Referências - -- Canvas UI (gate): plan-pessoas-ajustes-ui-draft.canvas.tsx (seção C132) -- Planos: [pessoas-qualquer-capacidade-para-toda-pessoa.md](pessoas-qualquer-capacidade-para-toda-pessoa.md) (C128 — criação pela primeira cidade), [pessoas-lista-unificada.md](pessoas-lista-unificada.md) (C100 — regra de visibilidade por capacidade), [pessoas-edicao-inplace-lista.md](pessoas-edicao-inplace-lista.md) (C116) -- `AGENTS.md` — transações multi-coleção, escopo de acesso por capacidade