Skip to content

Comments

feat: add report bookings feature#22709

Closed
husniabad wants to merge 14 commits intocalcom:mainfrom
husniabad:feature/report-bookings
Closed

feat: add report bookings feature#22709
husniabad wants to merge 14 commits intocalcom:mainfrom
husniabad:feature/report-bookings

Conversation

@husniabad
Copy link
Contributor

What does this PR do?

This PR introduces a new feature allowing users to report suspicious bookings from their /bookings list. It adds a "Report" option to the booking actions, which opens a new dialog to collect details. Users can optionally cancel an upcoming booking directly when reporting it.

This change includes UI updates to the bookings list, the new "Report Booking" dialog component, and the necessary backend API and database models (BookingReport, ReportReason) to handle submissions.

  • Fixes Report bookings #22696 (GitHub issue number)
  • Fixes CAL-6139 (Linear issue number - should be visible at the bottom of the GitHub issue description)

Visual Demo (For contributors especially)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code (A decent size PR without self-review might be rejected).
  • I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
  • I confirm automated tests are in place that prove my fix is effective or that my feature works.

How should this be tested?

  • Navigate to the /bookings page (upcoming, past, or cancelled).

  • Select "Report" from a booking's action menu.

  • For an upcoming booking: Fill out the dialog and click "Report", or check the "Cancel booking?" box, and click "Report and Cancel".

  • Expected: The booking is Reported and now displays a "Reported" badge, if you checked the "cancel" checkox the booking wil marked "Reported" and shift to cancelled bookings. A new entry exists in the BookingReport database table.

  • For a past or cancelled booking: Fill out the dialog and click "Report".

  • Expected: The booking now displays a "Reported" badge. The "Report" option is no longer available for this booking.

@husniabad husniabad requested a review from a team as a code owner July 24, 2025 05:21
@husniabad husniabad requested a review from a team July 24, 2025 05:21
@vercel
Copy link

vercel bot commented Jul 24, 2025

@husniabad is attempting to deploy a commit to the cal Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jul 24, 2025

Walkthrough

Adds end-to-end booking reporting: frontend UI (ReportBookingDialog, report actions in BookingListItem, reported badge, i18n), a getReportActions helper and tests, Prisma schema + migration for BookingReport and ReportReason, TRPC schema/procedure/handler (reportBooking) that creates reports and can invoke existing cancellation flow, backend booking queries augmented to include reports, and small validation/handler updates to allow cancellations originating from reports.

Assessment against linked issues

