From eedf67013c0305897c3aa01507e6f1204d6bd600 Mon Sep 17 00:00:00 2001 From: Waleed Latif Date: Wed, 11 Feb 2026 11:54:50 -0800 Subject: [PATCH] feat(creators): added referrers, code redemption, campaign tracking, etc --- .../(auth)/components/utm-cookie-setter.tsx | 33 + apps/sim/app/(auth)/layout.tsx | 6 +- apps/sim/app/api/attribution/route.ts | 201 + .../sim/app/api/referral-code/redeem/route.ts | 157 + .../v1/admin/referral-campaigns/[id]/route.ts | 117 + .../api/v1/admin/referral-campaigns/route.ts | 108 + .../subscription/components/index.ts | 1 + .../components/referral-code/index.ts | 1 + .../referral-code/referral-code.tsx | 101 + .../components/subscription/subscription.tsx | 6 + apps/sim/app/workspace/page.tsx | 2 + apps/sim/hooks/use-referral-attribution.ts | 40 + apps/sim/lib/billing/credits/bonus.ts | 64 + packages/db/constants.ts | 5 + .../db/migrations/0154_many_vindicator.sql | 42 + .../db/migrations/meta/0154_snapshot.json | 10972 ++++++++++++++++ packages/db/migrations/meta/_journal.json | 7 + packages/db/schema.ts | 56 + 18 files changed, 11918 insertions(+), 1 deletion(-) create mode 100644 apps/sim/app/(auth)/components/utm-cookie-setter.tsx create mode 100644 apps/sim/app/api/attribution/route.ts create mode 100644 apps/sim/app/api/referral-code/redeem/route.ts create mode 100644 apps/sim/app/api/v1/admin/referral-campaigns/[id]/route.ts create mode 100644 apps/sim/app/api/v1/admin/referral-campaigns/route.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/referral-code/index.ts create mode 100644 apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/settings-modal/components/subscription/components/referral-code/referral-code.tsx create mode 100644 apps/sim/hooks/use-referral-attribution.ts create mode 100644 apps/sim/lib/billing/credits/bonus.ts create mode 100644 packages/db/migrations/0154_many_vindicator.sql create mode 100644 packages/db/migrations/meta/0154_snapshot.json diff --git a/apps/sim/app/(auth)/components/utm-cookie-setter.tsx b/apps/sim/app/(auth)/components/utm-cookie-setter.tsx new file mode 100644 index 000000000..efd8f9ae5 --- /dev/null +++ b/apps/sim/app/(auth)/components/utm-cookie-setter.tsx @@ -0,0 +1,33 @@ +'use client' + +import { useEffect } from 'react' +import { useSearchParams } from 'next/navigation' + +const UTM_KEYS = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_content'] as const +const COOKIE_NAME = 'sim_utm' +const COOKIE_MAX_AGE = 3600 // 1 hour + +export function UtmCookieSetter() { + const searchParams = useSearchParams() + + useEffect(() => { + const hasUtm = UTM_KEYS.some((key) => searchParams.get(key)) + if (!hasUtm) return + + const utmData: Record = {} + for (const key of UTM_KEYS) { + const value = searchParams.get(key) + if (value) { + utmData[key] = value + } + } + + utmData.referrer_url = document.referrer || '' + utmData.landing_page = window.location.pathname + utmData.created_at = Date.now().toString() + + document.cookie = `${COOKIE_NAME}=${encodeURIComponent(JSON.stringify(utmData))}; path=/; max-age=${COOKIE_MAX_AGE}; SameSite=Lax` + }, [searchParams]) + + return null +} diff --git a/apps/sim/app/(auth)/layout.tsx b/apps/sim/app/(auth)/layout.tsx index aaa76e0bf..a1d1b9948 100644 --- a/apps/sim/app/(auth)/layout.tsx +++ b/apps/sim/app/(auth)/layout.tsx @@ -1,7 +1,8 @@ 'use client' -import { useEffect } from 'react' +import { Suspense, useEffect } from 'react' import AuthBackground from '@/app/(auth)/components/auth-background' +import { UtmCookieSetter } from '@/app/(auth)/components/utm-cookie-setter' import Nav from '@/app/(landing)/components/nav/nav' // Helper to detect if a color is dark @@ -28,6 +29,9 @@ export default function AuthLayout({ children }: { children: React.ReactNode }) }, []) return ( + + +
{/* Header - Nav handles all conditional logic */}