9.2 KiB
B156 — Assessores por dobradinha
Status: blocked (Issue: #360)
Atualizado em: 2026-08-04
Item do roadmap: docs/roadmap.md (Trilha B, item B156)
Impeccable: C — coluna nova na lista + seção nova no detalhe (superfície existente)
Appetite: ~0,75 dia eng; migration de campo + coluna + seção detalhe
Model: composer-2.5
Responsável: —
Dados → decisão → apresentação
Dados: N/A (atribuição categórica de pessoas, sem KPI ou métrica).
Contexto
Nas sessões de planejamento de campanha, a coordenação designa assessores para gerir cada dobradinha — um assessor fica responsável por cuidar do relacionamento com o deputado estadual parceiro: garantir que receba material (adesivos, santinhos), manter contato e alinhar a atuação nos municípios onde a dobradinha está presente.
Hoje essa designação existe só fora do sistema. Não há campo no modelo de
StateDeputy para registrar quais assessores respondem por qual dobradinha.
O que já existe:
StateDeputy(dobradinha):name,slug,party,notes,createdByMunicipality.advisors: padrão estabelecido de atribuição de assessores (hasMany →campaignUser, validated comeligibleCampaignStaffWhere, accesscanManageCampaignStaffFieldpara unrestricted)- Lista
/campanha/dobradinhascom colunas Nome, Partido, Municípios, Lideranças - Detalhe
/campanha/dobradinhas/[slug]com seções Municípios e Lideranças - Access de
StateDeputy: staff lê, unrestricted (+admin) cria/edita, admin-only delete
Por que não é um problema de produto conhecido antes: a campanha está entrando na fase de operação concreta das dobradinhas (distribuir material, coordenar agendas). Sem o vínculo no sistema, a coordenação pergunta "quem está com a dobradinha X?" e a resposta está na cabeça das pessoas.
Objetivos
- Adicionar campo
advisorshasMany →campaignUseremStateDeputy - Access de leitura: staff vê; escrita: unrestricted (coordinator + candidate)
atribui — mesmo padrão de
Municipality.advisors - Validation: assessores designados devem ser staff elegível
(
eligibleCampaignStaffWhere) — mesmo padrão devalidateMunicipalityAdvisors - Coluna "Assessores" na lista
/campanha/dobradinhas— chips com nome, link para/campanha/assessores/[id], toggle add/remove com busca inline (mesmo componenteMunicipalityPortfolioCellgeneralizado, ou umAdvisorRelationCellse o padrão divergir) - Seção "Assessores" no detalhe
/campanha/dobradinhas/[slug]— lista de nomes linkados com badge de contagem, paralela às seções Municípios e Lideranças - Loader
loadStateDeputyDetaile view modelStateDeputyRowViewModelincluem advisors - Migration nova (campo de relacionamento)
Decisões travadas
- Campo no
StateDeputy, não em superfície separada. A relação é intrínseca à dobradinha — não precisa de collection de join nem de UI genérica de "atribuições". Rejeitado: tabela de joinStateDeputyAssignment(overengineered para um relacionamento 1:N simples); gerenciar do lado do assessor (outro job, útil mas separado: "que dobradinhas este assessor gere?"). - Access = mesmo padrão de
Municipality.advisors. Unrestricted atribui; staff lê. Rejeitado: só coordinator (candidate ficaria cego); leader vê (leader é lockdown — não vê dobradinhas de forma alguma). - Coluna na lista, não só no detalhe. O job "quem cobre qual dobradinha?" é comparativo entre linhas — pede tabela. Rejeitado: só no detalhe (obriga abrir N fichas para responder a pergunta); tabela pivô assessor×dobradinha (outra tela, outro appetite — bom, mas separado).
- Sem alterar
Municipality.advisorsou a página de assessores. Este item modela dobradinha→assessores; o inverso (assessor→dobradinhas) é natural como follow-up mas não neste appetite.
Questões em aberto
- Coluna "Assessores" na lista usa qual padrão de chip?
Opções: A) mesmo
MunicipalityPortfolioCellgeneralizado (chips de nome + X para remover + busca inline com toggle) — padrão que já serve municípios e lideranças na mesma lista | B)LeadershipStateDeputyRelationCelladaptado (chips sem o drawer de busca, edição só no detalhe). Recomendação: A — o job principal é "editar onde se vê", e o componente de municípios na mesma página já prova que funciona com advisors. O custo de generalizar é baixo (passarcollection/labelFieldcomo parâmetro) e o ganho de UX é alto. - No detalhe, a edição dos assessores é inline (chips com busca) ou link "gerencie na lista"? Recomendação: chips com busca inline (mesmo padrão da lista) — "edit where you see".
- Exibir assessores da dobradinha na busca global do Início
(B52 já cobre
searchHomeStateDeputies)? Recomendação: não neste appetite — B52 retorna hits de dobradinha por nome; assessores associados entram como card de contexto se/quando o desenho de resultado evoluir (follow-up). - Ordenação/filtro por assessor na lista de dobradinhas? Recomendação: não neste appetite — coluna de exibição resolve o job imediato; filtro/ordenação é follow-up natural se a lista crescer (hoje tem ~dezenas de dobradinhas).
Não escopo
- Página de assessor mostrando "suas dobradinhas" (inverso da relação)
- Filtro por assessor na lista de dobradinhas
- Ordenação por assessor
- Ações rápidas de dobradinha no contexto do assessor
- Alterar
Municipality.advisorsou a página/campanha/assessores - Consent novo (staff interno gerenciando staff — sem LGPD adicional)
- Notificações quando um assessor é designado/removido
- Sincronizar a designação com eventos de campanha ou distribuição de material
Rabbit holes
- Generalizar
MunicipalityPortfolioCellpara qualquer relação. O componente hoje aceitaownerId,municipalityIds,municipalityIndex,commitAction— fortemente tipado para municípios. Generalizar para qualquer{ id, label, href }com action genérica é um refactor que toca 3+ call sites (lideranças, dobradinhas, assessores). Mitigação: começar com uma cópia especializadaAdvisorPortfolioCell(só o que muda é a fonte de busca:campaignUsercomeligibleCampaignStaffWhereem vez do portfolio index). Se a duplicação doer, extrair depois (padrão "second use extracts"). - Edição de assessores direto na lista versus só no detalhe. Se a coluna for read-only com link para detalhe, o appetite cai pela metade mas o job "comparar e editar" fica pior. Mitigação: começar com edição inline; se o componente de busca de staff for complexo demais, fallback para só exibição + link no detalhe.
Adiado com gatilho
- Filtro/ordenação por assessor na lista. Revisitar quando: a lista de dobradinhas passar de ~40 itens e a rolagem visual para achar "as minhas" virar atrito medido.
- Busca global retornando assessores como contexto da dobradinha. Revisitar quando: o card de resultado de busca de dobradinha for redesenhado com metadados expandidos.
Dependências
- Nenhuma dura. Reusa
eligibleCampaignStaffWhere,canManageCampaignStaffField,canReadCampaignStaffField, e o padrão deMunicipality.advisors. - Sem dependência de B19 (assessores) — o campo
campaignUserjá existe.
Canvas UI
Canvas de rascunho UI/UX pendente (sem skill canvas disponível nesta
sessão). O executor deve criar o canvas no gate de implementação.
Resumo textual da superfície:
Lista /campanha/dobradinhas:
- Nova coluna "Assessores" entre "Municípios" e "Lideranças" (ou após "Lideranças" se fizer mais sentido de leitura)
- Célula: chips com nome do assessor (ex. "Maria Silva ✕"), link para
/campanha/assessores/[id], toggle de adição via busca inline decampaignUser(eligibleCampaignStaffWhere) - Comportamento: passar o mouse revela ✕ para remover; busca no canto da célula (input que filtra staff elegível)
- Vazio: "—" (sem estado especial)
Detalhe /campanha/dobradinhas/[slug]:
- Nova
<section>entre "Lideranças associadas" e "Editar dobradinha" (ou como terceiro card no grid 2-col, se couber) - Cabeçalho: ícone
UserCog+ "Assessores responsáveis" +<Badge>com contagem - Lista de chips: nome linkado para
/campanha/assessores/[id] - Adição: input de busca inline que filtra staff elegível e sugere nomes
- Vazio: "Nenhum assessor designado. Atribua pela lista de dobradinhas."
Referências
src/collections/StateDeputy.ts— coleção atual (semadvisors)src/collections/Municipality.ts— padrãoadvisors(L298–308) + validaçãosrc/utilities/access/stateDeputies.ts— access atualsrc/utilities/access/shared.ts—eligibleCampaignStaffWheresrc/utilities/stateDeputyData.ts— loaders da lista e detalhesrc/app/(campaign)/campanha/(app)/dobradinhas/page.tsx— listasrc/app/(campaign)/campanha/(app)/dobradinhas/[slug]/page.tsx— detalhedocs/plans/gerenciar-assessores.md— B19, padrão de gestão de assessoressrc/components/campaign/shared/MunicipalityPortfolioCell.tsx— padrão de chip-cell com busca inline (referência para o componente de advisors)