Objective Addressed Explanation
Allow reporting for any booking shown in /bookings (upcoming, past, cancelled) [#22696, CAL-6139]
UI: Add "Report" to dropdown for upcoming/past; show "Report" next to Video Options for cancelled; show "Reported" badge and prevent re-reporting [#22696, CAL-6139]
Report Dialog: title/subtitle, required reason (Spam, Don’t know this person, Other), optional comments, cancel-checkbox only for upcoming, info text, and buttons reflecting checkbox state [#22696, CAL-6139]
Behavior: Persist BookingReport in DB and call existing cancellation flow when "Cancel booking" selected; permissions allow users who can see the booking to report (owner/attendee/team admin/owner) [#22696, CAL-6139]

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Added idempotencyKey: String? @unique to Booking model (packages/prisma/schema.prisma; migration present in packages/prisma/migrations/20250823231324_add_booking_report/migration.sql) This field is unrelated to the reporting feature objectives; no requirement in linked issues asks for an idempotencyKey on bookings.
Addition of getMembershipIdsWhereUserIsAdminOwner helper (packages/lib/server/queries/teams/index.ts) and its extraction used in bookings get handler (packages/trpc/server/routers/viewer/bookings/get.handler.ts) While used by report access checks, introducing a new exported helper is a broader API surface change not requested by the linked issues; reasonableness is likely, but the helper itself is beyond the stated reporting requirements.

Possibly related PRs

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

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.

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.

@github-actions github-actions bot added bookings area: bookings, availability, timezones, double booking consumer ui area: UI, frontend, button, form, input ✨ feature New feature or request ❗️ migrations contains migration files and removed ✨ feature New feature or request bookings area: bookings, availability, timezones, double booking ui area: UI, frontend, button, form, input consumer labels Jul 24, 2025
@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Jul 24, 2025
@graphite-app graphite-app bot requested a review from a team July 24, 2025 05:21
@dosubot dosubot bot added bookings area: bookings, availability, timezones, double booking ✨ feature New feature or request labels Jul 24, 2025
@github-actions github-actions bot added consumer ui area: UI, frontend, button, form, input labels Jul 24, 2025
@graphite-app
Copy link

graphite-app bot commented Jul 24, 2025

Graphite Automations

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

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

"Add community label" took an action on this PR • (07/24/25)

1 label 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: 3

🧹 Nitpick comments (10)
apps/web/components/booking/bookingActions.report.test.ts (2)

9-121: Well-structured mock factory with good override capability.

The createMockContext function provides comprehensive mock data and allows for flexible customization through the overrides parameter. The Date calculations are straightforward and appropriate for test scenarios.

Consider extracting the time calculations to constants for better readability:

function createMockContext(overrides: Partial<BookingActionContext> = {}): BookingActionContext {
  const now = new Date();
+  const ONE_DAY_MS = 24 * 60 * 60 * 1000;
+  const ONE_HOUR_MS = 60 * 60 * 1000;
-  const startTime = new Date(now.getTime() + 24 * 60 * 60 * 1000); // Tomorrow
-  const endTime = new Date(startTime.getTime() + 60 * 60 * 1000); // 1 hour later
+  const startTime = new Date(now.getTime() + ONE_DAY_MS); // Tomorrow
+  const endTime = new Date(startTime.getTime() + ONE_HOUR_MS); // 1 hour later

157-187: Good coverage for timing scenarios.

The tests properly verify that report actions are available for both past and upcoming bookings, ensuring the feature works across different time contexts.

For consistency with the earlier suggestion, consider extracting the time calculation constants:

    it("should return report action for past bookings", () => {
+      const ONE_DAY_MS = 24 * 60 * 60 * 1000;
+      const ONE_HOUR_MS = 60 * 60 * 1000;
-      const pastDate = new Date(Date.now() - 24 * 60 * 60 * 1000); // Yesterday
+      const pastDate = new Date(Date.now() - ONE_DAY_MS); // Yesterday
      const context = createMockContext({
        booking: {
          startTime: pastDate.toISOString(),
-          endTime: new Date(pastDate.getTime() + 60 * 60 * 1000).toISOString(),
+          endTime: new Date(pastDate.getTime() + ONE_HOUR_MS).toISOString(),
        } as any,
apps/web/public/static/locales/en/common.json (2)

3389-3404: Prefer namespacing & avoid overly-generic keys

Keys such as "report", "reason_required" or "reported" are very broad and risk clashing with future, unrelated strings.
Consider prefixing all new entries with a shared context, e.g. report_booking_*, to keep the dictionary tidy and self-describing:

-  "report": "Report",
-  "reason_required": "Reason is required",
-  "reported": "Reported",
+  "report_booking_action": "Report",
+  "report_booking_reason_required": "Reason is required",
+  "report_booking_status_reported": "Reported",

This also makes dead-key searches & i18n extraction easier.
(Other keys in this block already follow the pattern, so aligning all of them keeps things consistent.)


3399-3401: Slight wording improvement

"attendee_not_notified_report" reads awkwardly.
Suggestion:

-  "attendee_not_notified_report": "Attendee will not be notified that this booking was reported"
+  "attendee_not_notified_report": "Attendee will not be notified that this booking has been reported"

Present-perfect tense is more natural here.

apps/web/components/dialog/ReportBookingDialog.tsx (2)

34-42: Remove unused parameter

The bookingTitle parameter is renamed to _bookingTitle but never used in the component.

Either remove the parameter from the interface and props, or use it if it's intended for future use:

 export function ReportBookingDialog({
   isOpen,
   onClose,
   bookingId,
-  bookingTitle: _bookingTitle,
   isUpcoming,
   isCancelled = false,
   onSuccess,
 }: ReportBookingDialogProps) {

79-84: Consider optimizing form reset behavior

The form is reset both when closing the dialog and after successful submission. Consider resetting only on successful submission to preserve user input if they accidentally close the dialog.

 const handleClose = () => {
   if (!isSubmitting) {
     onClose();
-    form.reset();
   }
 };
packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (2)

52-56: Optimize team admin/owner authorization checks

The current implementation awaits each check sequentially. Consider parallelizing these checks for better performance.

-  const isTeamAdminOrOwner =
-    booking.eventType?.teamId &&
-    ((await isTeamAdmin(user.id, booking.eventType.teamId)) ||
-      (await isTeamOwner(user.id, booking.eventType.teamId)));
+  let isTeamAdminOrOwner = false;
+  if (booking.eventType?.teamId) {
+    const [isAdmin, isOwner] = await Promise.all([
+      isTeamAdmin(user.id, booking.eventType.teamId),
+      isTeamOwner(user.id, booking.eventType.teamId)
+    ]);
+    isTeamAdminOrOwner = isAdmin || isOwner;
+  }

22-44: Consider optimizing the duplicate report check

Instead of fetching all reports and checking length, consider using a count query or exists check for better performance.

Update the booking query to only check if a report exists:

       reports: {
+        take: 1,
         where: {
           reportedById: user.id,
         },
       },

This will limit the query to fetch at most one report, which is sufficient for the existence check.

packages/prisma/migrations/20250723214119_add_booking_report/migration.sql (1)

1-2: Consider consistent enum naming convention

The enum value 'dont_know_person' uses snake_case while 'SPAM' and 'OTHER' use UPPER_CASE. Consider using consistent naming.

-CREATE TYPE "ReportReason" AS ENUM ('SPAM', 'dont_know_person', 'OTHER');
+CREATE TYPE "ReportReason" AS ENUM ('SPAM', 'DONT_KNOW_PERSON', 'OTHER');

Note: This would require updating the Prisma schema mapping as well.

apps/web/components/booking/BookingListItem.tsx (1)

403-419: Improve readability and consider edge cases.

The logic correctly adds a report action for non-cancelled bookings, but could be improved:

  1. The conditional logic could be more readable
  2. Consider extracting the report action creation into a helper function for reusability
+const createReportAction = (hasBeenReported: boolean, onReport: () => void): ActionType => {
+  return hasBeenReported
+    ? {
+        id: "report",
+        label: t("already_reported"),
+        icon: "flag",
+        disabled: true,
+      }
+    : {
+        id: "report",
+        label: t("report"),
+        icon: "flag",
+        disabled: false,
+        onClick: onReport,
+      };
+};

 if (!isCancelled) {
-  const reportAction: ActionType = hasBeenReported
-    ? {
-        id: "report",
-        label: t("already_reported"),
-        icon: "flag",
-        disabled: true,
-      }
-    : {
-        id: "report",
-        label: t("report"),
-        icon: "flag",
-        disabled: false,
-        onClick: () => setIsReportDialogOpen(true),
-      };
+  const reportAction = createReportAction(hasBeenReported, () => setIsReportDialogOpen(true));
   afterEventActions.push(reportAction);
 }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 611f76b and 0880cc0.

📒 Files selected for processing (13)
  • apps/web/components/booking/BookingListItem.tsx (6 hunks)
  • apps/web/components/booking/bookingActions.report.test.ts (1 hunks)
  • apps/web/components/booking/bookingActions.ts (2 hunks)
  • apps/web/components/dialog/ReportBookingDialog.test.tsx (1 hunks)
  • apps/web/components/dialog/ReportBookingDialog.tsx (1 hunks)
  • apps/web/public/static/locales/en/common.json (1 hunks)
  • packages/prisma/migrations/20250723214119_add_booking_report/migration.sql (1 hunks)
  • packages/prisma/schema.prisma (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/_router.tsx (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/reportBooking.schema.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/reportBooking.test.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

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

Flag excessive Day.js use in performance-critical code. Functions like .add, .diff, .isBefore, and .isAfter are slow, especially in timezone mode. Prefer .utc() for better performance. Where possible, replace with native Date and direct .valueOf() comparisons for faster execution. Recommend using native methods or Day.js .utc() consistently in hot paths like loops.

Files:

  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • apps/web/components/booking/bookingActions.ts
  • packages/trpc/server/routers/viewer/bookings/reportBooking.schema.ts
  • packages/trpc/server/routers/viewer/bookings/reportBooking.test.ts
  • packages/trpc/server/routers/viewer/bookings/_router.tsx
  • apps/web/components/booking/bookingActions.report.test.ts
  • apps/web/components/dialog/ReportBookingDialog.test.tsx
  • apps/web/components/dialog/ReportBookingDialog.tsx
  • packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts
  • apps/web/components/booking/BookingListItem.tsx
🧠 Learnings (5)
packages/trpc/server/routers/viewer/bookings/get.handler.ts (2)

Learnt from: CR
PR: calcom/cal.com#0
File: .cursor/rules/review.mdc:0-0
Timestamp: 2025-07-21T13:54:11.770Z
Learning: Applies to backend/**/*.{ts,tsx} : For Prisma queries: Include selects the relationship AND all the fields of the table the relationship belongs to.

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

packages/trpc/server/routers/viewer/bookings/_router.tsx (1)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

apps/web/public/static/locales/en/common.json (1)

Learnt from: bandhan-majumder
PR: #22359
File: packages/lib/server/locales/en/common.json:1336-1339
Timestamp: 2025-07-14T16:31:45.233Z
Learning: When making localization changes for new features, it's often safer to add new strings rather than modify existing ones to avoid breaking existing functionality that depends on the original strings. This approach allows for feature-specific customization while maintaining backward compatibility.

apps/web/components/dialog/ReportBookingDialog.tsx (1)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

apps/web/components/booking/BookingListItem.tsx (1)

Learnt from: eunjae-lee
PR: #22106
File: packages/features/insights/components/FailedBookingsByField.tsx:65-71
Timestamp: 2025-07-15T12:59:34.389Z
Learning: In the FailedBookingsByField component (packages/features/insights/components/FailedBookingsByField.tsx), although routingFormId is typed as optional in useInsightsParameters, the system automatically enforces a routing form filter, so routingFormId is always present in practice. This means the data always contains only one entry, making the single-entry destructuring approach safe.

🧬 Code Graph Analysis (3)
packages/trpc/server/routers/viewer/bookings/_router.tsx (2)
packages/trpc/server/routers/viewer/bookings/reportBooking.schema.ts (1)
  • ZReportBookingInputSchema (5-10)
packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (1)
  • reportBookingHandler (18-100)
apps/web/components/booking/bookingActions.report.test.ts (1)
apps/web/components/booking/bookingActions.ts (2)
  • BookingActionContext (6-34)
  • getReportActions (188-201)
apps/web/components/booking/BookingListItem.tsx (1)
apps/web/components/dialog/ReportBookingDialog.tsx (1)
  • ReportBookingDialog (34-146)
⏰ 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). (3)
  • GitHub Check: Type check / check-types
  • GitHub Check: Tests / Unit
  • GitHub Check: Linters / lint
🔇 Additional comments (25)
packages/trpc/server/routers/viewer/bookings/get.handler.ts (1)

560-573: LGTM! Clean integration of booking reports data.

The jsonArrayFrom subquery correctly fetches associated booking reports with proper field selection and ordering. The descending order by createdAt ensures the most recent reports appear first, which is appropriate for UI display.

apps/web/components/booking/bookingActions.ts (2)

188-201: LGTM! Well-structured report action implementation.

The getReportActions function follows the established pattern in the file, returning a properly structured action with appropriate icon ("flag") and localized label.


255-256: LGTM! Consistent with existing action label handling.

The report case is properly added to the switch statement, maintaining consistency with other action types.

packages/trpc/server/routers/viewer/bookings/_router.tsx (3)

11-11: LGTM! Import follows established pattern.

The schema import is consistent with other input schema imports in the file.


24-24: LGTM! Handler type cache addition follows convention.

The type definition is properly added to the handler cache, maintaining consistency with other handlers.


104-111: LGTM! Procedure implementation follows established patterns.

The reportBooking procedure correctly uses:

  • authedProcedure (appropriate for user-specific operations)
  • mutation (correct for state-changing operations)
  • Dynamic import pattern consistent with other procedures
  • Proper input validation with ZReportBookingInputSchema
packages/trpc/server/routers/viewer/bookings/reportBooking.schema.ts (2)

5-10: LGTM! Well-designed schema with appropriate field types.

The schema is thoughtfully designed:

  • bookingId as number correctly matches database ID type
  • reason using native enum ensures type safety
  • description as optional string provides flexibility for additional context
  • cancelBooking with false default prevents accidental cancellations

12-12: LGTM! Standard type inference export.

Type export follows TypeScript/zod conventions for schema inference.

packages/trpc/server/routers/viewer/bookings/reportBooking.test.ts (4)

22-49: LGTM! Well-structured test setup with comprehensive mock data.

The mock data covers all essential booking properties and user scenarios needed for thorough testing. The beforeEach cleanup ensures test isolation.


50-83: LGTM! Comprehensive happy path test.

The test validates both the success response and the correct database interaction, ensuring the handler behaves as expected for valid inputs.


85-98: LGTM! Thorough error case coverage.

The test suite properly validates:

  • Booking not found scenarios
  • Access control enforcement (user must be owner, attendee, or team admin/owner)
  • Duplicate report prevention

All error cases correctly expect TRPCError to be thrown.

Also applies to: 100-125, 127-150


152-179: LGTM! Combined functionality test is well-implemented.

The test validates the integrated reporting and cancellation workflow, ensuring both the report is created and the booking cancellation handler is invoked when requested.

apps/web/components/booking/bookingActions.report.test.ts (3)

1-8: LGTM! Clean test setup with appropriate imports.

The imports are well-organized and the mock translation function is simple but effective for testing purposes.


123-155: Comprehensive test coverage for basic functionality and status variations.

The tests effectively verify that the report action is consistently available across all booking statuses. The data-driven approach for testing multiple statuses is efficient and thorough.


189-253: Thorough coverage for different booking types.

The tests effectively verify that the report action is available for both team and individual bookings, ensuring the feature works regardless of the booking's organizational structure.

The detailed mock data setup, while verbose, clearly demonstrates the different scenarios being tested.

apps/web/components/dialog/ReportBookingDialog.test.tsx (1)

1-155: Comprehensive test coverage!

The test suite thoroughly covers the ReportBookingDialog component with well-structured tests for rendering, user interactions, form submission, and conditional behavior. The mocks are properly configured and the test cases follow best practices.

packages/prisma/migrations/20250723214119_add_booking_report/migration.sql (1)

4-27: Well-structured migration!

The migration properly defines the BookingReport table with appropriate constraints, indexes, and CASCADE rules for maintaining referential integrity.

packages/prisma/schema.prisma (2)

730-749: Well-designed schema additions!

The ReportReason enum and BookingReport model are properly structured with:

  • Appropriate field types and defaults
  • Proper foreign key relationships with CASCADE rules
  • Necessary indexes for query performance
  • Clear mapping for the enum value

423-423: Proper relation setup!

The bidirectional relations between User ↔ BookingReport and Booking ↔ BookingReport are correctly established.

Also applies to: 817-817

apps/web/components/booking/BookingListItem.tsx (6)

55-55: LGTM: Import statement is correct.

The import for ReportBookingDialog follows the established pattern and is properly placed with other dialog imports.


136-136: LGTM: State variable follows established pattern.

The isReportDialogOpen state variable is consistent with other dialog state variables in the component (e.g., isNoShowDialogOpen, rejectionDialogIsOpen).


750-764: LGTM: Cancelled booking report action rendering is well implemented.

The conditional rendering and button properties are correctly implemented:

  • Proper conditional rendering with cancelledBookingReportAction &&
  • Correct event handlers and disabled state
  • Appropriate styling and accessibility with tooltip
  • Good use of data-testid for testing

777-788: LGTM: ReportBookingDialog integration is well implemented.

The dialog integration correctly:

  • Passes all required props including booking details and state flags
  • Handles the onSuccess callback to invalidate cache and refresh data
  • Uses proper state management with isReportDialogOpen
  • Follows the established pattern for other dialogs in the component

831-835: LGTM: Reported badge implementation is correct.

The badge logic correctly:

  • Checks if reports exist and have length > 0
  • Uses appropriate red variant to indicate reporting status
  • Follows the established pattern for other badges
  • Uses proper spacing classes

400-402: No changes needed for report-checking logic

The reports subquery is always selected in the booking resolver (returning an empty array when there are no reports), and BookingReport.reportedById is non-nullable in the Prisma schema. Likewise, loggedInUser.userId is populated from the authenticated session (and guaranteed to be defined by the time this component renders). Your existing optional chaining and fallback to false already cover all edge cases here.

Copy link
Contributor

@Devanshusharma2005 Devanshusharma2005 left a comment

Choose a reason for hiding this comment

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

Could you please address the coderabbit suggestions and unit tests are also failing, Marking it draft until then. Feel free to rfr.

@Devanshusharma2005 Devanshusharma2005 marked this pull request as draft July 24, 2025 05:42
@husniabad husniabad force-pushed the feature/report-bookings branch from 0880cc0 to 1020be3 Compare July 24, 2025 07:31
@husniabad
Copy link
Contributor Author

Hi @Devanshusharma2005 all suggestions and changes addressed, could you please check again

@husniabad husniabad marked this pull request as ready for review July 24, 2025 08:02
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)
packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (1)

76-97: Improved error handling addresses previous feedback.

The cancellation error handling has been significantly improved from the previous review. The implementation now:

  • Captures cancellation errors properly
  • Provides clear error messages to users
  • Returns error details in the response

This addresses the previous concern about silent cancellation failures.

🧹 Nitpick comments (2)
packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (2)

22-45: Consider optimizing the database query structure.

While the query is functionally correct, the nested include structure could be optimized for better performance and maintainability.

Consider flattening the query structure and using more specific selections:

  const booking = await prisma.booking.findUnique({
    where: {
      id: bookingId,
    },
    include: {
      attendees: {
        select: {
          email: true,
        },
      },
      eventType: {
        select: {
+         teamId: true,
-         team: {
-           select: {
-             id: true,
-             name: true,
-             parentId: true,
-           },
-         },
        },
      },
      reports: {
        where: {
          reportedById: user.id,
        },
+       select: {
+         id: true,
+       },
      },
    },
  });

This reduces the data transferred and improves query performance by only selecting necessary fields.


62-64: Good duplicate prevention, but consider the user experience.

The logic correctly prevents duplicate reports, but the error message could be more user-friendly.

Consider a more informative error message:

  if (booking.reports.length > 0) {
-   throw new TRPCError({ code: "BAD_REQUEST", message: "You have already reported this booking" });
+   throw new TRPCError({ 
+     code: "BAD_REQUEST", 
+     message: "You have already reported this booking. Multiple reports are not allowed." 
+   });
  }
📜 Review details

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

📥 Commits

Reviewing files that changed from the base of the PR and between 0880cc0 and 1020be3.

📒 Files selected for processing (12)
  • apps/web/components/booking/BookingListItem.tsx (6 hunks)
  • apps/web/components/booking/bookingActions.report.test.ts (1 hunks)
  • apps/web/components/booking/bookingActions.ts (2 hunks)
  • apps/web/components/dialog/ReportBookingDialog.test.tsx (1 hunks)
  • apps/web/components/dialog/ReportBookingDialog.tsx (1 hunks)
  • apps/web/public/static/locales/en/common.json (1 hunks)
  • packages/prisma/migrations/20250723214119_add_booking_report/migration.sql (1 hunks)
  • packages/prisma/schema.prisma (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/_router.tsx (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/reportBooking.schema.ts (1 hunks)
✅ Files skipped from review due to trivial changes (2)
  • packages/trpc/server/routers/viewer/bookings/reportBooking.schema.ts
  • apps/web/public/static/locales/en/common.json
🚧 Files skipped from review as they are similar to previous changes (9)
  • apps/web/components/booking/bookingActions.report.test.ts
  • apps/web/components/booking/bookingActions.ts
  • packages/trpc/server/routers/viewer/bookings/_router.tsx
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • apps/web/components/dialog/ReportBookingDialog.tsx
  • packages/prisma/schema.prisma
  • apps/web/components/booking/BookingListItem.tsx
  • apps/web/components/dialog/ReportBookingDialog.test.tsx
  • packages/prisma/migrations/20250723214119_add_booking_report/migration.sql
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{js,jsx,ts,tsx}

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

Flag excessive Day.js use in performance-critical code. Functions like .add, .diff, .isBefore, and .isAfter are slow, especially in timezone mode. Prefer .utc() for better performance. Where possible, replace with native Date and direct .valueOf() comparisons for faster execution. Recommend using native methods or Day.js .utc() consistently in hot paths like loops.

Files:

  • packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts
🧠 Learnings (1)
packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (1)

Learnt from: CarinaWolli
PR: #22296
File: packages/lib/bookings/filterHostsBySameRoundRobinHost.ts:41-42
Timestamp: 2025-07-22T11:42:47.592Z
Learning: The filterHostsBySameRoundRobinHost function in packages/lib/bookings/filterHostsBySameRoundRobinHost.ts has a known limitation where it doesn't work correctly with fixed hosts or round robin groups. This is pre-existing technical debt that was already broken before the round robin groups feature. CarinaWolli has documented this in Linear issue CAL-6134 for future fix.

⏰ 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: Codacy Static Code Analysis
🔇 Additional comments (4)
packages/trpc/server/routers/viewer/bookings/reportBooking.handler.ts (4)

1-16: LGTM! Clean imports and type definitions.

The imports are well-organized and the type definitions follow TypeScript best practices. The handler options type properly extends the TRPC pattern.


51-60: Excellent authorization logic with comprehensive access control.

The authorization properly covers all access scenarios:

  • Booking owner access
  • Attendee access
  • Team admin/owner access for team bookings

The implementation correctly handles both team admin and team owner permissions.


99-108: Well-structured response with comprehensive error information.

The response structure properly handles all scenarios and provides clear feedback to the user about both reporting and cancellation outcomes.


78-82: Booking startTime comparison is timezone-safe
The booking.startTime value is normalized to UTC (see handleNewBooking.ts using dayjs(reqBody.start).utc()), and stored/passed as an ISO timestamp. Both new Date(booking.startTime) and new Date() operate on the same UTC‐based millisecond epoch, so the > comparison correctly reflects absolute time regardless of client timezone. No changes needed here.

@husniabad
Copy link
Contributor Author

Hi @kart1ka @emrysal any updates on this PR, is there any conflicts found?

Copy link
Contributor

@kart1ka kart1ka left a comment

Choose a reason for hiding this comment

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

left a few comments

Copy link
Contributor

Choose a reason for hiding this comment

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

Pls add the report booking feature on recurring booking as well.

Image

Copy link
Contributor

Choose a reason for hiding this comment

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

A user should also be able to report an unconfirmed booking.

Image

@husniabad husniabad force-pushed the feature/report-bookings branch from 6ae7041 to 8caf959 Compare September 21, 2025 14:21
@github-actions github-actions bot added enterprise area: enterprise, audit log, organisation, SAML, SSO High priority Created by Linear-GitHub Sync labels Sep 21, 2025
@husniabad husniabad force-pushed the feature/report-bookings branch from 8caf959 to e890cdd Compare September 21, 2025 14:32
@husniabad
Copy link
Contributor Author

Hi @joeauyeung @kart1ka @Devanshusharma2005 @CarinaWolli

I've addressed the feedback by creating a single, optimized query (getTeamDataForAdmin) to replace multiple DB calls, improving performance.
I also centralized all membership logic into packages/lib/server/queries/membership.ts, following the repository pattern. This included removing duplicated helper functions from another module and updating all 18 affected files to use this single source of truth.
The code is now faster and more maintainable.

In addition, I addressed other frontend suggestions by @Devanshusharma2005 and @joeauyeung
Please review the new changes and give me feedback

@husniabad husniabad marked this pull request as ready for review September 21, 2025 14:47
@eunjae-lee
Copy link
Contributor

Hi @husniabad Thanks for your contribution ! This work looks amazing and almost finished. If you don't mind, let me take over this pull request as I am going to finalize some things here and there while aligning with the the upcoming backend changes (in separate PRs)

Comment on lines 3 to 44
export async function isTeamAdmin(userId: number, teamId: number) {
const team = await prisma.membership.findFirst({
where: {
userId,
teamId,
accepted: true,
OR: [{ role: "ADMIN" }, { role: "OWNER" }],
},
include: {
team: {
select: {
metadata: true,
parentId: true,
isOrganization: true,
},
},
},
});
if (!team) return false;
return team;
}

export async function isTeamOwner(userId: number, teamId: number) {
return !!(await prisma.membership.findFirst({
where: {
userId,
teamId,
accepted: true,
role: "OWNER",
},
}));
}

export async function isTeamMember(userId: number, teamId: number) {
return !!(await prisma.membership.findFirst({
where: {
userId,
teamId,
accepted: true,
},
}));
}
Copy link
Member

Choose a reason for hiding this comment

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

@eunjae-lee would you be down to use the functions we already have for checkTeamAdmin etc - makes my life migrating to PBAC a bit easier

@husniabad
Copy link
Contributor Author

Hi @husniabad Thanks for your contribution ! This work looks amazing and almost finished. If you don't mind, let me take over this pull request as I am going to finalize some things here and there while aligning with the the upcoming backend changes (in separate PRs)

Hi @eunjae-lee
Sounds good, please feel free to take it over. I understand the need to align it with the ongoing backend changes like the PBAC migration. Thanks for your help!

@eunjae-lee eunjae-lee marked this pull request as draft September 24, 2025 09:36
devin-ai-integration bot added a commit that referenced this pull request Sep 24, 2025
- Add BOOKING_REPORT type to WatchlistType enum
- Create BookingReportLog model for booking-specific metadata
- Update WatchlistRepository with booking report methods
- Migrate tRPC handlers to use Watchlist system
- Update UI components to work with new data structure
- Maintain all existing functionality while using Watchlist backend

Stacks on: #22709

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
@Udit-takkar Udit-takkar mentioned this pull request Oct 7, 2025
3 tasks
@husniabad husniabad marked this pull request as ready for review October 7, 2025 23:19
@husniabad
Copy link
Contributor Author

Hi @kart1ka @CarinaWolli is this PR still valid with new table "BookingReport"?

@Udit-takkar
Copy link
Contributor

Hey @husniabad, Thanks for your contribution. However, we’re handling this internally as the priority of this feature request has been escalated. we’ve decided to proceed with an approach where users can report a booking and admins can review all reported bookings and add them to the watchlist by confirming on the admin dashboard and few other changes.

@Udit-takkar Udit-takkar closed this Oct 8, 2025
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 community Created by Linear-GitHub Sync consumer enterprise area: enterprise, audit log, organisation, SAML, SSO ✨ feature New feature or request High priority Created by Linear-GitHub Sync ❗️ migrations contains migration files size/XXL ui area: UI, frontend, button, form, input

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Report bookings

9 participants