import Link from 'next/link' import type { ReactNode } from 'react' import { CampaignInfoHint } from '@/components/campaign/CampaignInfoHint' import { MunicipalityHoverTooltip } from '@/components/campaign/MunicipalityHoverTooltip' import { MunicipalityListExpectedVotesControl } from '@/components/campaign/MunicipalityListExpectedVotesControl' import { Progress } from '@/components/ui/Progress' import { CAMPAIGN_CONCEPTS_PATH, campaignConceptHref, type CampaignConceptId, } from '@/lib/campaignIntelligenceConcepts' import { formatElectionNumber } from '@/lib/electionInsights' import { formatGoalCoverageDeficitLabel, formatGoalCoverageRatioLabel, formatRatioAsPercentLabel, goalCoverageProgressPercent, type MunicipalityGoalCoverage, } from '@/utilities/goalCoverage' import type { MunicipalityPotential, RollOff } from '@/utilities/municipalityPotential' import type { VoteEstimateScenarioViewModel } from '@/utilities/voteEstimate' import type { MunicipalityPledgeCoverageView } from '@/utilities/votePledgeData' /** * One `dt`/`dd` pair in the diagnostic grid, with a hover/focus/tap tooltip * explaining the metric — deliberately not the "?" icon button pattern used * by `CampaignInfoHint` in the card title (that stays reserved for * card-level context that persists open; this is a per-metric definition * that dismisses on its own), per the E8 UI feedback: these are dense * numbers that need an inline explanation, not a separate affordance to * seek out. Reuses `MunicipalityHoverTooltip` (same shadcn Tooltip stack * wired for baseline/sortable-header hints), which handles the tap-to-open * itself. * * A real ` ) /** * Two-tier tooltip copy: a short lead sentence naming what the metric * measures (its one bolded term), then a visually secondary formula line — * split per the E8 `/impeccable critique` finding that a single ~40-word * sentence with three technical terms didn't resolve the coordinator's * confusion. `formula` sits at `text-background/70` (tooltip content is * dark, so this lightens rather than muting against a light background). * * `conceptID` appends E18's "Saiba mais" deep link (every caller documents * one, so this is required, not optional). The tooltip content is hoverable * (Radix keeps it open while the pointer is inside) and its own touch-dismiss * ignores taps on the content, so mouse and touch both reach the link; a * keyboard user can't tab into tooltip content, which is why the card * title's `CampaignInfoHint` Popover carries the same link. */ const MetricExplanation = ({ lead, formula, conceptID, }: { lead: ReactNode formula?: ReactNode conceptID: CampaignConceptId }) => (

{lead}

{formula ?

{formula}

: null} Saiba mais
) const formatRollOff = (rollOff: RollOff | null): string => { if (!rollOff) return 'Sem majoritária 2022 disponível' const votesLabel = formatElectionNumber(rollOff.votes) const percentLabel = rollOff.percentOfTurnout != null ? ` (${formatRatioAsPercentLabel(rollOff.percentOfTurnout)} do comparecimento)` : '' return `${votesLabel}${percentLabel}` } /** * E8 "conta da cadeira" — meta × comprometido para este município, com o * bloco de diagnóstico (teto do campo, captura, share intracampo, roll-off). * Cenário fixo `central`: o detalhe não tem seletor de cenário (a lista e o * dashboard têm). */ export const MunicipalityGoalAccountCard = ({ municipalityID, expectedVotes, pledgeCoverage, suggestedGoal, goalCoverage, potential, }: { municipalityID: number expectedVotes: VoteEstimateScenarioViewModel pledgeCoverage: MunicipalityPledgeCoverageView | null suggestedGoal: number goalCoverage: MunicipalityGoalCoverage potential: MunicipalityPotential }) => { const usesMesaEstimate = expectedVotes.central != null const latestIntraFieldShareYear = Math.max( ...Object.keys(potential.intraFieldShareByYear).map(Number), ) const latestIntraFieldShare = potential.intraFieldShareByYear[latestIntraFieldShareYear] ?? null return (

Conta da cadeira

Meta é a estimativa da mesa; sem estimativa, usa a meta sugerida — a votação do próprio Jorge Solla aqui em 2022 (cenário média).

Comprometido é só a soma das declarações de lideranças — nunca a meta.

O roll-off e o teto do campo (abaixo) só usam 2022 — a majoritária de 2014/2018 já foi importada, mas ainda não entra nessas contas.

{/* Keyboard path into E18's documentation: Popover content is tabbable, tooltip content is not, so this link (unlike the per-metric ones) is reachable without a pointer. */} Como cada número é calculado

Votos estimados

Meta usada na conta: {formatElectionNumber(goalCoverage.goal)} ( {usesMesaEstimate ? 'estimativa da mesa' : 'meta sugerida: votação de 2022 aqui'})

{formatGoalCoverageRatioLabel(goalCoverage)} Cobertura da meta
{goalCoverage.coverageRatio != null ? ( // h-2 (vs. the shared default h-1): at 0% coverage — the most common // early-campaign state — the empty track is the only visual signal, // and a 4px hairline read as near-invisible in the E8 // `/impeccable critique`. ) : null}

{formatGoalCoverageDeficitLabel(goalCoverage)}

Teto do campo projeta quantos votos válidos o campo pode alcançar em 2026. } formula="Fórmula: votos do presidencial do campo em 2022 (1º turno), ajustados pelo crescimento de comparecimento projetado para a disputa de deputado federal." conceptID="teto-do-campo" /> } /> Captura mostra quanto do teto do campo Jorge Solla conquistou em 2022. } formula="Fórmula: votos de Solla (2022) ÷ teto do campo (presidencial 2022). Só diagnóstico — não entra na meta." conceptID="captura" /> } /> Share intracampo mostra a fração dos votos do campo, só na disputa de deputado federal, que foi para Jorge Solla nesse ano. } formula="O denominador é o próprio campo nessa disputa — não o teto presidencial de 2022, que é o denominador da Captura." conceptID="share-intracampo" /> } /> Roll-off mede quanto voto em branco/nulo a disputa de deputado federal atrai a mais que a presidencial, no mesmo pleito. } formula="Fórmula: (brancos + nulos, deputado federal 2022) − (brancos + nulos, presidencial 2022)." conceptID="roll-off" /> } />
{!usesMesaEstimate && suggestedGoal <= 0 ? (

Sem votação própria em 2022 aqui, então não há meta sugerida — é praça de expansão: cadastre a estimativa da mesa manualmente se decidir investir.

) : null}
) }