From bb974a1df92b4c8c040b60809d25dba913de4441 Mon Sep 17 00:00:00 2001 From: Filip Borum Poulsen Date: Tue, 25 Apr 2023 12:28:38 +0200 Subject: [PATCH] Added User and Trainer Moved and Cancelled email notifications --- server/src/interfaces/trainer.ts | 1 + server/src/mail/trainerCancelledOrder.ts | 34 +++++++ server/src/mail/trainerMovedOrder.ts | 43 ++++++++ server/src/mail/userCancelledOrder.ts | 12 +++ server/src/mail/userMovedOrder.ts | 28 +++++- server/src/routes/order.ts | 3 +- server/src/routes/orderNewTime.ts | 8 +- server/src/routes/trainer/order.ts | 122 +++++++++++++++++------ 8 files changed, 214 insertions(+), 37 deletions(-) create mode 100644 server/src/mail/trainerCancelledOrder.ts create mode 100644 server/src/mail/trainerMovedOrder.ts diff --git a/server/src/interfaces/trainer.ts b/server/src/interfaces/trainer.ts index d250dbb..731465a 100644 --- a/server/src/interfaces/trainer.ts +++ b/server/src/interfaces/trainer.ts @@ -4,5 +4,6 @@ interface Trainer { last_name: string center_id: number center_name: string + email?: string } export default Trainer; \ No newline at end of file diff --git a/server/src/mail/trainerCancelledOrder.ts b/server/src/mail/trainerCancelledOrder.ts new file mode 100644 index 0000000..6b3498c --- /dev/null +++ b/server/src/mail/trainerCancelledOrder.ts @@ -0,0 +1,34 @@ +import { transporter } from "."; +import { smtpAddress } from "../environment"; +import { Order } from "../interfaces/order"; +import User from "../interfaces/user"; +import { formatDate, formatTime } from "../utils/dates"; + +export async function sendTrainerCancelledOrderEmail(user: User, order: Order) { + await transporter.sendMail({ + from: `Fitness World <${smtpAddress}>`, + to: user.email, + subject: "Tid aflyst - Fitness World", + text: + `Hej, ${user.first_name} ${user.last_name}. + +Vi er desværre blevet nødt til at aflyse din tid ved ${order.trainer.first_name} ${order.trainer.last_name} i ${order.trainer.center_name} ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} + +Du kan altid gå ind og bestille en ny. +Vi håber at se dig en anden dag. + +Venlig hilsen Fitness World.` + }) + + await transporter.sendMail({ + from: `Fitness World <${smtpAddress}>`, + to: order.trainer.email, + subject: "Tid aflyst - Fitness World", + text: + `Hej, ${order.trainer.first_name} ${order.trainer.last_name}. + +Du har aflyst en tid med ${user.first_name} ${user.last_name} i ${order.trainer.center_name} ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} + +Venlig hilsen Fitness World.` + }) +} \ No newline at end of file diff --git a/server/src/mail/trainerMovedOrder.ts b/server/src/mail/trainerMovedOrder.ts new file mode 100644 index 0000000..b3db9dd --- /dev/null +++ b/server/src/mail/trainerMovedOrder.ts @@ -0,0 +1,43 @@ +import { Dayjs } from "dayjs"; +import { transporter } from "."; +import { smtpAddress } from "../environment"; +import { Order, OrderObject } from "../interfaces/order"; +import User from "../interfaces/user"; +import { formatDate, formatTime } from "../utils/dates"; + +export async function sendTrainerMovedOrderEmail(user: User, order: Order, oldStartDate: Date | Dayjs, oldEndDate: Date | Dayjs) { + await transporter.sendMail({ + from: `Fitness World <${smtpAddress}>`, + to: user.email, + subject: "Ordre flyttet - Fitness World", + text: + `Hej, ${user.first_name} ${user.last_name}. + +Vi er desværre blevet nødt til at flytte din tid ved ${order.trainer.first_name} ${order.trainer.last_name} i ${order.trainer.center_name} + +Fra: ${formatDate(oldStartDate)} ${formatTime(oldStartDate)} - ${formatTime(oldEndDate)} + +Til: ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} + +Vi håber at tiden passer, ellers er du velkommen til selv at foreslå en ny dato. + +Venlig hilsen Fitness World.` + }) + + + await transporter.sendMail({ + from: `Fitness World <${smtpAddress}>`, + to: order.trainer.email, + subject: "Ordre flyttet - Fitness World", + text: + `Hej, ${order.trainer.first_name} ${order.trainer.last_name}. + +Du har flyttet en tid med ${user.first_name} ${user.last_name} i ${order.trainer.center_name} + +Fra: ${formatDate(oldStartDate)} ${formatTime(oldStartDate)} - ${formatTime(oldEndDate)} + +Til: ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} + +Venlig hilsen Fitness World.` + }) +} \ No newline at end of file diff --git a/server/src/mail/userCancelledOrder.ts b/server/src/mail/userCancelledOrder.ts index ac6160b..b137538 100644 --- a/server/src/mail/userCancelledOrder.ts +++ b/server/src/mail/userCancelledOrder.ts @@ -16,6 +16,18 @@ Du har afbestilt din tid ved ${order.trainer.first_name} ${order.trainer.last_na Vi håber at se dig en anden dag. +Venlig hilsen Fitness World.` + }) + + await transporter.sendMail({ + from: `Fitness World <${smtpAddress}>`, + to: order.trainer.email, + subject: "Ordre afbestilt - Fitness World", + text: + `Hej, ${order.trainer.first_name} ${order.trainer.last_name}. + +${user.first_name} ${user.last_name} har aflyst sin tid i ${order.trainer.center_name} + Venlig hilsen Fitness World.` }) } \ No newline at end of file diff --git a/server/src/mail/userMovedOrder.ts b/server/src/mail/userMovedOrder.ts index 5d59492..f449384 100644 --- a/server/src/mail/userMovedOrder.ts +++ b/server/src/mail/userMovedOrder.ts @@ -1,10 +1,11 @@ +import { Dayjs } from "dayjs"; import { transporter } from "."; import { smtpAddress } from "../environment"; import { Order, OrderObject } from "../interfaces/order"; import User from "../interfaces/user"; import { formatDate, formatTime } from "../utils/dates"; -export async function sendUserMovedOrderEmail(user: User, order: Order) { +export async function sendUserMovedOrderEmail(user: User, order: Order, oldStartDate: Date | Dayjs, oldEndDate: Date | Dayjs) { await transporter.sendMail({ from: `Fitness World <${smtpAddress}>`, to: user.email, @@ -12,9 +13,30 @@ export async function sendUserMovedOrderEmail(user: User, order: Order) { text: `Hej, ${user.first_name} ${user.last_name}. -Din tid ved ${order.trainer.first_name} ${order.trainer.last_name} i ${order.trainer.center_name} er blevet flyttet. +Du har flyttet din tid ved ${order.trainer.first_name} ${order.trainer.last_name} i ${order.trainer.center_name} -Vi glæder os til at se dig ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} +Fra: ${formatDate(oldStartDate)} ${formatTime(oldStartDate)} - ${formatTime(oldEndDate)} + +Til: ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} + +Vi glæder os til at se dig. + +Venlig hilsen Fitness World.` + }) + + + await transporter.sendMail({ + from: `Fitness World <${smtpAddress}>`, + to: order.trainer.email, + subject: "Ordre flyttet - Fitness World", + text: + `Hej, ${order.trainer.first_name} ${order.trainer.last_name}. + +${user.first_name} ${user.last_name} har flyttet sin tid i ${order.trainer.center_name} + +Fra: ${formatDate(oldStartDate)} ${formatTime(oldStartDate)} - ${formatTime(oldEndDate)} + +Til: ${formatDate(order.startDate)} ${formatTime(order.startDate)} - ${formatTime(order.endDate)} Venlig hilsen Fitness World.` }) diff --git a/server/src/routes/order.ts b/server/src/routes/order.ts index 55497f6..39ccc1e 100644 --- a/server/src/routes/order.ts +++ b/server/src/routes/order.ts @@ -35,7 +35,8 @@ router.post("/order/:id/cancel", UserAuth, async (req: AuthedRequest, res: Respo 'first_name',trainer_user.first_name, 'last_name',trainer_user.last_name, 'center_id',trainers.center_id, - 'center_name',centers.name + 'center_name',centers.name, + 'email',trainer_user.email ) as trainer, json_build_object( 'id', order_user.id, diff --git a/server/src/routes/orderNewTime.ts b/server/src/routes/orderNewTime.ts index b434bbf..f9b745a 100644 --- a/server/src/routes/orderNewTime.ts +++ b/server/src/routes/orderNewTime.ts @@ -66,7 +66,8 @@ router.put("/order/:id", UserAuth, async (req: AuthedRequest, res: Response) => 'first_name',trainer_user.first_name, 'last_name',trainer_user.last_name, 'center_id',trainers.center_id, - 'center_name',centers.name + 'center_name',centers.name, + 'email',trainer_user.email ) as trainer, json_build_object( 'id', order_user.id, @@ -189,10 +190,13 @@ router.put("/order/:id", UserAuth, async (req: AuthedRequest, res: Response) => timeslot_id ]); + const oldStartDate = order.startDate; + const oldEndDate = order.endDate; + order.startDate = startDate; order.endDate = endDate; - sendUserMovedOrderEmail(user, order); + sendUserMovedOrderEmail(user, order, oldStartDate, oldEndDate); return res.sendStatus(204); } catch (error: DatabaseError | Error | any) { diff --git a/server/src/routes/trainer/order.ts b/server/src/routes/trainer/order.ts index 3221940..59ac15e 100644 --- a/server/src/routes/trainer/order.ts +++ b/server/src/routes/trainer/order.ts @@ -6,8 +6,11 @@ import { client } from "../../db"; import { DatabaseError } from "pg"; import { AuthedRequest } from "../../interfaces/auth"; import Trainer from "../../interfaces/trainer"; -import { OrderObject, OrderObjectStatus, OrderStatus, PaymentIntentStatus } from "../../interfaces/order"; +import { Order, OrderObject, OrderObjectStatus, OrderStatus, PaymentIntentStatus } from "../../interfaces/order"; import { TrainerAuth } from "../../middlewares/auth"; +import User from "../../interfaces/user"; +import { sendTrainerMovedOrderEmail } from "../../mail/trainerMovedOrder"; +import { sendTrainerCancelledOrderEmail } from "../../mail/trainerCancelledOrder"; const router: Router = express.Router(); @@ -121,18 +124,38 @@ const orderSchema = Joi.object({ router.put("/trainer/order/:id", TrainerAuth, async (req: AuthedRequest, res: Response) => { try { const lookupResult = await client.query(` - SELECT - order_status, - start_time, - end_time, - trainer_id, - timeslot_id - FROM orders - LEFT JOIN payment_intents ON payment_intents.id = orders.payment_intent - LEFT JOIN reserved_timeslots ON reserved_timeslots.id = orders.timeslot_id - LEFT JOIN trainers ON trainers.id = reserved_timeslots.trainer_id - WHERE orders.id = $1 - AND trainers.user_id = $2 + SELECT + orders.id, + order_status, + price, + created_at, + payment_intents.status as payment_intent_status, + start_time as "startDate", + end_time as "endDate", + timeslot_id, + json_build_object( + 'id',trainers.id, + 'first_name',trainer_user.first_name, + 'last_name',trainer_user.last_name, + 'center_id',trainers.center_id, + 'center_name',centers.name, + 'email',trainer_user.email + ) as trainer, + json_build_object( + 'id', order_user.id, + 'first_name', order_user.first_name, + 'last_name', order_user.last_name, + 'email', order_user.email + ) as user + FROM orders + LEFT JOIN reserved_timeslots ON reserved_timeslots.id = orders.timeslot_id + LEFT JOIN payment_intents ON payment_intents.id = orders.payment_intent + LEFT JOIN trainers ON trainers.id = reserved_timeslots.trainer_id + LEFT JOIN centers on trainers.center_id = centers.id + LEFT JOIN users AS trainer_user on trainer_user.id = trainers.user_id + LEFT JOIN users AS order_user on order_user.id = orders.user_id + WHERE orders.id = $1 + AND trainer_user.id = $2 AND order_status = 'Confirmed'; `, [ req.params.id, @@ -143,10 +166,14 @@ router.put("/trainer/order/:id", TrainerAuth, async (req: AuthedRequest, res: Re return res.sendStatus(404); } - const order: ChangeOrderLookup = lookupResult.rows[0]; + const user: User = lookupResult.rows[0].user; + lookupResult.rows[0].user = undefined; + const timeslot_id: number = lookupResult.rows[0].timeslot_id; + lookupResult.rows[0].timeslot_id = undefined; + const order: Order = lookupResult.rows[0]; - const originalStartDate: dayjs.Dayjs = dayjs(order.start_time).utcOffset(0); - const originalEndDate: dayjs.Dayjs = dayjs(order.end_time).utcOffset(0); + const originalStartDate: dayjs.Dayjs = dayjs(order.startDate).utcOffset(0); + const originalEndDate: dayjs.Dayjs = dayjs(order.endDate).utcOffset(0); /** The duration of the original timeslot in miliseconds */ const originalDuration: number = originalEndDate.diff(originalStartDate); @@ -182,10 +209,10 @@ router.put("/trainer/order/:id", TrainerAuth, async (req: AuthedRequest, res: Re AND reserved_timeslots.id != $4 ) as time_already_reserved; `, [ - order.trainer_id, + order.trainer.id, startDate.toISOString(), endDate.toISOString(), - order.timeslot_id + timeslot_id ]); const time_already_reserved: boolean = timeslotValidQuery.rows[0].time_already_reserved; @@ -218,9 +245,17 @@ router.put("/trainer/order/:id", TrainerAuth, async (req: AuthedRequest, res: Re `, [ startDate.toISOString(), endDate.toISOString(), - order.timeslot_id + timeslot_id ]); + const oldStartDate = order.startDate + const oldEndDate = order.endDate; + + order.startDate = startDate; + order.endDate = endDate; + + sendTrainerMovedOrderEmail(user, order, oldStartDate, oldEndDate); + return res.sendStatus(204); } catch (error: DatabaseError | Error | any) { console.error(error); @@ -231,16 +266,37 @@ router.put("/trainer/order/:id", TrainerAuth, async (req: AuthedRequest, res: Re router.post("/trainer/order/:id/cancel", TrainerAuth, async (req: AuthedRequest, res: Response) => { try { const lookupResult = await client.query(` - SELECT - order_status, - payment_intents.status as payment_intent_status, - start_time - FROM orders - LEFT JOIN payment_intents ON payment_intents.id = orders.payment_intent - LEFT JOIN reserved_timeslots ON reserved_timeslots.id = orders.timeslot_id - LEFT JOIN trainers ON trainers.id = reserved_timeslots.trainer_id - WHERE orders.id = $1 - AND trainers.user_id = $2 + SELECT + orders.id, + order_status, + price, + created_at, + payment_intents.status as payment_intent_status, + start_time as "startDate", + end_time as "endDate", + json_build_object( + 'id',trainers.id, + 'first_name',trainer_user.first_name, + 'last_name',trainer_user.last_name, + 'center_id',trainers.center_id, + 'center_name',centers.name, + 'email',trainer_user.email + ) as trainer, + json_build_object( + 'id', order_user.id, + 'first_name', order_user.first_name, + 'last_name', order_user.last_name, + 'email', order_user.email + ) as user + FROM orders + LEFT JOIN reserved_timeslots ON reserved_timeslots.id = orders.timeslot_id + LEFT JOIN payment_intents ON payment_intents.id = orders.payment_intent + LEFT JOIN trainers ON trainers.id = reserved_timeslots.trainer_id + LEFT JOIN centers on trainers.center_id = centers.id + LEFT JOIN users AS trainer_user on trainer_user.id = trainers.user_id + LEFT JOIN users AS order_user on order_user.id = orders.user_id + WHERE orders.id = $1 + AND trainer_user.id = $2 AND order_status = 'Confirmed'; `, [ req.params.id, @@ -251,9 +307,11 @@ router.post("/trainer/order/:id/cancel", TrainerAuth, async (req: AuthedRequest, return res.sendStatus(404); } - const order: CancelOrderLookup = lookupResult.rows[0]; + const user: User = lookupResult.rows[0].user; + lookupResult.rows[0].user = undefined; + const order: Order = lookupResult.rows[0]; - const dateValidation = Joi.date().min(Date.now()).validate(order.start_time) + const dateValidation = Joi.date().min(Date.now()).validate(order.startDate) if (dateValidation.error !== undefined) { return res.status(400).send({ message: "Timeslot has already occurred" }); } @@ -267,6 +325,8 @@ router.post("/trainer/order/:id/cancel", TrainerAuth, async (req: AuthedRequest, req.params.id ]); + sendTrainerCancelledOrderEmail(user, order); + return res.sendStatus(204); } catch (error: DatabaseError | Error | any) { console.error(error);