* feat: add seat tracking infrastructure for monthly proration
Add seat change logging infrastructure with operationId for idempotency.
This PR adds the foundation for monthly proration billing by tracking
seat additions and removals, gated behind the monthly-proration feature flag.
- Add operationId field to SeatChangeLog for idempotency
- Update SeatChangeLogRepository to support upsert with operationId
- Add feature flag guard in SeatChangeTrackingService
- Integrate seat tracking in team member invites
- Integrate seat tracking in bulk user deletions
- Integrate seat tracking in team service operations
- Integrate seat tracking in DSYNC user creation
When monthly-proration feature flag is disabled, seat logging is skipped
and behavior remains unchanged.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* feat: add monthly proration processing
Add monthly proration billing processing that works on top of the seat
tracking infrastructure. This PR implements the core proration logic,
webhook handlers, and integration with Stripe billing.
- Enhance MonthlyProrationService to process seat change logs
- Add payment webhook handlers (invoice.payment_succeeded, invoice.payment_failed)
- Update subscription webhook to sync billing period on renewals
- Update TeamBillingService to skip real-time updates when proration enabled
- Enhance StripeBillingService with proration capabilities
- Add Tasker enhancements for processing queues
- Update team creation/upgrade routes
Depends on: feat/monthly-proration-seat-tracking
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
* fix: remove unused logger from SeatChangeTrackingService
* fix: description for calculation
* fix null check on trial
* chore: no more prisma calls
* add feature flag check
* fix stub
---------
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: store utm params in stripe on signup
* fix: fallback to cookie when query params don't contain valid UTM data
Changed from else-if to separate if statement so that when query
params exist but don't contain valid UTM data, the cookie fallback
is still tried. Previously, any request with non-UTM query params
would skip the stored cookie data entirely.
Co-Authored-By: unknown <>
* fix: e2e
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Adds an end-to-end test for the Feature Opt-In banner flow. This test verifies the complete user journey from seeing the banner to enabling a feature.
Currently `OPT_IN_FEATURES` in `packages/features/feature-opt-in/config.ts` is empty, so this e2e test skips. Once we uncomment the `bookings-v3` item from the config, this e2e test will run and pass. This is a preparation for when we enable the feature.
**Changes:**
- Creates a new e2e test file (`feature-opt-in-banner.e2e.ts`) that tests the complete opt-in flow
- Adds `data-testid` attributes to banner and dialog components for reliable test selectors
## 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 - test 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. Add the `ready-for-e2e` label to run e2e tests in CI
2. The test should pass when run with `PLAYWRIGHT_HEADLESS=1 yarn e2e feature-opt-in-banner.e2e.ts`
**Test requirements:**
- The `bookings-v3` feature flag must exist in the Features table (the test uses `prisma.feature.upsert` to ensure this)
- The test will skip if `bookings-v3` is not in `OPT_IN_FEATURES`
## 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 commented my code, particularly in hard-to-understand areas
- [x] I have checked if my changes generate no new warnings
## Updates since last revision
- Replaced text-based locators (`getByText`, `getByRole`) with `data-testid` selectors for more reliable element targeting
- Added `data-testid` attributes to `FeatureOptInBanner`, `FeatureOptInConfirmDialog`, and `FeatureOptInSuccessDialog` components
## Human Review Checklist
- [ ] Verify the `data-testid` attributes don't conflict with existing test IDs in the codebase
- [ ] Confirm the test correctly skips when `bookings-v3` is not in `OPT_IN_FEATURES`
- [ ] Check that the added `data-testid` attributes follow the project's naming conventions
---
Link to Devin run: https://app.devin.ai/sessions/97158ab1b7414e3988ba803b30d95b3e
Requested by: @eunjae-lee
* fix: prevent CalDAV duplicate invitations with RFC-compliant SCHEDULE-AGENT
Fixes#9485
Adds SCHEDULE-AGENT=CLIENT to ATTENDEE lines in CalDAV createEvent and
updateEvent operations per RFC 6638 Section 7.1. This tells CalDAV servers
(Fastmail, NextCloud, etc.) that the client handles scheduling, preventing
duplicate invitation emails.
Implementation includes:
- RFC 5545 compliant line folding (max 75 octets per line)
- UTF-8 aware byte counting for international names
- Proper handling of :mailto: format in ATTENDEE lines
- Skips lines that already have SCHEDULE-AGENT parameter
* fix: address Cubic review - unfold lines and case-insensitive matching
- Add unfoldLines() to handle RFC 5545 folded lines before processing
- Use case-insensitive regex (gim flag) per RFC 5545 spec
- Simplify regex to properly capture params and value separately
* fix: only unfold/refold ATTENDEE lines, preserve other lines
- Match ATTENDEE lines including folded continuations
- Unfold only the matched ATTENDEE line
- Re-fold after adding SCHEDULE-AGENT=CLIENT
- Other iCal lines remain untouched (no RFC 5545 violation)
* fix: check SCHEDULE-AGENT only in params, not value
Only check for existing SCHEDULE-AGENT in the params portion,
not the value (which contains email/CN). This prevents false
positives when email contains "schedule-agent" substring.
* fix: handle quoted colons and exact SCHEDULE-AGENT matching
1. Colon parsing: Match :mailto:/:http:/:urn: patterns, or fallback
to finding colon not inside quotes
2. SCHEDULE-AGENT check: Use regex to match exact parameter name
(preceded by ; or at start), not substring match
* add tests
* Update .env.example
---------
Co-authored-by: Anik Dhabal Babu <adhabal2002@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* perf: add composite index on EventType(parentId, teamId)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Apply suggestion from @keithwillcode
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add assigned badge to team event types
Add an 'Assigned' badge to Round Robin and Collective event types
when the current user is a host/participant of the event type.
This provides visibility to users without edit permissions that
they are included in the rotation.
Changes:
- Add isCurrentUserHost flag in getEventTypesFromGroup handler
- Display blue 'Assigned' badge in event types listing view
- Add i18n translation for 'assigned' text
Co-Authored-By: ali@cal.com <ali@cal.com>
* refactor: remove redundant isTeamEvent check for assigned badge
Co-Authored-By: ali@cal.com <ali@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ali@cal.com <ali@cal.com>
* chore: add new view for two step slot selection for embed
* fix: make sure two step slot selection is false by default
* chore: update embed playground to showcase two step slot selection
* chore: add tests
* chore: update embed snippet generator code to include two step slot selection
* chore: update docs
* fix: back button styling
* chore: implement feedback from cubic
* fix: add missing isEnableTwoStepSlotSelectionVisible properties to test mock store
Co-Authored-By: unknown <>
* chore: implement PR feedback
* chore: update slot selection modal
* chore: add prop to hide available times header
* fix: scope two-step slot selection visibility to mobile only
Restores the isMobile check in the timeslot visibility guard to ensure
desktop embeds continue to show the booking UI when two-step slot
selection is enabled. The feature should only hide the timeslot list
on mobile devices.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* fixup: use translations for loading instead of actual word
* fix: type check
* fix: add window.matchMedia mock for jsdom test environment
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: add window.matchMedia mock to packages/testing/src/setupVitest.ts
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: unit tests failing
* add a width style to the two-step slot selection embed container.
* refactor: rename enableTwoStepSlotSelection to useSlotsViewOnSmallScreen
- Rename external-facing embed config option from enableTwoStepSlotSelection to useSlotsViewOnSmallScreen
- Update URL parameter parsing in embed-iframe.ts
- Update type definitions in types.ts and index.d.ts
- Update internal variable names to slotsViewOnSmallScreen for consistency
- Update documentation, playground examples, and tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Add config to prefill all booking fields so that slot has confirm button along with it
* chore: implement PR feedback
* fix: move twoStepSlotSelection embed outside misc-embeds div to fix e2e test
The e2e test was failing because the misc-embeds div content was
intercepting pointer events on mobile viewport. This follows the same
pattern used for skeletonDemo - the embed is now outside misc-embeds
and the div is hidden when testing this namespace.
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: prevent pointer event interception in twoStepSlotSelection embed
Hide heading and note elements and set pointer-events: none on container
elements while keeping pointer-events: auto on the iframe container.
This prevents the container from intercepting clicks on the iframe
during mobile viewport e2e tests.
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: hide all non-essential content for twoStepSlotSelection e2e test
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: disable pointer-events on body for twoStepSlotSelection e2e test
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: remove pointer-events: auto on container to let clicks pass through to iframe
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: explicitly set pointer-events: none on container and inline-embed-container
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: also set pointer-events: none on html element to prevent interception
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: make .place div fill viewport for twoStepSlotSelection e2e test
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: failing embed tests
* fixup
* fixup fixup
* fix: failing tests
* fix: update checks for verifying prefilled date
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
* refactor: remove redundant filtering and return filtered users from service
* Simplify user retrieval by returning users directly
* Fix formatting issue in users.service.ts
* Filter out null users in getDynamicEventType
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: make linting required for CI
- Remove continue-on-error from lint workflow so CI fails on lint errors
- Fix 2 lint errors: avoid importing from @trpc/server in lib package
- Add trpcErrorUtils.ts to handle TRPC errors without circular dependencies
- Update lint-staged to show warnings but not block commits
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: add message field validation to isTRPCErrorLike type guard
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add Cloudflare URL Scanner integration for malicious URL detection
- Add MALICIOUS_URL_IN_WORKFLOW to LockReason enum
- Add URL_SCANNING_ENABLED constant for feature flag
- Create urlScanner.ts utility for Cloudflare Radar URL Scanner API
- Create scanWorkflowUrls task for async URL scanning with polling
- Integrate URL scanning into scanWorkflowBody task
- Add URL scanning for event type redirect URLs
- Lock user accounts when malicious URLs are detected
- Fix pre-existing lint issues (parseInt radix, optional chaining)
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: address biome lint warnings and TypeScript iterator errors
- Wrap iterators with Array.from() to fix TS2802 errors
- Add biome-ignore comments for process.env usage
- Extract helper functions to reduce function length
- Move exports to end of file per useExportsLast rule
- Remove problematic imports that cause TypeScript errors
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: address cubic-dev-ai review comments for URL scanning
- Fix P0: Re-fetch workflow steps before scheduling notifications to use actual verifiedAt values from database instead of overriding with new Date()
- Fix P1: Mark workflow step as verified in submitWorkflowStepForUrlScanning when URL scanning is disabled or no URLs found
- Fix P1: Add whitelistWorkflows parameter to submitUrlForUrlScanning for consistency
- Fix P2: Preserve URL context in error results in urlScanner.ts scanUrls function
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: add select clause to Prisma query for workflow steps
Address cubic-dev-ai P2 comment: Use select to fetch only the required
fields (id, action, sendTo, emailSubject, reminderBody, template, sender,
verifiedAt) instead of fetching all columns from workflowStep table.
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: add select clause to Prisma query in scanWorkflowBody.ts
Address cubic-dev-ai P2 review comment: Use select to fetch only the
required fields (id, action, sendTo, emailSubject, reminderBody,
template, sender, verifiedAt) instead of fetching all columns.
Co-Authored-By: peer@cal.com <peer@cal.com>
* test: add unit tests for URL scanning functionality
- Add tests for urlScanner.ts (extractUrlsFromHtml, isUrlScanningEnabled)
- Add tests for scanWorkflowUrls.ts (happy/unhappy paths for URL scanning task)
- Add tests for scanWorkflowBody.ts (happy/unhappy paths for workflow body scanning)
Tests cover:
- URL extraction from HTML content
- URL normalization and deduplication
- Handling of malicious URLs and user locking
- Fail-open behavior for API errors
- Whitelisted user handling
- Iffy spam detection integration
Co-Authored-By: peer@cal.com <peer@cal.com>
* test: remove incomplete test that provides no value
Removed the 'should mark all steps as verified when neither Iffy nor URL scanning is enabled' test as it used vi.doMock() which doesn't work after module import, had no assertions, and gave false confidence in test coverage.
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: use Cloudflare bulk scanning endpoint to reduce API quota usage
- Added submitUrlsForBulkScanning function that uses /urlscanner/v2/bulk endpoint
- Updated scanUrls to use bulk submission instead of individual URL submissions
- Bulk endpoint accepts up to 100 URLs per request, batching is handled automatically
- Reduces API quota usage as suggested by keithwillcode
Co-Authored-By: peer@cal.com <peer@cal.com>
* Update packages/features/tasker/tasks/scanWorkflowUrls.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: address cubic-dev-ai review comments
- P1: Sanitize URLs before logging to prevent exposing sensitive query parameters
- P2: Extract handleUrlScanningForStep helper function to reduce code duplication
- P2: Use vi.stubGlobal for fetch mock in tests for proper cleanup
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: address volnei review comments on PR #26387
- Move vi.unstubAllGlobals() to afterEach hook in iffyScanBody tests
- Restore updateMany optimization when URL scanning is disabled
Co-Authored-By: peer@cal.com <peer@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* feat: add OAuth client developer settings page with approval workflow
- Add new developer OAuth page at /settings/developer/oAuth for users to submit OAuth client requests
- Transform admin OAuth page into management dashboard for reviewing/approving submissions
- Add OAuthClientApprovalStatus enum (PENDING, APPROVED, REJECTED) to track submission status
- Add userId and createdAt fields to OAuthClient model for tracking submissions
- Create email notifications for admin (new submission) and user (approval)
- Add sidebar navigation link in developer section below API keys
- Add comprehensive translations for new UI strings
- Create OAuthClientRepository for data access following repository pattern
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: re-export generateSecret for backward compatibility
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat: make logo mandatory and list items clickable for OAuth clients
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: add missing translation keys and remove client secret from details dialog
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: address cubic AI reviewer comments
- Remove duplicate 'there' JSON key in common.json
- Add select clause to findByUserId to avoid exposing clientSecret
- Add @@index([userId]) to OAuthClient model for query performance
- Update migration to include the index
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: address PR review comments - fix indentation and use useCopy hook
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: change react-dom/server import to fix Turbopack compatibility
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Revert "fix: change react-dom/server import to fix Turbopack compatibility"
This reverts commit c3e0b709c2d88fd221143cb4ce9cd25bb8c94277.
* fix: use email service pattern for OAuth client notifications
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add try-catch around email sending to handle Turbopack react-dom/server issue
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Revert "fix: add try-catch around email sending to handle Turbopack react-dom/server issue"
This reverts commit fc9d47cd773505ebc5ee2696718aad4a8a98be77.
* fix: improve OAuth client UI with skeleton loaders and smaller dialog styling
- Replace 'Loading...' text with proper skeleton loaders in both developer and admin OAuth client views
- Make client_id and copy button smaller in dialogs using size='sm' and text-sm styling
- Add 'client_id' translation key to common.json for proper i18n
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: improve skeleton loader to match actual OAuth client list structure
- Remove divide-y from container and use conditional border-b on rows
- Match the exact structure from oauth-clients-view.tsx L126-160
- Use proper spacing for text elements (mt-1 instead of space-y-2)
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix skeleton
* rename the selected oauth client dialog
* fix: address PR feedback - admin auth, dropdown styling, sidebar label
- Add defense-in-depth admin authorization check in updateClientStatus handler
- Fix broken dropdown menu by using DropdownItem with StartIcon prop
- Fix sidebar menu label from 'oAuth' to 'oauth_clients' to match developer view
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* update common.json
* feat: show client secret in approval email for confidential OAuth clients
- Add regenerateSecret method to OAuthClientRepository
- Regenerate secret when admin approves a PENDING confidential client
- Include client secret in approval notification email
- Add one-time warning message about storing the secret securely
- Only regenerate on first approval (not re-approvals)
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* feat: add Website URL field, fix logo styling, show client secret after approval
- Add Website URL field to OAuth client forms (admin and developer views)
- Fix Upload Logo section styling by wrapping in Label div with proper gap
- Display client secret in dialog after admin approves a confidential OAuth client
- Add websiteUrl field to Prisma schema with migration
- Update tRPC handlers and repository to support websiteUrl
- Add translation keys for new UI elements
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: move clientSecret variable declaration outside if block for proper scoping
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: dont expose client secret in emails
* refactor: dont regenerate secret upon status change
* refactor: reuse existing hash function
* refactor: rename admin/oAuth to admin/oauth page
* refactor: deduplicate oauth repositories
* refactor: remove withGlobalPrisma from oauth repository
* refactor: developer oauth page
* refactor: oauth status by default accepted
* refactor: request oauth status when creating
* refactor ux
* fix: address Cubic AI code review feedback
- Add purpose field to plain text email body for accessibility
- Convert NewOAuthClientButton to inline JSX to avoid React anti-pattern
- Trigger re-approval when redirectUri changes for security
- Add e.preventDefault() for Space key to prevent page scroll
- Change default approvalStatus to PENDING for defense-in-depth
- Use oauth_clients translation key for consistency
- Add meaningful alt text to Avatar for accessibility
- Remove onClick from DialogClose to prevent double-run close effects
- Return NOT_FOUND for non-owner delete to prevent resource enumeration
Co-Authored-By: unknown <>
* common.json file
* refactor: delete all prisma migrations
* refactor: have just 1 prisma migration
* revert: some devin changes
* fix: typecheck
* test: owner OAuth crud
* test: admin OAuth approval / rejection
* fix: address Cubic AI review feedback (confidence 9/10 issues)
- schema.prisma: Remove @default("") from purpose field to make it required
- schema.prisma: Use UTC-aware timezone expression for createdAt default
- OAuthClientFormFields.tsx: Localize redirect URI placeholder using t()
- common.json: Add redirect_uri_placeholder translation key
Co-Authored-By: unknown <>
* cubic changes
* refactor: dont log sensitive info and rethrow error
* cubic feedback
* refactor: make oauth client purpose optional
* refactor: admin/oauth not allowed if not logged in
* refactor: admin view skeleton
* refactor: rename state
* refactor: get rid of redundant mapping
* refactor: remove redundant handler
* refactor: remove redundant handler
* refactor: re-usable new oauth client button
* refactor: dialogs
* refactor: modals
* refactor: handler names, dialog, skeleton
* fix: purpose being null
* refactor: rename handler and delete old oauth admin page
* fix: purpose in submission
* refactor: handler names
* refactor: rename
* refactor: update handler
* refactor: rename approvalStatus -> status
* refactor: simplify modal
* refactor: name
* dont require repproval if redirectUri changes
* fix: remove integration sync index creation
* refactor: require re-approval if redirectUri updated
* fix: flaky e2e test
* fix: flaky e2e test
* fix: flaky e2e test
* fix: remove duplicate common.json keys
* refactor: replace team@cal.com with SUPPORT_MAIL_ADDRESS
* refactor: generate client secret on handler level
* fix: authorization code only available to approved clients
* refactor: cubic review dont display exclamation
* refactor: cubic review website_url in common json
* fix: dont default in ui to approved status
* refactor: optiona logo in schema create handler
* fix: tests
* fix: tests
* fix: /authorize redirect if client not approved or show error
* test: authorize page with invalid client id
* refactor: dont allow refreshing tokens unless approved client
* fix: flaky e2e test
* fix: flaky e2e test
* fix: flaky e2e test
* fix: flaky e2e test
* fix: flaky e2e test
* fix: flaky e2e test
* chore: warn that pending client is not usable
* fix: approve and reject buttons
* fix: /authorize show error if client not approved
* refactor: info message about editing oauth client and status
* change info alert to warning
* try to fix ci test
* debug: failing e2e test
* fix: improve session propagation in oauth-client-admin E2E test
- Add navigateToAdminOAuthPage helper that waits for listClients API call
- If the API call doesn't arrive (session issue), reload page to force session refresh
- This fixes the CI flakiness where admin page wasn't loading due to session not having ADMIN role
Co-Authored-By: lauris@cal.com <lauris@cal.com>
* fix: register waitForResponse before navigating in E2E test
- Register the listClients waitForResponse promise BEFORE page.goto()
- This ensures the response isn't missed during page load
- Also register the promise before reload in the catch block
Co-Authored-By: lauris@cal.com <lauris@cal.com>
* fix: rename oAuth folder to oauth for case-sensitive filesystems
The admin OAuth page route was at /settings/admin/oAuth (capital A) but the
code references /settings/admin/oauth (lowercase). This caused 404 errors
on case-sensitive filesystems (Linux).
Also improved the E2E test navigation helper to retry with delays if the
admin page doesn't load immediately, handling session propagation timing.
Co-Authored-By: lauris@cal.com <lauris@cal.com>
* test style
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: eunjae@cal.com <hey@eunjae.dev>
Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
Co-authored-by: supalarry <laurisskraucis@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <1082092+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
Co-authored-by: lauris@cal.com <lauris@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
These columns are marked as deprecated in the Prisma schema and will be
removed in a follow-up migration. This PR removes all code references to
prepare for the column removal.
Changes:
- Remove startTime/endTime from ProfileRepository userSelect and methods
- Remove startTime/endTime from UserRepository userSelect and methods
- Remove startTime/endTime from me/get.handler.ts API response
- Remove endTime from API v1 user validation schema
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: remove Object.freeze hack in getConnectedCalendars
The previous implementation used Object.freeze to prevent the destinationCalendar
object from being mutated when looping through multiple calendar credentials.
This was a workaround for a bug where the wrong calendar's primaryEmail and
integrationTitle would be set when multiple credentials existed.
This refactor properly fixes the issue by:
1. Only setting destinationCalendar once (when first found)
2. Using a flag to track when the destination calendar is found in the current credential
3. Only setting primaryEmail and integrationTitle for the credential that contains
the destination calendar
Fixes the hack mentioned in: https://github.com/calcom/cal.com/pull/7644
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: move destinationCalendar enrichment outside the loop
Per feedback, moved the destinationCalendar finding and enrichment
completely outside the Promise.all loop. After all connected calendars
are fetched, we search through the results to find and enrich the
destination calendar with primaryEmail and integrationTitle.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: ensure default calendars with trigger.dev apiv2
* test: add unit and e2e tests for CalendarsTasker integration
- Add unit tests for CalendarsTasker.dispatch when enableAsyncTasker is true
- Add e2e test to verify ensureDefaultCalendarsForUser is called when creating membership
- Mock CalendarsTasker and ConfigService in unit tests
- Test both async (Trigger.dev) and sync (Bull queue) paths
- Fix missing return types on helper functions in e2e tests
- Add *.spec.ts to biome test file exceptions for noExcessiveLinesPerFunction rule
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: filter out Google system calendars from calendar list
Google system calendars (holidays, birthdays) don't return proper
free/busy information, making them useless for availability checking.
This change filters them out from the listCalendars() method so users
won't see these calendars as selectable options.
Filtered calendar types:
- Holiday calendars (#holiday@group.v.calendar.google.com)
- Birthdays/contacts calendar (#contacts@group.v.calendar.google.com)
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* test: add tests for Google system calendar filtering
Tests verify that listCalendars() correctly filters out:
- Holiday calendars (#holiday@group.v.calendar.google.com)
- Birthdays/contacts calendar (#contacts@group.v.calendar.google.com)
Test cases cover:
- Filtering holiday calendars
- Filtering birthdays calendar
- Mixed calendars (keeping regular, filtering system)
- All system calendars (returns empty array)
- Null/undefined calendar IDs
- Various international holiday calendar formats
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Refactor duplicate UI components (SettingsGroup, SettingRow, NavigationRow) into [SettingsUI.tsx]
- Centralize showNotAvailableAlert in utils/alerts.ts
- Replace in-app browser links with Feature not available alerts for iOS on:
- More tab items (Apps, Routing, Workflows, Insights, Support)
- Event Type Other tab (Apps, Workflows, Webhooks)
- Event Type Advanced tab (Private Links)
- Remove View public page from Profile Sheet on iOS
- Remove Preview actions from Event Type (List menu and Detail quick actions) on iOS
- Add Learn More links to Advanced Tab settings
* feat: add option to hide duration selector in booking page for multiple durations
- Add 'hideDurationSelectorInBookingPage' field to event type metadata schema
- Add checkbox under Default Duration select in event type settings
- Modify Duration component to hide selector when setting is enabled
- URL params can still set duration when selector is hidden
- Add i18n translation for the new checkbox label
- Add e2e tests for the new functionality
Co-Authored-By: ali@cal.com <ali@cal.com>
* fix: rename checkbox label to 'Hide duration selector'
Co-Authored-By: ali@cal.com <ali@cal.com>
* fix: replace text locator with data-testid in E2E test
Replace `text=Multiple duration` locator with `page.getByTestId("event-types").locator('a[title="Multiple duration"]')` to follow E2E test best practices.
Co-Authored-By: unknown <>
* fix: add data-testid to hide duration selector checkbox for E2E tests
Co-Authored-By: ali@cal.com <ali@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ali@cal.com <ali@cal.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
* feat: remove Revert.dev dependency from Pipedrive integration
- Replace Revert API calls with direct Pipedrive OAuth2 and REST API
- Update OAuth flow to use Pipedrive's authorization endpoint
- Implement token exchange in callback handler
- Map Revert's unified API format to Pipedrive's native API structure
- Remove REVERT_* environment variables from configuration
- Update app config to reflect direct Pipedrive integration
- Follow patterns from other CRM integrations like HubSpot
Breaking change: Requires PIPEDRIVE_CLIENT_ID and PIPEDRIVE_CLIENT_SECRET
environment variables instead of REVERT_* variables
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* feat: implement proper token refresh logic for Pipedrive OAuth
- Add token refresh implementation following Pipedrive OAuth2 specification
- Use proper Basic Auth for token refresh requests
- Handle token refresh errors with appropriate logging
- Update access token and expiry date after successful refresh
- Add note about database credential update requirement
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* feat: add database credential updates for Pipedrive token refresh
- Import updateTokenObjectInDb utility for proper credential management
- Store credentialId in constructor for database updates
- Update token refresh logic to persist refreshed tokens to database
- Follow Cal.com patterns for OAuth credential management
- Ensure tokens are properly updated after successful refresh
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* refactor: use OAuthManager for Pipedrive token management
- Refactor CrmService.ts to use OAuthManager instead of manual token refresh
- Create getPipedriveAppKeys helper function for client credentials
- Update callback.ts to use the new helper function
- Add requestPipedrive helper method for all API calls
- Implement proper token refresh callbacks (isTokenObjectUnusable, isAccessTokenUnusable)
This addresses the review comments from hariom and cubic on PR #22492.
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: validate token endpoint response before storing Pipedrive credentials
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: handle invalid timezones gracefully in getLuckyUser
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: move timezone validation to getCalendarsEvents with offset conversion
- Move invalid timezone handling from getLuckyUser.ts to getCalendarsEvents.ts
- Add convertOffsetToEtcGmt() to convert offset formats (GMT-05:00, UTC+08:00) to valid IANA Etc/GMT timezones
- Preserves timezone precision for fairness calculations instead of falling back to UTC
- Add unit tests for timezone conversion scenarios
* refactor: extract timezone conversion to dedicated file
- Create timezone-conversion.ts with concise convertOffsetToIanaTimezone and normalizeTimezone functions
- Update getCalendarsEvents.ts to import from the new file
- Follow kebab-case naming convention for utility files
* test: add unit tests for timezone conversion proving Intl.DateTimeFormat behavior
- Add tests proving Intl.DateTimeFormat throws RangeError for offset formats (GMT-05:00, UTC+08:00)
- Add tests for convertOffsetToIanaTimezone function
- Add tests for normalizeTimezone function
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Early return if date range is empty
* test: add tests for empty workingHours early return optimization
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: Volnei Munhoz <volnei@cal.com>
PR #27012 introduced logic to disable apps without valid keys during seeding.
This caused the alby payment app tests to fail because the alby app was
disabled in the App table when its environment keys weren't configured.
This fix adds an ensureAppEnabled helper function that enables the app
in the App table before running the tests, treating it as a mock/test
setup step.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Integrates booking audit logging for the edit location functionality, following the pattern established in PR #26046 (booking creation/rescheduling audit).
This PR adds audit logging when a booking's location is changed through:
1. **Web app** (tRPC handler): `packages/trpc/server/routers/viewer/bookings/editLocation.handler.ts`
2. **API v2**: `apps/api/v2/src/ee/bookings/2024-08-13/services/booking-location.service.ts`
### Changes:
- Added `actionSource` as a **required** parameter to `editLocationHandler` (no fallback)
- Added optional `userUuid` parameter (defaults to logged-in user's uuid)
- Added `ValidActionSource` type that excludes "UNKNOWN" for client-facing APIs
- Captures old location before update for audit data
- Calls `BookingEventHandlerService.onLocationChanged()` after successful location update
- Web app uses `actionSource: "WEBAPP"`, API v2 uses `actionSource: "API_V2"`
- Updated router to explicitly pass `actionSource: "WEBAPP"`
- Updated test to pass `actionSource: "WEBAPP"`
- **API v2**: Uses NestJS dependency injection pattern with `BookingEventHandlerService` injected via constructor
### Updates since last revision:
- **Created `BookingEventHandlerModule`** (`apps/api/v2/src/lib/modules/booking-event-handler.module.ts`) to encapsulate `BookingEventHandlerService` and its dependencies (Logger, TaskerService, HashedLinkService, BookingAuditProducerService)
- Updated both bookings modules (2024-04-15 and 2024-08-13) to import `BookingEventHandlerModule` instead of listing individual providers
- This reduces code duplication and makes dependency management cleaner
## 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. N/A - using existing audit infrastructure that is already tested.
## How should this be tested?
1. Update a booking's location through the web app
2. Update a booking's location through API v2
3. Verify audit logs are created with:
- Correct `bookingUid`
- Correct `actor` (user who made the change)
- Correct `source` ("WEBAPP" or "API_V2")
- Correct `auditData.location.old` and `auditData.location.new` values
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have checked if my changes generate no new warnings
## Human Review Checklist
- [ ] Verify all callers of `editLocationHandler` pass `actionSource` (router updated, test updated)
- [ ] Verify `organizationId` derivation is correct in both handlers (tRPC uses `booking.user?.profiles?.[0]?.organizationId`, API v2 uses `existingBookingHost.organizationId`)
- [ ] Confirm audit call placement after location update is intentional (audit failures would fail the operation even though location was already updated)
- [ ] Note: API v2 has its own implementation and does NOT reuse `editLocationHandler` - this is correct
- [ ] Verify `BookingEventHandlerModule` properly exports `BookingEventHandlerService` and is imported in both bookings modules
- [ ] Verify the `updateBookingLocationInDb` return value (`{ updatedLocation }`) is destructured and used correctly for audit data
- [ ] Verify API v2 uses `bookingLocation` for audit `new` value, while tRPC uses `updatedLocation` from DB update
---
Link to Devin run: https://app.devin.ai/sessions/fd1d439779674050a26ea3fa7d799943
Requested by: @hariombalhara
## What does this PR do?
Adds auto-accept logic to the `/v2/teams/{teamId}/memberships` endpoint, similar to what exists in the `/v2/organizations/{orgId}/teams/{teamId}/memberships` endpoint.
When a user is added to a sub-team of an organization, the system now checks if the user's email domain matches the organization's `orgAutoAcceptEmail` setting. If it matches and the organization is verified, the membership is automatically accepted (overriding any `accepted: false` in the request).
### Changes:
- Modified `TeamsMembershipsService.createTeamMembership()` to check if the team has a parent organization and apply auto-accept logic using the existing `OrganizationMembershipService.shouldAutoAccept()` method
- Exported `OrganizationMembershipService` from `OrganizationsModule` for dependency injection
- Added `TeamsModule` and `UsersModule` imports to `TeamsMembershipsModule`
- Added required dependencies (`OrganizationMembershipService`, `OAuthClientRepository`, etc.) to `TeamsModule` and `TeamsSchedulesModule` since they provide `TeamsMembershipsService` directly
- Added comprehensive E2E tests for auto-accept scenarios
- Added `createOrgSettings` and `deleteOrgSettings` helper methods to test fixtures
## 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?
The E2E tests cover the following scenarios:
1. **Auto-accept when email matches** - User with `@acme.com` email is auto-accepted when org has `orgAutoAcceptEmail: "acme.com"`
2. **Case-insensitive matching** - User with `@ACME.COM` email matches `orgAutoAcceptEmail: "acme.com"`
3. **Override accepted:false** - Even if `accepted: false` is passed, it's overridden to `true` when email matches
4. **No auto-accept for non-matching emails** - User with `@external.com` email is NOT auto-accepted
5. **EventTypes assignment** - Verifies user is added to event types with `assignAllTeamMembers: true`
To run the tests:
```bash
TZ=UTC yarn test:e2e --testPathPattern="teams-memberships.controller.e2e-spec"
```
## Human Review Checklist
- [ ] Verify the auto-accept logic matches the existing implementation in `organizations-teams-memberships.controller.ts`
- [ ] Confirm all modules that provide `TeamsMembershipsService` directly (`TeamsModule`, `TeamsSchedulesModule`, `OrganizationsModule`) have the required dependencies
- [ ] Verify `updateNewTeamMemberEventTypes` is called when auto-accept triggers (controller checks `membership.accepted` after service returns)
- [ ] Consider if organization membership creation is needed when a user is auto-accepted into a sub-team but doesn't have org membership yet (not implemented in this PR)
## 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 commented my code, particularly in hard-to-understand areas
- [x] I have checked if my changes generate no new warnings
---
Link to Devin run: https://app.devin.ai/sessions/578b8904a5a94385b59031f9616379cc
Requested by: @hariombalhara
* fix: use WEBAPP_URL for booking URLs when domains differ
For EU deployments where WEBAPP_URL (app.cal.eu) differs from
WEBSITE_URL (cal.com), use WEBAPP_URL for non-org booking URLs.
This fixes incorrect bookingUrl in API responses for EU instance.
* Created new shared utility getTldPlus1 and updated code to use shared function instead of inline copy
* use same comment
* feat: integrate DateTimePickerAndroid for date/time selection
- Added @react-native-community/datetimepicker dependency for native date/time picker functionality.
- Replaced modal-based date/time picker implementation with DateTimePickerAndroid for improved user experience.
- Updated RescheduleScreen to utilize the new date/time picker methods, enhancing the selection process and ensuring better styling consistency.
* feat: add LimitsTabDatePicker component for date selection
- Introduced a new LimitsTabDatePicker component utilizing DateTimePickerAndroid for improved date selection on Android.
- Implemented date parsing and formatting for user-friendly display.
- Enhanced user interaction with a pressable element to open the date picker.
* feat: add EditAvailabilityOverrideScreen for managing schedule overrides
- Introduced EditAvailabilityOverrideScreen component to allow users to create and edit availability overrides.
- Implemented date and time selection using DateTimePickerAndroid for improved user experience.
- Added functionality to handle existing overrides, including editing and deleting options.
- Enhanced user interaction with clear alerts for success and error states during save operations.
* fixed lint warnings, info
* address cubics comments
---------
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
* chore: update teams schedules module
* chore: update schedules and team events repository
* chore: input for teams schedules
* feat: add logic to filter schedules based on event type id
* fix: bad import
* chore: add input for get user schedules
* feat: add logic in user schedule to filter schedules based on event type id
* chore: update e2e tests
* fix: add EventTypesModule_2024_06_14 import to SchedulesModule_2024_06_11
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: import path
* chore: implement cubic feedback
* fix: replace .finally() with try/finally in E2E tests
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* chore: implement PR feedback
* fix: correct schedule precedence order for team hosts
Fix the effective schedule resolution to follow the intended precedence:
host schedule > user default schedule > event type schedule
This addresses Cubic AI review feedback with confidence 9/10:
- schedules.service.ts: Fix precedence in getUserSchedulesForEventType
- teams-schedules.service.ts: Fix precedence in getTeamSchedulesForEventType
Co-Authored-By: unknown <>
* fixup
---------
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Converts a `console.log` statement to use the proper `@calcom/lib/logger` with debug level in the raqbUtils.server.ts file. This ensures the log message only appears when debug logging is enabled, reducing noise in production logs.
## 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. N/A - logging change only.
## How should this be tested?
This is a simple logging change. No specific testing required beyond CI passing.
The log message "A group option found. Using all its sub-options instead" will now only appear when the logger level is set to debug (level 2 or lower via `NEXT_PUBLIC_LOGGER_LEVEL`).
## Checklist
- [x] My code follows the style guidelines of this project
- [x] I have checked if my changes generate no new warnings
<!-- Link to Devin run: https://app.devin.ai/sessions/45d95cd7127541ffac96764f251ae1c9 -->
<!-- Requested by: @hariombalhara -->
- Add 40 minutes to the multipleDurationOptions array in EventSetupTab
- Add comprehensive tests for 40-minute slot generation including:
- Full day slot generation
- Correct time intervals
- Default duration handling
- Same day booking with time constraints
- Multiple date ranges
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: ali@cal.com <ali@cal.com>
* feat: add PlatformOrganizationBillingTasker with sync and trigger.dev versions
- Create PlatformOrganizationBillingTasker extending Tasker base class
- Add sync tasker (PlatformOrganizationBillingSyncTasker) for synchronous execution
- Add trigger.dev tasker (PlatformOrganizationBillingTriggerTasker) for async execution
- Create trigger.dev task for incrementing subscription usage
- Add PlatformOrganizationBillingTaskService with business logic
- Add PlatformOrganizationBillingRepository for data access
- Add createSubscriptionUsageRecord method to StripeBillingService
- Follow BookingEmailAndSmsTasker pattern for consistency
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: move repository methods per PR feedback
- Move findPlatformOrgByUserId to OrganizationRepository
- Create new PlatformBillingRepository for billing queries
- Remove PlatformOrganizationBillingRepository (consolidated)
- Update task service and trigger.dev task to use new repositories
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* feat: add DI using @evyweb/ioctopus for platform billing tasker
- Create di/tasker/ directory following BookingEmailAndSmsTasker pattern
- Add tokens.ts with DI tokens for all billing tasker classes
- Add module files for PlatformBillingRepository, TaskService, SyncTasker, TriggerTasker, Tasker
- Add container files with getter functions
- Update trigger.dev task to use DI container instead of manual instantiation
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: add select statement to PlatformBillingRepository.findByTeamId
Only select subscriptionId field since that's the only field used by the task service
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* feat: add NestJS DI modules for platform billing tasker in API v2
- Add platform billing tasker exports to platform-libraries/organizations.ts
- Export IBillingProviderService type from platform-libraries
- Create StripeBillingProviderService wrapper implementing IBillingProviderService
- Create PrismaPlatformBillingRepository extending PlatformBillingRepository
- Create NestJS service wrappers for all platform billing tasker classes
- Create PlatformBillingTaskerModule with all providers and exports
- Update PlatformOrganizationBillingTaskService to use Pick<IBillingProviderService>
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* feat: handle cancel and reschedule usage
* fix: restore IsUserInBillingOrg to billing module providers
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* chore: add idempotency key
* fix: just log error]
* fix: logger and typo
* fix: address Cubic AI review feedback (high confidence issues)
- Fix grammar error: 'Delayed task are' -> 'Delayed tasks are' in PlatformOrganizationBillingSyncTasker.ts (3 occurrences)
- Re-throw error after logging in increment-usage.ts to enable trigger.dev retry mechanism
Co-Authored-By: unknown <>
* fix: remove duplicate code and use DI instead
* fix: remove orThrow on find first in findPlatformOrgByUserId
* refactor: prevent usage increment task from re-throwing errors
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Fixes a bug where the UI shows team members who match segment criteria even when they don't have host entries in the database. When `assignAllTeamMembers` is `true` and segment filtering is enabled, members would appear in the Assignment tab but wouldn't actually be assigned during booking because they lack host entries.
**The Problem:**
- The filtering logic in `EditWeightsForAllTeamMembers` short-circuited when `assignAllTeamMembers` was true, showing all segment-matching members regardless of whether they had host entries
- The `handleSave` function only updates hosts that already exist in the `value` array, filtering out members without host entries
- This created a misleading UI where members appeared but wouldn't be assigned
**The Fix:**
- Removed the `assignAllTeamMembers` prop from `EditWeightsForAllTeamMembers` component entirely since the filtering should always check for existing host entries
- Updated the filter in `EditWeightsForAllTeamMembers.tsx` to always check for existing host entries in the `value` array
- Added `filterMemberIds` prop to `Segment` component to filter matching team members to only show those who are hosts of the event-type
- `AddMembersWithSwitch` now passes the list of non-fixed host user IDs to the Segment component via `filterMemberIds`
## 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.
- [ ] 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 team event type with round-robin scheduling
2. Enable "Assign all team members" toggle
3. Enable segment filtering with criteria that matches some team members
4. Verify that only members who have host entries (are actually assigned to the event type) appear in the "Edit team member weights" sheet
5. Members matching segment criteria but without host entries should NOT be displayed
## 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 the filtering logic correctly shows only members with host entries in both `EditWeightsForAllTeamMembers` and `Segment` components
- [ ] Confirm this aligns with the actual booking behavior (members without host entries won't be assigned)
- [ ] Check that `filterMemberIds` prop is correctly passed through `AddMembersWithSwitch` → `MembersSegmentWithToggle` → `Segment` → `MatchingTeamMembers`
- [ ] Verify the filter `value.filter((host) => !host.isFixed).map((host) => host.userId)` correctly extracts non-fixed host IDs
---
Link to Devin run: https://app.devin.ai/sessions/3333b41226d0452e98ef7ac358511f5b
Requested by: @hariombalhara
* ci(companion): add separate typecheck workflow to catch type errors
This adds a new companion-typecheck.yml workflow that runs TypeScript type
checking for the companion app. This would have caught the missing useEffect
import issue in PR #26931.
Changes:
- Add new companion-typecheck.yml workflow file
- Update pr.yml to call the new workflow when companion files change
- Add typecheck-companion to the required jobs list
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* add lint checks
* test
* remove
* update typecheck
* address review
* chore:- hide apps with missing required keys from app store
* update
* Delete packages/app-store/_utils/hasRequiredAppKeys.test.ts
* Update validateAppKeys.ts
* Update validateAppKeys.ts
* Update validateAppKeys.test.ts
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: hide navigation when ?standalone=true URL parameter is present
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat: standalone page config to use in companion
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: peer@cal.com <peer@cal.com>
* docs: add feature opt-in banner system plan
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* docs: iterate on feature opt-in banner plan based on feedback
- Use config.ts for feature metadata instead of TRPC response
- Consolidate localStorage dismissal into single key to avoid bloat
- Replace separate success dialog with in-dialog success state for smoother UX
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* feat: implement feature opt-in banner system
- Add checkFeatureOptInEligibility TRPC procedure to check if user can opt-in
- Create useFeatureOptInBanner hook with localStorage dismissal state
- Create FeatureOptInBanner floating component for bottom-right display
- Create FeatureOptInConfirmDialog with role-based options and success state
- Integrate banner into bookings-view.tsx as example usage
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* feat: add i18n translation keys for feature opt-in banner
* refactor: create FeatureOptInBannerWrapper for simpler consumer integration
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: use @calcom/lib/webstorage and add Zod schema for localStorage
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: address PR review comments - use PBAC, move logic to service, improve typing
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
# Conflicts:
# packages/features/feature-opt-in/services/FeatureOptInService.ts
* test: add comprehensive tests for checkFeatureOptInEligibility method
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: correct import and Checkbox type errors
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* update message
* invalidate after dialog dismiss
* feat: simulate user opt-in to determine banner eligibility
When a feature has a strict policy and org/team hasn't explicitly
enabled it, user opt-in alone won't enable the feature. This change
simulates what would happen if the user opts in and only shows the
banner if opting in would actually enable the feature.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add early return for missing featureConfig and clarify simulation comment
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix repository usage
* refactor: simplify FeatureOptInConfirmDialog UI and remove unused translation
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: replace radio buttons with multi-select dropdown in FeatureOptInConfirmDialog
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* resize banner
* refactor: use AnimatedPopover pattern for team selection and rename titleI18nKey to nameI18nKey
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: change 'Just for me' to 'For me', fix width clipping, remove divider
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* update style
* feat: make 'Just for me' mutually exclusive with teams/org and store opt-ins in localStorage
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: remove barrel imports and update to direct imports
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add modal prop to AnimatedPopover to fix scroll lock conflict in dialogs
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: wrap Popover.Content with Portal to prevent layout interference in dialogs
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: use flexbox gap instead of space-y to prevent popover wrapper from affecting layout
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Revert "fix: use flexbox gap instead of space-y to prevent popover wrapper from affecting layout"
This reverts commit 8385adafa890c2f2674aa3ae3b10ca6e0c55ba5c.
* Revert "fix: wrap Popover.Content with Portal to prevent layout interference in dialogs"
This reverts commit e84301a35a99b3559d47c2d37316af290834ca2d.
* Revert "fix: add modal prop to AnimatedPopover to fix scroll lock conflict in dialogs"
This reverts commit b015af2f1cf31ef7087c02cb8535d96cc4a34125.
* feat: replace Radix Dialog and AnimatedPopover with coss-ui components
This migration uses @coss/ui Dialog and Popover components which are built on
Base UI instead of Radix. Base UI handles scroll locking and positioning
differently, which should fix the gap issue when opening the popover inside
the dialog.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: replace remaining @calcom/ui components with coss-ui
- Replace Divider with Separator from @coss/ui
- Replace Label with Label from @coss/ui
- Replace CheckboxField with Checkbox + Label from @coss/ui
- Replace Icon with direct lucide-react icons (UserIcon, BuildingIcon, UsersIcon, CheckIcon)
- Replace showToast with toastManager.add() from @coss/ui
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: replace Popover with Menu component from coss-ui
- Replace Popover + FilterCheckboxField with Menu + MenuCheckboxItem
- MenuCheckboxItem provides built-in checkbox indicator and styling
- MenuSeparator provides proper menu separators
- Cleaner API with onCheckedChange callback
- Better keyboard navigation support
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: align icon and text on same line in MenuCheckboxItem, widen dialog
- Wrap icon and text in flex container with items-center gap-2
- Change dialog width from sm:max-w-md to sm:max-w-lg to prevent text wrapping
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* use dimmed bg for coss dialog title
* refactor: change nameI18nKey/descriptionI18nKey to nested i18n object
- Change OptInFeatureConfig interface to use i18n: { name, description }
- Update all component usages to use featureConfig.i18n.name and featureConfig.i18n.description
- Update test mocks to use new structure
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* feat: add i18n.title to OptInFeatureConfig type definition
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* update texts
* use title instead of name
* update banner style
* refactor: split FeatureOptInConfirmDialog into separate success and confirmation dialogs
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: don't record dismissed info in local storage after successful opt-in
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add checkFeatureOptInEligibility to IFeatureOptInService interface
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* update common.json
* refactor: move tRPC usage from features package to apps/web/modules
Addresses Cubic AI review feedback (confidence 9/10):
- Remove @calcom/trpc import from FeatureOptInConfirmDialog.tsx
- Add FeatureOptInMutations type for passing mutation functions via props
- Move tRPC hooks to useFeatureOptInBanner hook in apps/web/modules
- Pass mutations through FeatureOptInBannerWrapper to dialog component
This maintains the architectural constraint that packages/features
should not import from @calcom/trpc to prevent circular dependencies.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Change 'Booking not found' to throw NOT_FOUND (404) instead of plain Error
- Preserve ErrorWithCode for ChargeCardFailure to properly map to BAD_REQUEST (400)
- Add specific error handling for 'User is not a member of the team' -> FORBIDDEN (403)
- Add specific error handling for 'User ID is required' and 'No payment credential found' -> BAD_REQUEST (400)
- Keep INTERNAL_SERVER_ERROR (500) only for actual server errors like 'Payment app not implemented'
- Update test to expect ErrorWithCode to be re-thrown instead of wrapped in translated Error
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update transaction timeout for large managed event-types
* test: add findMany mock for batch update optimization in handleChildrenEventTypes tests
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: reduce batch size to 50 and use skipDuplicates for workflow linkage
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: add comprehensive tests for batch execution and retry behavior
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: use logger instead of console.log and fix type error
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: update test file to mock logger instead of console.log
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: type issues
* fixup! fix: type issues
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: keith@cal.com <keithwillcode@gmail.com>
* feat: redesign team creation flow to match onboarding-v3 design
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* feat: skip invite options and go directly to email invite step
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* feat: invalidate teams query on submit to refresh teams list
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: use server action to invalidate server-side teams cache
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: conditionally render avatar in browser view to remove extra padding
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix browser view
* test: update team creation E2E tests to use new onboarding-v3 flow
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* test: update auth-index.e2e.ts to use new team creation flow
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* test: update team-management.e2e.ts to use new team creation flow
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: add error handling for createTeam and improve CSV parsing for quoted fields
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* ci(companion): add separate typecheck workflow to catch type errors
This adds a new companion-typecheck.yml workflow that runs TypeScript type
checking for the companion app. This would have caught the missing useEffect
import issue in PR #26931.
Changes:
- Add new companion-typecheck.yml workflow file
- Update pr.yml to call the new workflow when companion files change
- Add typecheck-companion to the required jobs list
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* add lint checks
* test
* remove
* update typecheck
* address review
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(teams): improve membership validation for org admins
- Add organization scope validation to removeMember handler
- Ensure team operations are scoped to users organization context
- Update related tests
* test(teams): align removeMember tests with updated interface
- Add organizationId to test contexts
- Add team.findMany mock for org validation
- Add test cases for org scope validation
* refactor(teams): use TeamRepository for org validation in LegacyRemoveMemberService
- Add findByIdsAndOrgId method to TeamRepository
- Inject TeamRepository into LegacyRemoveMemberService via constructor
- Update RemoveMemberServiceFactory to instantiate and inject TeamRepository
- Update tests to mock TeamRepository instead of direct prisma calls
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: keith@cal.com <keithwillcode@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add CalendarsTasker with sync and trigger.dev versions
This PR implements a CalendarsTasker following the same pattern as
PlatformOrganizationBillingTasker to replace the logic in
apps/api/v2/src/ee/calendars/processors/calendars.processor.ts
New files created:
- CalendarsTasker main orchestrator extending Tasker base class
- CalendarsSyncTasker for sync execution
- CalendarsTriggerTasker for async execution via trigger.dev
- CalendarsTaskService with business logic for ensuring default calendars
- trigger.dev task with queue config and retry settings
- DI modules using @evyweb/ioctopus
- NestJS DI modules for API v2
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: import and deasync
* style: format trigger.config.ts dirs array
Co-Authored-By: unknown <>
* refactor: move prisma query to UserRepository and set onboarding to true
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* refactor: remove credential.key from UserRepository method
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Part 1 of monthly proration feature.
Adds seat change tracking infrastructure with operationId for idempotency.
**Dependency chain:**
- This PR (seat tracking)
- PR #27002 depends on this
* fix(ui): update coss ui components
* fix(sidebar): remove redundant overflow-auto from SidebarContent
ScrollArea already provides the scroll container, so keeping overflow-auto
on the child creates a nested scroll area and prevents scrollFade from
reflecting actual overflow.
Addresses Cubic AI review feedback with confidence 9/10.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: replace loggedInUsersTz with timeZone in busy-times API
- Add new timeZone parameter to CalendarBusyTimesInput
- Mark loggedInUsersTz as deprecated
- Update getBusyTimes endpoint to prioritize timeZone parameter
- Maintain backward compatibility with loggedInUsersTz
- Add validation to ensure at least one timezone parameter is provided
- Update API documentation with new parameter example
Fixes#25423
* fix: improve timezone validation in CalendarBusyTimesInput
- Use custom ValidatorConstraint instead of problematic ValidateIf
- Properly validate that at least one timezone parameter is provided
- Improve code quality and clarity
* fix: update DTOs for getBusyTimes
* cleanup
* fix: abstract normalize timezone logic into platform types util function
* chore: update e2e tests
* fixup
* fixup
---------
Co-authored-by: Ryukemeister <sahalrajiv6900@gmail.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
* fix init
* remove comment
* fix to v2
* type fix
* edge case
* test fix to suit the date storage method
* fix for atoms
* backward compatibility
* fix test mock
* test fix?
* testing a theory
* address cubic
* fix: pass seatUid when rescheduling seated booking from booking limit error
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* test: add test case for seatUid in booking limit reschedule error for seated events
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: ensure default selected calendars handleEnsureDefaultCalendars
* fix: bypass cache when ensureDefaultSelectedCalendars is true
When ensureDefaultSelectedCalendars is true, the cache should be bypassed
to ensure the defaults logic actually runs. Previously, cached data would
be returned before the ensure-defaults logic could execute.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: timezone select component
* fix: type error
* fix: type error
* fix: use object assign
* fix: use data. length
* fix: use props.data reference instead of length for memoization
The useMemo dependency on props.data?.length could return stale timezone
options when the array contents change but the length stays the same.
Using the full props.data reference ensures updates propagate correctly.
Fix identified by Cubic AI (confidence 9/10)
Co-Authored-By: unknown <>
* fix: use data. length
* fix: use props.data reference instead of length for memoization
The useMemo dependency on props.data?.length could return stale timezone
options when the array contents change but the length stays the same.
Using the full props.data reference ensures updates propagate correctly.
Fix identified by Cubic AI (confidence 9/10)
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Test that params are properly passed to async tasker methods
- Test that params are passed in correct order
- Test fallback to sync tasker when async tasker fails
- Test params are passed correctly to sync tasker fallback
- Test error handling when both taskers fail
- Test logging of dispatch information with args
- Test behavior when async tasker is disabled (missing env vars)
- Test error details logging
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* init: team invite link endpoint
* chore: add e2e tests
* fix: use SUCCESS_STATUS and ERROR_STATUS constants in invite output DTO
Address Cubic AI review feedback by using proper enum typing with
SUCCESS_STATUS and ERROR_STATUS constants from @calcom/platform-constants
for consistency with other API v2 output DTOs.
Co-Authored-By: unknown <>
* fixup: add missing memberships module
* fixup: revert previous change, not needed
* chore: use createInvite from teams service
* chore: remove unused value
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: calEventParser CalendarEvent ISP
* update
* fix: complete CalEventParser ISP refactoring - update call sites and fix type errors
- Update getUid call sites to pass only uid instead of whole CalendarEvent
- Update getLocation call sites to pass narrow shape with videoCallData, additionalInformation, location, uid
- Update narrow input shapes to accept null for location (matching CalendarEvent type)
- Update recurringEvent type to accept RecurringEvent | null instead of boolean
- Update videoCallData type to be more flexible ({ type?: string; url?: string })
- Fix ManageLink.tsx to pass recurringEvent directly instead of converting to boolean
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* test: update CalEventParser tests to use narrow input shapes
- Update getPublicVideoCallUrl test to pass uid instead of calEvent
- Update getVideoCallPassword tests to pass videoCallData instead of calEvent
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* few fix
* fix type error
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
- Add interpolation: { escapeValue: false } to AttendeeRequestEmail.tsx
- Add interpolation: { escapeValue: false } to attendee-request-email.ts
- Add interpolation: { escapeValue: false } to bookings-single-view.tsx
This prevents special characters like '/' from being HTML-encoded to '/'
in booking confirmation emails and success page dialogs.
Fixes#26938
* fix: validate hosts belong to team in managed event type create/update
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: add e2e tests for managed event type host validation
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: move update test after get team event-types test to fix ordering
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The UpdateInputAddressLocation_2024_08_13 and related location types were
referenced via getSchemaPath() but not registered with @ApiExtraModels,
causing them to be missing from the generated OpenAPI spec.
This fix adds @ApiExtraModels decorator to the BookingLocationController
to register all location types used in the UpdateBookingLocationInput.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add optimistic updates to EditAvailabilityNameScreen
- Refactor useUpdateSchedule hook with optimistic updates
- Update EditAvailabilityNameScreen to use mutation hook instead of direct API call
- Update EditAvailabilityNameScreen.ios.tsx with same pattern
- Cache is updated immediately on save, then synced with server
- On error, cache is rolled back to previous state
* fix(companion): fix cache update for schedule list when detail cache is empty
- Update list cache in onMutate even when detail cache doesn't exist
- Remove onSettled invalidation that was causing issues with staleTime: Infinity
- Add fallback invalidation in onSuccess when list cache doesn't exist
* fix(companion): fix AvailabilityDetailScreen not reflecting cache updates
The previous optimistic updates implementation in EditAvailabilityNameScreen
correctly updated the React Query cache, but AvailabilityDetailScreen was
still using direct CalComAPIService.getScheduleById() calls with local
useState, completely bypassing the cache.
The disconnect:
- EditAvailabilityNameScreen → useUpdateSchedule → Updates React Query Cache ✓
- AvailabilityDetailScreen → CalComAPIService.getScheduleById → Local state ✗
This meant when a user saved changes to a schedule's name/timezone, the
cache was updated but the detail screen (and subsequently the list screen)
never saw those updates because they weren't reading from the cache.
Changes:
- Refactor AvailabilityDetailScreen.tsx to use useScheduleById hook
- Refactor AvailabilityDetailScreen.ios.tsx to use useScheduleById hook
- Replace direct API calls with React Query for cache synchronization
- Add RefreshControl for pull-to-refresh support
- Use mutation hooks (useSetScheduleAsDefault, useDeleteSchedule) for actions
- Derive availability/overrides data using useMemo from query result
Now when EditAvailabilityNameScreen updates the cache, AvailabilityDetailScreen
automatically reflects those changes because both read from the same cache.
* feat(companion): refactor EditAvailabilityOverrideScreen to use mutation hook
- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook
- Remove local isSaving state, use isPending from mutation hook instead
- Cache is now updated automatically via the mutation hook's optimistic updates
- Consistent pattern with EditAvailabilityNameScreen refactoring
* feat(companion): refactor EditAvailabilityDayScreen to use mutation hook
- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook
- Remove local isSaving state, use isPending from mutation hook instead
- Cache is now updated automatically via the mutation hook's optimistic updates
- Consistent pattern with EditAvailabilityNameScreen and EditAvailabilityOverrideScreen
* fix(companion): refactor edit-availability-hours routes to use useScheduleById hook
The working hours page (page 2 in the flow) was showing stale data because
it used direct CalComAPIService.getScheduleById() calls with local useState,
bypassing the React Query cache.
The disconnect:
- EditAvailabilityDayScreen → useUpdateSchedule → Updates React Query Cache ✓
- edit-availability-hours route → CalComAPIService.getScheduleById → Local state ✗
This meant when a user saved changes to a day's availability, the cache was
updated but the working hours page never saw those updates because it wasn't
reading from the cache.
Changes:
- Refactor edit-availability-hours.tsx to use useScheduleById hook
- Refactor edit-availability-hours.ios.tsx to use useScheduleById hook
- Replace direct API calls with React Query for cache synchronization
- Now all 3 pages in the flow read from the same cache
* fix(companion): make Date Overrides section clickable when empty
Previously, when there were no date overrides, the Date Overrides section
was just a plain View without any press handler, making it impossible for
users to navigate to the edit override page to add new overrides.
This follows the same pattern as other sections (Weekly Schedule, Timezone)
which are always clickable regardless of their content state.
Changes:
- Wrap the 'No Overrides' section in AppPressable with navigation handler
- Add chevron-forward icon to indicate it's tappable
- Apply fix to both AvailabilityDetailScreen.tsx and .ios.tsx
* fix(companion): use useEffect instead of useMemo for side effects
- Change useMemo to useEffect for onActionsReady callback (side effect)
- Move error handling (showErrorAlert, router.back) to useEffect
- Keep early return for error state after useEffect hooks
Fixes Cubic AI review feedback (confidence 9/10)
* feat(companion): add optimistic updates to EventTypeDetail screen
- Enhance useUpdateEventType hook with full optimistic update support
- Add onMutate callback to update cache immediately
- Add onSuccess callback to sync with server response
- Add onError callback to rollback on failure
- Update both eventTypes.detail(id) and eventTypes.lists() caches
- Refactor handleSave in event-type-detail.tsx
- Replace direct CalComAPIService.updateEventType() with useUpdateEventType hook
- Replace direct CalComAPIService.createEventType() with useCreateEventType hook
- Remove manual fetchEventTypeData() call after save (cache updates automatically)
- Replace local saving state with mutation hooks' isPending states
- Refactor handleDelete in event-type-detail.tsx
- Replace direct CalComAPIService.deleteEventType() with useDeleteEventType hook
This ensures the event types list updates immediately after save/delete
without requiring manual refresh.
* feat: monthly-proration-taskerh
* remove cronjob from tasker implementation
* feat: add DI for MonthlyProrationService and TriggerDevLoggerServiceModule
- Create TriggerDevLoggerServiceModule for DI injection of TriggerDevLogger
- Add tokens for TriggerDevLogger in shared.tokens.ts
- Create MonthlyProrationService DI module and container
- Update processMonthlyProrationBatch.ts to use DI container
- Use redactError in Tasker.ts to avoid logging sensitive information
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add optimistic updates to EditAvailabilityNameScreen
- Refactor useUpdateSchedule hook with optimistic updates
- Update EditAvailabilityNameScreen to use mutation hook instead of direct API call
- Update EditAvailabilityNameScreen.ios.tsx with same pattern
- Cache is updated immediately on save, then synced with server
- On error, cache is rolled back to previous state
* fix(companion): fix cache update for schedule list when detail cache is empty
- Update list cache in onMutate even when detail cache doesn't exist
- Remove onSettled invalidation that was causing issues with staleTime: Infinity
- Add fallback invalidation in onSuccess when list cache doesn't exist
* fix(companion): fix AvailabilityDetailScreen not reflecting cache updates
The previous optimistic updates implementation in EditAvailabilityNameScreen
correctly updated the React Query cache, but AvailabilityDetailScreen was
still using direct CalComAPIService.getScheduleById() calls with local
useState, completely bypassing the cache.
The disconnect:
- EditAvailabilityNameScreen → useUpdateSchedule → Updates React Query Cache ✓
- AvailabilityDetailScreen → CalComAPIService.getScheduleById → Local state ✗
This meant when a user saved changes to a schedule's name/timezone, the
cache was updated but the detail screen (and subsequently the list screen)
never saw those updates because they weren't reading from the cache.
Changes:
- Refactor AvailabilityDetailScreen.tsx to use useScheduleById hook
- Refactor AvailabilityDetailScreen.ios.tsx to use useScheduleById hook
- Replace direct API calls with React Query for cache synchronization
- Add RefreshControl for pull-to-refresh support
- Use mutation hooks (useSetScheduleAsDefault, useDeleteSchedule) for actions
- Derive availability/overrides data using useMemo from query result
Now when EditAvailabilityNameScreen updates the cache, AvailabilityDetailScreen
automatically reflects those changes because both read from the same cache.
* feat(companion): refactor EditAvailabilityOverrideScreen to use mutation hook
- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook
- Remove local isSaving state, use isPending from mutation hook instead
- Cache is now updated automatically via the mutation hook's optimistic updates
- Consistent pattern with EditAvailabilityNameScreen refactoring
* feat(companion): refactor EditAvailabilityDayScreen to use mutation hook
- Replace direct CalComAPIService.updateSchedule calls with useUpdateSchedule hook
- Remove local isSaving state, use isPending from mutation hook instead
- Cache is now updated automatically via the mutation hook's optimistic updates
- Consistent pattern with EditAvailabilityNameScreen and EditAvailabilityOverrideScreen
* fix(companion): refactor edit-availability-hours routes to use useScheduleById hook
The working hours page (page 2 in the flow) was showing stale data because
it used direct CalComAPIService.getScheduleById() calls with local useState,
bypassing the React Query cache.
The disconnect:
- EditAvailabilityDayScreen → useUpdateSchedule → Updates React Query Cache ✓
- edit-availability-hours route → CalComAPIService.getScheduleById → Local state ✗
This meant when a user saved changes to a day's availability, the cache was
updated but the working hours page never saw those updates because it wasn't
reading from the cache.
Changes:
- Refactor edit-availability-hours.tsx to use useScheduleById hook
- Refactor edit-availability-hours.ios.tsx to use useScheduleById hook
- Replace direct API calls with React Query for cache synchronization
- Now all 3 pages in the flow read from the same cache
* fix(companion): make Date Overrides section clickable when empty
Previously, when there were no date overrides, the Date Overrides section
was just a plain View without any press handler, making it impossible for
users to navigate to the edit override page to add new overrides.
This follows the same pattern as other sections (Weekly Schedule, Timezone)
which are always clickable regardless of their content state.
Changes:
- Wrap the 'No Overrides' section in AppPressable with navigation handler
- Add chevron-forward icon to indicate it's tappable
- Apply fix to both AvailabilityDetailScreen.tsx and .ios.tsx
* fix(companion): use useEffect instead of useMemo for side effects
- Change useMemo to useEffect for onActionsReady callback (side effect)
- Move error handling (showErrorAlert, router.back) to useEffect
- Keep early return for error state after useEffect hooks
Fixes Cubic AI review feedback (confidence 9/10)
* fix(event-types): keep slug in sync with title until manually edited
Changed from checking `touchedFields` to `dirtyFields` when deciding
whether to sync the slug with the title. This fixes the issue where
merely focusing on the slug field would stop the sync, even if the
user didn't actually edit it.
Now the slug stays in sync with the title until the user actually
modifies the slug value.
Note: The hardcoded "Slug" label for platform users was preserved from
the original code. Localizing it would be a separate enhancement.
Fixes#26265
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: add ensureProtocol helper to handle URLs without protocol
Some container orchestration tools (like Coolify) strip the protocol
from URL environment variables. This causes `new URL()` to throw
`ERR_INVALID_URL` because strings like "sub.domain.com" are invalid
without a protocol prefix.
This fix adds an `ensureProtocol` helper function that:
- Returns empty string for null/undefined URLs
- Preserves URLs that already have http:// or https://
- Prepends https:// to URLs missing the protocol
Applied to WEBAPP_URL, WEBSITE_URL, and CAL_URL env var parsing.
Fixes#25774
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* Clarify URL protocol handling in comments
Updated comment to clarify handling of URLs.
* Refactor slug handling in CreateEventTypeForm
---------
Co-authored-by: simiondolha <simiondolha@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* feat: add GitHub Action to check Prisma migrations match schema
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: use CI_DATABASE_URL secret instead of hardcoded URL
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add test field to verify prisma migration check workflow
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use git-based check for prisma changes to bypass paths-filter issue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add migration for test field to verify prisma migration check workflow
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: revert test field and migration after verifying workflow
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: split paths-filter into two actions to fix predicate-quantifier issue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: rename has_companion to has-companion for consistent naming
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: update steps.filter references to steps.filter-exclusions
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: make sure we dont implement directly from @calcom/features
* fix: preserve the original booking duration instead of using the default event type length
* chore: update tests
* fix: merge conflicts
* chore: revert linting changes
* chore: implement PR feedback
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
This PR adds https://vercel.com/blog/introducing-react-best-practices for your coding agent using `npx add-skill vercel-labs/agent-skills` command
The skills are added to `.claude/`, `.cursor/`, and `.opencode/` directories to provide React and Next.js performance optimization guidance for AI-assisted workflows.
## Updates since last revision
Addressed Cubic AI review feedback for issues with confidence >= 9/10:
- **rerender-dependencies.md** - Replaced `console.log(user.id)` with `fetchUserDetails(user.id)` to avoid logging sensitive information
- **server-after-nonblocking.md** - Removed `sessionCookie` from `logUserAction` call to avoid logging sensitive authentication data, added `await` to async call
- **bundle-conditional.md** - Added `loadError` state and `setLoadError` setter to fix undefined `setEnabled` reference
- **advanced-event-handler-refs.md** - Updated `useWindowEvent` handler signature to accept `Event` parameter and forward it to the stored handler
- **rerender-derived-state.md** - Closed `<nav>` elements in both examples for valid JSX
Fixes applied to both `.claude` and `.cursor` skill directories for consistency.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] N/A 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.
- [x] N/A, I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
These are documentation files for AI coding agents. No runtime testing required - review the markdown files to verify the example code snippets are correct.
## Checklist for human review
- [ ] Verify example code snippets in the skill files are syntactically correct
- [ ] Confirm the fixes don't introduce new issues in the documentation examples
- [ ] Check that `.claude` and `.cursor` directories have consistent content
---
Link to Devin run: https://app.devin.ai/sessions/f7f7e67fdeea4b22a4817d63ed9e1759
Requested by: unknown ()
## What does this PR do?
When a filter segment is selected and the user clicks it again to deselect, this PR now clears all active filters in addition to deselecting the segment.
Previously, deselecting a segment only called `setSegmentId(null)`, which removed the segment selection but left the filters in place. Now it calls `clearAll()` which clears both the segment selection and all active filters.
## 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. Go to a page with DataTable and filter segments (e.g., Bookings page or Organization Members page)
2. Select a filter segment (e.g., "My Bookings" system segment or a user-created segment)
3. Verify filters are applied (URL contains `activeFilters`)
4. Click the same segment again to deselect it
5. Verify the segment is deselected AND all filters are cleared (URL should no longer contain `activeFilters`)
An e2e test has been added to `filter-segment.e2e.ts`:
- `Deselecting a segment clears all active filters`
## 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 that calling `clearAll()` instead of `setSegmentId(null)` is the intended behavior (clears both segment selection and all active filters)
- [ ] Consider if there are use cases where users might want to keep filters when deselecting a segment
---
> **Link to Devin run**: https://app.devin.ai/sessions/44f22b80d9c442bdb334d04dac2b476d
> **Requested by**: @eunjae-lee
* feat(agents): add modular engineering rules from 2026 standards
Add a rules directory with individual rule files derived from the
Cal.com Engineering in 2026 and Beyond blog post. Rules are organized
by section (architecture, quality, data, api, performance, testing,
patterns, culture) following the Vercel agent-skills structure.
Includes:
- _sections.md defining rule categories and impact levels
- _template.md for creating new rules
- 14 individual rule files covering key engineering standards
- README documenting the rules structure and usage
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(agents): consolidate DI and Repository+DTO docs into rules
- Move di-pattern.md content to rules/patterns-di-pattern.md
- Extract Repository + DTO section from knowledge-base.md into:
- rules/data-repository-methods.md (method naming conventions)
- rules/data-dto-boundaries.md (DTO location and naming)
- Update knowledge-base.md to reference the new rule files
- Delete old di-pattern.md file
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore(agents): remove stub reference sections from knowledge-base.md
The rules directory is self-contained with its own README, so these
redirect sections are unnecessary clutter.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor(agents): combine DI pattern rules into single file
Merged patterns-di-pattern.md into patterns-dependency-injection.md
to eliminate overlap and create one comprehensive DI guide.
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>
* fix(user-dropdown): make SSR-safe and fix ESLint warning for Beacon
* fix: added ssr guard in second useEffect and added type for beacon instead of ts-ignoring
* fix: add dependency array to Beacon useEffect
Address review feedback by adding [user?.username] dependency array
to the Beacon session-data useEffect, improving performance by
preventing unnecessary re-runs on every render.
Co-authored-by: albin-baby-2002 <albinbaby2002@gmail.com>
Co-Authored-By: unknown <>
* test: add automated tests for UserDropdown Beacon functionality
Add comprehensive tests for:
- Beacon session-data calls with correct payload
- Handling of undefined Beacon (no errors)
- Username change triggering useEffect re-run
- SSR safety (window undefined check)
- Component rendering states (loading, user available, no user)
Co-authored-by: albin-baby-2002 <albinbaby2002@gmail.com>
Co-Authored-By: unknown <>
* refactor: remove useless comments and misleading SSR test
- Remove redundant comments above vi.mock calls (per keithwillcode feedback)
- Remove SSR safety test that provided false coverage since jsdom always has window defined (per Cubic feedback)
- The 'should not throw error when Beacon is undefined' test already covers graceful handling
Co-Authored-By: unknown <>
* fix: remove unused @ts-expect-error directives
The Window interface is augmented with optional Beacon and Support properties,
so TypeScript allows deleting them without errors.
Co-Authored-By: unknown <>
* fix: handle lazy-loaded Beacon with polling
When Beacon loads lazily after component mount, the useEffect now polls
every second until Beacon becomes available, then sends session data
and clears the interval. This ensures session info is always sent even
when HelpScout loads asynchronously.
Added test to verify lazy loading behavior works correctly.
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: albin-baby-2002 <albinbaby2002@gmail.com>
* fix: remove Stale label when Devin starts completing a stale PR
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update Devin prompt to remove Stale label after pushing changes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: use lowercase 'stale' label to match repository label
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Update stale-pr-devin-completion.yml
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: get event-types non org users
* fix: add excludeOrgUsers parameter to findByUsername for targeted filtering
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: create dedicated findByUsernameExcludingOrgUsers function
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: add e2e test for same username org vs non-org user event types
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: move e2e test for same username org vs non-org to 2024_06_14
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: add organizationId to orgUser in e2e test
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: reorder afterAll cleanup to delete users before team
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: add organizationId null check to findByUsernameExcludingOrgUsers
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* Revert "fix: add organizationId null check to findByUsernameExcludingOrgUsers"
This reverts commit 0d31e3d5d7c0eaa408939d697ea790521e520a3c.
* fixup! fix: add organizationId null check to findByUsernameExcludingOrgUsers
* fix: restore /public suffix in e2e test URL
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: use findByUsernameExcludingOrgUsers in getEventTypeByUsernameAndSlug when no org context
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fixup! fix: use findByUsernameExcludingOrgUsers in getEventTypeByUsernameAndSlug when no org context
* refactor: remove unused getEventTypesPublicByUsername from 2024_06_14 service
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fix: test user creation
* chore: add test for org event-types
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* docs: improve beforeEventBuffer and afterEventBuffer descriptions in API v2
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* fixup! docs: improve beforeEventBuffer and afterEventBuffer descriptions in API v2
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* introduce a dependsOn permission string
* fix permissions toggle
* add DFS to allow permission chains
* Update packages/features/pbac/utils/permissionTraversal.ts
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
* fix splitPerms to work with attributes or dotted resources
* CodeRabbit Generated Unit Tests: Add unit tests for permissions hooks, registry, and traversal
* Fix UTG issues (iteration 1)
---------
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
* Documentation edits made through Mintlify web editor
* Documentation edits made through Mintlify web editor
---------
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* clean-up nav
* Documentation edits made through Mintlify web editor
* Documentation edits made through Mintlify web editor
* further clean up
* add oauth in sidebar
* --
* land in v2
* land in v2
* Documentation edits made through Mintlify web editor
* Documentation edits made through Mintlify web editor
* cleanup
* Documentation edits made through Mintlify web editor
* deprecated v1
---------
Co-authored-by: Syed Ali Shahbaz <alishahbaz7@gmail.com>
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
* refactor: migrate MembershipRole usages to PBAC permission checks
- Refactor get.handler.ts to use PermissionCheckService for canUpdateTeams
- Refactor checkForInvalidAppCredentials.ts to use getTeamIdsWithPermission
- Refactor outOfOffice.utils.ts to use checkPermission for ooo.update
- Refactor checkIfOrgNeedsUpgrade.handler.ts to use organization.manageBilling
- Refactor getActiveOnOptions.handler.ts to use eventType.update permission
- Refactor WorkflowRepository.ts to use workflow.update permission
- Refactor organization.tsx to use team.update permission
- Refactor getEventTypesByViewer.ts to use eventType.update permission
- Refactor getPublicEvent.ts to use team.read permission for private teams
- Update CreateNewOutOfOfficeEntryButton.tsx to use canUpdateOOO prop
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: use organization.update permission instead of team.update for org management
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: rename teamsWithEventTypeManagePermission to teamsWithEventTypeUpdatePermission
Renamed variable to match the permission string being used (eventType.update)
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf(app-store): convert services to factory functions to narrow SDK type exports
This change converts CRM, Calendar, Payment, and Analytics services from
exporting classes to exporting factory functions that return interface types.
This prevents SDK types (HubSpot, Stripe, etc.) from leaking into the type
system when TypeScript emits .d.ts files.
Services modified:
- CRM: hubspot, salesforce, closecom, pipedrive-crm, zoho-bigin, zohocrm
- Calendar: all 13 calendar services
- Payment: stripe, paypal, alby, btcpayserver, hitpay, mock-payment-app
- Analytics: dub
Video adapters were audited and already use factory pattern.
WIP: Salesforce CRM service has a type error that needs fixing.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(salesforce): add type assertion for appOptions in factory function
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: update remaining consumers to use factory functions
- getAnalytics.ts: call factory function instead of new
- getConnectedApps.ts: call factory function instead of new
- salesforce/CrmService.ts: fix type assertion with default value
- salesforce/routingForm/incompleteBookingAction.ts: use factory function
- salesforce/routingFormBookingFormHandler.ts: use factory function
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(salesforce): add SalesforceCRM interface for Salesforce-specific methods
- Create SalesforceCRM interface extending CRM with findUserEmailFromLookupField and incompleteBookingWriteToRecord methods
- Add createSalesforceCrmServiceWithSalesforceType factory function for internal Salesforce modules
- Update routing form files to use the new factory function with proper typing
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(salesforce): correct return type in SalesforceCRM interface
The findUserEmailFromLookupField method returns { email: string; recordType: RoutingReasons } | undefined,
not string | undefined. Updated the interface to match the actual implementation.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: update all call sites to use factory functions
- Update calendar API add files to use factory functions (7 files)
- Update Google Calendar test files to use factory functions (3 files)
- Update Salesforce test files to use SalesforceCRM interface
- Add testMode parameter to createSalesforceCrmServiceWithSalesforceType
- Remove unused @ts-expect-error directive in bookingScenario.ts
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: add GoogleCalendar interface and update factory functions for service-specific methods
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: add type annotation to attendee parameter in google-calendar.e2e.ts
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: update selected-calendars/route.ts to use GoogleCalendar factory function
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: update remaining GoogleCalendarService call sites to use factory function
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: remove explicit type annotation in google-calendar.e2e.ts to fix type error
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: update test mocks to use factory function pattern for calendar and payment services
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix: convert MockPaymentService to factory function in setupVitest.ts
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(api-v2): call calendar service factory functions instead of using new
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* perf(trpc): add TypeScript project references to reduce build file count
- Add tsconfig.build.json to app-store with composite settings for declaration emit
- Add tsconfig.server.build.json to trpc with project references
- Add types and typesVersions to app-store package.json to redirect type resolution to dist-types
- Add build:types script to app-store for generating declarations
- Add dist-types to .gitignore
This reduces the tRPC build file count from 7,733 to 5,618 files (27% reduction)
by having TypeScript resolve to prebuilt .d.ts files instead of source files.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(app-store): add fallback to typesVersions for CI compatibility
The typesVersions now prefers dist-types but falls back to source files
when dist-types don't exist. This fixes CI type-check failures while
still allowing the optimization when dist-types are built.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* Added build dependency for tRPC on app-store
* fix(app-store): exclude test directories from tsconfig.build.json
Exclude __tests__, __mocks__, and tests directories from the build
config to prevent test utility files (which import from outside rootDir)
from being included in the declaration emit.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(turbo): add build:types task definition for app-store
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(turbo): remove circular build dependency between trpc and app-store
The build:types task for app-store transitively imports from @calcom/features,
which imports from @calcom/trpc. This creates a circular build dependency when
@calcom/trpc#build depends on @calcom/app-store#build:types.
Solution: Remove the turbo dependency. The typesVersions fallback pattern
['./dist-types/*', './*'] will use source files when dist-types/ doesn't exist,
and use prebuilt declarations when they do exist (for faster builds).
Also exclude dist-types from regular tsconfig.json to prevent 'Cannot write file'
errors when dist-types/ exists.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* refactor(app-store): rename dist-types to types for consistency
Rename the prebuilt TypeScript declarations folder from dist-types to types
to match the convention used by @calcom/trpc and other packages in the monorepo.
Updated files:
- tsconfig.build.json: declarationDir and exclude
- tsconfig.json: exclude
- package.json: typesVersions paths
- turbo.json: build:types outputs
- .gitignore: ignore path
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(api-v2): update ICS calendar test to mock factory function instead of class prototype
The IcsFeedCalendarService is now a factory function, not a class, so
jest.spyOn(IcsFeedCalendarService.prototype, 'listCalendars') no longer works.
Updated to use jest.spyOn(appStore, 'IcsFeedCalendarService').mockImplementation()
to mock the factory function return value instead.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* refactor(app-store): rename factory functions to Build* naming convention
- Rename Calendar factory functions from create*CalendarService to BuildCalendarService
- Rename CRM factory functions from create*CrmService to BuildCrmService
- Rename Payment factory functions from PaymentService to BuildPaymentService
- Rename Analytics factory function from createDubAnalyticsService to BuildAnalyticsService
- Update all index.ts re-exports to use new names
- Update all call sites throughout the codebase
- Update test mocks to use new factory function names
This makes it clear that these are factory functions, not class constructors.
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(app-store): update remaining CalendarService and PaymentService imports to Build* names
- Update api/add.ts files for applecalendar, caldavcalendar, exchange2013calendar, exchange2016calendar, exchangecalendar, ics-feedcalendar
- Update mock-payment-app index.ts to export BuildPaymentService
- Fix googlecalendar test import alias for createInMemoryDelegationCredentialForBuildCalendarService
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(tests): update payment service mocks to use BuildPaymentService
- Update setupVitest.ts to use BuildPaymentService instead of PaymentService
- Update handlePayment.test.ts mock to use BuildPaymentService
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* refactor: address PR review comments - rename to Build* and remove unused code
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* refactor: remove explanatory comments and fix indentation
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* chore: update .gitignore to use *.tsbuildinfo pattern
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* fix(api-v2): update IcsFeedCalendarService to BuildIcsFeedCalendarService in e2e test
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* style: format CalendarService.test.ts
Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>
* Delete apps/ui-playground/next-env.d.ts
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add db schema
* Add `CredentialRepository.findByTeamIdAndSlugs`
* Add enabled app slugs for attribute syncing
* Create repository for `IntegrationAttributeSync`
* Create zod schemas
* Create `AttributeSyncUserRuleOutputMapper`
* Create `IntegrationAttributeSyncService`
* Create DI contianer
* Create trpc endpoints
* Create page
* Include team name in `CredentialRepository.findByTeamIdAndSlugs`
* Update schema and relations
* Update types and schemas
* Add more methods to IntegrationAttributeSyncRepository
* Add more services to `IntegrationAttributeSyncService`
- getById
- Init updateIncludeRulesAndMappings
* Refactor `getTeams.handler` to use repository
* Create `createAttributeSync` trpc endpoint
* Create `updateAttributeSync` trpc endpoint
* Add router to trpc
* Create attribute sync child components
* Pass custom actions to `FormCard`
* Create `IntegrationAttributeSyncCard`
* Pass inital props via server side
* Fix prop
* Only refetch on mutation
* Fixes
* Add form error when duplicate field and attribute combo
* Add `updateTransactionWithRUleAndMappings` logic
* Adjust zod schemas
* Service add `updateIncludeRulesAndMappings`
* Pass orgId from server to component
* Rename types
* Add deleteById method to repository
* Add name to integrationAttributeSync
* Add deleteById method to service
* Rename method
* Add deleteAttributeSync trpc endpoint
* Make the IntegrationAttributeSyncCard a dummy component
* test: add tests for IntegrationAttributeSync feature
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Move creating a attribute sync record to the service
* Add i18n strings
* Safe select credential in find by id and team
* Fix default credentialId value in form
* Update repository return types
* Add i18n string
* Make credentialId optional for form schema
* Fix label
* Add cascade deletes
* Add verification that syncs belong to org
* Create mapper for repository output
* Type fixes
* Remove old test file
* Pass `attributeOptions` from parent to children
* Infer types from zod schema
* Type fixes
* Type fix
* Clean up
* Add i18n strings
* Remove unused file
* Address feedback
* Add migration file
* Address feedback
* Add validation for new integration values
* Remove unused router
* Move away from z.infer to z.ZodType
* Clean up comments
* Type fix
* Type fixes
* Type fix
* fix: add passthrough to syncFormDataSchema to preserve extra fields
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove incorrect test that expected extra fields to pass through syncFormDataSchema
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add endpoint for SF to call
* Create scratch org config
* Create sf cli scripts
* Create package logic
* Update README
* Remove unused file
* Add indexes
* Add aria label
* Address feedback - consistent validation
* Fix import paths for attribute types
* Add `CredentialRepository.findByAppIdAndKeyValue`
* Get credential by instance URL
* Verify incoming sfdc orgId matches credential sfdc orgId
* Rename method
* Get user name integration syncs
* refactor: change attributeSyncRules array to singular attributeSyncRule
The database schema enforces a one-to-one relationship between
IntegrationAttributeSync and AttributeSyncRule (via @unique constraint),
and the UI only supports a single rule. This change makes the TypeScript
type match the database schema and UI behavior.
Changes:
- Update IntegrationAttributeSync interface to use attributeSyncRule: AttributeSyncRule | null
- Update mapper to return singular rule instead of wrapping in array
- Update UI component to access sync.attributeSyncRule directly
- Update IIntegrationAttributeSyncUpdateParams Omit type
- Update tests to use attributeSyncRule: null instead of attributeSyncRules: []
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Convert `membershipRepository.findAllByUserId` to a normal method
* Add temp files to git ignore
* Init and process team conditions
* Biome formatting
* Add `AttributeService` to get user attributes
* Add DI container for AttributeService
* AttributeSyncService evaluate attribute conditions
* Create DI container for attributeSyncService
* Return result for full condition
* Evaluate if attribute sync should apply to user
* Add method
* Change PrismaAttributeOptionRepository to instance methods
* Init AttributeSyncFieldMappingsService and process attribute syncs
* Add AttributeSyncFieldMappingService DI container
* Refactor orgId to organizationId
* Add membership validation to sync field service
* AttributeSYncFieldMappingService use repository methods
* AttributeSyncFieldMappingService.processMappings add mapping logic
* Add DI tokens
* user-sync endpoint to implement attribute syncing
* Validate team belongs to org for rule
* test: add tests for AttributeSyncRuleService, AttributeSyncFieldMappingService, and AttributeService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Remove duplicate migration file
* Fix merge conflict
* fix: resolve type errors in attribute sync feature (#26814)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Rename variable
* Fix log typo
* Fix file name
* Add error logging
* Use credential teamId
* fix: add missing mockTeamRepository and team validation to tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Fix naming
* Fix file import
* Type fix
* Pass MembershipRepository as a dep in AttributeSyncFieldMappingService
* Type fix
* fix: add mockMembershipRepository to AttributeSyncFieldMappingService tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Update packages/app-store/salesforce/api/user-sync.ts
Add error handling when getting orgId from stored salesforce id
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: address Cubic code review comments
- PrismaAttributeRepository: use nested select instead of include: true for options
- CredentialRepository: use this.primaClient instead of global prisma, use select instead of include for relations
- AttributeSyncFieldMappingService: optimize O(n*m) complexity with Map lookup for O(1) access
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Fix typo in CredentialRepository
* Update README
* Update sfdx-project
* Add SFDC package tests
* fix: improve Salesforce Apex test assertions to verify actual behavior
- Enhanced CalComHttpMock to track HTTP callout invocations and capture requests
- Updated UserUpdateHandlerTest to verify HTTP callouts are made with correct data
- Updated CalComCalloutQueueableTest to verify HTTP callouts are made correctly
- Replaced System.assert(true, ...) with meaningful assertions that verify:
- Correct number of HTTP callouts
- Request body contains expected fields
- Request method is POST
Addresses Cubic AI review feedback (confidence 9/10 issues only)
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2026-01-15 09:19:57 -05:00
1006 changed files with 59279 additions and 9675 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
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
about:Suggest a feature or bug report for cal.com/platform
title:""
labels:["platform","🚨 needs approval"]
assignees:""
---
### Is your proposal related to a problem?
<!--
Provide a clear and concise description of what the problem is.
For example, "I'm always frustrated when..."
-->
(Write your answer here.)
### Describe the solution you'd like
<!--
Provide a clear and concise description of what you want to happen.
-->
(Describe your proposed solution here.)
### Describe alternatives you've considered
<!--
Let us know about other solutions you've tried or researched.
-->
(Write your answer here.)
### Additional context
<!--
Is there anything else you can add about the proposal?
You might want to link to related issues here, if you haven't already.
-->
(Write your answer here.)
### Requirement/Document
<!--
Is there any type of document that could support that feature?
-->
(Share it here.)
---
##### House rules
- If this issue has a `🚨 needs approval` label, don't start coding yet. Wait until a core member approves feature request by removing this label, then you can start coding.
- For clarity: Non-core member issues automatically get the `🚨 needs approval` label.
- Your feature ideas are invaluable to us! However, they undergo review to ensure alignment with the product's direction.
- Follow Best Practices lined out in our [Contributor Docs](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
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.