Skip to content

Commit

Permalink
create subscribe form
Browse files Browse the repository at this point in the history
  • Loading branch information
RyanClementsHax committed May 13, 2024
1 parent d708773 commit cbaa548
Show file tree
Hide file tree
Showing 5 changed files with 168 additions and 5 deletions.
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,14 @@

1. (Recommended) Use VSCode and install the recommended extensions
2. Run `npm run install` to install all of the dependencies
3. Create a `.env.production.local` file and put the following contents in it
3. Create a `.env.local` file and put the following keys in it

```shell
BEEHIIV_PUBLICATION_ID="<beehiiv publication id>"
BEEHIIV_API_KEY="<beehiiv api key>"
```

4. Create a `.env.production.local` file and put the following contents in it

```.env
NEXT_PUBLIC_VERCEL_URL=ryanclements.dev
Expand Down
76 changes: 76 additions & 0 deletions app/api/subscribe/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/* eslint-disable no-console */

export async function POST(request: Request): Promise<Response> {
const publicationId = process.env.BEEHIIV_PUBLICATION_ID
if (!publicationId) {
console.error('Must have BEEHIIV_PUBLICATION_ID defined')
return Response.json(
{
message: 'Oops! Something went wrong'
},
{
status: 500
}
)
}
const apiKey = process.env.BEEHIIV_API_KEY
if (!apiKey) {
console.error('Must have BEEHIIV_API_KEY defined')
return Response.json(
{
message: 'Oops! Something went wrong'
},
{
status: 500
}
)
}

const { email, referringUrl } = await request.json()
if (!email) {
return Response.json(
{
message: 'Please submit a valid email'
},
{
status: 400
}
)
}
const url = new URL(referringUrl)
// https://developers.beehiiv.com/docs/v2/1a77a563675ee-create
const response = await fetch(
`https://api.beehiiv.com/v2/publications/${publicationId}/subscriptions`,
{
method: 'POST',
headers: {
Authorization: `Bearer ${apiKey}`,
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify({
email,
reactivate_existing: true,
send_welcome_email: true,
utm_source: url.searchParams.get('ref') || url.host,
referring_site: referringUrl,
utm_medium: url.host
})
}
)
if (!response.ok) {
console.error(await response.json())
return Response.json(
{
message: 'Oops! Something went wrong.'
},
{
status: 500
}
)
} else {
return Response.json({
message: "You've been subscribed!"
})
}
}
70 changes: 70 additions & 0 deletions components/pages/posts/[slug]/SubscribeForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
'use client'

import { useState } from 'react'

interface FormState {
ok: boolean
message: string
}

const initialState: FormState = {
ok: true,
message: ''
}

export function SubscribeForm(): JSX.Element {
const [formState, setFormState] = useState(initialState)
return (
<form
className="card mx-auto flex flex-col gap-4 bg-surface-base-elevation-100 text-on-surface-base"
onSubmit={async e => {
e.preventDefault()
const email = e.currentTarget.email.value
const res = await fetch('/api/subscribe', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ email, referringUrl: window.location.href })
})
const { message } = await res.json()
setFormState({
ok: res.ok,
message
})
}}
>
<h2 className="text-xl">
Subscribe to receive more like this in your inbox.
</h2>
<p className="text-on-surface-base-muted">
No spam <span className="italic">ever.</span>
</p>
<div className="mx-auto flex gap-4">
<label htmlFor="email" className="sr-only">
Email
</label>
<input
required
type="text"
name="email"
className="overflow-hidden rounded-md border border-borderColor bg-surface-base px-3 py-2 text-left text-sm shadow-sm focus:border-borderColor-focus focus:outline-none focus:ring-1 focus:ring-ringColor-focus"
placeholder="Enter your email"
/>
<button className="rounded-md bg-accent-400 px-3 py-2 text-sm text-on-surface-accent shadow-sm hover:bg-surface-accent-hover focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-surface-accent">
Subscribe
</button>
</div>
{formState.message && (
<p
aria-live="polite"
className={`text-sm ${
formState.ok === true ? 'text-success' : 'text-danger'
}`}
>
{formState.message}
</p>
)}
</form>
)
}
8 changes: 4 additions & 4 deletions components/pages/posts/[slug]/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { Layout } from 'components/Layout'
import { A11yStaticImageData } from 'lib/content/images'
import Link from 'next/link'
import { ShareLinks } from 'components/ShareLinks'
import { SubscribeForm } from './SubscribeForm'

export interface PostDetailsProps {
post: RenderablePost
Expand Down Expand Up @@ -52,12 +53,11 @@ const ContentContainer: React.FC<{ children?: React.ReactNode }> = ({
const Closer: React.FC<{ title: string }> = ({ title }) => (
<section className="flex flex-col gap-12 text-center">
<hr className="border-borderColor" />
<p>
Did you enjoy the post? Consider supporting me and my tea addition 🤗🍵.
</p>
<SubscribeForm />
<p>You can also support me and my tea addition 🤗🍵.</p>
<BuyMeACoffeeButton />
<p className="mx-auto flex gap-2">
Or sharing with others
Or share with others
<ShareLinks title={title} />
</p>
</section>
Expand Down
10 changes: 10 additions & 0 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ export default {
offBase: colors.gray[200],
brand: primary.DEFAULT,
primary: primary[100],
accent: {
DEFAULT: colors.yellow[300],
hover: colors.yellow[500]
},
active: primary[100],
success: colors.green[100],
warning: colors.yellow[50],
Expand All @@ -78,6 +82,7 @@ export default {
},
brand: primary[100],
primary: primary[900],
accent: colors.yellow[900],
active: colors.zinc[900],
success: colors.green[900],
warning: colors.yellow[900],
Expand Down Expand Up @@ -138,6 +143,10 @@ export default {
offBase: colors.gray[800],
brand: primary[800],
primary: '#1f2330', // primary[900] @ 50% opacity on colors.zinc[900]
accent: {
DEFAULT: colors.amber[300],
hover: colors.amber[500]
},
active: primary[900],
success: '#172a21', // colors.green[800] @ 30% opacity on colors.zinc[900]
warning: '#392817', // colors.yellow[800] @ 50% opacity on colors.zinc[900]
Expand All @@ -155,6 +164,7 @@ export default {
},
brand: primary[100],
primary: primary[100],
accent: colors.amber[900],
active: primary[100],
success: colors.green[200],
warning: colors.yellow[200],
Expand Down

0 comments on commit cbaa548

Please sign in to comment.