* feat: calendar cache and sync - wip
* Add env.example
* refactor on CalendarCacheEventService
* remove test console.log
* Fix type checks errors
* chore: remove pt comment
* add route.ts
* chore: fix tests
* Improve cache impl
* chore: update recurring event id
* chore: small improvements
* calendar cache improvements
* Fix remove dynamic imports
* Add cleanup stale cache
* Fix tests
* add event update
* type fixes
* feat: add comprehensive tests for new calendar subscription API routes
- Add tests for /api/cron/calendar-subscriptions-cleanup route (9 tests)
- Add tests for /api/cron/calendar-subscriptions route (10 tests)
- Add tests for /api/webhooks/calendar-subscription/[provider] route (11 tests)
- Add missing feature flags for calendar-subscription-cache and calendar-subscription-sync
- All 30 tests pass with comprehensive coverage of authentication, feature flags, error handling, and service instantiation
Tests cover:
- Authentication scenarios (API key validation, Bearer tokens, query parameters)
- Feature flag combinations (cache/sync enabled/disabled states)
- Success and error handling (including non-Error exceptions)
- Service instantiation with proper dependency injection
- Provider validation for webhook endpoints
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add comprehensive tests for calendar subscription services, repositories, and adapters
- Add unit tests for CalendarSubscriptionService with subscription, webhook, and event processing
- Add unit tests for CalendarCacheEventService with cache operations and cleanup
- Add unit tests for CalendarSyncService with Cal.com event filtering and booking operations
- Add unit tests for CalendarCacheEventRepository with CRUD operations
- Add unit tests for SelectedCalendarRepository with calendar selection management
- Add unit tests for GoogleCalendarSubscriptionAdapter with subscription and event fetching
- Add unit tests for Office365CalendarSubscriptionAdapter with placeholder implementation
- Add unit tests for AdaptersFactory with provider management and adapter creation
- Fix lint issues by removing explicit 'any' type casting and unused variables
- All tests follow Cal.com conventions using Vitest framework with proper mocking
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: improve calendar-subscriptions-cleanup test performance by adding missing mocks
- Add comprehensive mocks for defaultResponderForAppDir, logger, performance monitoring, and Sentry
- Fix slow test execution (933ms -> <100ms) caused by missing dependency mocks
- Ensure consistent test performance across different environments
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* Fix tests
* Fix tests
* type fix
* Fix coderabbit comments
* Fix types
* Fix test
* Update apps/web/app/api/cron/calendar-subscriptions/route.ts
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Fixes by first review
* feat: add database migrations for calendar cache and sync fields
- Add CalendarCacheEventStatus enum with confirmed, tentative, cancelled values
- Add new fields to SelectedCalendar: channelId, channelKind, channelResourceId, channelResourceUri, channelExpiration, syncSubscribedAt, syncToken, syncedAt, syncErrorAt, syncErrorCount
- Create CalendarCacheEvent table with foreign key to SelectedCalendar
- Add necessary indexes and constraints for performance and data integrity
Fixes database schema issues causing e2e test failures with 'column does not exist' errors.
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* only google-calendar for now
* docs: add Calendar Cache and Sync feature documentation
- Add comprehensive feature overview and motivation
- Document feature flags with SQL examples
- Include SQL examples for enabling features for users and teams
- Reference technical documentation files
Addresses PR #23876 documentation requirements
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* docs: update calendar subscription README with comprehensive documentation
- Undo incorrect changes to main README.md
- Update packages/features/calendar-subscription/README.md with:
- Feature overview and motivation
- Environment variables section
- Complete feature flags documentation with SQL examples
- SQL examples for enabling features for users and teams
- Detailed architecture documentation
Addresses PR #23876 documentation requirements
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix docs
* Fix test to available calendars
* Fix test to available calendars
* add migration and sync boilerplate
* fix typo
* remove double log
* sync boilerplate
* remove console.log
* only subscribe for google calendar
* adjust for 3 months fetch
* only subscribe for teams that have feature enabled
* adjust tests
* chore: safe increment error count
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* calendar sync
* test: add comprehensive tests for CalendarSyncService
- Add comprehensive test coverage for CalendarSyncService methods
- Test handleEvents, cancelBooking, and rescheduleBooking functionality
- Cover edge cases like missing UIDs, malformed UIDs, and error handling
- Fix dynamic import usage in CalendarSyncService to use .default
- Remove invalid properties from handleNewBooking call to fix type errors
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: add skipCalendarSyncTaskCreation flag to handleCancelBooking to prevent infinite loops
- Add skipCalendarSyncTaskCreation field to bookingCancelSchema in zod-utils.ts
- Update handleCancelBooking to skip EventManager.cancelEvent when flag is true
- Update CalendarSyncService.cancelBooking to pass skipCalendarSyncTaskCreation: true
- Update CalendarSyncService.rescheduleBooking to pass skipCalendarSyncTaskCreation: true
- Update tests to verify the flags are passed correctly
This prevents infinite loops when calendar events are cancelled/rescheduled from
external calendars (Google/Office365) which trigger webhooks to Cal.com, which
would otherwise try to update the external calendar again.
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: rename flag to skipCalendarSyncTaskCancellation and use static imports
- Rename skipCalendarSyncTaskCreation to skipCalendarSyncTaskCancellation in handleCancelBooking
- Convert dynamic imports to static imports in CalendarSyncService
- Update tests to use vi.hoisted for proper mock hoisting with static imports
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add error handling and static import for handleNewBooking
- Create index.ts entry point for handleNewBooking directory
- Add try-catch error handling to cancelBooking and rescheduleBooking
- Log errors but don't block calendar sync operations
- Update tests to verify errors are caught and logged, not thrown
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: use RegularBookingService directly and fix safeStringify usage
- Remove handleNewBooking/index.ts and call getRegularBookingService().createBooking() directly in CalendarSyncService
- Fix safeStringify usage: apply to error itself, not wrapper object
- Update tests to mock getRegularBookingService instead of handleNewBooking
Addresses PR comments from Volnei and Cubic-dev-ai
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: restore handleNewBooking/index.ts and fix bookingData structure for calendar sync
- Restore handleNewBooking/index.ts as entry point for static imports
- Fix CalendarSyncService.rescheduleBooking to use correct bookingData structure
with required fields (eventTypeId, start, end, timeZone, language, metadata)
- Use rescheduleUid to indicate this is a reschedule operation
- Fix safeStringify usage in error logging (wrap only the error, not the whole object)
- Update tests to match new bookingData structure
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: address PR comments - use RegularBookingService directly, remove unnecessary flags, fix booking reference update
- Remove handleNewBooking/index.ts wrapper and use getRegularBookingService().createBooking() directly in CalendarSyncService
- Remove allRemainingBookings and cancelSubsequentBookings flags from cancelBooking (only cancel the specific booking, not the entire series)
- Move bookingReference update outside skipCalendarSyncTaskCancellation block for data consistency
- Update tests to match new implementation
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* chore: remove unnecessary comment from zod-utils.ts
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add Sentry metrics telemetry and fix null assertions in CalendarSyncService
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: use dynamic import for getRegularBookingService to avoid RAQB import in server context
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: use dynamic import for findTeamMembersMatchingAttributeLogic to avoid RAQB import in server context
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* improve booking update
* add more tests and edge cases
* fix: add required actionSource and Sentry mocks to calendar sync
Add actionSource: "SYSTEM" to handleCancelBooking call after it became
required, and mock @sentry/nextjs in test files.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: hide branding for teams
* fix: remove unused organizationId and username fields from profiles select
Addresses Cubic AI review feedback (confidence 9/10) to select only
the profile fields that are actually used. The organizationId and
username fields were fetched but never referenced in this function.
Co-Authored-By: unknown <>
* fix: unit tests
* fix: add prisma named export to test mock
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* Add tests: packages/features/profile/lib/hideBranding.test.ts
Generated by Paragon from proposal for PR #27643
* chore: implement cubic feedback
* fix: merge conflicts
* fix: unit tests
* fixup
* refactor: implement DI pattern for event type service
* fix: atoms build
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: workflow auto translation
* tests: add unit tests
* refactor: tests and workflow
* fix: type err
* fix: type err
* fix: remove redundant index on WorkflowStepTranslation
The @@index on [workflowStepId, field, targetLocale] duplicates the @@unique
constraint on the same columns. A unique index already provides efficient
lookups, so the separate @@index adds storage overhead and write latency
without benefit.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* fix: correct locale mapping when translation API returns null
Map translations with their corresponding locales before filtering to
preserve correct locale-to-translation associations. Previously, filtering
out null translations would reindex the array, causing incorrect locale
mappings when any translation in the batch failed.
Also fixes pre-existing lint warnings:
- Move exports to end of file
- Add explicit return type to processTranslations
- Replace ternary with if-else for upsertMany selection
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: address review feedback for workflow auto-translation
- Add change detection before creating translation tasks
- Rename userLocale to sourceLocale in task props for clarity
- Show source language in UI with new translation key
- Extract SUPPORTED_LOCALES to shared translationConstants.ts
- Fix locale mapping bug in translateEventTypeData.ts
- Add WhatsApp translation support
- Abstract translation lookup into shared translationLookup.ts helper
- Restore if-else readability for SCANNING_WORKFLOW_STEPS
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-Authored-By: unknown <>
* fix: update test to use sourceLocale instead of userLocale
Co-Authored-By: unknown <>
* refactor: feedback
* fix: handle first time
* fix: tests
* fix: tests
* fix: address Cubic AI review feedback (confidence 9/10 issues)
- WhatsApp translation: Apply variable substitution using getSMSMessageWithVariables
and clear contentSid when using translated body to ensure Twilio uses the
translated text instead of the original template
- update.handler.ts: Change sourceLocale assignment from ?? to || for consistency
with tasker payload behavior (line 481)
- ITranslationService.ts: Rename methods from plural to singular naming:
- getWorkflowStepTranslations -> getWorkflowStepTranslation
- getEventTypeTranslations -> getEventTypeTranslation
Updated all call sites and tests accordingly
Co-Authored-By: unknown <>
* fix: address Cubic AI review feedback (confidence 9/10+ issues)
- Fix getSMSMessageWithVariables to handle WHATSAPP_ATTENDEE action for
locale and timezone (confidence 9/10)
- Remove WhatsApp translation feature that set contentSid to undefined
since Twilio ignores body parameter for WhatsApp and requires
pre-approved Message Templates (confidence 10/10)
Co-Authored-By: unknown <>
* fix: translatio
* Add tests: packages/features/eventTypeTranslation/repositories/EventTypeTranslationRepository.test.ts
Generated by Paragon from proposal for PR #27087
* Add tests: packages/features/tasker/tasks/translateWorkflowStepData.test.ts
Generated by Paragon from proposal for PR #27087
* chore: nit
* chore: verfied atg
* fix: set sourceLocale for new steps, add shouldDirty to checkbox, remove spec docs
- Set sourceLocale fallback in addedSteps mapping to fix stale detection mismatch
- Add { shouldDirty: true } to autoTranslateEnabled checkbox onChange
- Remove specs/workflow-translation/ directory (planning docs, not for repo)
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
Co-Authored-By: unknown <>
* chore: add specs back
* fix: type error
* fix: type error
* fix: type err
* fix: tests
* refactor: feedback
* fix: type err
* refactor
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
* perf: improve getTotalBookingDuration query
* perf: improve getTotalBookingDuration query
* test: add unit tests for getTotalBookingDurationForUsers and yearly duration pre-fetching
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: integration tests
* fix: update tests
* fix: use vi.stubEnv instead of direct process.env modification for test isolation
Replace direct deletion of process.env variables with vi.stubEnv() to ensure
automatic restoration after tests, preventing flaky tests and side effects
in parallel execution.
Issue identified by Cubic AI (confidence: 9/10)
Co-Authored-By: unknown <>
* chore: add log
* refactor: imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add no users found fallback action for routing forms
- Add NoUsersFoundFallbackActionType enum with CustomPageMessage and ExternalRedirectUrl options
- Add noUsersFoundFallbackAction field to zodNonRouterRoute schema
- Add NoUsersFoundFallback UI component in RouteBuilder for configuring fallback action
- Update handleResponse to return noUsersFoundFallbackAction when no team members match
- Add i18n translation keys for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: change fallback to use same action options as main route
- Replace noUsersFoundFallbackAction with fallbackAction that has same structure as main action
- Add action dropdown and input fields to fallback attributes query builder section
- Support Event redirect, External URL, and Custom page options in fallback
- Remove separate NoUsersFoundFallback toggle component
- Update handleResponse to return fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure fallbackAction type is always defined in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add guards for route.fallbackAction in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only return fallbackAction when no users are found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: restructure fallback section to mirror main route structure
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure backwards compatibility for existing routes with fallbackAttributesQueryValue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction with main event type for backwards compatibility
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include fallbackAction in getRoutesToSave to persist changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use fallbackAction in getRoutedUrl when no team members found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use i18n for fallback label and add tests for fallback action
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: do not trigger fallback action when CRM contact owner is found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: prioritize fallbackAction over fallbackAttributesQueryValue
When a route has a fallbackAction configured, skip the
fallbackAttributesQueryValue to ensure the fallbackAction is triggered
when no team members are found. This maintains backwards compatibility
by only using fallbackAttributesQueryValue when fallbackAction is not set.
Also treats teamMemberIdsMatchingAttributeLogic being null (routing couldn't
run, e.g., missing orgId) the same as an empty array for the purpose of
triggering the fallbackAction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract RouteActionSelector shared component for action selection UI
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add i18n string for default custom page message
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* revert: remove webhook trigger changes (to be moved to separate PR)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add ROUTING_FORM_FALLBACK_HIT to PayloadBuilderFactory trigger mapping
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* style: apply biome formatting fixes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: restore removed comments in RouteBuilder
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add prisma migration for ROUTING_FORM_FALLBACK_HIT enum value
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add ROUTING_FORM_FALLBACK_HIT to FormTriggerEvents type union
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add test coverage for triggerFallbackWebhook function
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: consolidate fallback webhook firing with form submission webhooks
Move ROUTING_FORM_FALLBACK_HIT webhooks into the same Promise.all() batch
as FORM_SUBMITTED / FORM_SUBMITTED_NO_EVENT, eliminating redundant
getWebhooks/getWebhookTargetEntity/getOrgIdFromMemberOrTeamId calls.
- Add fallbackAction parameter to _onFormSubmission and onSubmissionOfFormResponse
- Remove triggerFallbackWebhook function and all its usages/imports
- Move getFallbackAction() earlier in handleResponse.ts and pass result through
- Update all tests to verify batched webhook behavior
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add UI option for trigger
* feat: persist fallbackAction on queued form responses
When a routing form response is queued, the fallbackAction determined
during routing is now stored on the QueuedFormResponse record. When
the queued response is later converted to a real FormResponse, the
stored fallbackAction is passed to onSubmissionOfFormResponse so the
ROUTING_FORM_FALLBACK_HIT webhook fires correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add coverage for fallbackAction in queued form response flow
Verify that fallbackAction is persisted when queuing and passed through
to onSubmissionOfFormResponse when the queued response is converted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace request.url with WEBAPP_URL from @calcom/lib/constants as the base URL
for NextResponse.redirect() in booking confirmation API routes. Behind a reverse
proxy, request.url resolves to http://localhost:3000 instead of the public domain.
Fixes#20358
Co-Authored-By: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* SMS reminders not trigger
* addressed devin review
* Add tests: packages/features/ee/workflows/lib/service/scheduleLazySMSWorkflow.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/ee/workflows/lib/reminders/reminderScheduler.smsLazy.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/tasker/tasks/sendWorkflowSMS.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/ee/workflows/lib/reminders/smsReminderManager.test.ts
Generated by Paragon from proposal for PR #27942
* Delete packages/features/ee/workflows/lib/reminders/reminderScheduler.smsLazy.test.ts
* fix: update sendWorkflowSMS tests to match refactored implementation
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: use smsReminderNumber fallback for attendee phone in SMS workflows
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* revert: keep only scheduleSMSReminders.ts changes, revert all other files
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: restore reminderScheduler.ts and smsReminderManager.ts to original branch state
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: use smsReminderNumber fallback for attendee phone in WhatsApp reminders
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add strategyName property to ISeatBillingStrategy interface and all implementations
- Log which strategy handled the invoice.upcoming webhook (not just whether it applied)
- Always log strategy result (not just when applied=true) for better observability
- Rename highWaterMarkApplied to strategyApplied since applied covers multiple strategies
Co-Authored-By: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(caldav): use consistent UIDs and inject VTIMEZONE in iCalendar output
Two remaining CalDAV interop issues from #9485:
1. UID consistency: use the booking's canonical UID (event.uid) instead
of always generating a new random UUID. CalDAV servers use UID as the
event identifier — different UIDs cause duplicate calendar entries.
2. VTIMEZONE injection: the ics library generates UTC times with no
VTIMEZONE block. CalDAV servers like Fastmail read this as UTC and
send scheduling emails with wrong times. Per RFC 5545 §3.6.5,
DTSTART with TZID requires a matching VTIMEZONE component. We now
build a proper VTIMEZONE using binary-searched DST transitions for
the event's year, handling Northern/Southern hemisphere correctly.
* fix: use pre-transition offset for VTIMEZONE DTSTART per RFC 5545
The DTSTART in VTIMEZONE components must represent the local time
interpreted with the pre-transition offset (TZOFFSETFROM), not the
post-transition offset. For example, US Eastern spring forward DTSTART
should be 02:00 (EST), not 03:00 (EDT).
* Remove comments on UID handling in createEvent
Removed comments about UID handling for calendar events.
* Revise injectVTimezone documentation
Update injectVTimezone function documentation to clarify UTC handling.
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: use import.meta.env instead of process.env in embed.ts
Using import.meta.env is Vite's native way to handle environment variables
in browser bundles. This prevents biome from auto-adding 'import process
from node:process' which breaks the embed.js in browsers.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Remove unncessar comment
* fix: migrate remaining process.env to import.meta.env in embed.ts
Complete the migration started by @hariombalhara by also converting
process.env usages at lines 1681, 1682, and 1700 to import.meta.env.
Update env.d.ts to include EMBED_PUBLIC_EMBED_FINGER_PRINT,
EMBED_PUBLIC_EMBED_VERSION, and INTEGRATION_TEST_MODE types.
This ensures no process.env references remain in embed.ts, fully
preventing Biome from auto-adding import process from node:process.
Co-Authored-By: unknown <>
* fix: add biome overrides for embed-core to prevent auto-import of node:process
Reverts import.meta.env back to process.env since API V2's tsconfig uses
module: commonjs which doesn't support import.meta. Instead, adds biome
overrides for embed-core to disable useNodejsImportProtocol, noProcessEnv,
and noProcessGlobal rules that were causing biome to auto-add
'import process from node:process' on save.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update routing form identifier hint text with learn more link
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* UI improvements
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* fix: prevent deselecting active option in ToggleGroup
Radix UI's single-type ToggleGroup allows deselection by default,
calling onValueChange("") when the active item is clicked.
Fix by converting to controlled mode with proper dual-mode support:
- Controlled (value prop): parent owns state, component just filters
empty values from onValueChange. Parent can still reject changes.
- Uncontrolled (defaultValue prop): internal useState prevents
deselection by only updating state for non-empty values.
* Update ToggleGroup.tsx
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
* fix: add CSRF protection to OAuth callback via HMAC-signed nonce
The OAuth state parameter was used only for passing application data
(returnTo, teamId) with no cryptographic binding to the user session.
An attacker could authorize their own account on a provider, capture the
authorization code, and trick a logged-in user into visiting the callback
URL to link the attacker's account to the victim's Cal.com profile.
Changes:
- encodeOAuthState: generate a random nonce and HMAC-sign it with
NEXTAUTH_SECRET + userId, injecting both into the OAuth state
- decodeOAuthState: verify the HMAC on callback using timingSafeEqual;
skip verification when nonce is absent (backwards compatible with apps
that don't yet use encodeOAuthState)
- Stripe callback: replace raw state.returnTo redirect with
getSafeRedirectUrl to prevent open redirect, remove redundant
getReturnToValueFromQueryState, add missing return on access_denied
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make CSRF nonce verification mandatory with allowlist for exempt apps
Makes nonce/HMAC verification mandatory by default in decodeOAuthState,
preventing attackers from bypassing CSRF protection by omitting nonce
fields from the state parameter.
Apps not yet migrated to encodeOAuthState (stripe, basecamp3, dub,
webex, tandem) are explicitly allowlisted and pass their slug to
decodeOAuthState to skip verification.
Addresses review feedback (identified by cubic) about the conditional
check being trivially bypassable.
Co-Authored-By: unknown <>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Replace hardcoded color with semantic text-success class
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove color class completely from display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add valuesWithParams support for translatable complex field values
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor(booking-audit): use discriminated union for displayFields and update consumers
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add explicit return type to getBookingHistoryHandler to bust stale tRPC build cache
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: replace $t() nested interpolation with separate translation keys and add translationsWithParams tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(meta-pixel): add trackingEvent to zod schema
* fix: do not allow trackingEvent to be optional
* feat(meta-pixel): add trackingEvent selection dropdown to UI
* feat(meta-pixel): add trackingEvent to Event Type App Data
* fix(meta-pixel): implement history API hook for SPA conversion tracking
* fix(meta-pixel): check for cancel in URL and prevent Re-wrapping of pushState
* Update EventTypeAppSettingsInterface.tsx
* fix: make trackingEvent optional in zod schema to fix type-check
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
* feat: add keyboard shortcuts and tooltips to booking slideover buttons
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: remove duplicate useBookingLocation import and fix import ordering
- Removed duplicate import of useBookingLocation from non-existent
@calcom/web/modules/bookings/hooks/useBookingLocation path
- Fixed import ordering to satisfy biome organizeImports rules
- Removed unnecessary code comment
- Original feature by @PeerRich via Devin AI
Co-Authored-By: unknown <>
* use same style arrows for both button and remove bydefault focus from them
* fix join button tooltip hover
* fix: disable keyboard shortcuts when overlays/dialogs are open on BookingDetailsSheet
Co-Authored-By: unknown <>
* fix: use focus-based detection instead of selector-based overlay detection for keyboard shortcuts
Co-Authored-By: unknown <>
* fix: allow keyboard shortcuts when focus is on sheet ancestors
Co-Authored-By: unknown <>
* fix: use capture phase for keyboard handler to prevent Enter from activating focused buttons
Co-Authored-By: unknown <>
* fix: allow shortcuts when focus is on page elements outside any Radix portal
Co-Authored-By: unknown <>
* fix: handle calendar event clicks in onInteractOutside to prevent sheet close/reopen
Co-Authored-By: unknown <>
* fix: stop arrow key propagation to prevent Radix dropdown from opening during booking navigation
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: always stop arrow key propagation when sheet is active, even at first/last booking
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: extract keyboard handler into testable utility with tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: resolve type errors in keyboard handler config and test mocks
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: add e2e tests for booking sheet keyboard shortcuts
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: replace text= locators with data-testid selectors in e2e tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* made 3 fixes: Fix 1 — Stabilize handleNext/handlePrevious/handleClose with useCallback
In BookingDetailsSheet.tsx, all three handler functions were plain arrow functions recreated on every render, causing the useEffect to tear down and re-attach the document keydown listener unnecessarily. Wrapped all three in useCallback with proper dependency arrays (the Zustand store functions they call).
Fix 2 — data-booking-list-item verified (no change needed)
Confirmed that data-booking-list-item is rendered on BookingListItem.tsx and data-booking-calendar-event is rendered on Event.tsx. The onInteractOutside handler in the final merged state correctly checks both selectors. No code change required.
Fix 3 — Removed dead code from JoinMeetingButton
Reverted JoinMeetingButton back to a plain function component:
Removed forwardRef wrapping (no caller passes a ref)
Removed showTooltip prop (unused — tooltip is handled by the parent BookingDetailsSheet)
Removed ref prop from the inner Button
Removed unused Tod forwardRef imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Co-authored-by: eunjae@cal.com <hey@eunjae.dev>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
* init
* wiring up
* fix type
* feat: implement DI pattern for webhook producer in API v2
- Export IWebhookProducerService and getWebhookProducer from platform-libraries
- Add WEBHOOK_PRODUCER token and useFactory provider in RegularBookingModule
- Inject webhookProducer in RegularBookingService and pass to base class
This follows the composition root pattern where only the NestJS module
knows about getWebhookProducer(), and all consumers depend only on the
IWebhookProducerService interface via constructor injection.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* test: migrate BOOKING_REQUESTED tests to new webhook architecture
- Remove failing BOOKING_REQUESTED tests from fresh-booking.test.ts (4 tests)
- Remove failing BOOKING_REQUESTED tests from reschedule.test.ts (2 tests)
- Remove failing BOOKING_REQUESTED test from collective-scheduling.test.ts (1 test)
- Replace WebhookTaskConsumer.test.ts with placeholder (constructor changed)
- Create new webhook architecture test suite:
- producer/WebhookTaskerProducerService.test.ts (14 tests)
- consumer/WebhookTaskConsumer.test.ts (8 tests)
- consumer/triggers/booking-requested.test.ts (8 tests)
The new test suite is organized by trigger type for extensibility as more
triggers are migrated to the producer/consumer pattern.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* test: remove paid events BOOKING_REQUESTED test (moved to new architecture)
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* wrap webhook in own try-catch
* wire datafetcher
* fix
* fix v2
* fix circular dependency
* --
* merge-conflict-resolve
* mreg-conflict-resolve
* remove early return
* test: add integration tests for BOOKING_REQUESTED webhook producer invocation
Cover all 8 scenarios verifying the booking flow correctly invokes
the webhook producer for BOOKING_REQUESTED:
1. Basic confirmation → queueBookingRequestedWebhook called
2. Booker-is-organizer + confirmation → still called
3. Confirmation threshold NOT met → not called (BOOKING_CREATED instead)
4. Confirmation threshold IS met → called
5. Paid event + confirmation → called after payment succeeds
6. Reschedule + confirmation (non-organizer) → called (not BOOKING_RESCHEDULED)
7. Reschedule + confirmation (organizer) → not called (BOOKING_RESCHEDULED instead)
8. Collective scheduling + confirmation → called
Adds reusable MockWebhookProducer helper in @calcom/testing for
extendable use as more webhook triggers migrate to the new architecture.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix bug
* fix conditional check
* remove unnecessary comment
* add missing expect
* remove empty test
* clean up
* tasker config
* --
* fix missing metadata
* remove faulty if else
* test: add payload content verification tests for BOOKING_REQUESTED webhook
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* remove unnecessary tests
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
* feat: replace admin API settings page with link to API docs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: remove unused AdminAPIView component
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: rename admin API tab to API Docs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: change URL prefill behavior to skip only invalid fields
Previously, when prefilling booking form fields from URL query parameters,
if any single field failed validation, all prefilling was skipped and an
empty object was returned.
This change modifies the behavior so that only invalid fields are skipped,
while valid fields are still prefilled. The implementation:
- Adds validateSingleFieldForPrefill() to validate individual fields
- Adds parseFieldByField() to collect valid fields when full parse fails
- Modifies getBookingResponsesPartialSchema to use field-by-field parsing
as a fallback when full schema validation fails
- Logs which specific fields were skipped and why
This improves user experience by preserving valid prefill data even when
some fields have invalid values.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: reduce code duplication in partial prefill validation
Simplify the implementation by integrating field-by-field error handling
directly into the existing preprocess() function instead of using separate
helper functions.
Changes:
- Remove validateSingleFieldForPrefill() and parseFieldByField() functions
- Simplify getBookingResponsesPartialSchema to directly use preprocess()
- Add try-catch wrapper in preprocess() when isPartialSchema=true
- Continue processing other fields even if one fails during preprocessing
- Log skipped fields with reasons at debug level
This reduces code duplication while maintaining the same behavior of
skipping only invalid fields during URL prefill.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* refactor: rename getRefinedValue to superRefineField and add edge case tests
- Rename getRefinedValue to superRefineField for clarity
- Remove duplicate multiselect test block
- Add test for empty object name field during partial prefill
- Add test for invalid URL format during partial prefill
- Add test for null responses during partial prefill
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Simplifu
* Simplif more
* Enhance booking responses schema tests to validate required fields and name variants. Updated error handling for empty names and added tests for first and last name requirements. Refactored schema validation logic to improve clarity and maintainability.
* fix types
* test: add test for email validation behavior during partial prefill
Documents that email validation is relaxed to z.string() during partial prefill,
consistent with phone field behavior where partial values like '+91' are accepted.
This addresses Cubic AI feedback (confidence 9/10) to verify email handling.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix
* Add tests: packages/features/bookings/lib/getBookingResponsesSchema.test.ts
Generated by Paragon from proposal for PR #26982
* Revert "Add tests: packages/features/bookings/lib/getBookingResponsesSchema.test.ts"
This reverts commit fc2b7ad7ca8099bc4f68e97b383ea16b41cdd772.
* Simplify
* fix: sanitize partial prefill log to avoid exposing user input data
Replaced raw Zod issues array in console.warn with a count of
validation errors to prevent sensitive user data from being logged.
Issue identified by cubic (confidence 9/10).
Co-Authored-By: unknown <>
* fix
* Remove throw Error
* Refactor booking responses schema tests to use helper functions for assertions
- Introduced `expectResponsesToBe` and `expectParsingToFail` helper functions to streamline test assertions.
- Replaced direct assertions with these helper functions for improved readability and maintainability.
- Removed redundant error handling code to simplify test logic.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add duplicate functionality for managed event types
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat: add disclaimer and redirect for managed event type duplication
- Add info alert disclaimer in duplicate modal for managed event types
- Pass schedulingType to duplicate dialog via query params
- Redirect to assignment tab after duplicating managed event types
- Add translation key for managed event type duplicate disclaimer
Co-Authored-By: peer@cal.com <peer@cal.com>
* test: add tests for managed event type duplication
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: replace PrismaMock unit test with Prisma integration test for duplicate handler (#27973)
* refactor: replace PrismaMock unit test with Prisma integration test for duplicate handler
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: re-throw TRPCErrors in duplicate handler catch block and assert specific error codes in tests
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
* fix: allow booking rejection via GET for email clients that do not support forms
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* Refactor test cases for GET handler in route.test.ts
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Replace 3 sequential queries (with expensive attendee email IN clauses)
with 2 parallel queries using indexed eventType.teamId filtering and
in-application user filtering with Sets for O(1) lookups.
Fix managed events booking-limit behavior: managed bookings now only
match by organizer userId (not attendee email), preserving the original
query semantics where managed bookings used userId-only matching.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Fix exclusion filter - include all team members
* Fix display when members aren't saved in the DB
* Update tests
* test: add missing negation operator tests for TEXT, NUMBER, and compound rules
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert non-intentional changes to AddMembersWithSwitch.tsx
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: decouple large calendar from features
* fix: move calendar to features since this is the better approach
* chore: move OOO slots to features
* feat: add DefaultOutOfOfficeSlot fallback for calendar OOO rendering
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* chore: cleanup unused LargeCalendar component
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Replaced dayjs with date-fns for better performance and consistency in date handling. Added Tooltip for displaying formatted timestamps and improved search functionality by translating field labels and values. Cleaned up code for better readability.
* fix: deep link reschedule audit log to booking drawer history tab
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: make booking drawer tab-agnostic for cross-tab deep links
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use status-agnostic /bookings URL for audit log deep links
- Update audit service URLs from /bookings/upcoming?uid=... to /bookings?uid=...
- Add /bookings/page.tsx redirect that routes to /bookings/upcoming preserving query params
- Update tests to expect new URL format
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* refactor: use client-side replaceState instead of server redirect for booking deep links
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use router.replace instead of replaceState to update tab and booking list
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* improvements
* refactor: extract deep link logic from BookingListContainer into usePreSelectedBooking hook
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use proper BookingOutput status type in test helper
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: decouple getTabForBooking from BookingOutput type for simpler testing
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: accept Date | string for endTime in BookingForTabResolution interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: eliminate initialBookingUid prop drilling and revert formatting-only changes
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use AuditDeepLink wrapper to preserve target=_blank through ServerTrans cloneElement
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Improve code organization
* refactor: rename usePreSelectedBooking to useSwitchToCorrectStatusTab
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: push preSelectedBooking into store so drawer opens on direct navigation
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* fixes
* fixes
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: revert assignmentReason breaking change in webhook payloads
Remove the new { category, details } format from EventPayloadType to
maintain backward compatibility for webhook consumers. The new format
is stripped at all webhook payload construction sites by destructuring
assignmentReason out of CalendarEvent before spreading into the payload.
A sanitizeAssignmentReasonForWebhook function provides an additional
safety net in sendPayload itself. Emails and booking single view
continue to use the new format via CalendarEvent.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: strip assignmentReason from handlePaymentSuccess webhook payload
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: strip assignmentReason from triggerWebhooks and handleSeats webhook payloads
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* refactor: use zod safeParse instead of type assertion in sanitizeAssignmentReasonForWebhook
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add configurable landing page feature
- Add useUserPreferences hook for persistent storage of landing page preference
- Add LandingPagePicker component for both iOS and Android/web platforms
- Update tabs index to redirect based on user preference
- Update bookings index to accept initial filter from URL params
- Add App Settings section in More screen with landing page selector
- Clear user preferences on logout for fresh state
* fix(companion): remove try-finally for React Compiler compatibility
* fix(companion): use router.replace for dynamic landing page redirect
Replace Redirect component with router.replace() to fix TypeScript
strict typing issue with expo-router's Href type for dynamic routes.
* fix(companion): use literal route strings for TypeScript strict typing
Use switch statement with literal route strings instead of dynamic
string variable to satisfy expo-router's strict Href type checking.
* working fix
* better css
* refactor: apply biome formatting to small packages + packages/lib
Format packages/sms, packages/prisma, packages/platform/libraries,
packages/platform/examples, packages/platform/types, packages/emails,
and packages/lib.
Excludes packages/platform/examples/base/src/pages/[bookingUid].tsx
due to pre-existing lint errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* revert: remove packages/platform formatting changes
Revert biome formatting for packages/platform as requested.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: enable onboarding-v3 globally and update e2e tests
- Add migration to set onboarding-v3 feature flag enabled=true
- Rewrite onboarding.e2e.ts for v3 flow (Plan Selection → Personal Settings → Calendar)
- Update URL assertions in signup, team-invitation, org-invitation, ab-tests-redirect, and auth tests to accept both old and new onboarding paths
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: use pathname-only matching in waitForURL to prevent false matches on callbackUrl query param
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* use test IDs
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* feat: add signup watchlist review feature flag and handler logic
- Add 'signup-watchlist-review' global feature flag
- Add SIGNUP to WatchlistSource enum in Prisma schema
- When flag enabled, lock new signups and add email to watchlist
- Show 'account under review' message on signup page
- Add i18n strings for review UI
- Create seed migration for the feature flag
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* test: add isAccountUnderReview tests to fetchSignup test suite
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: address Cubic AI review feedback (confidence >= 9/10)
- Remove 'import process from node:process' in signup-view.tsx (P0 bug in 'use client' component)
- Move watchlist review check before checkoutSessionId early return in calcomSignupHandler (P1 premium bypass)
- Revert selfHostedHandler to original state (out of scope per user request)
- Add test mocks for FeaturesRepository and GlobalWatchlistRepository
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: remove node:process import from useFlags.ts (client-side file)
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: remove !token condition from watchlist review check
Token is present in normal email-verified signups, so the !token
condition was incorrectly skipping watchlist review for verified users.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* Apply suggestion from @cubic-dev-ai[bot]
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* refactor: move user lock to UserRepository.lockByEmail
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: use cached getFeatureRepository() instead of deprecated FeaturesRepository
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: remove user locking, keep only watchlist addition on signup review
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: lock user on signup review, remove watchlist entry on unlock
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Pruned the 'License' section and Pushed it before 'Acknowledgement' to avoid confusion and Clarity
* Improves README file by pruning License Section and Aligning Properly
* Remove License section from README.md
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: update data-table hook imports after refactor (#27833)
The data-table hooks (useDataTable, useFilterValue) were moved from
@calcom/features/data-table to ~/data-table/hooks/ in #27833 but this
file was missed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update DataTableProvider import path in insights-wrong-routing-view
Another import missed in #27833 refactor — DataTableProvider moved from
@calcom/features/data-table/ to ~/data-table/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* factory and statergie
* chore: use correct method of DI
* feat: add onchagne
* add logic to HWM stat
* add webhook resolver methods to each statergy
* move seat tracking + webhooks over to own statergy
* Move to factory base approach
* move logic to correct class
* rename create -> createByTeamId
* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED
Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.
Identified by cubic (https://cubic.dev)
Co-Authored-By: unknown <>
* feat: active user billing
* add tests
* UI side for users on active billing
* use correct period of stripe sub
* feat: claude feedback
* fix: skip Stripe sync for canceled/expired subscriptions to prevent repeated API calls
Co-Authored-By: unknown <>
* feat: feedback
* feat: only render when active users mode is set
* fix type error
* fix: constants + feature flags
* fix: default to null in tests
* chore: use seats in test
* fix: use node:crypto protocol for Node.js builtin imports
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Fixes footer text overlap in the Booking Details drawer where action buttons covered the last content items on smaller screens.
**Root cause:** `sm:-my-6` on `SheetFooter` applied both negative top and bottom margins. The negative top margin pulled the footer up into the body content area, causing the overlap.
**Fix:**
- Removed `-mb-4` and `sm:-my-6` from `SheetFooter`, keeping only horizontal negative margins (`-mx-4` / `sm:-mx-6`) for edge-to-edge stretch
- Added `pb-0` to `SheetContent` to remove bottom padding so the footer sits flush at the bottom on all breakpoints
<img width="1098" height="1362" alt="image" src="https://github.com/user-attachments/assets/0418df56-a432-4edf-8e5b-664a5e8c18ac" />
- Fixes #XXXX (GitHub issue number)
- Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)
## Visual Demo (For contributors especially)
#### Image Demo (if applicable):
See screenshot above showing the overlap bug. The footer actions ("..." button) overlap with the last booking history entry.
## Mandatory Tasks (DO NOT REMOVE)
- [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). If N/A, write N/A here and check the checkbox.
- [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Open any booking in the bookings list to open the details drawer
2. Scroll to the bottom of the drawer content
3. Verify the footer actions (buttons, dropdown) do not overlap with the last content item
4. Test on both mobile and desktop viewport sizes — footer should sit flush at the bottom with no extra gap on either
## Human Review Checklist
- [ ] Verify footer appearance on **mobile** (below `sm` breakpoint) — no gap below footer, no overlap with content
- [ ] Verify footer appearance on **desktop** (`sm+` breakpoint) — same checks
- [ ] Confirm the footer still stretches edge-to-edge horizontally on both breakpoints
## Checklist
- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my changes generate no new warnings
- My PR is too large (>500 lines or >10 files) and should be split into smaller PRs
---
Link to Devin run: https://app.devin.ai/sessions/b9d130b8a7a54f039b90b53921b19c78
Requested by: @hariombalhara
* Add DB table for wrong assignment reports
* When report is submitted write to the db
* Prevent duplicate reportings
* test: add migration and tests for WrongAssignmentReport table
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add unique constraint on bookingUid and booking access check for hasWrongAssignmentReport
- Add @unique constraint on bookingUid in WrongAssignmentReport model to prevent duplicate reports at DB level
- Add booking ownership check using BookingAccessService in hasWrongAssignmentReport endpoint
- Refactor hasWrongAssignmentReport into separate handler and schema files
Addresses Cubic AI review feedback on PR #27405
Co-Authored-By: unknown <>
* feat: add routingFormId to WrongAssignmentReport and fix Select clearing
- Add routingFormId field to WrongAssignmentReport model in schema.prisma
- Add relation to App_RoutingForms_Form with SetNull on delete
- Update WrongAssignmentReportRepository.createReport to accept routingFormId
- Update BookingRepository.findByUidIncludeEventTypeAndTeamAndAssignmentReason to include routedFromRoutingFormReponse
- Extract routingFormId from booking in reportWrongAssignment handler
- Fix Select clearing issue: handle null case when user clears team member selection
- Update tests to include routingFormId field
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add migration for routingFormId in WrongAssignmentReport
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add wrong assignment reports dashboard under routing tab
- Add reviewedById and reviewedAt fields to WrongAssignmentReport model
- Add repository methods for listing reports by status and updating status
- Create tRPC endpoints for fetching reports and updating status
- Create dashboard UI with pending/reviewed tabs showing routing form name
- Add translation keys for dashboard UI
- Integrate dashboard into routing insights page
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: move hooks before early return and fix indentation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address Udit's review comments
- hasWrongAssignmentReport: throw UNAUTHORIZED error instead of returning false
- reportWrongAssignment: add try-catch for Prisma P2002 unique constraint error
- WrongAssignmentReport: add Team relation to teamId field
- WrongAssignmentReportRepository: use findUnique instead of findFirst
- reportWrongAssignment: use i18n for error messages
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: revert hasWrongAssignmentReport to return false when user lacks access
Per PR checklist, hasWrongAssignmentReport should return { hasReport: false }
when user lacks access to booking, not throw an error. This allows the UI
to gracefully treat 'no access' as 'no report exists'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: update mocks for findUnique and i18n in unit tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: throw UNAUTHORIZED in hasWrongAssignmentReport and squash migrations
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: move wrong assignment reports to its own tab under Insights
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Use data table for wrong reports
* Add option to view routing trace
* feat: add view routing form submission action to wrong assignment reports
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add RoutingFormResponseSheet component for viewing form submissions
- Create slide-out sheet to display routing form responses
- Map option IDs to display labels for select/multiselect fields
- Handle both legacy and modern option formats
- Add i18n strings: form_submission, no_responses_found
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: improve wrong assignment reports UX
- Integrate RoutingFormResponseSheet as slide-out panel instead of new tab
- Fix dropdown padding by using StartIcon prop instead of manual Icon
- Allow direct status changes for reviewed reports (no need to reopen first)
- Remove unused Icon import
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: change User relation onDelete from Cascade to SetNull in WrongAssignmentReport
Address Hariom's review feedback:
- Changed reportedById from Int to Int? (nullable)
- Changed reportedBy relation from onDelete: Cascade to onDelete: SetNull
- Updated migration SQL to reflect these changes
This preserves wrong assignment reports even when the reporting user is deleted,
as the data is still useful for analysis.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add missing i18n strings for wrong assignment reports dashboard
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: move form response display value resolution server-side
Replace client-side option ID to label resolution in
RoutingFormResponseSheet with a new lean tRPC endpoint
(getFormResponseDisplay) that resolves values server-side using
the existing getHumanReadableFieldResponseValue utility. This
enforces DTO boundaries by returning a clean pre-resolved payload
instead of leaking internal option format details to the client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add report wrong assignment button to routing trace sheet
Wire up the WrongAssignmentDialog from the routing trace sheet header
so users can flag wrong assignments directly while viewing the trace.
The report button is disabled with a tooltip when a report already
exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: reuse existing WrongAssignmentDialog from parent
Replace the duplicate WrongAssignmentDialog in RoutingTraceSheet with
a callback to the existing instance in BookingActionsDropdown. This
reduces the prop surface from a 6-field reportContext object to an
onReport callback and hasExistingReport boolean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use repository instead of direct Prisma in getFormResponseDisplay
Replace direct Prisma query with PrismaRoutingFormResponseRepository's
findByIdIncludeForm method. Extend the method to also select form name,
description, userId, and teamId needed for display and auth checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: replace direct Prisma calls with repository methods
Use MembershipRepository.hasMembership() for auth checks and
TeamRepository.findAllByParentId() for child team queries instead
of direct Prisma calls. Replace direct user query with
UserRepository.getTimeZoneAndDefaultScheduleId(). Remove unused
seed script.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use PBAC services for wrong assignment report auth
Replace manual MembershipRepository.hasMembership() checks with
PBAC-aware permission checking. getWrongAssignmentReports uses
createTeamPbacProcedure middleware since teamId is in input.
updateWrongAssignmentReportStatus uses PermissionCheckService
directly since teamId is discovered from the report entity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve type errors in RoutingFormResponseSheet and wrong-routing view
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add seed script for wrong assignment reports test data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Revert "feat: add seed script for wrong assignment reports test data"
This reverts commit 0bd60e9661858a59aab1573d14d57d81733b7991.
* Only update reviewed fields when not pending
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: wrap handleStatusChange in useCallback to fix useMemo recalculation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Fix routing sheet UI
* fix: use appropriate error message in getFormResponseDisplay handler
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract WrongAssignmentReportService from tRPC handlers
Move business logic (booking lookup, duplicate check, report creation,
webhook dispatch, org-level team resolution) into a dedicated service
in packages/features. Handlers become thin controllers that only handle
auth checks and delegate to the service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restrict report status updates to admin/owner roles
Remove MembershipRole.MEMBER from fallbackRoles in
updateWrongAssignmentReportStatus permission check. Updating report
status is an administrative action that should be limited to team
admins and owners.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: localize hard-coded success message in WrongAssignmentReportService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: rename reviewed tab to handled and add missing translations
Rename the "Reviewed" tab to "Handled" since it groups three distinct
statuses (Reviewed, Resolved, Dismissed). Also add missing translation
keys for "resolved" and "dismissed" status badges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* merge: resolve conflicts with main branch
Merge main into devin/1769747741-wrong-assignment-dashboard, resolving conflicts in:
- BookingActionsDropdown.tsx: use main's booking prop with PR's fragment structure
- reportWrongAssignment.handler.ts: keep PR's service-based approach
- reportWrongAssignment.handler.test.ts: use main's class-based mocks with PR's additions
- WrongAssignmentReportService.ts: align with main's repo method and field names
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add guard when accessing assignmentReasonSortedByCreatedAt
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: address Cubic AI review feedback - select projection and useLocale refactor
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove stale TRPCError assertion in test (service throws ErrorWithCode)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: remove unused findByTeamIdAndStatus and findByTeamIdAndStatuses methods
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* perf: add composite index on WrongAssignmentReport and narrow findByIdIncludeForm select
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* perf: use lightweight findTeamIdById in update-status handler instead of findById
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: hariom@cal.com <hariombalhara@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: alex@cal.com <me@alexvanandel.com>
## What does this PR do?
Fixes an issue where underscores in form field identifiers were being stripped when converting to workflow template variables. According to the documentation, users should be able to use variables like `{COMPANY_NAME}` or `{MONTHLY_INFRASTRUCTURE_SPEND}`, but the regex in `formatIdentifierToVariable` was removing underscores, causing these variables to not match.
**The problem:**
- Form field identifier: `Company_Name` or `Monthly_Infrastructure_Spend`
- Expected variable: `{COMPANY_NAME}` or `{MONTHLY_INFRASTRUCTURE_SPEND}`
- Actual variable (before fix): `{COMPANYNAME}` or `{MONTHLYINFRASTRUCTURESPEND}`
**The fix:**
- Updated the regex from `[^a-zA-Z0-9 ]` to `[^a-zA-Z0-9_ ]` to preserve underscores in `formatIdentifierToVariable`
- Added a private `formatIdentifierToVariableLegacy` function that maintains the old behavior (strips underscores)
- Added `getVariableFormats` helper that returns both current and legacy formats for backward compatibility
- Added `convertResponsesToVariableFormats` helper in `executeAIPhoneCall.ts` to convert form responses to both variable formats
- Updated `customTemplate.ts` matching logic to support both variable formats
- Updated `executeAIPhoneCall.ts` to include both variable formats when building dynamic variables
This ensures existing templates using `{COMPANYNAME}` continue to work while new templates can use the documented `{COMPANY_NAME}` format.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - no docs changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Create a routing form with a field identifier containing underscores (e.g., `Company_Name`)
2. Create a workflow with a template using `{COMPANY_NAME}` (with underscore)
3. Submit the form and verify the variable is replaced correctly
4. Also test with `{COMPANYNAME}` (without underscore) to verify backward compatibility
**Automated tests added:**
- `customTemplate.test.ts` - 15 tests covering `formatIdentifierToVariable`, `getVariableFormats`, and template variable replacement
- `executeAIPhoneCall.test.ts` - 6 tests covering `convertResponsesToVariableFormats` function for form responses
## Human Review Checklist
- [ ] Verify the regex change `[^a-zA-Z0-9_ ]` correctly preserves underscores
- [ ] Verify backward compatibility: both `{COMPANY_NAME}` and `{COMPANYNAME}` should work for the same form field
- [ ] Verify `convertResponsesToVariableFormats` correctly generates both variable formats
- [ ] Review test coverage for edge cases (empty responses, undefined values)
---
Link to Devin run: https://app.devin.ai/sessions/9d5d90178714493086d94691865c3e07
Requested by: @hariombalhara
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/calcom/cal.com/pull/27571">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin">
</picture>
</a>
<!-- devin-review-badge-end -->
* feat: re-render bookings page after feature opt-in without full refresh
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: make onOptInSuccess a required prop
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: maintain uid parameter in BookingDetailsSheet URL
Move the isSyncedFromUrlToStoreRef guard from the effect level to inside
the Zustand subscription callback. This ensures the Store→URL subscription
is always created while still preventing premature URL overwrites during
URL→Store sync.
Also adds an E2E test verifying the uid parameter appears in the URL
after clicking a booking item in the v3 bookings view.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Update apps/web/playwright/bookings-list.e2e.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: clean up bookings-v3 feature flag after E2E test
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* refactor: apply biome formatting to packages/features (batch 1 - small subdirs)
Format small subdirectories in packages/features: di, flags, holidays, oauth,
settings, users, assignment-reason, selectedCalendar, hashedLink, host, form,
form-builder, availability, data-table, pbac, schedules, troubleshooter,
eventtypes, calendar-subscription, and root-level files.
Also includes straggler apps/web BookEventForm.tsx.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 2 - medium subdirs)
Format medium subdirectories in packages/features: auth, credentials,
calendars, routing-forms, routing-trace, attributes, watchlist, calAIPhone,
tasker, and webhooks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 3 - bookings + insights)
Format bookings and insights subdirectories in packages/features.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 4 - ee)
Format packages/features/ee subdirectory covering billing, workflows,
organizations, teams, managed-event-types, round-robin, dsync,
integration-attribute-sync, and payments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 5 - booking-audit part 1)
Format booking-audit di, actions, common, dto, repository, and types
subdirectories in packages/features/booking-audit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 6 - booking-audit part 2)
Format booking-audit service subdirectory in packages/features/booking-audit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* perf: use UNION ALL with DISTINCT for bookings query optimization
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add unionAll and distinct to kysely test mocks
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add integration tests for getBookings query
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add totalCount deduplication test for multi-branch union scenario
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: fix totalCount assertion to be exact instead of gte
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Apply suggestion from @keithwillcode
* perf: restore COUNT(DISTINCT id) for totalCount query
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add tests: packages/trpc/server/routers/viewer/bookings/get.handler.integration-test.ts
Generated by Paragon from proposal for PR #27841
* Add tests: packages/trpc/server/routers/viewer/bookings/get.handler.test.ts
Generated by Paragon from proposal for PR #27841
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add no users found fallback action for routing forms
- Add NoUsersFoundFallbackActionType enum with CustomPageMessage and ExternalRedirectUrl options
- Add noUsersFoundFallbackAction field to zodNonRouterRoute schema
- Add NoUsersFoundFallback UI component in RouteBuilder for configuring fallback action
- Update handleResponse to return noUsersFoundFallbackAction when no team members match
- Add i18n translation keys for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: change fallback to use same action options as main route
- Replace noUsersFoundFallbackAction with fallbackAction that has same structure as main action
- Add action dropdown and input fields to fallback attributes query builder section
- Support Event redirect, External URL, and Custom page options in fallback
- Remove separate NoUsersFoundFallback toggle component
- Update handleResponse to return fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure fallbackAction type is always defined in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add guards for route.fallbackAction in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only return fallbackAction when no users are found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: restructure fallback section to mirror main route structure
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure backwards compatibility for existing routes with fallbackAttributesQueryValue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction with main event type for backwards compatibility
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include fallbackAction in getRoutesToSave to persist changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use fallbackAction in getRoutedUrl when no team members found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use i18n for fallback label and add tests for fallback action
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: do not trigger fallback action when CRM contact owner is found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: prioritize fallbackAction over fallbackAttributesQueryValue
When a route has a fallbackAction configured, skip the
fallbackAttributesQueryValue to ensure the fallbackAction is triggered
when no team members are found. This maintains backwards compatibility
by only using fallbackAttributesQueryValue when fallbackAction is not set.
Also treats teamMemberIdsMatchingAttributeLogic being null (routing couldn't
run, e.g., missing orgId) the same as an empty array for the purpose of
triggering the fallbackAction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract RouteActionSelector shared component for action selection UI
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add i18n string for default custom page message
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* revert: remove webhook trigger changes (to be moved to separate PR)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback query builder when fallbackAction is explicitly set
The fallback attributes query builder was being shown for new routes
when fallbackAction was not set, causing an extra 'Add rule' button
to appear. This broke e2e tests that relied on the button index.
Now the fallback query builder is only shown when fallbackAction is
explicitly set to EventTypeRedirectUrl, which happens after the route
is saved and reloaded (via auto-initialization in useRoutes).
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use data-testid for attribute query builder selectors in e2e tests
- Add data-testid='attributes-query-builder' to the main attributes query builder div
- Update addAttributeRoutingRule and addAttributeRoutingRuleWithOperator to use the data-testid selector instead of nth() indices
- This makes the tests more robust and independent of DOM structure changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction for new routes in useCreateRoute hook
- New routes created via useCreateRoute now initialize fallbackAction
- This ensures new routes have a valid fallbackAction when saved
- Matches the initialization logic for existing routes in useRoutes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction at save time when action is EventTypeRedirectUrl
- When saving routes, if fallbackAction is undefined but action is EventTypeRedirectUrl,
initialize fallbackAction from the main action
- This handles the case where user creates a new route and changes action to an event type
- Fixes e2e test failures where form save was failing due to undefined fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction when main action changes to EventTypeRedirectUrl
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback section when main action has valid event type selected
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: check for valid fallbackAction event type before preserving existing value
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Give more structure to main and fallback routes
* Update comment
* Rename fallbackAttributesQueryBuilder to matchingMembersFallbackRoute
* Display custom event redirect input
* fix: address Cubic AI review feedback for routing form fallback
- Simplify getFallbackAction logic to use checkedFallback flag instead of
manually parsing attributesQueryValue (confidence 9/10)
- Add i18n translation for 'And connect with specific team members' string
using t('connect_with_specific_team_members') (confidence 9/10)
Co-Authored-By: unknown <>
* fix: update getFallbackAction to use checkedFallback and handle missing orgId case
- Use checkedFallback flag as primary indicator for triggering fallback action
- Also handle edge case where attribute routing was configured but couldn't run
due to missing orgId (teamMemberIdsMatchingAttributeLogic is null)
- Update test to mock checkedFallback: true when expecting fallback action
Co-Authored-By: unknown <>
* Address UI comment
* fix: Use fallbackAttributesQueryValue when fallback action is event redirect
Only use fallbackAttributesQueryValue when:
1. fallbackAction is EventTypeRedirectUrl (attribute routing applies)
2. fallbackAction is not set (backwards compatibility)
Skip it for CustomPageMessage and ExternalRedirectUrl fallback actions
since attribute routing doesn't apply to those action types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: correct JSX syntax error in matchingMembersFallbackRoute
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract getCustomSlug helper to deduplicate custom event type slug logic
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback attribute filtering for teams under orgs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: show fallback action for all team forms, gate attribute filtering on isOrganization
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
The Date.now() method returns time in epoch miliseconds, adding 60 * 60 = 3.6seconds leads to a bug that results in expiring the access token in 3.6 seconds, although it been valid. It creates unnecessary api calls to the zoho servers before most the api calls.
* feat: add UTM parameter data to insights bookings CSV download
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* test: add UTM data tests and update snapshots for csvDataTransformer
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* fix: use Tracking relation for UTM data instead of flat fields on Booking
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When disableCancelling or disableRescheduling is enabled on an event type,
the confirmation email still showed non-functional cancel/reschedule links.
The ManageLink email component already had correct conditional logic to
hide these links, but two flows constructed the CalendarEvent manually
without including the flags: the booking confirmation handler and the
payment booking flow. This caused the flags to be undefined, which
ManageLink interpreted as enabled.
Added disableCancelling and disableRescheduling to the Prisma select and
CalendarEvent object in both confirm.handler.ts and getBooking.ts.
Closes#22906
* factory and statergie
* chore: use correct method of DI
* feat: add onchagne
* add logic to HWM stat
* add webhook resolver methods to each statergy
* move seat tracking + webhooks over to own statergy
* Move to factory base approach
* move logic to correct class
* rename create -> createByTeamId
* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED
Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.
Identified by cubic (https://cubic.dev)
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize getBusyTimesForLimitChecks with batched parallel queries and raw SQL
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* test: add unit tests for getBusyTimesForLimitChecks batched query optimization
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* refactor: use Prisma findMany instead of raw SQL for better testability
The batching (50 user chunks) and parallel execution (Promise.all) provide
the main performance optimization. Using findMany instead of $queryRaw
maintains the same performance characteristics while being compatible
with prismock for testing.
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: type error
* refactor: add integration test
* chore: remove log
* chore: remove log
* fix: avoid logging PII (userId) in busyTimes debug log
Replace JSON.stringify(busyTimes) with count to avoid exposing userId
in logs. Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* chore: undo
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update booking page terms text to use APP_NAME instead of 'our'
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* remove changes for platform booker
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* inviteMemberUtils in featuers
* update imports
* wip
* wip
* fix: remove PII from log statements in inviteMemberUtils
- Remove email (usernameOrEmail) from error log in sendSignupToOrganizationEmail
- Replace full user object with userId in debug log in sendExistingUserTeamInviteEmails
Co-Authored-By: unknown <>
* revert: restore original code for pure relocation (no formatting/logic changes)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* fix: update test mocks to use new import path for inviteMemberUtils
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert: restore original formatting in proration test and handleGroupEvents
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add DB table for wrong assignment reports
* When report is submitted write to the db
* Prevent duplicate reportings
* test: add migration and tests for WrongAssignmentReport table
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add unique constraint on bookingUid and booking access check for hasWrongAssignmentReport
- Add @unique constraint on bookingUid in WrongAssignmentReport model to prevent duplicate reports at DB level
- Add booking ownership check using BookingAccessService in hasWrongAssignmentReport endpoint
- Refactor hasWrongAssignmentReport into separate handler and schema files
Addresses Cubic AI review feedback on PR #27405
Co-Authored-By: unknown <>
* feat: add routingFormId to WrongAssignmentReport and fix Select clearing
- Add routingFormId field to WrongAssignmentReport model in schema.prisma
- Add relation to App_RoutingForms_Form with SetNull on delete
- Update WrongAssignmentReportRepository.createReport to accept routingFormId
- Update BookingRepository.findByUidIncludeEventTypeAndTeamAndAssignmentReason to include routedFromRoutingFormReponse
- Extract routingFormId from booking in reportWrongAssignment handler
- Fix Select clearing issue: handle null case when user clears team member selection
- Update tests to include routingFormId field
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add migration for routingFormId in WrongAssignmentReport
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address Udit's review comments
- hasWrongAssignmentReport: throw UNAUTHORIZED error instead of returning false
- reportWrongAssignment: add try-catch for Prisma P2002 unique constraint error
- WrongAssignmentReport: add Team relation to teamId field
- WrongAssignmentReportRepository: use findUnique instead of findFirst
- reportWrongAssignment: use i18n for error messages
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: revert hasWrongAssignmentReport to return false when user lacks access
Per PR checklist, hasWrongAssignmentReport should return { hasReport: false }
when user lacks access to booking, not throw an error. This allows the UI
to gracefully treat 'no access' as 'no report exists'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: update mocks for findUnique and i18n in unit tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: throw UNAUTHORIZED in hasWrongAssignmentReport and squash migrations
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: change User relation onDelete from Cascade to SetNull in WrongAssignmentReport
Address Hariom's review feedback:
- Changed reportedById from Int to Int? (nullable)
- Changed reportedBy relation from onDelete: Cascade to onDelete: SetNull
- Updated migration SQL to reflect these changes
This preserves wrong assignment reports even when the reporting user is deleted,
as the data is still useful for analysis.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: hide feature opt-in banner and feedback forms during impersonation
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: keep opt-in banner visible during impersonation, only hide feedback forms
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: allow feedback form during impersonation in development mode
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add activeFilters validator to DataTableProvider for filter segment validation
- Add validateActiveFilters prop to DataTableProvider to filter out inaccessible values
- Create useActiveFiltersValidator hook in bookings module to validate filter values
- Integrate validator in bookings-view.tsx to validate userId, eventTypeId, teamId filters
- Add comprehensive tests for the filter validation logic
This provides a scalable solution for handling stale filter segment data by validating
filters at the caller level using already-fetched accessible resource IDs.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add type guard for eventTypeIds to ensure number[] type
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: delay segment filter application until validator is ready
- Change useActiveFiltersValidator to return 'loading' state while data is fetching
- Add pending segment handling in DataTableProvider to delay filter application
- Expose isValidatorPending in context for consumers to disable queries
- Update BookingListContainer to disable bookings query while validator is pending
This prevents both errors from invalid filters and flash from fetching without filters.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: extract useSegmentManagement hook from DataTableProvider
- Extract segment management logic into dedicated useSegmentManagement hook
- Reduces DataTableProvider from ~500 lines to ~330 lines
- Use NuqsSetter type to match nuqs useQueryState setter signatures
- Maintains all existing functionality and type safety
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: export ActiveFiltersValidator types from data-table lib/types
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: import ActiveFiltersValidatorState from lib/types instead of hook
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Revert "refactor: extract useSegmentManagement hook from DataTableProvider"
This reverts commit dd588ac4cfb1d709c71d1f621994a7e8f3118f33.
* refactor: split DataTableProvider into fine-grained context providers
- Create DataTableStateContext for raw nuqs state management
- Create DataTableSegmentContext for segment management
- Create DataTableFiltersContext for filter manipulation
- DataTableProvider now composes all providers with a bridge component
- Maintains backward compatibility via combined DataTableContext
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: apply segment filters when navigating back to page with existing URL state
When navigating back to a page with a segment already selected in the URL,
the segment was being marked as selected but its filters were not being
applied. This caused non-deterministic behavior where the filter segment
would sometimes not take effect.
The fix ensures that when segmentIdRaw exists in the URL and segments are
fetched, we also call applySegmentFilters() (with validator timing support)
instead of just setting the selectedSegment state.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: simplify filter validator to only handle multi-select
These filters are always multi-select, so remove unnecessary
single-select handling code and tests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: cancel debounced search term on unmount
Cancel the debounced search term on unmount/dependency change to avoid
queued updates firing after the provider unmounts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Users with personal emails (e.g. Gmail) could enter the org creation flow
at /settings/organizations/new but only hit the company email error at
checkout, with no way to fix it from the form. After changing their email
in settings, the cached store email was still used.
- Add server-side isCompanyEmail check in page.tsx to redirect early
- Add client-side check in CreateANewOrganizationForm with clear messaging
- Always use current session email for non-admin users (not cached store)
- Add tests for isCompanyEmail utility
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: add composite index on (formId, createdAt) for App_RoutingForms_FormResponse
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: add migration for formId, createdAt composite index
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Update migration.sql
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: migrate IP-based rate limiting from Unkey to Cloudflare
Remove Unkey rate limiting for IP-based endpoints that will now be
handled by Cloudflare Enterprise Advanced Rate Limiting:
- Regular booking creation (createBooking:{hashedIP})
- Recurring booking creation (createRecurringBooking:{hashedIP})
- Instant meeting creation (instant.event-{hashedIP})
- Booking cancellation for unauthenticated users (api:cancel-ip:{hashedIP})
- Forgot password (forgotPassword:{hashedIP})
- Reset password (api:reset-password:{hashedIP})
- Signup (api:signup:{hashedIP})
- API v1 requests ({userId} with auto-lock)
- Global proxy rate limiting (common namespace)
Rate limiting that remains in Unkey (user/entity-based):
- Login (hashedEmail)
- Booking cancellation for authenticated users (api:cancel-user:{userId})
- 2FA setup/enable/disable (api:totp-*:{userId})
- SMS sending (team/org/user based)
- Email verification (various patterns)
- Team member operations (userId based)
- Routing forms (formId:responseHash)
- AI phone calls (userId based)
Also includes Cloudflare configuration proposal document with
recommended rules using JA4 fingerprinting for enhanced protection.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove cloudflare proposal doc from PR
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove cloudflare comments and keep common rate limiting type
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: revert global middleware from PR #25080 and restore core rate limits
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: restore instantMeeting rate limiting
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: restore email fallback in forgot-password rate limiting
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Remove the static file check
* refactor: add POST_METHODS_ALLOWED_API_ROUTES to proxy matcher
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore API v1 rate limiting to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore reset-password, cancel, book/event, book/recurring-event to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: use POST_METHODS_ALLOWED_API_ROUTES spread in matcher instead of hardcoded routes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore signup route to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: update proxy matcher tests for POST_METHODS_ALLOWED_API_ROUTES spread
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Rename var
* refactor: revert matcher to static strings, add sync-check test for POST_METHODS_ALLOWED_API_ROUTES
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* removed dead routing forms rewrite code
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: remove IS_PRODUCTION gate from BookingAuditProducer
Remove the IS_PRODUCTION check that was preventing booking audits from
being queued in production. Audits are still properly gated by:
1. Organization check: Audits are skipped for non-organization bookings
(organizationId === null)
2. Feature flag: The BookingAuditTaskConsumer checks if the 'booking-audit'
feature is enabled for the organization via featuresRepository.checkIfTeamHasFeature()
The IS_PRODUCTION gate was intentionally added to prevent logs from being
created in production while the action data versioning was being actively
reviewed and finalized. Without proper versioning handling, the Booking
History UI could crash when encountering unversioned data. Now that the
versioning system is in place, this gate can be safely removed.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert formatting, keep only IS_PRODUCTION removal
Reverts the unintended formatting changes from the previous commit.
Only removes the IS_PRODUCTION gate without changing indentation.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add booking-audit feature flag check in producer to avoid unnecessary task creation
- Query booking-audit and booking-email-sms-tasker flags in parallel before fireBookingEvents
- Pass isBookingAuditEnabled through BookingEventHandler to producer's queueTask method
- Add conditional check in queueTask with debug log when skipping audit
- Reuse pre-queried isBookingEmailSmsTaskerEnabled flag instead of querying again
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make isBookingAuditEnabled required and pass it through all booking audit flows
- Make isBookingAuditEnabled a required property in BookingAuditProducerService interface
- Update all BookingEventHandler methods to require isBookingAuditEnabled
- Add feature flag check in all flows that call booking audit:
- handleSeats (seat booking/rescheduling)
- RecurringBookingService (bulk bookings)
- handleCancelBooking (booking cancellation)
- handleConfirmation (booking acceptance)
- roundRobinReassignment (automatic reassignment)
- roundRobinManualReassignment (manual reassignment)
- trpc handlers: addGuests, confirm, editLocation, requestReschedule
- Skip queueing audit tasks when feature is disabled with debug logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add featuresRepository dependency to RecurringBookingService DI module
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: make isBookingAuditEnabled optional for non-main flows
Keep feature flag check only in main flows (handleSeats, RegularBookingService, RecurringBookingService) which are frequently triggered. For other flows (handleCancelBooking, handleConfirmation, roundRobinReassignment, etc.), rely on the existing consumer-level check.
Changes:
- Revert feature flag check from non-main flows
- Make isBookingAuditEnabled optional in interface for non-main flow methods
- Keep isBookingAuditEnabled required for main flow methods (queueCreatedAudit, queueRescheduledAudit, queueSeatBookedAudit, queueSeatRescheduledAudit, queueBulkCreatedAudit, queueBulkRescheduledAudit)
- Update BookingEventHandlerService to use required params for main flows and optional for non-main flows
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: remove isBookingAuditEnabled from non-main flow methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make isBookingAuditEnabled required in all BookingEventHandlerService methods
- Add isBookingAuditEnabled as required parameter in all BookingAuditProducerService interface methods
- Update BookingAuditTaskerProducerService to use simplified check (!params.isBookingAuditEnabled)
- Update BookingEventHandlerService to require isBookingAuditEnabled in all methods
- Update all callers to query booking-audit feature flag and pass isBookingAuditEnabled:
- handleCancelBooking
- handleConfirmation
- roundRobinReassignment
- roundRobinManualReassignment
- addGuests.handler
- confirm.handler
- editLocation.handler
- requestReschedule.handler
- Inject featuresRepository in API V2's booking-location.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: update roundRobinReassignment tests to include isBookingAuditEnabled
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: update roundRobinManualReassignment tests to include isBookingAuditEnabled
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: inject featuresRepository in API V2 RecurringBookingService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: import PrismaWorkerModule for PrismaFeaturesRepository dependency
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use booking's organization for feature flag check in addGuests handler
Use booking.user?.profiles?.[0]?.organizationId instead of user.organizationId
to check the booking-audit feature flag. This ensures the feature flag is
checked against the booking's organization rather than the actor's organization,
which is consistent with other handlers in this PR.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* Add comment
* fix: use user.organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use booking's organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert to user.organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add isBookingAuditEnabled to onNoShowUpdated calls after main merge
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add isBookingAuditEnabled to onNoShowUpdated calls and update tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: prevent 500 error when deleting calendar events with empty uid
This fix addresses a 500 error that occurs when trying to delete a Google
Calendar event with an empty event ID, which results in a malformed API URL.
Root cause: When calendar event creation fails or returns without an ID,
booking references were being created with empty uids. Later, when trying
to delete these events (e.g., during reschedule), the empty uid caused
a 404 from Google Calendar API which was then thrown as a 500 error.
Changes:
1. CalendarManager.deleteEvent: Added validation to skip deletion if
bookingRefUid is empty, with appropriate error logging. This is a
safety net for existing bad data in the database.
2. EventManager.create: Added filtering to exclude booking references
with empty uids from being stored, with error logging to help
diagnose the root cause of missing event IDs.
3. EventManager.updateLocation: Same filtering applied for consistency.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: revert EventManager filtering to preserve existing behavior
The test expects that booking references with empty uids are still created
when calendar event creation fails. This is intentional behavior to track
failed calendar syncs. The CalendarManager.deleteEvent fix handles the
deletion case gracefully by skipping the API call when uid is empty.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: throw ErrorWithCode instead of silently returning when bookingRefUid is empty
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: validate uid before calling calendar.deleteEvent to prevent 500 error
- Add uid check in lastAttendeeDeleteBooking.ts before calling calendar.deleteEvent
- Add uid check in EventManager.updateAllCalendarEvents before calling calendar.deleteEvent
- Revert CalendarManager.deleteEvent changes (validation at call sites is the proper fix)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: add error logging safeguard in CalendarManager.deleteEvent for empty bookingRefUid
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: move bookingRefUid check before getCalendar call
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: prevent storing booking references with empty uid when calendar event creation fails
Root cause fix: Change the check from 'if (createdEvent)' to 'if (createdEvent.createdEvent)'
in createAllCalendarEvents to prevent failed calendar events from being added to results.
This prevents empty uids from being stored in the database in the first place, which
was causing 500 errors when trying to delete non-existent calendar events later.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* test: update test to expect no calendar reference when calendar event creation fails
The root cause fix changes behavior so that failed calendar events no longer
create booking references with empty uids. This test now expects an empty
references array when calendar event creation fails, which is the correct
behavior that prevents 500 errors later when trying to delete non-existent
calendar events.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: use createdEvent.success instead of createdEvent.createdEvent
Using success is more semantically clear since it explicitly checks the
success status rather than checking if the nested object exists.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: use optional chaining for createdEvent?.success
Using optional chaining is safer in case createdEvent is somehow undefined.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert: remove root cause fix to preserve backfilling script compatibility
The root cause fix (checking createdEvent?.success instead of createdEvent)
would break the backfilling script that relies on booking references with
empty uids to identify which bookings need backfilling.
This PR now only includes defensive checks to prevent 500 errors when
deleting calendar events with empty uids.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: move repositories from lib to features domain folders
- Move HolidayRepository to features/holidays/repositories
- Move PrismaTrackingRepository to features/bookings/repositories
- Move PrismaBookingPaymentRepository to features/bookings/repositories
- Move PrismaRoutingFormResponseRepository to features/routing-forms/repositories
- Move PrismaAssignmentReasonRepository to features/assignment-reason/repositories
- Move VerificationTokenRepository to features/auth/repositories
- Move WorkspacePlatformRepository to features/workspace-platform/repositories
- Move DTO files to their respective feature domains
- Merge lib DestinationCalendarRepository into features version
- Merge lib SelectedCalendarRepository into features version
- Update all import paths across the codebase
This follows the vertical slice architecture pattern by organizing
repositories by domain rather than by technical layer.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: update VerificationTokenService import path to new location
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: update test file imports to use new repository locations
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* mv
* fix structure
* fix
* refactor: merge unit tests for SelectedCalendarRepository into single file
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: disable save button when workflow has no changes
* test: wait for save button to be enabled before clicking
* test: ensure input value is set before pressing Enter
* test fix
* fix: add shouldDirty to enable save button on name change
* test: clean up workflow test fixtures
* fix: disable save button when workflow has no changes
* test: update workflow E2E tests
* test: fix workflow E2E
* fix: enable save button when workflow form fields change
* fix tests
* fix: disable save button when workflow has no changes
* remove comments
* chore: improve fixture logic
* cleanup: remove unnecessary changes
* update workflow fixture to handle team workflow creation
---------
Co-authored-by: Pallav <90088723+Pallava-Joshi@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
Co-authored-by: Romit <romitgabani@icloud.com>
Co-authored-by: Romit <romitgabani1.work@gmail.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
* test: add E2E tests for assignAllTeamMembers with attribute segment filters
Cover all 4 attribute types (SINGLE_SELECT, MULTI_SELECT, TEXT, NUMBER)
with booking flow verification plus edge cases (no match, all match).
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use auto-retrying assertion for no-match edge case test
Replace flaky waitForTimeout(2000) + count check with Playwright's
toHaveCount(0, { timeout: 15000 }) which retries until availability
settles, preventing CI timing issues.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add exclusion tests for members without attribute assigned
Add 4 new E2E tests covering the case where team members have no
attribute assigned at all (not just a different value). Each attribute
type (SINGLE_SELECT, MULTI_SELECT, TEXT, NUMBER) is tested to verify
that members without the attribute are excluded from the booking pool.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add negation operator tests and restructure exclusion tests
- Restructure 'Members without attribute assigned' tests: assign
matching attribute to teammate-1 (not owner) so round-robin can't
coincidentally pass when the bug exists
- Add 4 negation operator tests (select_not_any_in, multiselect_not_some_in,
not_equal for TEXT and NUMBER) that verify unassigned members should
match negation filters. Marked with test.fail() as they confirm a
known bug where getLogicResultForAllMembers skips members without
attribute assignments entirely.
- Add edge case: no members have attribute assigned -> no available slots
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: remove test.fail() markers from negation tests after bug fix
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add comprehensive E2E tests for routing form attribute conditions
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: assert routedTeamMemberIds in URL instead of email in results
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: assert both routedTeamMemberIds in URL and email in results
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include users without attribute assignments in exclusion condition evaluation
Users with no attribute assigned were excluded from the team members list
entirely, causing exclusion operators (not_equals, not_some_in, not_like,
not_between, etc.) to never match them. Now getAttributesAssignmentData
includes all team members, even those without attribute assignments.
Also adds unit tests and E2E tests for this scenario.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: update getAttributes test to reflect new behavior for empty attributes
Users without attribute assignments are now included with empty attributes
so exclusion operators can match them. Updated test expectation accordingly.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: remove redundant usersWithAssignments code (fix already in _prepareAssignmentData from main)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: defer email validation to after first blur on signup form
Change react-hook-form validation mode from "onChange" to "onTouched"
so that the "Invalid email" error only appears after the user has
interacted with the field and moved away, not on every keystroke.
Password strength hints still update in real-time since onTouched
validates on change after the field has been touched (blurred once).
Fixes#19163
* test: add unit tests for signup email validation mode
Verify the onTouched form validation behavior:
- No error shown while user is typing
- Error appears only after blur with invalid email
- No error for valid email after blur
- Revalidation on each keystroke after first blur
* remove eslint comment
---------
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Add aria-label to the TimeFormatToggle and LayoutToggle components
on the booking page so screen readers can identify these radio groups.
Uses existing translation keys "time_format" and "layout".
Fixes#20703
- Remove unused attendeeIds field and unnecessary prisma.booking.findUnique from beforeEach
- Store booking.id in testData to avoid re-querying booking in test body
- Track additional attendee emails via additionalAttendeeEmails array for proper afterEach cleanup
- Remove fragile prisma.attendee.delete (throws P2025 on missing record) from test body
- Use deleteMany-based cleanupTestData for second attendee cleanup instead
The test was flaky because:
1. It used prisma.attendee.delete (singular) which throws if record is already gone
2. It re-queried the booking via findUnique mid-test which could fail if cascade-deleted
3. The second attendee email was not tracked in afterEach cleanup
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: prevent textarea resize overlapping buttons in rejection dialog
The rejection reason textarea in the booking rejection dialog allows
manual resizing via the drag handle, which can cause it to expand over
the dialog footer buttons. Adding resize-none prevents this overlap.
Fixes#17536
* Update RejectionReasonDialog.tsx
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
* fix(booking): make provider label and location link visible on mobile
* correct order
* remove comments
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
* fix: resolve organizer default conferencing app credential for location update
- Fix Google Meet credential lookup in EventManager to properly map 'google:meet' to 'google_video' type
- Add automatic credential ID resolution when selecting 'Organizer Default App' as location
- Fixes 'Location update failed' error when editing round-robin team event location to organizer default app (Google Meet) with multiple Google Calendar connections
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix update
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Fix exclusion filter - include all team members
* Fix display when members aren't saved in the DB
* Update tests
* Use integration tests for findTeamMembersMatchingAttributeLogic for better behaviour testing
* Add test that verifies that it was working earlier and broken in b/w and not working again
* Revert changes to AddMembersWithSwitch.tsx
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(ui): change phone booking display from Organizer Phone Number to Phone Call
The label \"Organizer Phone Number\" was confusing for attendees on the
booking page, as it exposed internal terminology. Replaced with the
more user-friendly \"Phone Call\" label across the booking UI, companion
app, and test assertions.
Closes#13010
* fix: remove duplicate phone_call i18n key
The phone_call key already existed in en/common.json (line 1685,
Cal.ai Voice Agent section). Removed the duplicate entry we added.
Found by ai-codex review.
* fix: use LOGO constant for generic OG image instead of hardcoded value
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* test: add unit tests for OgImages module
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: remove unused LOGO import from OgImages test
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* test: update OgImages tests to reflect LOGO_DARK constant
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* add comment
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Revert "Revert "feat: redesign team creation flow to match onboarding-v3 design (#26733)""
This re-applies the team creation redesign from PR #26733 which was
previously reverted. Cherry-picked from 2540423ba3.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: check org/username slug when in org context
* fix: use expect(page).toHaveURL() instead of page.waitForURL() in team e2e test
Co-Authored-By: unknown <>
* fix: redirect URL
* fix: flag import
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add upcoming bookings widget for iOS and Android
- Add iOS widget using @bacons/apple-targets with SwiftUI
- Add Android widget using react-native-android-widget
- Configure App Groups for iOS data sharing between app and widget
- Create widget sync hook to update widget data when bookings change
- Widget displays up to 5 upcoming bookings with title, time, and attendee
- Widget refreshes every 15 minutes and when app goes to background
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): refactor useWidgetSync to avoid React Compiler limitation
Move conditional logic outside of try/catch block to fix React Compiler
error: 'Support value blocks (conditional, logical, optional chaining,
etc) within a try/catch statement'
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): fix widget data sync issues
- Fix Swift widget to read data as JSON string (format used by react-native-shared-group-preferences)
- Add fallback to read as raw Data for compatibility
- Fix query key mismatch by checking multiple common filter combinations
- Ensure widget finds cached bookings regardless of which filter the app used
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): use ExtensionStorage from @bacons/apple-targets for widget data
- Replace react-native-shared-group-preferences with ExtensionStorage
- ExtensionStorage properly stores data in iOS App Groups
- Add ExtensionStorage.reloadWidget() call to trigger widget refresh
- This ensures the widget picks up new data immediately
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat(companion): add upcoming bookings widget for ios - all shapes (#27564)
* ios working widget
* fix(companion): remove sensitive data logging from widget storage
Remove console.log statements that were logging full widgetData payload
and formatted widget bookings containing attendee names and booking
details. This addresses security concerns identified by Cubic AI review
(confidence 9/10).
Co-Authored-By: unknown <>
* dark mode
* new look
* fix deeplink
* fix biome lint
* fix(companion): remove booking titles from console logs to avoid PII exposure
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* lock the versions and dev mode logging
* remove dynamic imports
* feat(companion): upcoming bookings widget android (#27689)
* companion-upcoming-bookings-widget-android
* fix(companion): remove stale relative countdowns and fix past-time clamping in Android widget
- Remove relative countdown text (In 15m, In 1h, Now) that becomes stale
due to Android widget 30+ min update interval (Cubic violation 2)
- Remove Math.max(0, minutes) clamping that caused ended meetings to
display as 'Now' indefinitely (Cubic violation 1)
- Simplify accent color logic to use startTimeISO directly
- Keep absolute time display (date + startTime) which remains accurate
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
* fix: event not created
* Update CalendarManager.ts
* Add tests: packages/features/calendars/lib/CalendarManager.ts
Generated by Paragon from proposal for PR #27675
* Add tests: packages/features/calendars/lib/CalendarManager.test.ts
Generated by Paragon from proposal for PR #27675
* Export processEvent for testing and refactor logic
* Fix missing newline at end of CalendarManager.test.ts
Add missing newline at the end of the file.
* fix test
* refactor: simplify link-as-an-app template to config.json only
- Remove api/, components/, index.ts, package.json from link-as-an-app template
- Add externalLink field to AppMetaSchema for external URL configuration
- Update API handler to dynamically create handlers for external link apps
- External link apps no longer create credentials (just redirect to URL)
- Update CLI to handle externalLinkUrl field for link-as-an-app template
- CLI now skips package.json update for apps without package.json
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: simplify external link handler to bypass credential creation
External link apps now directly return the redirect URL without going
through the AppDeclarativeHandler type, avoiding type conflicts with
the createCredential return type requirement.
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: migrate 24 external link apps to simplified structure
Migrated the following apps to use config.json with externalLink field
instead of api/add.ts handlers:
- amie, autocheckin, baa-for-hipaa, bolna, chatbase, clic, cron, deel
- elevenlabs, fonio-ai, framer, granola, greetmate-ai, lindy, millis-ai
- monobot, n8n, pipedream, raycast, retell-ai, synthflow, telli, vimcal
- wordpress
Each app now only contains: config.json, DESCRIPTION.md, static/
Removed: api/, components/, index.ts, package.json
Co-Authored-By: peer@cal.com <peer@cal.com>
* nit
* Fix merge conflict issue
* refactor: generate REDIRECT_APPS dynamically during app-store build
- Add logic to build.ts to detect apps with externalLink field
- Generate redirect-apps.generated.ts with list of redirect app slugs
- Update redirectApps.ts to import from generated file
- Revert [...]args].ts to original state (Flow 1 is preferred)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: cast REDIRECT_APPS to readonly string[] for includes check
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix error with React
* Take exteral link URL as input
* deleted bun.lockb
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* chore: Integrate mark-no-show booking audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Simplify host no-show audit and add comment for attendee actor
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Make actionSource required with ValidActionSource type and remove unnecessary comments
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove merge conflict markers from bookings.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: implement SYSTEM as a booking audit source for background tasks, including no-show triggers.
* refactor: Move prisma query to BookingRepository for audit logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Conflict resolution
* refactor: introduce `handleMarkHostNoShow` for public viewer and standardize actor and action source parameters for no-show actions.
* refactor: Combine HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService into NoShowUpdatedActionService
- Create new NoShowUpdatedAuditActionService with combined schema supporting both noShowHost and noShowAttendee as optional fields
- Update BookingAuditActionServiceRegistry to use combined service with NO_SHOW_UPDATED action type
- Update BookingAuditTaskerProducerService with single queueNoShowUpdatedAudit method
- Update BookingAuditProducerService.interface.ts with combined method
- Update BookingEventHandlerService with single onNoShowUpdated method
- Update handleMarkNoShow.ts to use combined audit service
- Update tasker tasks (triggerGuestNoShow, triggerHostNoShow) to use combined service
- Add NO_SHOW_UPDATED to Prisma BookingAuditAction enum
- Remove old separate HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService files
This allows a single API action (e.g., API V2 markAbsent) that updates both host and attendee no-show status to be logged as a single audit event.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: Add migration for NO_SHOW_UPDATED audit action enum value
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update no-show audit schema to use host and attendees array
- Rename noShowHost to host and noShowAttendee to attendees (remove redundant prefix)
- Change attendees from single value to array to support multiple attendees in single audit entry
- Update handleMarkNoShow.ts to create single audit entry for all attendees
- Update tasker tasks (triggerGuestNoShow, triggerHostNoShow) to use new schema
- Update display data types to reflect new schema structure
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Update schema to be more clear. Call onNoShowUpdated immediately after DB update as audit only cares about DB update, and this would avoid any accidental Audit update on DB change
* chore: accommodate schema changes and fix type errors for no-show audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update migration to remove old no-show enum values
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use updated attendees in webhook payload for guest no-show
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove duplicate imports and fix actor parameter in bookings.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Move booking query to BookingRepository and remove unused method
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: centralize no-show audit event firing and attendee fetching within `handleMarkNoShow` for improved clarity.
* fix: Build attendeesNoShow as Record with attendee IDs as keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix and add tests for handleMarkBoShow
* refactor: Move prisma queries to AttendeeRepository and BookingRepository
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Correct return type for updateNoShow method (noShow can be null)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update handleMarkNoShow tests to mock new repository methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix triggerGuestNoShow
* refactor: Move triggerGuestNoShow queries to AttendeeRepository and add unit tests
- Add new methods to AttendeeRepository:
- findByBookingId: Get attendees with id, email, noShow
- findByBookingIdWithDetails: Get full attendee details
- updateManyNoShowByBookingIdAndEmails: Update specific attendees
- updateManyNoShowByBookingIdExcludingEmails: Update all except specific emails
- Refactor triggerGuestNoShow.ts to use AttendeeRepository instead of direct Prisma calls
- Add comprehensive unit tests for triggerGuestNoShow following handleMarkNoShow.test.ts pattern:
- Mock repositories and external services
- In-memory DB simulation
- Test core functionality, audit logging, webhook payload, error handling, and edge cases
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Move triggerHostNoShow queries to repositories and delete unit test file
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Convert repository methods to use named parameters objects
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Enhance no-show handling by consolidating audit logging and updating repository interactions
- Refactor `buildResultPayload` to accept a structured argument for better clarity.
- Update `updateAttendees` to use a named parameter object for improved readability.
- Introduce `fireNoShowUpdatedEvent` to centralize no-show audit logging for both hosts and guests.
- Remove redundant methods and streamline attendee retrieval in `AttendeeRepository`.
- Add comprehensive unit tests for no-show event handling, ensuring accurate audit logging and webhook triggering.
* test: Add tests for handleMarkHostNoShow guest actor and unmarking host no-show
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: update attendeesNoShow validation to handle numeric keys
- Changed attendeesNoShow schema to use z.coerce.number() for key validation, ensuring string keys are correctly coerced to numbers.
- Updated test to validate attendeesNoShow data using numeric key comparison, improving robustness of the audit data checks.
* test: Add integration tests for NoShowUpdatedAuditActionService coerce fix
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add graceful error handling for audit log enrichment
- Add hasError field to EnrichedAuditLog type
- Create buildFallbackAuditLog() method for failed enrichments
- Wrap enrichAuditLog() in try-catch to handle errors gracefully
- Add booking_audit_action.error_processing translation key
- Update BookingHistory.tsx to show warning icon for error logs
- Hide 'Show details' button for logs with hasError
- Add comprehensive test cases for error handling scenarios
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Enhance booking audit action services with new display fields and improved validation
- Added "attendee_no_show_updated" and "no_show_updated" translations to common.json.
- Updated IAuditActionService to include new methods for handling display fields and migration.
- Enhanced NoShowUpdatedAuditActionService to differentiate between host and attendee no-show updates.
- Improved ReassignmentAuditActionService to ensure consistent handling of audit data.
- Refactored BookingAuditViewerService for better clarity and maintainability.
* refactor: Use attendeeEmail instead of attendeeId as key in audit data and store host's userUuid
- Updated schema to use attendee email as key instead of attendee ID because attendee records can be reused with different person's data
- Store host's userUuid in audit data with format: host: { userUuid, noShow: { old, new } }
- Updated fireNoShowUpdated to accept hostUserUuid parameter
- Added uuid field to BookingRepository.findByUidIncludeEventTypeAttendeesAndUser
- Updated NoShowUpdatedAuditActionService getDisplayFields to work with email-based keys
- Added attendeeRepository to DI modules for BookingAuditTaskConsumer
- Updated tests to match new schema format
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Update integration tests to use new schema format with host.userUuid and email keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: correct audit schema for SYSTEM source and ensure attendee lookup by bookingId
- Fix schema inconsistency: use host.userUuid format instead of hostNoShow
- Add user.uuid to getBooking select for audit logging
- Log warning when hostUserUuid is undefined but host no-show is being updated
- Use email as key for attendeesNoShow (not attendee ID) to match schema
- Fetch attendees by bookingId before updating to ensure correct scoping
- Remove unused safeStringify import
* refactor: Change attendeesNoShow schema from Record to Array format
- Update NoShowUpdatedAuditActionService schema to use array format:
attendeesNoShow: Array<{attendeeEmail: string, noShow: {old, new}}>
- Update handleMarkNoShow.ts to build attendeesNoShow as array
- Update common.ts fireNoShowUpdatedEvent to convert Map to array
- Update all tests to use new array format with attendeeEmail property
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update attendeesNoShow schema to use array format in triggerNoShow tasks
- Refactor `triggerGuestNoShow` and `triggerHostNoShow` to replace Map with array for `attendeesNoShowAudit`.
- Update `fireNoShowUpdatedEvent` to handle the new array format for attendees.
- Ensure consistent data structure across no-show audit logging for better clarity and maintainability.
* fix: Update ReassignmentAuditActionService to use GetDisplayFieldsParams interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update ReassignmentAuditActionService tests to use GetDisplayFieldsParams interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use handleMarkAttendeeNoShow for API v2 mark absent endpoint
- Export handleMarkAttendeeNoShow from platform-libraries
- Update API v2 bookings service to use handleMarkAttendeeNoShow with actionSource
- Add validation for userUuid before calling handleMarkAttendeeNoShow
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Enhance display fields structure in booking audit components
- Updated `BookingHistory.tsx` to allow for more flexible display fields, including optional raw values and arrays of values.
- Introduced `DisplayFieldValue` component for rendering display fields, improving clarity and maintainability.
- Adjusted `IAuditActionService` and `NoShowUpdatedAuditActionService` to align with the new display fields structure.
- Ensured consistent handling of display fields across the booking audit service for better data representation.
* fix: Remove debug code that duplicated first attendee in display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update attendee repository methods to use structured parameters
- Modified `updateNoShow`, `updateManyNoShowByBookingIdAndEmails`, and `updateManyNoShowByBookingIdExcludingEmails` methods to accept structured `where` and `data` parameters for better clarity and maintainability.
- Updated calls to these methods throughout the codebase to reflect the new parameter structure.
- Removed unused `findByIdWithNoShow` method and adjusted related logic to streamline attendee data retrieval.
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update test mock to match new AttendeeRepository.updateNoShow signature
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: rename handleMarkAttendeeNoShow to handleMarkNoShow and update related types
- Renamed `handleMarkAttendeeNoShow` to `handleMarkNoShow` for clarity.
- Updated type definitions to reflect the new naming convention.
- Modified the `markNoShow` handler to require `userUuid` and adjusted related logic.
- Enhanced the `fireNoShowUpdatedEvent` to accommodate changes in event type structure.
- Updated references across various files to ensure consistency with the new function name.
* handle null value
* fix: add explicit parentheses for operator precedence clarity
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix cubic reported bug
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* removed previious pr ommit from thi sone
* fixed addressed issues
* fallback to non-empty label for variant fields
* fallback to default label
* normalize empty labels and fallback to default when label equals field name
* fix: initially use empty label for name field (fullName variant)
Co-authored-by: Cursor <cursoragent@cursor.com>
* fix: remove normalization of empty field labels in FormBuilder
* fix: enhance label handling in getTranslatedConfigVariants function
* Update apps/web/modules/event-types/components/tabs/advanced/FormBuilder.tsx
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Pallav <90088723+Pallava-Joshi@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
* Integrate creation/rescheduling booking audit
* fix: add missing hostUserUuid to booking audit test data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix-ci
* feat: enhance booking audit with seat reference
- Added support for seat reference in booking audit actions.
- Updated localization for booking creation to include seat information.
- Modified relevant services to pass attendee seat ID during booking creation.
* fix: update test data to match schema requirements
- Add seatReferenceUid: null to default mock audit log data
- Add seatReferenceUid: null to multiple audit logs test case
- Convert SEAT_RESCHEDULED test data to use numeric timestamps instead of ISO strings
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Allow nullish seatReferenceUid
* feat: enhance booking audit to support rescheduledBy information
- Updated booking audit actions to include rescheduledBy details, allowing tracking of who rescheduled a booking.
- Refactored related services to accommodate the new rescheduledBy parameter in booking events.
- Adjusted type definitions and function signatures to reflect the changes in the booking audit context.
* Avoid possible run time issue
* Fix imoport path
* fix failing test due to merge from main\
* Pass useruuid
* Refactor getDisplayTitle method signatures to use _params for consistency across audit action services. Update IAuditActionService to include dbStore in GetDisplayTitleParams for improved data handling during title retrieval.
* fix: extend bulk-fetching optimization to actor and impersonator enrichment
- Collect actor userUuids (for USER type actors) in collectDataRequirements
- Collect impersonator UUIDs from log.context.impersonatedBy
- Update enrichActorInformation to use dbStore.getUserByUuid() for USER actors
- Update enrichImpersonator to use dbStore.getUserByUuid()
- Update tests to verify findByUuids is called with correct aggregated UUIDs
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Remove dbStore usage from audit action services after merge
- Update CreatedAuditActionService to use userRepository.findByUuid directly
- Remove unused dbStore parameter from RescheduledAuditActionService
- Sync test file with prepare-for-no-show-audit branch
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Restore dbStore bulk-fetching optimization pattern
- Add dbStore parameter and getDataRequirements method to IAuditActionService interface
- Update CreatedAuditActionService to use dbStore.getUserByUuid() for bulk-fetched data
- Update RescheduledAuditActionService to accept dbStore parameter
- Update BookingAuditViewerService to:
- Collect data requirements from all audit logs
- Bulk-fetch users with findByUuids() before enrichment
- Pass dbStore to action services and enrichment methods
- Update tests to verify findByUuids is called for bulk-fetching optimization
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove duplicate fireBookingEvents method in RecurringBookingService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* revert: Remove formatting-only changes in audit action services
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Make getDataRequirements required and fetch rescheduled logs early
- Make getDataRequirements a required method in IAuditActionService interface
- Add getDataRequirements to all action services that didn't have it
- Fetch rescheduled logs early to include their data requirements in bulk fetch
- Update ReassignmentAuditActionService to use dbStore pattern instead of repository
- Update NoShowUpdatedAuditActionService to use dbStore pattern
- Update tests to use new dbStore pattern
- Handle invalid data gracefully in collectDataRequirements
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Remve unncessary variable
* feat: Implement Actor Strategy pattern for bulk-fetching attendees and credentials
- Add ActorStrategies.ts with strategy pattern for all 5 actor types
- Extend EnrichmentDataStore with StoredAttendee and StoredCredential types
- Add findByIds method to CredentialRepository for bulk-fetching
- Update BookingAuditViewerService to use strategy pattern:
- Replace switch statement in enrichActorInformation with strategy dispatch
- Update collectDataRequirements to collect attendeeIds and credentialIds
- Update buildEnrichmentDataStore to bulk-fetch in parallel
- Update tests to use findByIds mock and reflect graceful handling of missing data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Restore error-throwing for actors with missing required IDs
- USER actor now throws error when userUuid is missing
- ATTENDEE actor now throws error when attendeeId is missing
- Updated tests to expect hasError: true for fallback logs
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Restore original test assertions, only update mocks for bulk methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Add contract verification tests for getDataRequirements
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Clean up imports and improve type definitions in BookingAuditTaskConsumer and BookingAuditViewerService
- Consolidated import statements for better readability
- Enhanced type definitions for clarity and consistency
- Updated method signatures to use destructured parameters for improved maintainability
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Split contract verification tests into separate files per action service
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Rename contract test files to .test.ts and merge into existing test file
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add runtime validation to EnrichmentDataStore for undeclared data access
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Make declaredRequirements required and add ensureFetched helper
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Simplify EnrichmentDataStore with constructor + fetch() pattern
- Remove unused userIds and bookingUids from DataRequirements
- Remove unused getUserById and getBookingByUid methods
- Refactor constructor to accept requirements and repositories
- Add fetch() method that fetches data and populates maps
- Use Map keys as source of truth for declared requirements
- Update getters to throw when accessing undeclared data
- Add comprehensive unit tests for EnrichmentDataStore
- Update BookingAuditViewerService to use new API
- Update contractVerification.ts to remove unused tracking
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Clean up imports and improve type definitions in BookingAuditTaskConsumer and BookingAuditViewerService
- Consolidated import statements for better readability
- Enhanced type definitions for clarity and consistency
- Updated method signatures to use destructured parameters for improved maintainability
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Include contextRequirements in mergeDataRequirements and add accessedData assertions
- Fix bug where contextRequirements (impersonator UUIDs) were not merged
- Add accessedData assertions to CreatedAuditActionService.test.ts
- Add accessedData assertions to ReassignmentAuditActionService.test.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: replace FeaturesRepository with DI-based feature repositories
Migrate from the legacy FeaturesRepository pattern to separate DI-based repositories:
- Add checkIfFeatureIsEnabledGlobally to IFeatureRepository
- Add getTeamsWithFeatureEnabled to ITeamFeatureRepository
- Update CalendarSubscriptionService to use featureRepository, teamFeatureRepository, and userFeatureRepository
- Update SelectedCalendarRepository.findNextSubscriptionBatch to filter by teamIds instead of featureIds
- Update OnboardingPathService to use DI via getFeatureRepository()
- Remove prisma parameter from OnboardingPathService callsites
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: update tests to use new DI-based feature repositories
Update CalendarSubscriptionService and SelectedCalendarRepository tests
to use the new separate repository interfaces:
- featureRepository for global feature checks
- teamFeatureRepository for team-level feature checks
- userFeatureRepository for user-level feature checks
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: update API routes to use DI-based feature repositories
Update cron and webhook routes to use the new separate repository
interfaces instead of the combined FeaturesRepository.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove prisma arg from getGettingStartedPathWithParams call
The OnboardingPathService method no longer requires a prisma argument
as it now uses DI containers internally.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: update route tests to expect new DI-based feature repositories
Update service instantiation tests to expect featureRepository,
teamFeatureRepository, and userFeatureRepository instead of the
old featuresRepository.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: optimize global feature check and add guard in checkForNewSubscriptions
- Use targeted select for only `enabled` field in checkIfFeatureIsEnabledGlobally
- Add global feature flag guard in checkForNewSubscriptions to avoid unnecessary
DB queries and API calls when the cache feature is globally disabled
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* remove redundant comment
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Validates webhook URLs on create and update:
- HTTPS required (HTTP allowed for self-hosted and E2E)
- Blocks private IP ranges and localhost
- Blocks cloud metadata endpoints
Existing webhooks are preserved: validation only applies when URL is created or changed.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: ux audit changes
* fix: remove locale-insensitive toLowerCase() on translation
Remove .toLowerCase() call on t('minutes') translation as it is
locale-insensitive and can corrupt translations (e.g., Turkish
dotted/dotless 'i'). The translation itself should provide the
desired casing.
Addresses Cubic AI review feedback with confidence 9/10.
Co-Authored-By: unknown <>
* fix: redirect url tool tip not working
* fix: type check
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize team page data fetching by reducing host user data
For team view pages (/team/[slug]), event type hosts only need minimal
user data for avatar display (id, name, username, avatarUrl).
Previously, the full userSelect was used which included:
- teams (with nested team data)
- credentials (with app and destinationCalendars)
- email, bio
This optimization reduces data transfer significantly for teams with
many event types and hosts. With 441K requests and 54GB outgoing data
(~128KB per request), even small reductions per request add up.
The full userSelect is still used when !isTeamView to support the
connectedApps feature.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize team page serialization to reduce data transfer
- Replace spread operators with explicit field selection for eventTypes
- Only send minimal user data needed for UserAvatarGroup: name, username, avatarUrl, profile
- Override eventTypes in return props with minimalEventTypes
- Reduces per-request data transfer by excluding unnecessary fields like email, bio, teams, credentials
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add missing fields needed by EventTypeDescription component
Add metadata, seatsPerTimeSlot, requiresConfirmation to minimalEventTypes
Add id to user object for proper key handling
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: remove serialization changes that broke TypeScript types
Keep only the query-level optimization in queries.ts which reduces
database load by fetching minimal user data for event type hosts
when isTeamView=true.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: reduce team page client payload with explicit DTO types
- Create TeamPage.types.ts with explicit DTO types for minimal data
- Update getServerSideProps.tsx to return only fields needed by the UI
- Update team-view.tsx to use explicit types instead of inferSSRProps
This reduces the data sent to the client by:
- Event types: only id, title, slug, description, length, schedulingType,
recurringEvent, metadata, requiresConfirmation, seatsPerTimeSlot
- Event type users: only id, name, username, avatarUrl, avatar, profile
- Members: only fields needed by Team component
- Children: only slug and name
- Parent: only id, slug, name, isOrganization, isPrivate, logoUrl
Combined with the query-level optimization, this should significantly
reduce the ~128KB average payload per request.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Revert: explicit DTO approach due to type compatibility issues
The explicit DTO types broke compatibility with existing components
(EventTypeDescription, UserAvatarGroup) which expect specific type
structures. Keeping only the query-level optimization.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: reduce team page client payload with minimal data serialization
- Update EventTypeDescription to accept minimal type (only fields actually used)
- Update UserAvatarGroup to accept minimal user type
- Update UserAvatar to accept minimal profile type
- Update Team component to accept minimal member type
- Update getServerSideProps to explicitly select only needed fields
This reduces the ~128KB client payload by removing unused fields from:
- Event types: removed hidden, price, currency, lockTimeZoneToggleOnBookingPage, etc.
- Event type users: only send name, username, avatarUrl, avatar, profile.username, profile.organization.slug
- Team members: only send fields needed by Team component
- Team parent/children: only send fields needed for display
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: revert component type changes, keep serialization optimization
- Revert EventTypeDescription, UserAvatarGroup, UserAvatar, Team component types to original
- Keep serialization optimization in getServerSideProps.tsx
- Add missing fields (price, currency, hidden, etc.) for EventTypeDescription compatibility
- Add full profile structure for UserAvatarGroup compatibility
This reduces client payload by explicitly selecting only needed fields while
maintaining type compatibility with existing component usages.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add missing fields for component type compatibility
- Add lockedTimeZone and canSendCalVideoTranscriptionEmails for EventTypeDescription
- Add organizationId to members for Team component MemberType
- Add name, calVideoLogo, bannerUrl to organization for UserProfile type
- Reorder fields to match baseEventTypeSelect structure
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The error 'TypeError: value.hasOwnProperty is not a function' can occur
when objects are created without the standard Object prototype (e.g., via
Object.create(null) or certain parsing methods). Using
Object.prototype.hasOwnProperty.call() is the safe way to check for
property existence on any object.
Fixed in:
- GetSlotsInputPipe (isById, isByUsernameAndEventTypeSlug, isByTeamSlugAndEventTypeSlug)
- CancelBookingInputPipe (isCancelSeatedBookingInput)
- CreateBookingInputPipe (isRecurringBookingInput, isInstantBookingInput)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add delayed formbricks tracking for feature opt-in
Adds delayed Formbricks survey tracking for feature opt-in. When a user
opts into a feature, this allows triggering a Formbricks action after a
configurable delay (e.g., 24 hours later) to collect feedback once
they've had time to use the feature.
Key changes:
- Added `formbricks` config option to `OptInFeatureConfig` interface
with `actionName` and `delayMs` properties
- Created `useFormbricksOptInTracking` hook that handles the delayed
tracking logic
- Added `isFeatureTracked` / `setFeatureTracked` storage helpers to
prevent duplicate tracking
- Integrated the tracking hook into `useFeatureOptInBanner`
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* upgrade formbricks
* feat: replace formbricks popup with custom feedback dialog
Instead of using Formbricks' built-in popup, we now show a custom
Cal.com-styled feedback dialog that submits responses directly to
Formbricks API via tRPC mutation.
- Add FeedbackDialog component with emoji rating selector
- Add feedback tRPC router for server-side Formbricks submission
- Update useFormbricksOptInTracking to return dialog state
- Add survey config fields (surveyId, questions) to config
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: position feedback dialog at bottom-right corner
- Use base-ui Dialog primitives for custom positioning
- Position dialog at bottom-right to avoid Intercom overlap
- Use z-index 10000 (below Intercom's high z-index)
- Keep blocking backdrop for modal behavior
- Use i18n keys for title/description
- Add survey IDs for bookings-v3 feedback
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add i18n keys for feedback dialog title/description
Allow each feature to specify custom i18n keys for the feedback dialog
title and description via the formbricks config.
- Add titleKey/descriptionKey to formbricks config interface
- Pass i18n keys through feedbackDialogProps
- Add bookings_v3_feedback_title/description translation keys
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: move FeedbackDialog into FeatureOptInBannerWrapper
Better encapsulation - consumers of the feature opt-in banner
no longer need to handle the feedback dialog separately.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add 5 second delay before showing feedback dialog
Ensures the page has time to finish loading before showing
the feedback dialog, avoiding showing it while skeletons
are still visible.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: simplify feedback dialog UI
- Remove redundant question labels
- Add "(optional)" to comment placeholder
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: remove emoji button borders and add footer gap
- Remove borders from rating emoji buttons
- Add proper gap between textarea and footer (pb-4)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: delayMs is opt-in waiting period, not setTimeout delay
delayMs represents the minimum time that must pass since opt-in
before showing the feedback form (e.g., 3 days). If not enough
time has passed, we skip showing the form entirely instead of
setting a long setTimeout.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: custom feedback dialog for feature opt-in
- Replace Formbricks popup with Cal.com-styled dialog
- Add configurable delay (waitAfterDays) before showing feedback
- Position dialog at bottom-right, non-blocking
- Add localStorage tracking to prevent duplicate feedback
- Add device targeting (showOn: desktop/mobile/all)
- Create tRPC endpoint for Formbricks API submission
- Use proper logger for error handling
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: rename tracking terminology to feedback
- Rename useFormbricksOptInTracking → useOptInFeedback
- Rename FormbricksOptInTrackingResult → OptInFeedbackState
- Rename formbricksTracking property → feedback
- Rename FormbricksTrackingState → FeedbackState
We no longer "track" events to Formbricks. Instead, we show
our custom feedback dialog when conditions are met.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: set waitAfterDays to 3 for production feedback delay
Co-Authored-By: unknown <>
* fix: update formbricks JS SDK usage for v3.0.0
The @formbricks/js SDK v3.0.0 changed its API:
- setup() no longer accepts debug, userId, or attributes
- Use setUserId() and setAttributes() after setup instead
- track() now expects { hiddenFields: ... } or undefined
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* add owner email to admin oauth view
* fix type error
* fix oauth flow with sign up
* improve e2e tests
* fix: use dynamic user email in OAuth client e2e test
The test 'editing redirect uri of an approved client triggers reapproval'
creates a new user and assigns the OAuth client to that user, but was
incorrectly expecting 'admin@example.com' as the owner email. Changed to
use user.email to correctly validate the owner email matches the created
user.
Addresses Cubic AI review feedback (confidence: 9/10)
Co-Authored-By: unknown <>
---------
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: apply biome formatting to Phase 1 packages
Packages formatted:
- packages/app-store-cli
- packages/config
- packages/dayjs
- packages/debugging
- packages/embeds/embed-react
- packages/embeds/embed-snippet
- packages/kysely
- packages/platform/constants
- packages/platform/utils
- packages/testing
- packages/tsconfig
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: restore non-null assertions in embed-snippet to fix type error
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: remove ESM import from CommonJS config file
Biome incorrectly added 'import process from node:process' to a CommonJS
file that uses require(). This breaks Jest which expects CommonJS syntax.
The process global is already available in Node.js without explicit import.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: exclude packages/embeds from Phase 1 formatting
Per user request, reverting all biome formatting changes in packages/embeds
to handle separately.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: restore const enum for BookingLocations
Biome changed 'const enum' to 'enum' which is a semantic change.
Reverting to keep the PR purely formatting-only.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: refactor UI to use coss + coupons UI
* fix: use TRPCError and remove sensitive logging in createCoupon handler
- Replace plain Error with TRPCError for consistent tRPC error handling
- Use INTERNAL_SERVER_ERROR for missing env configuration
- Use UNAUTHORIZED for permission denied errors
- Use BAD_REQUEST for API failure errors
- Remove console.warn that logged username (sensitive info)
Co-Authored-By: unknown <>
* feat: add copy button and fix 0 on % off
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* wip
* fix: claue reveiew commentsd
* add seed test
* fix: seed test plus subscriptin undefined in webhook
* chore: udpate triggr
* Integration test + HWM seed script
* add feature flag for hmw
* Remove redudant docs
* feat: add hwm-seating feature flag for high water mark billing
- Add new hwm-seating feature flag to control HWM billing logic
- Update HighWaterMarkService to use hwm-seating flag
- Update SeatChangeTrackingService to use hwm-seating flag
- Update BillingPeriodService to use hwm-seating flag
- Add flag to migration SQL
- Enable flag in seed script and integration test
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add hwm-seating to useFlags and fix test mocks
- Add hwm-seating to useFlags.ts initial data
- Add shouldApplyHighWaterMark mock to TeamBillingService tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix test
* feat: move to invoice paid subscription
* fix: transactions for transiants updates + reusable util
* fix: address Cubic AI review feedback for HWM billing
- Fix dotenv import hoisting in seed-hwm-test.ts by using side-effect import
- Remove logging of sensitive information (password/emails) in seed script
- Fix incorrect newHighWaterMark return value when update is not performed
Co-Authored-By: unknown <>
* add additional unit tests
* improve DI
* fix migration + restore package.json trigger update
* feat:move to featurerepo+update tests
* fix mocks
* fix DI calls
* revert faeturerepo to featuresrepository
* fix: pass featureRepository to BillingPeriodService and HighWaterMarkService
- Fix TeamBillingService.ts: pass featureRepository to BillingPeriodService constructor
- Fix _invoice.upcoming.ts: pass featureRepository to HighWaterMarkService constructor
- Fix hwm-webhook-utils.ts: pass featureRepository to HighWaterMarkService constructor
These services require featureRepository for feature flag checks but production
call sites were missing this required dependency, causing runtime errors.
Addresses Cubic AI review feedback on PR #27559
Co-Authored-By: unknown <>
* fix import
* update feature repo usage to features
* remove test thats mocking the wrong stuff
* revert RegularBookingService changes
* remove hide branding
* remove change in redudant file
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add delete confirmation dialog to webhook list items
* adds appname for translation key used in alert
* extract delete wehook dialog into separate component
* add e2e test
* chore update e2e locator
---------
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
Co-authored-by: Romit <romitgabani@icloud.com>
- Changed checkIfSuccessfullyConfiguredInWorkspace to throw detailed errors instead of returning boolean
- Updated testDelegationCredentialSetup in Google Calendar and Office365 Calendar services to throw specific errors
- Updated assertWorkspaceConfigured to let errors propagate naturally
- Updated Calendar interface type to reflect Promise<void> return type
- Updated CalendarCacheWrapper and CalendarTelemetryWrapper to match new interface
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(analytics): handle null/undefined tracking IDs in embed endpoint
PR 26976 introduced strict validation for analytics app schemas,
but this broke the embed endpoint when apps are installed with
null tracking IDs (apps enabled but not yet configured)
Changes:
- Add nullishToEmpty preprocessor to convert null/undefined to ""
- Update all analytics schemas to handle null gracefully
- Change GTM to allowEmpty: true for consistency
- Fix createPrefixedIdSchema to not add prefix to empty strings
- Add tests for null handling
* fix(analytics): add optional() to schemas for type compatibility
The preprocess schemas output string type but BookerEvent has
trackingId?: string | undefined. Adding .optional() makes the
types compatible while preserving null → "" conversion at runtime
* refactor: use z.union+transform for proper type inference
Replace z.preprocess() with z.union([string, null, undefined]).transform()
pattern. This ensures TypeScript correctly infers:
- Input: string | null | undefined
- Output: string (always)
Remove .optional() from tracking ID fields to output string, not string | undefined
* fix(analytics): make trackingId optional in metapixel and plausible schemas
Address Cubic AI review feedback (confidence 9/10) to keep trackingId
optional in the schema to avoid breaking existing payloads that omit
the field entirely. The union with null/undefined handles the value
transformation, but the property itself must be optional to allow
payloads without the key.
Co-Authored-By: unknown <>
* fix: restore .optional() for missing tracking fields
* fix: revert safeUrlSchema to maintain databuddy type compat
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* se organizer default app
* nit
* Add tests: packages/features/bookings/lib/handleNewBooking/getBookingData.test.ts
Generated by Paragon from proposal for PR #27620
* Replace n x m array functions with maps
* feat: add attributeIds filter to findManyByOrgMembershipIds
Allow filtering attribute-to-user assignments by specific attribute IDs.
This enables querying only the attributes needed for a routing rule
instead of fetching all attributes for all team members.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: extract attribute IDs from routing rules and filter queries
Add extractAttributeIdsFromQueryValue helper to parse routing rules
and identify which attributes are referenced. Pass these IDs through
getAttributesAssignmentData to _queryAllData to only fetch the
attribute assignments that are actually needed for evaluation.
For a team with 785 members and 14 attributes, if a routing rule
only references 2-3 attributes, this reduces the query from ~3500
rows to ~500 rows.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: use attribute filtering in findTeamMembersMatchingAttributeLogic
Extract attribute IDs from the routing rules (main + fallback) and
pass them to getAttributesForLogic to only fetch necessary data.
This completes the optimization to reduce database load when
evaluating routing rules that only reference a subset of attributes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: filter attributes query by attribute IDs
Also filter the attributes query (not just attributeToUser assignments)
by the attribute IDs referenced in routing rules.
For a routing rule that references 2 out of 14 attributes with 6524
total options, this reduces:
- Attributes fetched: 14 → 2
- Options fetched: 6524 → ~930 (proportional reduction)
- Fewer regex replacements in replaceAttributeOptionIdsWithOptionLabel
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add comprehensive test coverage for attribute routing query optimization
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Revert "Replace n x m array functions with maps"
This reverts commit 312eeb03bd1aba19760f093c798861a23b5fc66f.
* fix: re-establish extractAttributeIdsFromQueryValue mock after resetAllMocks
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: optimize myStats tRPC call to only trigger when Intercom is enabled
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: fetch myStats on-demand when Intercom session is created, not upfront
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove unnecessary comments
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: skip platform billing for non-platform-managed users in 2024-08-13 API
- Add isPlatformManaged check in billBooking() to skip billing for regular users
- Add isPlatformManaged check in billRescheduledBooking() to skip billing for regular users
- Add isPlatformManagedUserBooking check in cancelBooking() to skip billing cancellation for regular users
- Add E2E tests to verify billing behavior for both regular and platform-managed users
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: reuse booking data for isPlatformManaged instead of fetching user
- Capture isPlatformManagedUserBooking from raw booking data returned by regularBookingService.createBooking() and recurringBookingService.createBooking()
- Pass the flag through the output booking objects
- Update billBooking() and billRescheduledBooking() to use the flag instead of fetching user from database
- Matches the pattern used in 2024-04-15 controller
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: skip platform billing in booking service for non platform
* fix: use double negation for boolean type safety in isPlatformManagedUserBooking
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: ensure default calendars
* test: add E2E tests for delegation credential controller and update tasker config
- Add E2E tests to verify ensureDefaultCalendars is called when enabling delegation credentials
- Update calendars tasker config to use medium-1x machine for retry on OOM
- Set minimum retry backoff to 60 seconds (1 minute between retries)
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: update tasker config to use small-2x machine with outOfMemory retry on medium-1x
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: update E2E tests to properly spy on service instance and use valid workspace platform slug
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* ci: add CALCOM_SERVICE_ACCOUNT_ENCRYPTION_KEY to E2E API v2 workflow
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: add encryption key to E2E test file for delegation credentials
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* revert: remove CALCOM_SERVICE_ACCOUNT_ENCRYPTION_KEY from workflow (moved to test file)
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: move encryption key to setEnvVars.ts for E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use valid format for service account encryption key
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: encrypt service account key in E2E test for delegation credentials
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: mock updateDelegationCredentialEnabled to bypass Google API call in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: get service from app.get() after initialization for proper spy setup in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use jest.mock() to mock toggleDelegationCredentialEnabled and bypass Google API calls in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use Service.prototype pattern for spying on ensureDefaultCalendars in E2E tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: move spy setup to beforeAll before app.init() for proper NestJS interception
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(api-v2): add GET /v2/bookings/by-seat/{seatUid} endpoint
- Add new endpoint to retrieve a booking by its seat reference UID
- Add getByReferenceUidIncludeBookingWithAttendeesAndUserAndEvent method to BookingSeatRepository
- Add getBookingBySeatUid method to BookingsService
- Add e2e tests for the new endpoint
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use Object.prototype.hasOwnProperty.call for ES compatibility in e2e tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use select instead of include in BookingSeatRepository for security and performance
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: add teamId and userId to eventType select for access check
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: return seatUid attendee when not admin and seatsShowAttendees is false
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use select for eventType in repository and fetch full eventType for access check
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* users not joining org/team
* fix: address Cubic AI review feedback for E2E tests
- Replace waitForURL with expect(page).toHaveURL() for fail-fast URL checks
- Replace Prisma include with select to fetch only needed fields
Both issues had confidence score 9/10 from Cubic AI review.
Co-Authored-By: unknown <>
* fix: add select to Prisma query in getServerSideProps
Address Cubic AI review feedback - use select to fetch only the id field
instead of retrieving all user columns, following project guidelines to
avoid over-fetching and exposing sensitive data.
Co-Authored-By: unknown <>
* refactor: remove comments from newly added E2E tests
Clean up the E2E tests by removing unnecessary comments as requested.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: avatar URL breaking for team/orgs in booker embed
* fix: api v2 breaking because of type mismatch
* fix: atoms build
* chore: implement PR feedback
The checkout step deletes the review-context.json file that was extracted
from the artifact. The 'Post comment with Devin session link' step was
trying to read this file after checkout, causing ENOENT errors.
Fixed by using the PR number from the extract-prompt step output instead
of reading the deleted file.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add GitHub workflows to sync agents/ to Devin Knowledge
- Add parse-to-devin-knowledge.ts to convert agents/ markdown to Devin Knowledge JSON
- Add validate-format.ts to validate rules have frontmatter and knowledge-base sections start with 'When...'
- Add sync-to-devin.ts to sync knowledge entries to Devin API
- Add export-devin-knowledge.sh to backup existing Devin knowledge
- Add validate-agents-format.yml workflow to validate format on PRs
- Add sync-agents-to-devin.yml workflow to sync on merge to main
- Add devin-knowledge.json to .gitignore (generated file)
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: use tsx instead of ts-node for better ESM support in CI
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* docs: add missing knowledge entries from Devin backup
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: move agents scripts to scripts/ folder
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: rename scripts to devin-knowledge-* for clarity
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: move scripts to scripts/devin/ with clearer names
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* rename DEVIN_API_TOKEN to DEVIN_API_KEY
* docs: fix usage comment script path
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: remove folder creation from sync script (API doesn't support it)
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add -S flag to shebang for proper env execution
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* use pull_request_target
* fix: add -S flag to shebang in sync-knowledge-to-devin.ts for proper env execution
Co-Authored-By: unknown <>
* restructure workflows
* refactor: consolidate agent docs and split knowledge-base into modular rules
- Delete knowledge-base.md, migrate content to 17 new rule files
- Delete coding-standards.md (content duplicated in AGENTS.md and rules)
- Add ci- and reference- prefixes to rules/_sections.md
- Update AGENTS.md to reference new rule files
- Update agents/README.md as rules index
- Clean up parse-local-knowledge.ts (remove deleted file references)
New rule files:
- testing-playwright, testing-mocking, testing-timezone
- ci-check-failures, ci-type-check-first, ci-git-workflow
- data-prisma-migrations, data-prisma-feature-flags
- quality-error-handling, quality-imports, quality-pr-creation, quality-code-comments
- architecture-features-modules
- patterns-workflow-triggers, patterns-app-store
- reference-file-locations, reference-local-dev
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Revert "refactor: consolidate agent docs and split knowledge-base into modular rules"
This reverts commit 8251b6b214c7c01a3bfe2137c6aa9292dd72427e.
* refactor: reorganize agent docs - extract coding rules, keep domain knowledge
- Slim down knowledge-base.md (356 → 96 lines) to domain knowledge only
- Add Business rules section (managed events, orgs/teams, OAuth clients)
- Delete coding-standards.md (content moved to rules)
- Create 19 new rule files for coding guidelines:
- quality-*: PR creation, error handling, imports, comments, code review
- testing-*: playwright, mocking, timezone, incremental
- ci-*: check failures, type-check-first, git workflow
- data-prisma-*: migrations, feature flags
- patterns-*: workflow triggers, app store
- architecture-features-modules, reference-file-locations, reference-local-dev
- Update agents/README.md as rules index (43 total rules)
- Update _sections.md with CI/CD and Reference sections
- Clean up parse-local-knowledge.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: rename knowledge-base sections to start with "When..."
Update section headers to pass validation rules:
- "Business Rules" → "When working with managed events, organizations, or OAuth clients"
- "Product & Codebase Knowledge" → "When you need product or codebase context"
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: split knowledge-base business rules into separate sections
Split the combined "When working with managed events, organizations, or OAuth clients"
section into three distinct ## sections for better Devin triggering:
- When working with managed event types
- When working with organizations and teams
- When working with OAuth clients
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: simplify trigger description logic in parse-local-knowledge.ts
Since validate-local-knowledge.ts enforces that all section titles must start
with "When...", we can remove the manual fallback logic and just use the
title directly as the trigger description.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: simplify knowledge-base section validation
Simplify the validation to only check that section titles start with "When..."
since we've standardized on that pattern. Remove the special cases for error,
file naming, PR, and repo note sections.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add script to delete all Devin knowledge entries
Add delete-all-devin-knowledge.ts script that:
- Lists all knowledge entries before deletion
- Requires interactive confirmation (Y) to proceed
- Blocks execution in non-TTY environments (CI, piped input)
- Shows progress while deleting entries
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add API reference links to delete-all-devin-knowledge script
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: replace NOW() with CURRENT_TIMESTAMP in feature flag migration example
Co-Authored-By: unknown <>
* add -S
* docs: consolidate agent documentation and add AI setup guide
- Remove duplicated Project Structure & Tech Stack from agents/README.md
- Condense Commands section in AGENTS.md, link to agents/commands.md
- Add AI-Assisted Development section to root README.md explaining
the agents/ folder structure and symlink configuration
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: remove incorrect CLAUDE.md symlink claim from README
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* test: add e2e tests for team filter in bookings list
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: replace text locators with data-testid selectors in team filter tests
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: apply formatting to resolved merge conflict
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Use workflow_run pattern to enable the Cubic AI to Devin review workflow
for PRs from external forks. The pull_request_review event doesn't have
access to secrets when triggered from forks, so we split into two workflows:
1. cubic-devin-review-trigger.yml - Lightweight workflow that triggers on
pull_request_review and saves the review context as an artifact
2. cubic-devin-review.yml - Main workflow that triggers on workflow_run,
downloads the artifact, and processes it with full permissions/secrets
This follows the same pattern used by on-changes-requested.yml and re-draft.yml.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: consolidate agent config folders into agents directory
- Move .claude/skills and .claude/rules content to agents/
- Remove duplicate .cursor/ and .goose/ folders
- Create symlinks from .claude/ and .cursor/ to agents/
- Convert review.mdc to quality-review-checklist.md with proper frontmatter
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* feat: add Cal.com APIv2 skills to agents directory
Move the Cal.com API v2 skills from PR #27445 into the consolidated
agents/skills/ directory structure. This includes:
- SKILL.md - Main skill file with API overview
- references/authentication.md - Authentication methods
- references/bookings.md - Bookings API reference
- references/calendars.md - Calendars API reference
- references/event-types.md - Event types API reference
- references/schedules.md - Schedules API reference
- references/slots-availability.md - Slots and availability API reference
- references/webhooks.md - Webhooks API reference
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: update calcom-api skill to use Claude Code frontmatter format
Remove non-standard frontmatter fields (license, metadata) and keep only
the Claude Code supported fields (name, description) as per the
Claude Code skills specification.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: restructure calcom-api SKILL.md as index file
Convert SKILL.md from a comprehensive API doc (317 lines) to a concise
index file (108 lines) that references the detailed documentation in
the references/ folder. This follows the Claude Code skills pattern of
keeping SKILL.md focused with supporting files for detailed content.
The SKILL.md now:
- Provides a quick start guide with essential examples
- References all 7 detailed reference docs in a table
- Lists common workflows and best practices
- Points to external resources
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* docs: preserve deleted content in reference files
Move content that was removed from SKILL.md to appropriate reference files:
- Add Error Handling and Pagination sections to authentication.md
- Add Organization endpoints to event-types.md
- Add Core Concepts section back to SKILL.md
This ensures no useful API documentation is lost during the restructuring.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: skip platform billing for non-platform-managed users
- Add isPlatformManaged to user select in saveBooking and findBookingQuery
- Update 2024-04-15 booking controller to check isPlatformManaged before billing
- Update 2024-08-13 bookings service billBooking methods to check isPlatformManaged
- Update buildDryRunBooking to include isPlatformManaged in user object
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: update buildDryRunBooking test to include uuid and isPlatformManaged fields
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: simplify to only check isPlatformManaged in normal booking flow
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: add E2E tests for billing behavior based on isPlatformManaged flag
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* chore: only trigger platform billing for platform user bookingd
* test: add E2E tests for cancel and recurring booking billing behavior
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: correct expected status code for cancel booking endpoint (201 instead of 200)
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: clear oversized images from onboarding store during migration
This fixes the 429 errors caused by large images stored in IndexedDB
before PR #27285 added cropping functionality.
Changes:
- Add version 1 to zustand persist config with migration function
- Migration clears images exceeding thresholds:
- Logos/avatars: 500KB (properly cropped are ~50-150KB)
- Banners: 1.5MB (properly cropped are ~200-500KB)
- Add comprehensive tests for migration logic
When users refresh the page, the migration runs automatically and
clears any oversized images, requiring them to re-upload using the
new cropping flow.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* chore: remove migration test file
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* show personal credits even if on team plan
* show personal credits even when on teams plan
* fix learn more link
---------
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
- Skip retrying and alerting for temp/sandbox subscription IDs
- Skip retrying and alerting for non-usage-based subscriptions
- Log expected errors at info level instead of error level
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add proration invoice and reminder email templates
Add email templates for monthly proration billing notifications:
- ProrationInvoiceEmail: Sent when invoice is created for additional seats
- ProrationReminderEmail: Sent 7 days later if invoice remains unpaid
Includes:
- React email templates using V2BaseEmailHtml
- BaseEmail classes for rendering
- Billing email service functions
- Translation keys for email content
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add tasker integration for proration email notifications
Add task handlers for proration billing email flow:
- sendProrationInvoiceEmail: Sends invoice email and schedules reminder
- sendProrationReminderEmail: Sends reminder if invoice still unpaid
- cancelProrationReminder: Cancels scheduled reminder on payment success
The calling job should trigger these tasks after:
1. MonthlyProrationService.createProrationForTeam() succeeds with invoice
2. handleProrationPaymentSuccess() is called (to cancel reminder)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: i18n + feedback from cubic + morgan
* fix: use default tasker import instead of non-existent getTasker
Co-Authored-By: unknown <>
* fix: add trigger tasks plus DI
* use for await
* fix query
* fix: remove duplicate JOIN alias in findTeamMembersWithPermission query
The raw SQL query had two INNER JOINs using the same alias 'u':
- INNER JOIN "users" u ON m."userId" = u.id
- INNER JOIN "User" u ON m."userId" = u.id
This would cause a SQL error at runtime. Removed the duplicate JOIN
with incorrect table name ("User" instead of "users").
Fixes issue identified by Cubic AI review.
Co-Authored-By: unknown <>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add redirect option for non-routing form bookings
Add a new event type option that redirects to a custom URL when the booking
was not made through a routing form (no cal.routingFormResponseId or
cal.queuedFormResponseId query parameters).
Changes:
- Add redirectUrlOnNoRoutingFormResponse field to EventType schema
- Add UI toggle in Advanced tab to configure the redirect URL
- Implement redirect logic in bookingSuccessRedirect hook
- Add translations for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to test builder
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to BookerEvent type
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to getPublicEventSelect
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirect logic to getServerSideProps for non-routing form bookings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove redirectUrlOnNoRoutingFormResponse from booking success flow
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use 'in' operator for type narrowing on redirectUrlOnNoRoutingFormResponse
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: exclude redirectUrlOnNoRoutingFormResponse from test assertions
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include redirectUrlOnNoRoutingFormResponse in test assertions and update description
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to mockUpdatedEventType
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove redirect logic from instant meetings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to EventTypeRepository select
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to form default values
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Reword string
* fix: bust tRPC cache for redirectUrlOnNoRoutingFormResponse
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: guard redirect when rescheduleUid or bookingUid is present
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add redirectUrlOnNoRoutingFormResponse to dynamicEvent defaults
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add bookingUid guard to team getServerSideProps redirect
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(analytics): add input validation to analytics app schemas
Add strict input validation for tracking IDs and URLs in analytics
app integrations to ensure data conforms to expected formats
* fix: remove optional/default to fix type inference
Remove .optional() and .default("") from schemas with transform/refine
chains to preserve correct TypeScript type inference
* fix: restore .optional() for type compatibility
* fix(e2e): use valid GTM container ID format in analytics test
Co-Authored-By: pedro@cal.com <pedro.castro@ideware.com.br>
* fix(analytics): address Cubic AI review feedback
- Tighten Meta Pixel ID regex from {1,20} to {15,16} digits (confidence 9.5/10)
- Fix PostHog error message to mention underscores are allowed (confidence 9/10)
Co-Authored-By: unknown <>
* fix(analytics): enforce domain label boundaries and prevent consecutive dots
Co-Authored-By: pedro@cal.com <pedro.castro@ideware.com.br>
* refactor: extract shared validation schemas
- Create analytics-schemas.ts with reusable safeUrlSchema, alphanumericIdSchema, and numericIdSchema
- Update databuddy, insihts, matomo, plausible, posthog, and umami to use shared schemas
- Keep app-specific schemas local (domain validation, UUID patterns, etc.)
* fix(analytics): enforce exactly 10 characters for GA4 Measurement ID
Tighten GA4 regex from {1,20} to {10} to match the documented format
G-XXXXXXXXXX. This addresses Cubic AI review feedback (confidence 9/10)
that the regex was too permissive compared to the error message.
Co-Authored-By: unknown <>
* refactor: add createPrefixedIdSchema factory for GTM/GA4/Fathom
- Add factory function to handle prefixed IDs with configurable options
(prefix, addPrefixIfMissing, allowEmpty)
- Consolidate GTM, GA4, and Fathom schemas using the shared factory
- Standardize imports to use @calcom/app-store alias
* fix: reject prefix-only IDs like "G-" or "GTM-" without content
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add updateCurrentTimeOnFocus prop to Calendar component that enables
the current time indicator to update when the page is refocused.
This helps users see the accurate current time after switching tabs.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
When the routing form redirects to an event type, it now passes
cal.crmLookupDone=true to indicate that the CRM contact owner lookup
was already performed. The event type SSR checks for this flag and
skips the duplicate CRM lookup, improving performance.
This fixes the scenario where no CRM contact owner exists - previously
the lookup would be performed twice (once in the router, once in SSR),
but now it's only performed once.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Reset queue correctly all places
* test: verify UI config persists on second modal open (#27422)
* test: verify UI config persists on second modal open
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: update iframe selection logic in getEmbedIframe function
Refactor the getEmbedIframe function to improve iframe selection by using a more specific selector and ensuring the last iframe is targeted in cases of repeated modal openings. This change enhances reliability in iframe handling during tests.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix test
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: split flag repositories into Prisma and Cached layers
- Rename FeatureRepository to PrismaFeatureRepository (raw DB access)
- Rename TeamFeatureRepository to PrismaTeamFeatureRepository (raw DB access)
- Rename UserFeatureRepository to PrismaUserFeatureRepository (raw DB access)
- Create CachedFeatureRepository with @Memoize wrapping PrismaFeatureRepository
- Create CachedTeamFeatureRepository with @Memoize/@Unmemoize wrapping PrismaTeamFeatureRepository
- Create CachedUserFeatureRepository with @Memoize/@Unmemoize wrapping PrismaUserFeatureRepository
- Update DI tokens, modules, and containers for all 6 repositories
- Update imports in FeatureOptInService and related modules
- Update tests to use new repository structure
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: simplify @Memoize key patterns and delegate batch methods to Prisma
- Use direct function references for @Memoize key (e.g., KEY.all instead of () => KEY.all())
- Simplify batch methods in Cached repositories to delegate to Prisma repository
- Update tests to reflect the new delegation pattern
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add orderBy to TeamRepository.findAllByParentId for deterministic results
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: update TeamRepository test to expect orderBy in findAllByParentId
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: cleanup features repository and add specialized repository methods (#27195)
* refactor: cleanup features repository and add findBySlug, update methods
- Remove unused methods from FeaturesRepository (keep getTeamsWithFeatureEnabled)
- Add findAll(), findBySlug(), update() to IFeatureRepository interface
- Add findAll() with caching to CachedFeatureRepository
- Add findBySlug() with caching to CachedFeatureRepository
- Add update() with Unmemoize to CachedFeatureRepository
- Add checkIfFeatureIsEnabledGlobally() to CachedFeatureRepository
- Update toggleFeatureFlag.handler.ts to use repository instead of raw Prisma
- Add comprehensive unit tests for all new methods
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: update updatedAt timestamp in feature update method
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: move feature check methods to specialized repositories
- Replace getUserFeaturesStatus with two checkIfUserHasFeature calls in bookings page
- Move checkIfTeamHasFeature to PrismaTeamFeatureRepository with pass-through in CachedTeamFeatureRepository
- Move checkIfUserHasFeature and checkIfUserHasFeatureNonHierarchical to PrismaUserFeatureRepository with pass-throughs in CachedUserFeatureRepository
- Add getEnabledFeatures to PrismaTeamFeatureRepository with caching in CachedTeamFeatureRepository
- Keep FeaturesRepository methods as pass-throughs for backward compatibility
- Update test to expect updatedAt in feature update
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: remove getUserFeaturesStatus and unused methods from FeaturesRepository
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* restore comment
* fix: invalidate all-features cache on update and enabledFeatures cache on upsert/delete
- CachedFeatureRepository: Add KEY.all() to @Unmemoize keys in update() to prevent stale findAll() results
- CachedTeamFeatureRepository: Add KEY.enabledFeatures(teamId) to @Unmemoize keys in upsert() and delete() to prevent stale getEnabledFeatures() results
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add CachedUserFeatureRepository tests
Add comprehensive tests for CachedUserFeatureRepository covering:
- findByUserIdAndFeatureId (cache hit, cache miss, not found)
- findByUserIdAndFeatureIds (empty input, multiple features)
- upsert (with cache invalidation)
- delete (with cache invalidation)
- findAutoOptInByUserId (cache hit, cache miss, not found)
- setAutoOptIn (with cache invalidation)
Co-Authored-By: unknown <>
* test: remove tests for methods removed from FeaturesRepository
Remove integration tests for methods that were intentionally removed:
- getUserFeatureStates
- getTeamsFeatureStates
- getUserAutoOptIn
- getTeamsAutoOptIn
- setUserAutoOptIn
- setTeamAutoOptIn
Co-Authored-By: unknown <>
* avoid N+1 query
* refactor: add select clauses to PrismaFeatureRepository queries
- Add explicit select clauses to findAll, findBySlug, and update methods
- Only fetch fields needed for FeatureDto (slug, enabled, description, type, stale, lastUsedAt, createdAt, updatedAt, updatedBy)
- Update tests to expect select clauses
- Fix UserFeatureRepository test to use findMany mock
Co-Authored-By: unknown <>
* fix bad conflict resolved
* use userId
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: move shared components from apps/web to packages/features
Move components that don't require dependency injection:
- DisconnectIntegrationModal
- Booking components (Header, Section, TimeFormatToggle, PayIcon, Price)
- useInitializeWeekStart hook
- TeamEventTypeForm
- Event type components (AssignAllTeamMembers, BulkEditDefaultForEventsModal, etc.)
- Event type dialogs (HostEditDialogs, ManagedEventDialog)
- Location components (LocationInput, types)
- Tab components (EventLimitsTab, EventRecurringTab, etc.)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: update import paths to use @calcom/features
Update imports in apps/web and packages/platform/atoms to reference
the moved components from @calcom/features instead of @calcom/web.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: delete original files from apps/web/modules
Complete the file move by deleting the original files that were
copied to packages/features. This makes it a proper move instead
of a copy, reducing the PR size significantly.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: update import paths to use @calcom/features for moved files
Update import paths in apps/web files that reference components
that were moved from apps/web/modules to packages/features:
- LearnMoreLink
- ChildrenEventTypeSelect
- AssignAllTeamMembers
- WeightDescription
- LocationCustomClassNames (types)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: update additional import paths for moved components
Update import paths in apps/web files that reference components
moved from apps/web/modules to packages/features:
- AppList.tsx: BulkEditDefaultForEventsModal
- schedule-view.tsx: BulkUpdatParams type
- AddMembersWithSwitch.tsx: AssignAllTeamMembers, CheckedTeamSelect
- EventTypeWebWrapper.tsx: ChildrenEventType, ManagedEventDialog
- DefaultLocationSettings.tsx: LocationCustomClassNames, LocationInput
- Locations.tsx: LocationCustomClassNames, LocationInput
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: resolve circular dependency by adding isPlatform prop to Header
This fixes the circular dependency where @calcom/atoms (CalendarViewComponent)
was importing Header from @calcom/features, but Header was importing
useIsPlatform from @calcom/atoms.
The fix adds an isPlatform prop to the Header component so it no longer
needs to import useIsPlatform from @calcom/atoms. Callers now pass the
isPlatform value directly:
- atoms components pass isPlatform={true}
- web components pass the isPlatform prop they receive
Fix confidence: 9/10 (Cubic AI)
Co-Authored-By: unknown <>
* fix
* fix
* fix
* fix
* fix
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: handle email invites in team event-type assignment by preventing NaN userId.
* fix(i18n): improve French translation for login and identifier
Updating 'Connexion' to 'Se connecter' for consistency with Google/SAML login buttons and improving the identifier label.
* Update apps/web/public/static/locales/fr/common.json
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* revert: remove team assignment logic fix to focus on translations.
---------
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
The incrementDate function was using .day() incorrectly, which sets the day
of the week for the CURRENT week rather than finding the same weekday in
the next month. This caused the function to return dates in the previous
month when the target date fell early in the week.
For example, when incrementing March 2, 2026 (Monday) by a month:
- Old behavior: April 2 (Thursday) + .day(1) = March 30 (wrong month!)
- New behavior: First Monday in April = April 6 (correct)
This bug caused flaky E2E test failures in booking-limits and duration-limits
tests because the 'year limit with multiple limits set' test would try to
book on a date that was still in the same month, which was blocked by the
month limit (3 bookings already existed in that month).
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add guest limits and rate limiting to booking-guests endpoint
- Add ArrayMaxSize(10) validation to limit guests per request to 10
- Add aggressive rate limiting (5 requests/minute) via @Throttle decorator
- Add total guest limit check (max 30 guests per booking) to prevent abuse
- Update API documentation to reflect new limits
This prevents scammers from using the endpoint to send spam emails
to hundreds of guests through our system.
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* docs: update openapi.json with guest limits and rate limiting info
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(salesforce): add field rules for round robin skip
- Add rrSkipFieldRules schema to appDataSchema in zod.ts
- Implement applyFieldRules method in CrmService.ts for post-query filtering
- Integrate field rules into getContacts method when forRoundRobinSkip=true
- Add UI component for configuring field rules in EventTypeAppCardInterface
- Add translations for new UI strings
- Add comprehensive tests for field rules functionality
Field rules allow users to specify conditions based on Salesforce record fields
with 'ignore' or 'must_include' actions. Rules are evaluated with AND logic
and gracefully handle missing fields by skipping those rules.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add children prop to Section.SubSectionHeader for field rules
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat(salesforce): add edit button for field rules
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat(salesforce): apply field rules to GraphQL results
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat(salesforce): add Redis caching for field validation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix(salesforce): remove Redis caching to fix ERR_INVALID_THIS error
- Removed Redis caching for field validation that was causing ERR_INVALID_THIS errors
- Simplified field rules filtering to try query directly and skip filtering if it fails
- Restored ensureFieldsExistOnObject method for other uses (write to record)
- Field rules now gracefully handle invalid fields by skipping filtering
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: reformat getAttributes.ts
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(salesforce): dynamically build GraphQL query for field rules with multi-edge iteration
- Add buildDynamicAccountQuery to inject field rule fields into GraphQL query
- Add passesFieldRules to evaluate ignore/must_include rules against UIAPI nodes
- Validate field rules via getObjectFieldNames (in-memory + Redis cache) before branching
- Remove applyFieldRulesToGraphQLRecords (used jsforce conn in GraphQL path)
- Iterate all edges in Tiers 1 and 2 instead of only the first result
- Rank Tier 3 accounts by contact count and fallback to next on field rule failure
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(salesforce): add field rule routing trace steps
Add trace calls alongside field rule info logs for routing visibility:
- fieldRulesValidated: records validation result in CrmService
- fieldRuleFilteredRecord: records when account is filtered at each tier
- fieldRuleEvaluated: records individual rule evaluation details
- allRecordsFilteredByFieldRules: records when SOQL records are all filtered
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(salesforce): remove duplicate field validation and fix disabled Select options
applyFieldRules now receives pre-validated rules from the early
validation block instead of re-validating internally. Also adds
the missing options prop to the disabled Select in the field rules UI.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat(salesforce): include failed rule details in field rule trace steps
Rename passesFieldRules to getFailingFieldRule so the caller receives
the specific rule that caused filtering. The fieldRuleFilteredRecord
trace step now includes failedRule with field, value, and action.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test(salesforce): add GraphQL path tests for field rules
Cover field rule filtering across all three GraphQL resolution tiers
(contact, account, related contacts) with ignore and must_include
actions, multi-edge fallback, case-insensitivity, and dynamic query
selection.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Abstract new field rules settings
* Reduce extra SOQL call
* fix(salesforce): fix type errors in CrmService and GraphQL client
- Add missing import for getRedisService from @calcom/features/di/containers/Redis
- Fix ensureFieldsExistOnObject to properly return Field[] instead of incomplete function
- Use Array.from() instead of spread operator for Set and Map iterators to fix downlevelIteration errors
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: move Booker hooks from packages/features to apps/web/modules
Migrate the following Booker hooks:
- useOverlayCalendar
- useSkipConfirmStep
- useInitializeWeekStart
- useIsQuickAvailabilityCheckFeatureEnabled
- useDecoyBooking
These hooks are only imported by apps/web files, making them safe to move
without creating circular dependencies.
Part of the tRPC-driven UI migration from packages/features to apps/web/modules.
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: revert useIsQuickAvailabilityCheckFeatureEnabled move and fix import paths
- Reverted useIsQuickAvailabilityCheckFeatureEnabled.ts back to packages/features since it's imported by useSlots.ts in packages/features (would create circular dependency)
- Fixed import paths in useOverlayCalendar.ts to use @calcom/features paths
- Fixed import paths in useSkipConfirmStep.ts to use @calcom/features paths
- Updated Booker.tsx to import useIsQuickAvailabilityCheckFeatureEnabled from original location
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix: use @calcom/web alias for hook imports to fix vitest resolution
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor
* migrate more hooks
* fix
* mv types
* fix
* fix
* fix
* fix
* revert: remove formatting-only changes to make PR easier to review
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* fix
* fix
* revert: restore original import ordering in moved hooks
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix imports
* fix
* fix
* wip
* wip
* wip
* wip
* wip
* wip
* fix: add isBrowser guard to useSlotsViewOnSmallScreen hook
Address Cubic AI review feedback (confidence 9/10) by adding the isBrowser
guard to the useSlotsViewOnSmallScreen hook to follow the file's client-only
contract and avoid executing during SSR/prerendering.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When the eligibility check returns "already_enabled", store this in
local storage to prevent repeated API calls on subsequent page loads.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add tooltip for disabled Cancel button on past bookings
Show a tooltip explaining "You cannot cancel a past booking" when hovering over the disabled Cancel dropdown item for bookings that have already occurred.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
* refactor: Extract tooltip condition into named variable
Improves readability by extracting the tooltip open condition into a descriptive variable `showPastBookingCancelTooltip`.
Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Haiku 4.5 <noreply@anthropic.com>
* feat: TOUTOC in handleSeats
* fix type check
* refactor: replace Prisma include with select in integration tests
Address Cubic AI review feedback (confidence 9/10) to use select instead
of include in Prisma queries to limit returned fields and avoid pulling
full related records.
Co-Authored-By: unknown <>
* fix seat count
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Add menuPlacement="bottom" to the reason Select in the OOO modal
to ensure dropdown opens downwards consistently with other selects.
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
The previous implementation relied on the hosts array returned from
findTeamEventTypes, which is limited to 5 hosts for display purposes.
This caused the 'Assigned' badge to not show for users who were hosts
but not in the first 5 returned by the query.
This fix queries the Host table directly with a single batch query to
get all event type IDs where the current user is a host, ensuring the
badge shows correctly regardless of how many hosts an event type has.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ali@cal.com <ali@cal.com>
Adds encrypted credential storage using a new keyring system:
- New `encryptedKey` column with AES-256-GCM encryption
- Decryption in getCalendarsEvents with fallback to legacy `key`
- buildCredentialCreateData service for credential creation
- Phase 1: Google Calendar only, other integrations follow
* feat(companion): add dark mode support using NativeWind system preference
- Set userInterfaceStyle to 'automatic' in app.json to follow system preference
- Add useColorScheme hook from NativeWind to detect color scheme
- Update root layout with dynamic colors for StatusBar, containers, and modals
- Update tabs layout with dynamic colors for tab bar and icons
- Update all Stack.Screen options to use dynamic background colors and blur effects
- Support both iOS and Android with appropriate dark mode colors
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): use NativeWind dark: variant classes for automatic dark mode
Use dark: Tailwind variant classes instead of conditional logic based on
useColorScheme for the container background. This ensures NativeWind
automatically applies dark mode styles based on system preference.
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix(companion): use darkMode 'media' for automatic dark mode detection
- Changed darkMode from 'class' to 'media' in tailwind.config.js
- Updated global.css to use @media (prefers-color-scheme: dark) instead of .dark selector
- This ensures NativeWind properly detects system color scheme on iOS/Android
Co-Authored-By: peer@cal.com <peer@cal.com>
* dark mode starting
* feat(companion): add dark mode support to core infrastructure (Phase 1)
* feat(companion): add dark mode support to tab pages (Phase 2)
* fix(companion): add dark mode support to booking list components for font visibility
* fix(companion): add dark mode support to EventTypeListItem components
* event types and bookings page
* Availability list page
* more page, and some ui fixes
* feat(companion): add dark mode support to detail screens and modals
- BookingDetailScreen (iOS & Android): dynamic colors for all UI elements
- AvailabilityDetailScreen (iOS & Android): dark mode backgrounds and text
- profile-sheet (iOS & Android): dark mode support for profile modal
- RescheduleScreen (all platforms): dark mode for date/time pickers
- EditLocationScreen (iOS & Android): dark mode for location editor
- AddGuestsScreen: partial dark mode implementation (in progress)
Uses iOS system colors: #000000 (black), #1C1C1E (secondary), #FFFFFF (white), #38383A (border), #8E8E93 (text secondary)
* fix typecheck and lint
* feat(companion): add dark mode support to remaining screens
- ScreenWrapper: Fix hardcoded #800020 color with dynamic destructive color
- EditAvailabilityDayScreen: Add dark mode for Android/Web and iOS
- EditAvailabilityHoursScreen: Add dark mode for Android/Web and iOS
- EditAvailabilityNameScreen: Add dark mode for Android/Web and iOS
- EditAvailabilityOverrideScreen: Add dark mode for Web, iOS, and Android
- MarkNoShowScreen: Add dark mode with dynamic colors for no-show states
- MeetingSessionDetailsScreen: Add dark mode for session details
- ViewRecordingsScreen: Add dark mode for recordings list
Uses iOS system colors (#000000, #1C1C1E, #38383A, #8E8E93) and
useColorScheme hook for automatic theme detection.
* fix(companion): fix iOS modal dark mode backgrounds for transparent glass UI
* fix(companion): fix dark mode for booking action screens in transparent background mode
* fix(companion): fix Android dark mode for transparent background in EditAvailabilityOverrideScreen
* refactor(companion): centralize dark mode colors and align with main website
- Update tailwind.config.js with centralized color definitions
- Create constants/colors.ts for inline style color references
- Replace iOS system colors with main website neutral grays:
- #1C1C1E -> #171717 (secondary dark background)
- #38383A -> #4D4D4D (dark border)
- #8E8E93 -> #A3A3A3 (secondary text dark)
- Keep pure black (#000000) for main backgrounds (OLED friendly)
- Add semantic color mappings for consistent UI styling
* feat(companion): complete dark mode implementation across all priority files
- Priority 1: Updated SettingsUI.tsx, BasicsTab.tsx, LimitsTab.tsx, AdvancedTab.tsx, RecurringTab.tsx, AvailabilityTab.tsx, event-type-detail.tsx
- Priority 2: Updated edit-availability-hours/name/override/day.tsx, oauth/callback.tsx
- Priority 3: Updated AvailabilityListScreen.tsx with dark mode for modals and list items
- Priority 4: Updated BookingActionsModal.tsx, BookingActionsModal.ios.tsx, GlassModalHeader.tsx, LoginScreen.tsx, LocationsList.tsx, SearchHeader.tsx, toast.tsx
Dark mode colors used:
- Main bg: #000000 (cal.bg.dark)
- Secondary bg: #171717 (cal.bg.secondaryDark)
- Muted bg: #262626 (cal.bg.mutedDark)
- Borders: #4D4D4D (cal.border.dark)
- Secondary text: #A3A3A3 (cal.text.secondaryDark)
* fix(companion): add dark mode support to iOS header, GlassView modals, and main content background
* fix(companion): add dark mode support to toggle/switch components
* fix(companion): use iOS green (#34C759) for toggle ON state in dark mode
* fix(companion): use white track + black thumb for dark mode toggles
* fix(companion): add dark mode support to login page logo and button
* dark mode to many pages, centralize the colors
* dark mode skelaton
* Availability pages android
* toggles
* headers
* red color for dark mode
* copy button
* white flash on bottom ui native ios sheet
* bottom sheet buttons
* bottom sheet in gray color
* bottom sheet again.. yeahh
* toggle green
* toggle green
* fix lint and typechecks
* fix exntension error
* Update companion/components/event-type-detail/tabs/LimitsTabDatePicker.android.tsx
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* address cubic comments
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: peer@cal.com <peer@cal.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: allow wildcard prefix in normalizeDomain function
The normalizeDomain function now accepts domains with *. prefix
(e.g., *.cal.com) for wildcard domain matching in the watchlist.
The domain part after *. is validated using the existing regex.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: allow wildcard domains in validation checks
Update WatchlistOperationsService and CreateBlocklistEntryModal to
accept wildcard domain prefixes (*.) in domain validation. This ensures
wildcard domains like *.cal.com can be added through both the API and UI.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Remove 7 environment variables no longer referenced in the codebase:
- NEXT_PUBLIC_CONSOLE_URL
- NEXT_PUBLIC_STRIPE_PRICING_TABLE_PUBLISHABLE_KEY
- NEXT_PUBLIC_PLAIN_CHAT_EXCLUDED_PATHS
- NEXT_PUBLIC_VERCEL_ENV
- NEXT_PUBLIC_VERCEL_BRANCH_URL
- PRISMA_FIELD_ENCRYPTION_KEY
- PRISMA_GENERATE_DATAPROXY
Deduplicate 6 entries that appeared twice in globalEnv:
- CALCOM_CREDENTIAL_SYNC_SECRET
- INTERCOM_SECRET
- NEXT_PUBLIC_ORG_SELF_SERVE_ENABLED
- ORG_MONTHLY_CREDITS
- STRIPE_ORG_PRODUCT_ID
- VAPID_PRIVATE_KEY
Also removes the unused vars from .env.example.
Reduces globalEnv from 321 to 308 entries, lowering the surface
for unnecessary Turbo cache invalidation.
* feat: add wildcard domain matching to watchlist
When a domain is blocked in the watchlist (e.g., cal.com), all subdomains
are now also blocked (e.g., app.cal.com, sub.app.cal.com).
Changes:
- Add getParentDomains() utility to extract all parent domains
- Update GlobalBlockingService.isBlocked() and areBlocked() for wildcard matching
- Update OrganizationBlockingService.isBlocked() and areBlocked() for wildcard matching
- Add comprehensive tests for wildcard domain matching
- More specific subdomain matches take precedence over parent domain matches
- Email matches still take precedence over domain matches
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: make wildcard domain matching configurable
- *.cal.com blocks all subdomains (app.cal.com, sub.app.cal.com, etc.)
- cal.com only blocks exact matches (not subdomains)
- Updated getWildcardPatternsForDomain to generate wildcard patterns
- Added domainMatchesWatchlistEntry for configurable matching logic
- Updated GlobalBlockingService and OrganizationBlockingService
- Updated tests to verify new behavior
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: simplify wildcard pattern generation to strip only first part
- Only strips the first part before the first dot
- Handles multi-part TLDs correctly (e.g., .co.uk)
- Returns empty array for two-part domains (e.g., cal.com -> no pattern)
- app.cal.com -> *.cal.com
- bloody-hell.cal.co.uk -> *.cal.co.uk
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Add routing trace presenters
Add domain-specific presenters (SalesforceRoutingTracePresenter,
RoutingFormTracePresenter) that format trace steps into human-readable
strings, and a core RoutingTracePresenter that delegates to them based
on step domain. Includes unit tests for all presenters.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add findByBookingUid to RoutingTraceRepository
Add method to look up a routing trace by booking UID, needed by the
routing trace presenter tRPC endpoint.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add getRoutingTrace tRPC endpoint
Expose routing trace data for a booking via viewer.bookings.getRoutingTrace.
Tries the permanent RoutingTrace first (round robin bookings), then falls
back to PendingRoutingTrace via the booking's form response relation.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Add routing trace side sheet to booking list item
Add a route icon button on booking list items that came from routing
forms. Clicking it opens a side sheet displaying the full routing trace
as human-readable steps. Adds RoutingTraceSheet component, store state,
and translation key.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: Move routing trace action to dropdown menu
Move the routing trace button from a standalone icon on the booking list
item into the actions dropdown menu alongside "Report wrong assignment".
The RoutingTraceSheet is now rendered from BookingActionsDropdown.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Improve routing trace UI and handle unnamed routes
- Redesign RoutingTraceSheet with vertical timeline layout, domain badges,
skeleton loading state, and millisecond timestamps
- Use Salesforce app-store icon for Salesforce steps
- Fall back to "Unnamed route" when route name is missing or matches route ID
- Fix dropdown menu icon to git-merge (valid icon, unique in menu)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: Add tests for getRoutingTrace handler and findByBookingUid repository method
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Abstract functions
* Fix build error
* Add permission check
* fix: Update getRoutingTrace tests to include ctx and mock BookingAccessService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Replaces direct instantiation of `FeaturesRepository` with the dependency-injected version using `getFeaturesRepository()` from the DI container in the feature flags router.
**Key improvement:** The DI-ed version uses Redis caching, replacing the previous in-memory 5-minute cache implementation which was not ideal. This follows the established DI pattern in the codebase for loose coupling, better testability, and more robust caching across instances.
## Type of change
- [x] Refactor (non-breaking change that improves code quality)
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - no documentation changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Verify feature flags functionality works as expected:
- Feature flag list endpoint should return all features
- Team feature check should work correctly
2. No environment variables needed beyond standard setup
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have checked if my changes generate no new warnings
## Human Review Checklist
- [ ] Verify `getFeaturesRepository()` returns a properly configured instance from the DI container
- [ ] Confirm the DI container properly injects the Prisma client
- [ ] Verify Redis caching is working as expected for feature flag data
---
**Link to Devin run:** https://app.devin.ai/sessions/2ff6e4551c8649e4b6178b31a2195327
**Requested by:** @eunjae-lee
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Replace hardcoded color with semantic text-success class
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove color class completely from display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: implement webhook tasker with async/sync fallback
This PR implements a webhook tasker with async/sync fallback architecture
to fix failing E2E tests. The solution follows the existing proration
tasker pattern and uses Dependency Injection with @evyweb/ioctopus.
Key changes:
- Create IWebhookTasker interface for webhook delivery
- Implement WebhookSyncTasker for immediate execution (E2E tests)
- Implement WebhookAsyncTasker for queued execution (production)
- Create main WebhookTasker class extending Tasker<IWebhookTasker>
- Add DI modules and tokens for all tasker components
- Update WebhookTaskerProducerService to use new WebhookTasker
- Add unit tests for sync and async taskers
The ENABLE_ASYNC_TASKER flag automatically selects the appropriate mode:
- Production: Uses WebhookAsyncTasker to queue tasks
- E2E Tests: Uses WebhookSyncTasker for immediate execution
This ensures webhooks are delivered immediately in E2E tests without
requiring the cron job that processes queued tasks.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: update WebhookTaskerProducerService tests for new interface
Update tests to use the new deps-based constructor and
mockWebhookTasker.deliverWebhook instead of mockTasker.create
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: use moduleLoader pattern for WebhookProducerService in container
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* refactor: replace InternalTasker with Trigger.dev for webhook delivery
This commit refactors the WebhookTasker to use Trigger.dev instead of
InternalTasker, following the pattern established in BookingEmailAndSmsTasker
and PlatformOrganizationBillingTasker (PR #26803).
Changes:
- Replace WebhookAsyncTasker with WebhookTriggerTasker that uses trigger.dev
- Create trigger.dev task files (deliver-webhook.ts, config.ts, schema.ts)
- Update DI modules to use WebhookTriggerTasker
- Remove old InternalTasker-based implementation
- Update unit tests for new implementation
The WebhookSyncTasker continues to execute webhooks immediately for E2E tests
where ENABLE_ASYNC_TASKER is automatically false.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: update comments to reflect Trigger.dev usage instead of InternalTasker
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add invoice URL to proration and test seed scripts
- Add invoiceUrl field to MonthlyProration schema
- Update billing service to return hosted_invoice_url after finalizing
- Save invoice URL when creating proration invoices
- Add seed script for testing proration with real Stripe data
- Add cleanup script for test data removal
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add due invoice banner and invitation blocking for orgs
- Add DueInvoiceBanner component showing overdue prorations
- Add DueInvoiceService for checking blocking status and banner data
- Block invitations when proration invoices are 7+ days overdue
- Allow sub-team invites for existing org members (exception)
- Show banner to users with billing management permissions
- Link directly to Stripe invoice URL when available
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: set subscriptionId in team metadata in seed script
The seed script was not setting subscriptionId on the team metadata,
causing checkIfOrgNeedsUpgrade to treat the org as needing upgrade
and showing the "trialing" banner.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* push trigger test to script
* fix mocks
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* fix: preserve UI config across iframe resets for element-click embeds
For element-click embeds, ui() is called before the iframe exists, and
iframeReset() clears the queue when the modal is opened. This causes UI
config like hideEventTypeDetails, cssVarsPerTheme, and styles to be lost.
This fix stores UI config in a persistent property that survives iframe
resets and applies it when the iframe becomes ready.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add assertion to verify hideEventTypeDetails config is applied in element-click embed
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add mergeUiConfig function and related tests for UI configuration merging
- Introduced mergeUiConfig function to handle merging of UI configuration, specifically for cssVarsPerTheme at theme level.
- Added comprehensive tests for mergeUiConfig covering various scenarios including merging themes, handling missing properties, and preserving existing configuration values.
- Updated utils.test.ts to include tests for the new function and ensure correct behavior in different merging situations.
* Fixes
* Remove process import as it breaks embed.js
* Simplify
* fix name
* fix name
* fix: use forEach instead of for...of for URLSearchParams iteration
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* feat: add organization.passwordReset PBAC permission
Allow org admins/owners to reset passwords for members of their
organization via a new PBAC permission. Previously this was only
available to system-level admins.
- Add PasswordReset to CustomAction enum and PERMISSION_REGISTRY
- Create migration to grant permission to admin_role (owner has wildcard)
- Add org-scoped tRPC endpoint using createOrgPbacProcedure
- Handler validates org membership, prevents self-targeting, and blocks
resetting owner passwords
- Wire permission through MemberPermissions, getOrgMembersPageData, and
the org members table UI dropdown
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: use targeted select in org password reset to avoid over-fetching
Replace findById with findForPasswordReset repository method that only
selects email, name, and locale instead of the full userSelect which
includes sensitive fields like twoFactorSecret and backupCodes.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add unit tests for sendPasswordReset handler
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
## What does this PR do?
Adds a `displayLocations` property to the `OptInFeatureConfig` interface to control where opt-in features are displayed (settings page, banner, or both), and applies this filtering at the callers' side.
## How to test locally
```
update "Feature" set enabled = true where slug='bookings-v3';
```
Run it to globally enable the flag, and then go to the settings page to see the "Features" menu.
---
**Changes:**
- Added `OptInFeatureDisplayLocation` type with values `"settings"` | `"banner"`
- Added optional `displayLocations` property to `OptInFeatureConfig` interface
- Added helper functions:
- `getFeatureDisplayLocations()` - returns display locations with default of `['settings']`
- `shouldDisplayFeatureAt()` - checks if a feature should display at a specific location
- `getOptInFeaturesForLocation()` - filters features by location
- Updated `getOptInFeaturesForScope()` to accept an optional `displayLocation` parameter for filtering
- Simplified `HAS_*_OPT_IN_FEATURES` constants to use `getOptInFeaturesForScope(scope, "settings").length > 0`
- Updated `FeatureOptInService.listFeaturesForUser()` to filter by 'settings' location
- Updated `FeatureOptInService.listFeaturesForTeam()` to filter by 'settings' location
- Updated `useFeatureOptInBanner` hook to check for 'banner' location before showing
**Default behavior:** If `displayLocations` is omitted, features default to `['settings']` only.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - internal config change only.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Verify the helper functions work as expected:
- `getFeatureDisplayLocations({ slug: "test", ... })` should return `["settings"]` (default)
- `getFeatureDisplayLocations({ slug: "test", displayLocations: ["banner"] })` should return `["banner"]`
- `shouldDisplayFeatureAt(feature, "settings")` should return `true` for features without `displayLocations`
- `getOptInFeaturesForScope("user", "banner")` should only return user-scoped features with `"banner"` in their `displayLocations`
2. Verify caller-side filtering:
- `listFeaturesForUser()` and `listFeaturesForTeam()` should only return features with 'settings' in displayLocations
- Banner hook should only show features with 'banner' in displayLocations
- `HAS_*_OPT_IN_FEATURES` constants should only be true if there are features with 'settings' location
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] My changes generate no new warnings
---
### Human Review Checklist
- [ ] Verify the default behavior (defaulting to `['settings']`) matches requirements
- [ ] Confirm the filtering logic is applied correctly in all callers (service methods, banner hook, constants)
- [ ] Verify the banner hook correctly prevents showing features without 'banner' in displayLocations
- [ ] Note: The test mock returns all features regardless of `displayLocation` parameter - the filtering logic in `getOptInFeaturesForScope` isn't directly tested. Consider if this is acceptable or if tests should be added.
**Link to Devin run:** https://app.devin.ai/sessions/a064ee43a56d458caf2892b55959f1ea
**Requested by:** @eunjae-lee
* fix: exclude third-party video apps from areCalendarEventsEnabled setting
When areCalendarEventsEnabled is false, video meeting creation for third-party
video apps (like Daily.co) was being skipped, causing the meetingUrl field in
webhook payloads to contain the location identifier (integrations:daily) instead
of the actual video meeting URL.
This fix adds a skipCalendarEvent option to EventManager.create() that:
- Skips calendar event creation when true
- Skips CRM event creation when true (CRM events are tied to calendar events)
- Still creates video meetings for third-party video apps
The calling code in handleConfirmation.ts and RegularBookingService.ts now uses
this option when areCalendarEventsEnabled is false, ensuring video meetings are
still created while respecting the platform customer's preference to manage
their own calendar events.
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: update E2E tests to verify skipCalendarEvent option is passed when areCalendarEventsEnabled is false
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: address PR review feedback - remove comments and update E2E tests to spy on createAllCalendarEvents
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: add spy for createAllCRMEvents in e2e tests when areCalendarEventsEnabled is false
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* test: add missing spy for createAllCRMEvents in beforeEach hook
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use videoCallUrl from metadata for webhook payload location
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* fix: simplify webhook location to use metadata?.videoCallUrl || evt.location
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* docs: add description about video call link limitations when calendar events disabled
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* i18n: add translation key for calendar events disabled video limitation
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* fix: update translation text to accurately reflect video app behavior
Co-Authored-By: lauris@cal.com <lauris.skraucis@gmail.com>
* Update common.json
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
## What does this PR do?
Updates the embed lifecycle documentation to clarify that `bookerReopened` and `bookerReloaded` events are only applicable for prerendered modals that are now visible.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - this PR is itself a documentation update.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works. N/A - documentation only change.
## How should this be tested?
This is a documentation-only change. Review the accuracy of the added "Applicability" notes for both events.
## Checklist
- [x] I have read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- [x] My code follows the style guidelines of this project
- [x] I have checked if my changes generate no new warnings
---
### Reviewer Checklist
- [ ] Verify that the documentation accurately reflects when `bookerReopened` and `bookerReloaded` events fire (only for prerendered modals)
---
> [!NOTE]
> **Link to Devin run**: https://app.devin.ai/sessions/35a0c5d4d5fc4feb9a5af2d8d340b9c5
> **Requested by**: @hariombalhara
* refactor: join teams with feature enabled directly in findNextSubscriptionBatch query
- Changed findNextSubscriptionBatch to accept featureId instead of teamIds
- Join TeamFeatures table directly in the query instead of separate queries
- Updated CalendarSubscriptionService.checkForNewSubscriptions to pass featureId
- Removed separate getTeamsWithFeatureEnabled call
- Updated tests to reflect the new query structure
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: change featureId to featureIds array for flexibility
- Changed findNextSubscriptionBatch to accept featureIds array instead of single featureId
- Updated Prisma query to use 'in' operator for multiple feature IDs
- Updated CalendarSubscriptionService to pass array with single feature
- Updated all tests to use featureIds array
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add DB schema
* Init zod schema
* Init RoutingTrace and PendingRoutingTrace repository interfaces
* Create PrismaPendingRoutingTraceRepository
* Init RoutingTraceService
* Create RoutingTraceService container
* User routing trace service in routing
* Create RoutingTraceRepository and PrismaRoutingTraceRepoistory
* Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository
* Update DI containers
* RoutingTraceService create process booking method
* Use pending routing trace rather than URL params
* Fix schema
* Fix writing assignment reason for routed booking
* Remove from service
* Refactor RoutingTraceService to not rely on async local storage
* Pass RoutingTraceService through routing call
* Add attribute-logic-evaluated to routing trace step
* Add routing trace to trpc endpoint
* Add CRM routing trace step
* Fix extracting routing trace to assignment reason
* Add back CRM params to prevent refetching
* test: Add unit tests for RoutingTraceService and repositories
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: Add missing mock for RoutingTraceService in getRoutedUrl tests
Also fix pre-existing lint issues in the test file:
- Add explicit types to mockForm and mockSerializableForm variables
- Add explicit type to url parameter in mockContext
- Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>'
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Link pending form submission to routing trace
* Clean up
* Add lookup field assignment reasons
* Rename to PendingRoutingTrace
* Add migration file
* fix: Update RoutingTraceService tests to use assignmentReasonRepository mock
- Add getStepsCount() method back to RoutingTraceService
- Add queuedFormResponseId support to processForBooking method
- Update tests to use mockAssignmentReasonRepository instead of prisma mock
- Remove test for missing routingTraceRepository (all deps now required)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Remove PII (organizer email) from log payload in RoutingTraceService
Addresses Cubic AI review feedback with confidence 9/10.
Logging PII violates sensitive information logging rules.
Co-Authored-By: unknown <>
* Write field values at the time of routing
* Write attributes used to route
* feat: add CRM routing trace service
Add CrmRoutingTraceService as a reusable wrapper around RoutingTraceService
for CRM-specific tracing. Also adds CrmRoutingTraceServiceInterface type
to support passing trace services through the CRM call chain.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add Salesforce routing trace infrastructure
Add SalesforceRoutingTrace static class with 19 trace methods covering:
- Account resolution (SOQL path): searching by website, contact domain
- Lookup field queries
- Owner lookups (contact, lead, account)
- Validation and skip scenarios
- GraphQL three-tier resolution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: wire CRM trace service through call chain
Pass crmTrace parameter through the CRM call chain:
- routerGetCrmContactOwnerEmail creates CrmRoutingTraceService
- Passes to app booking form handlers and CRM round robin skip
- CrmManager.getContacts accepts and forwards crmTrace
- All handlers accept optional crmTrace parameter
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: add trace instrumentation to Salesforce CRM service
Instrument Salesforce CRM methods with routing trace steps:
- getContacts: trace owner lookups for contact/lead/account
- getAccountIdBasedOnEmailDomainOfContacts: trace website and domain searches
- findUserEmailFromLookupField: trace lookup field queries
- GetAccountRecordsForRRSkip (GraphQL): trace three-tier resolution
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: rename SalesforceRoutingTrace to SalesforceRoutingTraceService
Consistent naming with CrmRoutingTraceService.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: add unit tests for CrmRoutingTraceService and SalesforceRoutingTraceService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: use AsyncLocalStorage for CRM routing trace
Replace explicit crmTrace parameter passing with AsyncLocalStorage:
- Add AsyncLocalStorage to RoutingTraceService with getCurrent() and runAsync()
- Update SalesforceRoutingTraceService to auto-resolve trace from AsyncLocalStorage
- Remove CrmRoutingTraceService wrapper (no longer needed)
- Remove crmTrace parameter from all CRM method signatures
- Wrap CRM operations in routingTraceService.runAsync() context
This is cleaner than threading crmTrace through 5+ function layers.
The trace context is available within the withReporting wrapper.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Use async local storage for `CrmRoutingTraceService`
* fix: correct template literal syntax in RoutingTraceService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use narrowed eventTypeId variable in nested async function
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: update SalesforceRoutingTraceService tests to use AsyncLocalStorage API
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: rename crmTrace to crmRoutingTraceService for consistency
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
* Add DB schema
* Init zod schema
* Init RoutingTrace and PendingRoutingTrace repository interfaces
* Create PrismaPendingRoutingTraceRepository
* Init RoutingTraceService
* Create RoutingTraceService container
* User routing trace service in routing
* Create RoutingTraceRepository and PrismaRoutingTraceRepoistory
* Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository
* Update DI containers
* RoutingTraceService create process booking method
* Use pending routing trace rather than URL params
* Fix schema
* Fix writing assignment reason for routed booking
* Remove from service
* Refactor RoutingTraceService to not rely on async local storage
* Pass RoutingTraceService through routing call
* Add attribute-logic-evaluated to routing trace step
* Add routing trace to trpc endpoint
* Add CRM routing trace step
* Fix extracting routing trace to assignment reason
* Add back CRM params to prevent refetching
* test: Add unit tests for RoutingTraceService and repositories
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: Add missing mock for RoutingTraceService in getRoutedUrl tests
Also fix pre-existing lint issues in the test file:
- Add explicit types to mockForm and mockSerializableForm variables
- Add explicit type to url parameter in mockContext
- Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>'
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Link pending form submission to routing trace
* Clean up
* Add lookup field assignment reasons
* Rename to PendingRoutingTrace
* Add migration file
* fix: Update RoutingTraceService tests to use assignmentReasonRepository mock
- Add getStepsCount() method back to RoutingTraceService
- Add queuedFormResponseId support to processForBooking method
- Update tests to use mockAssignmentReasonRepository instead of prisma mock
- Remove test for missing routingTraceRepository (all deps now required)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Remove PII (organizer email) from log payload in RoutingTraceService
Addresses Cubic AI review feedback with confidence 9/10.
Logging PII violates sensitive information logging rules.
Co-Authored-By: unknown <>
* Write field values at the time of routing
* Write attributes used to route
* fix: Add CHECK constraint to ensure at least one response ID is set in routing trace tables
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Address PR review comments for routing trace feature
- Add checkedFallback to routing trace step data (Comment 11)
- Extract hardcoded domain/step strings to constants (Comment 13)
- Use @default(now()) for createdAt in PendingRoutingTrace and RoutingTrace (Comment 15)
- Add DEFAULT CURRENT_TIMESTAMP to migration for createdAt fields
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use UTC-safe timezone expression for createdAt defaults in routing trace tables
Addresses Cubic AI review feedback (confidence 9/10) to prevent timezone
issues by using dbgenerated("timezone('UTC', now())") instead of bare now()
for the createdAt fields in PendingRoutingTrace and RoutingTrace models.
Co-Authored-By: unknown <>
* fix: Align migration SQL with Prisma's expected timezone syntax
Use 'UTC'::text cast in timezone() function to match Prisma's generated SQL.
Co-Authored-By: unknown <>
* fix: Revert migration SQL to match Prisma schema timezone syntax
Remove ::text cast from timezone() function to match what Prisma generates
from the schema definition.
Co-Authored-By: unknown <>
* fix: Use explicit ::text cast in timezone() for PostgreSQL compatibility
PostgreSQL normalizes timezone('UTC', now()) to timezone('UTC'::text, now())
internally. Update both schema and migration to use the explicit cast to
ensure they match and pass the migration check.
Co-Authored-By: unknown <>
* feat: Add RoutingFormTraceService domain class for type-safe routing traces
- Create RoutingFormTraceService class with routeMatched, fallbackRouteUsed,
attributeLogicEvaluated, and attributeFallbackUsed methods
- Add ROUTING_FORM_STEPS constants
- Add unit tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: Integrate RoutingFormTraceService for routing form tracing
- Add routingFormTraceService parameter to findMatchingRoute
- Record route_matched or fallback_route_used step when route is selected
- Use "Default Route" as name for form-level fallback routes
- Refactor findTeamMembersMatchingAttributeLogic to use RoutingFormTraceService
- Update handleResponse to accept and pass routingFormTraceService
- Create RoutingFormTraceService in getRoutedUrl and pass through the flow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* test: Add test coverage for RoutingFormTraceService integration
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: Integrate RoutingFormTraceService for routing form tracing
- Add routingFormTraceService parameter to findMatchingRoute
- Record route_matched or fallback_route_used step when route is selected
- Use "Default Route" as name for form-level fallback routes
- Refactor findTeamMembersMatchingAttributeLogic to use RoutingFormTraceService
- Update handleResponse to accept and pass routingFormTraceService
- Create RoutingFormTraceService in getRoutedUrl and pass through the flow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: Restore schema and migration from main branch
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use correct parameter name routingFormTraceService in tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use 'default_route' instead of 'Default Route' for fallback route name
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* Add DB schema
* Init zod schema
* Init RoutingTrace and PendingRoutingTrace repository interfaces
* Create PrismaPendingRoutingTraceRepository
* Init RoutingTraceService
* Create RoutingTraceService container
* User routing trace service in routing
* Create RoutingTraceRepository and PrismaRoutingTraceRepoistory
* Add findByFormResponseId and findByQueuedFormResponseId to PendingRoutingTraceRepository
* Update DI containers
* RoutingTraceService create process booking method
* Use pending routing trace rather than URL params
* Fix schema
* Fix writing assignment reason for routed booking
* Remove from service
* Refactor RoutingTraceService to not rely on async local storage
* Pass RoutingTraceService through routing call
* Add attribute-logic-evaluated to routing trace step
* Add routing trace to trpc endpoint
* Add CRM routing trace step
* Fix extracting routing trace to assignment reason
* Add back CRM params to prevent refetching
* test: Add unit tests for RoutingTraceService and repositories
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: Add missing mock for RoutingTraceService in getRoutedUrl tests
Also fix pre-existing lint issues in the test file:
- Add explicit types to mockForm and mockSerializableForm variables
- Add explicit type to url parameter in mockContext
- Replace 'as any' with 'as unknown as InstanceType<typeof UserRepository>'
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Link pending form submission to routing trace
* Clean up
* Add lookup field assignment reasons
* Rename to PendingRoutingTrace
* Add migration file
* fix: Update RoutingTraceService tests to use assignmentReasonRepository mock
- Add getStepsCount() method back to RoutingTraceService
- Add queuedFormResponseId support to processForBooking method
- Update tests to use mockAssignmentReasonRepository instead of prisma mock
- Remove test for missing routingTraceRepository (all deps now required)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Remove PII (organizer email) from log payload in RoutingTraceService
Addresses Cubic AI review feedback with confidence 9/10.
Logging PII violates sensitive information logging rules.
Co-Authored-By: unknown <>
* Write field values at the time of routing
* Write attributes used to route
* fix: Add CHECK constraint to ensure at least one response ID is set in routing trace tables
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Address PR review comments for routing trace feature
- Add checkedFallback to routing trace step data (Comment 11)
- Extract hardcoded domain/step strings to constants (Comment 13)
- Use @default(now()) for createdAt in PendingRoutingTrace and RoutingTrace (Comment 15)
- Add DEFAULT CURRENT_TIMESTAMP to migration for createdAt fields
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use UTC-safe timezone expression for createdAt defaults in routing trace tables
Addresses Cubic AI review feedback (confidence 9/10) to prevent timezone
issues by using dbgenerated("timezone('UTC', now())") instead of bare now()
for the createdAt fields in PendingRoutingTrace and RoutingTrace models.
Co-Authored-By: unknown <>
* fix: Align migration SQL with Prisma's expected timezone syntax
Use 'UTC'::text cast in timezone() function to match Prisma's generated SQL.
Co-Authored-By: unknown <>
* fix: Revert migration SQL to match Prisma schema timezone syntax
Remove ::text cast from timezone() function to match what Prisma generates
from the schema definition.
Co-Authored-By: unknown <>
* fix: Use explicit ::text cast in timezone() for PostgreSQL compatibility
PostgreSQL normalizes timezone('UTC', now()) to timezone('UTC'::text, now())
internally. Update both schema and migration to use the explicit cast to
ensure they match and pass the migration check.
Co-Authored-By: unknown <>
* fix: Use CURRENT_TIMESTAMP for createdAt defaults (standard Cal.com pattern)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: Use @default(now()) for createdAt in routing trace tables to match migration
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Pedro Castro <pedro@cal.com>
* chore: Disables syncing of calendarList on overlay calendar fetch
* Unwrap ToggledConnectedCalendars
* Pick the right type for connectedCalendars
* further type amends
* Type fixes, tons of em
* Some further fixups consistent with what was before
* fix: resolve type incompatibility in getConnectedDestinationCalendars return type
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: update DestinationCalendarProps to accept tRPC handler return type
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: use generic type for DestinationCalendarProps to accept tRPC enriched types
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: simplify DestinationCalendarProps type for better compatibility
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: export ConnectedCalendar type for consistent type inference
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: use permissive type for connectedCalendars to accept tRPC enriched types
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: export ConnectedCalendar and ConnectedDestinationCalendars types from platform-libraries
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: update ConnectedCalendar type to use boolean | null for primary field
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* Update ConnectedCalendarItem
* Undo some of Devins fixes, more fixes
* Fixup the destination calendar return type, historically not null
* Change init to undefined to deal with null
* Approach to connect selected calendars with the right credential
* This return type is used way too much everywhere, not refactoring
* Add the selectedCalendars to the type
* Actually fix overlay calendar
* set calendarsToLoad param as required
* Apply suggestion from @cubic-dev-ai[bot]
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Add new translation for 'Calendar Settings'
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-01-28 14:20:19 -03:00
2869 changed files with 107910 additions and 93505 deletions
description:React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
license:MIT
metadata:
author:vercel
version:"1.0.0"
---
# Vercel React Best Practices
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 45 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
## When to Apply
Reference these guidelines when:
- Writing new React components or Next.js pages
- Implementing data fetching (client or server-side)
`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.
Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).
Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.
**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.
When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
## Use toSorted() Instead of sort() for Immutability
`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
This applies to all CSS transforms and transitions (`transform`, `opacity`, `translate`, `scale`, `rotate`). The wrapper div allows browsers to use GPU acceleration for smoother animations.
Use explicit ternary operators (`? :`) instead of `&&` for conditional rendering when the condition can be `0`, `NaN`, or other falsy values that render.
**Incorrect (renders "0" when count is 0):**
```tsx
functionBadge({count}:{count: number}){
return(
<div>
{count&&<spanclassName="badge">{count}</span>}
</div>
)
}
// When count = 0, renders: <div>0</div>
// When count = 5, renders: <div><span class="badge">5</span></div>
This is especially helpful for large and static SVG nodes, which can be expensive to recreate on every render.
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler automatically hoists static JSX elements and optimizes component re-renders, making manual hoisting unnecessary.
When rendering content that depends on client-side storage (localStorage, cookies), avoid both SSR breakage and post-hydration flickering by injecting a synchronous script that updates the DOM before React hydrates.
var theme = localStorage.getItem('theme') || 'light';
var el = document.getElementById('theme-wrapper');
if (el) el.className = theme;
} catch (e) {}
})();
`,
}}
/>
</>
)
}
```
The inline script executes synchronously before showing the element, ensuring the DOM already has the correct value. No flickering, no hydration mismatch.
This pattern is especially useful for theme toggles, user preferences, authentication states, and any client-only data that should render immediately without flashing default values.
Reduce SVG coordinate precision to decrease file size. The optimal precision depends on the viewBox size, but in general reducing precision should be considered.
**Incorrect (excessive precision):**
```svg
<pathd="M 10.293847 20.847362 L 30.938472 40.192837"/>
When updating state based on the current state value, use the functional update form of setState instead of directly referencing the state variable. This prevents stale closures, eliminates unnecessary dependencies, and creates stable callback references.
**Incorrect (requires state as dependency):**
```tsx
functionTodoList() {
const[items,setItems]=useState(initialItems)
// Callback must depend on items, recreated on every items change
The first callback is recreated every time `items` changes, which can cause child components to re-render unnecessarily. The second callback has a stale closure bug—it will always reference the initial `items` value.
**Correct (stable callbacks, no stale closures):**
```tsx
functionTodoList() {
const[items,setItems]=useState(initialItems)
// Stable callback, never recreated
constaddItems=useCallback((newItems: Item[])=>{
setItems(curr=>[...curr,...newItems])
},[])// ✅ No dependencies needed
// Always uses latest state, no stale closure risk
1.**Stable callback references** - Callbacks don't need to be recreated when state changes
2.**No stale closures** - Always operates on the latest state value
3.**Fewer dependencies** - Simplifies dependency arrays and reduces memory leaks
4.**Prevents bugs** - Eliminates the most common source of React closure bugs
**When to use functional updates:**
- Any setState that depends on the current state value
- Inside useCallback/useMemo when state is needed
- Event handlers that reference state
- Async operations that update state
**When direct updates are fine:**
- Setting state to a static value: `setCount(0)`
- Setting state from props/arguments only: `setName(newName)`
- State doesn't depend on previous value
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, the compiler can automatically optimize some cases, but functional updates are still recommended for correctness and to prevent stale closure bugs.
Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.
**Incorrect (runs on every render):**
```tsx
functionFilteredList({items}:{items: Item[]}){
// buildSearchIndex() runs on EVERY render, even after initialization
Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations.
For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.
**Note:** If your project has [React Compiler](https://react.dev/learn/react-compiler) enabled, manual memoization with `memo()` and `useMemo()` is not necessary. The compiler automatically optimizes re-renders.
Use Next.js's `after()` to schedule work that should execute after a response is sent. This prevents logging, analytics, and other side effects from blocking the response.
`React.cache()` only works within one request. For data shared across sequential requests (user clicks button A then button B), use an LRU cache.
**Implementation:**
```typescript
import{LRUCache}from'lru-cache'
constcache=newLRUCache<string,any>({
max: 1000,
ttl: 5*60*1000// 5 minutes
})
exportasyncfunctiongetUser(id: string){
constcached=cache.get(id)
if(cached)returncached
constuser=awaitdb.user.findUnique({where:{id}})
cache.set(id,user)
returnuser
}
// Request 1: DB query, result cached
// Request 2: cache hit, no DB query
```
Use when sequential user actions hit multiple endpoints needing the same data within seconds.
**With Vercel's [Fluid Compute](https://vercel.com/docs/fluid-compute):** LRU caching is especially effective because multiple concurrent requests can share the same function instance and cache. This means the cache persists across requests without needing external storage like Redis.
**In traditional serverless:** Each invocation runs in isolation, so consider Redis for cross-process caching.
The React Server/Client boundary serializes all object properties into strings and embeds them in the HTML response and subsequent RSC requests. This serialized data directly impacts page weight and load time, so **size matters a lot**. Only pass fields that the client actually uses.
description:Review UI code for Web Interface Guidelines compliance. Use when asked to "review my UI", "check accessibility", "audit design", "review UX", or "check my site against best practices".
argument-hint:<file-or-pattern>
metadata:
author:vercel
version:"1.0.0"
---
# Web Interface Guidelines
Review files for compliance with Web Interface Guidelines.
## How It Works
1. Fetch the latest guidelines from the source URL below
2. Read the specified files (or prompt user for files/pattern)
3. Check against all rules in the fetched guidelines
4. Output findings in the terse `file:line` format
MESSAGE="New Cubic AI review feedback has been submitted on PR #${{ github.event.pull_request.number }}.
MESSAGE="New Cubic AI review feedback has been submitted on PR #${{ steps.extract-prompt.outputs.pr-number }}.
IMPORTANT: Before fixing any issue, check the individual Cubic comment on the PR for that issue's confidence score. The confidence score appears as 'Fix confidence (alpha): X/10' in each comment. Note that the (alpha) may or not be there. Don't be picky about that text. But only fix issues where the confidence score is 9/10 or higher. If you cannot find a confidence score for an issue, skip it.
@@ -102,7 +133,7 @@ jobs:
run:|
CUBIC_PROMPT=$(cat /tmp/cubic-prompt.txt)
FULL_PROMPT="You are addressing code review feedback from Cubic AI on PR #${{ github.event.pull_request.number }} in repository ${{ github.repository }}.
FULL_PROMPT="You are addressing code review feedback from Cubic AI on PR #${{ steps.extract-prompt.outputs.pr-number }} in repository ${{ github.repository }}.
Your tasks:
1. Clone the repository ${{ github.repository }} locally.
@@ -127,11 +158,11 @@ jobs:
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg prompt "$FULL_PROMPT" \
--arg title "Cubic AI Review: PR #${{ github.event.pull_request.number }}" \
--arg title "Cubic AI Review: PR #${{ steps.extract-prompt.outputs.pr-number }}" \
if [ "$HTTP_CODE" -lt 200 ] || [ "$HTTP_CODE" -ge 300 ]; then
@@ -161,10 +192,14 @@ jobs:
? `### Devin AI is addressing Cubic AI's review feedback\n\nA Devin session has been created to address the issues identified by Cubic AI.\n\n[View Devin Session](${sessionUrl})`
: `### Devin AI is addressing Cubic AI's review feedback\n\nNew feedback has been sent to the existing Devin session.\n\n[View Devin Session](${sessionUrl})`;
// Use the PR number from the extract-prompt step output instead of reading the file
MESSAGE="IMPORTANT: When you have finished addressing Cubic AI's feedback (whether you pushed changes or not), you MUST update the GitHub comment to show completion status.
Use the GitHub API to update comment ID ${COMMENT_ID} on PR #${{ github.event.pull_request.number }} in repository ${{ github.repository }}.
Use the GitHub API to update comment ID ${COMMENT_ID} on PR #${{ steps.extract-prompt.outputs.pr-number }} in repository ${{ github.repository }}.
Add a completion status section at the bottom of the comment:
- If you pushed changes: Add '\n\n---\n:white_check_mark: Pushed commit [\`{SHORT_HASH}\`](https://github.com/${{ github.repository }}/commit/{FULL_HASH})' where {SHORT_HASH} is the first 7 characters of the commit hash and {FULL_HASH} is the full commit hash.
description: React and Next.js performance optimization guidelines from Vercel Engineering. This skill should be used when writing, reviewing, or refactoring React/Next.js code to ensure optimal performance patterns. Triggers on tasks involving React components, Next.js pages, data fetching, bundle optimization, or performance improvements.
license: MIT
metadata:
author: vercel
version: "1.0.0"
---
# Vercel React Best Practices
Comprehensive performance optimization guide for React and Next.js applications, maintained by Vercel. Contains 45 rules across 8 categories, prioritized by impact to guide automated refactoring and code generation.
## When to Apply
Reference these guidelines when:
- Writing new React components or Next.js pages
- Implementing data fetching (client or server-side)
`useEffectEvent` provides a cleaner API for the same pattern: it creates a stable function reference that always calls the latest version of the handler.
Import directly from source files instead of barrel files to avoid loading thousands of unused modules. **Barrel files** are entry points that re-export multiple modules (e.g., `index.js` that does `export * from './module'`).
Popular icon and component libraries can have **up to 10,000 re-exports** in their entry file. For many React packages, **it takes 200-800ms just to import them**, affecting both development speed and production cold starts.
**Why tree-shaking doesn't help:** When a library is marked as external (not bundled), the bundler can't optimize it. If you bundle it to enable tree-shaking, builds become substantially slower analyzing the entire module graph.
**Incorrect (imports entire library):**
```tsx
import { Check, X, Menu } from 'lucide-react'
// Loads 1,583 modules, takes ~2.8s extra in dev
// Runtime cost: 200-800ms on every cold start
import { Button, TextField } from '@mui/material'
// Loads 2,225 modules, takes ~4.2s extra in dev
```
**Correct (imports only what you need):**
```tsx
import Check from 'lucide-react/dist/esm/icons/check'
import X from 'lucide-react/dist/esm/icons/x'
import Menu from 'lucide-react/dist/esm/icons/menu'
When comparing arrays with expensive operations (sorting, deep equality, serialization), check lengths first. If lengths differ, the arrays cannot be equal.
In real-world applications, this optimization is especially valuable when the comparison runs in hot paths (event handlers, render loops).
**Incorrect (always runs expensive comparison):**
```typescript
function hasChanges(current: string[], original: string[]) {
// Always sorts and joins, even when lengths differ
Two O(n log n) sorts run even when `current.length` is 5 and `original.length` is 100. There is also overhead of joining the arrays and comparing the strings.
**Correct (O(1) length check first):**
```typescript
function hasChanges(current: string[], original: string[]) {
// Early return if lengths differ
if (current.length !== original.length) {
return true
}
// Only sort/join when lengths match
const currentSorted = current.toSorted()
const originalSorted = original.toSorted()
for (let i = 0; i < currentSorted.length; i++) {
if (currentSorted[i] !== originalSorted[i]) {
return true
}
}
return false
}
```
This new approach is more efficient because:
- It avoids the overhead of sorting and joining the arrays when lengths differ
- It avoids consuming memory for the joined strings (especially important for large arrays)
## Use toSorted() Instead of sort() for Immutability
`.sort()` mutates the array in place, which can cause bugs with React state and props. Use `.toSorted()` to create a new sorted array without mutation.
**Incorrect (mutates original array):**
```typescript
function UserList({ users }: { users: User[] }) {
// Mutates the users prop array!
const sorted = useMemo(
() => users.sort((a, b) => a.name.localeCompare(b.name)),
[users]
)
return <div>{sorted.map(renderUser)}</div>
}
```
**Correct (creates new array):**
```typescript
function UserList({ users }: { users: User[] }) {
// Creates new sorted array, original unchanged
const sorted = useMemo(
() => users.toSorted((a, b) => a.name.localeCompare(b.name)),
[users]
)
return <div>{sorted.map(renderUser)}</div>
}
```
**Why this matters in React:**
1. Props/state mutations break React's immutability model - React expects props and state to be treated as read-only
2. Causes stale closure bugs - Mutating arrays inside closures (callbacks, effects) can lead to unexpected behavior
**Browser support (fallback for older browsers):**
`.toSorted()` is available in all modern browsers (Chrome 110+, Safari 16+, Firefox 115+, Node.js 20+). For older environments, use spread operator:
```typescript
// Fallback for older browsers
const sorted = [...items].sort((a, b) => a.value - b.value)
Use React's `<Activity>` to preserve state/DOM for expensive components that frequently toggle visibility.
**Usage:**
```tsx
import { Activity } from 'react'
function Dropdown({ isOpen }: Props) {
return (
<Activity mode={isOpen ? 'visible' : 'hidden'}>
<ExpensiveMenu />
</Activity>
)
}
```
Avoids expensive re-renders and state loss.
Some files were not shown because too many files have changed in this diff
Show More
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.