Skip to content

Comments

fix:branding disabled updated#24391

Closed
hackice20 wants to merge 29 commits intocalcom:mainfrom
hackice20:fix/branding-disabled-updated
Closed

fix:branding disabled updated#24391
hackice20 wants to merge 29 commits intocalcom:mainfrom
hackice20:fix/branding-disabled-updated

Conversation

@hackice20
Copy link
Contributor

What does this PR do?

Fixes: Cal.com branding disabled in settings: Cal.com branding still occurs in emails #23850

I ensure emails respect the "Disable Cal.com branding" setting by:

  • Adding hideBranding?: boolean to CalendarEvent
  • Passing hideBranding when building/assembling events (new booking, confirm, payment, cancellation, reschedule)
  • Updating BaseScheduledEmail to hide the footer/logo when hideBranding is true
  • Implementing comprehensive branding logic with shouldHideBrandingForEvent function
  • Adding sensitive data permission controls with canViewSensitiveBookingData
  • Resolving merge conflicts with upstream main branch
  • Fixing build errors (variable name conflicts)

Scope: Comprehensive branding control across all email flows and booking operations, plus merge conflict resolution.

Visual Demo

Screencast.From.2025-10-09.21-57-39.mp4

Before: Emails always showed Cal.com footer/logo regardless of the branding setting.

After: With branding disabled, emails render without Cal.com footer/logo; with branding enabled, footer/logo appears as usual.

Tip: Use MailHog to view emails in dev.

How should this be tested?

Environment:

  • Ensure email points to MailHog: EMAIL_SERVER_HOST=localhost, EMAIL_SERVER_PORT=1025
  • Optionally: E2E_TEST_MAILHOG_ENABLED=1
  • Start MailHog UI: http://localhost:8025

Minimal data:

  • A user or team with access to "Disable Cal.com branding"
  • One instant booking event type (and optionally one that requires confirmation)

Steps:

  1. Turn ON "Disable Cal.com branding" in Appearance settings
  2. Create/book an event (organizer + attendee)
  3. Check MailHog: both organizer/attendee emails should show no Cal.com logo/footer
  4. Turn OFF the setting, repeat booking, verify logo/footer reappears (control)
  5. Repeat with:
    • "Requires confirmation" flow (make request, then confirm → check both emails)
    • Team event type (if applicable)
    • Cancellation emails (cancel a booking)
    • Reschedule emails (reschedule a booking)
    • Hidden booking fields (test permission visibility)
    • UTM parameters (test sensitive data visibility)

Expected:

  • When disabled: no branding (no logo/footer)
  • When enabled: branding appears as usual
  • No content loss; links and CTAs behave normally
  • Permission-based data visibility works correctly

Additional Changes in This Update

  • Merge Conflicts Resolved: Successfully merged with upstream main
  • Build Errors Fixed: Resolved hideBranding variable name conflicts
  • ESLint Warnings Fixed: Clean code with no linting issues
  • All Branding Logic Preserved: No functional changes to existing behavior

Mandatory Tasks (DO NOT REMOVE)

  • I have self-reviewed the code
  • N/A (no docs change required; behavior now matches existing setting semantics)
  • I confirmed behavior via local tests (manual E2E through MailHog)
  • I resolved merge conflicts with upstream main
  • I fixed build errors and ESLint warnings

Checklist

  • I have read the contributing guide
  • My code follows the project's style guidelines
  • I added concise comments where the intent isn't obvious
  • I checked that my changes don't introduce new warnings
  • I resolved all merge conflicts
  • I fixed all build errors

hackice20 and others added 25 commits September 15, 2025 23:14
- Replace calculateHideBrandingForBooking with passed hideBranding value
- Fix inconsistent branding logic in handleNewBooking.ts line 1297
- Fix inconsistent branding logic in confirm.handler.ts line 277
- Pass calculated hideBranding to handleConfirmation function
- Remove unused calculateHideBrandingForBooking function

Resolves CodeRabbit issues with incomplete comprehensive branding implementation.
- Renamed calculated hideBranding variable to avoid conflict with function parameter
- Fixed ESLint warnings (removed any type, prefixed unused var)
- Preserves all branding logic while fixing build error
- No functional changes to branding behavior
@hackice20 hackice20 requested a review from a team as a code owner October 9, 2025 16:50
@github-actions
Copy link
Contributor

github-actions bot commented Oct 9, 2025

Hey there and thank you for opening this pull request! 👋🏼

We require pull request titles to follow the Conventional Commits specification and it looks like your proposed title needs to be adjusted.

Details:

No release type found in pull request title "fix:branding disabled updated". Add a prefix to indicate what kind of release this pull request corresponds to. For reference, see https://www.conventionalcommits.org/

Available types:
 - feat: A new feature
 - fix: A bug fix
 - docs: Documentation only changes
 - style: Changes that do not affect the meaning of the code (white-space, formatting, missing semi-colons, etc)
 - refactor: A code change that neither fixes a bug nor adds a feature
 - perf: A code change that improves performance
 - test: Adding missing tests or correcting existing tests
 - build: Changes that affect the build system or external dependencies (example scopes: gulp, broccoli, npm)
 - ci: Changes to our CI configuration files and scripts (example scopes: Travis, Circle, BrowserStack, SauceLabs)
 - chore: Other changes that don't modify src or test files
 - revert: Reverts a previous commit

@vercel
Copy link

vercel bot commented Oct 9, 2025

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

A member of the Team first needs to authorize it.

@graphite-app graphite-app bot added the community Created by Linear-GitHub Sync label Oct 9, 2025
@graphite-app graphite-app bot requested a review from a team October 9, 2025 16:50
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 9, 2025

Walkthrough

Adds a new permission check canViewSensitiveBookingData and uses it server-side and in the bookings page props to gate exposure of sensitive booking responses. Introduces comprehensive hideBranding computation (shouldHideBrandingForEvent) and propagates hideBranding through booking flows (new booking, confirmation, cancellation, reschedule, add-guests, edit-location, payments, no-show, round-robin, reschedule-attendee/owner flows). Expands Prisma selects to include branding fields (owner, team, parent), extends CalendarEvent and CalendarEventBuilder with hideBranding, and adds hideLogo support to email templates.

Possibly related PRs

Pre-merge checks and finishing touches

❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Title Check ❓ Inconclusive The title “fix:branding disabled updated” is vague and does not clearly describe the main change of adding a hideBranding flag and updating email flows to respect the Disable Cal.com branding setting. It references branding but lacks context about what was fixed or how it impacts the system. As a result, it fails to convey the purpose and scope of the PR to someone browsing the history. Consider renaming the title to clearly summarize the core change, for example “feat: respect Disable Cal.com branding setting in email flows” or “add hideBranding flag and update email branding logic.”
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed The description clearly explains the addition of the hideBranding property to CalendarEvent, updates to BaseScheduledEmail, the implementation of comprehensive branding logic via shouldHideBrandingForEvent, and the incorporation of sensitive-data controls with canViewSensitiveBookingData, all of which directly reflect the changes in the PR. It also details merge conflict resolutions, build error fixes, and testing steps that align with the modifications. Therefore, the description is relevant and satisfies the criteria for this check.
Docstring Coverage ✅ Passed No functions found in the changes. Docstring coverage check skipped.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • 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

Comment @coderabbitai help to get the list of available commands and usage tips.

@hackice20
Copy link
Contributor Author

@dosubot dosubot bot added bookings area: bookings, availability, timezones, double booking emails area: emails, cancellation email, reschedule email, inbox, spam folder, not getting email 🐛 bug Something isn't working labels Oct 9, 2025
@CLAassistant
Copy link

CLAassistant commented Oct 9, 2025

CLA assistant check
All committers have signed the CLA.

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: 9

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
packages/features/ee/round-robin/roundRobinManualReassignment.ts (1)

547-568: Use the computed branding flag instead of hard-coding true.

We correctly compute hideBranding via shouldHideBrandingForEvent, but the reminder scheduling path still hard-codes hideBranding: true. That forces all manual-reassignment workflow emails to hide Cal.com branding even when the appearance setting says it should be shown. Replace this literal with the computed flag so reminders honor the branding preference.

-        hideBranding: true,
+        hideBranding,
packages/features/bookings/lib/handleConfirmation.ts (1)

441-446: Propagate the computed branding flag to workflows

scheduleWorkflowsForNewBooking still receives the default hideBranding argument (which is always false unless callers override it), so confirmation workflows never get the newly computed calculatedHideBranding. This regresses the branding toggle for any confirmation-triggered workflow. Please wire the computed value through instead.

-        hideBranding: hideBranding,
+        hideBranding: calculatedHideBranding,
packages/features/bookings/lib/payment/getBooking.ts (1)

156-163: Limit Prisma profile fields (select only what’s needed)

You only use organizationId and username; fetch just those to reduce PII and payload.

As per coding guidelines

-  const organizerOrganizationProfile = await prisma.profile.findFirst({
-    where: {
-      userId: booking.userId ?? undefined,
-    },
-  });
+  const organizerOrganizationProfile = await prisma.profile.findFirst({
+    where: { userId: booking.userId ?? undefined },
+    select: { organizationId: true, username: true },
+  });
🧹 Nitpick comments (11)
packages/emails/src/templates/BaseScheduledEmail.tsx (1)

