mirror of
https://github.com/RooVetGit/Roo-Code.git
synced 2026-09-05 08:10:14 +00:00
feat: add CLI preview landing page with HubSpot form integration
- Create /cli-preview route with noindex robots meta - Add HubSpotForm component with placeholder state for unconfigured forms - Implement UTM attribution parsing and hidden field mapping - Add PostHog analytics events for funnel tracking - Include post-submit success state with Cloud CTA links - Support environment variables: - NEXT_PUBLIC_HUBSPOT_PORTAL_ID - NEXT_PUBLIC_HUBSPOT_FORM_ID_CLI_PREVIEW
This commit is contained in:
parent
e3b90fb182
commit
44f9b0366b
4 changed files with 561 additions and 0 deletions
257
apps/web-roo-code/src/app/cli-preview/cli-preview-content.tsx
Normal file
257
apps/web-roo-code/src/app/cli-preview/cli-preview-content.tsx
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect, useState } from "react"
|
||||
import { useSearchParams, usePathname } from "next/navigation"
|
||||
import { usePostHog } from "posthog-js/react"
|
||||
import Link from "next/link"
|
||||
|
||||
import { HubSpotForm } from "@/components/forms/hubspot-form"
|
||||
import { parseAttributionParams, attributionToHiddenFields } from "@/lib/attribution"
|
||||
import { EXTERNAL_LINKS } from "@/lib/constants"
|
||||
|
||||
export function CliPreviewContent() {
|
||||
const searchParams = useSearchParams()
|
||||
const pathname = usePathname()
|
||||
const posthog = usePostHog()
|
||||
const [isSubmitted, setIsSubmitted] = useState(false)
|
||||
const [hiddenFields, setHiddenFields] = useState<Record<string, string>>({})
|
||||
|
||||
// Get HubSpot config from environment
|
||||
const portalId = process.env.NEXT_PUBLIC_HUBSPOT_PORTAL_ID
|
||||
const formId = process.env.NEXT_PUBLIC_HUBSPOT_FORM_ID_CLI_PREVIEW
|
||||
|
||||
// Parse attribution parameters and set hidden fields
|
||||
useEffect(() => {
|
||||
const attributionParams = parseAttributionParams(searchParams, pathname)
|
||||
const fields = attributionToHiddenFields(attributionParams)
|
||||
setHiddenFields(fields)
|
||||
|
||||
// Capture pageview event with attribution
|
||||
posthog?.capture("cli_preview_viewed", {
|
||||
...attributionParams,
|
||||
page_path: pathname,
|
||||
})
|
||||
}, [searchParams, pathname, posthog])
|
||||
|
||||
// Capture form render events
|
||||
useEffect(() => {
|
||||
if (portalId && formId) {
|
||||
posthog?.capture("cli_preview_form_rendered", {
|
||||
...hiddenFields,
|
||||
})
|
||||
} else {
|
||||
posthog?.capture("cli_preview_form_placeholder_shown", {
|
||||
...hiddenFields,
|
||||
})
|
||||
}
|
||||
}, [portalId, formId, hiddenFields, posthog])
|
||||
|
||||
const handleFormSubmit = () => {
|
||||
setIsSubmitted(true)
|
||||
posthog?.capture("cli_preview_form_submitted", {
|
||||
...hiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
const handleLoginClick = () => {
|
||||
posthog?.capture("cli_preview_nextstep_login_clicked", {
|
||||
...hiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
const handleSignupClick = () => {
|
||||
posthog?.capture("cli_preview_nextstep_signup_clicked", {
|
||||
...hiddenFields,
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gradient-to-b from-white to-gray-50 dark:from-gray-950 dark:to-gray-900">
|
||||
{/* Hero Section */}
|
||||
<section className="mx-auto max-w-7xl px-6 py-12 sm:py-20 lg:px-8">
|
||||
<div className="mx-auto max-w-3xl text-center">
|
||||
<h1 className="text-4xl font-bold tracking-tight text-gray-900 dark:text-white sm:text-6xl">
|
||||
Get an early preview of the Roo Code CLI
|
||||
</h1>
|
||||
<p className="mt-6 text-lg leading-8 text-gray-600 dark:text-gray-300">
|
||||
If you like Roo Code in VS Code, you will love scripting it. Join the early preview to help
|
||||
shape a CLI designed for power users, automation, and teams.
|
||||
</p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Benefits Section */}
|
||||
<section className="mx-auto max-w-7xl px-6 py-12 lg:px-8">
|
||||
<div className="mx-auto max-w-3xl">
|
||||
<ul className="space-y-4">
|
||||
<li className="flex items-start gap-4">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">
|
||||
Run Roo Code workflows from the terminal
|
||||
</h3>
|
||||
<p className="mt-1 text-gray-600 dark:text-gray-400">
|
||||
Execute powerful AI-driven coding tasks directly from your command line
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-4">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">
|
||||
Bring your own model keys and configs
|
||||
</h3>
|
||||
<p className="mt-1 text-gray-600 dark:text-gray-400">
|
||||
Full control over your AI models and configuration settings
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-4">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">
|
||||
Designed for automation and repeatability
|
||||
</h3>
|
||||
<p className="mt-1 text-gray-600 dark:text-gray-400">
|
||||
Perfect for CI/CD pipelines and scripted workflows
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
<li className="flex items-start gap-4">
|
||||
<div className="flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-blue-100 text-blue-600 dark:bg-blue-900/20 dark:text-blue-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-5 w-5"
|
||||
viewBox="0 0 20 20"
|
||||
fill="currentColor">
|
||||
<path
|
||||
fillRule="evenodd"
|
||||
d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z"
|
||||
clipRule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div>
|
||||
<h3 className="font-semibold text-gray-900 dark:text-white">
|
||||
Help us prioritize features that matter
|
||||
</h3>
|
||||
<p className="mt-1 text-gray-600 dark:text-gray-400">
|
||||
Your feedback shapes the future of the Roo Code CLI
|
||||
</p>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
{/* Form Section */}
|
||||
<section className="mx-auto max-w-7xl px-6 py-12 lg:px-8">
|
||||
<div className="mx-auto max-w-2xl">
|
||||
{!isSubmitted ? (
|
||||
<>
|
||||
<div className="mb-8 text-center">
|
||||
<h2 className="text-3xl font-bold text-gray-900 dark:text-white">
|
||||
Join the Early Preview
|
||||
</h2>
|
||||
<p className="mt-4 text-gray-600 dark:text-gray-400">
|
||||
We're rolling access out in batches. Tell us what you want to do with the CLI
|
||||
and we'll follow up with next steps.
|
||||
</p>
|
||||
</div>
|
||||
<HubSpotForm
|
||||
portalId={portalId}
|
||||
formId={formId}
|
||||
onSubmitted={handleFormSubmit}
|
||||
hiddenFields={hiddenFields}
|
||||
className="rounded-lg bg-white p-8 shadow-lg dark:bg-gray-800"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div className="rounded-lg bg-white p-12 text-center shadow-lg dark:bg-gray-800">
|
||||
<div className="mb-6">
|
||||
<div className="mx-auto flex h-16 w-16 items-center justify-center rounded-full bg-green-100 text-green-600 dark:bg-green-900/20 dark:text-green-400">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-8 w-8"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor">
|
||||
<path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
strokeWidth={2}
|
||||
d="M5 13l4 4L19 7"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
<h3 className="mb-4 text-2xl font-bold text-gray-900 dark:text-white">
|
||||
Thank You for Your Interest!
|
||||
</h3>
|
||||
<p className="mb-8 text-gray-600 dark:text-gray-400">
|
||||
We've received your request. Our team will review your submission and reach out
|
||||
with next steps shortly.
|
||||
</p>
|
||||
<div className="mb-6 border-t border-gray-200 pt-6 dark:border-gray-700">
|
||||
<p className="mb-4 text-sm font-medium text-gray-900 dark:text-white">
|
||||
While you wait, jump into Roo Code Cloud to try Cloud Agents and stay close to new
|
||||
releases.
|
||||
</p>
|
||||
<div className="flex flex-col items-center justify-center gap-4 sm:flex-row">
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.CLOUD_APP_LOGIN}
|
||||
onClick={handleLoginClick}
|
||||
className="inline-flex w-full items-center justify-center rounded-lg border border-gray-300 bg-white px-6 py-3 text-sm font-semibold text-gray-900 shadow-sm transition-colors hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-800 dark:text-white dark:hover:bg-gray-700 sm:w-auto">
|
||||
Log in to Cloud
|
||||
</Link>
|
||||
<Link
|
||||
href={EXTERNAL_LINKS.CLOUD_APP_SIGNUP}
|
||||
onClick={handleSignupClick}
|
||||
className="inline-flex w-full items-center justify-center rounded-lg bg-blue-600 px-6 py-3 text-sm font-semibold text-white shadow-sm transition-colors hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 sm:w-auto">
|
||||
Sign up for Cloud
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
49
apps/web-roo-code/src/app/cli-preview/page.tsx
Normal file
49
apps/web-roo-code/src/app/cli-preview/page.tsx
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import type { Metadata } from "next"
|
||||
|
||||
import { SEO } from "@/lib/seo"
|
||||
import { ogImageUrl } from "@/lib/og"
|
||||
import { CliPreviewContent } from "./cli-preview-content"
|
||||
|
||||
const TITLE = "CLI Early Preview"
|
||||
const DESCRIPTION =
|
||||
"If you like Roo Code in VS Code, you will love scripting it. Join the early preview to help shape a CLI designed for power users, automation, and teams."
|
||||
const OG_DESCRIPTION = "Early preview of the Roo Code CLI"
|
||||
const PATH = "/cli-preview"
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
robots: {
|
||||
index: false,
|
||||
follow: false,
|
||||
},
|
||||
alternates: {
|
||||
canonical: `${SEO.url}${PATH}`,
|
||||
},
|
||||
openGraph: {
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
url: `${SEO.url}${PATH}`,
|
||||
siteName: SEO.name,
|
||||
images: [
|
||||
{
|
||||
url: ogImageUrl(TITLE, OG_DESCRIPTION),
|
||||
width: 1200,
|
||||
height: 630,
|
||||
alt: TITLE,
|
||||
},
|
||||
],
|
||||
locale: SEO.locale,
|
||||
type: "website",
|
||||
},
|
||||
twitter: {
|
||||
card: SEO.twitterCard,
|
||||
title: TITLE,
|
||||
description: DESCRIPTION,
|
||||
images: [ogImageUrl(TITLE, OG_DESCRIPTION)],
|
||||
},
|
||||
}
|
||||
|
||||
export default function CliPreviewPage() {
|
||||
return <CliPreviewContent />
|
||||
}
|
||||
173
apps/web-roo-code/src/components/forms/hubspot-form.tsx
Normal file
173
apps/web-roo-code/src/components/forms/hubspot-form.tsx
Normal file
|
|
@ -0,0 +1,173 @@
|
|||
"use client"
|
||||
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
declare global {
|
||||
interface Window {
|
||||
hbspt?: {
|
||||
forms: {
|
||||
create: (options: {
|
||||
region?: string
|
||||
portalId: string
|
||||
formId: string
|
||||
target: string
|
||||
onFormSubmitted?: () => void
|
||||
}) => void
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export interface HubSpotFormProps {
|
||||
portalId?: string
|
||||
formId?: string
|
||||
region?: string
|
||||
className?: string
|
||||
onSubmitted?: () => void
|
||||
hiddenFields?: Record<string, string>
|
||||
}
|
||||
|
||||
export function HubSpotForm({
|
||||
portalId,
|
||||
formId,
|
||||
region = "na1",
|
||||
className = "",
|
||||
onSubmitted,
|
||||
hiddenFields = {},
|
||||
}: HubSpotFormProps) {
|
||||
const formContainerRef = useRef<HTMLDivElement>(null)
|
||||
const [isScriptLoaded, setIsScriptLoaded] = useState(false)
|
||||
const [hasError, setHasError] = useState(false)
|
||||
const formCreatedRef = useRef(false)
|
||||
|
||||
// Check if HubSpot IDs are configured
|
||||
const isConfigured = Boolean(portalId && formId)
|
||||
|
||||
// Load HubSpot script
|
||||
useEffect(() => {
|
||||
if (!isConfigured) return
|
||||
|
||||
// Check if script already exists
|
||||
const existingScript = document.querySelector('script[src*="js.hsforms.net"]')
|
||||
if (existingScript) {
|
||||
setIsScriptLoaded(true)
|
||||
return
|
||||
}
|
||||
|
||||
// Create and load script
|
||||
const script = document.createElement("script")
|
||||
script.src = "https://js.hsforms.net/forms/v2.js"
|
||||
script.async = true
|
||||
script.defer = true
|
||||
|
||||
script.onload = () => {
|
||||
setIsScriptLoaded(true)
|
||||
}
|
||||
|
||||
script.onerror = () => {
|
||||
console.error("Failed to load HubSpot form script")
|
||||
setHasError(true)
|
||||
}
|
||||
|
||||
document.body.appendChild(script)
|
||||
|
||||
return () => {
|
||||
// Don't remove the script on unmount as it might be used by other forms
|
||||
}
|
||||
}, [isConfigured])
|
||||
|
||||
// Create form once script is loaded
|
||||
useEffect(() => {
|
||||
if (!isConfigured || !isScriptLoaded || !window.hbspt || !formContainerRef.current || formCreatedRef.current) {
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
// Mark as created before calling create to prevent double-creation
|
||||
formCreatedRef.current = true
|
||||
|
||||
window.hbspt.forms.create({
|
||||
region,
|
||||
portalId: portalId!,
|
||||
formId: formId!,
|
||||
target: `#${formContainerRef.current.id}`,
|
||||
onFormSubmitted: () => {
|
||||
if (onSubmitted) {
|
||||
onSubmitted()
|
||||
}
|
||||
},
|
||||
})
|
||||
|
||||
// Set hidden fields after form is created
|
||||
// HubSpot forms need a moment to render
|
||||
setTimeout(() => {
|
||||
if (Object.keys(hiddenFields).length > 0) {
|
||||
Object.entries(hiddenFields).forEach(([name, value]) => {
|
||||
const input = document.querySelector<HTMLInputElement>(`input[name="${name}"]`)
|
||||
if (input) {
|
||||
input.value = value
|
||||
}
|
||||
})
|
||||
}
|
||||
}, 500)
|
||||
} catch (error) {
|
||||
console.error("Error creating HubSpot form:", error)
|
||||
setHasError(true)
|
||||
}
|
||||
}, [isConfigured, isScriptLoaded, portalId, formId, region, onSubmitted, hiddenFields])
|
||||
|
||||
// Show placeholder if not configured
|
||||
if (!isConfigured) {
|
||||
return (
|
||||
<div className={`rounded-lg border border-dashed border-gray-300 bg-gray-50 p-8 text-center ${className}`}>
|
||||
<div className="mx-auto max-w-md space-y-4">
|
||||
<div className="text-4xl">📝</div>
|
||||
<h3 className="text-lg font-semibold text-gray-900">Form Configuration Pending</h3>
|
||||
<p className="text-sm text-gray-600">
|
||||
The HubSpot form for CLI preview is not yet configured. Please set the following environment
|
||||
variables:
|
||||
</p>
|
||||
<div className="rounded bg-white p-4 text-left font-mono text-xs text-gray-700">
|
||||
<div>NEXT_PUBLIC_HUBSPOT_PORTAL_ID</div>
|
||||
<div>NEXT_PUBLIC_HUBSPOT_FORM_ID_CLI_PREVIEW</div>
|
||||
</div>
|
||||
{process.env.NODE_ENV === "development" && (
|
||||
<p className="text-xs text-gray-500">
|
||||
This message is only visible in development. In production, consider showing a contact email
|
||||
or alternative CTA.
|
||||
</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Show error state if script failed to load
|
||||
if (hasError) {
|
||||
return (
|
||||
<div className={`rounded-lg border border-red-200 bg-red-50 p-8 text-center ${className}`}>
|
||||
<div className="mx-auto max-w-md space-y-4">
|
||||
<div className="text-4xl">⚠️</div>
|
||||
<h3 className="text-lg font-semibold text-red-900">Unable to Load Form</h3>
|
||||
<p className="text-sm text-red-700">
|
||||
We're having trouble loading the form. This might be due to an ad blocker or connectivity
|
||||
issue.
|
||||
</p>
|
||||
<p className="text-sm text-gray-600">
|
||||
Please reach out to us directly at{" "}
|
||||
<a href="mailto:support@roocode.com" className="font-medium text-blue-600 hover:underline">
|
||||
support@roocode.com
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
// Render form container
|
||||
return (
|
||||
<div className={className}>
|
||||
<div ref={formContainerRef} id="hubspot-form-cli-preview" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
82
apps/web-roo-code/src/lib/attribution.ts
Normal file
82
apps/web-roo-code/src/lib/attribution.ts
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
/**
|
||||
* Parse attribution parameters from URL search params
|
||||
*/
|
||||
export interface AttributionParams {
|
||||
utm_source?: string
|
||||
utm_medium?: string
|
||||
utm_campaign?: string
|
||||
utm_content?: string
|
||||
utm_term?: string
|
||||
ref?: string
|
||||
landing_path?: string
|
||||
landing_url?: string
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract attribution parameters from URLSearchParams
|
||||
*/
|
||||
export function parseAttributionParams(searchParams: URLSearchParams, pathname?: string): AttributionParams {
|
||||
const params: AttributionParams = {}
|
||||
|
||||
// Extract UTM parameters
|
||||
const utmSource = searchParams.get("utm_source")
|
||||
const utmMedium = searchParams.get("utm_medium")
|
||||
const utmCampaign = searchParams.get("utm_campaign")
|
||||
const utmContent = searchParams.get("utm_content")
|
||||
const utmTerm = searchParams.get("utm_term")
|
||||
const ref = searchParams.get("ref")
|
||||
|
||||
if (utmSource) params.utm_source = utmSource
|
||||
if (utmMedium) params.utm_medium = utmMedium
|
||||
if (utmCampaign) params.utm_campaign = utmCampaign
|
||||
if (utmContent) params.utm_content = utmContent
|
||||
if (utmTerm) params.utm_term = utmTerm
|
||||
if (ref) params.ref = ref
|
||||
|
||||
// Add landing path and URL if available
|
||||
if (typeof window !== "undefined") {
|
||||
if (pathname) {
|
||||
params.landing_path = pathname
|
||||
}
|
||||
params.landing_url = window.location.href
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
/**
|
||||
* Get referrer from document or explicit ref parameter
|
||||
*/
|
||||
export function getReferrer(searchParams: URLSearchParams): string | undefined {
|
||||
// First check for explicit ref parameter
|
||||
const explicitRef = searchParams.get("ref")
|
||||
if (explicitRef) {
|
||||
return explicitRef
|
||||
}
|
||||
|
||||
// Fall back to document referrer
|
||||
if (typeof document !== "undefined" && document.referrer) {
|
||||
return document.referrer
|
||||
}
|
||||
|
||||
return undefined
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert attribution params to HubSpot hidden fields format
|
||||
*/
|
||||
export function attributionToHiddenFields(params: AttributionParams): Record<string, string> {
|
||||
const fields: Record<string, string> = {}
|
||||
|
||||
// Only include fields that have values
|
||||
if (params.utm_source) fields.utm_source = params.utm_source
|
||||
if (params.utm_medium) fields.utm_medium = params.utm_medium
|
||||
if (params.utm_campaign) fields.utm_campaign = params.utm_campaign
|
||||
if (params.utm_content) fields.utm_content = params.utm_content
|
||||
if (params.utm_term) fields.utm_term = params.utm_term
|
||||
if (params.ref) fields.ref = params.ref
|
||||
if (params.landing_path) fields.landing_path = params.landing_path
|
||||
if (params.landing_url) fields.landing_url = params.landing_url
|
||||
|
||||
return fields
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue