mirror of
https://github.com/fsolla/teqo.git
synced 2026-11-13 15:13:07 +00:00
- committed artifact src/lib/electionAggregates/bahia-federal-baseline.json (Solla nominal + federal T1 valid votes per catalog municipality × TSE year, Salvador split by zone) generated by pnpm build:election-aggregates from the locally seeded TSE data; typed loader src/lib/bahiaElectionAggregates.ts; snapshot spec pins artifact ↔ catalog ↔ baseline ticket - map bundle: historical years now read the artifact (was 6 statewide city×zone scans per dashboard visit); compare mode keeps only the OTHER candidate on the DB path (Solla side from the artifact) - loadMunicipalityScope (React cache()): one municipality find + pledge aggregate per request shared by dashboard KPIs, map bundle, and the municípios list overview (was 2-3 duplicate loads) - unstable_cache + election-tse tag on the immutable elections-tab loaders (baseline, candidate comparison, candidate options); tag added to the /api/revalidate allowlist — runbook: re-seed then bust the tag - municipalityMapContract.ts: client-safe map vocabulary/bundle types so the client panel no longer imports the server data module (build had pulled @payload-config into the client graph); municipalityMapData is server-only and re-exports the contract; PLAZA_MAP_* renamed to MUNICIPALITY_MAP_* - petition generateStaticParams enumerates bare ids (getPetitionIds)
102 lines
3.4 KiB
TypeScript
102 lines
3.4 KiB
TypeScript
import Link from 'next/link'
|
|
|
|
import type { BahiaMapFeatureInfo } from '@/components/campaign/BahiaMap'
|
|
import { Button } from '@/components/ui/button'
|
|
import { formatElectionNumber } from '@/lib/electionInsights'
|
|
import type { MunicipalityMapNavigation } from '@/utilities/municipalityMapNavigation'
|
|
import type { MunicipalityMapScaleMode } from '@/utilities/municipalityMapContract'
|
|
|
|
type MapFeatureReadoutProps = {
|
|
feature: BahiaMapFeatureInfo | null
|
|
metricValue: number | undefined
|
|
rawMetricValue: number | undefined
|
|
metricLabel: string
|
|
scaleMode: MunicipalityMapScaleMode
|
|
comparisonActive: boolean
|
|
navigation: MunicipalityMapNavigation | null
|
|
}
|
|
|
|
const percentFormatter = new Intl.NumberFormat('pt-BR', {
|
|
style: 'percent',
|
|
maximumFractionDigits: 1,
|
|
})
|
|
|
|
const formatMetricValue = (
|
|
value: number | undefined,
|
|
scaleMode: MunicipalityMapScaleMode,
|
|
comparisonActive: boolean,
|
|
): string => {
|
|
if (value === undefined) return 'Sem dados'
|
|
if (comparisonActive) {
|
|
const prefix = value > 0 ? '+' : ''
|
|
return `${prefix}${formatElectionNumber(value)}`
|
|
}
|
|
if (scaleMode === 'percentValid') return percentFormatter.format(value)
|
|
return formatElectionNumber(value)
|
|
}
|
|
|
|
export const MapFeatureReadout = ({
|
|
feature,
|
|
metricValue,
|
|
rawMetricValue,
|
|
metricLabel,
|
|
scaleMode,
|
|
comparisonActive,
|
|
navigation,
|
|
}: MapFeatureReadoutProps) => {
|
|
if (!feature) {
|
|
return (
|
|
<p className="min-h-11 content-center text-sm text-muted-foreground" aria-live="polite">
|
|
Passe o cursor ou toque em um município para ver os votos.
|
|
</p>
|
|
)
|
|
}
|
|
|
|
const formattedValue = formatMetricValue(metricValue, scaleMode, comparisonActive)
|
|
const showPercentSecondary =
|
|
!comparisonActive &&
|
|
scaleMode === 'percentValid' &&
|
|
metricValue !== undefined &&
|
|
rawMetricValue !== undefined &&
|
|
rawMetricValue > 0
|
|
|
|
return (
|
|
<div
|
|
className="flex flex-col gap-2 rounded-lg border bg-muted/30 px-3 py-2 sm:flex-row sm:items-center sm:justify-between"
|
|
aria-live="polite"
|
|
>
|
|
<div className="flex min-w-0 flex-col gap-0.5">
|
|
<p className="truncate text-sm font-medium">{feature.name}</p>
|
|
<p className="text-sm text-muted-foreground">
|
|
<span className="sr-only">{metricLabel}: </span>
|
|
<span className="tabular-nums">{formattedValue}</span>
|
|
{!comparisonActive && metricValue !== undefined && scaleMode === 'absolute' ? (
|
|
<span className="text-muted-foreground"> {metricLabel}</span>
|
|
) : null}
|
|
{!comparisonActive && metricValue !== undefined && scaleMode === 'percentValid' ? (
|
|
<span className="text-muted-foreground"> dos válidos</span>
|
|
) : null}
|
|
{comparisonActive && metricValue !== undefined ? (
|
|
<span className="text-muted-foreground"> (diferença)</span>
|
|
) : null}
|
|
</p>
|
|
{showPercentSecondary ? (
|
|
<p className="text-xs text-muted-foreground tabular-nums">
|
|
{formatElectionNumber(rawMetricValue)} votos
|
|
</p>
|
|
) : null}
|
|
{navigation?.kind === 'zones' ? (
|
|
<p className="text-xs text-muted-foreground">
|
|
Toque de novo no mapa ou role até as Praças por zona abaixo.
|
|
</p>
|
|
) : null}
|
|
</div>
|
|
|
|
{navigation?.kind === 'navigate' ? (
|
|
<Button asChild className="min-h-11 shrink-0">
|
|
<Link href={`/campanha/municipios/${navigation.slug}`}>Abrir Praça</Link>
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
)
|
|
}
|