67-67: LGTM: Branding logic correctly extended.

The OR condition properly hides the logo when either platformClientId or hideBranding is truthy, which aligns with the PR objective to respect the "Disable Cal.com branding" setting.

Consider simplifying the Boolean conversion for better readability:

-      hideLogo={Boolean(props.calEvent.platformClientId) || Boolean(props.calEvent.hideBranding)}
+      hideLogo={Boolean(props.calEvent.platformClientId || props.calEvent.hideBranding)}

This achieves the same result with a single Boolean conversion while maintaining the same defensive type coercion.

packages/features/bookings/lib/handleCancelBooking.ts (4)

85-99: Consolidate profile reads; avoid duplicate DB query.

You fetch Profile twice (here and again at Lines 267-271). Collapse into one query and reuse to cut a DB roundtrip.

Apply within this segment:

-  // For user events, we need to fetch the user's profile to get the organization ID
-  const userOrganizationId = !bookingToDelete.eventType?.team
-    ? (
-        await prisma.profile.findFirst({
-          where: {
-            userId: bookingToDelete.userId,
-          },
-          select: {
-            organizationId: true,
-          },
-        })
-      )?.organizationId
-    : null;
+  // For user events, fetch once and reuse (org + username)
+  const ownerProfileLite = !bookingToDelete.eventType?.team
+    ? await prisma.profile.findFirst({
+        where: { userId: bookingToDelete.userId },
+        select: { organizationId: true, username: true },
+      })
+    : null;
+
+  const userOrganizationId = ownerProfileLite?.organizationId ?? null;

Outside this range, replace the second fetch:

// Remove the prisma.profile.findFirst at Lines 267-271 and reuse:
const ownerProfile = ownerProfileLite;

As per coding guidelines.


100-106: Avoid sentinel 0 for eventTypeId.

Passing 0 can skew logs/metrics. Guard and skip compute if missing.

-  const hideBranding = await shouldHideBrandingForEvent({
-    eventTypeId: bookingToDelete.eventTypeId ?? 0,
-    team: bookingToDelete.eventType?.team ?? null,
-    owner: bookingToDelete.user ?? null,
-    organizationId: bookingToDelete.eventType?.team?.parentId ?? userOrganizationId ?? null,
-  });
+  const hideBranding =
+    bookingToDelete.eventTypeId
+      ? await shouldHideBrandingForEvent({
+          eventTypeId: bookingToDelete.eventTypeId,
+          team: bookingToDelete.eventType?.team ?? null,
+          owner: bookingToDelete.user ?? null,
+          organizationId: bookingToDelete.eventType?.team?.parentId ?? userOrganizationId ?? null,
+        })
+      : false;

107-118: Include booking identifiers in branding debug log.

Helps correlate logs quickly.

   log.debug("Branding configuration", {
     hideBranding,
     eventTypeId: bookingToDelete.eventTypeId,
+    bookingId: bookingToDelete.id,
+    bookingUid: bookingToDelete.uid,
     teamHideBranding: bookingToDelete.eventType?.team?.hideBranding,
     teamParentHideBranding: bookingToDelete.eventType?.team?.parent?.hideBranding,
     ownerHideBranding: bookingToDelete.user?.hideBranding,
     teamParentId: bookingToDelete.eventType?.team?.parentId,
     userOrganizationId,
     finalOrganizationId: bookingToDelete.eventType?.team?.parentId ?? userOrganizationId,
     isTeamEvent: !!bookingToDelete.eventType?.team,
   });

633-633: Prefer named export over default.

Improves tree-shaking and refactoring.

As per coding guidelines.

packages/lib/builders/CalendarEvent/class.ts (1)

38-38: Addition aligns with PR intent

hideBranding on CalendarEvent is appropriate and non-breaking.

Consider adding a short doc comment to clarify semantics (e.g., “when true, suppress Cal.com logo/footer in emails”).

packages/features/bookings/lib/handleNewBooking.ts (1)

2459-2459: Prefer named export over default for handler

Switch to named export for tree-shaking and clearer imports. As per coding guidelines.

-export default handler;
+export { handler };
packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)

74-106: Minimize owner selection to only required fields

Avoid fetching unnecessary owner data. Use a selective owner projection.

-          owner: true,
+          owner: { select: { id: true, hideBranding: true } },
packages/trpc/server/routers/viewer/bookings/get.handler.ts (1)

14-14: LGTM: robust hidden-fields sanitization with batched field loading

  • Batches eventType.bookingFields and filters hidden fields when user lacks permission.
  • Safely handles stringified JSON and unknown schemas.

Consider reducing potential N+1 DB calls from canViewSensitiveBookingData by memoizing membership/host checks per request or extending the API to accept precomputed context.

Also applies to: 72-76, 703-736, 748-787, 804-808

packages/features/bookings/lib/payment/getBooking.ts (1)

164-186: Standardize organizationId derivation for branding

Use a single, predictable fallback order to avoid divergent branding results across flows (e.g., prefer org profile id, then team.parentId). Also consider extracting a helper for reuse.

-  const hideBranding = booking.eventType?.id
+  // Prefer organizer org context, then team parent org
+  const organizationId =
+    organizerOrganizationId ?? booking.eventType?.team?.parentId ?? null;
+  const hideBranding = booking.eventType?.id
     ? await shouldHideBrandingForEvent({
         eventTypeId: booking.eventType.id,
         team: booking.eventType.team
           ? {
               hideBranding: booking.eventType.team.hideBranding,
               parent: booking.eventType.team.parent
                 ? {
                     hideBranding: booking.eventType.team.parent.hideBranding,
                   }
                 : null,
             }
           : null,
         owner: booking.eventType.owner
           ? {
               id: booking.eventType.owner.id,
               hideBranding: booking.eventType.owner.hideBranding,
             }
           : null,
-        organizationId: organizerOrganizationId ?? booking.eventType.team?.parentId ?? null,
+        organizationId,
       }).catch(() => !!booking.eventType?.owner?.hideBranding)
     : false;
packages/app-store/wipemycalother/lib/reschedule.ts (1)

227-227: Prefer named export over default export

Named exports improve tree-shaking and refactoring.

As per coding guidelines

-export default Reschedule;
+export { Reschedule };
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 5aa24c8 and f21be1a.

📒 Files selected for processing (26)
  • apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx (4 hunks)
  • apps/web/modules/bookings/views/bookings-single-view.tsx (2 hunks)
  • packages/app-store/vital/lib/reschedule.ts (3 hunks)
  • packages/app-store/wipemycalother/lib/reschedule.ts (3 hunks)
  • packages/emails/src/components/V2BaseEmailHtml.tsx (2 hunks)
  • packages/emails/src/templates/BaseScheduledEmail.tsx (1 hunks)
  • packages/features/CalendarEventBuilder.ts (1 hunks)
  • packages/features/bookings/lib/getBookingToDelete.ts (3 hunks)
  • packages/features/bookings/lib/handleBookingRequested.ts (4 hunks)
  • packages/features/bookings/lib/handleCancelBooking.ts (6 hunks)
  • packages/features/bookings/lib/handleConfirmation.ts (16 hunks)
  • packages/features/bookings/lib/handleNewBooking.ts (6 hunks)
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts (2 hunks)
  • packages/features/bookings/lib/payment/getBooking.ts (5 hunks)
  • packages/features/credentials/handleDeleteCredential.ts (5 hunks)
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts (3 hunks)
  • packages/features/handleMarkNoShow.ts (4 hunks)
  • packages/lib/builders/CalendarEvent/class.ts (1 hunks)
  • packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts (1 hunks)
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (4 hunks)
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (6 hunks)
  • packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts (11 hunks)
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/util.ts (2 hunks)
  • packages/types/Calendar.d.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.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:

  • packages/types/Calendar.d.ts
  • packages/lib/builders/CalendarEvent/class.ts
  • packages/app-store/vital/lib/reschedule.ts
  • packages/features/bookings/lib/payment/getBooking.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/CalendarEventBuilder.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/getBookingToDelete.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
  • packages/features/handleMarkNoShow.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleNewBooking.ts
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
  • packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/features/bookings/lib/handleCancelBooking.ts
  • packages/features/bookings/lib/handleBookingRequested.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:

  • packages/types/Calendar.d.ts
  • packages/emails/src/templates/BaseScheduledEmail.tsx
  • packages/lib/builders/CalendarEvent/class.ts
  • packages/emails/src/components/V2BaseEmailHtml.tsx
  • packages/app-store/vital/lib/reschedule.ts
  • packages/features/bookings/lib/payment/getBooking.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • apps/web/modules/bookings/views/bookings-single-view.tsx
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/CalendarEventBuilder.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/getBookingToDelete.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
  • packages/features/handleMarkNoShow.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleNewBooking.ts
  • apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
  • packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/features/bookings/lib/handleCancelBooking.ts
  • packages/features/bookings/lib/handleBookingRequested.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/types/Calendar.d.ts
  • packages/emails/src/templates/BaseScheduledEmail.tsx
  • packages/lib/builders/CalendarEvent/class.ts
  • packages/emails/src/components/V2BaseEmailHtml.tsx
  • packages/app-store/vital/lib/reschedule.ts
  • packages/features/bookings/lib/payment/getBooking.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • apps/web/modules/bookings/views/bookings-single-view.tsx
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/CalendarEventBuilder.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/getBookingToDelete.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
  • packages/features/handleMarkNoShow.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleNewBooking.ts
  • apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
  • packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/features/bookings/lib/handleCancelBooking.ts
  • packages/features/bookings/lib/handleBookingRequested.ts
