diff --git a/apps/web/.env.example b/apps/web/.env.example index 3a2a69e681e..251cfa2b8ca 100644 --- a/apps/web/.env.example +++ b/apps/web/.env.example @@ -95,7 +95,6 @@ UNSPLASH_ACCESS_KEY= STORAGE_ACCESS_KEY_ID= STORAGE_SECRET_ACCESS_KEY= STORAGE_ENDPOINT= -STORAGE_BASE_URL= # Used for internal monitoring & paging # You can remove this by removing `DUB_SLACK_HOOK_CRON` and `DUB_SLACK_HOOK_LINKS` from the codebase diff --git a/apps/web/app/api/track/visit/route.ts b/apps/web/app/api/track/visit/route.ts index 133e44187a3..c30cf77a45d 100644 --- a/apps/web/app/api/track/visit/route.ts +++ b/apps/web/app/api/track/visit/route.ts @@ -95,6 +95,7 @@ export const POST = withAxiom(async (req: AxiomRequest) => { workspaceId: cachedLink.projectId, skipRatelimit: true, ...(referrer && { referrer }), + shouldPassClickId: true, }); } })(), diff --git a/apps/web/app/api/user/route.ts b/apps/web/app/api/user/route.ts index dab5a52ef3e..9ae90632691 100644 --- a/apps/web/app/api/user/route.ts +++ b/apps/web/app/api/user/route.ts @@ -2,6 +2,7 @@ import { DubApiError } from "@/lib/api/errors"; import { hashToken, withSession } from "@/lib/auth"; import { storage } from "@/lib/storage"; import { ratelimit, redis } from "@/lib/upstash"; +import { base64ImageSchema } from "@/lib/zod/schemas/misc"; import { sendEmail } from "@dub/email"; import { unsubscribe } from "@dub/email/resend/unsubscribe"; import ConfirmEmailChange from "@dub/email/templates/confirm-email-change"; @@ -15,7 +16,7 @@ import { z } from "zod"; const updateUserSchema = z.object({ name: z.preprocess(trim, z.string().min(1).max(64)).optional(), email: z.preprocess(trim, z.string().email()).optional(), - image: z.string().url().optional(), + image: base64ImageSchema.nullish(), source: z.preprocess(trim, z.string().min(1).max(32)).optional(), defaultWorkspace: z.preprocess(trim, z.string().min(1)).optional(), }); diff --git a/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/form.tsx b/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/form.tsx index fd7d7a9ff9b..aa4810b9b9e 100644 --- a/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/form.tsx +++ b/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/form.tsx @@ -23,6 +23,8 @@ import { toast } from "sonner"; export function Form() { const router = useRouter(); const { isMobile } = useMediaQuery(); + const [isUploading, setIsUploading] = useState(false); + const [hasSubmitted, setHasSubmitted] = useState(false); const { activeWorkspaceDomains, loading } = useDomains(); const { id: workspaceId, slug: workspaceSlug, mutate } = useWorkspace(); @@ -35,8 +37,6 @@ export function Form() { formState: { isSubmitting }, } = useFormContext(); - const [hasSubmitted, setHasSubmitted] = useState(false); - const { executeAsync, isPending } = useAction(onboardProgramAction, { onSuccess: () => { router.push(`/${workspaceSlug}/programs/new/rewards`); @@ -59,8 +59,6 @@ export function Form() { }); }; - const [isUploading, setIsUploading] = useState(false); - // Handle logo upload const handleUpload = async (file: File) => { setIsUploading(true); diff --git a/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/overview/page-client.tsx b/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/overview/page-client.tsx index 6afeacd5b37..6164db514f2 100644 --- a/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/overview/page-client.tsx +++ b/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/overview/page-client.tsx @@ -12,17 +12,25 @@ import Link from "next/link"; import { useMemo } from "react"; import { useFormContext } from "react-hook-form"; import { toast } from "sonner"; +import { mutate } from "swr"; export function PageClient() { const { getValues, formState: { isSubmitting, isSubmitSuccessful }, } = useFormContext(); - const { id: workspaceId, slug: workspaceSlug, mutate } = useWorkspace(); + const { + id: workspaceId, + slug: workspaceSlug, + mutate: mutateWorkspace, + } = useWorkspace(); const data = getValues(); const { executeAsync, isPending } = useAction(onboardProgramAction, { + onSuccess: async () => { + await mutate(`/api/programs?workspaceId=${workspaceId}`); + }, onError: ({ error }) => { toast.error(error.serverError); }, @@ -36,8 +44,6 @@ export function PageClient() { workspaceId, step: "create-program", }); - - mutate(); }; const isValid = useMemo(() => { diff --git a/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/rewards/form.tsx b/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/rewards/form.tsx index dc903d7ad18..4d9ef9de24e 100644 --- a/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/rewards/form.tsx +++ b/apps/web/app/app.dub.co/(new-program)/[slug]/programs/new/rewards/form.tsx @@ -77,12 +77,23 @@ export function Form() { formState: { isSubmitting }, } = useFormContext(); - const [programType, rewardful, amount] = watch([ + const [programType, rewardful, amount, maxDuration] = watch([ "programType", "rewardful", "amount", + "maxDuration", ]); + useEffect(() => { + if (programType === "new") { + setValue("rewardful", null); + } else if (programType === "import") { + setValue("type", null); + setValue("amount", null); + setValue("maxDuration", null); + } + }, [programType]); + const { executeAsync, isPending } = useAction(onboardProgramAction, { onSuccess: () => { router.push(`/${workspaceSlug}/programs/new/partners`); @@ -94,30 +105,15 @@ export function Form() { }); const onSubmit = async (data: ProgramData) => { - if (!workspaceId) return; - - const programData = { - ...data, - ...(programType === "new" && { - rewardful: null, - apiKeyPrefix: null, - amount: data.amount ? data.amount * 100 : null, - }), - ...(programType === "import" && { - type: null, - amount: null, - maxDuration: null, - }), - }; + if (!workspaceId) { + return; + } setHasSubmitted(true); + await executeAsync({ - ...programData, + ...data, workspaceId, - maxDuration: - Infinity === Number(programData.maxDuration) - ? null - : programData.maxDuration, step: "configure-reward", }); }; @@ -216,10 +212,6 @@ const NewProgramForm = ({ register, watch, setValue }: FormProps) => { useEffect(() => { setCommissionStructure(maxDuration === 0 ? "one-off" : "recurring"); - - if (maxDuration === null) { - setValue("maxDuration", Infinity); - } }, [maxDuration]); return ( @@ -320,7 +312,7 @@ const NewProgramForm = ({ register, watch, setValue }: FormProps) => { if (value === "recurring") { setCommissionStructure("recurring"); - setValue("maxDuration", 3, { + setValue("maxDuration", 12, { shouldValidate: true, }); } @@ -351,7 +343,15 @@ const NewProgramForm = ({ register, watch, setValue }: FormProps) => { Duration