Skip to content

Comments

feat: hide member filter for users with only MEMBER role#23097

Merged
Udit-takkar merged 4 commits intomainfrom
devin/hide-member-filter-1755187558
Aug 19, 2025
Merged

feat: hide member filter for users with only MEMBER role#23097
Udit-takkar merged 4 commits intomainfrom
devin/hide-member-filter-1755187558

Conversation

@eunjae-lee
Copy link
Contributor

@eunjae-lee eunjae-lee commented Aug 14, 2025

feat: hide member filter for users with only MEMBER role

Summary

This PR conditionally hides the "Member" filter on the bookings page for users who only have MEMBER role across all their teams. The change prevents MEMBER-only users from encountering backend permission errors when attempting to filter bookings by members.

Key Changes:

  • Modified userId column definition in bookings-listing-view.tsx to use enableColumnFilter: user?.isTeamAdminOrOwner ?? false
  • Leverages existing isTeamAdminOrOwner flag from the me query to determine filter visibility
  • Users with ADMIN or OWNER role in any team will still see the filter

Context: The backend already throws permission errors for MEMBER-only users trying to filter by userIds (line 152 in packages/trpc/server/routers/viewer/bookings/get.handler.ts). This frontend change provides better UX by hiding the filter entirely rather than letting users encounter an error.

Screenshot 2025-08-18 at 16 45 11

(↑ that "Member" filter is hidden when the current user is not an owner or an admin.)

Review & Testing Checklist for Human

  • UI Verification: Confirm that enableColumnFilter: false actually hides the Member filter column (not just disables it) for MEMBER-only users
  • Role Testing: Test with multiple user scenarios - pure MEMBER role vs. users with ADMIN/OWNER in at least one team
  • Mixed Role Edge Cases: Verify behavior for users with different roles across different teams (the critical use case mentioned in the original request)
  • Regression Testing: Ensure ADMIN/OWNER users can still access and use the Member filter without issues
  • End-to-End Flow: Test that MEMBER-only users no longer encounter the backend permission error when navigating the bookings page

Recommended Test Plan:

  1. Set up users with different role combinations in the development environment
  2. Navigate to /bookings page as each user type
  3. Verify filter visibility matches expected behavior
  4. For ADMIN/OWNER users, test that the member filter works functionally

Diagram

%%{ init : { "theme" : "default" }}%%
graph TB
    BookingsView["apps/web/modules/bookings/views/<br/>bookings-listing-view.tsx"]:::major-edit
    MeQuery["packages/trpc/server/routers/viewer/<br/>me/get.handler.ts"]:::context
    BookingsHandler["packages/trpc/server/routers/viewer/<br/>bookings/get.handler.ts"]:::context
    UseMeQuery["packages/trpc/react/hooks/<br/>useMeQuery.ts"]:::context

    MeQuery -->|"returns isTeamAdminOrOwner flag"| UseMeQuery
    UseMeQuery -->|"provides user data"| BookingsView
    BookingsView -->|"conditionally enables userId filter"| BookingsView
    BookingsHandler -->|"throws error for MEMBER userIds filter"| BookingsView

    subgraph Legend
        L1[Major Edit]:::major-edit
        L2[Minor Edit]:::minor-edit  
        L3[Context/No Edit]:::context
    end

    classDef major-edit fill:#90EE90
    classDef minor-edit fill:#87CEEB
    classDef context fill:#FFFFFF
Loading

Notes

  • Risk Level: 🟡 Medium - The change is simple but relies on untested UI behavior and backend logic correctness
  • Testing Gap: Unable to verify UI changes locally due to development database login issues
  • Dependency: Success depends on isTeamAdminOrOwner flag being computed correctly for all edge cases

Link to Devin run: https://app.devin.ai/sessions/ad3dbd91dc8d48c4920f267908c420f0
Requested by: @eunjae-lee

- 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: eunjae@cal.com <hey@eunjae.dev>
@devin-ai-integration
Copy link
Contributor

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR that start with 'DevinAI'.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Aug 14, 2025

Walkthrough

The member (userId) column filter in the bookings listing was changed from always enabled to enabled only for team admins/owners (enableColumnFilter now uses user?.isTeamAdminOrOwner ?? false). Playwright E2E tests were updated: a setup helper distinguishes admin vs non-admin flows, tests were refactored, and an admin-specific test verifies the admin-only Member filter. No other column configurations or exported declarations changed.

Tip

🔌 Remote MCP (Model Context Protocol) integration is now available!

Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats.

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch devin/hide-member-filter-1755187558

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

CodeRabbit Commands (Invoked using PR/Issue comments)

Type @coderabbitai help to get the list of available commands.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Status, Documentation and Community

  • Visit our Status Page to check the current availability of CodeRabbit.
  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@vercel
Copy link

