diff --git a/apps/web/playwright/bookings-list.e2e.ts b/apps/web/playwright/bookings-list.e2e.ts index 1b25d303de9538..8ca5972dc412cd 100644 --- a/apps/web/playwright/bookings-list.e2e.ts +++ b/apps/web/playwright/bookings-list.e2e.ts @@ -4,6 +4,7 @@ import { prisma } from "@calcom/prisma"; import { BookingStatus } from "@calcom/prisma/client"; import { MembershipRole } from "@calcom/prisma/enums"; +import { createTeamEventType } from "./fixtures/users"; import type { Fixtures } from "./lib/fixtures"; import { test } from "./lib/fixtures"; import { localize, setupManagedEvent } from "./lib/testUtils"; @@ -298,6 +299,129 @@ test.describe("Bookings", () => { firstUpcomingBooking.locator(`text=${firstUserBooking!.title}`) ).toBeVisible(); }); + + test("People filter includes bookings where filtered person is attendee", async ({ + page, + users, + bookings, + }) => { + const firstUser = await users.create( + { name: "First", email: "first@cal.com" }, + { + hasTeam: true, + teamRole: MembershipRole.ADMIN, + } + ); + const teamId = (await firstUser.getFirstTeamMembership()).teamId; + const secondUser = await users.create({ name: "Second", email: "second@cal.com" }); + const thirdUser = await users.create({ name: "Third", email: "third@cal.com" }); + // Add teammates to the team + await prisma.membership.createMany({ + data: [ + { + teamId: teamId, + userId: secondUser.id, + role: MembershipRole.MEMBER, + accepted: true, + }, + { + teamId: teamId, + userId: thirdUser.id, + role: MembershipRole.MEMBER, + accepted: true, + }, + ], + }); + const teamEvent = await createTeamEventType( + { id: firstUser.id }, + { id: teamId }, + { teamEventSlug: "team-event-slug" } + ); + + //Create a TeamEventType booking where ThirdUser is attendee + const thirdUserAttendeeTeamEventBookingFixture = await createBooking({ + title: "ThirdUser is Attendee for TeamEvent", + bookingsFixture: bookings, + relativeDate: 6, + organizer: firstUser, + organizerEventType: teamEvent, + attendees: [{ name: "Third", email: thirdUser.email, timeZone: "Europe/Berlin" }], + }); + const thirdUserAttendeeTeamEvent = await thirdUserAttendeeTeamEventBookingFixture.self(); + + //Create a IndividualEventType booking where ThirdUser,SecondUser are attendees and FirstUser is organizer + const thirdUserAttendeeIndividualBookingFixture = await createBooking({ + title: "ThirdUser is Attendee and FirstUser is Organizer", + bookingsFixture: bookings, + relativeDate: 3, + organizer: firstUser, + organizerEventType: firstUser.eventTypes[0], + attendees: [ + { name: "Third", email: thirdUser.email, timeZone: "Europe/Berlin" }, + { name: "Second", email: secondUser.email, timeZone: "Europe/Berlin" }, + ], + }); + const thirdUserAttendeeIndividualBooking = await thirdUserAttendeeIndividualBookingFixture.self(); + + //Create a IndividualEventType booking where ThirdUser is organizer and FirstUser,SecondUser are attendees + const thirdUserOrganizerBookingFixture = await createBooking({ + title: "ThirdUser is Organizer and FirstUser is Attendee", + bookingsFixture: bookings, + organizer: thirdUser, + relativeDate: 2, + organizerEventType: thirdUser.eventTypes[0], + attendees: [ + { name: "First", email: firstUser.email, timeZone: "Europe/Berlin" }, + { name: "Second", email: secondUser.email, timeZone: "Europe/Berlin" }, + ], + }); + const thirdUserOrganizerBooking = await thirdUserOrganizerBookingFixture.self(); + + //Create a booking where FirstUser is organizer and SecondUser is attendee + await createBooking({ + title: "FirstUser is Organizer and SecondUser is Attendee", + bookingsFixture: bookings, + organizer: firstUser, + relativeDate: 4, + organizerEventType: firstUser.eventTypes[0], + attendees: [{ name: "Second", email: secondUser.email, timeZone: "Europe/Berlin" }], + }); + + //admin login + //Select 'ThirdUser' in people filter + await firstUser.apiLogin(); + await Promise.all([ + page.waitForResponse((response) => /\/api\/trpc\/bookings\/get.*/.test(response.url())), + page.waitForResponse((response) => /\/api\/trpc\/bookings\/get.*/.test(response.url())), + page.goto(`/bookings/upcoming?status=upcoming&userIds=${thirdUser.id}`), + ]); + + //expect only 3 bookings (out of 4 total) to be shown in list. + //where ThirdUser is either organizer or attendee + const upcomingBookingsTable = page.locator('[data-testid="upcoming-bookings"]'); + const bookingListItems = upcomingBookingsTable.locator('[data-testid="booking-item"]'); + const bookingListCount = await bookingListItems.count(); + expect(bookingListCount).toBe(3); + + //verify with the booking titles + const firstUpcomingBooking = bookingListItems.nth(0); + await expect( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + firstUpcomingBooking.locator(`text=${thirdUserOrganizerBooking!.title}`) + ).toBeVisible(); + + const secondUpcomingBooking = bookingListItems.nth(1); + await expect( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + secondUpcomingBooking.locator(`text=${thirdUserAttendeeIndividualBooking!.title}`) + ).toBeVisible(); + + const thirdUpcomingBooking = bookingListItems.nth(2); + await expect( + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + thirdUpcomingBooking.locator(`text=${thirdUserAttendeeTeamEvent!.title}`) + ).toBeVisible(); + }); }); async function createBooking({ diff --git a/apps/web/playwright/fixtures/users.ts b/apps/web/playwright/fixtures/users.ts index b2b7340ea183df..a57d38e7cf0c11 100644 --- a/apps/web/playwright/fixtures/users.ts +++ b/apps/web/playwright/fixtures/users.ts @@ -86,7 +86,7 @@ const createTeamWorkflow = async (user: { id: number }, team: { id: number }) => }); }; -const createTeamEventType = async ( +export const createTeamEventType = async ( user: { id: number }, team: { id: number }, scenario?: { diff --git a/packages/app-store/intercom/api/initialize.ts b/packages/app-store/intercom/api/initialize.ts index 600a4095eca7cb..525e6fada1640d 100644 --- a/packages/app-store/intercom/api/initialize.ts +++ b/packages/app-store/intercom/api/initialize.ts @@ -1,5 +1,7 @@ import type { NextApiRequest, NextApiResponse } from "next"; + import { WEBAPP_URL } from "@calcom/lib/constants"; + import type { NewCanvas } from "../lib"; export default async function handler(req: NextApiRequest, res: NextApiResponse) { diff --git a/packages/trpc/server/routers/viewer/bookings/get.handler.ts b/packages/trpc/server/routers/viewer/bookings/get.handler.ts index 4a5e41c6d6bd8c..058e5e6236c855 100644 --- a/packages/trpc/server/routers/viewer/bookings/get.handler.ts +++ b/packages/trpc/server/routers/viewer/bookings/get.handler.ts @@ -102,6 +102,18 @@ export async function getBookings({ } if (filters?.userIds && filters.userIds.length > 0) { + const users = await prisma.user.findMany({ + where: { + id: { + in: filters.userIds, + }, + }, + select: { + email: true, + }, + }); + const attendeeEmailIds = users && users.length > 0 ? users.map((user) => user.email) : []; + bookingWhereInputFilters.userIds = { AND: [ { @@ -134,6 +146,17 @@ export async function getBookings({ }, }, }, + ...(attendeeEmailIds.length > 0 + ? [ + { + attendees: { + some: { + email: { in: attendeeEmailIds }, + }, + }, + }, + ] + : []), ], }, ],