From 1e89e571450c56077c024b3464715952e3007cf7 Mon Sep 17 00:00:00 2001 From: m5r Date: Sat, 28 Aug 2021 05:09:45 +0800 Subject: [PATCH] click to action on landing page --- app/api/_types.ts | 4 - app/api/newsletter/subscribe.ts | 56 -------- app/landing-page/components/cta-form.tsx | 46 ++++++ app/landing-page/components/phone-mockup.tsx | 26 ++++ .../components/referral-banner.tsx | 35 +++++ app/landing-page/mutations/join-waitlist.ts | 23 +++ app/landing-page/pages/index.tsx | 134 +----------------- app/messages/api/webhook/incoming-message.ts | 6 +- .../mailchimp.ts | 0 9 files changed, 140 insertions(+), 190 deletions(-) delete mode 100644 app/api/_types.ts delete mode 100644 app/api/newsletter/subscribe.ts create mode 100644 app/landing-page/components/cta-form.tsx create mode 100644 app/landing-page/components/phone-mockup.tsx create mode 100644 app/landing-page/components/referral-banner.tsx create mode 100644 app/landing-page/mutations/join-waitlist.ts rename app/api/newsletter/_mailchimp.ts => integrations/mailchimp.ts (100%) diff --git a/app/api/_types.ts b/app/api/_types.ts deleted file mode 100644 index b528718..0000000 --- a/app/api/_types.ts +++ /dev/null @@ -1,4 +0,0 @@ -export type ApiError = { - statusCode: number; - errorMessage: string; -}; diff --git a/app/api/newsletter/subscribe.ts b/app/api/newsletter/subscribe.ts deleted file mode 100644 index 0cc60ea..0000000 --- a/app/api/newsletter/subscribe.ts +++ /dev/null @@ -1,56 +0,0 @@ -import type { BlitzApiRequest, BlitzApiResponse } from "blitz"; -import zod from "zod"; - -import type { ApiError } from "../_types"; -import appLogger from "../../../integrations/logger"; -import { addSubscriber } from "./_mailchimp"; - -type Response = {} | ApiError; - -const logger = appLogger.child({ route: "/api/newsletter/subscribe" }); - -const bodySchema = zod.object({ - email: zod.string().email(), -}); - -export default async function subscribeToNewsletter(req: BlitzApiRequest, res: BlitzApiResponse) { - if (req.method !== "POST") { - const statusCode = 405; - const apiError: ApiError = { - statusCode, - errorMessage: `Method ${req.method} Not Allowed`, - }; - logger.error(apiError); - - res.setHeader("Allow", ["POST"]); - res.status(statusCode).send(apiError); - return; - } - - let body; - try { - body = bodySchema.parse(req.body); - } catch (error: any) { - const statusCode = 400; - const apiError: ApiError = { - statusCode, - errorMessage: "Body is malformed", - }; - logger.error(error); - - res.status(statusCode).send(apiError); - return; - } - - try { - await addSubscriber(body.email); - } catch (error: any) { - console.log("error", error.response?.data); - - if (error.response?.data.title !== "Member Exists") { - return res.status(error.response?.status ?? 400).end(); - } - } - - res.status(200).end(); -} diff --git a/app/landing-page/components/cta-form.tsx b/app/landing-page/components/cta-form.tsx new file mode 100644 index 0000000..e8b86af --- /dev/null +++ b/app/landing-page/components/cta-form.tsx @@ -0,0 +1,46 @@ +import { useMutation } from "blitz"; +import { useForm } from "react-hook-form"; + +import joinWaitlist from "../mutations/join-waitlist"; + +type Form = { + email: string; +}; + +export default function CTAForm() { + const [joinWaitlistMutation] = useMutation(joinWaitlist); + const { + handleSubmit, + register, + formState: { isSubmitted }, + } = useForm
(); + const onSubmit = handleSubmit(async ({ email }) => { + if (isSubmitted) { + return; + } + + return joinWaitlistMutation({ email }); + }); + + return ( + + {isSubmitted ? ( +

+ You're on the list! We will be in touch soon +

+ ) : ( +
+ + +
+ )} +
+ ); +} diff --git a/app/landing-page/components/phone-mockup.tsx b/app/landing-page/components/phone-mockup.tsx new file mode 100644 index 0000000..653c5b4 --- /dev/null +++ b/app/landing-page/components/phone-mockup.tsx @@ -0,0 +1,26 @@ +import mockupImage from "../images/mockup-image-01.png"; +import iphoneMockup from "../images/iphone-mockup.png"; + +export default function PhoneMockup() { + return ( +
+
+ Features illustration + +
+
+ ); +} diff --git a/app/landing-page/components/referral-banner.tsx b/app/landing-page/components/referral-banner.tsx new file mode 100644 index 0000000..a8c2fb9 --- /dev/null +++ b/app/landing-page/components/referral-banner.tsx @@ -0,0 +1,35 @@ +import { XIcon } from "@heroicons/react/outline"; + +export default function ReferralBanner() { + const isDisabled = true; + if (isDisabled) { + return null; + } + + return ( +
+
+
+

+ 🎉 New: Get one month free for every friend that joins and subscribe! + + + {" "} + Learn more + + +

+
+
+ +
+
+
+ ); +} diff --git a/app/landing-page/mutations/join-waitlist.ts b/app/landing-page/mutations/join-waitlist.ts new file mode 100644 index 0000000..fad6453 --- /dev/null +++ b/app/landing-page/mutations/join-waitlist.ts @@ -0,0 +1,23 @@ +import { resolver } from "blitz"; +import { z } from "zod"; + +import appLogger from "../../../integrations/logger"; +import { addSubscriber } from "../../../integrations/mailchimp"; + +const logger = appLogger.child({ mutation: "join-waitlist" }); + +const bodySchema = z.object({ + email: z.string().email(), +}); + +export default resolver.pipe(resolver.zod(bodySchema), async ({ email }, ctx) => { + try { + await addSubscriber(email); + } catch (error: any) { + logger.error(error.response?.data); + + if (error.response?.data.title !== "Member Exists") { + throw error; + } + } +}); diff --git a/app/landing-page/pages/index.tsx b/app/landing-page/pages/index.tsx index fd449e4..4152438 100644 --- a/app/landing-page/pages/index.tsx +++ b/app/landing-page/pages/index.tsx @@ -1,12 +1,11 @@ import type { BlitzPage } from "blitz"; import { Head } from "blitz"; -import { XIcon } from "@heroicons/react/outline"; import Header from "../components/header"; - -import iphoneMockup from "../images/iphone-mockup.png"; -import mockupImage from "../images/mockup-image-01.png"; import Checkmark from "../components/checkmark"; +import CTAForm from "../components/cta-form"; +import PhoneMockup from "../components/phone-mockup"; +import ReferralBanner from "../components/referral-banner"; const LandingPage: BlitzPage = () => { return ( @@ -30,9 +29,7 @@ const LandingPage: BlitzPage = () => {
- {/* Hero content */}
- {/* Content */}

@@ -45,24 +42,7 @@ const LandingPage: BlitzPage = () => { Coming soon! 🐚 Keep your phone number and pay less for your communications, even abroad.

- {/* CTA form */} -
- - {/* Success message */} - {/*

Thanks for subscribing!

*/} -
+
  • @@ -79,77 +59,7 @@ const LandingPage: BlitzPage = () => {

- {/* Mobile mockup */} -
-
- {/* Glow illustration */} - - {/* Image inside mockup size: 290x624px (or 580x1248px for Retina devices) */} - Features illustration - {/* iPhone mockup */} - -
-
+
@@ -161,40 +71,6 @@ const LandingPage: BlitzPage = () => { ); }; -function ReferralBanner() { - const isDisabled = true; - if (isDisabled) { - return null; - } - - return ( -
-
-
-

- 🎉 New: Get one month free for every friend that joins and subscribe! - - - {" "} - Learn more - - -

-
-
- -
-
-
- ); -} - LandingPage.suppressFirstRenderFlicker = true; export default LandingPage; diff --git a/app/messages/api/webhook/incoming-message.ts b/app/messages/api/webhook/incoming-message.ts index 7906f97..6b3035f 100644 --- a/app/messages/api/webhook/incoming-message.ts +++ b/app/messages/api/webhook/incoming-message.ts @@ -2,12 +2,16 @@ import type { BlitzApiRequest, BlitzApiResponse } from "blitz"; import { getConfig } from "blitz"; import twilio from "twilio"; -import type { ApiError } from "../../../api/_types"; import appLogger from "../../../../integrations/logger"; import db from "../../../../db"; import insertIncomingMessageQueue from "../queue/insert-incoming-message"; import notifyIncomingMessageQueue from "../queue/notify-incoming-message"; +type ApiError = { + statusCode: number; + errorMessage: string; +}; + const logger = appLogger.child({ route: "/api/webhook/incoming-message" }); const { serverRuntimeConfig } = getConfig(); diff --git a/app/api/newsletter/_mailchimp.ts b/integrations/mailchimp.ts similarity index 100% rename from app/api/newsletter/_mailchimp.ts rename to integrations/mailchimp.ts