Skip to content
Open
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
2 changes: 1 addition & 1 deletion app/(frontend)/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function NotFound() {
href="/"
className="inline-flex items-center gap-2 px-4 py-2 mt-4 text-xs font-bold text-white uppercase transition-colors bg-red-600 rounded-full w-max hover:bg-red-700"
>
Return Home <ArrowRightIcon size={16} />
Return Home <ArrowRightIcon />
</Link>
</div>
)
Expand Down
5 changes: 0 additions & 5 deletions app/(frontend)/renaissance/schedule/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,6 @@ export default function RenaissanceSpeakers() {
</h2>

<div className="space-y-4 text-renaissance-dark">
<div className="grid grid-cols-[100px_1fr] items-center gap-2 md:gap-4">
<time className="text-3xl font-renaissance">10:30am</time>
<p className="text-lg font-bold">Check-in Opens</p>
</div>

<div className="grid grid-cols-[100px_1fr] items-center gap-2 md:gap-4">
<time className="text-3xl font-renaissance">10:45am</time>
<p className="text-lg font-bold">Doors Open</p>
Expand Down
1 change: 1 addition & 0 deletions app/(frontend)/renaissance/speakers/page.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { ArrowRightIcon } from 'lucide-react'
import { Metadata } from 'next'
import Image, { StaticImageData } from 'next/image'
import Link from 'next/link'
import { Fragment } from 'react'
Expand Down
45 changes: 45 additions & 0 deletions app/api/subscribe/subscribe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// pages/api/subscribe.ts
import type { NextApiRequest, NextApiResponse } from 'next';
import mailchimp from '@mailchimp/mailchimp_marketing';

mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_API_KEY,
server: process.env.MAILCHIMP_SERVER_PREFIX,
});

async function subscribe(email: string) {
try {
const response = await mailchimp.lists.addListMember(process.env.MAILCHIMP_LIST_ID, {
email_address: email,
status: "pending", // Use "subscribed" for no double opt-in
});
return response;
} catch (error) {
throw error;
}
}

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method === 'POST') {
const { email_address } = req.body;

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

try {
const response = await subscribe(email_address);
res.status(200).json({ status: 'success', data: response });
} catch (error) {
// Log detailed error for server-side debugging
console.error(error);

// Send generic error message to client
res.status(500).json({ status: 'error', message: 'Failed to subscribe, please try again.' });
}
} else {
// If not a POST request, indicate allowed method
res.setHeader('Allow', ['POST']);
res.status(405).end('Method ${req.method} Not Allowed');
}
}
30 changes: 30 additions & 0 deletions app/mailinglist.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// mailingList.ts

import { z } from 'zod';
import { subscribeToMailingListService } from '@/components/services/MailingListService';

export async function subscribeToMailingList(formData: FormData) {
const schema = z.object({
email: z.string().email(),
});

const validatedFields = schema.safeParse({
email: formData.get('email'),
});

if (!validatedFields.success) {
return {
message: 'Failed to subscribe to newsletter',
errors: validatedFields.error.flatten().fieldErrors,
};
}

const { email } = validatedFields.data;

try {
await subscribeToMailingListService(email);
return { message: 'Subscribed to newsletter!', success: true };
} catch (error) {
return { message: 'Failed to subscribe to newsletter' };
}
}
2 changes: 1 addition & 1 deletion app/not-found.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ export default function NotFound() {
href="/"
className="inline-flex items-center gap-2 px-4 py-2 mt-4 text-xs font-bold text-white uppercase transition-colors bg-red-600 rounded-full w-max hover:bg-red-700"
>
Return Home <ArrowRightIcon size={16} />
Return Home <ArrowRightIcon />
</Link>
</div>
)
Expand Down
6 changes: 5 additions & 1 deletion components/global/Footer/FooterLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
} from '@icons-pack/react-simple-icons'
import Link from 'next/link'

import MailingListForm from '@/components/mailinglist/MailingListForm'

export default function FooterLayout() {
const year = new Date().getFullYear().toString()

Expand Down Expand Up @@ -96,7 +98,9 @@ export default function FooterLayout() {
</div>
</div>
</div>

<div className="flex flex-col items-center justify-between gap-4 pt-6 mt-6 border-t md:flex-row">
<MailingListForm />
</div>
<div className="flex flex-col items-center justify-between gap-4 pt-6 mt-6 border-t md:flex-row">
<p>&copy; {year} TEDxNortheasternU · All Rights Reserved</p>

Expand Down
87 changes: 87 additions & 0 deletions components/global/Footer/mail-signup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
'use client'

import React, { FormEvent, useState } from 'react'
const mailchimp = require('@mailchimp/mailchimp_marketing')

mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_API_KEY,
server: 'us17',
})

interface EmailState {
email: string
subscriptionResponse: string
error: boolean
}