**/*.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:

  • packages/emails/src/templates/BaseScheduledEmail.tsx
  • packages/emails/src/components/V2BaseEmailHtml.tsx
  • apps/web/modules/bookings/views/bookings-single-view.tsx
  • apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx
🧠 Learnings (4)
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/getBookingToDelete.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/trpc/server/routers/viewer/bookings/get.handler.ts
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
📚 Learning: 2025-08-21T13:55:23.470Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/service/FormWebhookService.ts:0-0
Timestamp: 2025-08-21T13:55:23.470Z
Learning: In the new webhook architecture for Cal.com, schedulePayload is considered legacy code that doesn't fit the clean architecture. The new architecture keeps webhook scheduling logic within the Service layer, specifically through a new method WebhookService.scheduleDelayedWebhooks, rather than using the old centralized schedulePayload helper.

Applied to files:

  • packages/features/bookings/lib/handleConfirmation.ts
🧬 Code graph analysis (17)
packages/app-store/vital/lib/reschedule.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/payment/getBooking.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
apps/web/modules/bookings/views/bookings-single-view.tsx (1)
packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts (1)
  • canViewSensitiveBookingData (44-87)
packages/features/credentials/handleDeleteCredential.ts (2)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleConfirmation.ts (3)
packages/lib/getTeamIdFromEventType.ts (1)
  • getTeamIdFromEventType (3-29)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/app-store/wipemycalother/lib/reschedule.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/trpc/server/routers/viewer/bookings/get.handler.ts (1)
packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts (1)
  • canViewSensitiveBookingData (44-87)
packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/handleMarkNoShow.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleNewBooking.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx (1)
packages/lib/server/queries/bookings/canViewSensitiveBookingData.ts (1)
  • canViewSensitiveBookingData (44-87)
packages/features/ee/round-robin/roundRobinManualReassignment.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts (4)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/types/Calendar.d.ts (1)
  • CalendarEvent (164-228)
packages/emails/email-manager.ts (1)
  • sendLocationChangeEmailsAndSMS (640-671)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (2)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleCancelBooking.ts (2)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/emails/email-manager.ts (1)
  • sendCancelledEmailsAndSMS (476-534)
packages/features/bookings/lib/handleBookingRequested.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
🔇 Additional comments (40)
packages/emails/src/components/V2BaseEmailHtml.tsx (2)

24-24: LGTM! Clear prop addition for branding control.

The optional hideLogo prop is well-named and appropriately typed. Making it optional ensures backward compatibility with existing usage.


185-185: LGTM! Conditional rendering logic is correct.

The logo will be hidden when hideLogo is true and displayed otherwise (including when undefined), which aligns with the PR's goal to respect the branding settings.

packages/features/credentials/handleDeleteCredential.ts (3)

16-17: LGTM: Necessary imports for branding logic.

The imports for getOrgIdFromMemberOrTeamId and shouldHideBrandingForEvent are required to compute and propagate the hideBranding flag through the cancellation flow.


254-283: LGTM: Expanded select for hideBranding computation.

The Prisma select correctly fetches the additional fields (eventType.id, team/owner hideBranding, parentId, parent hideBranding) needed to compute the hideBranding flag via shouldHideBrandingForEvent. The use of select adheres to the coding guidelines.


403-403: LGTM: hideBranding correctly propagated to cancellation emails.

The computed hideBranding flag is properly passed to sendCancelledEmailsAndSMS, ensuring that emails respect the branding preferences when a payment credential is removed.

packages/features/bookings/lib/handleNewBooking/getEventTypesFromDB.ts (2)

46-51: LGTM! Branding hierarchy properly captured.

The addition of hideBranding for both team and team.parent correctly supports the hierarchical branding logic needed for this feature. The shallow nesting and minimal field selection keep the query efficient.


115-115: owner.id is required by branding logic and downstream handlers
The field is consumed in shouldHideBrandingForEvent (via ProfileRepository.findByUserIdAndOrgSlug) and in various booking/payment flows (e.g., processPaymentRefund), so it should remain selected.

packages/features/bookings/lib/getBookingToDelete.ts (3)

26-26: LGTM! User-level branding flag added.

The hideBranding field on the user is correctly added to support user-level branding preferences for deletion workflows.


44-44: Approve selecting eventType.id
Verified eventType.id is passed into shouldHideBrandingForEvent across booking handlers.


58-63: Parent.hideBranding is correctly consumed by hideBranding logic. Verified shouldHideBrandingForEventUsingProfile passes team.parent.hideBranding into resolveHideBranding, which governs organization-level branding. No changes needed.

packages/features/handleMarkNoShow.ts (2)

186-191: LGTM: Branding fields appropriately selected.

The additions of team.hideBranding and team.parent.hideBranding correctly support the branding computation logic added later in the file.


330-330: LGTM: Correctly propagates comprehensive branding decision.

The computed hideBranding flag is appropriately passed to workflow scheduling, replacing the simpler owner-only check with the comprehensive logic that considers team and organization settings.

packages/features/bookings/lib/handleCancelBooking.ts (3)

327-335: LGTM: hideBranding propagated on evt.

This ensures branding flows to webhooks and emails via CalendarEvent.


608-614: LGTM: email gating + branding-aware log.

Conditional send and logged branding config look correct.


376-394: No changes required for hideBranding support. sendCancelledReminders accepts the hideBranding arg and passes it through to processWorkflowStep.

apps/web/modules/bookings/views/bookings-single-view.tsx (2)

107-107: Prop plumbing looks good

canViewSensitiveBookingData added to props and consumed in component.


705-744: UTM params correctly gated by permission

Replacing isHost with canViewSensitiveBookingData tightens access to sensitive tracking data.

packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (2)

21-21: Import is appropriate

Branding helper correctly imported here.


63-91: Prisma select is minimal and sufficient

Only required fields are selected (owner/team hideBranding, email-related flags, metadata). This aligns with “select only what you need” guidance.

apps/web/modules/bookings/views/bookings-single-view.getServerSideProps.tsx (4)

15-15: Import OK

Permission helper correctly pulled from server queries.


229-240: Gating internal notes by permission

Early-return when no teamId or no permission prevents leaking presets.


278-279: Prop exposed for client gating

Plumbing canViewSensitiveBookingData to client enables consistent UI decisions.


198-215: Server-side permission check is sound

getEventTypesFromDB already selects hosts, team, parent, and schedulingType needed by canViewSensitiveBookingData; no further changes.

packages/app-store/vital/lib/reschedule.ts (2)

8-8: Import OK

Branding helper correctly introduced.


37-63: Prisma select is lean and relevant

Selected fields cover email + branding needs without overfetching.

packages/features/bookings/lib/handleNewBooking.ts (2)

1318-1319: Good: hideBranding propagated to CalendarEventBuilder

Passing the computed flag into the builder centralizes branding in downstream emails/workflows.


2199-2200: Verify downstream API signatures accept hideBranding
Confirm that scheduleMandatoryReminder and WorkflowService.schedule* methods include a hideBranding parameter in their signatures and that it’s propagated through to the email rendering.

packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (2)

7-9: LGTM: centralized branding utilities imported

Consistent with other flows.


170-197: Branding logic and data selection are correct
The Prisma select on eventType includes owner.hideBranding, team.hideBranding, and team.parent.hideBranding required by shouldHideBrandingForEvent.

packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts (1)

10-12: LGTM: consistent branding computation and propagation across update location flow

  • orgId resolved via getOrgIdFromMemberOrTeamId; fallback to owner.hideBranding on error is safe.
  • evt.hideBranding set and passed to connected apps, DB update, and emails.

Also applies to: 269-297, 298-302, 311-312, 316-325, 328-329, 334-335

packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)

278-280: LGTM: hideBranding propagated to downstream flows
Evt includes hideBranding and it flows through to handleConfirmation (which supports it) and workflow scheduling.

packages/features/bookings/lib/payment/getBooking.ts (3)

90-96: Team.branding fields selection looks good

Selecting team.hideBranding and parent.hideBranding is appropriate for branding decisions.


41-43: Owner.id selection is necessary

Adding owner.id enables user-event branding resolution. LGTM.


229-230: evt.hideBranding propagation

Carrying hideBranding on CalendarEvent is correct and keeps downstream consumers consistent.

packages/app-store/wipemycalother/lib/reschedule.ts (2)

38-61: Branding-related selects look good

Selecting eventType.id/team/owner branding fields is appropriate and minimal.


208-209: Applying hideBranding to the email payload

Setting builder.calendarEvent.hideBranding ensures reschedule emails honor the setting. LGTM.

packages/trpc/server/routers/viewer/bookings/util.ts (2)

117-125: Context typing updates look correct

Augmenting eventType shape with branding fields matches the new selects.


52-56: Remove outdated credential leak warning
This query only selects destinationCalendar; there is no credentials include here, so there’s no risk of exposing credential.key.

Likely an incorrect or invalid review comment.

packages/types/Calendar.d.ts (1)

227-228: CalendarEvent.hideBranding addition LGTM

The new flag is appropriately optional and aligns with email/template usage.

packages/features/CalendarEventBuilder.ts (1)

271-277: withHideBranding builder method looks good

Fluent setter is consistent with existing API and type-safe.

Comment on lines 295 to 319
const hideBranding = bookingToReschedule.eventType?.id
? await shouldHideBrandingForEvent({
eventTypeId: bookingToReschedule.eventType.id,
team: bookingToReschedule.eventType.team
? {
hideBranding: bookingToReschedule.eventType.team.hideBranding,
parent: bookingToReschedule.eventType.team.parent
? {
hideBranding: bookingToReschedule.eventType.team.parent.hideBranding,
}
: null,
}
: null,
owner: bookingToReschedule.eventType.owner
? {
id: bookingToReschedule.eventType.owner.id,
hideBranding: bookingToReschedule.eventType.owner.hideBranding,
}
: null,
organizationId: bookingToReschedule.eventType.team?.parentId || null,
}).catch(() => !!bookingToReschedule.eventType?.owner?.hideBranding)
: false;

builder.calendarEvent.hideBranding = hideBranding;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Pass correct organizationId for user events and use builder API

For user events (no team), organizationId is currently null, which can miss org-level branding settings. Use the current user’s org when team is absent. Prefer builder.withHideBranding if available and log the catch for observability.

Apply:

-  const hideBranding = bookingToReschedule.eventType?.id
+  const orgIdForBranding =
+    bookingToReschedule.eventType?.team?.parentId ??
+    (user.profile?.organizationId ?? null);
+
+  const hideBranding = bookingToReschedule.eventType?.id
     ? await shouldHideBrandingForEvent({
       eventTypeId: bookingToReschedule.eventType.id,
       team: bookingToReschedule.eventType.team
         ? {
             hideBranding: bookingToReschedule.eventType.team.hideBranding,
             parent: bookingToReschedule.eventType.team.parent
               ? {
                   hideBranding: bookingToReschedule.eventType.team.parent.hideBranding,
                 }
               : null,
           }
         : null,
       owner: bookingToReschedule.eventType.owner
         ? {
             id: bookingToReschedule.eventType.owner.id,
             hideBranding: bookingToReschedule.eventType.owner.hideBranding,
           }
         : null,
-      organizationId: bookingToReschedule.eventType.team?.parentId || null,
-    }).catch(() => !!bookingToReschedule.eventType?.owner?.hideBranding)
+      organizationId: orgIdForBranding,
+    }).catch((e) => {
+      log.warn(
+        "shouldHideBrandingForEvent failed; falling back to owner.hideBranding",
+        safeStringify({ bookingUid: bookingToReschedule.uid, error: e instanceof Error ? e.message : String(e) })
+      );
+      return !!bookingToReschedule.eventType?.owner?.hideBranding;
+    })
     : false;
 
-  builder.calendarEvent.hideBranding = hideBranding;
+  if (typeof (builder as any).withHideBranding === "function") {
+    (builder as any).withHideBranding(hideBranding);
+  } else {
+    builder.calendarEvent.hideBranding = hideBranding;
+  }

As per coding guidelines

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const hideBranding = bookingToReschedule.eventType?.id
? await shouldHideBrandingForEvent({
eventTypeId: bookingToReschedule.eventType.id,
team: bookingToReschedule.eventType.team
? {
hideBranding: bookingToReschedule.eventType.team.hideBranding,
parent: bookingToReschedule.eventType.team.parent
? {
hideBranding: bookingToReschedule.eventType.team.parent.hideBranding,
}
: null,
}
: null,
owner: bookingToReschedule.eventType.owner
? {
id: bookingToReschedule.eventType.owner.id,
hideBranding: bookingToReschedule.eventType.owner.hideBranding,
}
: null,
organizationId: bookingToReschedule.eventType.team?.parentId || null,
}).catch(() => !!bookingToReschedule.eventType?.owner?.hideBranding)
: false;
builder.calendarEvent.hideBranding = hideBranding;
const orgIdForBranding =
bookingToReschedule.eventType?.team?.parentId ??
(user.profile?.organizationId ?? null);
const hideBranding = bookingToReschedule.eventType?.id
? await shouldHideBrandingForEvent({
eventTypeId: bookingToReschedule.eventType.id,
team: bookingToReschedule.eventType.team
? {
hideBranding: bookingToReschedule.eventType.team.hideBranding,
parent: bookingToReschedule.eventType.team.parent
? {
hideBranding: bookingToReschedule.eventType.team.parent.hideBranding,
}
: null,
}
: null,
owner: bookingToReschedule.eventType.owner
? {
id: bookingToReschedule.eventType.owner.id,
hideBranding: bookingToReschedule.eventType.owner.hideBranding,
}
: null,
organizationId: orgIdForBranding,
}).catch((e) => {
log.warn(
"shouldHideBrandingForEvent failed; falling back to owner.hideBranding",
safeStringify({
bookingUid: bookingToReschedule.uid,
error: e instanceof Error ? e.message : String(e),
})
);
return !!bookingToReschedule.eventType?.owner?.hideBranding;
})
: false;
if (typeof (builder as any).withHideBranding === "function") {
(builder as any).withHideBranding(hideBranding);
} else {
builder.calendarEvent.hideBranding = hideBranding;
}
🤖 Prompt for AI Agents
In packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
around lines 295 to 319, the code sets organizationId to null for user-owned
events and directly assigns builder.calendarEvent.hideBranding; change this to
pass the current user's organizationId when eventType.team is absent (e.g., use
bookingToReschedule.eventType.owner?.organizationId or the request user org),
call the builder API method withHideBranding (builder.withHideBranding or
equivalent) instead of setting calendarEvent.hideBranding directly, and replace
the silent .catch(() => !!...) with a catch that logs the error for
observability while falling back to the owner/team hideBranding value; ensure
the resolved boolean from shouldHideBrandingForEvent is applied to the
builder.withHideBranding call.

- Fix organizationId resolution to properly handle user events in organizations
- Add comprehensive branding logic across all booking lifecycle handlers
- Implement profile lookup fallback for user organization ID
- Add hideBranding property to CalendarEvent interface
- Add withBranding method to CalendarEventBuilder
- Update email templates to conditionally hide logo based on branding
- Improve error handling and logging for branding computation
- Optimize Prisma queries by replacing include with select
- Remove credentials exposure in booking handlers
- Ensure consistent branding across reschedule, cancel, and confirmation flows
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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
packages/features/ee/round-robin/roundRobinManualReassignment.ts (1)

557-588: Inconsistent branding flag—line 580 hardcodes true instead of using computed hideBranding.

At line 580, hideBranding: true is hardcoded when rescheduling existing workflow reminders for the new host. Meanwhile, line 645 correctly uses the computed hideBranding value for new event workflows. This inconsistency means:

  • Existing workflow reminders always hide branding (line 580).
  • New event workflows respect the computed branding setting (line 645).

For consistency, line 580 should reference the computed hideBranding variable (available since line 488).

Apply this diff to use the computed value:

       await scheduleEmailReminder({
         evt: {
           ...evt,
           metadata: workflowEventMetadata,
           eventType,
           bookerUrl,
         },
         action: WorkflowActions.EMAIL_HOST,
         triggerEvent: workflow.trigger,
         timeSpan: {
           time: workflow.time,
           timeUnit: workflow.timeUnit,
         },
         sendTo: [newUser.email],
         template: workflowStep.template,
         emailSubject: workflowStep.emailSubject || undefined,
         emailBody: workflowStep.reminderBody || undefined,
         sender: workflowStep.sender || SENDER_NAME,
-        hideBranding: true,
+        hideBranding: hideBranding,
         includeCalendarEvent: workflowStep.includeCalendarEvent,
         workflowStepId: workflowStep.id,
         verifiedAt: workflowStep.verifiedAt,
       });
packages/trpc/server/routers/viewer/bookings/util.ts (1)

140-143: Critical type mismatch: user type includes credentials, but they're not selected.

The type declares User & { destinationCalendar, credentials }, implying all User fields plus those extras are present. However, the select at lines 60–66 only fetches name, email, timeZone, locale, and destinationCalendar—credentials are not selected, and many User fields are omitted. Code accessing booking.user.credentials will compile but fail at runtime.

Replace the intersection with an explicit type matching the selected fields:

     user:
-      | (User & {
-          destinationCalendar: DestinationCalendar | null;
-          credentials: Credential[];
-        })
+      | {
+          name: string | null;
+          email: string;
+          timeZone: string;
+          locale: string | null;
+          destinationCalendar: DestinationCalendar | null;
+        }
       | null;
♻️ Duplicate comments (3)
packages/features/handleMarkNoShow.ts (1)

174-181: Branding computation fixes look solid.

Owner id added, team/parent branding selected, and branding calc wrapped in try/catch with safe default. Passing hideBranding to workflow scheduling is correct.

Also applies to: 187-193, 221-266, 350-356

packages/features/bookings/lib/handleNewBooking.ts (1)

464-501: Resolve orgId via helper to avoid miscomputed branding for personal/org-managed events

Deriving organizationId via team.parentId/profile is brittle. Use getOrgIdFromMemberOrTeamId to cover both team and personal events consistently. This was flagged earlier.

Apply this diff:

-  // Calculate hide branding setting using comprehensive logic that considers team and organization settings
-  // For user events, fetch the user's profile to get the organization ID
-  const userOrganizationId = !eventType.team
-    ? (
-        await prisma.profile.findFirst({
-          where: {
-            userId: eventType.owner?.id,
-          },
-          select: {
-            organizationId: true,
-          },
-        })
-      )?.organizationId
-    : null;
-  
-  const orgIdForBranding = eventType.team?.parentId ?? userOrganizationId ?? null;
-  
+  // Calculate hide branding using consistent org resolution
+  const orgIdForBranding = await getOrgIdFromMemberOrTeamId({
+    memberId: eventType.owner?.id ?? null,
+    teamId: eventType.team?.id ?? null,
+  });
+
   const hideBranding = await shouldHideBrandingForEvent({
     eventTypeId: eventType.id,
     team: eventType.team
       ? {
           hideBranding: eventType.team.hideBranding,
           parent: eventType.team.parent
             ? {
                 hideBranding: eventType.team.parent.hideBranding,
               }
             : null,
         }
       : null,
     owner: eventType.owner
       ? {
           id: eventType.owner.id,
           hideBranding: eventType.owner.hideBranding,
         }
       : null,
-    organizationId: orgIdForBranding,
+    organizationId: orgIdForBranding ?? null,
   });
packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)

148-185: Use getOrgIdFromMemberOrTeamId for branding orgId (consistency + correctness)

Replace manual profile/parentId logic with the helper to correctly derive org for user events and org-managed users. This aligns with other handlers and prior feedback.

-// For user events, fetch the user's profile to get the organization ID
-const userOrganizationId = !booking.eventType?.team
-  ? (
-      await prisma.profile.findFirst({
-        where: {
-          userId: booking.userId,
-        },
-        select: {
-          organizationId: true,
-        },
-      })
-    )?.organizationId
-  : null;
-
-const orgIdForBranding = booking.eventType?.team?.parentId ?? userOrganizationId ?? null;
+const orgIdForBranding = await getOrgIdFromMemberOrTeamId({
+  memberId: booking.userId ?? null,
+  teamId: booking.eventType?.team?.id ?? booking.eventType?.teamId ?? null,
+});
 
 const hideBranding = await shouldHideBrandingForEvent({
   eventTypeId: booking.eventType?.id || 0,
   team: booking.eventType?.team
     ? {
         hideBranding: booking.eventType.team.hideBranding,
         parent: booking.eventType.team.parent
           ? {
               hideBranding: booking.eventType.team.parent.hideBranding,
             }
           : null,
       }
     : null,
   owner: booking.eventType?.owner
     ? {
         id: booking.eventType.owner.id,
         hideBranding: booking.eventType.owner.hideBranding,
       }
     : null,
-  organizationId: orgIdForBranding,
+  organizationId: orgIdForBranding ?? null,
 });
🧹 Nitpick comments (16)
packages/features/ee/round-robin/roundRobinManualReassignment.ts (1)

471-507: Profile fetch is redundant—shouldHideBrandingForEvent fetches it again.

The code queries prisma.profile at lines 475‑483 to retrieve userOrganizationId, then passes that organizationId to shouldHideBrandingForEvent. Based on the relevant code snippet from packages/lib/hideBranding.ts, that function internally calls ProfileRepository.findByUserIdAndOrgSlug (lines 91‑95 in the snippet) to fetch the profile again when organizationId is provided.

This double fetch is inefficient. Consider one of the following:

  1. Fetch the full profile once and pass the organizationId without a second fetch inside shouldHideBrandingForEvent.
  2. Skip the profile fetch here if shouldHideBrandingForEvent can derive organizationId internally (requires refactoring the utility function).

If refactoring shouldHideBrandingForEvent is out of scope for this PR, document this as a known inefficiency and track it in a follow-up issue.

packages/features/credentials/handleDeleteCredential.ts (1)

268-268: Remove unused field selection.

The parentId field is selected but never used in the subsequent code. The parent relation object is used instead (lines 349-353).

As per coding guidelines, Prisma queries should only select data you need.

Apply this diff to remove the unused field:

                     select: {
                       id: true,
                       name: true,
-                      parentId: true,
                       hideBranding: true,
packages/features/bookings/lib/handleSeats/reschedule/owner/combineTwoSeatedBookings.ts (2)

139-167: Harden branding computation: add try/catch, null-safety, and parallelize team/profile fetches.

  • Risk: organizerUser is assumed; organizerUser.id access can throw if undefined.
  • Resilience: shouldHideBrandingForEvent errors would currently bubble and break reschedule email flow.
  • Type-safety: avoid (teamForBranding as any) by shaping a minimal typed object.
  • Perf: fetch team and organizer profile concurrently.

Apply this refactor:

-    const teamForBranding = eventType.team?.id
-      ? await prisma.team.findUnique({
-          where: { id: eventType.team.id },
-          select: {
-            id: true,
-            hideBranding: true,
-            parentId: true,
-            parent: {
-              select: {
-                hideBranding: true,
-              },
-            },
-          },
-        })
-      : null;
-
-    const hideBranding = await shouldHideBrandingForEvent({
-      eventTypeId: eventType.id,
-      team: (teamForBranding as any) ?? null,
-      owner: organizerUser ?? null,
-      organizationId: (await (async () => {
-        if (teamForBranding?.parentId) return teamForBranding.parentId;
-        const organizerProfile = await prisma.profile.findFirst({
-          where: { userId: organizerUser.id },
-          select: { organizationId: true },
-        });
-        return organizerProfile?.organizationId ?? null;
-      })()),
-    });
+    if (!organizerUser) {
+      loggerWithEventDetails.warn("organizerUser missing; defaulting hideBranding=false");
+    }
+    const [teamForBranding, organizerProfile] = await Promise.all([
+      eventType.team?.id
+        ? prisma.team.findUnique({
+            where: { id: eventType.team.id },
+            select: {
+              id: true,
+              hideBranding: true,
+              parentId: true,
+              parent: { select: { hideBranding: true } },
+            },
+          })
+        : Promise.resolve(null),
+      organizerUser
+        ? prisma.profile.findFirst({
+            where: { userId: organizerUser.id },
+            select: { organizationId: true },
+          })
+        : Promise.resolve(null),
+    ]);
+    const orgIdForBranding = teamForBranding?.parentId ?? organizerProfile?.organizationId ?? null;
+    let hideBranding = false;
+    try {
+      hideBranding = await shouldHideBrandingForEvent({
+        eventTypeId: eventType.id,
+        team: teamForBranding ?? null,
+        owner: organizerUser ?? null,
+        organizationId: orgIdForBranding,
+      });
+    } catch (e) {
+      loggerWithEventDetails.error("Failed to compute hideBranding (reschedule owner combine)", e);
+      hideBranding = false;
+    }

176-183: Remove as any and pass a typed CalendarEvent with hideBranding.

CalendarEvent now supports hideBranding in this PR. Avoid unsafe casts.

-      (
-        {
-          ...copyEvent,
-          additionalNotes,
-          cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`,
-          hideBranding,
-        } as any
-      ),
+      {
+        ...copyEvent,
+        additionalNotes,
+        cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`,
+        hideBranding,
+      },
packages/features/bookings/lib/handleCancelBooking.ts (2)

100-106: Guard branding computation with try/catch to avoid blocking cancellations.

If shouldHideBrandingForEvent throws (DB hiccup, etc.), the whole cancellation fails. Default to false and log.

-  const hideBranding = await shouldHideBrandingForEvent({
-    eventTypeId: bookingToDelete.eventTypeId ?? 0,
-    team: bookingToDelete.eventType?.team ?? null,
-    owner: bookingToDelete.user ?? null,
-    organizationId: bookingToDelete.eventType?.team?.parentId ?? userOrganizationId ?? null,
-  });
+  let hideBranding = false;
+  try {
+    hideBranding = await shouldHideBrandingForEvent({
+      eventTypeId: bookingToDelete.eventTypeId ?? 0,
+      team: bookingToDelete.eventType?.team ?? null,
+      owner: bookingToDelete.user ?? null,
+      organizationId: bookingToDelete.eventType?.team?.parentId ?? userOrganizationId ?? null,
+    });
+  } catch (e) {
+    log.error("Failed to compute hideBranding for cancellation", e);
+    hideBranding = false;
+  }

606-621: Simplify condition; keep behavior identical.

Use simpler logic for readability.

-    if (!platformClientId || (platformClientId && arePlatformEmailsEnabled)) {
+    if (!platformClientId || arePlatformEmailsEnabled) {
packages/features/bookings/lib/handleSeats/reschedule/attendee/attendeeRescheduleSeatedBooking.ts (2)

26-55: Add resilience + parallelism for branding; avoid brittle null access.

  • organizerUser.id access can throw if organizerUser missing.
  • Compute team/profile in parallel and guard shouldHideBrandingForEvent.
-  const teamForBranding = eventType.team?.id
-    ? await prisma.team.findUnique({
-        where: { id: eventType.team.id },
-        select: {
-          id: true,
-          hideBranding: true,
-          parentId: true,
-          parent: {
-            select: {
-              hideBranding: true,
-            },
-          },
-        },
-      })
-    : null;
-
-  const hideBranding = await shouldHideBrandingForEvent({
-    eventTypeId: eventType.id,
-    team: (teamForBranding as any) ?? null,
-    owner: organizerUser ?? null,
-    organizationId: (await (async () => {
-      if (teamForBranding?.parentId) return teamForBranding.parentId;
-      const organizerProfile = await prisma.profile.findFirst({
-        where: { userId: organizerUser.id },
-        select: { organizationId: true },
-      });
-      return organizerProfile?.organizationId ?? null;
-    })()),
-  });
+  if (!organizerUser) {
+    // Non-blocking default; we still proceed with emails if branding can't be computed
+    // eslint-disable-next-line no-console
+    console.warn("organizerUser missing; defaulting hideBranding=false");
+  }
+  const [teamForBranding, organizerProfile] = await Promise.all([
+    eventType.team?.id
+      ? prisma.team.findUnique({
+          where: { id: eventType.team.id },
+          select: {
+            id: true,
+            hideBranding: true,
+            parentId: true,
+            parent: { select: { hideBranding: true } },
+          },
+        })
+      : Promise.resolve(null),
+    organizerUser
+      ? prisma.profile.findFirst({
+          where: { userId: organizerUser.id },
+          select: { organizationId: true },
+        })
+      : Promise.resolve(null),
+  ]);
+  const orgIdForBranding = teamForBranding?.parentId ?? organizerProfile?.organizationId ?? null;
+  let hideBranding = false;
+  try {
+    hideBranding = await shouldHideBrandingForEvent({
+      eventTypeId: eventType.id,
+      team: teamForBranding ?? null,
+      owner: organizerUser ?? null,
+      organizationId: orgIdForBranding,
+    });
+  } catch (e) {
+    // eslint-disable-next-line no-console
+    console.error("Failed to compute hideBranding (attendee reschedule); defaulting to false", e);
+    hideBranding = false;
+  }

86-86: Remove as any when sending emails; pass typed CalendarEvent with hideBranding.

-    await sendRescheduledSeatEmailAndSMS({ ...evt, hideBranding } as any, seatAttendee as Person, eventType.metadata);
+    await sendRescheduledSeatEmailAndSMS({ ...evt, hideBranding }, seatAttendee as Person, eventType.metadata);
-  await sendRescheduledSeatEmailAndSMS({ ...copyEvent, hideBranding } as any, seatAttendee as Person, eventType.metadata);
+  await sendRescheduledSeatEmailAndSMS({ ...copyEvent, hideBranding }, seatAttendee as Person, eventType.metadata);

Also applies to: 128-128

packages/features/bookings/lib/handleSeats/reschedule/owner/moveSeatedBookingToNewTimeSlot.ts (2)

34-62: Make branding calc robust; perform team/profile queries in parallel.

Guard computation and avoid (teamForBranding as any).

-  const teamForBranding = eventType.team?.id
-    ? await prisma.team.findUnique({
-        where: { id: eventType.team.id },
-        select: {
-          id: true,
-          hideBranding: true,
-          parentId: true,
-          parent: {
-            select: {
-              hideBranding: true,
-            },
-          },
-        },
-      })
-    : null;
-
-  const hideBranding = await shouldHideBrandingForEvent({
-    eventTypeId: eventType.id,
-    team: (teamForBranding as any) ?? null,
-    owner: organizerUser ?? null,
-    organizationId: (await (async () => {
-      if (teamForBranding?.parentId) return teamForBranding.parentId;
-      const organizerProfile = await prisma.profile.findFirst({
-        where: { userId: organizerUser.id },
-        select: { organizationId: true },
-      });
-      return organizerProfile?.organizationId ?? null;
-    })()),
-  });
+  const [teamForBranding, organizerProfile] = await Promise.all([
+    eventType.team?.id
+      ? prisma.team.findUnique({
+          where: { id: eventType.team.id },
+          select: {
+            id: true,
+            hideBranding: true,
+            parentId: true,
+            parent: { select: { hideBranding: true } },
+          },
+        })
+      : Promise.resolve(null),
+    prisma.profile.findFirst({
+      where: { userId: organizerUser.id },
+      select: { organizationId: true },
+    }),
+  ]);
+  const orgIdForBranding = teamForBranding?.parentId ?? organizerProfile?.organizationId ?? null;
+  let hideBranding = false;
+  try {
+    hideBranding = await shouldHideBrandingForEvent({
+      eventTypeId: eventType.id,
+      team: teamForBranding ?? null,
+      owner: organizerUser ?? null,
+      organizationId: orgIdForBranding,
+    });
+  } catch (e) {
+    loggerWithEventDetails.error("Failed to compute hideBranding (owner reschedule move)", e);
+    hideBranding = false;
+  }

131-138: Avoid as any when passing to reschedule emails; use typed CalendarEvent.

-      (
-        {
-          ...copyEvent,
-          additionalNotes,
-          cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`,
-          hideBranding,
-        } as any
-      ),
+      {
+        ...copyEvent,
+        additionalNotes,
+        cancellationReason: `$RCH$${rescheduleReason ? rescheduleReason : ""}`,
+        hideBranding,
+      },
packages/app-store/vital/lib/reschedule.ts (1)

227-227: Prefer the builder API for branding

We now have CalendarEventBuilder.withBranding; please call it instead of mutating builder.calendarEvent directly so future builder invariants stay centralized.

packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (1)

198-218: Log branding resolution failures

If shouldHideBrandingForEvent throws we silently swallow the exception. Please log the failure (as done in other flows) before falling back so we can trace branding issues in prod.

packages/app-store/wipemycalother/lib/reschedule.ts (1)

227-227: Use withBranding helper

Please switch to builder.withBranding(hideBranding) rather than poking builder.calendarEvent—keeps all builder mutations going through the public API.

packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)

339-339: Route branding through the builder

Now that CalendarEventBuilder exposes withBranding, please call it here instead of assigning to builder.calendarEvent so the builder remains the single point of truth for event mutations.

packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)

99-104: Prisma select scope: minimize to needed fields (owner)

Avoid owner: true. Select only fields used for branding (id, hideBranding).

As per coding guidelines

Example (outside exact range):

owner: {
  select: {
    id: true,
    hideBranding: true,
  },
},
packages/features/bookings/lib/handleBookingRequested.ts (1)

85-93: Use getOrgIdFromMemberOrTeamId for org derivation

Unify org resolution across handlers; avoids extra queries and edge-case misses.

-const organizationIdForBranding = teamForBranding?.parentId
-  ? teamForBranding.parentId
-  : (
-      await prisma.profile.findFirst({
-        where: { userId: booking.userId ?? undefined },
-        select: { organizationId: true },
-      })
-    )?.organizationId ?? null;
+const organizationIdForBranding =
+  (await getOrgIdFromMemberOrTeamId({
+    memberId: booking.userId ?? null,
+    teamId: booking.eventType?.teamId ?? null,
+  })) ?? null;
📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 f21be1a and 437dd37.

📒 Files selected for processing (19)
  • packages/app-store/vital/lib/reschedule.ts (3 hunks)
  • packages/app-store/wipemycalother/lib/reschedule.ts (3 hunks)
  • packages/emails/src/templates/BaseScheduledEmail.tsx (1 hunks)
  • packages/features/CalendarEventBuilder.ts (1 hunks)
  • packages/features/bookings/lib/handleBookingRequested.ts (4 hunks)
  • packages/features/bookings/lib/handleCancelBooking.ts (6 hunks)
  • packages/features/bookings/lib/handleConfirmation.ts (17 hunks)
  • packages/features/bookings/lib/handleNewBooking.ts (6 hunks)
  • packages/features/bookings/lib/handleSeats/reschedule/attendee/attendeeRescheduleSeatedBooking.ts (4 hunks)
  • packages/features/bookings/lib/handleSeats/reschedule/owner/combineTwoSeatedBookings.ts (4 hunks)
  • packages/features/bookings/lib/handleSeats/reschedule/owner/moveSeatedBookingToNewTimeSlot.ts (3 hunks)
  • packages/features/credentials/handleDeleteCredential.ts (5 hunks)
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts (5 hunks)
  • packages/features/handleMarkNoShow.ts (6 hunks)
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (5 hunks)
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (10 hunks)
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (5 hunks)
  • packages/trpc/server/routers/viewer/bookings/util.ts (2 hunks)
  • packages/types/Calendar.d.ts (2 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/emails/src/templates/BaseScheduledEmail.tsx
  • packages/types/Calendar.d.ts
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/features/bookings/lib/handleSeats/reschedule/owner/combineTwoSeatedBookings.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/handleSeats/reschedule/attendee/attendeeRescheduleSeatedBooking.ts
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
  • packages/features/bookings/lib/handleCancelBooking.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/bookings/lib/handleNewBooking.ts
  • packages/features/handleMarkNoShow.ts
  • packages/features/bookings/lib/handleSeats/reschedule/owner/moveSeatedBookingToNewTimeSlot.ts
  • packages/features/CalendarEventBuilder.ts
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
  • packages/features/bookings/lib/handleBookingRequested.ts
  • packages/app-store/wipemycalother/lib/reschedule.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:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/features/bookings/lib/handleSeats/reschedule/owner/combineTwoSeatedBookings.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/handleSeats/reschedule/attendee/attendeeRescheduleSeatedBooking.ts
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
  • packages/features/bookings/lib/handleCancelBooking.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/bookings/lib/handleNewBooking.ts
  • packages/features/handleMarkNoShow.ts
  • packages/features/bookings/lib/handleSeats/reschedule/owner/moveSeatedBookingToNewTimeSlot.ts
  • packages/features/CalendarEventBuilder.ts
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
  • packages/features/bookings/lib/handleBookingRequested.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/features/bookings/lib/handleSeats/reschedule/owner/combineTwoSeatedBookings.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/bookings/lib/handleSeats/reschedule/attendee/attendeeRescheduleSeatedBooking.ts
  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
  • packages/features/bookings/lib/handleCancelBooking.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/features/bookings/lib/handleNewBooking.ts
  • packages/features/handleMarkNoShow.ts
  • packages/features/bookings/lib/handleSeats/reschedule/owner/moveSeatedBookingToNewTimeSlot.ts
  • packages/features/CalendarEventBuilder.ts
  • packages/features/ee/round-robin/roundRobinManualReassignment.ts
  • packages/features/bookings/lib/handleBookingRequested.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
🧠 Learnings (6)
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/app-store/vital/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/features/credentials/handleDeleteCredential.ts
  • packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/wipemycalother/lib/reschedule.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/trpc/server/routers/viewer/bookings/util.ts
  • packages/features/bookings/lib/handleConfirmation.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
📚 Learning: 2025-08-21T13:55:23.470Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/service/FormWebhookService.ts:0-0
Timestamp: 2025-08-21T13:55:23.470Z
Learning: In the new webhook architecture for Cal.com, schedulePayload is considered legacy code that doesn't fit the clean architecture. The new architecture keeps webhook scheduling logic within the Service layer, specifically through a new method WebhookService.scheduleDelayedWebhooks, rather than using the old centralized schedulePayload helper.

Applied to files:

  • packages/features/bookings/lib/handleConfirmation.ts
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking fields are restricted structures containing only specific fields (id, eventTypeId, userId, and sometimes additional fields like startTime or smsReminderNumber) rather than full database booking objects, so there are no security or PII leakage concerns when using these booking objects in webhook payloads.

Applied to files:

  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
📚 Learning: 2025-08-21T12:28:42.018Z
Learnt from: alishaz-polymath
PR: calcom/cal.com#23247
File: packages/features/webhooks/lib/factory/WebhookPayloadFactory.ts:274-282
Timestamp: 2025-08-21T12:28:42.018Z
Learning: In BookingPaymentInitiatedDTO and other webhook DTOs in packages/features/webhooks/lib/dto/types.ts, the booking field is a restricted structure containing only specific fields (id, eventTypeId, userId) rather than the full database booking object, so there are no security or PII leakage concerns when passing the booking object to buildEventPayload.

Applied to files:

  • packages/trpc/server/routers/viewer/bookings/confirm.handler.ts
🧬 Code graph analysis (15)
packages/app-store/vital/lib/reschedule.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleSeats/reschedule/owner/combineTwoSeatedBookings.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleConfirmation.ts (4)
packages/lib/getTeamIdFromEventType.ts (1)
  • getTeamIdFromEventType (3-29)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/lib/server/service/workflows.ts (1)
  • WorkflowService (10-131)
packages/features/bookings/lib/handleSeats/reschedule/attendee/attendeeRescheduleSeatedBooking.ts (3)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/emails/email-manager.ts (1)
  • sendRescheduledSeatEmailAndSMS (323-342)
packages/types/Calendar.d.ts (1)
  • Person (33-46)
packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleCancelBooking.ts (2)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/emails/email-manager.ts (1)
  • sendCancelledEmailsAndSMS (476-534)
packages/features/credentials/handleDeleteCredential.ts (2)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/trpc/server/routers/viewer/bookings/addGuests.handler.ts (2)
packages/lib/getOrgIdFromMemberOrTeamId.ts (1)
  • getOrgIdFromMemberOrTeamId (47-58)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleNewBooking.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/handleMarkNoShow.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleSeats/reschedule/owner/moveSeatedBookingToNewTimeSlot.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/ee/round-robin/roundRobinManualReassignment.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/features/bookings/lib/handleBookingRequested.ts (3)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/emails/email-manager.ts (2)
  • sendOrganizerRequestEmail (433-436)
  • sendAttendeeRequestEmailAndSMS (451-454)
packages/lib/server/service/workflows.ts (1)
  • WorkflowService (10-131)
packages/app-store/wipemycalother/lib/reschedule.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
🔇 Additional comments (18)
packages/features/ee/round-robin/roundRobinManualReassignment.ts (2)

382-382: LGTM—Correctly strips cancellationReason from scheduled/updated emails.

The destructuring removes cancellationReason from events passed to sendRoundRobinScheduledEmailsAndSMS (line 387) and sendRoundRobinUpdatedEmailsAndSMS (line 436), while preserving it for the cancellation email (line 407) and workflow handling (line 445). This ensures cancellation reasons appear only in cancellation emails, not in new-booking notifications.


645-645: LGTM—Correctly applies computed hideBranding to new event workflows.

The computed branding flag is properly passed to scheduleWorkflowReminders, ensuring new event workflows respect the organization/team branding settings.

packages/trpc/server/routers/viewer/bookings/util.ts (1)

27-56: LGTM on eventType select with branding fields.

The switch from include to select with explicit field enumeration correctly addresses the previous review feedback and aligns with the coding guideline to always use select for Prisma queries. The addition of hideBranding flags on team, parent, and owner properly supports the PR's branding-control objective.

packages/features/credentials/handleDeleteCredential.ts (3)

16-19: LGTM! Imports are used appropriately.

The new imports are correctly utilized in the branding logic and error handling below.


338-367: Excellent error handling improvement!

The error handling for shouldHideBrandingForEvent now logs failures before falling back to the owner's branding preference. This addresses the concern from the previous review and ensures unexpected errors are visible for debugging while maintaining a safe fallback.


407-407: LGTM! Branding flag correctly propagated.

The computed hideBranding value is correctly passed to the email function, ensuring cancellation emails respect the branding preference.

packages/features/bookings/lib/handleCancelBooking.ts (1)

86-99: Upstream select includes required branding fields Selections for getBookingToDelete include team.hideBranding, team.parent.hideBranding, team.parentId, and user.hideBranding; shouldHideBrandingForEvent will compute correctly.

packages/features/CalendarEventBuilder.ts (1)

263-274: Nice builder extensions

withOneTimePassword and the new withBranding helper look solid—this will make downstream updates cleaner.

packages/features/bookings/lib/handleNewBooking.ts (4)

1346-1347: Good: centralized branding applied to event build

withBranding(hideBranding) is the right place; ensures consistent downstream usage.


2227-2231: Good: pass hideBranding into workflow scheduling (payment initiated)

Propagation is consistent.


2408-2412: Good: include hideBranding in mandatory reminders

Matches expected behavior.


2421-2426: Good: include hideBranding in workflows for new booking

Propagation looks correct.

packages/trpc/server/routers/viewer/bookings/confirm.handler.ts (1)

295-296: Good: propagate hideBranding to evt and handleConfirmation

Consistent with the branding model.

Also applies to: 357-358

packages/features/bookings/lib/handleBookingRequested.ts (1)

116-120: Good: hideBranding applied to emails and workflows

Propagation looks correct.

Also applies to: 162-179

packages/features/bookings/lib/handleConfirmation.ts (4)

3-5: LGTM: EventManager imports/types

Imports look correct.


75-76: LGTM: hideBranding plumbed through handleConfirmation signature

Non-breaking default and explicit propagation.

Also applies to: 87-88


99-108: LGTM: compute org/team context and calculated branding once

Good use of getOrgIdFromMemberOrTeamId + shouldHideBrandingForEvent.

Also applies to: 109-151


189-195: LGTM: hideBranding forwarded to emails and workflow scheduling

Propagation is consistent across confirmation, reminders, and paid workflows.

Also applies to: 442-449, 451-459, 659-665

Comment on lines +11 to 12
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fix prisma import (build blocker)

@calcom/prisma exports a named prisma. Default import will fail.

Apply this diff:

-import prisma from "@calcom/prisma";
+import { prisma } from "@calcom/prisma";
🤖 Prompt for AI Agents
In packages/features/bookings/lib/handleBookingRequested.ts around lines 11 to
12, the file currently uses a default import from "@calcom/prisma" which fails
because the package exports a named prisma; replace the default import with the
named export (e.g., import { prisma } from "@calcom/prisma") and keep the Prisma
types import as-is or adjust to import type if needed so the code references the
exported named prisma instead of a default.

Comment on lines 334 to +421
//Workflows - set reminders for confirmed events
try {
if (workflows.length > 0) {
const _isFirstBooking = !recurringEvent || !recurringEvent.count;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Undefined identifier recurringEvent (compile error)

recurringEvent is not defined in this scope. You already compute _isFirstBooking per iteration. Remove the stray declaration.

-  if (workflows.length > 0) {
-    const _isFirstBooking = !recurringEvent || !recurringEvent.count;
+  if (workflows.length > 0) {
🤖 Prompt for AI Agents
In packages/features/bookings/lib/handleConfirmation.ts around lines 418 to 421,
there is a reference to an undefined identifier `recurringEvent` when computing
_isFirstBooking; remove the stray `recurringEvent` usage and instead rely on the
per-iteration value already computed for _isFirstBooking (or compute it from the
current loop's recurring info). Edit the conditional to use existing variables
in scope (e.g., the iteration's recurring object or its count) so the compiler
error is resolved and no undefined identifier remains.

Comment on lines +128 to 136
team?: {
id: number;
name: string;
parentId?: number | null;
hideBranding?: boolean | null;
parent?: { hideBranding?: boolean | null } | null;
} | null;
owner?: { id: number; hideBranding?: boolean | null } | null;
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Critical type mismatch: eventType type is too broad.

The type declares EventType & { team?: ..., owner?: ... }, which implies all EventType fields are present. However, the actual select at lines 27–56 only fetches specific fields (id, title, teamId, hideOrganizerEmail, customReplyToEmail, seatsPerTimeSlot, seatsShowAttendees, recurringEvent). Code that accesses booking.eventType.someOtherField will compile but fail at runtime.

Replace the intersection with an explicit type matching the selected fields:

 export type BookingsProcedureContext = {
   booking: Booking & {
     eventType:
-      | (EventType & {
+      | ({
+          id: number;
+          title: string;
+          teamId: number | null;
+          hideOrganizerEmail: boolean;
+          customReplyToEmail: string | null;
+          seatsPerTimeSlot: number | null;
+          seatsShowAttendees: boolean | null;
+          recurringEvent: Prisma.JsonValue | null;
           team?: {
             id: number;
             name: string;
             parentId?: number | null;
             hideBranding?: boolean | null;
             parent?: { hideBranding?: boolean | null } | null;
           } | null;
           owner?: { id: number; hideBranding?: boolean | null } | null;
         })
       | null;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
team?: {
id: number;
name: string;
parentId?: number | null;
hideBranding?: boolean | null;
parent?: { hideBranding?: boolean | null } | null;
} | null;
owner?: { id: number; hideBranding?: boolean | null } | null;
})
export type BookingsProcedureContext = {
booking: Booking & {
eventType:
| ({
id: number;
title: string;
teamId: number | null;
hideOrganizerEmail: boolean;
customReplyToEmail: string | null;
seatsPerTimeSlot: number | null;
seatsShowAttendees: boolean | null;
recurringEvent: Prisma.JsonValue | null;
team?: {
id: number;
name: string;
parentId?: number | null;
hideBranding?: boolean | null;
parent?: { hideBranding?: boolean | null } | null;
} | null;
owner?: { id: number; hideBranding?: boolean | null } | null;
})
| null;
};
};

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 (2)
packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)

339-340: Use builder.withHideBranding when present.

Prefer the builder API over direct property mutation.

-  builder.calendarEvent.hideBranding = hideBranding;
+  if (typeof (builder as any).withHideBranding === "function") {
+    (builder as any).withHideBranding(hideBranding);
+  } else {
+    builder.calendarEvent.hideBranding = hideBranding;
+  }
packages/app-store/vital/lib/reschedule.ts (1)

227-227: Use builder.withHideBranding when present.

Prefer the builder API; fall back to direct assignment.

-    builder.calendarEvent.hideBranding = hideBranding;
+    if (typeof (builder as any).withHideBranding === "function") {
+      (builder as any).withHideBranding(hideBranding);
+    } else {
+      builder.calendarEvent.hideBranding = hideBranding;
+    }
🧹 Nitpick comments (5)
packages/app-store/wipemycalother/lib/reschedule.ts (2)

37-63: Trim Prisma select to only needed fields (customReplyToEmail appears unused here).

Drop unused fields to keep payload minimal.

As per coding guidelines

           select: {
             id: true,
             teamId: true,
             parentId: true,
             metadata: true,
             hideOrganizerEmail: true,
-            customReplyToEmail: true,
             owner: {
               select: {
                 id: true,
                 hideBranding: true,
               },
             },

227-227: Prefer builder API to set hideBranding if available.

Use withHideBranding when present; fall back to direct assignment.

-    builder.calendarEvent.hideBranding = hideBranding;
+    if (typeof (builder as any).withHideBranding === "function") {
+      (builder as any).withHideBranding(hideBranding);
+    } else {
+      builder.calendarEvent.hideBranding = hideBranding;
+    }
packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)

297-312: Use existing helper to compute organizationId; avoid extra DB query.

getOrgIdFromMemberOrTeamId is imported; prefer it over fetching profile.

As per coding guidelines

-  // For user events, fetch the user's profile to get the organization ID
-  const userOrganizationId = !bookingToReschedule.eventType?.team
-    ? (
-        await prisma.profile.findFirst({
-          where: {
-            userId: bookingToReschedule.userId,
-          },
-          select: {
-            organizationId: true,
-          },
-        })
-      )?.organizationId
-    : null;
-  
-  const computedOrgId = bookingToReschedule.eventType?.team?.parentId ?? userOrganizationId ?? null;
+  const computedOrgId =
+    bookingToReschedule.eventType?.team?.parentId ??
+    (await getOrgIdFromMemberOrTeamId({
+      memberId: bookingToReschedule.userId,
+      teamId: bookingToReschedule.eventType?.team?.id ?? null,
+    })) ??
+    null;
packages/app-store/vital/lib/reschedule.ts (2)

37-63: Trim Prisma select to only needed fields (customReplyToEmail appears unused here).

Keep selects minimal.

As per coding guidelines

           select: {
             id: true,
             teamId: true,
             parentId: true,
             metadata: true,
             hideOrganizerEmail: true,
-            customReplyToEmail: true,
             owner: {
               select: {
                 id: true,
                 hideBranding: true,
               },
             },

201-225: Consider extracting a shared helper for branding computation.

This logic repeats across modules; a util would reduce duplication and drift.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • 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 437dd37 and 919daef.

📒 Files selected for processing (3)
  • packages/app-store/vital/lib/reschedule.ts (3 hunks)
  • packages/app-store/wipemycalother/lib/reschedule.ts (3 hunks)
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (5 hunks)
🧰 Additional context used
📓 Path-based instructions (3)
**/*.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:

  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/vital/lib/reschedule.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:

  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/vital/lib/reschedule.ts
