From c89a1877ae998ef35cd55929fc70de68f063b27d Mon Sep 17 00:00:00 2001 From: Lauris Skraucis Date: Wed, 16 Apr 2025 09:59:02 +0200 Subject: [PATCH] feat: organization booking hooks (#20611) * docs: request auth headers * feat: useOrganizationsBookings hook * feat: useOrganizationUserBookings hook * rename hooks * query params * fix: organizations users bookings extract user from userId not auth * fix: passing array from hook * docs * correct header doc --------- Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com> --- .../organizations-bookings.controller.ts | 21 ++-- ...organizations-users-bookings-controller.ts | 16 ++- .../organization-users-bookings.service.ts | 19 +-- apps/api/v2/swagger/documentation.json | 62 ++++++++++ docs/api-reference/v2/openapi.json | 62 ++++++++++ docs/platform/bookings-hooks.mdx | 108 +++++++++++++++++- .../atoms/hooks/bookings/useBookings.ts | 44 ++++--- .../bookings/useOrganizationBookings.ts | 38 ++++++ .../bookings/useOrganizationUserBookings.ts | 34 ++++++ packages/platform/atoms/index.ts | 3 + .../2024-08-13/inputs/get-bookings.input.ts | 6 + .../bookings}/get-org-bookings.input.ts | 3 + .../types/organizations/bookings/index.ts | 1 + .../platform/types/organizations/index.ts | 1 + 14 files changed, 373 insertions(+), 45 deletions(-) create mode 100644 packages/platform/atoms/hooks/organizations/bookings/useOrganizationBookings.ts create mode 100644 packages/platform/atoms/hooks/organizations/bookings/useOrganizationUserBookings.ts rename {apps/api/v2/src/modules/organizations/bookings/inputs => packages/platform/types/organizations/bookings}/get-org-bookings.input.ts (89%) create mode 100644 packages/platform/types/organizations/bookings/index.ts diff --git a/apps/api/v2/src/modules/organizations/bookings/organizations-bookings.controller.ts b/apps/api/v2/src/modules/organizations/bookings/organizations-bookings.controller.ts index bac9775b69..9873750e68 100644 --- a/apps/api/v2/src/modules/organizations/bookings/organizations-bookings.controller.ts +++ b/apps/api/v2/src/modules/organizations/bookings/organizations-bookings.controller.ts @@ -1,5 +1,11 @@ import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service"; import { API_VERSIONS_VALUES } from "@/lib/api-versions"; +import { + OPTIONAL_X_CAL_CLIENT_ID_HEADER, + OPTIONAL_X_CAL_SECRET_KEY_HEADER, + OPTIONAL_API_KEY_HEADER, + OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER, +} from "@/lib/docs/headers"; import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator"; import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator"; import { Roles } from "@/modules/auth/decorators/roles/roles.decorator"; @@ -8,15 +14,12 @@ import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.g import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard"; import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard"; import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; -import { OrganizationsUsersService } from "@/modules/organizations/users/index/services/organizations-users-service"; import { UserWithProfile } from "@/modules/users/users.repository"; import { Controller, UseGuards, Get, Param, ParseIntPipe, Query, HttpStatus, HttpCode } from "@nestjs/common"; -import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; -import { GetBookingsOutput_2024_08_13 } from "@calcom/platform-types"; - -import { GetOrganizationsBookingsInput } from "./inputs/get-org-bookings.input"; +import { GetBookingsOutput_2024_08_13, GetOrganizationsBookingsInput } from "@calcom/platform-types"; @Controller({ path: "/v2/organizations/:orgId/bookings", @@ -24,11 +27,11 @@ import { GetOrganizationsBookingsInput } from "./inputs/get-org-bookings.input"; }) @UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard) @DocsTags("Orgs / Bookings") +@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER) +@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER) +@ApiHeader(OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER) export class OrganizationsBookingsController { - constructor( - private readonly bookingsService: BookingsService_2024_08_13, - private readonly orgUsersService: OrganizationsUsersService - ) {} + constructor(private readonly bookingsService: BookingsService_2024_08_13) {} @Get("/") @ApiOperation({ summary: "Get Organization Bookings" }) diff --git a/apps/api/v2/src/modules/organizations/users/bookings/controllers/organizations-users-bookings-controller.ts b/apps/api/v2/src/modules/organizations/users/bookings/controllers/organizations-users-bookings-controller.ts index 8ea6d60cc4..4ce0780571 100644 --- a/apps/api/v2/src/modules/organizations/users/bookings/controllers/organizations-users-bookings-controller.ts +++ b/apps/api/v2/src/modules/organizations/users/bookings/controllers/organizations-users-bookings-controller.ts @@ -1,6 +1,10 @@ import { API_VERSIONS_VALUES } from "@/lib/api-versions"; +import { + OPTIONAL_X_CAL_CLIENT_ID_HEADER, + OPTIONAL_X_CAL_SECRET_KEY_HEADER, + OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER, +} from "@/lib/docs/headers"; import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator"; -import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator"; import { Roles } from "@/modules/auth/decorators/roles/roles.decorator"; import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard"; import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard"; @@ -10,11 +14,10 @@ import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard"; import { IsUserInOrg } from "@/modules/auth/guards/users/is-user-in-org.guard"; import { OrganizationUsersBookingsService } from "@/modules/organizations/users/bookings/services/organization-users-bookings.service"; import { Controller, UseGuards, Get, Query, ParseIntPipe, Param } from "@nestjs/common"; -import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; +import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger"; import { SUCCESS_STATUS } from "@calcom/platform-constants"; import { GetBookingsInput_2024_08_13, GetBookingsOutput_2024_08_13 } from "@calcom/platform-types"; -import { User } from "@calcom/prisma/client"; @Controller({ path: "/v2/organizations/:orgId/users/:userId/bookings", @@ -22,6 +25,9 @@ import { User } from "@calcom/prisma/client"; }) @UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, IsUserInOrg, PlatformPlanGuard, IsAdminAPIEnabledGuard) @DocsTags("Orgs / Users / Bookings") +@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER) +@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER) +@ApiHeader(OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER) export class OrganizationsUsersBookingsController { constructor(private readonly organizationUsersBookingsService: OrganizationUsersBookingsService) {} @@ -30,13 +36,13 @@ export class OrganizationsUsersBookingsController { @PlatformPlan("ESSENTIALS") @ApiOperation({ summary: "Get all bookings of an organization user" }) async getOrganizationUserBookings( - @GetUser() user: User, @Param("orgId", ParseIntPipe) orgId: number, + @Param("userId", ParseIntPipe) userId: number, @Query() query: GetBookingsInput_2024_08_13 ): Promise { const bookings = await this.organizationUsersBookingsService.getOrganizationUserBookings( orgId, - user, + userId, query ); diff --git a/apps/api/v2/src/modules/organizations/users/bookings/services/organization-users-bookings.service.ts b/apps/api/v2/src/modules/organizations/users/bookings/services/organization-users-bookings.service.ts index c93214e398..2acc56bf11 100644 --- a/apps/api/v2/src/modules/organizations/users/bookings/services/organization-users-bookings.service.ts +++ b/apps/api/v2/src/modules/organizations/users/bookings/services/organization-users-bookings.service.ts @@ -1,18 +1,21 @@ import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service"; -import { Injectable } from "@nestjs/common"; +import { UsersRepository } from "@/modules/users/users.repository"; +import { Injectable, NotFoundException } from "@nestjs/common"; import { GetBookingsInput_2024_08_13 } from "@calcom/platform-types"; -import { User } from "@calcom/prisma/client"; @Injectable() export class OrganizationUsersBookingsService { - constructor(private readonly bookingsService: BookingsService_2024_08_13) {} + constructor( + private readonly bookingsService: BookingsService_2024_08_13, + private readonly usersRepository: UsersRepository + ) {} - async getOrganizationUserBookings( - orgId: number, - user: Pick, - queryParams: GetBookingsInput_2024_08_13 - ) { + async getOrganizationUserBookings(orgId: number, userId: number, queryParams: GetBookingsInput_2024_08_13) { + const user = await this.usersRepository.findById(userId); + if (!user) { + throw new NotFoundException(`getOrganizationUserBookings - User ${userId} not found`); + } return await this.bookingsService.getBookings(queryParams, { orgId, id: user.id, email: user.email }); } } diff --git a/apps/api/v2/swagger/documentation.json b/apps/api/v2/swagger/documentation.json index a015696ef3..22bc436197 100644 --- a/apps/api/v2/swagger/documentation.json +++ b/apps/api/v2/swagger/documentation.json @@ -1275,6 +1275,33 @@ "operationId": "OrganizationsBookingsController_getAllOrgTeamBookings", "summary": "Get Organization Bookings", "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` is api key prefixed with cal_ or managed user access token", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-secret-key", + "in": "header", + "description": "For platform customers - OAuth client secret key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-client-id", + "in": "header", + "description": "For platform customers - OAuth client ID", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "status", "required": false, @@ -4972,6 +4999,33 @@ "operationId": "OrganizationsUsersBookingsController_getOrganizationUserBookings", "summary": "Get all bookings of an organization user", "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` is api key prefixed with cal_ or managed user access token", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-secret-key", + "in": "header", + "description": "For platform customers - OAuth client secret key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-client-id", + "in": "header", + "description": "For platform customers - OAuth client ID", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "orgId", "required": true, @@ -4980,6 +5034,14 @@ "type": "number" } }, + { + "name": "userId", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, { "name": "status", "required": false, diff --git a/docs/api-reference/v2/openapi.json b/docs/api-reference/v2/openapi.json index 477637d88c..cab1189550 100644 --- a/docs/api-reference/v2/openapi.json +++ b/docs/api-reference/v2/openapi.json @@ -1225,6 +1225,33 @@ "operationId": "OrganizationsBookingsController_getAllOrgTeamBookings", "summary": "Get Organization Bookings", "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` is api key prefixed with cal_ or managed user access token", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-secret-key", + "in": "header", + "description": "For platform customers - OAuth client secret key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-client-id", + "in": "header", + "description": "For platform customers - OAuth client ID", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "status", "required": false, @@ -4756,6 +4783,33 @@ "operationId": "OrganizationsUsersBookingsController_getOrganizationUserBookings", "summary": "Get all bookings of an organization user", "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "value must be `Bearer ` where `` is api key prefixed with cal_ or managed user access token", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-secret-key", + "in": "header", + "description": "For platform customers - OAuth client secret key", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "x-cal-client-id", + "in": "header", + "description": "For platform customers - OAuth client ID", + "required": false, + "schema": { + "type": "string" + } + }, { "name": "orgId", "required": true, @@ -4764,6 +4818,14 @@ "type": "number" } }, + { + "name": "userId", + "required": true, + "in": "path", + "schema": { + "type": "number" + } + }, { "name": "status", "required": false, diff --git a/docs/platform/bookings-hooks.mdx b/docs/platform/bookings-hooks.mdx index 305d53eddb..544549f20f 100644 --- a/docs/platform/bookings-hooks.mdx +++ b/docs/platform/bookings-hooks.mdx @@ -5,13 +5,13 @@ description: Overview of all the hooks associated with bookings. Optimize your development workflow using our custom hooks. Our custom hooks are designed to simplify the integration of the Cal.com API into your products. With these hooks, you can effortlessly manage state, handle side effects, and optimize performance, allowing you to focus on building exceptional user experiences without the hassle of complex API interactions. -Below is a list of hooks you can find that are associated with bookings: +Below is a list of hooks you can find that are associated with bookings. Notably, all of these hooks are available only for platform customers. -### 1. `useBookings` +### 1. get all bookings - `useBookings` -The useBookings hook returns an array of all the booking data, allowing you to filter bookings by status, such as ***upcoming,*** ***recurring***, ***past,*** ***cancelled,*** or ***unconfirmed.*** It also supports pagination through the take and skip parameters. Additionally, various properties can be passed to useBookings, which can be referenced in the [following](https://github.com/calcom/cal.com/blob/307b2946a2cb6dcdb32dbee6c4f448e8a01c4285/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts#L32) documentation for detailed information. +The useBookings hook returns an array of bookings that belong to the user whose access token is passed to [\](https://cal.com/docs/platform/atoms/cal-provider). You can filter bookings by status, event type id, attendee email etc. - [see this class](https://github.com/calcom/cal.com/blob/307b2946a2cb6dcdb32dbee6c4f448e8a01c4285/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts#L32) for filters you can pass to the hook. -Below code snippet shows how to use the useBookings hook to fetch bookings for a specific user. +Below code snippet shows how to use the useBookings hook to fetch bookings for a specific user using take, skip and status filters: ```js import { useBookings } from "@calcom/atoms"; @@ -41,7 +41,7 @@ export default function Bookings() { } ``` -### 2. `useBooking` +### 2. get specific booking - `useBooking` The useBooking hook returns detailed information about a single booking. Simply provide the booking's unique identifier ***(bookingUid)*** as a parameter to fetch its associated data. @@ -68,7 +68,7 @@ export default function Booking() { } ``` -### 3. `useCancelBooking` +### 3. cancel booking - `useCancelBooking` The useCancelBooking hook allows you to cancel a booking. This hook returns a mutation function that handles the cancellation process. To cancel a booking, you'll need to provide at least the booking's unique identifier ***(uid).*** While the uid is the only mandatory parameter, you can enhance the cancellation process by including optional parameters such as ***cancellationReason*** to specify why the booking is being canceled, and ***allRemainingBookings*** to specify whether to cancel all future recurring bookings. @@ -98,4 +98,100 @@ export default function CancelBooking() { ); } +``` + +### 4. get all bookings of a user as an organization admin - `useOrganizationUserBookings` + +The useBookings hooks returns bookings of user associated with the access token passed to the [\](https://cal.com/docs/platform/atoms/cal-provider), but this hook +allows organization admins to fetch bookings of any organization member. + +The access token passed to the [\](https://cal.com/docs/platform/atoms/cal-provider) must belong +to a managed user who is an organization admin. That means you have to create a managed user and then add an accepted membership with an admin or owner role by making a request to the [organizations memberships endpoint](https://cal.com/docs/api-reference/v2/orgs-memberships/create-a-membership). Example body: +```js +{ + "userId": 1006, + "accepted": true, + "role": "ADMIN" +} +``` +then you have to pass the access token of this managed user to the [CalProvider](https://cal.com/docs/platform/atoms/cal-provider) to then finally be able to +use the "useOrganizationUserBookings" by specifying "userId" which will return an array of that user's bookings. You can filter bookings by status, event type id, attendee email etc. - [see this class](https://github.com/calcom/cal.com/blob/307b2946a2cb6dcdb32dbee6c4f448e8a01c4285/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts#L32) for filters you can pass to the hook. + +Below code snippet shows how to use the "useOrganizationUserBookings" hook to fetch bookings for a specific user using take, skip and status filters: + +```js +import { useOrganizationUserBookings } from "@calcom/atoms"; + +export default function Bookings() { + const someUserId = 1218; + + const { isLoading: isLoadingUpcomingBookings, data: upcomingBookings } = useOrganizationUserBookings(someUserId, { + take: 50, + skip: 0, + status: ["upcoming"], + }); + + return ( + <> +

Upcoming bookings

+ {isLoadingUpcomingBookings &&

Loading...

} + {!isLoadingUpcomingBookings && !upcomingBookings &&

No upcoming bookings found

} + {!isLoading && + upcomingBookings && + (Boolean(upcomingBookings?.length)) && + [...upcomingBookings].map((booking) => { + return ( +

{booking.title}

+ ); + })} + + ); +} +``` + +### 5. get all organization bookings as an organization admin - `useOrganizationBookings` + +The useBookings hooks returns bookings of user associated with the access token passed to the [\](https://cal.com/docs/platform/atoms/cal-provider), but this hook +allows organization admins to fetch bookings of the whole organization. + +The access token passed to the [\](https://cal.com/docs/platform/atoms/cal-provider) must belong +to a managed user who is an organization admin. That means you have to create a managed user and then add an accepted membership with an admin or owner role by making a request to the [organizations memberships endpoint](https://cal.com/docs/api-reference/v2/orgs-memberships/create-a-membership). Example body: +```js +{ + "userId": 1006, + "accepted": true, + "role": "ADMIN" +} +``` +then you have to pass the access token of this managed user to the [CalProvider](https://cal.com/docs/platform/atoms/cal-provider) to then finally be able to +use the "useOrganizationBookings" which will return an array of organization bookings. You can filter bookings by status, user ids, event type ids, attendee email etc. - [see this class](https://github.com/calcom/cal.com/blob/307b2946a2cb6dcdb32dbee6c4f448e8a01c4285/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts#L32) for filters you can pass to the hook + you can use "userIds" filter like in example below. + +Below code snippet shows how to use the "useOrganizationBookings" hook to fetch bookings for a specific users using take, skip and userIds filters: + +```js +import { useOrganizationBookings } from "@calcom/atoms"; + +export default function Bookings() { + const { isLoading: isLoadingUpcomingBookings, data: upcomingBookings } = useOrganizationBookings({ + take: 50, + skip: 0, + userIds: [10, 11], + }); + + return ( + <> +

Upcoming bookings

+ {isLoadingUpcomingBookings &&

Loading...

} + {!isLoadingUpcomingBookings && !upcomingBookings &&

No upcoming bookings found

} + {!isLoading && + upcomingBookings && + (Boolean(upcomingBookings?.length)) && + [...upcomingBookings].map((booking) => { + return ( +

{booking.title}

+ ); + })} + + ); +} ``` \ No newline at end of file diff --git a/packages/platform/atoms/hooks/bookings/useBookings.ts b/packages/platform/atoms/hooks/bookings/useBookings.ts index 125b1bd8f4..5598d151ee 100644 --- a/packages/platform/atoms/hooks/bookings/useBookings.ts +++ b/packages/platform/atoms/hooks/bookings/useBookings.ts @@ -11,6 +11,8 @@ import type { GetBookingsInput_2024_08_13 } from "@calcom/platform-types"; import http from "../../lib/http"; +const QUERY_KEY = "use-bookings"; + export const useBookings = (input: GetBookingsInput_2024_08_13) => { const pathname = `/${V2_ENDPOINTS.bookings}`; const headers = { @@ -18,23 +20,7 @@ export const useBookings = (input: GetBookingsInput_2024_08_13) => { }; const bookingsQuery = useQuery({ - queryKey: [ - "use-bookings", - input.status, - input.attendeeEmail, - input.attendeeName, - input.eventTypeIds, - input.eventTypeId, - input.teamsIds, - input.teamId, - input.afterStart, - input.beforeEnd, - input.sortStart, - input.sortEnd, - input.sortCreated, - input.take, - input.skip, - ], + queryKey: [QUERY_KEY, ...extractBookingsQueryKeys(input)], queryFn: async () => { return http .get(pathname, { @@ -52,3 +38,27 @@ export const useBookings = (input: GetBookingsInput_2024_08_13) => { return bookingsQuery; }; + +export function extractBookingsQueryKeys(query: GetBookingsInput_2024_08_13) { + return [ + query.status, + query.attendeeEmail, + query.attendeeName, + query.eventTypeIds, + query.eventTypeId, + query.teamsIds, + query.teamId, + query.afterStart, + query.beforeEnd, + query.afterCreatedAt, + query.beforeCreatedAt, + query.afterUpdatedAt, + query.beforeUpdatedAt, + query.sortStart, + query.sortEnd, + query.sortCreated, + query.sortUpdatedAt, + query.take, + query.skip, + ]; +} diff --git a/packages/platform/atoms/hooks/organizations/bookings/useOrganizationBookings.ts b/packages/platform/atoms/hooks/organizations/bookings/useOrganizationBookings.ts new file mode 100644 index 0000000000..97f1f1cce3 --- /dev/null +++ b/packages/platform/atoms/hooks/organizations/bookings/useOrganizationBookings.ts @@ -0,0 +1,38 @@ +import { useQuery } from "@tanstack/react-query"; + +import { SUCCESS_STATUS } from "@calcom/platform-constants"; +import type { GetBookingsOutput_2024_08_13, GetOrganizationsBookingsInput } from "@calcom/platform-types"; + +import http from "../../../lib/http"; +import { extractBookingsQueryKeys } from "../../bookings/useBookings"; +import { useAtomsContext } from "../../useAtomsContext"; + +export const QUERY_KEY = "use-organization-bookings"; + +export const useOrganizationBookings = (query: GetOrganizationsBookingsInput) => { + const { organizationId } = useAtomsContext(); + + const pathname = `/organizations/${organizationId}/bookings`; + + const organizationBookingsQuery = useQuery({ + queryKey: [QUERY_KEY, ...extractOrganizationBookingsQueryKeys(query)], + queryFn: async () => { + return http + .get(pathname, { + params: query, + }) + .then((res) => { + if (res.data.status === SUCCESS_STATUS) { + return res.data.data; + } + throw new Error(res.data?.error?.message); + }); + }, + }); + + return organizationBookingsQuery; +}; + +function extractOrganizationBookingsQueryKeys(query: GetOrganizationsBookingsInput) { + return [...extractBookingsQueryKeys(query), query.userIds]; +} diff --git a/packages/platform/atoms/hooks/organizations/bookings/useOrganizationUserBookings.ts b/packages/platform/atoms/hooks/organizations/bookings/useOrganizationUserBookings.ts new file mode 100644 index 0000000000..911e600cfb --- /dev/null +++ b/packages/platform/atoms/hooks/organizations/bookings/useOrganizationUserBookings.ts @@ -0,0 +1,34 @@ +import { useQuery } from "@tanstack/react-query"; + +import { SUCCESS_STATUS } from "@calcom/platform-constants"; +import type { GetBookingsInput_2024_08_13, GetBookingsOutput_2024_08_13 } from "@calcom/platform-types"; + +import http from "../../../lib/http"; +import { extractBookingsQueryKeys } from "../../bookings/useBookings"; +import { useAtomsContext } from "../../useAtomsContext"; + +export const QUERY_KEY = "use-organization-user-bookings"; + +export const useOrganizationUserBookings = (userId: number, query: GetBookingsInput_2024_08_13) => { + const { organizationId } = useAtomsContext(); + + const pathname = `/organizations/${organizationId}/users/${userId}/bookings`; + + const organizationUserBookingsQuery = useQuery({ + queryKey: [QUERY_KEY, userId, ...extractBookingsQueryKeys(query)], + queryFn: async () => { + return http + .get(pathname, { + params: query, + }) + .then((res) => { + if (res.data.status === SUCCESS_STATUS) { + return res.data.data; + } + throw new Error(res.data?.error?.message); + }); + }, + }); + + return organizationUserBookingsQuery; +}; diff --git a/packages/platform/atoms/index.ts b/packages/platform/atoms/index.ts index 3c128b8f6c..3ff8f90e70 100644 --- a/packages/platform/atoms/index.ts +++ b/packages/platform/atoms/index.ts @@ -34,3 +34,6 @@ export { PaymentForm } from "./event-types/payments/PaymentForm"; export { useCreateEventType } from "./hooks/event-types/private/useCreateEventType"; export { useCreateTeamEventType } from "./hooks/event-types/private/useCreateTeamEventType"; + +export { useOrganizationBookings } from "./hooks/organizations/bookings/useOrganizationBookings"; +export { useOrganizationUserBookings } from "./hooks/organizations/bookings/useOrganizationUserBookings"; diff --git a/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts b/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts index 9e601238aa..bc00daee75 100644 --- a/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts +++ b/packages/platform/types/bookings/2024-08-13/inputs/get-bookings.input.ts @@ -86,6 +86,9 @@ export class GetBookingsInput_2024_08_13 { if (typeof value === "string") { return value.split(",").map((eventTypeId: string) => parseInt(eventTypeId)); } + if (Array.isArray(value)) { + return value.map((eventTypeId: string | number) => +eventTypeId); + } return value; }) @IsArray() @@ -116,6 +119,9 @@ export class GetBookingsInput_2024_08_13 { if (typeof value === "string") { return value.split(",").map((teamId: string) => parseInt(teamId)); } + if (Array.isArray(value)) { + return value.map((teamId: string | number) => +teamId); + } return value; }) @IsArray() diff --git a/apps/api/v2/src/modules/organizations/bookings/inputs/get-org-bookings.input.ts b/packages/platform/types/organizations/bookings/get-org-bookings.input.ts similarity index 89% rename from apps/api/v2/src/modules/organizations/bookings/inputs/get-org-bookings.input.ts rename to packages/platform/types/organizations/bookings/get-org-bookings.input.ts index 7aa1a9ed44..ff25ab7358 100644 --- a/apps/api/v2/src/modules/organizations/bookings/inputs/get-org-bookings.input.ts +++ b/packages/platform/types/organizations/bookings/get-org-bookings.input.ts @@ -11,6 +11,9 @@ export class GetOrganizationsBookingsInput extends GetBookingsInput_2024_08_13 { if (typeof value === "string") { return value.split(",").map((userId: string) => parseInt(userId)); } + if (Array.isArray(value)) { + return value.map((userId: string | number) => +userId); + } return value; }) @IsNumber({}, { each: true }) diff --git a/packages/platform/types/organizations/bookings/index.ts b/packages/platform/types/organizations/bookings/index.ts new file mode 100644 index 0000000000..4b0385cfc4 --- /dev/null +++ b/packages/platform/types/organizations/bookings/index.ts @@ -0,0 +1 @@ +export * from "./get-org-bookings.input"; diff --git a/packages/platform/types/organizations/index.ts b/packages/platform/types/organizations/index.ts index 9a4630082b..e9c5702375 100644 --- a/packages/platform/types/organizations/index.ts +++ b/packages/platform/types/organizations/index.ts @@ -1 +1,2 @@ export * from "./teams"; +export * from "./bookings";