-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
* Added admin page to send email * added send email api * added Circle Check icon * updated sengrid dependency * migrated API URLs to .env * debugged redirect to login in home * added table component * added admin page * updated API URL for sent status update * added sent field * added cron job to send email * added contact-stat schema * added API endpoints for contact-stats * updated cron job to interact with contact-stats collection * changed func name * migrated code to emailHandler * removed sent const * added Mass send feature * changed button hover color * added dependency * aded addEmailModal * added total number of emails added in response * add function add emails * implemented api endpoint * update UI when new email is added * added Delete endpoint and integration * added delete feature * improved UI * added more detailed alert * clear input field upon opening * renamed button * uncomment send email function call * updated email sender * added confirmation alert * renamed file and updated run instruction * updated more descriptive messege * updated dependency * deleted ununsed file * deleted unused import * added dependecy * return number of new emails added * send email using Sendgrid * Added email cron job * Checks if user is admin and redirects to email panel * Only allow admins on email page * added loading and unauthorized access html --------- Co-authored-by: shye <113232835+justshye@users.noreply.github.com>
- Loading branch information
Showing
10 changed files
with
756 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
const CONTACT_STAT_API_URL = process.env.NEXT_PUBLIC_CONTACT_STAT_API_URL; | ||
const SEND_EMAIL_API_URL = process.env.NEXT_PUBLIC_SEND_EMAIL_API_URL; | ||
|
||
const getContactStat = async () => { | ||
try { | ||
const response = await fetch(CONTACT_STAT_API_URL, { | ||
method: 'GET', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
}); | ||
|
||
const data = await response.json(); | ||
return data.contactStats; | ||
} catch (error) { | ||
console.error('Error fetching contact stats:', error); | ||
return []; | ||
} | ||
}; | ||
|
||
// Function to send emails | ||
const sendEmail = async recipient => { | ||
try { | ||
console.log(`Sending email to ${recipient}`); | ||
const apiUrl = SEND_EMAIL_API_URL; | ||
const response = await fetch(apiUrl, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ | ||
to: recipient, | ||
subject: 'Expand Your Hiring Reach with [Job Panel]!', | ||
html: ` | ||
<p>Hello from our team at [Job Panel],</p> | ||
<p>We are here to revolutionize your hiring process and help you reach a diverse pool of talent. Our web app offers a seamless solution to distribute your job postings to multiple platforms, including newcomers, indigenous communities, students, asylum-seekers, and individuals with disabilities.</p> | ||
<p>Here's what makes our job panel stand out:</p> | ||
<ul> | ||
<li><strong>Reach:</strong> Expand your reach and connect with a diverse range of candidates from various backgrounds and skillsets.</li> | ||
<li><strong>Simplicity:</strong> Easily distribute your job postings to multiple platforms with just a few clicks, saving you time and effort.</li> | ||
<li><strong>Diversity:</strong> Embrace diversity and inclusivity by reaching out to candidates from underrepresented communities.</li> | ||
<li><strong>Payment Plans:</strong> Choose from flexible payment plans that suit your budget and hiring needs.</li> | ||
</ul> | ||
<p>Ready to take your hiring to the next level? Sign up now via the link below and start reaching the talent you've been looking for!</p> | ||
<p><a href="https://job-bank-mu.vercel.app/" style="background-color: #4CAF50; color: white; padding: 10px 20px; text-decoration: none; border-radius: 5px;">Sign Up Now</a></p> | ||
<p>Don't miss out on the opportunity to find your next great hire effortlessly. Join us today and experience the power of our job panel!</p> | ||
<p>Best regards,</p> | ||
<p>The [Job Panel] Team</p>`, | ||
}), | ||
}); | ||
if (response.ok) { | ||
// Update the status of the email in the contactStat collection | ||
await updateSentStatus(recipient, true); | ||
} else { | ||
throw new Error('Failed to send email'); | ||
} | ||
} catch (error) { | ||
console.error('Error sending email:', error); | ||
} | ||
}; | ||
|
||
const updateSentStatus = async (email, sent) => { | ||
try { | ||
const response = await fetch(CONTACT_STAT_API_URL, { | ||
method: 'PATCH', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ email, sent }), | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to update sent status'); | ||
} | ||
} catch (error) { | ||
console.error('Error updating sent status:', error); | ||
} | ||
}; | ||
|
||
// Function to add email objects to the contactStat collection, take an array of email objects with email and sent properties | ||
const addEmailObjects = async emailObjects => { | ||
try { | ||
const response = await fetch(CONTACT_STAT_API_URL, { | ||
method: 'POST', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify(emailObjects), | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to add email objects'); | ||
} | ||
|
||
const responseBody = await response.json(); | ||
const { emailsAdded } = responseBody; | ||
return emailsAdded; | ||
} catch (error) { | ||
console.error('Error adding email objects:', error); | ||
} | ||
}; | ||
|
||
const deleteEmail = async email => { | ||
try { | ||
const response = await fetch(CONTACT_STAT_API_URL, { | ||
method: 'DELETE', | ||
headers: { | ||
'Content-Type': 'application/json', | ||
}, | ||
body: JSON.stringify({ email }), | ||
}); | ||
|
||
if (!response.ok) { | ||
throw new Error('Failed to delete email'); | ||
} | ||
} catch (error) { | ||
console.error('Error deleting email:', error); | ||
} | ||
}; | ||
|
||
const emailHandler = { | ||
getContactStat, | ||
sendEmail, | ||
updateSentStatus, | ||
addEmailObjects, | ||
deleteEmail, | ||
}; | ||
|
||
export default emailHandler; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
import { Libre_Franklin } from 'next/font/google'; | ||
|
||
const libre_franklin = Libre_Franklin({ | ||
subsets: ['latin'], | ||
display: 'swap', | ||
variable: '--font-libre_franklin', | ||
}); | ||
|
||
export default function Layout({ children }) { | ||
return ( | ||
<html lang="en"> | ||
<body className={libre_franklin.variable}>{children}</body> | ||
</html> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
'use client'; | ||
|
||
import React, { useState, useEffect, useCallback } from 'react'; | ||
import emailHandler from './emailHandler'; | ||
import { AdminPage } from '@/components/component/adminPage'; | ||
import Navbar from '@/components/ui/navbar'; | ||
|
||
export default function Home() { | ||
const [emails, setEmails] = useState([]); | ||
const [user, setUser] = useState(null); | ||
const [loading, setLoading] = useState(true); | ||
const [unauthorized, setUnauthorized] = useState(false); | ||
|
||
const links = [{ text: 'Logout', url: '/api/auth/logout' }]; | ||
|
||
const fetchUser = useCallback(async () => { | ||
try { | ||
const response = await fetch('/api/auth/me'); | ||
if (response.ok) { | ||
const userData = await response.json(); | ||
if (userData.data && userData.data.admin === true) { | ||
setUser(userData); | ||
console.log('User is admin'); | ||
} else { | ||
console.log('User is not admin'); | ||
setUnauthorized(true); | ||
setTimeout(() => { | ||
window.location.href = '/'; | ||
}, 3000); // Redirect after 3 seconds | ||
} | ||
} else { | ||
console.error('Failed to fetch user:', response.statusText); | ||
setUnauthorized(true); | ||
setTimeout(() => { | ||
window.location.href = '/'; | ||
}, 3000); // Redirect after 3 seconds | ||
} | ||
} catch (error) { | ||
console.error('Error fetching user:', error); | ||
setUnauthorized(true); | ||
setTimeout(() => { | ||
window.location.href = '/'; | ||
}, 3000); // Redirect after 3 seconds | ||
} finally { | ||
setLoading(false); | ||
} | ||
}, []); | ||
|
||
const fetchEmails = async () => { | ||
try { | ||
const data = await emailHandler.getContactStat(); | ||
setEmails(data); | ||
} catch (error) { | ||
console.error('Error fetching emails:', error); | ||
} | ||
}; | ||
|
||
useEffect(() => { | ||
fetchUser(); | ||
}, [fetchUser]); | ||
|
||
useEffect(() => { | ||
if (user) { | ||
fetchEmails(); | ||
} | ||
}, [user]); | ||
|
||
const updateEmails = (emailObj, isAdd) => { | ||
if (isAdd) { | ||
setEmails(prevEmails => [...prevEmails, emailObj]); | ||
} else if (!isAdd) { | ||
const updatedEmails = emails.filter( | ||
existingEmail => existingEmail.email !== emailObj.email | ||
); | ||
setEmails(updatedEmails); | ||
} | ||
}; | ||
|
||
const sendEmail = async recipient => { | ||
try { | ||
await emailHandler.sendEmail(recipient); | ||
} catch (error) { | ||
console.error('Error sending email:', error); | ||
} | ||
|
||
setEmails(prevEmails => | ||
prevEmails.map(emailObj => | ||
emailObj.email === recipient ? { ...emailObj, sent: true } : emailObj | ||
) | ||
); | ||
}; | ||
|
||
// Function to send emails to all recipients | ||
const massSendEmails = async () => { | ||
try { | ||
let emailSent = 0; | ||
// Loop through all emails and send email to each recipient | ||
for (const emailObj of emails) { | ||
// if email has not been sent, send email | ||
if (!emailObj.sent) { | ||
await sendEmail(emailObj.email); | ||
emailSent++; | ||
} | ||
} | ||
alert(`${emailSent} emails have been sent.`); | ||
} catch (error) { | ||
console.error('Error sending emails:', error); | ||
} | ||
}; | ||
|
||
if (loading) { | ||
return ( | ||
<div className="flex items-center justify-center min-h-screen"> | ||
<div className="text-xl">Loading...</div> | ||
</div> | ||
); | ||
} | ||
|
||
if (unauthorized) { | ||
return ( | ||
<div className="flex items-center justify-center min-h-screen"> | ||
<div className="text-xl text-red-600"> | ||
You do not have access to this page. Redirecting... | ||
</div> | ||
</div> | ||
); | ||
} | ||
|
||
return user ? ( | ||
<div> | ||
<Navbar links={links} /> | ||
<AdminPage | ||
data={emails} | ||
sendEmail={sendEmail} | ||
massSendEmails={massSendEmails} | ||
updateEmails={updateEmails} | ||
/> | ||
</div> | ||
) : null; | ||
} |
Oops, something went wrong.