vercel bot commented Aug 18, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Preview Comments Updated (UTC)
cal Ignored Ignored Aug 19, 2025 0:36am
cal-eu Ignored Ignored Aug 19, 2025 0:36am

@eunjae-lee eunjae-lee marked this pull request as ready for review August 18, 2025 14:49
@graphite-app graphite-app bot requested a review from a team August 18, 2025 14:49
@dosubot dosubot bot added bookings area: bookings, availability, timezones, double booking ui area: UI, frontend, button, form, input ✨ feature New feature or request labels Aug 18, 2025
@graphite-app
Copy link

graphite-app bot commented Aug 18, 2025

Graphite Automations

"Add consumer team as reviewer" took an action on this PR • (08/18/25)

1 reviewer was added to this PR based on Keith Williams's automation.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🔭 Outside diff range comments (1)
apps/web/modules/bookings/views/bookings-listing-view.tsx (1)

134-150: Gate userIds in the TRPC query when the viewer isn’t an admin/owner

Prevent backend permission errors when a non-admin user deep-links or loads a saved segment containing userId by only forwarding userIds if user?.isTeamAdminOrOwner is true.

Locations to update:

  • apps/web/modules/bookings/views/bookings-listing-view.tsx at the TRPC query (around line 134)

Apply this diff:

--- a/apps/web/modules/bookings/views/bookings-listing-view.tsx
+++ b/apps/web/modules/bookings/views/bookings-listing-view.tsx
@@ -134,7 +134,7 @@
       teamIds,
-      userIds,
+      userIds: user?.isTeamAdminOrOwner ? userIds : undefined,
       attendeeName,
       attendeeEmail,
       bookingUid,

Follow-up tests:

  • Load as a MEMBER-only user with ?userId=<some-id> or a saved segment including userId → expect no backend error and results as if userId wasn’t set.
  • Mixed-role users (Admin in one team, Member in another): ensure filtering by a user from a team where they’re only a Member doesn’t error (or constrain selectable values accordingly).

Optionally, clear any pre-existing userId filter chips/URL params when the viewer isn’t permitted.

🧹 Nitpick comments (3)
apps/web/modules/bookings/views/bookings-listing-view.tsx (3)

289-289: Use strict equality for status comparison

Use === for consistency and to avoid subtle coercion.

-      if (status === "recurring" || status == "unconfirmed" || status === "cancelled") {
+      if (status === "recurring" || status === "unconfirmed" || status === "cancelled") {

320-321: Fix missing useMemo deps to avoid stale “today” grouping and filtering

Both memos depend on status and/or user?.timeZone but don’t list them, which can produce stale computations when status or timezone changes.

-  }, [query.data]);
+  }, [query.data, status, user?.timeZone]);
-  }, [query.data]);
+  }, [query.data, user?.timeZone]);

Also applies to: 339-339


286-305: Reduce repeat Day.js computations inside loops

Hot paths filter/transform bookings per render and call dayjs() repeatedly. Cache today once in the current timezone to cut work inside loops.

Example tweak (outside the selected lines):

const todayStr = useMemo(
  () => dayjs().tz(user?.timeZone).format("YYYY-MM-DD"),
  [user?.timeZone]
);

// Then compare in filters/maps:
dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") !== todayStr
// and
dayjs(booking.startTime).tz(user?.timeZone).format("YYYY-MM-DD") === todayStr

Also applies to: 322-329

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between e18b790 and 61df9e3.

📒 Files selected for processing (1)
  • apps/web/modules/bookings/views/bookings-listing-view.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.tsx

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Always use t() for text localization in frontend code; direct text embedding should trigger a warning

Files:

  • apps/web/modules/bookings/views/bookings-listing-view.tsx
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/web/modules/bookings/views/bookings-listing-view.tsx
🧬 Code Graph Analysis (1)
apps/web/modules/bookings/views/bookings-listing-view.tsx (1)
scripts/prepare-local-for-delegation-credentials-testing.js (1)
  • user (15-17)
🔇 Additional comments (1)
apps/web/modules/bookings/views/bookings-listing-view.tsx (1)

181-187: Member filter gated by isTeamAdminOrOwner — solid, targeted fix

Using enableColumnFilter: user?.isTeamAdminOrOwner ?? false correctly hides the Member filter for MEMBER-only users and keeps it visible for Admin/Owner users. Defaulting to false during ME loading avoids flicker and accidental exposure.

Udit-takkar
Udit-takkar previously approved these changes Aug 19, 2025
@Udit-takkar Udit-takkar enabled auto-merge (squash) August 19, 2025 10:01
@github-actions
Copy link
Contributor

github-actions bot commented Aug 19, 2025

E2E results are ready!

hbjORbj
hbjORbj previously approved these changes Aug 19, 2025
@eunjae-lee eunjae-lee dismissed stale reviews from hbjORbj and Udit-takkar via b2ff508 August 19, 2025 12:36
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
apps/web/playwright/bookings-list.e2e.ts (1)

