CLASSROOMIO-T-213: Replace all email notifications with Nodemailer

This commit is contained in:
Rotimi Best
2023-10-07 06:08:08 +01:00
parent c000d1ddb1
commit e0812c66c1
35 changed files with 203 additions and 720 deletions
@@ -1,10 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import JoinRequestTemplate from './template.svelte';
import { getSendgrid, SENDGRID_FROM_NOREPLY } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
export async function POST({ request }) {
@@ -35,23 +32,19 @@ export async function POST({ request }) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const options = {
from: SENDGRID_FROM_NOREPLY,
await sendEmail({
from: `"${orgName} - ClassroomIO" <[email protected]>`,
to,
replyTo: teacherEmail,
subject: `[${courseName}] One more step left`,
html: render({
template: JoinRequestTemplate,
props: {
courseName,
studentFullname,
teacherEmail,
orgName
}
})
};
sendgrid.send(options);
content: `
<p>Hi ${studentFullname},</p>
<p>You are one step closer to joining: ${courseName}</p>
<p>Please send your proof of payment to: ${teacherEmail}, in order to join the course.</p>
<p>Talk to you soon and see you in class.</p>
<p>${orgName}</p>
`
});
return json({
success: true,
@@ -1,37 +0,0 @@
<script lang="ts">
import { Text } from 'svelte-email';
export let courseName = '';
export let studentFullname = '';
export let teacherEmail = '';
export let orgName = '';
const fontFamily =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif';
const paragraph = {
fontFamily,
fontSize: '16px',
lineHeight: '26px'
};
const paragraphContent = {
...paragraph,
margin: '20px 0'
};
const footerParagraph = {
...paragraph,
margin: '0px'
};
</script>
<Text style={paragraph}>Hi {studentFullname},</Text>
<Text style={paragraphContent}>
You are one step closer to joining: {courseName}
</Text>
<Text style={paragraphContent}>
Please send your proof of payment to: {teacherEmail}, in order to join the course.
</Text>
<Text style={footerParagraph}>Talk to you soon and see you in class.</Text>
<Text style={footerParagraph}>{orgName}</Text>
@@ -1,19 +0,0 @@
<script lang="ts">
import EmailTemplate from '$lib/components/Email/EmailTemplate.svelte';
import ProvePayment from './prove_payment.svelte';
export let courseName = '';
export let studentFullname = '';
export let orgName = '';
export let teacherEmail = '';
</script>
<EmailTemplate
children={ProvePayment}
props={{
courseName,
studentFullname,
orgName,
teacherEmail
}}
/>
@@ -1,10 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import WelcomeTemplate from './template.svelte';
import { getSendgrid, SENDGRID_FROM_NOREPLY } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
// Sends email to student when they successfully join a course
@@ -29,20 +26,19 @@ export async function POST({ request }) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const options = {
from: SENDGRID_FROM_NOREPLY,
await sendEmail({
from: `"${orgName} - ClassroomIO" <[email protected]>`,
to,
subject: `[${orgName}] Welcome to Class`,
html: render({
template: WelcomeTemplate,
props: {
orgName,
courseName
}
})
};
sendgrid.send(options);
subject: `[${orgName}] Welcome to Class 🎉`,
content: `
<p>Hi there,</p>
<p>Congratulations 🎉, you’ve successfully joined: <strong>${courseName}</strong></p>
<p>Everything has been set up for you to have an amazing learning experience.</p>
<p>If you run into any issues, don’t fail to reach out to your instructor(s).</p>
<p>Cheers,</p>
<p>${orgName}</p>
`
});
return json({
success: true,
@@ -1,15 +0,0 @@
<script lang="ts">
import EmailTemplate from '$lib/components/Email/EmailTemplate.svelte';
import Welcome from './welcome.svelte';
export let orgName = '';
export let courseName = '';
</script>
<EmailTemplate
children={Welcome}
props={{
orgName,
courseName
}}
/>
@@ -1,37 +0,0 @@
<script lang="ts">
import { Text } from 'svelte-email';
export let orgName = '';
export let courseName = '';
const fontFamily =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif';
const paragraph = {
fontFamily,
fontSize: '16px',
lineHeight: '26px'
};
const paragraphContent = {
...paragraph,
margin: '20px 0'
};
const footerParagraph = {
...paragraph,
margin: '0px'
};
</script>
<Text style={paragraph}>Hey there,</Text>
<Text style={paragraphContent}>
Congratulations, you’ve successfully joined “{courseName}"
</Text>
<Text style={paragraphContent}>
Everything has been set up for you to have an amazing learning experience.
</Text>
<Text style={paragraphContent}>
If you run into any issues, don’t fail to reach out to your instructor(s).
</Text>
<Text style={footerParagraph}>Cheers,</Text>
<Text style={footerParagraph}>{orgName}</Text>
@@ -1,10 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import JoinRequestTemplate from './template.svelte';
import { getSendgrid, SENDGRID_FROM_NOREPLY } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
export async function POST({ request }) {
@@ -34,21 +31,20 @@ export async function POST({ request }) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const options = {
from: SENDGRID_FROM_NOREPLY,
await sendEmail({
from: `ClassroomIO" <[email protected]>`,
to,
subject: `[${courseName}] Request to Join Course!`,
html: render({
template: JoinRequestTemplate,
props: {
courseName,
studentFullname,
studentEmail
}
})
};
sendgrid.send(options);
content: `
<p>Hi amazing tutor,</p>
<p> A new student has requested to join a course you are teaching: “${courseName}"</p>
<p style="font: bold;">Student details</p>
<p>
Name: ${studentFullname}<br />
Email: ${studentEmail}
</p>
`
});
return json({
success: true,
@@ -1,44 +0,0 @@
<script lang="ts">
import { Text, Link } from 'svelte-email';
export let courseName = '';
export let studentFullname = '';
export let studentEmail = '';
const fontFamily =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif';
const paragraph = {
fontFamily,
fontSize: '16px',
lineHeight: '26px'
};
const paragraphContent = {
...paragraph,
margin: '20px 0'
};
const footerParagraph = {
...paragraph,
margin: '0px'
};
const linkStyle = {
color: '#1d4ee2'
};
</script>
<Text style={paragraph}>Hi amazing tutor,</Text>
<Text style={paragraphContent}>
A new student has requested to join a course you are teaching: “{courseName}"
</Text>
<Text style={paragraphContent}>Student details</Text>
<Text style={paragraphContent}>
Name: {studentFullname}<br />
Email: {studentEmail}
</Text>
<Text style={footerParagraph}>Cheers</Text>
<Text style={footerParagraph}>
<Link style={linkStyle} href="https://classroomio.com">ClassroomIO</Link>
</Text>
@@ -1,17 +0,0 @@
<script lang="ts">
import EmailTemplate from '$lib/components/Email/EmailTemplate.svelte';
import JoinRequest from './join_request.svelte';
export let courseName = '';
export let studentFullname = '';
export let studentEmail = '';
</script>
<EmailTemplate
children={JoinRequest}
props={{
courseName,
studentFullname,
studentEmail
}}
/>
@@ -1,10 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import WelcomeTemplate from './template.svelte';
import { getSendgrid, SENDGRID_FROM_NOREPLY } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
export async function POST({ request }) {
@@ -28,19 +25,18 @@ export async function POST({ request }) {
return json({ success: false, message: 'Unauthenticated user' }, { status: 401 });
}
const options = {
from: SENDGRID_FROM_NOREPLY,
await sendEmail({
from: `"ClassroomIO" <[email protected]>`,
to,
subject: `[${courseName}] You've got a new student!`,
html: render({
template: WelcomeTemplate,
props: {
courseName
}
})
};
sendgrid.send(options);
subject: `[${courseName}] You've got a new student 🎉!`,
content: `
<p>Hi amazing tutor,</p>
<p>Congratulations 🎉, a new student has joined a course you are teaching: ${courseName}</p>
<p>We hope they have a great experience learning from the best (YOU).</p>
<p>If you run into any issues, please don’t fail to reach out to us, we’d love to make your teaching
experience as easy as possible.</p>
`
});
return json({
success: true,
@@ -1,13 +0,0 @@
<script lang="ts">
import EmailTemplate from '$lib/components/Email/EmailTemplate.svelte';
import Welcome from './welcome.svelte';
export let courseName = '';
</script>
<EmailTemplate
children={Welcome}
props={{
courseName
}}
/>
@@ -1,44 +0,0 @@
<script lang="ts">
import { Text, Link } from 'svelte-email';
export let courseName = '';
const fontFamily =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif';
const paragraph = {
fontFamily,
fontSize: '16px',
lineHeight: '26px'
};
const paragraphContent = {
...paragraph,
margin: '20px 0'
};
const footerParagraph = {
...paragraph,
margin: '0px'
};
const linkStyle = {
color: '#1d4ee2'
};
</script>
<Text style={paragraph}>Hi amazing tutor,</Text>
<Text style={paragraphContent}>
Congratulations, a new student has joined a course you are teaching: “{courseName}"
</Text>
<Text style={paragraphContent}>
We hope they have a great experience learning from the best (YOU).
</Text>
<Text style={paragraphContent}>
If you run into any issues, please don’t fail to reach out to us, we’d love to make your teaching
experience as easy as possible.
</Text>
<Text style={footerParagraph}>Cheers</Text>
<Text style={footerParagraph}>
<Link style={linkStyle} href="https://classroomio.com">ClassroomIO</Link>
</Text>
@@ -1,10 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import WelcomeTemplate from './template.svelte';
import { getSendgrid, SENDGRID_FROM_NOREPLY } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
export async function POST({ request }) {
@@ -31,22 +28,19 @@ export async function POST({ request }) {
const origin = request.headers.get('origin');
const inviteLink = `${origin}/org/${orgSiteName}/courses`;
const options = {
from: SENDGRID_FROM_NOREPLY,
await sendEmail({
from: `"[${orgName}] - ClassroomIO" <[email protected]>`,
to,
subject: `You have been invited to a course in ${orgName}!`,
html: render({
template: WelcomeTemplate,
props: {
name,
orgName,
inviteLink,
courseName
}
})
};
sendgrid.send(options);
content: `
<p>Hey ${name},</p>
<p> You have been given access to teach a course by ${orgName}</p>
<p>The course is titled: ${courseName}</p>
<div>
<a class="button" href="${inviteLink}">Open Dashboard</a>
</div>
`
});
return json({
success: true,
@@ -1,19 +0,0 @@
<script lang="ts">
import EmailTemplate from '$lib/components/Email/EmailTemplate.svelte';
import Welcome from './welcome.svelte';
export let name = '';
export let orgName = '';
export let inviteLink = '';
export let courseName = '';
</script>
<EmailTemplate
children={Welcome}
props={{
name,
orgName,
inviteLink,
courseName
}}
/>
@@ -1,53 +0,0 @@
<script lang="ts">
import { Text, Section, Button, Link } from 'svelte-email';
export let name = '';
export let orgName = '';
export let inviteLink = '';
export let courseName = '';
const fontFamily =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif';
const paragraph = {
fontFamily,
fontSize: '16px',
lineHeight: '26px'
};
const paragraphContent = {
...paragraph,
margin: '20px 0'
};
const footerParagraph = {
...paragraph,
margin: '0px'
};
const btnContainer = {
textAlign: 'center' as const
};
const button = {
fontFamily,
backgroundColor: '#1d4ee2',
borderRadius: '5px',
color: '#fff',
fontSize: '16px',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block'
};
</script>
<Text style={paragraph}>Hey {name},</Text>
<Text style={paragraphContent}>
You have been given access to teach a course by {orgName}
</Text>
<Text style={paragraphContent}>
The course is titled: {courseName}
</Text>
<Section style={btnContainer}>
<Button pX={12} pY={12} style={button} href={inviteLink}>Open Dashboard</Button>
</Section>
+12 -17
View File
@@ -1,10 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import TeacherInviteTemplate from './template.svelte';
import { getSendgrid, SENDGRID_FROM_NOREPLY } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
// API to send invite to teacher
@@ -45,20 +42,18 @@ export async function POST({ request }) {
const inviteLink = `${origin}/invite/t/${encodeURIComponent(btoa(inviteData))}`;
console.log('inviteData', inviteData);
const options = {
from: SENDGRID_FROM_NOREPLY,
await sendEmail({
to: email,
subject: `Join ${name} on ClassroomIO`,
html: render({
template: TeacherInviteTemplate,
props: {
orgName: name,
inviteLink
}
})
};
sendgrid.send(options);
subject: `Join ${name} on ClassroomIO 😃`,
content: `
<p>Hey there,</p>
<p> You have been invited to join ${name} on ClassroomIO 🎉🎉🎉.</p>
<div>
<a class="button" href="${inviteLink}">Accept Invitation</a>
</div>
`,
isPersonalEmail: true
});
return json({
success: true,
@@ -1,57 +0,0 @@
<script lang="ts">
import { Text, Section, Button, Link } from 'svelte-email';
export let orgName = '';
export let inviteLink = '';
const fontFamily =
'-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,Oxygen-Sans,Ubuntu,Cantarell,"Helvetica Neue",sans-serif';
const paragraph = {
fontFamily,
fontSize: '16px',
lineHeight: '26px'
};
const paragraphContent = {
...paragraph,
margin: '20px 0'
};
const footerParagraph = {
...paragraph,
margin: '0px'
};
const btnContainer = {
textAlign: 'center' as const
};
const button = {
fontFamily,
backgroundColor: '#1d4ee2',
borderRadius: '5px',
color: '#fff',
fontSize: '16px',
textDecoration: 'none',
textAlign: 'center' as const,
display: 'block'
};
const linkStyle = {
color: '#1d4ee2'
};
</script>
<Text style={paragraph}>Hey there,</Text>
<Text style={paragraphContent}>
You have been invited to join {orgName} on ClassroomIO.
</Text>
<Section style={btnContainer}>
<Button pX={12} pY={12} style={button} href={inviteLink}>Accept Invitation</Button>
</Section>
<Text style={footerParagraph}>Best regards</Text>
<Text style={footerParagraph}>Rotimi Best</Text>
<Text style={footerParagraph}
><Link style={linkStyle} href="https://classroomio.com">ClassroomIO</Link></Text
>
@@ -1,15 +0,0 @@
<script lang="ts">
import EmailTemplate from '$lib/components/Email/EmailTemplate.svelte';
import Invitation from './invitation.svelte';
export let orgName = '';
export let inviteLink = '';
</script>
<EmailTemplate
children={Invitation}
props={{
orgName,
inviteLink
}}
/>
+2 -5
View File
@@ -1,11 +1,7 @@
import { render } from 'svelte-email';
import { json } from '@sveltejs/kit';
import { getSendgrid, SENDGRID_FROM } from '$lib/utils/services/sendgrid';
import { getSupabase } from '$lib/utils/functions/supabase';
import { sendEmail } from '$lib/utils/services/notification/send';
const sendgrid = getSendgrid();
const supabase = getSupabase();
export async function POST({ request }) {
@@ -50,7 +46,8 @@ export async function POST({ request }) {
<div>
<a href="https://app.classroomio.com">Go to Dashboard</a>
</div>
`
`,
isPersonalEmail: true
});
return json({
+9 -4
View File
@@ -10,7 +10,11 @@
import type { CurrentOrg } from '$lib/utils/types/org.js';
import { ROLE } from '$lib/utils/constants/roles';
import { profile } from '$lib/utils/store/user.js';
import { sendStudentCourseWelcome, sendTeacherStudentJoinedCourse } from './utils';
import {
triggerSendEmail,
NOTIFICATION_NAME
} from '$lib/utils/services/notification/notification';
import { snackbar } from '$lib/components/Snackbar/store.js';
export let data;
@@ -57,19 +61,20 @@
loading = false;
if (addedMember.error) {
console.error('Error adding student to group', data.groupId);
snackbar.error('Joining failed, please contact your admin');
return;
}
// Send email welcoming student to the course
sendStudentCourseWelcome({
triggerSendEmail(NOTIFICATION_NAME.STUDENT_COURSE_WELCOME, {
to: $profile.email,
orgName: data.currentOrg?.name,
courseName: data.name
});
// Send notification to teacher(s) that a student has joined the course.
sendTeacherStudentJoinedCourse({
to: teachers,
triggerSendEmail(NOTIFICATION_NAME.TEACHER_STUDENT_JOINED, {
to: teachers[0],
courseName: data.name
});
-56
View File
@@ -1,56 +0,0 @@
import { supabase } from '$lib/utils/functions/supabase';
export async function sendStudentCourseWelcome(params: {
to: string;
orgName: string;
courseName: string;
}) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/email/course/student_welcome', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: accessToken
},
body: JSON.stringify(params)
});
if (!response.ok) {
throw new Error(await response.text());
}
const result = await response.json();
console.log('Sent invite email', result);
} catch (error) {
console.error('Error sending invite email', error);
}
}
export async function sendTeacherStudentJoinedCourse(params: {
to: Array<string>;
courseName: string;
}) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/email/course/teacher_student_joined', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: accessToken
},
body: JSON.stringify(params)
});
if (!response.ok) {
throw new Error(await response.text());
}
const result = await response.json();
console.log('Sent invite email', result);
} catch (error) {
console.error('Error sending invite email', error);
}
}
+1 -1
View File
@@ -47,7 +47,7 @@
<section class="max-w-6xl gap-5 mx-auto">
<div class="m-5">
<h1 class="text-3xl font-semibold">Dashboard</h1>
<h1 class="text-3xl font-semibold">Welcome</h1>
<div
class="w-full h-fit lg:h-[265px] flex md:items-center justify-between flex-col-reverse md:flex-row p-5 lg:p-10 rounded-md bg-primary-900 my-2"
>