From 8d6c8e331372fd10aff00ccb5ab120ed0aa1b5ff Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Francisco=20Ign=C3=A1cio=20Fontoura=20Solla?= Date: Mon, 27 Jul 2026 21:24:45 -0300 Subject: [PATCH] Move the level control onto B42's shared cell-edit overlay. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit B42 landed on main during this session and extracted `CampaignCellEditOverlay` — Popover on `md+`, Drawer below it, where a popover fights the virtual keyboard. The rebase auto-merged E14's "Nível" block into what had become the Drawer, which compiles and would have opened a popover inside a sheet, so the control now takes `variant` like its three siblings. The container's Popover branch is point for point what E14 had hand-written, so the desktop is unchanged; on touch, where there is no hover to carry it, the trigger now spells the level out instead of showing the bare numeral. --- .cursor/rules/projects/nucleos-eleitorais.mdc | 2 +- docs/plans/niveis-de-envolvimento.md | 1 + .../municipality/MunicipalityList.tsx | 2 + .../MunicipalityListLevelControl.tsx | 258 +++++++++--------- 4 files changed, 130 insertions(+), 133 deletions(-) diff --git a/.cursor/rules/projects/nucleos-eleitorais.mdc b/.cursor/rules/projects/nucleos-eleitorais.mdc index 7e6a90e1..2b3415b0 100644 --- a/.cursor/rules/projects/nucleos-eleitorais.mdc +++ b/.cursor/rules/projects/nucleos-eleitorais.mdc @@ -7,7 +7,7 @@ alwaysApply: false ## Status -- **E14 entregue em código (2026-07-27):** níveis de envolvimento N0–N4 por município, e o **primeiro escritor de produção do `allocationDecision`** (C12 ✓ existia desde 2026-07-24 com access e testes, sem nenhum caller). Migration `20260727_161752_add_municipality_engagement_level`: `engagementLevel` (select `n0…n4`, indexado, **sem default**), `levelNote` (textarea 2000), `levelChangedAt` (date indexada, `readOnly`, escrita por `canSetCampaignSystemField`) e o valor `movimento` no enum de `allocationDecision.outcome` — mover de nível não é `aceita` nem `descarta`, e fingir que era teria envenenado a triagem do E11. **Sem backfill:** o plano pedia default N2; carimbar N2 em 435 municípios afirmaria uma decisão que ninguém tomou, então nulo renderiza "Sem nível" e o filtro por ele **é** a fila de triagem inicial. Mover é decisão de realocação → `canManageMunicipalityEngagementLevel` (`isCampaignUnrestricted`: coordinator/candidate), leitura no bloco staff normal (`canReadCampaignStaffField`); o assessor lê e propõe por sinal. `setMunicipalityEngagementLevelRecord` segura `municipality-engagement-level:{id}` **antes** de ler o nível atual — sem o lock o `from` do snapshot é chute sob concorrência — e grava os dois documentos na mesma transação, com `snapshot = { from, to, reversalSignals, triangulatedShock, violations, overridden, previousLevelChangedAt }`: só os valores sobre os quais a decisão foi tomada, nunca o documento. Histerese do relatório §6.8 em `src/lib/engagementLevel.ts` (puro, client-safe): salto de 2 níveis só com choque triangulado, 3 semanas de proteção pós-promoção (só contra rebaixamento) e **um movimento por mês**, derivável só de `levelChangedAt` — nenhuma query extra. **A rota tem três estados, não dois:** `blocked` (409) devolve as violações tipadas em vez de virar "não foi possível salvar", porque um movimento barrado é uma decisão que a coordenação pode tomar mesmo assim — o Popover mantém o rascunho e oferece o override, que entra no snapshot com exatamente as regras que contrariou. **Submit explícito, não auto-save** (exceção que `campanha-edit-where-you-see` prevê para flows com nota/confirmação; efeito colateral: o controle não vira o 4º call site da máquina de debounce que o **B32+** vai auditar). Lista: 11ª coluna staff com badge outline numerado, filtro `?level=` **nativo** (ao contrário de `classe` (E10), o campo é armazenado e entra no `where`; "sem nível" não cabe num `in` e vira ramo `or` com `{ exists: false }`) e sort `nivel` **em memória**: `n0…n4` até ordena lexicograficamente, mas o Postgres põe NULL primeiro no DESC e sem backfill "N4 primeiro" abriria com uma página de "Sem nível" — `sortByNullableValue` afunda o nulo nas duas direções, como `sem_base` em `classe`, ao custo de carregar o escopo inteiro (o que o sort padrão `deficit` já faz). Verbete `nivel-de-envolvimento` no E18. **Dois achados:** com `overrideAccess: false` o Payload **descarta o campo em silêncio** quando o ator não pode escrevê-lo, em vez de rejeitar o update — o int do assessor prova que o nível **não mudou**, não que a chamada lançou; e o banco de dev local estava fora do histórico de migrations (`allocation_decision.outcome` era `varchar`, sem o tipo `enum_allocation_decision_outcome` que o `teqo_test`, construído só por migrations, tinha) — reparado à mão no `teqo`, nada a fazer em produção. `/campanha/municipios` em 17,7 kB / 322 kB; suítes 64 unit (604) e 53 int (427); Aikido 0 findings. Plano as-built: [`niveis-de-envolvimento.md`](../../../docs/plans/niveis-de-envolvimento.md). +- **E14 entregue em código (2026-07-27):** níveis de envolvimento N0–N4 por município, e o **primeiro escritor de produção do `allocationDecision`** (C12 ✓ existia desde 2026-07-24 com access e testes, sem nenhum caller). Migration `20260727_161752_add_municipality_engagement_level`: `engagementLevel` (select `n0…n4`, indexado, **sem default**), `levelNote` (textarea 2000), `levelChangedAt` (date indexada, `readOnly`, escrita por `canSetCampaignSystemField`) e o valor `movimento` no enum de `allocationDecision.outcome` — mover de nível não é `aceita` nem `descarta`, e fingir que era teria envenenado a triagem do E11. **Sem backfill:** o plano pedia default N2; carimbar N2 em 435 municípios afirmaria uma decisão que ninguém tomou, então nulo renderiza "Sem nível" e o filtro por ele **é** a fila de triagem inicial. Mover é decisão de realocação → `canManageMunicipalityEngagementLevel` (`isCampaignUnrestricted`: coordinator/candidate), leitura no bloco staff normal (`canReadCampaignStaffField`); o assessor lê e propõe por sinal. `setMunicipalityEngagementLevelRecord` segura `municipality-engagement-level:{id}` **antes** de ler o nível atual — sem o lock o `from` do snapshot é chute sob concorrência — e grava os dois documentos na mesma transação, com `snapshot = { from, to, reversalSignals, triangulatedShock, violations, overridden, previousLevelChangedAt }`: só os valores sobre os quais a decisão foi tomada, nunca o documento. Histerese do relatório §6.8 em `src/lib/engagementLevel.ts` (puro, client-safe): salto de 2 níveis só com choque triangulado, 3 semanas de proteção pós-promoção (só contra rebaixamento) e **um movimento por mês**, derivável só de `levelChangedAt` — nenhuma query extra. **A rota tem três estados, não dois:** `blocked` (409) devolve as violações tipadas em vez de virar "não foi possível salvar", porque um movimento barrado é uma decisão que a coordenação pode tomar mesmo assim — o Popover mantém o rascunho e oferece o override, que entra no snapshot com exatamente as regras que contrariou. **Submit explícito, não auto-save** (exceção que `campanha-edit-where-you-see` prevê para flows com nota/confirmação; efeito colateral: o controle não vira o 4º call site da máquina de debounce que o **B32+** vai auditar). Lista: 11ª coluna staff com badge outline numerado, filtro `?level=` **nativo** (ao contrário de `classe` (E10), o campo é armazenado e entra no `where`; "sem nível" não cabe num `in` e vira ramo `or` com `{ exists: false }`) e sort `nivel` **em memória**: `n0…n4` até ordena lexicograficamente, mas o Postgres põe NULL primeiro no DESC e sem backfill "N4 primeiro" abriria com uma página de "Sem nível" — `sortByNullableValue` afunda o nulo nas duas direções, como `sem_base` em `classe`, ao custo de carregar o escopo inteiro (o que o sort padrão `deficit` já faz). Verbete `nivel-de-envolvimento` no E18. **Dois achados:** com `overrideAccess: false` o Payload **descarta o campo em silêncio** quando o ator não pode escrevê-lo, em vez de rejeitar o update — o int do assessor prova que o nível **não mudou**, não que a chamada lançou; e o banco de dev local estava fora do histórico de migrations (`allocation_decision.outcome` era `varchar`, sem o tipo `enum_allocation_decision_outcome` que o `teqo_test`, construído só por migrations, tinha) — reparado à mão no `teqo`, nada a fazer em produção. **Rebase sobre o B42:** o merge encaixou o bloco "Nível" dentro da gaveta mobile que o B42 acabara de criar, então o controle largou o Popover próprio e passou a receber `variant` como os três irmãos (`CampaignCellEditOverlay`: Popover no `md+`, Drawer abaixo) — o ramo Popover do container é ponto a ponto o que o E14 tinha à mão, e no toque o trigger passa a soletrar o nível. Suítes pós-rebase: 66 unit (630) e 54 int (436); Aikido 0 findings. Plano as-built: [`niveis-de-envolvimento.md`](../../../docs/plans/niveis-de-envolvimento.md). - **E13 entregue em código (2026-07-27):** planejador de presença/giros. Núcleo decisório: `visitEligibility.ts` (puro, input explícito espelhando `TerritorialClassInput`) resolve as **5 condições ✓/—** (volume, headroom, rede, janela, encaixe) e as 3 contraindicações (`perdida`/`sem_rede`/`no_teto`, sempre aviso com contra-oferta, nunca bloqueio); `visitPlannerAnchors.ts` guarda as datas das fases (`construcao|consolidacao|ativacao`, cortes ilustrativos no precedente de `territorialClassAnchors.ts`, comparação por data civil da Bahia via `formatBahiaCivilDate` novo em `campaignTime.ts`); `visitPlannerData.ts` (`server-only`) agrupa candidatas por TI. Superfícies: card "Elegibilidade para visita" no `OverviewTab` do detalhe do município e compositor em `/campanha/atividades/giros` (TI → âncora/satélites/semente → gerar rascunhos), alcançado por botão "Planejar giro" na página de Atividades, sem entrada nova no sidebar. **A auditoria pré-implementação derrubou duas premissas do plano de 24/07:** `kind: 'visita'` nunca entrou na migration do C12 e **não entra** — `activity.deputyPresent` já É o marcador de presença do candidato (o giro é derivável por `deputyPresent` + TI + janela de datas), e criar o enum custaria migration por um kind sobreposto a `reuniao_apoio`/`ato`; e `allocationDecision` não tem write path em `src/` (só o int spec), então o "não vá" fica textual e gravar o override virou gatilho do **E11**, que precisa desse write path de qualquer forma. Terceira correção: "satélites contíguos" era impossível — `bahiaTerritories.ts` só tem pertencimento, adjacência de polígono existe apenas nos scripts de build, então **encaixe = mesmo TI**, dito com essas palavras na UI (construir adjacência é o rabbit hole "otimizador de rota" que o plano já proibia). **Duas decisões de implementação que valem por si:** (1) o teto-do-campo-não-capturado e as medianas do catálogo saíram do classificador do **E10** para `municipalityPotential.ts` (`uncapturedFieldVotes`, `catalogMedianUncapturedFieldVotes`, `catalogMedianProjectedValidVotes`, memoizadas) — sem isso o card "Elegibilidade" e a coluna "Classe" seriam duas contas do mesmo número na mesma tela, e ninguém saberia qual estava certa; (2) `createTourDraftActivitiesRecord` relê os ids das paradas com `overrideAccess: false` **dentro** da transação e aborta se algum não voltar, porque o `create` de `activity` é um booleano de staff e Payload não expressa restrição por município no create — buraco achado por int test, sem o qual um assessor semeava rascunhos no estado inteiro postando ids que o compositor nunca lhe ofereceu. Também: `loadMunicipalityVisitEligibility` roda o **mesmo** loader recortado no TI em vez de um segundo avaliador (encaixe é afirmação sobre vizinhos — não é avaliável para um município sozinho, e dois avaliadores começariam a discordar); `composeTourSuggestion` prioriza âncora → semente → satélites (a primeira versão deixava os satélites consumirem as vagas e a semente de expansão nunca aparecia); caps `TOUR_MAX_SATELLITES` 3 e `MAX_TOUR_STOPS` 8, verificados no cliente e revalidados na action; `TourRegionPicker` monta href com `usePathname` + `encodeURIComponent` em vez do serializador canônico (lição do **B14**: arrastaria `bahiaTerritories` + `municipalityCatalog` para o bundle). Dois verbetes E18 (`elegibilidade-para-visita`, `fase-do-calendario`). Suítes **fundidas, não acrescentadas** (arquivos separados empurraram unit e int para timeout): views do planejador em `tests/unit/visitEligibility.unit.spec.ts`, compositor em `tests/int/campaignActivity.int.spec.ts`; `CALENDAR_PHASES`/`VISIT_CONDITIONS`/`VISIT_CONTRAINDICATIONS` são exportadas e varridas lá para provar completude dos rótulos (um `const` usado só como tipo é warning de ESLint, e a varredura é a razão honesta de ser público). First Load JS: `/campanha/atividades/giros` 8,59 kB / 226 kB; detalhe do município 7,3 kB / 232 kB. Sem migration, sem collection, sem `Consent`, sem entidade `tour` persistida. Gate verde e bare (tsc, lint, format:check, knip com o P3 pré-existente, check:cycles, 429 unit+int, build local); Aikido **0 findings**. **e2e não serviu de veredito:** `campaignMunicipalities.e2e.spec.ts` falha em árvore stashada também, em teste diferente a cada rodada — o flake já ledgerizado. **Adiados com gatilho:** override "não vá" gravando `allocationDecision` → **E11**; níveis N0–N4 no gatilho do "não vá" → **E14** (até lá, classe E10 + rede vazia); padrões J como sugestão automática → E11 fase 2; entidade `tour` persistida → 3º giro real composto. Plano as-built: [`planejador-de-giros.md`](../../../docs/plans/planejador-de-giros.md). - **B42 entregue em código (2026-07-27):** polimento mobile de `/campanha/municipios`. **Escopo ampliado na sessão (decisão do usuário): Votos estimados entrou** junto com Tendência e Assessores, o que fez o item passar de 2 para **3 controles com a mesma casca** — critério de extração do repo — e **inverteu a questão em aberto do plano** (que recomendava branch local e extração só no `/simplify`): nasceu `src/components/campaign/shared/CampaignCellEditOverlay.tsx`, Popover em `md+` / Drawer abaixo. A casca carrega **só o container**: cada controle segue dono do seu auto-save, e como o `handleOpenChange` de cada um passa direto para `onOpenChange`, fechar o Drawer grava exatamente como dispensar o Popover (é o que preserva o flush da justificativa do **B24 ✓**). O **Sinal (B26 ✓) não migrou, e o motivo que registrei primeiro estava errado** — não é que "a casca não expressa isso sem virar genérica": o `
` não precisa envolver o `DrawerHeader` (título não é campo) e submit fora do form é HTML padrão via `form=""`. Falta só um **slot `footer`** (+ `formId` do chamador), débito com gatilho. Ele segue sendo o precedente visual (com "Fechar" no lugar de "Cancelar", porque aqui não há o que cancelar) e agora **consome** a classe de trigger da casca e os três atributos ARIA que lhe faltavam. **`useIsMobile` foi descartado por auditoria**: cards (`md:hidden`) e tabela (`hidden md:block`) são árvores irmãs, então `variant` por call site basta — media query trocaria o container depois da hidratação. **Navegação do card = link esticado, zero JS** (`` no `

