diff --git a/messages/en-US.json b/messages/en-US.json index 178a9bb9..4ed8e50d 100644 --- a/messages/en-US.json +++ b/messages/en-US.json @@ -1421,6 +1421,9 @@ "and": "and", "privacyPolicy": "privacy policy" }, + "signUpMarketing": { + "keepMeInTheLoop": "Keep me in the loop with news, updates, and new features by email." + }, "siteRequired": "Site is required.", "olmTunnel": "Olm Tunnel", "olmTunnelDescription": "Use Olm for client connectivity", diff --git a/server/private/lib/resend.ts b/server/private/lib/resend.ts index 56198e0b..17384ea3 100644 --- a/server/private/lib/resend.ts +++ b/server/private/lib/resend.ts @@ -16,7 +16,7 @@ import privateConfig from "#private/lib/config"; import logger from "@server/logger"; export enum AudienceIds { - SignUps = "5cfbf99b-c592-40a9-9b8a-577a4681c158", + SignUps = "6c4e77b2-0851-4bd6-bac8-f51f91360f1a", Subscribed = "870b43fd-387f-44de-8fc1-707335f30b20", Churned = "f3ae92bd-2fdb-4d77-8746-2118afd62549", Newsletter = "5500c431-191c-42f0-a5d4-8b6d445b4ea0" diff --git a/server/routers/auth/signup.ts b/server/routers/auth/signup.ts index e836d109..5b550390 100644 --- a/server/routers/auth/signup.ts +++ b/server/routers/auth/signup.ts @@ -30,7 +30,8 @@ export const signupBodySchema = z.object({ password: passwordSchema, inviteToken: z.string().optional(), inviteId: z.string().optional(), - termsAcceptedTimestamp: z.string().nullable().optional() + termsAcceptedTimestamp: z.string().nullable().optional(), + marketingEmailConsent: z.boolean().optional() }); export type SignUpBody = z.infer; @@ -55,7 +56,7 @@ export async function signup( ); } - const { email, password, inviteToken, inviteId, termsAcceptedTimestamp } = + const { email, password, inviteToken, inviteId, termsAcceptedTimestamp, marketingEmailConsent } = parsedBody.data; const passwordHash = await hashPassword(password); @@ -220,8 +221,8 @@ export async function signup( new Date(sess.expiresAt) ); res.appendHeader("Set-Cookie", cookie); - - if (build == "saas") { + if (build == "saas" && marketingEmailConsent) { + logger.debug(`User ${email} opted in to marketing emails during signup.`); moveEmailToAudience(email, AudienceIds.SignUps); } diff --git a/src/components/SignupForm.tsx b/src/components/SignupForm.tsx index c9cb6a48..76d2dfce 100644 --- a/src/components/SignupForm.tsx +++ b/src/components/SignupForm.tsx @@ -92,7 +92,8 @@ const formSchema = z message: "You must agree to the terms of service and privacy policy" } - ) + ), + marketingEmailConsent: z.boolean().optional() }) .refine((data) => data.password === data.confirmPassword, { path: ["confirmPassword"], @@ -123,7 +124,8 @@ export default function SignupForm({ email: emailParam || "", password: "", confirmPassword: "", - agreeToTerms: false + agreeToTerms: false, + marketingEmailConsent: false }, mode: "onChange" // Enable real-time validation }); @@ -135,7 +137,7 @@ export default function SignupForm({ passwordValue === confirmPasswordValue; async function onSubmit(values: z.infer) { - const { email, password } = values; + const { email, password, marketingEmailConsent } = values; setLoading(true); const res = await api @@ -144,7 +146,8 @@ export default function SignupForm({ password, inviteId, inviteToken, - termsAcceptedTimestamp: termsAgreedAt + termsAcceptedTimestamp: termsAgreedAt, + marketingEmailConsent: build === "saas" ? marketingEmailConsent : undefined }) .catch((e) => { console.error(e); @@ -489,56 +492,78 @@ export default function SignupForm({ )} /> {build === "saas" && ( - ( - - - { - field.onChange(checked); - handleTermsChange( - checked as boolean - ); - }} - /> - -
- - + + +
+
+ )} + /> + ( + + + + +
+ + {t("signUpMarketing.keepMeInTheLoop")} + + +
+
+ )} + /> + )} {error && (