Skip to content

Commit b8f0000

Browse files
eunjae-leedevin-ai-integration[bot]Udit-takkar
authored
feat: hide member filter for users with only MEMBER role (calcom#23097)
- Conditionally disable userId column filter based on isTeamAdminOrOwner flag - Prevents MEMBER-only users from encountering backend permission errors - Users with ADMIN/OWNER role in any team can still access the filter Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
1 parent 618ef63 commit b8f0000

2 files changed

Lines changed: 30 additions & 9 deletions

File tree

apps/web/modules/bookings/views/bookings-listing-view.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,7 @@ function BookingsContent({ status }: BookingsProps) {
180180
columnHelper.accessor((row) => row.type === "data" && row.booking.user?.id, {
181181
id: "userId",
182182
header: t("member"),
183-
enableColumnFilter: true,
183+
enableColumnFilter: user?.isTeamAdminOrOwner ?? false,
184184
enableSorting: false,
185185
cell: () => null,
186186
meta: {

apps/web/playwright/bookings-list.e2e.ts

Lines changed: 29 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,6 @@ test.describe("Bookings", () => {
479479
const filterItemsConfig = [
480480
{ key: "eventTypeId", name: "Event Type", testId: "add-filter-item-eventTypeId" },
481481
{ key: "teamId", name: "Team", testId: "add-filter-item-teamId" },
482-
{ key: "userId", name: "Member", testId: "add-filter-item-userId" },
483482
{ key: "attendeeName", name: "Attendees Name", testId: "add-filter-item-attendeeName" },
484483
{ key: "attendeeEmail", name: "Attendee Email", testId: "add-filter-item-attendeeEmail" },
485484
{ key: "dateRange", name: "Date Range", testId: "add-filter-item-dateRange" },
@@ -488,8 +487,16 @@ test.describe("Bookings", () => {
488487

489488
const getFilterItemLocator = (page: Page, testId: string) => page.locator(`[data-testid="${testId}"]`);
490489

491-
test.beforeEach(async ({ page, users }) => {
492-
const user = await users.create();
490+
const setup = async ({
491+
isAdmin,
492+
page,
493+
users,
494+
}: {
495+
isAdmin: boolean;
496+
page: Page;
497+
users: Fixtures["users"];
498+
}) => {
499+
const user = isAdmin ? await users.create(undefined, { hasTeam: true }) : await users.create();
493500
await user.apiLogin();
494501
const bookingsGetResponse = page.waitForResponse((response) =>
495502
/\/api\/trpc\/bookings\/get.*/.test(response.url())
@@ -498,9 +505,10 @@ test.describe("Bookings", () => {
498505
await bookingsGetResponse;
499506
await page.locator('[data-testid="add-filter-button"]').click();
500507
await expect(page.locator(searchInputSelector)).toBeVisible();
501-
});
508+
};
502509

503-
test("should show all filter items initially and after clearing search", async ({ page }) => {
510+
test("should show all filter items initially and after clearing search", async ({ page, users }) => {
511+
await setup({ isAdmin: false, page, users });
504512
const searchInput = page.locator(searchInputSelector);
505513

506514
// Initial check: all defined filter items should be visible
@@ -524,7 +532,18 @@ test.describe("Bookings", () => {
524532
}
525533
});
526534

527-
test("search should be case-insensitive", async ({ page }) => {
535+
test("should show admin-only filter", async ({ page, users }) => {
536+
await setup({ isAdmin: true, page, users });
537+
538+
await expect(
539+
getFilterItemLocator(page, "add-filter-item-userId"),
540+
`Item "Member" should be visible initially`
541+
).toBeVisible();
542+
});
543+
544+
test("search should be case-insensitive", async ({ page, users }) => {
545+
await setup({ isAdmin: true, page, users });
546+
528547
const searchInput = page.locator(searchInputSelector);
529548

530549
// Search for "member" (lowercase)
@@ -534,7 +553,8 @@ test.describe("Bookings", () => {
534553
await expect(getFilterItemLocator(page, "add-filter-item-teamId")).toBeHidden();
535554
});
536555

537-
test("should individually find each filter item by its full name", async ({ page }) => {
556+
test("should individually find each filter item by its full name", async ({ page, users }) => {
557+
await setup({ isAdmin: false, page, users });
538558
const searchInput = page.locator(searchInputSelector);
539559

540560
for (const targetItem of filterItemsConfig) {
@@ -550,7 +570,8 @@ test.describe("Bookings", () => {
550570
}
551571
});
552572

553-
test("should show no items for a non-matching search term", async ({ page }) => {
573+
test("should show no items for a non-matching search term", async ({ page, users }) => {
574+
await setup({ isAdmin: false, page, users });
554575
const searchInput = page.locator(searchInputSelector);
555576
const nonExistentTerm = "NonExistentFilterXYZ123";
556577
await searchInput.fill(nonExistentTerm);

0 commit comments

Comments
 (0)