Skip to content

Commit 021d5f3

Browse files
Merge pull request #225 from CodeChefVIT/subscribe-to-newsletter-button
Subscribe to newsletter button
2 parents fd77412 + 7eadf52 commit 021d5f3

File tree

4 files changed

+103
-4
lines changed

4 files changed

+103
-4
lines changed

.env.example

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,8 @@ KV_URL=""
2525
KV_REST_API_URL=""
2626
KV_REST_API_TOKEN=""
2727
KV_REST_API_READ_ONLY_TOKEN=""
28-
SERVER_URL=""
28+
SERVER_URL=""
29+
# Google Sheets Integration
30+
GOOGLE_CLIENT_EMAIL=""
31+
GOOGLE_PRIVATE_KEY=""
32+
SHEET_ID=""

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ yarn-error.log*
3434
# local env files
3535
# do not commit any .env files to git, except for the .env.example file. https://create.t3.gg/en/usage/env-variables#using-environment-variables
3636
.env
37+
.env.lcal
3738
.env*.local
3839

3940
# vercel

src/app/api/subscribe/route.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { NextResponse } from "next/server";
2+
import { google, sheets_v4 } from "googleapis";
3+
import { JWT } from "google-auth-library";
4+
5+
const SCOPES = ["https://www.googleapis.com/auth/spreadsheets"];
6+
const SHEET_ID = process.env.SHEET_ID;
7+
8+
async function getAuth(): Promise<JWT> {
9+
return new google.auth.JWT({
10+
email: process.env.GOOGLE_CLIENT_EMAIL,
11+
key: process.env.GOOGLE_PRIVATE_KEY?.replace(/\\n/g, "\n"),
12+
scopes: SCOPES,
13+
});
14+
}
15+
16+
async function appendEmailToSheet(email: string) {
17+
const authClient = await getAuth();
18+
const sheets: sheets_v4.Sheets = google.sheets({
19+
version: "v4",
20+
auth: authClient,
21+
});
22+
23+
await sheets.spreadsheets.values.append({
24+
spreadsheetId: SHEET_ID,
25+
range: "Sheet1!A:A",
26+
valueInputOption: "RAW",
27+
requestBody: {
28+
values: [[email]],
29+
},
30+
});
31+
}
32+
33+
export async function POST(req: Request) {
34+
try {
35+
const { email } = await req.json();
36+
37+
if (!email) {
38+
return NextResponse.json({ error: "Email is required" }, { status: 400 });
39+
}
40+
41+
await appendEmailToSheet(email);
42+
return NextResponse.json({ message: "Email added successfully" });
43+
} catch (error) {
44+
console.error("Error adding email:", error);
45+
return NextResponse.json({ error: "Failed to add email" }, { status: 500 });
46+
}
47+
}

src/components/Footer.tsx

Lines changed: 50 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Link from "next/link";
44
import { useTheme } from "next-themes";
55
import { useEffect, useState } from "react";
66
import { Button } from "./ui/button";
7+
import { Input } from "@/components/ui/input";
78
import {
89
FaFacebook,
910
FaGithub,
@@ -13,16 +14,40 @@ import {
1314
FaYoutube,
1415
} from "react-icons/fa6";
1516
import { Mail } from "lucide-react";
16-
17+
import toast from "react-hot-toast";
1718
export default function Footer() {
1819
const { theme } = useTheme();
1920
const [isDarkMode, setIsDarkMode] = useState<boolean | null>(true);
20-
21+
const [email, setEmail] = useState("");
2122
useEffect(() => {
2223
if (theme) {
2324
setIsDarkMode(theme === "dark");
2425
}
2526
}, [theme]);
27+
const handleSubscribe = async () => {
28+
if (!email || !email.includes("@")) {
29+
toast.error("Please Enter A Valid Email.");
30+
return;
31+
}
32+
33+
await toast.promise(
34+
fetch("/api/subscribe", {
35+
method: "POST",
36+
headers: { "Content-Type": "application/json" },
37+
body: JSON.stringify({ email }),
38+
}).then((res) => {
39+
if (!res.ok) throw new Error("Network response was not ok.");
40+
return res.json();
41+
}),
42+
{
43+
loading: "Subscribing...",
44+
success: "You've Successfully Subscribed!",
45+
error: "Something went wrong. Try again later.",
46+
}
47+
);
48+
49+
setEmail("");
50+
};
2651

2752
return (
2853
<footer className="w-full overflow-hidden bg-gradient-to-b from-[#F3F5FF] to-[#A599CE] px-12 py-12 font-sans text-white dark:from-[#070114] dark:to-[#1F0234]">
@@ -95,9 +120,31 @@ export default function Footer() {
95120
>
96121
97122
</Link>
123+
124+
<div className="mt-4 flex flex-col gap-2 w-full max-w-xs">
125+
<h3 htmlFor="email" className="font-jost text-2xl font-semibold">
126+
Subscribe For Updates:
127+
</h3>
128+
<div className="relative w-full">
129+
<Input
130+
id="email"
131+
type="email"
132+
placeholder="Enter Your Email"
133+
className="pr-24"
134+
value={email}
135+
onChange={(e) => setEmail(e.target.value)}
136+
/>
137+
<Button
138+
onClick={handleSubscribe}
139+
className="absolute right-0 top-0 h-full rounded-l-none rounded-r-md bg-[#562EE7] px-4 text-white hover:bg-[#4531b3]"
140+
>
141+
Subscribe!
142+
</Button>
143+
</div>
144+
</div>
98145
</div>
99146
</div>
100-
<p className="font-play mt-4 border-t border-[#130E1F] pt-12 text-center text-lg text-black dark:border-white/10 dark:text-white">
147+
<p className="font-play mt-8 border-t border-[#130E1F] pt-12 text-center text-lg text-black dark:border-white/10 dark:text-white">
101148
Made with ❤️ by Codechef-VIT
102149
</p>
103150
</footer>

0 commit comments

Comments
 (0)