>
)
}
diff --git a/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx b/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx
new file mode 100644
index 000000000..5410f81b8
--- /dev/null
+++ b/client/src/components/composite/Admin/AdminBookingView/WrappedAdminBookingCreationPopUp.tsx
@@ -0,0 +1,52 @@
+import { useUsersQuery } from "services/Admin/AdminQueries"
+import AdminBookingCreationPopUp from "./AdminBookingCreationPopUp"
+import { useEffect, useMemo } from "react"
+import { useAvailableBookingsQuery } from "services/Booking/BookingQueries"
+import { useAddUserToBookingMutation } from "services/Admin/AdminMutations"
+
+interface IWrappedAdminBookingCreationPopUp {
+ handleClose: () => void
+}
+
+const WrappedAdminBookingCreationPopUp = ({
+ handleClose
+}: IWrappedAdminBookingCreationPopUp) => {
+ const {
+ data: userPages,
+ fetchNextPage,
+ isFetchingNextPage,
+ hasNextPage
+ } = useUsersQuery()
+
+ const { data: bookingSlots } = useAvailableBookingsQuery()
+
+ const { mutateAsync: handleAddUserToBooking, isPending } =
+ useAddUserToBookingMutation()
+
+ useEffect(() => {
+ if (hasNextPage && !isFetchingNextPage) {
+ fetchNextPage()
+ }
+ }, [fetchNextPage, isFetchingNextPage, hasNextPage])
+
+ const users = useMemo(
+ () => userPages?.pages.flatMap((page) => page.data || []),
+ [userPages]
+ )
+
+ return (
+
+ )
+}
+
+export default WrappedAdminBookingCreationPopUp
diff --git a/client/src/components/composite/Admin/AdminMemberView/AdminSearchBar.tsx b/client/src/components/composite/Admin/AdminMemberView/AdminSearchBar.tsx
index d7481f4a6..071015d2d 100644
--- a/client/src/components/composite/Admin/AdminMemberView/AdminSearchBar.tsx
+++ b/client/src/components/composite/Admin/AdminMemberView/AdminSearchBar.tsx
@@ -2,6 +2,7 @@ import TextInput from "components/generic/TextInputComponent/TextInput"
import { debounce } from "components/utils/Utils"
interface IAdminSearchBar {
+ placeholder?: string
/**
* @param newQuery a **lower case** string representing the new query value
*/
@@ -10,7 +11,10 @@ interface IAdminSearchBar {
const ADMIN_SEARCH_BAR_DEFAULT_DEBOUNCE = 300 as const
-const AdminSearchBar = ({ onQueryChanged }: IAdminSearchBar) => {
+const AdminSearchBar = ({
+ onQueryChanged,
+ placeholder = "search"
+}: IAdminSearchBar) => {
const changeHandler = (e: React.ChangeEvent
) => {
const debouncedCallback = debounce(
() => onQueryChanged?.(e.target.value.toLowerCase()),
@@ -24,7 +28,7 @@ const AdminSearchBar = ({ onQueryChanged }: IAdminSearchBar) => {
data-testid="search-input"
type="text"
onChange={(e) => changeHandler(e)}
- placeholder="search"
+ placeholder={placeholder}
/>
)
}
diff --git a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx
index 15340d79a..3bab86343 100644
--- a/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx
+++ b/client/src/components/composite/Booking/BookingCreation/BookingCreation.tsx
@@ -9,19 +9,7 @@ import { BookingAvailability } from "models/Booking"
import { NEXT_YEAR_FROM_TODAY, TODAY } from "utils/Constants"
import { Timestamp } from "firebase/firestore"
import Checkbox from "components/generic/Checkbox/Checkbox"
-import { DateUtils, UnknownTimestamp } from "components/utils/DateUtils"
-
-type DateRange = {
- /**
- * Javascript date object representing the date of the first night for the booking
- */
- startDate: Date
-
- /**
- * Javascript date object representing the date of the last night for the booking
- */
- endDate: Date
-}
+import { DateRange, DateUtils } from "components/utils/DateUtils"
/*
* Swaps around dates if invalid
@@ -74,12 +62,6 @@ interface ICreateBookingSection {
*/
isPending?: boolean
}
-const UTCDatesEqual = (slot: UnknownTimestamp, date: Date) => {
- return DateUtils.dateEqualToTimestamp(
- DateUtils.convertLocalDateToUTCDate(date),
- slot
- )
-}
const NORMAL_PRICE = 40 as const
const SPECIAL_PRICE = 60 as const
@@ -101,7 +83,7 @@ export const CreateBookingSection = ({
const { startDate: currentStartDate, endDate: currentEndDate } =
selectedDateRange
- const disabledDates = bookingSlots.filter((slot) => slot.availableSpaces <= 0)
+ const disabledDates = DateUtils.unavailableDates(bookingSlots)
/**
* Function to be called to confirm the date range selected by the user.
@@ -216,13 +198,18 @@ export const CreateBookingSection = ({
}
tileDisabled={({ date, view }) =>
view !== "year" &&
- (!bookingSlots.some((slot) => UTCDatesEqual(slot.date, date)) ||
- disabledDates.some((slot) => UTCDatesEqual(slot.date, date)))
+ (!bookingSlots.some((slot) =>
+ DateUtils.UTCDatesEqual(slot.date, date)
+ ) ||
+ disabledDates.some((slot) =>
+ DateUtils.UTCDatesEqual(slot.date, date)
+ ))
}
tileContent={({ date }) => {
const slot = bookingSlots.find(
(slot) =>
- UTCDatesEqual(slot.date, date) && slot.availableSpaces > 0
+ DateUtils.UTCDatesEqual(slot.date, date) &&
+ slot.availableSpaces > 0
)
return slot ? (
diff --git a/client/src/components/utils/DateUtils.tsx b/client/src/components/utils/DateUtils.tsx
index d68036a20..07b144555 100644
--- a/client/src/components/utils/DateUtils.tsx
+++ b/client/src/components/utils/DateUtils.tsx
@@ -1,5 +1,17 @@
+import { BookingAvailability } from "models/Booking"
import { MS_IN_SECOND } from "utils/Constants"
+export type DateRange = {
+ /**
+ * Javascript date object representing the date of the first night for the booking
+ */
+ startDate: Date
+
+ /**
+ * Javascript date object representing the date of the last night for the booking
+ */
+ endDate: Date
+}
/**
* Utility type to allow us to handle cases where the timestamp may actually have
* `_seconds` or `_nanoseconds`
@@ -7,6 +19,21 @@ import { MS_IN_SECOND } from "utils/Constants"
export interface UnknownTimestamp extends Record {}
export const DateUtils = {
+ /**
+ * gets a list of dates that are unavailable from availability objects
+ *
+ * @param bookingSlots the list of booking slots for which to search for unavailbale dates
+ * @returns the fully unavailable dates
+ */
+ unavailableDates: (bookingSlots: BookingAvailability[]) =>
+ bookingSlots.filter((slot) => slot.availableSpaces <= 0),
+
+ UTCDatesEqual: (slot: UnknownTimestamp, date: Date) => {
+ return DateUtils.dateEqualToTimestamp(
+ DateUtils.convertLocalDateToUTCDate(date),
+ slot
+ )
+ },
datesToDateRange: (startDate: Date, endDate: Date) => {
const dateArray = []
const currentDate = new Date(startDate)
diff --git a/client/src/models/__generated__/schema.d.ts b/client/src/models/__generated__/schema.d.ts
index b93e61a08..b41782795 100644
--- a/client/src/models/__generated__/schema.d.ts
+++ b/client/src/models/__generated__/schema.d.ts
@@ -207,12 +207,13 @@ export interface components {
}[];
error?: string;
};
- /** @description Represents the structure of a request model for fetching bookings within a specific date range. */
- BookingsByDateRangeRequestModel: {
+ CreateBookingsRequestModel: {
/** @description Firestore timestamp, should represent a UTC date that is set to exactly midnight */
startDate: components["schemas"]["FirebaseFirestore.Timestamp"];
/** @description Firestore timestamp, should represent a UTC date that is set to exactly midnight */
endDate: components["schemas"]["FirebaseFirestore.Timestamp"];
+ /** @description List of users to add to the bookings between date range */
+ userIds: string[];
};
AllUserBookingSlotsResponse: {
error?: string;
@@ -278,6 +279,13 @@ export interface components {
}[];
error?: string;
};
+ /** @description Represents the structure of a request model for fetching bookings within a specific date range. */
+ BookingsByDateRangeRequestModel: {
+ /** @description Firestore timestamp, should represent a UTC date that is set to exactly midnight */
+ startDate: components["schemas"]["FirebaseFirestore.Timestamp"];
+ /** @description Firestore timestamp, should represent a UTC date that is set to exactly midnight */
+ endDate: components["schemas"]["FirebaseFirestore.Timestamp"];
+ };
BookingSlotUpdateResponse: {
error?: string;
message?: string;
@@ -536,7 +544,7 @@ export interface operations {
CreateBookings: {
requestBody: {
content: {
- "application/json": components["schemas"]["BookingsByDateRangeRequestModel"];
+ "application/json": components["schemas"]["CreateBookingsRequestModel"];
};
};
responses: {
diff --git a/client/src/services/Admin/AdminMutations.ts b/client/src/services/Admin/AdminMutations.ts
index 67db70248..856981a09 100644
--- a/client/src/services/Admin/AdminMutations.ts
+++ b/client/src/services/Admin/AdminMutations.ts
@@ -3,7 +3,10 @@ import AdminService from "./AdminService"
import { Timestamp } from "firebase/firestore"
import queryClient from "services/QueryClient"
import { BOOKING_AVAILABLITY_KEY } from "services/Booking/BookingQueries"
-import { ALL_USERS_QUERY } from "./AdminQueries"
+import {
+ ALL_BOOKINGS_BETWEEN_RANGE_QUERY,
+ ALL_USERS_QUERY
+} from "./AdminQueries"
import { CombinedUserData } from "models/User"
import { replaceUserInPage } from "./AdminUtils"
@@ -116,3 +119,16 @@ export function useMakeDatesUnavailableMutation(
}
})
}
+
+export function useAddUserToBookingMutation() {
+ return useMutation({
+ mutationKey: ["add-users-to-booking"],
+ retry: false,
+ mutationFn: AdminService.addUsersToBookingForDateRange,
+ onSuccess: () => {
+ queryClient.invalidateQueries({
+ queryKey: [ALL_BOOKINGS_BETWEEN_RANGE_QUERY]
+ })
+ }
+ })
+}
diff --git a/client/src/services/Admin/AdminQueries.ts b/client/src/services/Admin/AdminQueries.ts
index 38d21adce..4882f6499 100644
--- a/client/src/services/Admin/AdminQueries.ts
+++ b/client/src/services/Admin/AdminQueries.ts
@@ -3,6 +3,7 @@ import { Timestamp } from "firebase/firestore"
import AdminService from "./AdminService"
export const ALL_USERS_QUERY = "allUsers"
+export const ALL_BOOKINGS_BETWEEN_RANGE_QUERY = "bookings-between-range"
export function useUsersQuery() {
return useInfiniteQuery({
@@ -19,7 +20,7 @@ export function useAdminBookingsQuery(
endDate: Timestamp
) {
return useQuery({
- queryKey: ["bookingsBetweenRange", startDate, endDate],
+ queryKey: [ALL_BOOKINGS_BETWEEN_RANGE_QUERY, startDate, endDate],
queryFn: () =>
AdminService.getBookingsBetweenDateRange({
startDate,
diff --git a/client/src/services/Admin/AdminService.ts b/client/src/services/Admin/AdminService.ts
index 0d68a7c52..bae3d746b 100644
--- a/client/src/services/Admin/AdminService.ts
+++ b/client/src/services/Admin/AdminService.ts
@@ -130,6 +130,34 @@ const AdminService = {
`Failed to make dates ${startDate.toString()} to ${endDate.toString()} available`
)
return data
+ },
+ addUsersToBookingForDateRange: async function ({
+ startDate,
+ endDate,
+ userIds
+ }: {
+ startDate: Timestamp
+ endDate: Timestamp
+ userIds: string[]
+ }) {
+ const { response, data } = await fetchClient.POST(
+ "/bookings/create-bookings",
+ {
+ body: {
+ startDate,
+ endDate,
+ userIds
+ }
+ }
+ )
+
+ if (!response.ok) {
+ throw new Error(
+ `Failed to add the users ${userIds.join(",")} to the date range ${startDate.toString()} to ${endDate.toString()} `
+ )
+ }
+
+ return data?.data
}
} as const
diff --git a/server/src/middleware/__generated__/routes.ts b/server/src/middleware/__generated__/routes.ts
index 10508f6c6..6f3a659ef 100644
--- a/server/src/middleware/__generated__/routes.ts
+++ b/server/src/middleware/__generated__/routes.ts
@@ -172,11 +172,12 @@ const models: TsoaRoute.Models = {
"additionalProperties": false,
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
- "BookingsByDateRangeRequestModel": {
+ "CreateBookingsRequestModel": {
"dataType": "refObject",
"properties": {
"startDate": {"ref":"FirebaseFirestore.Timestamp","required":true},
"endDate": {"ref":"FirebaseFirestore.Timestamp","required":true},
+ "userIds": {"dataType":"array","array":{"dataType":"string"},"required":true},
},
"additionalProperties": false,
},
@@ -256,6 +257,15 @@ const models: TsoaRoute.Models = {
"additionalProperties": false,
},
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
+ "BookingsByDateRangeRequestModel": {
+ "dataType": "refObject",
+ "properties": {
+ "startDate": {"ref":"FirebaseFirestore.Timestamp","required":true},
+ "endDate": {"ref":"FirebaseFirestore.Timestamp","required":true},
+ },
+ "additionalProperties": false,
+ },
+ // WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
"BookingSlotUpdateResponse": {
"dataType": "refObject",
"properties": {
@@ -653,7 +663,7 @@ export function RegisterRoutes(app: Router) {
function BookingController_createBookings(request: ExRequest, response: ExResponse, next: any) {
const args: Record = {
- requestBody: {"in":"body","name":"requestBody","required":true,"ref":"BookingsByDateRangeRequestModel"},
+ requestBody: {"in":"body","name":"requestBody","required":true,"ref":"CreateBookingsRequestModel"},
};
// WARNING: This file was auto-generated with tsoa. Please do not modify it. Re-run tsoa to re-generate this file: https://github.com/lukeautry/tsoa
diff --git a/server/src/middleware/__generated__/swagger.json b/server/src/middleware/__generated__/swagger.json
index ee5f2261b..3b9bddc26 100644
--- a/server/src/middleware/__generated__/swagger.json
+++ b/server/src/middleware/__generated__/swagger.json
@@ -375,8 +375,7 @@
"type": "object",
"additionalProperties": false
},
- "BookingsByDateRangeRequestModel": {
- "description": "Represents the structure of a request model for fetching bookings within a specific date range.",
+ "CreateBookingsRequestModel": {
"properties": {
"startDate": {
"$ref": "#/components/schemas/FirebaseFirestore.Timestamp",
@@ -385,11 +384,19 @@
"endDate": {
"$ref": "#/components/schemas/FirebaseFirestore.Timestamp",
"description": "Firestore timestamp, should represent a UTC date that is set to exactly midnight"
+ },
+ "userIds": {
+ "items": {
+ "type": "string"
+ },
+ "type": "array",
+ "description": "List of users to add to the bookings between date range"
}
},
"required": [
"startDate",
- "endDate"
+ "endDate",
+ "userIds"
],
"type": "object",
"additionalProperties": false
@@ -594,6 +601,25 @@
"type": "object",
"additionalProperties": false
},
+ "BookingsByDateRangeRequestModel": {
+ "description": "Represents the structure of a request model for fetching bookings within a specific date range.",
+ "properties": {
+ "startDate": {
+ "$ref": "#/components/schemas/FirebaseFirestore.Timestamp",
+ "description": "Firestore timestamp, should represent a UTC date that is set to exactly midnight"
+ },
+ "endDate": {
+ "$ref": "#/components/schemas/FirebaseFirestore.Timestamp",
+ "description": "Firestore timestamp, should represent a UTC date that is set to exactly midnight"
+ }
+ },
+ "required": [
+ "startDate",
+ "endDate"
+ ],
+ "type": "object",
+ "additionalProperties": false
+ },
"BookingSlotUpdateResponse": {
"properties": {
"error": {
@@ -1255,7 +1281,7 @@
"content": {
"application/json": {
"schema": {
- "$ref": "#/components/schemas/BookingsByDateRangeRequestModel"
+ "$ref": "#/components/schemas/CreateBookingsRequestModel"
}
}
}
diff --git a/server/src/middleware/routes.test.ts b/server/src/middleware/routes.test.ts
index aac1b465a..7b9270210 100644
--- a/server/src/middleware/routes.test.ts
+++ b/server/src/middleware/routes.test.ts
@@ -1284,19 +1284,18 @@ describe("Endpoints", () => {
await cleanFirestore()
})
- it("should return userIds for successful bookings within the date range", async () => {
+ it("should create bookings for userIds within the date range", async () => {
const bookingSlotService = new BookingSlotService()
- const bookingDataService = new BookingDataService()
const startDate = dateToFirestoreTimeStamp(new Date("01/01/2022"))
const endDate = dateToFirestoreTimeStamp(new Date("12/31/2023"))
- const slot1 = await bookingSlotService.createBookingSlot({
+ await bookingSlotService.createBookingSlot({
date: dateToFirestoreTimeStamp(new Date("02/01/2023")),
max_bookings: 10
})
- const slot2 = await bookingSlotService.createBookingSlot({
+ await bookingSlotService.createBookingSlot({
date: dateToFirestoreTimeStamp(new Date("03/01/2023")),
max_bookings: 10
})
@@ -1307,28 +1306,17 @@ describe("Endpoints", () => {
max_bookings: 10
})
- await bookingDataService.createBooking({
- user_id: MEMBER_USER_UID,
- booking_slot_id: slot1.id,
- stripe_payment_id: ""
- })
-
- await bookingDataService.createBooking({
- user_id: GUEST_USER_UID,
- booking_slot_id: slot2.id,
- stripe_payment_id: ""
- })
-
const res = await request
.post("/bookings/create-bookings")
.set("Authorization", `Bearer ${adminToken}`)
.send({
startDate,
- endDate
+ endDate,
+ userIds: [GUEST_USER_UID, MEMBER_USER_UID]
})
expect(res.status).toEqual(200)
- expect(res.body.data).toHaveLength(2)
+ expect(res.body.data).toHaveLength(3)
expect.arrayContaining([
expect.objectContaining({
users: expect.arrayContaining([
@@ -1341,57 +1329,6 @@ describe("Endpoints", () => {
])
})
])
-
- // Check to see if the bookings are created correctly
- const result1 =
- await bookingDataService.getBookingsByUserId(MEMBER_USER_UID)
- const result2 =
- await bookingDataService.getBookingsByUserId(GUEST_USER_UID)
- const result3 = await bookingDataService.getBookingsBySlotId(slot1.id)
-
- expect(result1).toHaveLength(1)
- expect.arrayContaining([
- expect.objectContaining({ booking_slot_id: slot1.id }),
- expect.objectContaining({ user_id: MEMBER_USER_UID }),
- expect.objectContaining({ stripe_payment_id: "a" })
- ])
- expect(result2).toHaveLength(1)
- expect.arrayContaining([
- expect.objectContaining({ booking_slot_id: slot2.id }),
- expect.objectContaining({ user_id: GUEST_USER_UID }),
- expect.objectContaining({ stripe_payment_id: "" })
- ])
- expect(result1).toEqual(result3)
- })
-
- it("should return an empty array if no users have bookings within the date range", async () => {
- const startDate = dateToFirestoreTimeStamp(new Date("01/01/2024"))
- const endDate = dateToFirestoreTimeStamp(new Date("12/31/2024"))
-
- const bookingSlotService = new BookingSlotService()
- const bookingDataService = new BookingDataService()
-
- const slot1 = await bookingSlotService.createBookingSlot({
- date: dateToFirestoreTimeStamp(new Date("02/01/2025")), // Out of range date
- max_bookings: 10
- })
-
- await bookingDataService.createBooking({
- user_id: MEMBER_USER_UID,
- booking_slot_id: slot1.id,
- stripe_payment_id: ""
- })
-
- const res = await request
- .post("/bookings/create-bookings")
- .set("Authorization", `Bearer ${adminToken}`)
- .send({
- startDate,
- endDate
- })
-
- expect(res.status).toEqual(200)
- expect(res.body.data).toHaveLength(0)
})
it("should return unauthorized error for non-admin users", async () => {
@@ -1403,7 +1340,8 @@ describe("Endpoints", () => {
.set("Authorization", `Bearer ${memberToken}`)
.send({
startDate,
- endDate
+ endDate,
+ userIds: []
})
expect(res.status).toEqual(401)
@@ -1438,7 +1376,8 @@ describe("Endpoints", () => {
.set("Authorization", `Bearer ${adminToken}`)
.send({
startDate,
- endDate
+ endDate,
+ userIds: [MEMBER_USER_UID]
})
expect(res.status).toEqual(200)
diff --git a/server/src/service-layer/controllers/BookingController.ts b/server/src/service-layer/controllers/BookingController.ts
index 51daab6fe..b940798d5 100644
--- a/server/src/service-layer/controllers/BookingController.ts
+++ b/server/src/service-layer/controllers/BookingController.ts
@@ -1,6 +1,7 @@
import {
AvailableDatesRequestModel,
- BookingsByDateRangeRequestModel
+ BookingsByDateRangeRequestModel,
+ CreateBookingsRequestModel
} from "service-layer/request-models/UserRequests"
import { AvailableDatesResponse } from "service-layer/response-models/PaymentResponse"
import { Timestamp } from "firebase-admin/firestore"
@@ -39,7 +40,7 @@ export class BookingController extends Controller {
@Security("jwt", ["admin"])
@Post("create-bookings")
public async createBookings(
- @Body() requestBody: BookingsByDateRangeRequestModel
+ @Body() requestBody: CreateBookingsRequestModel
): Promise {
try {
const { startDate, endDate } = requestBody
@@ -63,32 +64,18 @@ export class BookingController extends Controller {
/** Iterating through each booking slot */
const bookingPromises = bookingSlots.map(async (slot) => {
- /** Getting the bookings for the current slot */
- const bookings = await bookingDataService.getBookingsBySlotId(slot.id)
-
- /** Extracting the all 3 Ids from the bookings */
- const userIds = bookings.map((booking) => booking.user_id)
- const slotIds = bookings.map((booking) => booking.booking_slot_id)
- const stripePaymentIds = bookings.map(
- (booking) => booking.stripe_payment_id
- )
-
- if (userIds.length === 0) {
- return
- }
-
+ let userIds = [...requestBody.userIds]
/** For every slotid add a booking for that id only if user doesn't already have a booking */
- const userIdsPromises = userIds.map(async (userId, i) => {
+ const userIdsPromises = userIds.map(async (userId) => {
if (
- (await bookingDataService.getBookingsByUserId(userIds[i]))
- .length !== 0
+ (await bookingDataService.getBookingsByUserId(userId)).length !== 0
) {
- delete userIds[i] // Remove user from list if they already have a booking
+ userIds = userIds.filter((id) => id !== userId) // Remove user from list if they already have a booking
} else {
await bookingDataService.createBooking({
- user_id: userIds[i],
- booking_slot_id: slotIds[i],
- stripe_payment_id: stripePaymentIds[i]
+ user_id: userId,
+ booking_slot_id: slot.id,
+ stripe_payment_id: "manual_entry"
})
}
})
diff --git a/server/src/service-layer/request-models/UserRequests.ts b/server/src/service-layer/request-models/UserRequests.ts
index 52fc059d0..8fe2e1e13 100644
--- a/server/src/service-layer/request-models/UserRequests.ts
+++ b/server/src/service-layer/request-models/UserRequests.ts
@@ -76,3 +76,11 @@ export interface BookingsByDateRangeRequestModel {
*/
endDate: Timestamp
}
+
+export interface CreateBookingsRequestModel
+ extends BookingsByDateRangeRequestModel {
+ /**
+ * List of users to add to the bookings between date range
+ */
+ userIds: string[]
+}