From 84abfa1a7efca9a5d3c592428335f8f96bf3ff98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Wed, 5 Aug 2026 00:19:46 -0300 Subject: [PATCH] docs(plan): clarify B159 UI alignment intent MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The plan previously suggested the UI wouldn't change — only the code would be refactored. Updated to make explicit that: - Dobradinhas editor changes from RelationChipCell to the same CampaignCellEditOverlay + Command + chips as Assessores/Liderancas. - Liderancas display gains an avatar stack matching Assessores/Dobradinhas. - Inline create is always name-only across all three columns (leadership phone is added later on the detail page). - Assessores is the reference — its UI doesn't change. Related #374 --- .../generalizar-colunas-relacao-municipios.md | 59 +++++++++++-------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/docs/plans/generalizar-colunas-relacao-municipios.md b/docs/plans/generalizar-colunas-relacao-municipios.md index f0e116ab..992bb83a 100644 --- a/docs/plans/generalizar-colunas-relacao-municipios.md +++ b/docs/plans/generalizar-colunas-relacao-municipios.md @@ -1,49 +1,62 @@ # Generalizar editor e display das colunas de relação na lista de municípios Status: ready -Atualizado em: 2026-08-04 +Atualizado em: 2026-08-05 Issue: #374 Priority: P2 Model: composer-2.5 -Impeccable: B — encaixe em `MunicipalityList` (`/campanha/municipios`); colunas já existentes (Assessores, Lideranças, Dobradinhas); sem rota nova -Canvas UI: N/A — consistência visual sobre padrões já em produção; o "antes" e "depois" são variações mínimas do mesmo elemento +Impeccable: B — encaixe em `MunicipalityList` (`/campanha/municipios`); colunas já existentes (Assessores, Lideranças, Dobradinhas); sem rota nova; a UI de Dobradinhas e Lideranças muda para igualar a de Assessores, mas os padrões visuais já estão em produção +Canvas UI: N/A — os padrões visuais de referência já existem em produção na coluna de Assessores (popover `CampaignCellEditOverlay` + Command + chips + criar inline; avatar stack `-space-x-2` com iniciais + tooltip); esta entrega replica esses mesmos padrões nas colunas de Dobradinhas e Lideranças, sem criar design novo Appetite: ~0,75 dia eng; extração de 2 componentes compartilhados + swap nos 3 call sites; sem migration, sem endpoint novo, sem collection Responsável: — ## Intenção -Hoje, na lista de municípios (`/campanha/municipios`), três colunas fazem a mesma coisa — editar relações N:N inline — mas cada uma implementa do seu jeito: +Hoje, na lista de municípios (`/campanha/municipios`), três colunas fazem a mesma coisa — editar relações N:N inline — mas cada uma tem UI diferente. O usuário vê inconsistência: -| Coluna | Editor (popover) | Display (view-only) | -| --------------- | --------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- | -| **Assessores** | `MunicipalityListAdvisorsControl` — `CampaignCellEditOverlay` + Command + chips + criar inline | `MunicipalityAdvisorAvatarStack` — círculos com iniciais + tooltip | -| **Lideranças** | `MunicipalityListLeadershipsControl` — `CampaignCellEditOverlay` + Command + chips + criar inline (name+phone) | ❌ _não tem_ — os chips de texto servem de trigger e display ao mesmo tempo | -| **Dobradinhas** | `MunicipalityStateDeputyRelationCell` sobre `RelationChipCell` — máquina de interação diferente dos outros dois | `StateDeputyAvatarStack` — círculos com iniciais (filtradas de partido) + tooltip | +| Coluna | Editor (popover) | Display (view-only) | +| --------------- | ------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------- | +| **Assessores** | `CampaignCellEditOverlay` + Command + chips + criar inline | Avatar stack (círculos com iniciais, `-space-x-2`, tooltip com nomes) — **referência** | +| **Lideranças** | `CampaignCellEditOverlay` + Command + chips + criar inline | ❌ _não tem_ — os chips de texto de edição fazem as vezes de display; visual diferente | +| **Dobradinhas** | `RelationChipCell` — máquina de interação visualmente diferente (outro container, outra affordance de busca) | Avatar stack (igual ao de assessores, mas implementação separada) | -O código duplicado já está doendo: `MunicipalityListAdvisorsControl` (431 linhas) e `MunicipalityListLeadershipsControl` (530 linhas) são irmãs estruturais — mesma máquina de estado (otimista, sequência de request, pendingCountRef, toggle, criar inline), mesmo Command+combobox, mesmo `CampaignCellEditOverlay`. A dobradinha usa um container diferente (`RelationChipCell` em trigger mode) que replica a mesma experiência com outra API. +O código duplicado é sintoma do desalinhamento: `MunicipalityListAdvisorsControl` (431 linhas) e `MunicipalityListLeadershipsControl` (530 linhas) são irmãs estruturais — mesma máquina de estado, mesmo Command, mesmo overlay. A dobradinha usa um container completamente diferente (`RelationChipCell`) que entrega uma experiência de edição distinta da que o usuário encontra nas outras duas colunas. E no display, o avatar stack de assessores e o de dobradinhas são o mesmo componente escrito duas vezes, enquanto lideranças sequer tem display próprio. -E no display, o avatar stack de assessores e o de dobradinhas são o mesmo componente escrito duas vezes, enquanto lideranças nem display tem — o trigger com Badges de texto serve de readout e de affordance de edição ao mesmo tempo. +**O pedido é alinhar a UI**: as três colunas devem parecer e funcionar iguais para o usuário. A coluna de **Assessores** é a referência — as outras duas mudam para igualá-la. -**O pedido é alinhar**: um editor compartilhado para as três colunas, e um display (avatar stack) compartilhado para as três. +- **Dobradinhas muda de editor:** larga `RelationChipCell` e adota o mesmo `CampaignCellEditOverlay` + Command + chips + criar inline de Assessores/Lideranças. +- **Lideranças muda de display:** ganha o avatar stack (círculos com iniciais + tooltip) que Assessores e Dobradinhas já usam, em vez de usar os chips de edição como único readout. ## Persona e fluxo -- **Persona / contexto:** Staff da campanha (coordenação, assessores) percorrendo a lista de municípios no desktop, editando vínculos inline. O fluxo já existe e funciona — a entrega não muda comportamento, só alinha implementação. -- **Job principal:** Editar (ou só ver) assessores, lideranças e dobradinhas de um município com a mesma affordance, sem surpresa de coluna para coluna. -- **Fluxo desejado:** Igual ao atual. A diferença é interna: o coordenador não percebe que a coluna de Dobradinhas usa um container diferente da de Assessores — porque depois desta entrega, não usa mais. +- **Persona / contexto:** Staff da campanha (coordenação, assessores) percorrendo a lista de municípios no desktop, editando vínculos inline. +- **Job principal:** Editar (ou só ver) assessores, lideranças e dobradinhas de um município com a **mesma UI** nas três colunas. Hoje cada coluna tem uma aparência e interação diferente — o usuário reaprende a cada coluna. +- **Fluxo desejado:** O usuário clica no gatilho de qualquer uma das três colunas e encontra o mesmo popover (`CampaignCellEditOverlay` com Command de busca + chips removíveis + criar inline). Fechado, vê o mesmo display de avatar stack com tooltip. A experiência é idêntica de coluna para coluna — a única diferença é o domínio (assessor, liderança, dobradinha) e o texto dos labels. +- **Mudanças visíveis para o usuário:** + - **Dobradinhas:** o editor popover muda — deixa de ser o container `RelationChipCell` e passa a ser o mesmo `CampaignCellEditOverlay` + Command + chips que Assessores e Lideranças usam. O display (avatar stack) já está alinhado e não muda. + - **Lideranças:** o display fechado muda — deixa de ser só os chips de texto do editor e ganha o avatar stack com círculos de iniciais + tooltip, igual a Assessores e Dobradinhas. O editor popover já está alinhado e não muda. + - **Assessores:** não muda — é a referência que as outras duas colunas passam a seguir. - **Anti-goals de produto:** - Não é uma quarta coluna nem um editor genérico de "qualquer relação" exposto ao usuário. - - Não é redesign do popover — o visual e a interação (Command, chips, criar inline) ficam iguais. + - Não é redesign do popover — o visual de referência (Assessores) é mantido; as outras duas colunas são trazidas até ele. - Não mexe em endpoint, schema, migration, Consent, nem access control. ## Objetivo e aceite -- As três colunas (Assessores, Lideranças, Dobradinhas) usam **o mesmo componente de popover de edição**, extraído das implementações atuais de assessores e lideranças. -- As três colunas usam **o mesmo componente de display** (avatar stack) para o estado fechado/view-only, extraído das implementações atuais de assessores e dobradinhas. -- Nenhuma regressão de comportamento: criar inline, remover chip, busca com acento, estado otimista, tratamento de erro e mensagens de toast continuam iguais por coluna. -- O contrato de props de `MunicipalityList` não muda (as colunas continuam recebendo `advisorOptions`, `leadershipOptions`, `stateDeputyOptions`, etc.). -- A coluna de dobradinhas deixa de usar `RelationChipCell` e passa a usar o popover compartilhado — a migração é interna, sem quebra de API pública. -- `MunicipalityListLeadershipsControl` ganha um display view-only (avatar stack), mesmo que hoje a coluna só seja visível para staff que edita — a peça existe e o contrato fica completo. +**Alinhamento de UI (visível para o usuário):** + +- A coluna de **Dobradinhas** tem o mesmo popover de edição que Assessores e Lideranças: `CampaignCellEditOverlay` + Command de busca + chips removíveis + criar inline. O usuário vê a mesma UI ao editar qualquer uma das três. +- A coluna de **Lideranças** tem o mesmo display fechado que Assessores e Dobradinhas: avatar stack com círculos de iniciais (`-space-x-2`, máx. 3) + tooltip com nomes. O usuário vê a mesma UI nas três colunas fechadas. +- A coluna de **Assessores** é a referência — sua UI não muda; as outras duas são trazidas até ela. + +**Alinhamento de código (interno):** + +- As três colunas usam **o mesmo componente de popover de edição**, extraído das implementações atuais de assessores e lideranças. +- As três colunas usam **o mesmo componente de display** (avatar stack), extraído das implementações atuais de assessores e dobradinhas. +- Nenhuma regressão funcional por coluna: criar inline (só nome; o telefone da liderança pode ser adicionado depois na ficha), remover chip, busca com acento, estado otimista, tratamento de erro e mensagens de toast continuam funcionando. +- O contrato de props de `MunicipalityList` não muda. +- A coluna de dobradinhas deixa de usar `RelationChipCell` e passa a usar o popover compartilhado. +- `MunicipalityListLeadershipsControl` ganha display view-only (avatar stack), mesmo que hoje a coluna só seja visível para staff que edita — a peça existe e o contrato fica completo. ## Dados (intenção) @@ -52,7 +65,7 @@ Dados: N/A — refactor de UI sem métrica, série, ranking ou consulta nova. ## Direção no codebase (hipótese) - **Áreas prováveis:** - - Novo componente shared: `src/components/campaign/shared/MunicipalityRelationEditor.tsx` — extrai a máquina de estado (otimista, sequência, pendingCountRef, toggle, inline create) de `MunicipalityListAdvisorsControl` + `MunicipalityListLeadershipsControl`, parametrizada por: endpoint, opções, labels/copy, build da trigger, e forma de criar inline (só nome vs. nome+telefone vs. nome+partido). + - Novo componente shared: `src/components/campaign/shared/MunicipalityRelationEditor.tsx` — extrai a máquina de estado (otimista, sequência, pendingCountRef, toggle, inline create) de `MunicipalityListAdvisorsControl` + `MunicipalityListLeadershipsControl`, parametrizada por: endpoint, opções, labels/copy, build da trigger. O criar inline é sempre só nome (o telefone da liderança, se necessário, é adicionado depois na ficha). - Novo componente shared: `src/components/campaign/shared/MunicipalityRelationAvatarStack.tsx` — unifica `MunicipalityAdvisorAvatarStack` (73 linhas) e `StateDeputyAvatarStack` (~15 linhas dentro de `MunicipalityStateDeputyRelationCell`), parametrizada por: função de iniciais, tooltip, empty state. - `MunicipalityListAdvisorsControl.tsx` → wrapper fino sobre `MunicipalityRelationEditor`. - `MunicipalityListLeadershipsControl.tsx` → wrapper fino sobre `MunicipalityRelationEditor` + ganha display view-only com `MunicipalityRelationAvatarStack`.