import 'server-only' import type { Payload } from 'payload' import { isPopulatedRelationship, relationshipId } from '@/lib/relationship' import type { CampaignDemandKind, CampaignDemandStatus } from '@/lib/schemas/campaignDemand' import type { Activity, CampaignDemand, CampaignUser } from '@/payload-types' import { isCampaignStaff } from '@/utilities/campaignAccess' import { buildDemandListHref, buildDemandListWhere, parseDemandListParams, type DemandListState, } from '@/utilities/demand/demandListUrl' import { loadCampaignUserNamesByIds, loadLeadershipContactNamesByIds, loadMunicipalityLabelsByIds, } from '@/utilities/loadNamesByIds' export { buildDemandListHref, parseDemandListParams } export type { DemandListState } const demandPageSize = 25 export type DemandRowViewModel = { id: number title: string slug: string kind: CampaignDemandKind status: CampaignDemandStatus municipalityId: number municipalityName: string municipalitySlug: string requesterName: string | null createdAt: string } const resolveMunicipalityAndRequesterNames = async ( payload: Payload, demands: CampaignDemand[], ): Promise<{ municipalityBy: Map requesterBy: Map }> => { const municipalityIDs = [ ...new Set( demands .map((demand) => relationshipId(demand.municipality)) .filter((id): id is number => id !== null), ), ] const leadershipIDs = [ ...new Set( demands .map((demand) => relationshipId(demand.leadership)) .filter((id): id is number => id !== null), ), ] const [municipalityBy, contactNameByLeadershipId] = await Promise.all([ loadMunicipalityLabelsByIds(payload, municipalityIDs), loadLeadershipContactNamesByIds(payload, leadershipIDs), ]) return { municipalityBy, requesterBy: contactNameByLeadershipId, } } const toDemandRow = ( demand: CampaignDemand, municipalityBy: Map, requesterBy: Map, ): DemandRowViewModel => { const municipality = municipalityBy.get(relationshipId(demand.municipality) ?? -1) const municipalityID = relationshipId(demand.municipality) const leadershipID = relationshipId(demand.leadership) return { id: demand.id, title: demand.title, slug: demand.slug, kind: demand.kind as CampaignDemandKind, status: demand.status as CampaignDemandStatus, municipalityId: municipalityID ?? 0, municipalityName: municipality?.name ?? 'MunicĂ­pio', municipalitySlug: municipality?.slug ?? '', requesterName: leadershipID ? (requesterBy.get(leadershipID) ?? null) : null, createdAt: demand.createdAt, } } export const loadDemandListPageData = async ( payload: Payload, user: CampaignUser, state: DemandListState, ): Promise<{ rows: DemandRowViewModel[] totalDocs: number totalPages: number openCount: number }> => { const [result, openCount] = await Promise.all([ payload.find({ collection: 'campaignDemand', where: buildDemandListWhere(state), depth: 0, limit: demandPageSize, page: state.page, sort: '-createdAt', user, overrideAccess: false, }), payload.count({ collection: 'campaignDemand', where: { status: { in: ['aberta', 'em_analise', 'escalada'] } }, user, overrideAccess: false, }), ]) const { municipalityBy, requesterBy } = await resolveMunicipalityAndRequesterNames( payload, result.docs as CampaignDemand[], ) return { rows: (result.docs as CampaignDemand[]).map((demand) => toDemandRow(demand, municipalityBy, requesterBy), ), totalDocs: result.totalDocs, totalPages: result.totalPages, openCount: openCount.totalDocs, } } export type DemandDetailViewModel = DemandRowViewModel & { description: string | null decisionNote: string | null decidedAt: string | null /** Staff-only (field access strips for leaders). */ cost: number | null receipts: Array<{ id: number; url: string | null; filename: string | null }> statusHistory: Array<{ status: CampaignDemandStatus note: string | null authorName: string | null createdAt: string | null }> canLeaderEdit: boolean activity: { title: string; slug: string } | null } export const loadDemandDetail = async ( payload: Payload, user: CampaignUser, slug: string, ): Promise => { const result = await payload.find({ collection: 'campaignDemand', where: { slug: { equals: slug } }, depth: 1, limit: 1, pagination: false, user, overrideAccess: false, }) const demand = result.docs[0] as CampaignDemand | undefined if (!demand) return null const { municipalityBy, requesterBy } = await resolveMunicipalityAndRequesterNames(payload, [ demand, ]) const row = toDemandRow(demand, municipalityBy, requesterBy) const authorIDs = [ ...new Set( (demand.statusHistory ?? []) .map((entry) => relationshipId(entry.author)) .filter((id): id is number => id !== null), ), ] const authorNameById = await loadCampaignUserNamesByIds(payload, authorIDs) return { ...row, description: demand.description ?? null, decisionNote: demand.decisionNote ?? null, decidedAt: demand.decidedAt ?? null, cost: isCampaignStaff(user) ? (demand.cost ?? null) : null, receipts: isCampaignStaff(user) ? (demand.receipts ?? []).flatMap((receipt) => typeof receipt === 'object' && receipt !== null ? [ { id: receipt.id, url: receipt.url ?? null, filename: receipt.filename ?? null, }, ] : [], ) : [], statusHistory: isCampaignStaff(user) ? (demand.statusHistory ?? []).map((entry) => ({ status: entry.status as CampaignDemandStatus, note: entry.note ?? null, authorName: authorNameById.get(relationshipId(entry.author) ?? -1) ?? null, createdAt: entry.createdAt ?? null, })) : [], canLeaderEdit: user.role === 'leader' && demand.status === 'aberta', activity: isPopulatedRelationship(demand.activity) ? { title: demand.activity.title, slug: demand.activity.slug } : null, } }