teqo-backup/src/utilities/campaignMunicipalityScope.ts
Francisco Ignácio Fontoura Solla cf6ae6c47c Pass 2 W2: correcoes de fronteira lib/utilities e client boundary
- helpers puros movidos utilities->lib: slug, phone, wordStartFilter, voteEstimate (9 inversoes lib->utilities zeradas)
- chaves de consent em src/lib/campaignConsentKeys.ts (client-safe); campaignInvite/campaignConsent re-exportam; onda0ConsentTexts referencia as constantes
- 'server-only' em 21 loaders Payload/cache (CLI segue com --conditions=react-server)
- ciclo de tipos supporterListFilters <-> supporterUi quebrado (SupporterListFilterInput estrutural; sqlFilters idem)
- votePledgeData dividido: votePledgeViews (puro, client-safe) + votePledgeData (loaders, server-only) - padrao contract-module
- predicados de role client-safe em src/lib/campaignRoles.ts; access/shared deriva deles (fonte unica); nav.ts para de importar o barrel campaignAccess (casts 'as CampaignUser' eliminados)
2026-07-25 14:38:41 -03:00

81 lines
2.2 KiB
TypeScript

import 'server-only'
import { cache } from 'react'
import type { Payload, Where } from 'payload'
import type { CampaignUser, Municipality } from '@/payload-types'
import { aggregatePledgesByMunicipality } from '@/utilities/votePledgeData'
import { type MunicipalityPledgeAggregate } from '@/utilities/votePledgeViews'
/** Superset of the fields the dashboard, map, and list overview consume. */
export type ScopedMunicipalityDoc = Pick<
Municipality,
| 'id'
| 'name'
| 'slug'
| 'kind'
| 'region'
| 'ibgeCode'
| 'advisors'
| 'priority'
| 'expectedVotes'
>
export type MunicipalityScope = {
municipalities: ScopedMunicipalityDoc[]
pledgeAggregates: Map<number, MunicipalityPledgeAggregate>
}
const loadScope = async (
payload: Payload,
user: CampaignUser,
where: Where,
): Promise<MunicipalityScope> => {
const result = await payload.find({
collection: 'municipality',
depth: 0,
limit: 0,
pagination: false,
select: {
name: true,
slug: true,
kind: true,
region: true,
ibgeCode: true,
advisors: true,
priority: true,
expectedVotes: true,
},
where,
user,
overrideAccess: false,
})
const municipalities = result.docs as ScopedMunicipalityDoc[]
const pledgeAggregates = await aggregatePledgesByMunicipality(
payload,
municipalities.map((municipality) => municipality.id),
)
return { municipalities, pledgeAggregates }
}
/**
* Request-scoped municipality scope (accessible docs + pledge aggregates),
* deduplicated with React `cache()`: the staff dashboard and the map bundle
* both need the same scope on one request and used to load it twice.
*
* `payload` (module singleton) and `user` (from the request-cached
* `getCampaignUser`) are reference-stable within a request; the `where` clause
* participates via its canonical JSON string.
*/
const loadScopeCached = cache((payload: Payload, user: CampaignUser, whereKey: string) =>
loadScope(payload, user, JSON.parse(whereKey) as Where),
)
export const loadMunicipalityScope = (
payload: Payload,
user: CampaignUser,
where: Where,
): Promise<MunicipalityScope> => loadScopeCached(payload, user, JSON.stringify(where))