/** * Tipos del organism `val-page-block` — constructor de páginas de plataforma * (ADR-077: `docs/adr/077-constructor-de-paginas-de-plataforma.md`, primera * aplicación real en Eklesee, Bloque 4: * `docs/eklesee/03-roadmap-y-abiertos.md`). * * Espejo 1:1 de `Block`/`Props` en `backend/go/services/page/types.go` + * `backend/go/services/page/blocks_validate.go` — la forma de cada `props` * sale de ahí, no se inventa acá. El catálogo de esta primera pasada son 5 * tipos (`hero`, `text`, `gallery`, `cta`, `faq`); `countdown`/`sponsors`/ * `progress` (catálogo completo del ADR) quedan fuera hasta que Bingo migre. * * El componente es i18n-agnóstico y agnóstico al dominio a propósito (mismo * criterio que `val-faq`/`val-article`): recibe `props` ya resueltos, sin * llamadas HTTP propias ni conocimiento de rutas de ninguna app. La app que * lo consume (Eklesee) resuelve `PageCTAIntent.action` a una ruta real — * este organism solo la emite via `(ctaAction)`. */ /** hero — props: `title` (requerido), `subtitle`/`image`/`align` (opcionales). */ export interface PageHeroBlockProps { title: string; subtitle?: string; /** URL de la imagen de fondo/acompañante. */ image?: string; align?: 'left' | 'center' | 'right'; } /** text — props: `body` (requerido), `heading` (opcional). */ export interface PageTextBlockProps { body: string; heading?: string; } /** gallery — props: `images` (requerido, 1..20 URLs, validado en backend). */ export interface PageGalleryBlockProps { images: string[]; } /** * Intención del CTA, NUNCA una URL (ADR-077: "el link del vendedor puede * regenerarse... sin dejar páginas publicadas apuntando a un token muerto"). * El catálogo de `action` es del backend (`page.CTAAction`, hoy solo * `apply_membership` — ver `services/page/types.go`). Este componente NO * resuelve la intención a una ruta: solo la recibe y la reemite tal cual via * `(ctaAction)` para que la app la resuelva contra su propio routing. */ export interface PageCTAIntent { action: string; } /** cta — props: `label` (requerido), `intent.action` (requerido). */ export interface PageCTABlockProps { label: string; intent: PageCTAIntent; } /** Un ítem de FAQ (pregunta + respuesta en texto plano). */ export interface PageFAQItemProps { question: string; answer: string; } /** faq — props: `items` (requerido, 1..50, validado en backend). */ export interface PageFAQBlockProps { items: PageFAQItemProps[]; } /** Discriminador de bloque — espejo de `page.BlockType` (backend). */ export type PageBlockType = 'hero' | 'text' | 'gallery' | 'cta' | 'faq'; interface PageBlockBase { /** * Orden de renderizado dentro de `Page.Blocks`. El backend lo re-estampa * SIEMPRE a partir de la posición del array al guardar (`SetBlocks` — * regla #7 backend/go/CLAUDE.md: nunca confiar en un `order` que venga del * cliente). Opcional acá: el consumidor típico ya itera el array en el * orden real, este campo es informativo. */ order?: number; } export interface PageHeroBlock extends PageBlockBase { type: 'hero'; props: PageHeroBlockProps; } export interface PageTextBlock extends PageBlockBase { type: 'text'; props: PageTextBlockProps; } export interface PageGalleryBlock extends PageBlockBase { type: 'gallery'; props: PageGalleryBlockProps; } export interface PageCTABlock extends PageBlockBase { type: 'cta'; props: PageCTABlockProps; } export interface PageFAQBlock extends PageBlockBase { type: 'faq'; props: PageFAQBlockProps; } /** Unión discriminada — espejo de `page.Block` (backend, `Props map[string]any` tipado por `Type`). */ export type PageBlock = PageHeroBlock | PageTextBlock | PageGalleryBlock | PageCTABlock | PageFAQBlock; /** * Props de ``. Alias de `PageBlock` con el nombre * `*Metadata` que sigue el resto de la lib (`convención: Props interface * → *Metadata`) — mismo tipo, dos nombres para dos contextos de uso (el * array de la página vs. el `@Input` de un componente puntual). */ export type PageBlockMetadata = PageBlock; export {};