From 80ebc3fe797287f60a798ad0825ca23bbbd28440 Mon Sep 17 00:00:00 2001 From: Keith Date: Sat, 6 Jul 2024 00:57:37 +0900 Subject: [PATCH] feat: support sendgrid for newsletter signup --- .../components/FooterMedia/SubscribeWrap.tsx | 70 ++++++++----------- .../components/FooterMedia/index.module.scss | 9 +++ src/env.mjs | 4 ++ src/server/api/root.ts | 2 + src/server/api/routers/newsletter.ts | 46 ++++++++++++ 5 files changed, 89 insertions(+), 42 deletions(-) create mode 100644 src/server/api/routers/newsletter.ts diff --git a/src/components/Footer/components/FooterMedia/SubscribeWrap.tsx b/src/components/Footer/components/FooterMedia/SubscribeWrap.tsx index f2582eac..76df7453 100644 --- a/src/components/Footer/components/FooterMedia/SubscribeWrap.tsx +++ b/src/components/Footer/components/FooterMedia/SubscribeWrap.tsx @@ -1,51 +1,44 @@ import React, { useState } from 'react' +import { api } from 'src/utils' import { ArrowIcon } from './icons' import styles from './index.module.scss' export const SubscribeWrap: React.FC = () => { - const [emailAddress, setEmailAddress] = useState('') - const [firstName, setFirstName] = useState('') + const [result, setResult] = useState<'success' | 'fail' | null>(null) + const { mutateAsync } = api.newsLetter.signup.useMutation() - const handleClick = () => { - const e = document.getElementById('mc-embedded-subscribe-form') as HTMLFormElement - e.submit() + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault() + e.stopPropagation() + const elm = e.currentTarget + if (!(elm instanceof HTMLFormElement)) return + + const firstName: string = (elm.fname as HTMLInputElement).value as string + const email: string = (elm.email as HTMLInputElement).value as string + if (!firstName || !email) return + const res = await mutateAsync({ email, firstName }) + if (res.success) { + setResult('success') + } else { + window.alert('Failed to subscribe, please contact media@nervos.org') + } + } + + if (result === 'success') { + return
Thank you for subscription!
} return ( -
+
- setFirstName(e.target.value)} - required - /> +
- setEmailAddress(e.target.value)} - required - /> -
+ +
+
@@ -57,14 +50,7 @@ export const SubscribeWrap: React.FC = () => {
- +
diff --git a/src/components/Footer/components/FooterMedia/index.module.scss b/src/components/Footer/components/FooterMedia/index.module.scss index e5cce975..df77cfbd 100644 --- a/src/components/Footer/components/FooterMedia/index.module.scss +++ b/src/components/Footer/components/FooterMedia/index.module.scss @@ -59,6 +59,10 @@ } .arrowIcon { + appearance: none; + background: none; + border: 0; + padding: 0; :hover { cursor: pointer; } @@ -91,4 +95,9 @@ } } } + .subscribed { + height: 71px; + margin-top: 26px; + text-shadow: 0px 0px 8px rgb(255, 255, 255); + } } diff --git a/src/env.mjs b/src/env.mjs index d2fba22e..596b0792 100644 --- a/src/env.mjs +++ b/src/env.mjs @@ -6,6 +6,8 @@ import { z } from 'zod' */ const server = z.object({ NODE_ENV: z.enum(['development', 'test', 'production']), + SENDGRID_LIST_ID: z.string().min(1), + SENDGRID_API_TOKEN: z.string().min(1), }) /** @@ -24,6 +26,8 @@ const client = z.object({ */ const processEnv = { NODE_ENV: process.env.NODE_ENV, + SENDGRID_LIST_ID: process.env.SENDGRID_LIST_ID, + SENDGRID_API_TOKEN: process.env.SENDGRID_API_TOKEN, // NEXT_PUBLIC_CLIENTVAR: process.env.NEXT_PUBLIC_CLIENTVAR, } diff --git a/src/server/api/root.ts b/src/server/api/root.ts index 7d25afeb..d4cbcdb7 100644 --- a/src/server/api/root.ts +++ b/src/server/api/root.ts @@ -1,5 +1,6 @@ import { createTRPCRouter } from './trpc' import { ckbRouter } from './routers/ckb' +import { newsLetterRouter } from './routers/newsletter' /** * This is the primary router for your server. @@ -8,6 +9,7 @@ import { ckbRouter } from './routers/ckb' */ export const appRouter = createTRPCRouter({ ckb: ckbRouter, + newsLetter: newsLetterRouter, }) // export type definition of API diff --git a/src/server/api/routers/newsletter.ts b/src/server/api/routers/newsletter.ts new file mode 100644 index 00000000..ddfb3d10 --- /dev/null +++ b/src/server/api/routers/newsletter.ts @@ -0,0 +1,46 @@ +import { z } from 'zod' +import { createTRPCRouter, publicProcedure } from '../trpc' +import { env } from '../../../env.mjs' + +// document: https://www.twilio.com/docs/sendgrid/api-reference/contacts/add-or-update-a-contact +const SEND_GRID_API_ENDPOINT = `https://api.sendgrid.com/v3/marketing/contacts` +const METHOD = 'PUT' +const TOKEN = env.SENDGRID_API_TOKEN +const SEND_GRID_LIST_ID = env.SENDGRID_LIST_ID + +export const newsLetterRouter = createTRPCRouter({ + signup: publicProcedure + .input( + z.object({ + email: z.string(), + firstName: z.string(), + }), + ) + .mutation(async ({ input: { email, firstName } }) => { + const data = { + list_ids: [SEND_GRID_LIST_ID], + contacts: [{ email, first_name: firstName }], + } + + const res = await fetch(SEND_GRID_API_ENDPOINT, { + method: METHOD, + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${TOKEN}`, + }, + body: JSON.stringify(data), + }) + + if (res.status !== 202) { + return { + success: false, + error: { + message: 'Failed to add user to newsletter', + status: res.status, + }, + } + } + + return { success: true } + }), +})