teqo-backup/src/lib/shareLinkAnnouncement.ts
Francisco Solla b8051bb7e0 feat(S29): link com destino trocável e página de anúncio da atividade
- shareLink ganha modo (direct|announcement), pool de destinos com "Destino no
  ar" por linha e dados do evento; destination escalar sai via migration com
  backfill lossless (legado vira direct com destino no ar)
- página de anúncio pública portada classe-a-classe, header campaign-site
  extraído (CampaignPageHeader) e ativação/troca sem reload por poll da rota
  /api/share-link/<slug>/live (fail-closed, no-store)
- agenda Google (action=TEMPLATE) + .ics por rota sob o slug; compartilhar o
  link curto (kind event); S19 intacto e 404 uniforme preservado

Related #1268
2026-09-23 11:48:12 -03:00

61 lines
1.6 KiB
TypeScript

import { formatBahiaEventDateLabel } from '@/lib/campaignTime'
/**
* S29 — pure view model of the public announcement page. The server read
* resolves the OG image and hands the client a serializable shape; the labels
* (Bahia date, trimmed location) are computed here, client-safe, with no
* Payload import.
*/
type ShareLinkAnnouncementMedia = {
alt?: string | null
}
export type ShareLinkAnnouncementSource = {
slug: string
title: string
description: string
image?: ShareLinkAnnouncementMedia | number | null
startsAt?: string | null
endsAt?: string | null
location?: string | null
}
export type ShareLinkAnnouncementView = {
slug: string
title: string
description: string
imageUrl: string | null
imageAlt: string
eventLabel: string | null
location: string | null
startsAt: string | null
endsAt: string | null
}
const isMedia = (
value: ShareLinkAnnouncementSource['image'],
): value is ShareLinkAnnouncementMedia => typeof value === 'object' && value !== null
export const buildShareLinkAnnouncementView = ({
link,
imageUrl,
}: {
link: ShareLinkAnnouncementSource
imageUrl: string | null
}): ShareLinkAnnouncementView => {
const mediaAlt = isMedia(link.image) ? link.image.alt?.trim() : null
const location = link.location?.trim()
return {
slug: link.slug,
title: link.title,
description: link.description,
imageUrl,
imageAlt: mediaAlt || link.title,
eventLabel: link.startsAt ? formatBahiaEventDateLabel(link.startsAt) || null : null,
location: location || null,
startsAt: link.startsAt ?? null,
endsAt: link.endsAt ?? null,
}
}