const NewsLetterSignUpForm: React.FC = () => {
const [email, setEmail] = useState<EmailState>({
email: '',
subscriptionResponse: '',
error: false,
})

const run = async () => {
try {
console.log(email.email)
console.log(mailchimp.server)

const response = await mailchimp.lists.addListMember('5be1f99edd', {
email_address: email.email,
status: 'subscribed',
})
console.log(response)

setEmail({
email: '',
subscriptionResponse: 'Subscription successful!',
error: false,
})
} catch (error) {
console.error('Error subscribing to list:', error)
setEmail({
...email,
subscriptionResponse: 'Subscription failed. Please try again.',
error: true,
})
}
}

return (
<div>
<div className="pr-4 md:w-1/2">
<h2 className="mb-4 text-xl font-bold">Join our Mailing List!</h2>
<p className="text-sm">
TEDxNortheasternU was created in the spirit of TED’s mission of ideas
worth spreading to the Northeastern University community. Subscribe to
learn more about our xLabs, fundraisers, and Spring conference.
</p>
</div>

<form
onSubmit={(e: FormEvent) => {
e.preventDefault()
run()
}}
>
<div className={'inputContainer'}>
<input
type={'email'}
placeholder={'Please enter your email address'}
value={email.email}
onChange={(e) => setEmail({ ...email, email: e.target.value })}
required
/>
</div>

<button type={'submit'}>Notify me</button>

<p style={{ color: email.error ? 'red' : 'green' }}>
{email.subscriptionResponse}
</p>
</form>
</div>
)
}

export default NewsLetterSignUpForm
39 changes: 39 additions & 0 deletions components/mailinglist/MailingListForm.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// MailingListForm.tsx
'use client'

import React, { useState } from 'react';
import { subscribeToMailingListService } from '@/components/services/MailingListService';

export default function MailingListForm() {
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');
const [error, setError] = useState('');

const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault();
try {
const response = await subscribeToMailingListService(email);
setMessage(response.message);
setEmail('');
setError('');
} catch (error) {
setError('Failed to subscribe. Please try again.');
console.log(error)
}
};

return (
<form onSubmit={handleSubmit}>
<input
type="email"
value={email}
onChange={(e) => setEmail(e.target.value)}
placeholder="Your email address"
required
/>
<button type="submit">Subscribe</button>
{message && <p>{message}</p>}
{error && <p>{error}</p>}
</form>
);
}
9 changes: 9 additions & 0 deletions components/pages/api/subscribe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
/pages/api/subscribe.js

export default async function handler(req, res) {
const email = req.body;

if (!email || !email.length) {
res.status(200).json({ error: "Please enter a email address" });
}
}
41 changes: 41 additions & 0 deletions components/pages/api/subscribe.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import mailchimp from '@mailchimp/mailchimp_marketing'
import md5 from 'md5'
import { NextApiRequest, NextApiResponse } from 'next'

interface ResponseData {
message: string
}

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>,
) {
const { email_address, status } = req.body

mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_API_KEY,
server: process.env.MAILCHIMP_API_SERVER,
})

try {
await mailchimp.lists.addListMember(process.env.MAILCHIMP_AUDIENCE_ID, {
email_address,
status: status === 'subscribed' ? 'subscribed' : 'pending',
})

res.status(200).json({
message: 'You will receive article updates in ${email_address}',
})
} catch (err) {
let errorMessage = 'Error adding to mailing list'
if (err.response) {
try {
const errorResponse = JSON.parse(err.response.text)
errorMessage = errorResponse.title
} catch (parseError) {
// Fallback error message
}
}
return res.status(err.status || 500).json({ message: errorMessage })
}
}
41 changes: 41 additions & 0 deletions components/pages/api/subscribe.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import mailchimp from '@mailchimp/mailchimp_marketing'
import md5 from 'md5'
import { NextApiRequest, NextApiResponse } from 'next'

interface ResponseData {
message: string
}

export default async function handler(
req: NextApiRequest,
res: NextApiResponse<ResponseData>,
) {
const { email_address, status } = req.body

mailchimp.setConfig({
apiKey: process.env.MAILCHIMP_API_KEY,
server: process.env.MAILCHIMP_API_SERVER,
})

try {
await mailchimp.lists.addListMember(process.env.MAILCHIMP_AUDIENCE_ID, {
email_address,
status: status === 'subscribed' ? 'subscribed' : 'pending',
})

res.status(200).json({
message: 'You will receive article updates in ${email_address}',
})
} catch (err) {
let errorMessage = 'Error adding to mailing list'
if (err.response) {
try {
const errorResponse = JSON.parse(err.response.text)
errorMessage = errorResponse.title
} catch (parseError) {
// Fallback error message
}
}
return res.status(err.status || 500).json({ message: errorMessage })
}
}
34 changes: 34 additions & 0 deletions components/services/MailingListService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// MailingListService.ts

export async function subscribeToMailingListService(email: string): Promise<{ message: string }> {
const API_KEY = process.env.MAILCHIMP_API_KEY;
const listId = process.env.MAILCHIMP_API_SERVER;
const batchId = process.env.MAILCHIMP_AUDIENCE_ID;

const data = {
email_address: email,
status: 'subscribed' // 'subscribed' to add the user to the list
};

try {
const response = await fetch(`https://${listId}.api.mailchimp.com/3.0/lists/${batchId}/members`, {
body: JSON.stringify(data),
headers: {
Authorization: `apikey ${API_KEY}`,
'Content-Type': 'application/json',
},
method: 'POST',
});

const responseData = await response.json();

if (!response.ok) {
throw new Error(responseData.detail || 'Failed to subscribe to the mailing list');
}

return { message: 'Successfully subscribed to the mailing list' };
} catch (error) {
console.log(error)
throw new Error('Failed to subscribe to the mailing list');
}
}
Loading