From b2920194637aa904c75bc49fc1a1b898e53f5997 Mon Sep 17 00:00:00 2001 From: bcho892 Date: Thu, 1 Aug 2024 20:18:34 +1200 Subject: [PATCH] add required functions and format all booking confirmation emails properly --- .../business-layer/services/StripeService.ts | 9 ++-- .../business-layer/utils/BookingUtils.test.ts | 33 ++++++++++++++ .../src/business-layer/utils/BookingUtils.ts | 44 +++++++++++++++++++ .../controllers/AdminController.ts | 8 +++- 4 files changed, 89 insertions(+), 5 deletions(-) diff --git a/server/src/business-layer/services/StripeService.ts b/server/src/business-layer/services/StripeService.ts index b094337aa..8f7e9c504 100644 --- a/server/src/business-layer/services/StripeService.ts +++ b/server/src/business-layer/services/StripeService.ts @@ -20,7 +20,10 @@ import { import BookingSlotService from "data-layer/services/BookingSlotsService" import console from "console" import MailService from "./MailService" -import BookingUtils from "../utils/BookingUtils" +import BookingUtils, { + CHECK_IN_TIME, + CHECK_OUT_TIME +} from "../utils/BookingUtils" const stripe = new Stripe(process.env.STRIPE_API_KEY) @@ -438,8 +441,8 @@ export default class StripeService { await new MailService().sendBookingConfirmationEmail( userAuthData.email, `${first_name} ${last_name}`, - session.metadata[START_DATE], - session.metadata[END_DATE] + `${session.metadata[START_DATE]} ${CHECK_IN_TIME} (check in)`, + `${BookingUtils.addOneDay(session.metadata[END_DATE])} ${CHECK_OUT_TIME} (check out)` ) } catch (error) { console.error(`Failed to send an email to the user ${uid}`, error) diff --git a/server/src/business-layer/utils/BookingUtils.test.ts b/server/src/business-layer/utils/BookingUtils.test.ts index d63b2b301..25944d922 100644 --- a/server/src/business-layer/utils/BookingUtils.test.ts +++ b/server/src/business-layer/utils/BookingUtils.test.ts @@ -159,4 +159,37 @@ describe("BookingUtils", () => { expect(result).toBe(false) }) }) + describe("BookingUtils.addOneDay", () => { + it("should add one day to a regular date", () => { + expect(BookingUtils.addOneDay("01/08/2024")).toBe("02/08/2024") + }) + + it("should handle end of month", () => { + expect(BookingUtils.addOneDay("31/07/2024")).toBe("01/08/2024") + }) + + it("should handle end of year", () => { + expect(BookingUtils.addOneDay("31/12/2024")).toBe("01/01/2025") + }) + + it("should handle leap year", () => { + expect(BookingUtils.addOneDay("28/02/2024")).toBe("29/02/2024") + expect(BookingUtils.addOneDay("29/02/2024")).toBe("01/03/2024") + }) + + it("should handle non-leap year", () => { + expect(BookingUtils.addOneDay("28/02/2023")).toBe("01/03/2023") + }) + + it("should handle single digit day and month", () => { + expect(BookingUtils.addOneDay("01/01/2024")).toBe("02/01/2024") + expect(BookingUtils.addOneDay("09/09/2024")).toBe("10/09/2024") + }) + + it("should handle invalid date format", () => { + expect(() => BookingUtils.addOneDay("2024/01/01")).toThrow() + expect(() => BookingUtils.addOneDay("01-01-2024")).toThrow() + expect(() => BookingUtils.addOneDay("invalid-date")).toThrow() + }) + }) }) diff --git a/server/src/business-layer/utils/BookingUtils.ts b/server/src/business-layer/utils/BookingUtils.ts index 8f6a02ca9..d712219e9 100644 --- a/server/src/business-layer/utils/BookingUtils.ts +++ b/server/src/business-layer/utils/BookingUtils.ts @@ -17,6 +17,9 @@ _earliestDate.setUTCHours(0, 0, 0, 0) export const _latestDate = new Date(_earliestDate) _latestDate.setFullYear(_earliestDate.getFullYear() + 1) +export const CHECK_IN_TIME = "3:00 pm" as const +export const CHECK_OUT_TIME = "11:00 am" as const + const BookingUtils = { /** * Used to check if the dates are within the acceptable range @@ -111,6 +114,47 @@ const BookingUtils = { const availableSlots = bookingSlot.max_bookings - bookingCount return availableSlots <= 0 + }, + /** + * Adds one day to the given date string in the format dd/mm/yyyy. + * + * This function parses the input date string, adds one day to it, and then + * formats it back to the dd/mm/yyyy string format. It correctly handles edge + * cases such as the end of a month, end of a year, and leap years. + * + * @param {string} dateString - The date string in the format dd/mm/yyyy. + * @returns {string} - The new date string, one day later, in the format dd/mm/yyyy. + * + * @throws {Error} - Throws an error if the input date string is not in the format dd/mm/yyyy. + * + * @example + * ```typescript + * addOneDay('31/07/2024'); // Returns '01/08/2024' + * addOneDay('28/02/2024'); // Returns '29/02/2024' + * addOneDay('31/12/2024'); // Returns '01/01/2025' + * ``` + * + * @remarks + * This function uses the JavaScript `Date` object to handle date manipulation. + * The `Date` object automatically adjusts for month and year boundaries, including + * leap years. The function assumes the input date string is valid and in the correct + * format. If the input date string is invalid or not in the format dd/mm/yyyy, the + * function will throw an error. + */ + addOneDay: (dateString: string): string => { + // Parse the input date string + const [day, month, year] = dateString.split("/").map(Number) + const date = new Date(year, month - 1, day) + + // Add one day + date.setDate(date.getDate() + 1) + + // Format the new date back to dd/mm/yyyy + const newDay = String(date.getDate()).padStart(2, "0") + const newMonth = String(date.getMonth() + 1).padStart(2, "0") + const newYear = date.getFullYear() + + return `${newDay}/${newMonth}/${newYear}` } } as const diff --git a/server/src/service-layer/controllers/AdminController.ts b/server/src/service-layer/controllers/AdminController.ts index 032a7e345..11b010508 100644 --- a/server/src/service-layer/controllers/AdminController.ts +++ b/server/src/service-layer/controllers/AdminController.ts @@ -52,6 +52,10 @@ import { UserAccountTypes } from "../../business-layer/utils/AuthServiceClaims" import { UserRecord } from "firebase-admin/auth" import { Timestamp } from "firebase-admin/firestore" import MailService from "business-layer/services/MailService" +import BookingUtils, { + CHECK_IN_TIME, + CHECK_OUT_TIME +} from "business-layer/utils/BookingUtils" @Route("admin") @Security("jwt", ["admin"]) @@ -247,8 +251,8 @@ export class AdminController extends Controller { mailService.sendBookingConfirmationEmail( userAuthData.email, `${first_name} ${last_name}`, - BOOKING_START_DATE, - BOOKING_END_DATE + `${BOOKING_START_DATE} ${CHECK_IN_TIME} (check in)`, + `${BookingUtils.addOneDay(BOOKING_END_DATE)} ${CHECK_OUT_TIME} (check out)` ) } catch (e) { console.error(