**/*.{ts,tsx,js,jsx}

⚙️ CodeRabbit configuration file

Flag default exports and encourage named exports. Named exports provide better tree-shaking, easier refactoring, and clearer imports. Exempt main components like pages, layouts, and components that serve as the primary export of a module.

Files:

  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/vital/lib/reschedule.ts
🧠 Learnings (3)
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma select uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/vital/lib/reschedule.ts
📚 Learning: 2025-09-03T11:54:05.409Z
Learnt from: supalarry
PR: calcom/cal.com#23514
File: apps/api/v2/src/ee/bookings/2024-08-13/services/bookings.service.ts:579-582
Timestamp: 2025-09-03T11:54:05.409Z
Learning: In calcom/cal.com bookings repository methods, when Prisma include uses `eventType: true`, all eventType fields including seatsShowAttendees are automatically included in the selection. Explicit field selection is not required when using `true` for nested relations.

Applied to files:

  • packages/app-store/wipemycalother/lib/reschedule.ts
  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
  • packages/app-store/vital/lib/reschedule.ts
📚 Learning: 2025-08-21T13:44:06.805Z
Learnt from: supalarry
PR: calcom/cal.com#23217
File: apps/api/v2/src/ee/event-types/event-types_2024_06_14/services/output-event-types.service.ts:93-94
Timestamp: 2025-08-21T13:44:06.805Z
Learning: In apps/api/v2/src/ee/event-types/event-types_2024_06_14/event-types.repository.ts, repository functions that use explicit Prisma select clauses (like getEventTypeWithSeats) are used for specific purposes and don't need to include all EventType fields like bookingRequiresAuthentication. These methods don't feed into the general OutputEventTypesService_2024_06_14 flow.

Applied to files:

  • packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts
🧬 Code graph analysis (3)
packages/app-store/wipemycalother/lib/reschedule.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
packages/app-store/vital/lib/reschedule.ts (1)
packages/lib/hideBranding.ts (1)
  • shouldHideBrandingForEvent (76-113)
⏰ Context from checks skipped due to timeout of 180000ms. 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 (4)
packages/app-store/wipemycalother/lib/reschedule.ts (2)

185-200: Org context derivation LGTM.

Using team.parentId with user’s org fallback is correct for user events.


201-225: Branding decision logic and logged fallback look solid.

Good inputs to shouldHideBrandingForEvent and sane fallback to owner.hideBranding with warn log.

packages/trpc/server/routers/viewer/bookings/requestReschedule.handler.ts (1)

63-90: EventType select looks appropriate.

Selected fields are used (bookingFields, hideOrganizerEmail, customReplyToEmail, metadata, branding flags).

packages/app-store/vital/lib/reschedule.ts (1)

185-200: Org context derivation LGTM.

team.parentId with user org fallback is correct for user events.

@hackice20 hackice20 closed this Oct 10, 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 🐛 bug Something isn't working community Created by Linear-GitHub Sync emails area: emails, cancellation email, reschedule email, inbox, spam folder, not getting email size/XXL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants