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
@@ -5,12 +5,14 @@
import IconButton from '$lib/components/IconButton/index.svelte';
import { NavClasses } from '$lib/utils/constants/reusableClass';
import NavIcons from './NavIcons.svelte';
import { SkeletonPlaceholder, SkeletonText } from 'carbon-components-svelte';
export let handleClick = () => {};
export let label = '';
export let isGroupActive = false;
export let isExpanded = true;
export let total = 0;
export let isLoading = true;
// export let subMenuItems = [];
function onClick() {
@@ -23,32 +25,39 @@
</script>
<div class="">
<button
class="item relative flex items-center py-3 px-4 ml-2 mb-1 {NavClasses.item} {isGroupActive &&
NavClasses.active} w-[95%]"
tabindex="0"
on:click={onClick}
>
<NavIcons name={label} />
<span class="font-bold text-md">{label}</span>
{#if total}
<span class="ml-1">({total})</span>
{/if}
<span class="grow" />
{#if label === 'Lessons'}
<IconButton size="small" stopPropagation={true} onClick={toggleIsExpanded}>
{#if isExpanded}
<ChevronUpIcon class="carbon-icon dark:text-white" />
{:else}
<ChevronDownIcon class="carbon-icon dark:text-white" />
{/if}
</IconButton>
{/if}
</button>
{#if isExpanded}
<div in:slide out:slide class="flex flex-col">
<slot />
{#if isLoading}
<div class="w-11/12 mx-auto">
<SkeletonText class="py-3 px-4 mb-3 rounded-md" style="height: 40px;" />
</div>
{:else}
<button
class="item relative flex items-center py-3 px-4 ml-2 mb-1 {NavClasses.item} {isGroupActive &&
NavClasses.active} w-[95%]"
tabindex="0"
on:click={onClick}
disabled={isLoading}
>
<NavIcons name={label} />
<span class="font-bold text-md">{label}</span>
{#if total}
<span class="ml-1">({total})</span>
{/if}
<span class="grow" />
{#if label === 'Lessons' && !isLoading}
<IconButton size="small" stopPropagation={true} onClick={toggleIsExpanded}>
{#if isExpanded}
<ChevronUpIcon class="carbon-icon dark:text-white" />
{:else}
<ChevronDownIcon class="carbon-icon dark:text-white" />
{/if}
</IconButton>
{/if}
</button>
{#if isExpanded}
<div in:slide out:slide class="flex flex-col">
<slot />
</div>
{/if}
{/if}
</div>
@@ -255,6 +255,7 @@
isGroupActive={(path || $page.url.pathname) === navItem.to}
isExpanded={isLessonActive}
total={navItem.isLecture ? ($lessons || []).length : 0}
isLoading={!$course.id}
>
{#if navItem.isLecture}
{#each $lessons as item, index}
@@ -12,10 +12,12 @@
import { currentOrg } from '$lib/utils/store/org';
import { getOrgTeam } from '$lib/utils/services/org';
import type { OrgTeamMember } from '$lib/utils/types/org';
import { sendTeacherCourseWelcome } from './utils';
import type { Course } from '$lib/utils/types';
import { VARIANTS } from '$lib/components/PrimaryButton/constants';
import { getStudentInviteLink } from '$lib/utils/functions/course';
import {
triggerSendEmail,
NOTIFICATION_NAME
} from '$lib/utils/services/notification/notification';
interface Tutor {
id: number;
@@ -52,7 +54,7 @@
role_id: ROLE.TUTOR
});
sendTeacherCourseWelcome({
triggerSendEmail(NOTIFICATION_NAME.WELCOME_TEACHER_TO_COURSE, {
to: tutor.email,
name: tutor.text,
orgName: $currentOrg.name,
@@ -1,31 +0,0 @@
import { supabase } from '$lib/utils/functions/supabase';
export async function sendTeacherCourseWelcome(params: {
to: string;
name: string;
orgName: string;
orgSiteName: string;
courseName: string;
}) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/email/course/teacher_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);
}
}
@@ -4,8 +4,11 @@
import PrimaryButton from '$lib/components/PrimaryButton/index.svelte';
import { coursePaymentValidation } from '$lib/utils/functions/validator';
import { Link } from 'carbon-components-svelte';
import { sendTeacherStudentJoinRequest, sendStudentRequestReceipt } from './utils';
import { currentOrg } from '$lib/utils/store/org';
import {
triggerSendEmail,
NOTIFICATION_NAME
} from '$lib/utils/services/notification/notification';
export let open = false;
export let paymentLink = '';
@@ -41,7 +44,7 @@
step = STEPS.STEP_2;
// Send email to first added teacher of student wanting to join
sendTeacherStudentJoinRequest({
triggerSendEmail(NOTIFICATION_NAME.SEND_TEACHER_STUDENT_BUY_REQUEST, {
to: teacherEmail,
courseName,
studentEmail: fields.email,
@@ -54,7 +57,7 @@
function onClickPaymentLink() {
// Send email to student to send proof of payment to teacher
sendStudentRequestReceipt({
triggerSendEmail(NOTIFICATION_NAME.STUDENT_PROOVE_COURSE_PAYMENT, {
to: fields.email,
courseName,
teacherEmail,
@@ -1,60 +0,0 @@
import { supabase } from '$lib/utils/functions/supabase';
export async function sendTeacherStudentJoinRequest(params: {
to: string;
courseName: string;
studentEmail: string;
studentFullname: string;
}) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/email/course/teacher_student_buycourse', {
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 sendStudentRequestReceipt(params: {
to: string;
courseName: string;
orgName: string;
studentFullname: string;
teacherEmail: string;
}) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/email/course/student_prove_payment', {
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);
}
}
+9 -2
View File
@@ -1,4 +1,4 @@
<script>
<script lang="ts">
import { page } from '$app/stores';
import { browser } from '$app/environment';
import NotificationIcon from 'carbon-icons-svelte/lib/Notification.svelte';
@@ -28,6 +28,10 @@
localStorage.setItem('mode', $appStore.isDark ? 'dark' : '');
}
}
function shortenName(name: string) {
return name?.substring(0, 2)?.toUpperCase() || '';
}
</script>
<nav class="{navClass} flex w-full p-2 md:px-6 bg-primary-700 h-[48px]">
@@ -58,7 +62,10 @@
className="mr-2"
/>
{:else}
<TextChip value={$currentOrg.shortName} className="bg-primary-200 font-bold" />
<TextChip
value={shortenName($currentOrg.name)}
className="bg-primary-200 font-bold mr-2"
/>
{/if}
<span class="line-clamp-1">
{$currentOrg.name}
@@ -14,7 +14,10 @@
import { profile } from '$lib/utils/store/user';
import { supabase } from '$lib/utils/functions/supabase';
import SectionTitle from '../SectionTitle.svelte';
import { sendInvite } from './utils';
import {
triggerSendEmail,
NOTIFICATION_NAME
} from '$lib/utils/services/notification/notification';
let emailsStr = '';
let errorMessage = '';
@@ -68,11 +71,13 @@
]);
}
// Send email invite
sendInvite(email, {
id: $currentOrg.id,
name: $currentOrg.name,
siteName: $currentOrg.siteName
triggerSendEmail(NOTIFICATION_NAME.INVITE_TEACHER, {
email: newMember.email,
org: {
id: $currentOrg.id,
name: $currentOrg.name,
siteName: $currentOrg.siteName
}
});
const isLast = index === emails.length - 1;
-28
View File
@@ -1,28 +0,0 @@
import { supabase } from '$lib/utils/functions/supabase';
export async function sendInvite(email: string, org: any) {
const { data } = await supabase.auth.getSession();
const accessToken = data.session?.access_token || '';
try {
const response = await fetch('/api/email/invite', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: accessToken
},
body: JSON.stringify({
email,
org
})
});
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);
}
}