teqo/src/utilities/plazaUpdatePageData.ts
Francisco Ignácio Fontoura Solla cf13c441aa Remodel /campanha around predefined Praças replacing electoral nuclei.
436 seeded Praças (municipality-based; per-TSE-zone in Salvador/Camaçari) become
the operational unit; roles renamed to coordinator/advisor/leader with advisors
scoped to administered Praças. Leadership is one record per person across many
Praças/organizations; vote pledges split leader-declared vs staff-estimated
(estimates never serialized to leaders). Adds organizations, demand workflow
with staff-only cost/receipts, action-plan results with deputy presence, manual
political trend, multi-candidate comparison and the diverging red/white/blue
map mode. Migration 20260721_020109_remodel_plazas deliberately resets the
campaign vertical in production (public site untouched); test suite ported to
the new model (unit 187 / int 306 / e2e 9 green).
2026-07-21 02:28:00 -03:00

120 lines
3.4 KiB
TypeScript

import type { Payload } from 'payload'
import type { PlazaUpdateKind } from '@/lib/schemas/plazaUpdate'
import { plazaUpdateKinds } from '@/lib/schemas/plazaUpdate'
import type { CampaignUser, PlazaUpdate } from '@/payload-types'
import { relationshipId } from '@/utilities/relationship'
export const plazaUpdatesPageSize = 10
export type PlazaUpdateViewModel = {
id: number
kind: PlazaUpdateKind
authorName: string
createdAt: string
worked: string | null
failed: string | null
needs: string | null
body: string | null
activeVolunteers: number | null
newSupports: number | null
}
export type PlazaUpdateFeedState = {
kind?: PlazaUpdateKind
page: number
}
export const parsePlazaUpdateFeedParams = (
searchParams: Record<string, string | string[] | undefined>,
): PlazaUpdateFeedState => {
const first = (value: string | string[] | undefined) => (Array.isArray(value) ? value[0] : value)
const rawKind = first(searchParams.updateKind)
const rawPage = first(searchParams.updatePage)
const page = rawPage && /^[1-9]\d*$/.test(rawPage) ? Number(rawPage) : 1
return {
...(plazaUpdateKinds.includes(rawKind as PlazaUpdateKind)
? { kind: rawKind as PlazaUpdateKind }
: {}),
page,
}
}
export const loadPlazaUpdatesFeed = async (
payload: Payload,
user: CampaignUser,
plazaID: number,
state: PlazaUpdateFeedState,
): Promise<{
updates: PlazaUpdateViewModel[]
totalDocs: number
totalPages: number
page: number
}> => {
const result = await payload.find({
collection: 'plazaUpdate',
where: {
and: [
{ plaza: { equals: plazaID } },
...(state.kind ? [{ kind: { equals: state.kind } }] : []),
],
},
depth: 0,
limit: plazaUpdatesPageSize,
page: state.page,
sort: '-createdAt',
user,
overrideAccess: false,
})
const authorIDs = [
...new Set(
result.docs
.map((update) => relationshipId((update as PlazaUpdate).author))
.filter((id): id is number => id !== null),
),
]
// Intentional admin bypass: resolves author display names for rows the
// actor already passed row-level access on.
const authors = authorIDs.length
? await payload.find({
collection: 'campaignUser',
where: { id: { in: authorIDs } },
depth: 0,
limit: 0,
pagination: false,
select: { name: true },
overrideAccess: true,
})
: { docs: [] }
const authorNameById = new Map(authors.docs.map((author) => [author.id, author.name]))
return {
updates: result.docs.map((update) => {
const doc = update as PlazaUpdate
return {
id: doc.id,
kind: doc.kind as PlazaUpdateKind,
authorName: authorNameById.get(relationshipId(doc.author) ?? -1) ?? 'Usuário',
createdAt: doc.createdAt,
worked: doc.worked ?? null,
failed: doc.failed ?? null,
needs: doc.needs ?? null,
body: doc.body ?? null,
activeVolunteers: doc.activeVolunteers ?? null,
newSupports: doc.newSupports ?? null,
}
}),
totalDocs: result.totalDocs,
totalPages: result.totalPages,
page: result.page ?? state.page,
}
}
export const loadPlazaUpdatesPreview = async (
payload: Payload,
user: CampaignUser,
plazaID: number,
): Promise<PlazaUpdateViewModel[]> =>
(await loadPlazaUpdatesFeed(payload, user, plazaID, { page: 1 })).updates.slice(0, 3)