Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Format the booking confirmation to be explicit about check in/out #734

Merged
merged 4 commits into from
Aug 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion client/src/utils/Constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ let NEXT_YEAR_FROM_TODAY = new Date(
NEXT_YEAR_FROM_TODAY = new Date(NEXT_YEAR_FROM_TODAY.toDateString())

const CHECK_OUT_TIME = "10:00am" as const
const CHECK_IN_TIME = "3:00pm" as const
const CHECK_IN_TIME = "11:00am" as const

export { TODAY, NEXT_YEAR_FROM_TODAY, CHECK_IN_TIME, CHECK_OUT_TIME }
9 changes: 6 additions & 3 deletions server/src/business-layer/services/StripeService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
p Kia ora #{name}

p UASC is stoked to confirm your booking for an unforgettable stay at your lodge between:
p UASC is stoked to confirm your booking for an unforgettable stay at our lodge between:

p <strong>#{startDate} to #{endDate}</strong>

Expand Down
33 changes: 33 additions & 0 deletions server/src/business-layer/utils/BookingUtils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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()
})
})
})
48 changes: 48 additions & 0 deletions server/src/business-layer/utils/BookingUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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 = "11:00 am" as const
export const CHECK_OUT_TIME = "10:00 am" as const

const BookingUtils = {
/**
* Used to check if the dates are within the acceptable range
Expand Down Expand Up @@ -111,6 +114,51 @@ 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)

if (isNaN(date.getTime()) || year < 2000) {
throw new Error("Invalid date")
}

// 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

Expand Down
8 changes: 6 additions & 2 deletions server/src/service-layer/controllers/AdminController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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"])
Expand Down Expand Up @@ -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(
Expand Down
Loading