diff --git a/src/components/XPixel.tsx b/src/components/XPixel.tsx index 9391f5f..f024755 100644 --- a/src/components/XPixel.tsx +++ b/src/components/XPixel.tsx @@ -18,6 +18,19 @@ import { Suspense, useEffect, useRef } from "react"; const X_PIXEL_ID = "re2t6"; +/* Conversion events configured in the X Ads UI, keyed by what they mean here. + Reported with twq('event', , {...}); the call queues if uwt.js + hasn't loaded yet, and is a no-op in dev where the tag never loads. */ +const X_EVENTS = { + pledgedToVote: "tw-re2t6-rekr4", +} as const; + +/** Report an X conversion. `email` is optional and used by X for identity + matching — uwt.js hashes it before it leaves the browser. */ +export function trackXEvent(event: keyof typeof X_EVENTS, email?: string) { + window.twq?.("event", X_EVENTS[event], { email_address: email ?? null }); +} + declare global { interface Window { twq?: (...args: unknown[]) => void; diff --git a/src/components/elections/PledgeButton.tsx b/src/components/elections/PledgeButton.tsx index 0be9d90..cc944c6 100644 --- a/src/components/elections/PledgeButton.tsx +++ b/src/components/elections/PledgeButton.tsx @@ -8,6 +8,7 @@ import { Button } from "@/components/ui/button"; import { hubspotPageContext } from "@/lib/hubspot-context"; import { pledgeSharePath } from "@/lib/elections/pledge-share"; import { DEFAULT_ELECTION_SLUG, getElection } from "@/lib/elections/registry"; +import { trackXEvent } from "@/components/XPixel"; /* "Pledge to vote" CTA — opens the same modal treatment as the navbar Subscribe button. Submitting records the pledge (via /api/elections/pledge @@ -108,6 +109,7 @@ export function PledgeButton({ } posthog.capture("pledged_to_vote", { source, election: config.slug }); + trackXEvent("pledgedToVote", email); // keep the button disabled while we navigate to the shared page; // prefer the server's record (canonical name + unguessable token) router.push(pledgeSharePath(config, data.name || name, data.shareToken));