diff --git a/apps/web/app/api/partner-profile/programs/route.ts b/apps/web/app/api/partner-profile/programs/route.ts index 9054364c819..a4bae0c6139 100644 --- a/apps/web/app/api/partner-profile/programs/route.ts +++ b/apps/web/app/api/partner-profile/programs/route.ts @@ -14,8 +14,51 @@ export const GET = withPartnerProfile(async ({ partner, searchParams }) => { program: searchParams.includeRewardsDiscounts ? { include: { - rewards: true, - discounts: true, + rewards: { + where: { + OR: [ + // program-wide rewards + { + partners: { + none: {}, + }, + }, + + // partner-specific rewards + { + partners: { + some: { + programEnrollment: { + partnerId: partner.id, + }, + }, + }, + }, + ], + }, + }, + + discounts: { + where: { + OR: [ + // program-wide discounts + { + programEnrollments: { + none: {}, + }, + }, + + // partner-specific discounts + { + programEnrollments: { + some: { + partnerId: partner.id, + }, + }, + }, + ], + }, + }, }, } : true, diff --git a/apps/web/app/api/track/click/route.ts b/apps/web/app/api/track/click/route.ts index aab53cc9177..3c20131f0b4 100644 --- a/apps/web/app/api/track/click/route.ts +++ b/apps/web/app/api/track/click/route.ts @@ -120,7 +120,7 @@ export const POST = withAxiom(async (req: AxiomRequest) => { workspaceId: cachedLink.projectId, skipRatelimit: true, ...(referrer && { referrer }), - trackConversion: cachedLink.trackConversion, + shouldPassClickId: true, }); } })(), diff --git a/apps/web/app/api/track/visit/route.ts b/apps/web/app/api/track/visit/route.ts index 84fbcd35ed5..133e44187a3 100644 --- a/apps/web/app/api/track/visit/route.ts +++ b/apps/web/app/api/track/visit/route.ts @@ -95,7 +95,6 @@ export const POST = withAxiom(async (req: AxiomRequest) => { workspaceId: cachedLink.projectId, skipRatelimit: true, ...(referrer && { referrer }), - trackConversion: cachedLink.trackConversion, }); } })(), diff --git a/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx b/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx index 490855d7d9b..c952e61c7be 100644 --- a/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx +++ b/apps/web/app/partners.dub.co/(apply)/[programSlug]/apply/form.tsx @@ -133,7 +133,6 @@ export function ProgramApplicationForm({ diff --git a/apps/web/lib/middleware/link.ts b/apps/web/lib/middleware/link.ts index 6e1b4ffbf4a..d31b999e754 100644 --- a/apps/web/lib/middleware/link.ts +++ b/apps/web/lib/middleware/link.ts @@ -167,6 +167,11 @@ export default async function LinkMiddleware( const url = testUrl || cachedLink.url; + // we only pass the clickId if: + // - trackConversion is enabled + // - it's a partner link + const shouldPassClickId = trackConversion || isPartnerLink; + // by default, we only index default dub domain links (e.g. dub.sh) // everything else is not indexed by default, unless the user has explicitly set it to be indexed const shouldIndex = isDubDomain(domain) || doIndex === true; @@ -244,8 +249,8 @@ export default async function LinkMiddleware( const cookieStore = cookies(); let clickId = cookieStore.get(dubIdCookieName)?.value; if (!clickId) { - // if trackConversion is enabled, check if clickId is cached in Redis - if (trackConversion) { + // if we need to pass the clickId, check if clickId is cached in Redis + if (shouldPassClickId) { const ip = process.env.VERCEL === "1" ? ipAddress(req) : LOCALHOST_IP; clickId = (await clickCache.get({ domain, key, ip })) || undefined; @@ -276,7 +281,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -297,12 +302,6 @@ export default async function LinkMiddleware( const { country } = process.env.VERCEL === "1" && req.geo ? req.geo : LOCALHOST_GEO_DATA; - // we only pass the clickId if: - // - trackConversion is enabled - // - not a partner link (TODO: add this later) !isPartnerLink - // - there is a clickId - const shouldPassClickId = trackConversion && clickId; - // rewrite to proxy page (/proxy/[domain]/[key]) if it's a bot and proxy is enabled if (isBot && proxy) { return createResponseWithCookies( @@ -330,7 +329,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -368,7 +367,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -408,7 +407,7 @@ export default async function LinkMiddleware( url: ios, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -442,7 +441,7 @@ export default async function LinkMiddleware( url: android, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -476,7 +475,7 @@ export default async function LinkMiddleware( url: geo[country], webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -510,7 +509,7 @@ export default async function LinkMiddleware( url, webhookIds, workspaceId, - trackConversion, + shouldPassClickId, }), ); @@ -521,7 +520,7 @@ export default async function LinkMiddleware( headers: new Headers({ destination: getFinalUrl(url, { req, - clickId: trackConversion ? clickId : undefined, + clickId: shouldPassClickId ? clickId : undefined, }), }), }, diff --git a/apps/web/lib/tinybird/record-click.ts b/apps/web/lib/tinybird/record-click.ts index 9c3de151bd6..109be2de1b2 100644 --- a/apps/web/lib/tinybird/record-click.ts +++ b/apps/web/lib/tinybird/record-click.ts @@ -37,7 +37,7 @@ export async function recordClick({ skipRatelimit, timestamp, referrer, - trackConversion, + shouldPassClickId, }: { req: Request; clickId?: string; @@ -50,7 +50,7 @@ export async function recordClick({ skipRatelimit?: boolean; timestamp?: string; referrer?: string; - trackConversion?: boolean; + shouldPassClickId?: boolean; }) { if (!clickId) { return null; @@ -165,7 +165,7 @@ export async function recordClick({ // cache the click data for 5 mins // we're doing this because ingested click events are not available immediately in Tinybird - trackConversion && + shouldPassClickId && redis.set(`clickCache:${clickId}`, clickData, { ex: 60 * 5, }), diff --git a/apps/web/tests/redirects/index.test.ts b/apps/web/tests/redirects/index.test.ts index bc1dea07f7b..4617a3c09b9 100644 --- a/apps/web/tests/redirects/index.test.ts +++ b/apps/web/tests/redirects/index.test.ts @@ -55,6 +55,20 @@ describe.runIf(env.CI)("Link Redirects", async () => { expect(response.status).toBe(302); }); + test("with dub_id and via", async () => { + const response = await fetch(`${h.baseUrl}/track-test`, { + ...fetchOptions, + headers: {}, + }); + + // the location should contain `?dub_id=` query param + expect(response.headers.get("location")).toMatch(/dub_id=[a-zA-Z0-9]+/); + // the location should contain `?via=track-test` query param + expect(response.headers.get("location")).toMatch(/via=track-test/); + expect(response.headers.get("x-powered-by")).toBe(poweredBy); + expect(response.status).toBe(302); + }); + test("with dub_client_reference_id", async () => { const response = await fetch(`${h.baseUrl}/client_reference_id`, { ...fetchOptions, @@ -167,7 +181,4 @@ describe.runIf(env.CI)("Link Redirects", async () => { expect(response.headers.get("x-powered-by")).toBe(poweredBy); expect(response.status).toBe(302); }); - - // DUMMY test to record a hit on track-test - await fetch(`${h.baseUrl}/track-test`); }); diff --git a/apps/web/ui/partners/program-card.tsx b/apps/web/ui/partners/program-card.tsx index 926546e07a8..a6faf84b08c 100644 --- a/apps/web/ui/partners/program-card.tsx +++ b/apps/web/ui/partners/program-card.tsx @@ -47,7 +47,7 @@ export function ProgramCard({ const card = (
diff --git a/apps/web/ui/partners/program-invite-card.tsx b/apps/web/ui/partners/program-invite-card.tsx index 3503dbfc17f..6b68efb972e 100644 --- a/apps/web/ui/partners/program-invite-card.tsx +++ b/apps/web/ui/partners/program-invite-card.tsx @@ -33,6 +33,18 @@ export function ProgramInviteCard({ }, }); + const reward = + program.rewards && program.rewards.length > 0 + ? program.rewards.find((r) => r.id !== program.defaultRewardId) || + program.rewards[0] + : null; + + const discount = + program.discounts && program.discounts.length > 0 + ? program.discounts.find((d) => d.id !== program.defaultDiscountId) || + program.discounts[0] + : null; + return (
@@ -57,8 +69,8 @@ export function ProgramInviteCard({ )}