From 9ba6aa19fc4ee771b6c117dcad6af569697b53a1 Mon Sep 17 00:00:00 2001
From: byohannes
Date: Sat, 13 Feb 2021 20:16:42 +0000
Subject: [PATCH 1/2] send email to all volunteers frontend
---
frontend/package-lock.json | 80 +++++++++++++++++++
frontend/package.json | 1 +
.../src/components/ClassCard/ClassCard.jsx | 24 +++++-
.../src/components/ClassCard/ClassCard.scss | 28 +++++--
.../src/components/ClassCard/EmailToAll.jsx | 56 +++++++++++++
.../src/components/ClassCard/EmailToAll.scss | 78 ++++++++++++++++++
frontend/src/scss/_buttons.scss | 20 +++++
7 files changed, 280 insertions(+), 7 deletions(-)
create mode 100644 frontend/src/components/ClassCard/EmailToAll.jsx
create mode 100644 frontend/src/components/ClassCard/EmailToAll.scss
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 202c8df..a473a01 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1761,6 +1761,14 @@
"resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.4.tgz",
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug=="
},
+ "@types/quill": {
+ "version": "1.3.10",
+ "resolved": "https://registry.npmjs.org/@types/quill/-/quill-1.3.10.tgz",
+ "integrity": "sha512-IhW3fPW+bkt9MLNlycw8u8fWb7oO7W5URC9MfZYHBlA24rex9rs23D5DETChu1zvgVdc5ka64ICjJOgQMr6Shw==",
+ "requires": {
+ "parchment": "^1.1.2"
+ }
+ },
"@types/react": {
"version": "16.9.44",
"resolved": "https://registry.npmjs.org/@types/react/-/react-16.9.44.tgz",
@@ -3680,6 +3688,11 @@
}
}
},
+ "clone": {
+ "version": "2.1.2",
+ "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz",
+ "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18="
+ },
"clone-deep": {
"version": "0.2.4",
"resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-0.2.4.tgz",
@@ -4032,6 +4045,15 @@
"sha.js": "^2.4.8"
}
},
+ "create-react-class": {
+ "version": "15.7.0",
+ "resolved": "https://registry.npmjs.org/create-react-class/-/create-react-class-15.7.0.tgz",
+ "integrity": "sha512-QZv4sFWG9S5RUvkTYWbflxeZX+JG7Cz0Tn33rQBJ+WFQTqTfUTjMjiv9tnfXazjsO5r0KhPs+AqCjyrQX6h2ng==",
+ "requires": {
+ "loose-envify": "^1.3.1",
+ "object-assign": "^4.1.1"
+ }
+ },
"cross-spawn": {
"version": "6.0.5",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-6.0.5.tgz",
@@ -5807,6 +5829,11 @@
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
},
+ "fast-diff": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.1.2.tgz",
+ "integrity": "sha512-KaJUt+M9t1qaIteSvjc6P3RbMdXsNhK61GRftR6SNxqmhthcd9MGIi4T+o0jD8LUSpSnSKXE20nLtJ3fOHxQig=="
+ },
"fast-glob": {
"version": "2.2.7",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.7.tgz",
@@ -9547,6 +9574,11 @@
"tslib": "^1.10.0"
}
},
+ "parchment": {
+ "version": "1.1.4",
+ "resolved": "https://registry.npmjs.org/parchment/-/parchment-1.1.4.tgz",
+ "integrity": "sha512-J5FBQt/pM2inLzg4hEWmzQx/8h8D0CiDxaG3vyp9rKrQRSDgBlhjdP5jQGgosEajXPSQouXGHOmVdgo7QmJuOg=="
+ },
"parent-module": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
@@ -10892,6 +10924,36 @@
"resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz",
"integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ=="
},
+ "quill": {
+ "version": "1.3.7",
+ "resolved": "https://registry.npmjs.org/quill/-/quill-1.3.7.tgz",
+ "integrity": "sha512-hG/DVzh/TiknWtE6QmWAF/pxoZKYxfe3J/d/+ShUWkDvvkZQVTPeVmUJVu1uE6DDooC4fWTiCLh84ul89oNz5g==",
+ "requires": {
+ "clone": "^2.1.1",
+ "deep-equal": "^1.0.1",
+ "eventemitter3": "^2.0.3",
+ "extend": "^3.0.2",
+ "parchment": "^1.1.4",
+ "quill-delta": "^3.6.2"
+ },
+ "dependencies": {
+ "eventemitter3": {
+ "version": "2.0.3",
+ "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-2.0.3.tgz",
+ "integrity": "sha1-teEHm1n7XhuidxwKmTvgYKWMmbo="
+ }
+ }
+ },
+ "quill-delta": {
+ "version": "3.6.3",
+ "resolved": "https://registry.npmjs.org/quill-delta/-/quill-delta-3.6.3.tgz",
+ "integrity": "sha512-wdIGBlcX13tCHOXGMVnnTVFtGRLoP0imqxM696fIPwIf5ODIYUHIvHbZcyvGlZFiFhK5XzDC2lpjbxRhnM05Tg==",
+ "requires": {
+ "deep-equal": "^1.0.1",
+ "extend": "^3.0.2",
+ "fast-diff": "1.1.2"
+ }
+ },
"raf": {
"version": "3.4.1",
"resolved": "https://registry.npmjs.org/raf/-/raf-3.4.1.tgz",
@@ -11175,6 +11237,11 @@
"scheduler": "^0.19.1"
}
},
+ "react-dom-factories": {
+ "version": "1.0.2",
+ "resolved": "https://registry.npmjs.org/react-dom-factories/-/react-dom-factories-1.0.2.tgz",
+ "integrity": "sha1-63cFxNs2+1AbOqOP91lhaqD/luA="
+ },
"react-error-overlay": {
"version": "6.0.8",
"resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-6.0.8.tgz",
@@ -11190,6 +11257,19 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
},
+ "react-quill": {
+ "version": "1.3.5",
+ "resolved": "https://registry.npmjs.org/react-quill/-/react-quill-1.3.5.tgz",
+ "integrity": "sha512-/W/rNCW+6QpGz8yQ9tFK5Ka/h/No1RqrcOOvCIOR092OiKzRFlU2xbPEwiP3Wgy/Dx13pi1YhjReDMX/5uotJg==",
+ "requires": {
+ "@types/quill": "1.3.10",
+ "create-react-class": "^15.6.0",
+ "lodash": "^4.17.4",
+ "prop-types": "^15.5.10",
+ "quill": "^1.3.7",
+ "react-dom-factories": "^1.0.0"
+ }
+ },
"react-redux": {
"version": "7.2.2",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.2.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 34e5f83..36fd74f 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -13,6 +13,7 @@
"react": "^16.14.0",
"react-dom": "^16.14.0",
"react-hook-form": "^6.10.1",
+ "react-quill": "^1.3.5",
"react-redux": "^7.2.2",
"react-router-dom": "^5.2.0",
"react-scripts": "^3.4.4",
diff --git a/frontend/src/components/ClassCard/ClassCard.jsx b/frontend/src/components/ClassCard/ClassCard.jsx
index ee85860..12cb60c 100644
--- a/frontend/src/components/ClassCard/ClassCard.jsx
+++ b/frontend/src/components/ClassCard/ClassCard.jsx
@@ -3,6 +3,7 @@ import { MonthNames } from "../../utils/MonthNames";
import { Link } from "react-router-dom";
import NewBookingForm from "../NewBookingForm/NewBookingForm.jsx";
import ClassVolunteersList from "../ClassVolunteersList/ClassVolunteersList.jsx";
+import EmailToAll from "./EmailToAll.jsx";
import axios from "axios";
import Alert from "../Alert/Alarm.jsx";
import Loading from "../Loading/Loading.jsx";
@@ -17,11 +18,16 @@ const ClassCard = ({ user, city, component, id, Class, WeekNumber }) => {
const [alertStatus, setAlertStatus] = useState(false);
const [alertType, setAlertType] = useState("");
const [alertMessage, setAlertMessage] = useState("message");
+ const [sendEmail, setSendEmail] = useState(false);
const closeConfirmationAlert = () => {
setCancelStatus(false);
};
+ const emailClose = () => {
+ setSendEmail(false);
+ };
+
const showAlert = (type, message) => {
setAlertStatus(true);
setAlertType(type);
@@ -180,6 +186,19 @@ const ClassCard = ({ user, city, component, id, Class, WeekNumber }) => {
{user === users[0].id && (
+ {component === "attendingvolunteers" && (
+ {
+ setSendEmail(true);
+ }}
+ className="classcard-email-bottom"
+ >
+
+ Send Email
+
+
+
+ )}
{
setCancelStatus(true);
@@ -234,6 +253,9 @@ const ClassCard = ({ user, city, component, id, Class, WeekNumber }) => {
)}
+ {sendEmail && (
+
+ )}
{!["upcomingclass", "coursecalendar", "overview"].includes(
component
) && }
@@ -263,4 +285,4 @@ const ClassCard = ({ user, city, component, id, Class, WeekNumber }) => {
);
};
-export default ClassCard;
+export default ClassCard;
\ No newline at end of file
diff --git a/frontend/src/components/ClassCard/ClassCard.scss b/frontend/src/components/ClassCard/ClassCard.scss
index f492bb5..c0bb384 100644
--- a/frontend/src/components/ClassCard/ClassCard.scss
+++ b/frontend/src/components/ClassCard/ClassCard.scss
@@ -3,6 +3,7 @@
.classcard-body {
animation: FadeIn 500ms;
+ transition: 200ms;
}
.classcard-main {
margin: 0 auto 30px auto;
@@ -106,7 +107,7 @@
font-size: 15px;
}
-.URLs-container{
+.URLs-container {
display: flex;
flex-wrap: nowrap;
}
@@ -213,6 +214,16 @@
cursor: default;
}
}
+.classcard-email-bottom {
+ @extend %btn-outline-success;
+ border-radius: 5px;
+ width: 60px;
+ height: 50px;
+ cursor: pointer;
+ margin-right: 10px;
+ font-size: 1rem;
+ outline: none;
+}
.classcard-cancel-bottom {
@extend %btn-outline-danger;
border-radius: 5px;
@@ -313,7 +324,8 @@
}
@media (min-width: 360px) {
- .classcard-cancel-bottom {
+ .classcard-cancel-bottom,
+ .classcard-email-bottom {
width: 90px;
}
.classcard-attend-bottom,
@@ -338,7 +350,8 @@
.classcard-cancel-bottom,
.classcard-edit-bottom,
- .classcard-attend-bottom {
+ .classcard-attend-bottom,
+ .classcard-email-bottom {
width: 150px;
}
}
@@ -408,13 +421,15 @@
}
.classcard-edit-bottom,
.classcard-attend-bottom,
+ .classcard-email-bottom,
.class-card-disable-attend-bottom {
width: 80px;
height: 50px;
font-size: 1rem;
}
- .classcard-cancel-bottom {
+ .classcard-cancel-bottom,
+ .classcard-email-bottom {
width: 80px;
height: 50px;
font-size: 1rem;
@@ -509,7 +524,8 @@
font-size: 1rem;
}
- .classcard-cancel-bottom {
+ .classcard-cancel-bottom,
+ .classcard-email-bottom {
width: 120px;
height: 50px;
font-size: 1rem;
@@ -543,4 +559,4 @@
cursor: default;
color: #aeaeae;
}
-}
+}
\ No newline at end of file
diff --git a/frontend/src/components/ClassCard/EmailToAll.jsx b/frontend/src/components/ClassCard/EmailToAll.jsx
new file mode 100644
index 0000000..c687497
--- /dev/null
+++ b/frontend/src/components/ClassCard/EmailToAll.jsx
@@ -0,0 +1,56 @@
+import React, { useRef } from "react";
+import ReactQuill from "react-quill";
+import axios from "axios";
+import "react-quill/dist/quill.snow.css";
+import "./EmailToAll.scss";
+
+const EmailToAll = ({ Class, emailClose }) => {
+ const emailText = useRef([]);
+ const sendEmail = async (e) => {
+ e.preventDefault();
+ if (
+ emailText.current[0].value === undefined ||
+ emailText.current[1].state.value === undefined ||
+ emailText.current[0].value === "" ||
+ emailText.current[1].state.value === "
"
+ ) {
+ alert("Please add your text to email!");
+ } else {
+
+ await axios.post("/api/v1/bookings/email/send", {
+ classId: Class._id,
+ subject: emailText.current[0].value,
+ emailText: emailText.current[1].state.value,
+ });
+ emailClose(false);
+ }
+ };
+ return (
+
+ );
+};
+
+export default EmailToAll;
\ No newline at end of file
diff --git a/frontend/src/components/ClassCard/EmailToAll.scss b/frontend/src/components/ClassCard/EmailToAll.scss
new file mode 100644
index 0000000..d456b0e
--- /dev/null
+++ b/frontend/src/components/ClassCard/EmailToAll.scss
@@ -0,0 +1,78 @@
+@import "../../scss/mixins";
+@import "../../scss/buttons";
+
+.email-body {
+ width: 60%;
+ height: 500px;
+ background-color: white;
+ border-radius: 10px;
+ margin: 50px 20% 0px 20%;
+ animation: FadeIn 500ms;
+}
+.close-bt {
+ width: 100%;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ i {
+ margin: 15px 15px 0px 0px;
+ font-size: 25px;
+ color: rgb(61, 61, 61);
+ transition: 200ms;
+ }
+ i:hover {
+ color: #dc3545;
+ transition: 200ms;
+ cursor: pointer;
+ }
+}
+.subject-email {
+ margin: 15px;
+ overflow-y: scroll;
+ width: calc(100% - 30px);
+ p {
+ margin-bottom: 10px;
+ font-family: "Open Sans", sans-serif;
+ }
+}
+.subject {
+ text-indent: 17px;
+ width: 100%;
+ height: 50px;
+ border: none;
+ border-top: 1px solid #cccccc;
+ border-right: 1px solid #cccccc;
+ border-bottom: 0px;
+ border-left: 1px solid #cccccc;
+ outline: none;
+}
+.app .ql-editor {
+ height: 220px;
+}
+.ql-editor {
+ overflow-y: auto;
+}
+
+.send{
+ @extend %btn-send ;
+ border-radius: 5px;
+ width: 120px;
+ height: 40px;
+ font-family: "Open Sans", sans-serif;
+ margin-left: 15px;
+ cursor: pointer;
+ font-size: 1rem;
+ outline: none;
+ }
+
+.cancel{
+ @extend %btn-cancel ;
+ border-radius: 5px;
+ width: 120px;
+ height: 40px;
+ font-family: "Open Sans", sans-serif;
+ margin-left: 15px;
+ cursor: pointer;
+ font-size: 1rem;
+ outline: none;
+ }
\ No newline at end of file
diff --git a/frontend/src/scss/_buttons.scss b/frontend/src/scss/_buttons.scss
index 14e2097..0e95dea 100644
--- a/frontend/src/scss/_buttons.scss
+++ b/frontend/src/scss/_buttons.scss
@@ -93,6 +93,26 @@
}
}
+%btn-send {
+ background-color: $btn-success-bg-hover;
+ border: 1px solid $btn-success-border;
+ color: white;
+ transition: 200ms;
+ &:hover {
+ background-color: rgb(0, 155, 0);
+ }
+}
+
+%btn-cancel {
+ background-color: white;
+ color: $btn-danger-border;
+ border: 1px solid $btn-danger-border;
+ transition: 200ms;
+ &:hover {
+ background-color: $btn-danger-bg-hover;
+ color: white;
+ }
+
%btn-no-bg {
background-color: transparent;
}
From 6517ba9dc681df7883fa15d2ee97742484856566 Mon Sep 17 00:00:00 2001
From: byohannes
Date: Sat, 13 Feb 2021 20:17:27 +0000
Subject: [PATCH 2/2] send email to all volunteers server api
---
controllers/bookings.js | 66 ++++++--
routes/bookings.js | 12 +-
.../Class.planner.admin.notification.email.js | 152 ++++++++++++++++++
....volunteer.confirmation.attending.email.js | 9 +-
utils/mailer.js | 84 +++++++---
utils/notification.js | 24 ++-
6 files changed, 296 insertions(+), 51 deletions(-)
create mode 100644 utils/emailTemplate/Class.planner.admin.notification.email.js
diff --git a/controllers/bookings.js b/controllers/bookings.js
index 11f7155..73f238d 100644
--- a/controllers/bookings.js
+++ b/controllers/bookings.js
@@ -1,6 +1,11 @@
-const Booking = require("../models/Booking");
-const validateBookingInput = require("../validation/booking");
-const { bookingConfirmationEmail } = require("../utils/notification");
+const Booking = require('../models/Booking');
+const validateBookingInput = require('../validation/booking');
+const {
+ bookingConfirmationEmail,
+ adminSendEmailToClassVolunteers,
+} = require('../utils/notification');
+const Class = require('../models/Class');
+const dayjs = require('dayjs');
exports.getBookings = async (req, res) => {
try {
const bookings = await Booking.find();
@@ -12,7 +17,7 @@ exports.getBookings = async (req, res) => {
} catch (err) {
return res.status(500).json({
success: false,
- error: "Server Error",
+ error: 'Server Error',
});
}
};
@@ -24,7 +29,7 @@ exports.getClassBookings = async (req, res) => {
if (!booking) {
return res.status(404).json({
success: false,
- error: "No booking found",
+ error: 'No booking found',
});
}
return res.status(200).json({
@@ -35,7 +40,7 @@ exports.getClassBookings = async (req, res) => {
} catch (err) {
return res.status(400).json({
success: false,
- error: "Could not get class bookings",
+ error: 'Could not get class bookings',
});
}
};
@@ -55,12 +60,12 @@ exports.createBooking = async (req, res) => {
return res.status(400).json({
success: false,
message:
- "Email already exists, You have already booked for this class, thanks!",
+ 'Email already exists, You have already booked for this class, thanks!',
});
} else if (err) {
return res.status(500).json({
success: false,
- error: "Server Error",
+ error: 'Server Error',
});
} else {
const allBooking = await Booking.find({ classId: req.body.classId });
@@ -73,6 +78,10 @@ exports.createBooking = async (req, res) => {
}
const newBooking = await Booking.create(req.body);
+ const classInfo = await Class.findById(req.body.classId);
+ newBooking.classDate = dayjs(classInfo.date).format('DD/MM/YYYY');
+ newBooking.classStartTime = classInfo.startTime;
+ newBooking.classEndTime = classInfo.endTime;
await bookingConfirmationEmail(newBooking);
return res.status(201).json({
success: true,
@@ -84,7 +93,7 @@ exports.createBooking = async (req, res) => {
} catch (err) {
return res.status(500).json({
success: false,
- error: "Server Error",
+ error: 'Server Error',
});
}
};
@@ -98,7 +107,7 @@ exports.deleteBooking = async (req, res) => {
if (!booking) {
return res.status(404).json({
success: false,
- error: "No booking found",
+ error: 'No booking found',
});
}
@@ -111,7 +120,7 @@ exports.deleteBooking = async (req, res) => {
} catch (err) {
return res.status(500).json({
success: false,
- error: "Server Error, could not delete booking",
+ error: 'Server Error, could not delete booking',
});
}
};
@@ -129,7 +138,7 @@ exports.updateBooking = async (req, res) => {
if (!booking) {
return res.status(400).json({
success: false,
- error: "booking not found!",
+ error: 'booking not found!',
});
}
return res.status(200).json({
@@ -139,7 +148,38 @@ exports.updateBooking = async (req, res) => {
} catch (err) {
return res.status(400).json({
success: false,
- error: "Could not update booking",
+ error: 'Could not update booking',
});
}
};
+
+exports.sendEmailToAllVolunteers = async (req, res) => {
+ try {
+ const emailData = req.body;
+ if (emailData.subject === '' || emailData.emailText === '') {
+ return res.status(400).json("'Subject or email can not be empty.'");
+ }
+ const classId = emailData.classId;
+ const classBookings = await Booking.find({ classId });
+ if (classBookings && !classBookings.length > 0) {
+ return res.status(400).json({
+ success: false,
+ message:
+ 'Sorry, there are no volunteers signed up for this class, thanks!',
+ });
+ }
+ const emails = [];
+ classBookings.forEach((booking) => emails.push(booking.email));
+ emailData.emails = emails;
+ await adminSendEmailToClassVolunteers(emailData);
+ return res.status(200).json({
+ success: true,
+ message: 'An email notification is sent to all volunteers, thanks!',
+ });
+ } catch (err) {
+ return res.status(400).json({
+ success: false,
+ error: 'Could not Send Emails',
+ });
+ }
+};
\ No newline at end of file
diff --git a/routes/bookings.js b/routes/bookings.js
index a96fd64..f547f63 100644
--- a/routes/bookings.js
+++ b/routes/bookings.js
@@ -1,4 +1,4 @@
-const express = require("express");
+const express = require('express');
const router = express.Router();
const {
getBookings,
@@ -6,14 +6,16 @@ const {
createBooking,
deleteBooking,
updateBooking,
-} = require("../controllers/bookings");
+ sendEmailToAllVolunteers,
+} = require('../controllers/bookings');
-router.route("/").get(getBookings).post(createBooking);
+router.route('/').get(getBookings).post(createBooking);
+router.route('/email/send').post(sendEmailToAllVolunteers);
router
- .route("/:id")
+ .route('/:id')
.delete(deleteBooking)
.put(updateBooking)
.get(getClassBookings);
-module.exports = router;
+module.exports = router;
\ No newline at end of file
diff --git a/utils/emailTemplate/Class.planner.admin.notification.email.js b/utils/emailTemplate/Class.planner.admin.notification.email.js
new file mode 100644
index 0000000..33ae673
--- /dev/null
+++ b/utils/emailTemplate/Class.planner.admin.notification.email.js
@@ -0,0 +1,152 @@
+exports.adminEmailNotification = (data) => {
+ return `
+
+
+
+
+
+ Admin Email Notification
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ `;
+ };
\ No newline at end of file
diff --git a/utils/emailTemplate/Class.planner.volunteer.confirmation.attending.email.js b/utils/emailTemplate/Class.planner.volunteer.confirmation.attending.email.js
index 0295b3c..1ca00e5 100644
--- a/utils/emailTemplate/Class.planner.volunteer.confirmation.attending.email.js
+++ b/utils/emailTemplate/Class.planner.volunteer.confirmation.attending.email.js
@@ -57,7 +57,12 @@ exports.volunteerConfirmationEmail = (data) => {
Dear ${data.fullName},
- This is a notification that you have signed up for a class as a ${data.roleName} .
+ This is a notification that you have signed up for a class as a ${data.roleName} on ${data.classDate} between ${data.classStartTime} and ${data.classEndTime} ,
+ add to calendar
+
+ add to calendar
+
{