From 2252fee43112fa3695b02a2a6544a5c3ea41b2a1 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 24 Jul 2025 11:24:19 +0530 Subject: [PATCH 01/24] wip PartnerStack import functionality --- .../[slug]/program/new/rewards/form.tsx | 68 ++++++++------ .../new/rewards/import-partnerstack-form.tsx | 91 +++++++++++++++++++ .../partners/set-partnerstack-token.ts | 34 +++++++ .../partners/start-partnerstack-import.ts | 5 +- apps/web/lib/partnerstack/schemas.ts | 5 + .../web/lib/zod/schemas/program-onboarding.ts | 1 + 6 files changed, 172 insertions(+), 32 deletions(-) create mode 100644 apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx create mode 100644 apps/web/lib/actions/partners/set-partnerstack-token.ts diff --git a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx index e26f5986503..e3c6f40be55 100644 --- a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx +++ b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx @@ -17,7 +17,7 @@ import { cn } from "@dub/utils"; import { useAction } from "next-safe-action/hooks"; import Link from "next/link"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useEffect, useMemo, useState } from "react"; import { useFormContext, UseFormRegister, @@ -25,6 +25,7 @@ import { UseFormWatch, } from "react-hook-form"; import { toast } from "sonner"; +import { ImportPartnerStackForm } from "./import-partnerstack-form"; import { ImportRewardfulForm } from "./import-rewardful-form"; import { ImportToltForm } from "./import-tolt-form"; @@ -64,11 +65,8 @@ type ImportSource = (typeof PROGRAM_IMPORT_SOURCES)[number]; export function Form() { const router = useRouter(); - const { id: workspaceId, slug: workspaceSlug, mutate } = useWorkspace(); const [hasSubmitted, setHasSubmitted] = useState(false); - const [selectedSource, setSelectedSource] = useState( - PROGRAM_IMPORT_SOURCES[0], - ); + const { id: workspaceId, slug: workspaceSlug, mutate } = useWorkspace(); const { register, @@ -78,8 +76,9 @@ export function Form() { formState: { isSubmitting }, } = useFormContext(); - const [programType, rewardful, tolt, amount] = watch([ + const [programType, importSource, rewardful, tolt, amount] = watch([ "programType", + "importSource", "rewardful", "tolt", "amount", @@ -96,17 +95,6 @@ export function Form() { } }, [programType]); - // Set the import source based on existing program data - useEffect(() => { - if (programType === "import") { - if (rewardful && rewardful.id) { - setSelectedSource(PROGRAM_IMPORT_SOURCES[0]); - } else if (tolt && tolt.id) { - setSelectedSource(PROGRAM_IMPORT_SOURCES[1]); - } - } - }, [programType, tolt, rewardful]); - const { executeAsync, isPending } = useAction(onboardProgramAction, { onSuccess: () => { router.push(`/${workspaceSlug}/program/new/partners`); @@ -145,7 +133,31 @@ export function Form() { const hideContinueButton = programType === "import" && (!rewardful || !rewardful.id) && - (!tolt || !tolt.id); + (!tolt || !tolt.id) && + importSource === "partnerstack"; + + const selectedSource = useMemo(() => { + return PROGRAM_IMPORT_SOURCES.find((source) => source.id === importSource); + }, [importSource]); + + const renderImportForm = () => { + switch (selectedSource?.id) { + case "rewardful": + return ( + + ); + case "tolt": + return ; + case "partnerstack": + return onSubmit} />; + default: + return null; + } + }; return (
@@ -208,8 +220,14 @@ export function Form() { items={ PROGRAM_IMPORT_SOURCES as unknown as InputSelectItemProps[] } - selectedItem={selectedSource} - setSelectedItem={setSelectedSource} + selectedItem={selectedSource ?? null} + setSelectedItem={(item: ImportSource) => { + if (item) { + setValue("importSource", item.id, { + shouldDirty: true, + }); + } + }} className="w-full" inputAttrs={{ placeholder: "Select import source", @@ -229,15 +247,7 @@ export function Form() { )} - {selectedSource.id === "rewardful" ? ( - - ) : ( - - )} + {renderImportForm()} )} diff --git a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx new file mode 100644 index 00000000000..aa6ee4be052 --- /dev/null +++ b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/import-partnerstack-form.tsx @@ -0,0 +1,91 @@ +"use client"; + +import { setPartnerStackTokenAction } from "@/lib/actions/partners/set-partnerstack-token"; +import useWorkspace from "@/lib/swr/use-workspace"; +import { Button, Input } from "@dub/ui"; +import { useAction } from "next-safe-action/hooks"; +import Link from "next/link"; +import { useState } from "react"; +import { toast } from "sonner"; + +export const ImportPartnerStackForm = ({ + onSuccess, +}: { + onSuccess: () => void; +}) => { + const { id: workspaceId } = useWorkspace(); + const [publicKey, setPublicKey] = useState(""); + const [secretKey, setSecretKey] = useState(""); + + const { executeAsync, isPending } = useAction(setPartnerStackTokenAction, { + onSuccess: () => { + onSuccess(); + toast.success("PartnerStack credentials saved successfully!"); + }, + onError: ({ error }) => { + toast.error(error.serverError); + }, + }); + + const onSubmit = async () => { + if (!workspaceId || !publicKey || !secretKey) { + toast.error("Please fill in all required fields."); + return; + } + + await executeAsync({ + workspaceId, + publicKey, + secretKey, + }); + }; + + return ( +
+
+ + setPublicKey(e.target.value)} + /> +
+ Find your PartnerStack API keys in your{" "} + + Settings + +
+
+ +
+ + setSecretKey(e.target.value)} + /> +
+ +
+ ); +}; diff --git a/apps/web/lib/actions/partners/set-partnerstack-token.ts b/apps/web/lib/actions/partners/set-partnerstack-token.ts new file mode 100644 index 00000000000..4634867a1b4 --- /dev/null +++ b/apps/web/lib/actions/partners/set-partnerstack-token.ts @@ -0,0 +1,34 @@ +"use server"; + +import { PartnerStackApi } from "@/lib/partnerstack/api"; +import { partnerStackImporter } from "@/lib/partnerstack/importer"; +import { partnerStackCredentialsSchema } from "@/lib/partnerstack/schemas"; +import { z } from "zod"; +import { authActionClient } from "../safe-action"; + +const schema = partnerStackCredentialsSchema.extend({ + workspaceId: z.string(), +}); + +export const setPartnerStackTokenAction = authActionClient + .schema(schema) + .action(async ({ parsedInput, ctx }) => { + const { workspace } = ctx; + const { publicKey, secretKey } = parsedInput; + + if (!workspace.partnersEnabled) { + throw new Error("You are not allowed to perform this action."); + } + + const partnerStackApi = new PartnerStackApi({ + publicKey, + secretKey, + }); + + await partnerStackApi.testConnection(); + + await partnerStackImporter.setCredentials(workspace.id, { + publicKey, + secretKey, + }); + }); diff --git a/apps/web/lib/actions/partners/start-partnerstack-import.ts b/apps/web/lib/actions/partners/start-partnerstack-import.ts index f07c004b055..8e7e254e570 100644 --- a/apps/web/lib/actions/partners/start-partnerstack-import.ts +++ b/apps/web/lib/actions/partners/start-partnerstack-import.ts @@ -4,13 +4,12 @@ import { getDefaultProgramIdOrThrow } from "@/lib/api/programs/get-default-progr import { getProgramOrThrow } from "@/lib/api/programs/get-program-or-throw"; import { PartnerStackApi } from "@/lib/partnerstack/api"; import { partnerStackImporter } from "@/lib/partnerstack/importer"; +import { partnerStackCredentialsSchema } from "@/lib/partnerstack/schemas"; import { z } from "zod"; import { authActionClient } from "../safe-action"; -const schema = z.object({ +const schema = partnerStackCredentialsSchema.extend({ workspaceId: z.string(), - publicKey: z.string().min(1), - secretKey: z.string().min(1), }); export const startPartnerStackImportAction = authActionClient diff --git a/apps/web/lib/partnerstack/schemas.ts b/apps/web/lib/partnerstack/schemas.ts index ee1af4fae0a..76168cccaa9 100644 --- a/apps/web/lib/partnerstack/schemas.ts +++ b/apps/web/lib/partnerstack/schemas.ts @@ -8,6 +8,11 @@ export const partnerStackImportSteps = z.enum([ "update-stripe-customers", ]); +export const partnerStackCredentialsSchema = z.object({ + publicKey: z.string().min(1), + secretKey: z.string().min(1), +}); + export const partnerStackImportPayloadSchema = z.object({ userId: z.string(), programId: z.string(), diff --git a/apps/web/lib/zod/schemas/program-onboarding.ts b/apps/web/lib/zod/schemas/program-onboarding.ts index 0e6758f3cf3..f1c57b40d9b 100644 --- a/apps/web/lib/zod/schemas/program-onboarding.ts +++ b/apps/web/lib/zod/schemas/program-onboarding.ts @@ -19,6 +19,7 @@ export const programInfoSchema = z.object({ export const programRewardSchema = z .object({ programType: z.enum(["new", "import"]), + importSource: z.enum(["rewardful", "tolt", "partnerstack"]).nullish(), rewardful: z .object({ maskedToken: z.string().nullish(), From 33f4b42ef2a1da0c77bfb10516f2c1d05bc842d6 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 24 Jul 2025 11:27:25 +0530 Subject: [PATCH 02/24] Update create-program.ts --- apps/web/lib/actions/partners/create-program.ts | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/apps/web/lib/actions/partners/create-program.ts b/apps/web/lib/actions/partners/create-program.ts index 512c4f15e25..c39f88d6384 100644 --- a/apps/web/lib/actions/partners/create-program.ts +++ b/apps/web/lib/actions/partners/create-program.ts @@ -3,6 +3,7 @@ import { createId } from "@/lib/api/create-id"; import { getDomainOrThrow } from "@/lib/api/domains/get-domain-or-throw"; import { createAndEnrollPartner } from "@/lib/api/partners/create-and-enroll-partner"; import { createPartnerLink } from "@/lib/api/partners/create-partner-link"; +import { partnerStackImporter } from "@/lib/partnerstack/importer"; import { rewardfulImporter } from "@/lib/rewardful/importer"; import { isStored, storage } from "@/lib/storage"; import { toltImporter } from "@/lib/tolt/importer"; @@ -49,6 +50,7 @@ export const createProgram = async ({ helpUrl, termsUrl, logo: uploadedLogo, + importSource, } = programDataSchema.parse(store.programOnboarding); await getDomainOrThrow({ workspace, domain }); @@ -138,8 +140,7 @@ export const createProgram = async ({ .then(({ url }) => url) : null; - // import the rewardful campaign if it exists - if (rewardful && rewardful.id) { + if (importSource === "rewardful" && rewardful?.id) { const credentials = await rewardfulImporter.getCredentials(workspace.id); await rewardfulImporter.setCredentials(workspace.id, { @@ -151,14 +152,17 @@ export const createProgram = async ({ programId: program.id, action: "import-campaign", }); - } - - // import the tolt program if it exists - if (tolt && tolt.id) { + } else if (importSource === "tolt") { await toltImporter.queue({ programId: program.id, action: "import-affiliates", }); + } else if (importSource === "partnerstack") { + await partnerStackImporter.queue({ + userId: user.id, + programId: program.id, + action: "import-partners", + }); } const reward = program.rewards?.[0]; From 7d2651991432c2c4ca08c7af241adf43dd9d8537 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 24 Jul 2025 11:42:02 +0530 Subject: [PATCH 03/24] consistent naming --- apps/web/app/(ee)/api/cron/import/tolt/route.ts | 12 ++++++------ apps/web/lib/tolt/api.ts | 2 +- .../{import-referrals.ts => import-customers.ts} | 4 ++-- .../{import-affiliates.ts => import-partners.ts} | 6 +++--- apps/web/lib/tolt/importer.ts | 4 ++-- 5 files changed, 14 insertions(+), 14 deletions(-) rename apps/web/lib/tolt/{import-referrals.ts => import-customers.ts} (98%) rename apps/web/lib/tolt/{import-affiliates.ts => import-partners.ts} (95%) diff --git a/apps/web/app/(ee)/api/cron/import/tolt/route.ts b/apps/web/app/(ee)/api/cron/import/tolt/route.ts index 9dea3a7d8b0..b190ca5848f 100644 --- a/apps/web/app/(ee)/api/cron/import/tolt/route.ts +++ b/apps/web/app/(ee)/api/cron/import/tolt/route.ts @@ -1,10 +1,10 @@ import { handleAndReturnErrorResponse } from "@/lib/api/errors"; import { verifyQstashSignature } from "@/lib/cron/verify-qstash"; import { cleanupPartners } from "@/lib/tolt/cleanup-partners"; -import { importAffiliates } from "@/lib/tolt/import-affiliates"; import { importCommissions } from "@/lib/tolt/import-commissions"; +import { importCustomers } from "@/lib/tolt/import-customers"; import { importLinks } from "@/lib/tolt/import-links"; -import { importReferrals } from "@/lib/tolt/import-referrals"; +import { importPartners } from "@/lib/tolt/import-partners"; import { importSteps } from "@/lib/tolt/importer"; import { updateStripeCustomers } from "@/lib/tolt/update-stripe-customers"; import { NextResponse } from "next/server"; @@ -30,14 +30,14 @@ export async function POST(req: Request) { const { action, ...payload } = schema.parse(JSON.parse(rawBody)); switch (action) { - case "import-affiliates": - await importAffiliates(payload); + case "import-partners": + await importPartners(payload); break; case "import-links": await importLinks(payload); break; - case "import-referrals": - await importReferrals(payload); + case "import-customers": + await importCustomers(payload); break; case "import-commissions": await importCommissions(payload); diff --git a/apps/web/lib/tolt/api.ts b/apps/web/lib/tolt/api.ts index 16520c79a23..7e2e05debef 100644 --- a/apps/web/lib/tolt/api.ts +++ b/apps/web/lib/tolt/api.ts @@ -72,7 +72,7 @@ export class ToltApi { }; } - async listAffiliates({ + async listPartners({ programId, startingAfter, }: { diff --git a/apps/web/lib/tolt/import-referrals.ts b/apps/web/lib/tolt/import-customers.ts similarity index 98% rename from apps/web/lib/tolt/import-referrals.ts rename to apps/web/lib/tolt/import-customers.ts index 90461779f24..de2a2c7c0ec 100644 --- a/apps/web/lib/tolt/import-referrals.ts +++ b/apps/web/lib/tolt/import-customers.ts @@ -8,7 +8,7 @@ import { ToltApi } from "./api"; import { MAX_BATCHES, toltImporter } from "./importer"; import { ToltAffiliate, ToltCustomer } from "./types"; -export async function importReferrals({ +export async function importCustomers({ programId, startingAfter, }: { @@ -112,7 +112,7 @@ export async function importReferrals({ await toltImporter.queue({ programId, - action: hasMore ? "import-referrals" : "import-commissions", + action: hasMore ? "import-customers" : "import-commissions", ...(hasMore && { startingAfter }), }); } diff --git a/apps/web/lib/tolt/import-affiliates.ts b/apps/web/lib/tolt/import-partners.ts similarity index 95% rename from apps/web/lib/tolt/import-affiliates.ts rename to apps/web/lib/tolt/import-partners.ts index cf3bcb1065d..4380b5bd785 100644 --- a/apps/web/lib/tolt/import-affiliates.ts +++ b/apps/web/lib/tolt/import-partners.ts @@ -6,7 +6,7 @@ import { ToltApi } from "./api"; import { MAX_BATCHES, toltImporter } from "./importer"; import { ToltAffiliate } from "./types"; -export async function importAffiliates({ +export async function importPartners({ programId, startingAfter, }: { @@ -41,7 +41,7 @@ export async function importAffiliates({ const defaultReward = saleReward || leadReward || clickReward; while (hasMore && processedBatches < MAX_BATCHES) { - const affiliates = await toltApi.listAffiliates({ + const affiliates = await toltApi.listPartners({ programId: toltProgramId, startingAfter, }); @@ -88,7 +88,7 @@ export async function importAffiliates({ await toltImporter.queue({ programId, - action: hasMore ? "import-affiliates" : "import-links", + action: hasMore ? "import-partners" : "import-links", ...(hasMore && { startingAfter }), }); } diff --git a/apps/web/lib/tolt/importer.ts b/apps/web/lib/tolt/importer.ts index 05d64a2dcee..03e9347335e 100644 --- a/apps/web/lib/tolt/importer.ts +++ b/apps/web/lib/tolt/importer.ts @@ -10,9 +10,9 @@ export const CACHE_KEY_PREFIX = "tolt:import"; export const PARTNER_IDS_KEY_PREFIX = "tolt:import:partnerIds"; export const importSteps = z.enum([ - "import-affiliates", + "import-partners", "import-links", - "import-referrals", + "import-customers", "import-commissions", "update-stripe-customers", // update the customers with their stripe customer ID "cleanup-partners", // remove partners with 0 leads From 1b2e82151009c61107b8838bdbf7cbc7d3e53b82 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 24 Jul 2025 11:44:48 +0530 Subject: [PATCH 04/24] Refactor Rewardful import functionality to standardize naming conventions. --- apps/web/app/(ee)/api/cron/import/rewardful/route.ts | 8 ++++---- apps/web/lib/rewardful/api.ts | 4 ++-- apps/web/lib/rewardful/import-campaign.ts | 2 +- .../{import-referrals.ts => import-customers.ts} | 4 ++-- .../{import-affiliates.ts => import-partners.ts} | 6 +++--- apps/web/lib/rewardful/importer.ts | 4 ++-- 6 files changed, 14 insertions(+), 14 deletions(-) rename apps/web/lib/rewardful/{import-referrals.ts => import-customers.ts} (98%) rename apps/web/lib/rewardful/{import-affiliates.ts => import-partners.ts} (94%) diff --git a/apps/web/app/(ee)/api/cron/import/rewardful/route.ts b/apps/web/app/(ee)/api/cron/import/rewardful/route.ts index 9a41a451879..b5c69b5a5e1 100644 --- a/apps/web/app/(ee)/api/cron/import/rewardful/route.ts +++ b/apps/web/app/(ee)/api/cron/import/rewardful/route.ts @@ -1,9 +1,9 @@ import { handleAndReturnErrorResponse } from "@/lib/api/errors"; import { verifyQstashSignature } from "@/lib/cron/verify-qstash"; -import { importAffiliates } from "@/lib/rewardful/import-affiliates"; import { importCampaign } from "@/lib/rewardful/import-campaign"; import { importCommissions } from "@/lib/rewardful/import-commissions"; -import { importReferrals } from "@/lib/rewardful/import-referrals"; +import { importReferrals } from "@/lib/rewardful/import-customers"; +import { importAffiliates } from "@/lib/rewardful/import-partners"; import { importSteps } from "@/lib/rewardful/importer"; import { NextResponse } from "next/server"; import { z } from "zod"; @@ -32,14 +32,14 @@ export async function POST(req: Request) { programId, }); break; - case "import-affiliates": + case "import-partners": await importAffiliates({ programId, rewardId, page, }); break; - case "import-referrals": + case "import-customers": await importReferrals({ programId, page, diff --git a/apps/web/lib/rewardful/api.ts b/apps/web/lib/rewardful/api.ts index a9c9c1e1bf6..cbee2a3215b 100644 --- a/apps/web/lib/rewardful/api.ts +++ b/apps/web/lib/rewardful/api.ts @@ -59,7 +59,7 @@ export class RewardfulApi { return data; } - async listAffiliates({ + async listPartners({ campaignId, page = 1, }: { @@ -79,7 +79,7 @@ export class RewardfulApi { return data; } - async listReferrals({ page = 1 }: { page?: number }) { + async listCustomers({ page = 1 }: { page?: number }) { const searchParams = new URLSearchParams(); searchParams.append("expand[]", "affiliate"); searchParams.append("conversion_state[]", "lead"); diff --git a/apps/web/lib/rewardful/import-campaign.ts b/apps/web/lib/rewardful/import-campaign.ts index 12f205c6afe..0778f759544 100644 --- a/apps/web/lib/rewardful/import-campaign.ts +++ b/apps/web/lib/rewardful/import-campaign.ts @@ -84,6 +84,6 @@ export async function importCampaign({ programId }: { programId: string }) { return await rewardfulImporter.queue({ programId, ...(rewardId && { rewardId }), - action: "import-affiliates", + action: "import-partners", }); } diff --git a/apps/web/lib/rewardful/import-referrals.ts b/apps/web/lib/rewardful/import-customers.ts similarity index 98% rename from apps/web/lib/rewardful/import-referrals.ts rename to apps/web/lib/rewardful/import-customers.ts index 06f750bcb4e..b0f77e8e67c 100644 --- a/apps/web/lib/rewardful/import-referrals.ts +++ b/apps/web/lib/rewardful/import-customers.ts @@ -36,7 +36,7 @@ export async function importReferrals({ let processedBatches = 0; while (hasMoreReferrals && processedBatches < MAX_BATCHES) { - const referrals = await rewardfulApi.listReferrals({ + const referrals = await rewardfulApi.listCustomers({ page: currentPage, }); @@ -63,7 +63,7 @@ export async function importReferrals({ if (hasMoreReferrals) { return await rewardfulImporter.queue({ programId: program.id, - action: "import-referrals", + action: "import-customers", page: currentPage, }); } diff --git a/apps/web/lib/rewardful/import-affiliates.ts b/apps/web/lib/rewardful/import-partners.ts similarity index 94% rename from apps/web/lib/rewardful/import-affiliates.ts rename to apps/web/lib/rewardful/import-partners.ts index d1cad5be071..387ae63f1ba 100644 --- a/apps/web/lib/rewardful/import-affiliates.ts +++ b/apps/web/lib/rewardful/import-partners.ts @@ -45,7 +45,7 @@ export async function importAffiliates({ }); while (hasMoreAffiliates && processedBatches < MAX_BATCHES) { - const affiliates = await rewardfulApi.listAffiliates({ + const affiliates = await rewardfulApi.listPartners({ campaignId, page: currentPage, }); @@ -78,12 +78,12 @@ export async function importAffiliates({ processedBatches++; } - const action = hasMoreAffiliates ? "import-affiliates" : "import-referrals"; + const action = hasMoreAffiliates ? "import-partners" : "import-customers"; await rewardfulImporter.queue({ programId: program.id, action, - ...(action === "import-affiliates" && rewardId && { rewardId }), + ...(action === "import-partners" && rewardId && { rewardId }), ...(hasMoreAffiliates ? { page: currentPage } : {}), }); } diff --git a/apps/web/lib/rewardful/importer.ts b/apps/web/lib/rewardful/importer.ts index 047c14d2312..f4f736ccbd0 100644 --- a/apps/web/lib/rewardful/importer.ts +++ b/apps/web/lib/rewardful/importer.ts @@ -14,8 +14,8 @@ export const CACHE_KEY_PREFIX = "rewardful:import"; export const importSteps = z.enum([ "import-campaign", - "import-affiliates", - "import-referrals", + "import-partners", + "import-customers", "import-commissions", ]); From 33746e27b5e9d795e56db725e35d5c6034185133 Mon Sep 17 00:00:00 2001 From: Kiran K Date: Thu, 24 Jul 2025 12:18:01 +0530 Subject: [PATCH 05/24] Refactor Tolt import functionality to use a unified payload schema and streamline import processes across various entities. --- .../app/(ee)/api/cron/import/tolt/route.ts | 13 ++---- .../[slug]/program/new/rewards/form.tsx | 41 ++++++----------- .../program/new/rewards/import-tolt-form.tsx | 46 +++++++++++++++---- .../lib/actions/partners/set-tolt-token.ts | 8 ++-- apps/web/lib/partnerstack/importer.ts | 11 +++-- apps/web/lib/partnerstack/types.ts | 10 ++-- apps/web/lib/tolt/import-commissions.ts | 22 ++++----- apps/web/lib/tolt/import-customers.ts | 20 ++++---- apps/web/lib/tolt/import-links.ts | 22 ++++----- apps/web/lib/tolt/import-partners.ts | 20 ++++---- apps/web/lib/tolt/importer.ts | 24 +++------- apps/web/lib/tolt/schemas.ts | 17 +++++++ apps/web/lib/tolt/types.ts | 7 +-- .../web/lib/zod/schemas/program-onboarding.ts | 2 +- 14 files changed, 132 insertions(+), 131 deletions(-) diff --git a/apps/web/app/(ee)/api/cron/import/tolt/route.ts b/apps/web/app/(ee)/api/cron/import/tolt/route.ts index b190ca5848f..7027cb057f0 100644 --- a/apps/web/app/(ee)/api/cron/import/tolt/route.ts +++ b/apps/web/app/(ee)/api/cron/import/tolt/route.ts @@ -5,19 +5,12 @@ import { importCommissions } from "@/lib/tolt/import-commissions"; import { importCustomers } from "@/lib/tolt/import-customers"; import { importLinks } from "@/lib/tolt/import-links"; import { importPartners } from "@/lib/tolt/import-partners"; -import { importSteps } from "@/lib/tolt/importer"; +import { toltImportPayloadSchema } from "@/lib/tolt/schemas"; import { updateStripeCustomers } from "@/lib/tolt/update-stripe-customers"; import { NextResponse } from "next/server"; -import { z } from "zod"; export const dynamic = "force-dynamic"; -const schema = z.object({ - action: importSteps, - programId: z.string(), - startingAfter: z.string().optional(), -}); - export async function POST(req: Request) { try { const rawBody = await req.text(); @@ -27,9 +20,9 @@ export async function POST(req: Request) { rawBody, }); - const { action, ...payload } = schema.parse(JSON.parse(rawBody)); + const payload = toltImportPayloadSchema.parse(JSON.parse(rawBody)); - switch (action) { + switch (payload.action) { case "import-partners": await importPartners(payload); break; diff --git a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx index e3c6f40be55..0da01955994 100644 --- a/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx +++ b/apps/web/app/(ee)/app.dub.co/(new-program)/[slug]/program/new/rewards/form.tsx @@ -73,16 +73,11 @@ export function Form() { handleSubmit, watch, setValue, + getValues, formState: { isSubmitting }, } = useFormContext(); - const [programType, importSource, rewardful, tolt, amount] = watch([ - "programType", - "importSource", - "rewardful", - "tolt", - "amount", - ]); + const [programType, importSource] = watch(["programType", "importSource"]); useEffect(() => { if (programType === "new") { @@ -121,21 +116,6 @@ export function Form() { }); }; - const buttonDisabled = - isSubmitting || - isPending || - hasSubmitted || - (programType === "new" && !amount) || - (programType === "import" && - (!rewardful || !rewardful.id) && - (!tolt || !tolt.id)); - - const hideContinueButton = - programType === "import" && - (!rewardful || !rewardful.id) && - (!tolt || !tolt.id) && - importSource === "partnerstack"; - const selectedSource = useMemo(() => { return PROGRAM_IMPORT_SOURCES.find((source) => source.id === importSource); }, [importSource]); @@ -151,9 +131,18 @@ export function Form() { /> ); case "tolt": - return ; + return ( + onSubmit(getValues())} + isPending={isSubmitting || isPending} + /> + ); case "partnerstack": - return onSubmit} />; + return ( + onSubmit(getValues())} /> + ); default: return null; } @@ -251,12 +240,12 @@ export function Form() { )} - {!hideContinueButton && ( + {programType !== "import" && (