` com `after:absolute after:inset-0` sobre `
`), então `MunicipalityList` continua Server Component; o botão "Abrir município" saiu — o plano previa testar "tap no título navega" quando o título **ainda não era link**, e a navegação nasceu junto. **Quem é `relative` é o trigger, não a célula:** a primeira versão posicionou os seis `
` do `
` e com eles os rótulos e o padding de cada célula, o que fez metade do card não editar nem navegar (P0 do critique, corrigido movendo `relative` para a classe compartilhada do trigger; o e2e passou a tocar num rótulo `dt` dentro da grade, exatamente a área que o bug engolia). **Dois props `layout` morreram** com a paridade pedida (`TerritorialClassReadout` = badge + `sr-only` do porquê; `MunicipalityListGoalCoverageCell` = sempre `compact`). `autoFocusScenario` suprimido no `sheet` para o teclado virtual não subir antes de o número ser lido; `municipalityName` é **exigido** onde há Drawer e **opcional** no Popover (união discriminada nos props de Votos estimados; o 3º call site, `MunicipalityGoalAccountCard`, é Popover sob o título do próprio município e não passa). A primeira versão o **proibia** no Popover (`?: never`) e criou assimetria na mesma linha da tabela — Tendência/Assessores nomeando o município no rótulo e Votos estimados não; resolvido para o lado de incluir porque a célula do nome é ``, **não** ``, então nada supre o contexto quando o foco entra num trigger (custo: 0,22 KiB gzip em 25 linhas). Sem migration/collection/action/`Consent`. **Critique + `/simplify` (mesma sessão), três defeitos de comportamento além do P0 acima:** (1) o Drawer focava o primeiro tabbable e subia o teclado virtual sobre a folha antes de ela ser lida — `initialFocus` no `DrawerTitle` (`tabIndex={-1}`), e o `preventContentAutoFocus` que devia mitigar isso era **inerte no branch `sheet`**, um prop que mentia → renomeado `preventPopoverAutoFocus`, comportamento do Drawer incondicional e pinado no spec; (2) `aria-label` **substitui** o conteúdo do trigger, então as quatro células anunciavam o verbo e engoliam o valor que o pill/avatar/número mostra → forma do Sinal ("Editar tendência em X — Favorável"), o que de passagem tirou `municipalityName` de payload RSC morto no desktop (27,9 KiB crus / 2,9 KiB gzip em 435 linhas); (3) fechar **commita** o rascunho e desmonta o `Alert`/`aria-live` que carregariam o erro, então falha na saída revertia o badge em silêncio → `toast.error` só quando já fechado, nos quatro controles. Mais: alvos de toque a 44px abaixo de `md` (inputs de votos, chip de assessor, linhas do `Command`), `CampaignHoverTooltip` na Classe do card (`sr-only` sozinho deixava o veredito inalcançável no toque — fecha o "Adiado com gatilho" do plano) e a frase da Cobertura em `sr-only` (`title` não é anunciado nem alcançado por toque). **Reuso: `LeadershipListSupportStatusControl` (B32 ✓) era o branch Popover da casca redigitado à mão** (classe de trigger byte-a-byte inclusive) → 4º call site, ~25 linhas a menos, nenhum prop novo; a string de trigger virou `campaignCellEditTriggerClassName`; `sheetBodyClassName` era o único prop de classe que substituía em vez de compor, o que obrigava o chamador de assessores a re-espalhar `px-4`. **Medido e não pago:** cada Drawer fechado ainda executa o corpo de `DrawerContent`, cujo `cn()` roda `tailwind-merge` sobre 4.068 caracteres (6,2 µs contra 0,8 µs do Popover que substituiu = +0,40 ms em 25 linhas, +7 ms no caminho de 435) — é troca, não adição; o fix é `{everOpened && …}` (não `{open && …}`, senão perde `data-starting-style`). Testes: 6 casos jsdom (3 controles × 2 variants; stubs de `ResizeObserver`/`scrollIntoView` que o `cmdk` exige) + e2e em viewport 390×844 (abrir Tendência não navega; toque num rótulo da grade abre o município). **Nada foi confirmado em device real:** a coleta de evidência visual falhou por falta de automação de browser na sessão, então alvos de toque estão checados por classe, não medidos. Gate tsc/lint/format/knip(P3)/cycles/**592 unit**/**420 int**/build (`/campanha/municipios` 13,4 kB / 320 kB); Aikido 0 findings. Falhas rotativas da suíte cheia reproduzidas **piores na árvore limpa** (4×3, mesmo teste de demanda na mesma linha) sob contenção de outra sessão Playwright — mesmo padrão de ambiente do B27 ✓/B32 ✓. **Cuidado operacional descoberto aqui: `git stash` é compartilhado entre worktrees** — stashar para medir baseline com sessões concorrentes traz o WIP alheio no `pop` (revertido com `git stash store `); prefira patch local. Plano: [`polimento-mobile-lista-municipios.md`](../../../docs/plans/polimento-mobile-lista-municipios.md). - **B41 entregue em código (2026-07-27):** scroll horizontal + coluna Município sticky em `/campanha/municipios` (`md+`). Espelha **B21 ✓** `TerritoryList`: `containerClassName="overflow-x-auto"`, coluna `name` com `sticky left-0 z-20`/`z-[5]` + `min-w-56 bg-background`; wrapper `overflow-visible` mantido. Cards mobile inalterados (**B42**). Sem migration/action/Consent. Critique: P1 `min-w`×`max-w` corrigido; edge fade Adiado. Gate tsc/lint/format/knip(P3)/cycles/540 unit/413 int/build; Aikido 0 findings. Plano: [`scroll-horizontal-lista-municipios.md`](../../../docs/plans/scroll-horizontal-lista-municipios.md). diff --git a/docs/plans/niveis-de-envolvimento.md b/docs/plans/niveis-de-envolvimento.md index 05a5da4c..4028a540 100644 --- a/docs/plans/niveis-de-envolvimento.md +++ b/docs/plans/niveis-de-envolvimento.md @@ -107,6 +107,7 @@ O que ficou diferente do plano, e o que só se soube implementando: - **Custo:** `/campanha/municipios` fica em 17,7 kB / 322 kB de First Load JS. Suítes: 64 unit (604 testes) e 53 int (427). Nada novo no bundle do cliente: a revisão de performance conferiu contra o build de produção que todo módulo que a ilha importa já estava nos chunks da rota. - **O que a revisão pós-entrega corrigiu:** (1) um `blocked` vindo do **servidor** — o caso que só existe sob concorrência, quando outra aba moveu o município — virava string no box de erro, sem o checkbox de override ao lado; agora as violações tipadas voltam ao mesmo lugar que as locais, deduplicadas por `id` e presas ao nível para o qual foram levantadas, que é a única forma de o terceiro estado da rota valer para quem mais precisa dele. (2) `formatEngagementLevelLabel` / `EMPTY_ENGAGEMENT_LEVEL_LABEL` mudaram de `municipalityLabels` para `lib/engagementLevel.ts`: importar um valor daquele módulo arrasta o catálogo de 435 entradas e o glossário de conceitos para dentro da ilha (o padrão de 21 kB do B14), hoje inócuo só porque o controle de tendência já mantém a cadeia viva. (3) `isEngagementLevel` saiu de três chamadas onde o tipo gerado pelo Payload já era a união fechada. - **Débito conhecido (não é do E14 sozinho):** `levelNote` entrou em `municipalityListSelect`, e o sort padrão do staff (`deficit`) roda esse select sobre os 435 municípios do escopo para renderizar 25 linhas — no teto de 2000 caracteres são ~870 KB lidos por request. Hoje custa zero (o campo nasce nulo em todos), e `politicalTrend.note` tem exatamente a mesma forma e já estava lá: o conserto é dos dois juntos (ler as notas só para a fatia da página), não de um. +- **No rebase, o controle deixou de ter Popover próprio.** O **B42** entrou no `main` durante esta sessão e extraiu `CampaignCellEditOverlay` — Popover no `md+`, Drawer abaixo dele, porque no celular o popover disputa espaço com o teclado virtual. O merge automático encaixou o bloco "Nível" dentro do que virou o Drawer, o que compilava e teria aberto um popover dentro de uma gaveta; o controle passou a receber `variant` como os três irmãos (`popover` na tabela, `sheet` no card). O ramo Popover do container é ponto a ponto o que o E14 tinha escrito à mão (mesma tooltip, mesmas classes de trigger, mesmo `onOpenAutoFocus`), então a troca não muda o desktop — e no toque, onde não há hover, o trigger passa a soletrar o nível em vez de mostrar só o numeral. - **Fora do escopo, confirmado na entrega:** `priority` continua intocado (18 arquivos + o seed `projectionSheetParse.ts` que escreve o campo), e nem o E8 nem o E9 leem o nível. ## Referências diff --git a/src/components/campaign/municipality/MunicipalityList.tsx b/src/components/campaign/municipality/MunicipalityList.tsx index d35bf572..b0c73a80 100644 --- a/src/components/campaign/municipality/MunicipalityList.tsx +++ b/src/components/campaign/municipality/MunicipalityList.tsx @@ -378,6 +378,7 @@ const municipalityListColumns = ({ level={municipality.engagementLevel} levelNote={municipality.levelNote} levelChangedAt={municipality.levelChangedAt} + variant="popover" /> ) : ( { level={municipality.engagementLevel} levelNote={municipality.levelNote} levelChangedAt={municipality.levelChangedAt} + variant="sheet" /> ) : ( { const [open, setOpen] = useState(false) const [saved, setSaved] = useState( @@ -189,144 +192,135 @@ export const MunicipalityListLevelControl = ({ const fieldId = (suffix: string) => `municipality-list-level-${suffix}-${municipalityID}` return ( - - -

{formatEngagementLevelLabel(currentLevel)}

- {currentNote ?

{currentNote}

: null} -
- ) : null - } - align="start" - openOnTouch={false} - disabled={open} - > - - - - - event.preventDefault()} - > -
- - Nível de envolvimento - - setDraftLevel(isEngagementLevel(event.target.value) ? event.target.value : '') - } - className="min-h-11 w-full" - > - {EMPTY_ENGAGEMENT_LEVEL_LABEL} - {engagementLevels.map((option) => ( - - {formatEngagementLevelLabel(option)} - - ))} - - - - Motivo -