Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ NEXTAUTH_SECRET="<a-generated-passphrase>"
EMAIL_SERVER="smtp://<your-email-smtp-info>"
FROM_EMAIL="investors@tincre.com"
NEXTAUTH_URL=http://localhost:3000
CONVERTKIT_API_URL=https://api.convertkit.com/v3/
CONVERTKIT_API_KEY=<your-api-key>
CONVERTKIT_FORM_ID=<your-form-id>
```

### Tests
Expand All @@ -43,6 +46,13 @@ repository. In addition, you'll need to edit `siteMetadata.js` for proper SEO up

> ℹ We are upgrading and standardizing the naming conventions so that they may be updated without examining the component codebase. ℹ

### Newsletter signup

The `Footer` component includes a signup for a newsletter through ConvertKit.

Feel free to replace or add to the functionality in `pages/api/convertkit.js`
for additional providers (such as Buttowndown or Mailchip).

### Database Infrastructure

We generally use PostgreSQL databases via ORMs at Tincre.
Expand Down
75 changes: 67 additions & 8 deletions components/Footer.jsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,41 @@
import FooterColumn from "./FooterColumn";
import Image from "next/image";
import { useState, useRef } from "react";

export default function Footer({ entityTitle, logoSrc, footerItems, socials }) {
const inputEl = useRef(null);
const [inputError, setInputError] = useState(false);
const [message, setMessage] = useState("");
const [subscribed, setSubscribed] = useState(false);

const subscribe = async (e) => {
e.preventDefault();

const res = await fetch(`/api/convertkit`, {
body: JSON.stringify({
email: inputEl.current.value,
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});

const { error } = await res.json();
if (error) {
setInputError(true);
setMessage(
"Your e-mail address is invalid or you are already subscribed!"
);
return;
}

inputEl.current.value = "";
setInputError(false);
setSubscribed(true);
setMessage("Successfully! 🎉 You are now subscribed.");
};

return (
<section id="footer" className="py-20">
<div className="container px-4 mx-auto">
Expand All @@ -23,14 +57,39 @@ export default function Footer({ entityTitle, logoSrc, footerItems, socials }) {
</a>
.
</p>
<input
className="w-full lg:w-2/3 mb-4 pl-4 py-3 mr-4 border border-2 rounded"
type="email"
placeholder="Type your e-mail"
/>
<button className="inline-block px-5 py-3 text-sm bg-indigo-500 hover:bg-indigo-600 text-white font-semibold border border-indigo-500 hover:border-indigo-600 rounded transition duration-200">
Sign up
</button>
<form onSubmit={subscribe}>
<label htmlFor="email-input" className="sr-only">
Email address
</label>
<input
className="w-full lg:w-2/3 mb-4 pl-4 py-3 mr-4 border border-2 rounded"
id="email-input"
name="email"
placeholder={
subscribed ? "You're subscribed ! 🎉" : "Enter your email"
}
ref={inputEl}
required
type="email"
disabled={subscribed}
/>
<button
className={`inline-block px-5 py-3 text-sm bg-indigo-500 hover:bg-indigo-600 text-white font-semibold border border-indigo-500 hover:border-indigo-600 rounded transition duration-200 ${
subscribed
? "cursor-default"
: "hover:bg-indigo-700 dark:hover:bg-indigo-300"
} focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-600 dark:ring-offset-black`}
type="submit"
disabled={subscribed}
>
{subscribed ? "Thank you!" : "Sign up"}
</button>
{inputError && (
<div className="pt-2 text-sm text-red-500 w-72 sm:w-96 dark:text-red-400">
{message}
</div>
)}
</form>
</div>
</div>
</div>
Expand Down
35 changes: 35 additions & 0 deletions pages/api/convertkit.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/* eslint-disable import/no-anonymous-default-export */
export default async (req, res) => {
const {email} = req.body;

if (!email) {
return res.status(400).json({error : 'Email is required'});
}

try {
const FORM_ID = process.env.CONVERTKIT_FORM_ID;
const API_KEY = process.env.CONVERTKIT_API_KEY;
const API_URL = process.env.CONVERTKIT_API_URL;

// Send request to ConvertKit
const data = {email, api_key : API_KEY};

const response = await fetch(`${API_URL}forms/${FORM_ID}/subscribe`, {
body : JSON.stringify(data),
headers : {
'Content-Type' : 'application/json',
},
method : 'POST',
});

if (response.status >= 400) {
return res.status(400).json({
error : `There was an error subscribing to the list.`,
});
}

return res.status(201).json({error : ''});
} catch (error) {
return res.status(500).json({error : error.message || error.toString()});
}
};
32 changes: 0 additions & 32 deletions pages/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,40 +38,8 @@ export default function Funded() {
const [state, setState] = useState(null);
const [decoded, setDecoded] = useState(null);

const inputEl = useRef(null);
const [inputError, setInputError] = useState(false);
const [message, setMessage] = useState("");
const [subscribed, setSubscribed] = useState(false);
const { data: session, status } = useSession();
const hostname = "investor.tincre.com";
const subscribe = async (e) => {
e.preventDefault();

const res = await fetch(`/api/convertkit`, {
body: JSON.stringify({
email: inputEl.current.value,
}),
headers: {
"Content-Type": "application/json",
},
method: "POST",
});

const { error } = await res.json();
if (error) {
setInputError(true);
setMessage(
"Your e-mail address is invalid or you are already subscribed!"
);
return;
}

inputEl.current.value = "";
setInputError(false);
setSubscribed(true);
setMessage("Successfully! 🎉 You are now subscribed.");
};

const { data, error } = useSwr(
"/api/session",
{ data: { sessionId: state?.sessionId } },
Expand Down