573-575: Also cover the 'member' search term for non-admins

As noted above, asserting that “member” yields no results for non-admins would directly validate the admin-only gating.

🧹 Nitpick comments (4)
apps/web/playwright/bookings-list.e2e.ts (4)

490-509: Make roles explicit in setup to avoid flakiness; emulate MEMBER-with-team for non-admin path

Right now, isAdmin=true creates a user with a team but doesn’t explicitly set the role; isAdmin=false creates a user without a team. Since the UI gating depends on isTeamAdminOrOwner, relying on fixture defaults can make these tests flaky and not perfectly representative of the “MEMBER-only across teams” scenario the PR targets.

  • Ensure the admin path is deterministically ADMIN/OWNER.
  • Ensure the non-admin path is deterministically MEMBER in a team (the real-world case that must hide the filter).

Apply this diff to make the role explicit:

-      const user = isAdmin ? await users.create(undefined, { hasTeam: true }) : await users.create();
+      const user = isAdmin
+        ? await users.create(undefined, { hasTeam: true, teamRole: MembershipRole.ADMIN })
+        : await users.create(undefined, { hasTeam: true, teamRole: MembershipRole.MEMBER });

Optional: reduce flakes by waiting for the “me” query to settle before opening the filter:

// before navigating
const meGetResponse = page.waitForResponse((r) => /\/api\/trpc\/viewer\.me\.get/.test(r.url()));
// after goto
await Promise.all([meGetResponse, bookingsGetResponse]);

510-512: Add an explicit assertion that Member filter is hidden for non-admin

This directly asserts the core requirement for MEMBER-only users.

   test("should show all filter items initially and after clearing search", async ({ page, users }) => {
     await setup({ isAdmin: false, page, users });
+    await expect(getFilterItemLocator(page, "add-filter-item-userId")).toBeHidden();

544-546: Strengthen case-insensitive search by asserting all non-member items are hidden

You already verify two items; complete the coverage for the rest to avoid silent regressions.

   test("search should be case-insensitive", async ({ page, users }) => {
     await setup({ isAdmin: true, page, users });
@@
   await expect(getFilterItemLocator(page, "add-filter-item-userId")).toBeVisible();
   await expect(getFilterItemLocator(page, "add-filter-item-eventTypeId")).toBeHidden();
   await expect(getFilterItemLocator(page, "add-filter-item-teamId")).toBeHidden();
+  await expect(getFilterItemLocator(page, "add-filter-item-attendeeName")).toBeHidden();
+  await expect(getFilterItemLocator(page, "add-filter-item-attendeeEmail")).toBeHidden();
+  await expect(getFilterItemLocator(page, "add-filter-item-dateRange")).toBeHidden();

556-558: Add a dedicated negative test for non-admins searching “member”

To explicitly guard the regression this PR addresses, add a non-admin test that proves the Member filter never appears—even when searching for it.

Example:

test("non-admin should not see Member filter (initially or when searching 'member')", async ({ page, users }) => {
  await setup({ isAdmin: false, page, users });
  await expect(getFilterItemLocator(page, "add-filter-item-userId")).toBeHidden();

  const searchInput = page.locator(searchInputSelector);
  await searchInput.fill("member");
  await expect(getFilterItemLocator(page, "add-filter-item-userId")).toBeHidden();
});
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

💡 Knowledge Base configuration:

  • MCP integration is disabled by default for public repositories
  • Jira integration is disabled by default for public repositories
  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 61df9e3 and b2ff508.

📒 Files selected for processing (1)
  • apps/web/playwright/bookings-list.e2e.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.ts

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

**/*.ts: For Prisma queries, only select data you need; never use include, always use select
Ensure the credential.key field is never returned from tRPC endpoints or APIs

Files:

  • apps/web/playwright/bookings-list.e2e.ts
**/*.{ts,tsx}

📄 CodeRabbit Inference Engine (.cursor/rules/review.mdc)

Flag excessive Day.js use in performance-critical code; prefer native Date or Day.js .utc() in hot paths like loops

Files:

  • apps/web/playwright/bookings-list.e2e.ts
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Install dependencies / Yarn install & cache
🔇 Additional comments (1)
apps/web/playwright/bookings-list.e2e.ts (1)

535-543: LGTM: Admin-only filter visibility covered

Positive path for admins is asserted correctly with a clear, focused check.

@Udit-takkar Udit-takkar merged commit b8f0000 into main Aug 19, 2025
38 of 39 checks passed
@Udit-takkar Udit-takkar deleted the devin/hide-member-filter-1755187558 branch August 19, 2025 13:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bookings area: bookings, availability, timezones, double booking consumer core area: core, team members only ✨ feature New feature or request ready-for-e2e ui area: UI, frontend, button, form, input

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants