import { Beat, PrimaryCTA, SecondaryCTA } from '../../core'; import { GlobalStore } from '../../core/stores/GlobalStore'; import { AnalyticsEventConfigEntry } from '../analytics/analyticsEventsConfig'; import { BeatActions } from '../beatActions'; import { CtaHandlerName } from './ctaHandlers'; export type CtaSlot = 'primary' | 'secondary'; /** Handler inputs: control values at the top level, the write set under `fields`. */ export type CtaParams = Record; /** Read-only view for state predicates evaluated during render. */ export type CtaStateContext = Pick; export interface CtaState { disabled: boolean; } /** Everything a step guard or handler can read while running. */ export interface CtaContext { beat: Beat; cta: PrimaryCTA | SecondaryCTA; slot: CtaSlot; stores: GlobalStore; hostActions: BeatActions; /** Free-form data declared on the step (or passed by an in-drawer caller). */ params: Record; } /** Runner-owned after-effects. `refreshBeat`: tell the host the beat is stale (it refetches). */ export type CtaEffect = 'refreshBeat'; /** What a CTA does when its `action_key` names this step. The backend picks the key per state. */ export interface CtaStep { /** Handler to await first (typed: a misspelt name fails typecheck). */ run?: CtaHandlerName; /** Forwarded to the handler as ctx.params; may compute from beat/stores at press time. */ params?: CtaParams | ((ctx: CtaStateContext) => CtaParams); /** Effects executed after `run` resolves successfully. */ onSuccess?: CtaEffect[]; /** Fired after `run` succeeds. No ctaConfig entry uses it yet. */ analytics?: AnalyticsEventConfigEntry; /** * After `run`: `default` = drawer if the CTA has a block, else nothing; `drawer` forces the * drawer; `none` stops. Omitted = `none` with `run`, `default` without. */ next?: 'default' | 'drawer' | 'none'; /** Skip the first-click `markCtaClicked` host effect. */ skipCtaClicked?: boolean; /** * Disable while true. Only for client-only state (a pick not yet made, a store loading); * data-derived rules belong to the backend, which omits the label instead. */ disabledWhen?: (ctx: CtaStateContext) => boolean; } /** Context for a handler run directly from a drawer (no card CTA / slot involved). */ export type CtaHandlerContext = Omit; export interface RunHandlerOptions { /** Runner-owned effects executed after the handler resolves. */ onSuccess?: CtaEffect[]; /** Catalogue entry (e.g. ANALYTICS_EVENTS_CONFIG.X) fired after the handler resolves (drawer Saves). */ analytics?: AnalyticsEventConfigEntry; } /** A named side effect. Must throw (or reject) on failure so the runner can report it. */ export type CtaHandler = (ctx: CtaContext) => Promise | void;