* fix: restore @ts-expect-error for CacheProvider type mismatch
PR #30140 removed these comments assuming they were stale now that
the repo is on TypeScript 5. The comments were about the compiler
version, but the actual incompatibility is between react-inlinesvg/provider's
CacheProvider (typed to return a bare ReactNode) and this repo's pinned
@types/react@18.0.26, which requires JSX components to return Element | null.
ReactNode includes undefined, so the JSX usage fails type-checking.
Verified yarn build completes successfully with this restored.
Fixes#30170
* docs: explain the actual @ts-expect-error compatibility condition
Per @coderabbitai review on #30171: the old wording ("remove when
upgrading typescript to v5") was itself stale/misleading, since the
incompatibility is with the pinned @types/react version, not the
TypeScript compiler version. Documents the real condition instead.
'the the event type' (slots API property description), 'for for new
booking' (EventManager debug log), and 'one-one one template'
(en locale).
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
The first member of a dynamic group decides which conferencing app the group
booking uses. getPublicEvent read users[0] straight from findUsersByUsername,
which does not guarantee the order given in the URL, so /alice+bob could end up
using bob's preference — or fall back to Cal Video when only alice has one set.
handleNewBooking already sorts by the dynamic user list for this exact reason
(sortUsersByDynamicList in getLocationValuesForDb.ts). Applying the same order
here keeps the advertised location and the booked location in sync.
PayPal order creation divided every amount by 100 before sending it,
regardless of currency. JPY has no minor unit, so `convertToSmallestCurrencyUnit`
stores it unscaled and dividing corrupted it: a JPY 10000 event asked PayPal
to collect 100.
Use the same zero-decimal-aware conversion that the price input and
`formatPrice` already use, so the amount charged matches the amount stored
and displayed. Currencies with minor units are unaffected — among PayPal's
supported currencies only JPY is both zero-decimal and supported, and the
added tests pin USD, EUR, HUF and TWD to their existing behaviour.
HitPay's payment-requests API takes `amount` as a decimal string in major
units, so the integration divided by 100 on the way out and multiplied by 100
on the way back in. Both conversions were unconditional, but the price input
writes the amount through `convertToSmallestCurrencyUnit`, which stores
zero-decimal currencies unscaled.
For the three zero-decimal currencies HitPay offers — JPY, KRW and VND — a
¥10,000 event therefore asked HitPay to collect ¥100. The two unconditional
conversions cancelled out in the database, so the Payment row and the booker's
page both still showed ¥10,000 while only 1% was charged, and `refund()` is
not implemented for this app.
Route both directions through the shared helpers so the amount charged matches
the amount stored and displayed. Currencies with a minor unit are unaffected;
the added tests pin USD and SGD to their existing behaviour.
* removed bug of language update work properly
* feat: implement general settings view and user form components for profile configuration
---------
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
* Add Clara app (link-as-an-app, AI & Automation)
Clara is a website conversion agent: it answers website visitors in text and
voice and books meetings into their Cal.com. This is a link-as-an-app listing
(same pattern as the existing Chatbase app) — it opens a setup guide at
claraconverts.com/guide/cal-com; nothing installs into the user's account.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* Point Clara listing CTA to the Cal.com setup guide
Addresses CodeRabbit review: the "Get started" link in DESCRIPTION.md pointed
to the homepage; point it to claraconverts.com/guide/cal-com so the CTA leads
straight to Cal.com onboarding (matching the app's externalLink).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
* fix: trim whitespace in parseIpFromHeaders to prevent IP banlist bypass
Fixes#29851
parseIpFromHeaders did not trim whitespace after splitting on comma
(or from array elements), so a padded x-forwarded-for header like
' 1.2.3.4 , 5.6.7.8' would fail an exact-match lookup against
IP_BANLIST, allowing a banned IP to bypass the check.
Added regression tests covering padded string, array, and an
end-to-end isIpInBanlist test reproducing the reported bypass.
* fix: address CodeRabbit review feedback
- Guard against undefined rawIp when parsing an empty header array
- Delete IP_BANLIST env var in test cleanup instead of assigning undefined
- Add regression test for empty array input to parseIpFromHeaders
* test: add regression test for tab/whitespace trimming in parseIpFromHeaders
The Hebrew locale was missing a space between the number and the minute/hour
short form abbreviation (e.g. "15ד׳" instead of "15 ד׳"). This caused
screen readers to pronounce the duration incorrectly ("15 metres") and
made the booking page duration text difficult to read.
Also fixes the same issue for hour_one_short.
* fix(bookings): refund all seat payments when cancelling a paid seated booking
A paid seated booking creates one Payment row per seat on the same bookingId (each seat's payment_intent.succeeded webhook flips only its own payment via handlePaymentSuccess). processPaymentRefund only refunded payment.find((p) => p.success) — the first successful payment — so fully cancelling a multi-seat paid booking with an auto-refund policy refunded a single seat and left the other attendees charged with no error surfaced.
Refund every successful, not-yet-refunded payment instead.
Tests: unit regression in processPaymentRefund.test.ts (all payments refunded, verified red before the fix); integration test in handleCancelBooking.test.ts proving a full cancel of a seated multi-payment booking hands all payments to the refund path.
* fix(bookings): make seat refunds resilient and drop dayjs in test
Address CodeRabbit review on #29685:
- processPaymentRefund now attempts every seat's refund independently and throws an aggregate error, so one provider failure no longer skips the remaining seats.
- Add a regression test for that resilience, and use native Date instead of dayjs for fixed-offset test timestamps.
When a booking question is optional and left unanswered, the field value
stored in customInputs can be null. The previous guard only checked
for empty strings, so null (and undefined) would coerce to the literal
strings "null"/"undefined" inside the template literal, polluting calendar
event descriptions.
Fixes#29688
Fixes an issue where cancellation emails were sending ICS files with
METHOD:REQUEST instead of METHOD:CANCEL. As per RFC 5546, calendar
clients only process cancellations correctly when METHOD:CANCEL is used,
otherwise cancelled events remain in attendee calendars with active
reminders.
- Use METHOD:CANCEL for cancelled booking ICS files
- Keep METHOD:REQUEST for non-cancelled booking invitations
- Preserve STATUS:CANCELLED and sequence handling
- Add tests to verify the generated ICS method for cancellations
* refactor: derive category metadata from single Record source of truth
* test: add test for category order and entries sync
* fix: check for duplicates
Adds `paths:` YAML frontmatter so each rule only loads into Claude Code
context when the agent is working on relevant files, keeping context lean
in a large monorepo.
- architecture-page-level-auth: fires on page.tsx and layout.tsx
- data-prisma-migrations: fires on packages/prisma/**
- testing-playwright: fires on playwright dirs, *.e2e.ts, and api e2e dirs
- api-thin-controllers: fires on *.controller.ts files only
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
generateUsernameSuggestion built its numeric suffix with
String(rand).padStart(4 - rand.toString().length, "0"). padStart's first
argument is the target total length, not the number of pad characters, so the
expression only padded single-digit numbers: rand=1 became "001" but rand=42
became "42" and rand=999 stayed "999". Suggested usernames therefore had
inconsistent suffix widths, and the magic 4 didn't match the 3-wide output the
existing test pins ("johnny001").
Pull the suffix into a small helper that always pads to 3 digits, so collisions
and the returned value share one definition and every suggestion is uniformly
zero-padded. Add unit tests for the first-attempt path and for two- and
three-digit suffixes; the two-digit case fails on the old code.
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
- Remove two console.log statements dumping private hashed link data
in update.handler.ts (fixes production data leakage)
- Replace console.log(e) with structured logger.error in
getScheduleByEventTypeSlug.handler.ts
- Fix @ts-expect-error by typing reduce accumulator as
Record<string, unknown> in update.handler.ts
- Narrow Prisma include to select in duplicate.handler.ts:
team and webhooks now fetch only required fields
Fixes#29500
Co-authored-by: lucadapice <dapice.lc@gmail.com>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
* fix: load more users on table scroll
Signed-off-by: Chayan Das <daschayan8837@gmail.com>
* refactor: move user listing logic to UserRepository
Add a dedicated listUsers method with search and cursor pagination support.
Add repository tests covering filtering and pagination.
Ensure user listing metadata remains consistent with the returned results.
Signed-off-by: Chayan Das <daschayan8837@gmail.com>
---------
Signed-off-by: Chayan Das <daschayan8837@gmail.com>
* refactor: use userRepository class methods for signup handlers
* feat: add user repo modules for DI
* feat: build user repo container
* test: integration test for user repository
* fix: use DI container in selfhosted
* refactor: tests, module, container and handlers
---------
Co-authored-by: Bandhan Majumder <bandhanmajumder16@gmail.com>
- disable update button until form is valid
- add required field validation
- add translation key for password complexity message
Signed-off-by: Chayan Das <daschayan8837@gmail.com>
getUserVerifiedPhoneNumber incorrectly called getUserVerifiedEmail
on the repository, causing verified phone numbers to never be found.
Fixes#29450
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
teamMetadataSchema.parse(team?.metadata) was called before checking
if team exists, causing a Zod validation error on undefined instead
of the intended NotFoundException. Also corrected the misleading
error message from 'user not found' to 'team not found'.
Co-authored-by: Pranav Gawande <pranavv00@users.noreply.github.com>
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
ProfilesRepository was routing create() and update() calls through
PrismaReadService, which silently breaks in read-replica production
environments. Added PrismaWriteService injection and routed write
operations through dbWrite, matching the existing pattern used by
TeamsRepository and other repositories.
Co-authored-by: Pranav Gawande <pranavv00@users.noreply.github.com>
Co-authored-by: Bandhan Majumder <133476557+bandhan-majumder@users.noreply.github.com>
Closes#29279
Minor typo fix in the "Out of scope vulnerabilities" list.
"Deadlinks" should be two words: "Dead links".
Co-authored-by: Premsugeendran <premsugeendran23@gmail.com>
* fix(docs): resolve documentation search in documentation header
* fix: move pagefind from devDependencies to dependencies
* fix(docs): inline pagefind in build script for reliable CI execution
Merge postbuild script into build command to ensure pagefind runs
in all CI environments (Vercel, Turbo, yarn workspace).
---------
Co-authored-by: Romit <romitgabani1.work@gmail.com>
* cleanup(webhooks): remove instant meeting trigger support
* remove dead code
* remove instant meeting from prisma enums and related cleanup
* fix(webhooks): make instant meeting enum migration atomic
- Fixes search mismatch for apps like 'Zoho CRM' vs 'zohocrm'
- Normalizes input by removing spaces, dashes, and underscores
- Updates ZohoCRM display name to 'Zoho CRM' for consistency
- Keeps change minimal without expanding search scope
The /org/[orgSlug]/[user] and /team/[slug] routes were removed in the Cal.diy fork (#28903/#28904) but their SSR helpers and Team screen component were left behind. No route, component, or test still imports them (verified with ripgrep). Deleting them removes dead code and makes it clearer that team/org pages are gone.
Replaced `==` with `===` for the ADMIN role comparison to follow
TypeScript best practices and match the project's coding conventions.
Co-authored-by: Test User <test@example.com>
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
Pins protobufjs to 7.5.5 via resolutions to patch GHSA-xq3m-2v4x-88gg
(arbitrary code execution, <7.5.5). The vulnerable 7.4.0 was pulled in
transitively through @opentelemetry/otlp-transformer, causing the
Security Audit CI job to fail on all PRs.
* feat: Cal.diy — community-driven MIT-licensed fork of Cal.com
This squashed commit contains all Cal.diy changes applied on top of calcom/cal.com main:
- Rebrand Cal.com to Cal.diy across the entire codebase
- Remove Enterprise Edition (EE) features, license checks, and AGPL restrictions
- Switch license from AGPL-3.0 to MIT
- Remove docs/ directory (migrated to Nextra at cal.diy)
- Remove dead code: org tests, EE tips, platform nav, premium username, SAML/SSO, etc.
- Clean up .env.example for self-hosted Cal.diy
- Update Docker image references to calcom/cal.diy
- Update README, CONTRIBUTING.md, and issue templates for Cal.diy community fork
- Add PR welcome bot for Cal.diy contributors
- Fix API v2 breaking changes oasdiff ignore entries
- Replace Blacksmith CI runners with default GitHub Actions
3893 files changed, 20789 insertions(+), 411020 deletions(-)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* refactor: remove org-specific /organizations/:orgId endpoints from API v2 atoms controllers (#1701)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: revert Cal.diy Inc to Cal.com, Inc. in license files, copyright notices, and package metadata (#1702)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* rip out org related comments in api v2
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Upgrades axios from 1.13.5 to 1.15.0 in apps/api/v2 and the root
resolutions field to resolve two critical vulnerabilities:
- GHSA-3p68-rc4w-qgx5: NO_PROXY hostname normalization bypass leading to SSRF
- GHSA-fvcv-3m26-pcqx: Unrestricted cloud metadata exfiltration via header injection
Both CVEs are fixed in axios >=1.15.0.
* chore(member-invite): early return for pending mutations while copying invite link
* typo fix
* make rabbit happy
---------
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
* fix: add vi.mock calls to prevent InstantBookingCreateService test flake
Add two vi.mock() calls ported from cal repo to prevent
'Closing rpc while fetch was pending' errors:
1. Mock @calcom/app-store/calendar.services.generated with a Proxy
to prevent real calendar modules (feishu, lark) from triggering
async getAppAccessToken fetch calls during test worker shutdown.
2. Mock @calcom/features/ee/organizations/di/OrganizationRepository.container
to prevent deep transitive import chain from triggering a Vitest
module-resolution RPC still in flight at shutdown.
Also removes the narrow feishucalendar mock which is now covered by
the broader calendar.services.generated Proxy mock.
* fix: update bookingScenario to use Proxy-based CalendarServiceMap
Replace the plain-object CalendarServiceMap mock with a Proxy-based
implementation that catches ALL calendar service accesses. This prevents
real calendar modules (feishu, lark, etc.) from being imported during
tests, which was causing 'Closing rpc while fetch was pending' errors
when the Vitest worker shut down.
Also update mockCalendar to use a shared mock map directly instead of
dynamically importing calendar.services.generated, avoiding loading
real calendar service modules during test execution.
* docs: add self-hosting troubleshooting guide
Add a dedicated troubleshooting page (docs/self-hosting/troubleshooting.mdx)
covering the most common self-hosting issues:
- 500 error during onboarding caused by missing STRIPE_PRIVATE_KEY (#25993)
- Redirect to localhost after deployment (NEXTAUTH_URL / NEXT_PUBLIC_WEBAPP_URL) (#21921)
- API v2 service not starting in Docker (missing REDIS_URL, JWT_SECRET, WEB_APP_URL)
- CLIENT_FETCH_ERROR in Docker logs
- SSL issues behind a reverse proxy
- Prisma user creation failure on first setup
Also adds the new page to the "Getting Started" navigation group in docs/docs.json.
Co-Authored-By: Paperclip <noreply@paperclip.ing>
* fix(docs): provide safer SSL troubleshooting alternatives
Replace the blanket NODE_TLS_REJECT_UNAUTHORIZED=0 recommendation
with three options in order of preference:
1. Use HTTP internally with proper header forwarding
2. Add internal CA to NODE_EXTRA_CA_CERTS
3. Disable TLS verification (last resort with security warning)
The previous guidance could expose users to MITM attacks on all
external API calls (Stripe, Google, etc.).
* fix(docs): correct NEXTAUTH_URL guidance to prevent OAuth breakage
The previous guidance recommended setting NEXTAUTH_URL to localhost
for SSL/DNS issues, which breaks OAuth callbacks since external
providers would redirect to localhost instead of the public domain.
- Replace localhost workaround with extra_hosts in docker-compose
- Add nginx proxy header configuration example
- Add warnings explaining why localhost breaks OAuth
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix(docs): correct troubleshooting guide based on codebase verification
- Stripe section: Rewrite to accurately reflect current behavior (app
gracefully handles missing keys by marking Stripe as 'not installed'
rather than crashing). Clarify that NEXT_PUBLIC_STRIPE_PUBLIC_KEY
belongs in .env.appStore.
- API v2 section: Add missing required vars (STRIPE_API_KEY,
STRIPE_WEBHOOK_SECRET, NEXTAUTH_SECRET) that crash the service if
absent. Move WEB_APP_URL to optional (it has a fallback default).
- CLIENT_FETCH_ERROR section: Add caveat about HTTPS URLs failing
with the extra_hosts approach when app listens on port 3000.
- Vercel note: Clarify that NEXTAUTH_URL is auto-inferred via
VERCEL_URL, not just 'left empty'.
- Database section: Replace unverifiable metadata/id advice with
actionable migration and setup guidance matching actual code in
apps/web/app/api/auth/setup/route.ts.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* docs: fix inaccuracies in self-hosting troubleshooting guide
- Add missing CALENDSO_ENCRYPTION_KEY to API v2 required variables
- Fix setup endpoint path from /api/auth/setup to /auth/setup
- Add note about NEXTAUTH_URL auto-derivation from NEXT_PUBLIC_WEBAPP_URL
* fix(docs): correct NEXTAUTH_URL derivation mechanism description
NextAuth infers the base URL from the request's Host header when
NEXTAUTH_URL is not set, not from NEXT_PUBLIC_WEBAPP_URL directly.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
---------
Co-authored-by: shockzM1 <shockz@dsn.so>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(bookings): add booking audit logging to instant bookings
wire up BookingEventHandlerService.onBookingCreated in
InstantBookingCreateService to emit audit events, matching the
pattern already used in RegularBookingService and
RecurringBookingService.
* refactor: extract fireBookingEvents and reuse existing orgId
* refactor: derive orgId once and pass to both webhook trigger and audit event
* fix: add missing return in webhook map callback
* refactor: make creationSource required for instant bookings
Both callers (WEBAPP and API_V2) always set creationSource, so validate
it upfront and use CreationSource enum type instead of string | null.
* fix: use ErrorWithCode instead of Error, pass userUuid to audit events
* fix: pass null for hostUserUuid in instant booking audit data
Instant bookings have status AWAITING_HOST with no assigned host,
so the booker's UUID should not be recorded as hostUserUuid.
* fix: address devin review - hostUserUuid and creationSource validation
* fix: address review - bookingMeta, getOrgIdFromMemberOrTeamId, required creationSource
- pass userUuid via bookingMeta instead of separate param (matches RegularBookingService pattern)
- restore getOrgIdFromMemberOrTeamId for proper org resolution instead of eventType.team.parentId
- make creationSource required with runtime validation instead of defaulting to WEBAPP
* fix: enforce creationSource at compile time instead of runtime
use Required<Pick<>> to make creationSource required in the type
signature. removes the runtime check since TypeScript catches
missing creationSource at build time.
* fix: simplify type signature and derive hostUserUuid from booking relation
- Replace Required<Pick<CreateInstantBookingData, 'creationSource'>> with inline { creationSource: CreationSource }
- Include user relation in booking create query to derive hostUserUuid
- Pass newBooking.user?.uuid instead of hardcoding null for userUuid in audit data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: trigger CI
* fix: add missing impersonatedByUserUuid to instant booking meta
The CreateBookingMeta type requires impersonatedByUserUuid. Set it to
null for non-impersonated instant bookings.
* fix: show 'awaiting host' in audit log for instant bookings
Use booking status AWAITING_HOST to display "Booked (awaiting host)"
instead of "Booked with Unknown" when no host has accepted yet.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add booking audit for instant meeting accept via connect-and-join
Extract fireInstantBookingAcceptedAuditEvent to InstantBookingCreateService
and fire it right after the DB update, before side-effect notifications.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* test: add audit logging tests for instant booking creation
* fix: simulate audit failure in resilience test (identified by cubic)
The test 'should not throw when booking audit event fails' was not
actually simulating an audit failure. Added vi.spyOn on
BookingEventHandlerService.prototype.onBookingCreated to reject with
an error, and assert the spy was called, proving the try/catch in
fireBookingEvents properly catches the error without breaking the
booking flow.
Co-Authored-By: bot_apk <apk@cognition.ai>
* test: add audit event tests for connectAndJoin and fix InstantBooking audit test
* test
---------
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: hariom@cal.com <hariombalhara@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: bot_apk <apk@cognition.ai>
In column view, time slots are always visible. selectFirstAvailableTimeSlotNextMonth
could click stale time slots from the current month before the schedule data refreshed
for the new month. Since isQuickAvailabilityCheckFeatureEnabled is always true in E2E,
isTimeSlotAvailable would check the stale slot against the new month's schedule data,
find no match, and permanently disable the confirm button.
Fix: wait for initial schedule data to load before setting up a waitForResponse listener
for getSchedule, then click incrementMonth and await the response before selecting slots.
Ported from calcom/cal#1107.
* Add Famulor app integration to app store
Introduces the Famulor app as a new automation integration in the app store, including configuration, API handler, assets, and documentation. Updates redirectApps utility to include 'famulor' for redirection support.
* fixed - famulor
* Update packages/app-store/famulor/api/add.ts
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Update packages/app-store/famulor/config.json
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* yarn app-store:build
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
* fix: use i18n for apps count with proper pluralization
Replace hardcoded "${installedAppsNumber} apps" with
t("number_apps", { count: installedAppsNumber }) for proper
i18n pluralization support. Removes the TODO comment that
flagged this issue.
Closes#28407
* fix: update e2e test to match i18n singular/plural apps count
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
- Fix Advanced users section: update "Clone calcom/docker" reference to
"calcom/cal.com" and remove outdated submodule update step
- Add required secret key generation instructions for NEXTAUTH_SECRET
and CALENDSO_ENCRYPTION_KEY in the Docker setup steps
- Add Calendar integration skip guidance for first-time users who get
stuck on the "Connect your Calendar" setup wizard step
- Update runtime variables table with generation commands for secrets
Fixes#24833
Co-authored-by: shockzM1 <shockz@dsn.so>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Both booking-sheet-keyboard.e2e.ts and bookings-list.e2e.ts intermittently
timeout in CI when clicking the booking item button before the DOM has
finished rendering. Adding explicit waitFor({ state: 'visible' }) on the
role=button element after the parent booking item is visible ensures the
click target is fully ready.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: mock @calcom/lib/i18n in EventManager.test.ts to prevent vitest worker shutdown flake
TranslationService transitively imports @calcom/lib/i18n which triggers slow
module resolution via vite's RPC. When the vitest worker shuts down before it
completes, it causes 'Closing rpc while fetch was pending' errors.
Mocking the module prevents the actual module resolution during test loading.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* chore: remove explanatory comments per review feedback
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: block localhost and loopback addresses in SSRF protection
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: block loopback IPs by hostname in SSRF protection
Add 127.0.0.1, ::1, [::1], and 0.0.0.0 to blocked hostnames list for
defense-in-depth protection against SSRF attacks targeting localhost.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Romit <romitgabani1.work@gmail.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
* fix: join multiple Reply-To addresses as comma-separated string
Some SMTP providers (e.g., SendLayer) reject emails when Reply-To is
passed as an array to nodemailer, which serializes it as multiple
Reply-To headers. Using a comma-joined string is RFC 2822 compliant
and works universally across all SMTP providers.
Fixes#28610
* test: add unit tests for getReplyToHeader SMTP compatibility
- Verify replyTo is always returned as comma-separated string, not array
- Test single email, multiple emails, and empty email cases
- Add RFC 5322 compliance test for SMTP compatibility
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* docs: add RFC 5322 reference comment to getReplyToHeader tests
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Lawrence Christian <67164412+LCNDevs@users.noreply.github.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
Co-authored-by: Romit <romitgabani1.work@gmail.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
- Add await to unawaited bookingSeatsRepositoryFixture.create calls
- Clean up leftover selected slots before seated event tests
The flakiness was caused by two issues:
1. Missing await on bookingSeatsRepositoryFixture.create() - the HTTP
request to fetch slots could execute before the booking seat record
was written to the database, leading to incorrect seat counts.
2. Leftover SelectedSlots records leaking between test groups - the
availability calculation fetches all unexpired reserved slots by
userId (not eventTypeId), so reserved slots from earlier tests
appeared as busy times when computing slots for seated event types.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: improve German translation for reschedule button
* fix: use accurate German translation for reschedule button
Change "Neuen Termin buchen" (Book new appointment) to "Neu terminieren"
(Re-schedule) to align with the English "Reschedule" meaning.
---------
Co-authored-by: Romit <romitgabani1.work@gmail.com>
* fix: mock delegationCredential in getRoutedUsers test to prevent flaky worker shutdown errors
The getRoutedUsers.test.ts file only tests the pure sync function
getRoutedUsersWithContactOwnerAndFixedUsers, but importing getRoutedUsers.ts
triggers a heavy transitive import chain:
getRoutedUsers.ts
-> @calcom/app-store/delegationCredential
-> _utils/getCalendar
-> calendar.services.generated (all calendar services)
-> CalendarService.ts -> ics/tsdav
Sometimes the vitest worker finishes tests and shuts down before all async
module resolution completes, causing:
Error: [vitest-worker]: Closing rpc while "fetch" was pending
Mock @calcom/app-store/delegationCredential to cut off the import chain
since the tested function doesn't use it.
* Clean up comments in getRoutedUsers.test.ts
Removed comments about mocking delegationCredential to improve code clarity.
* fix: stabilize flaky Team filter E2E tests in bookings-list
Backport fix from calcom/cal: replace fragile expect.poll().toBe(1)
with explicit toBeHidden() wait for filtered-out booking item followed
by toHaveCount(1). This prevents race conditions where the DOM hasn't
updated yet after the team filter API response returns.
Fixes all three Team filter tests:
- Team filter shows bookings for direct team event types
- Team filter shows bookings for managed event types (child events)
- Team filter excludes bookings from other teams
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* fix: adjust direct team event types test assertion
In cal.com, the team filter includes personal bookings of team members,
so instead of asserting the personal booking is hidden, verify that the
team booking is visible and present. The managed event types and
cross-team tests correctly use toBeHidden since those filters do
exclude the expected items.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* fix: strengthen direct team event types assertion with filter-active check
Add assertion that the teamId filter popover trigger is visible in the UI,
proving the filter was applied before checking the team booking is present.
This addresses the concern that toBeVisible alone would pass even without
the filter being active.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* chore: remove comments per review feedback
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: resolve flaky team-management E2E test
Fix two failure modes in the 'Can create teams via Wizard' test:
1. Strict mode violation: locator('[data-testid=new-team-btn]') resolves to
2 elements during Next.js streaming/hydration. Fixed by using .first().
2. Race condition in disband assertion: raw .count() check doesn't wait for
UI to update after team deletion. Replaced with Playwright's auto-retrying
toBeHidden() assertion with a 10s timeout.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* chore: remove explanatory comments per review feedback
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: harden seed script org settings upsert and P2002 error handling
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* fix: remove PII from P2002 log and recover existing user instead of returning null
- Replace username interpolation in log message with generic text (Cubic violation #2, confidence 9/10)
- On P2002, fetch the existing user from DB and return it with membership data instead of returning null, which was dropping users from org setup on retries (Cubic violation #3, confidence 9/10)
Co-Authored-By: bot_apk <apk@cognition.ai>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
The afterAll hooks in PBAC-related e2e specs were deleting the global
'pbac' feature definition via featuresRepositoryFixture.deleteBySlug().
When multiple test suites run in parallel, one suite's cleanup would
delete the feature while other suites still depend on it, causing
intermittent failures.
Each test already creates its own team-level feature flag association
and cleans that up correctly. The global feature definition does not
need per-suite deletion and is better left intact to avoid cross-suite
interference.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(form-builder): show default label when field label is only whitespace
* fix: add validation to ensure label is not empty or whitespace
* fix: enhance label validation to prevent empty or whitespace labels in FormBuilder
* fix: refactor label validation in FormBuilder to streamline whitespace checks
* chore: retrigger CI (flaky vitest worker shutdown)
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: restore moveTeamToOrg admin endpoint for organization migration
- Add moveTeamToOrg and removeTeamFromOrg functions to orgMigration.ts
- Restore API endpoint at /api/orgMigration/moveTeamToOrg
- Restore admin UI page at /settings/admin/orgMigrations/moveTeamToOrg
- Add helper functions for team redirect management
- Support moving team members along with the team
This endpoint allows admins to migrate teams to organizations after org creation,
which is needed as a temporary solution until proper org admin permissions are implemented.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: move moveTeamToOrg to lib/orgMigration and fix redirect URL
- Move moveTeamToOrg and removeTeamFromOrg functions from playwright/lib to lib/orgMigration.ts
- Update API endpoint to import from lib/orgMigration instead of playwright/lib
- Fix redirect URL format: use / instead of /team/
- Fix import path: use ../playwright/lib/orgMigration instead of ./playwright/lib/orgMigration
- Rename unused _dbRemoveTeamFromOrg in playwright file to satisfy linter
- Remove duplicate functions from playwright/lib/orgMigration.ts
This fixes the Vercel deployment failure caused by importing from test-only directories
in production API routes, and corrects the redirect URL format to match the original implementation.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: reuse existing createTeamsHandler for moveTeamToOrg endpoint
- Remove custom orgMigration.ts implementation
- Update API endpoint to call existing createTeamsHandler with org owner impersonation
- Remove moveMembers option from UI (always moves members by design)
- Fix Vercel deployment by removing playwright import from production code
- Use OrganizationRepository.adminFindById to fetch org owner
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: migrate moveTeamToOrg admin page and API to App Router
- Move admin page from pages/settings/admin/orgMigrations to app/(use-page-wrapper)/settings/(admin-layout)/admin/orgMigrations
- Convert API route from pages/api/orgMigration/moveTeamToOrg.ts to app/api/orgMigration/moveTeamToOrg/route.ts
- Create client view component in modules/settings/admin/org-migrations/
- Remove old pages directory files and getServerSideProps
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: correct import paths for App Router compatibility
- Fix @calcom/lib/server to @calcom/lib/server/i18n for getTranslation
- Fix @calcom/ui barrel import to specific component paths
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use TFunction type for getFormSchema parameter
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use buildLegacyRequest for App Router session compatibility
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Remove unused fn
* cleanup
* cleanup
* fix: ui
* fix: handle slug conflict error when moving team to organization
- Intercept Prisma P2002 unique constraint error when moving a team
- Convert to user-friendly CONFLICT error with clear message
- Add test case for slug conflict scenario
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use isPending instead of isLoading for tRPC mutation
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* fix: remove PII (emails) from admin log statement
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: use instanceof pattern for Prisma error detection
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* fix: use i18n key for slug conflict error message instead of hardcoded English string
Co-Authored-By: bot_apk <apk@cognition.ai>
* fix: narrow P2002 catch scope to only prisma.team.update call
Separates the try-catch for prisma.team.update (slug conflict) from
creditService.moveCreditsFromTeamToOrg to avoid misattributing credit
service P2002 errors as slug conflicts.
Co-Authored-By: bot_apk <apk@cognition.ai>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* refactor: evt creation
* refactor: evt creation
* refactor: evt creation
* fix: type
* test: update unit tests
* chore: add comments
* fix: handle undefined in withConditional to match NonNullable<T> type
Co-Authored-By: unknown <>
* refactor: evt
* fix: type err
* refactor: remove add videoCallData
* refactor: use error with code
* test: update unit test
* refactor: feedback
* refactor: remove conditional
* refactor: more strict types
* fix: type err
* chor: remove un used
* refactor: improve recurring event handling in RegularBookingService
Removed unnecessary whitespace and clarified comments regarding the attachment of recurring configurations in the booking handler. Adjusted logic to ensure that recurring settings are only applied when relevant.
* refactor: test
* fix: type error
* refactor: improve code
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: remove attendee endpoint
* fix: remove attendee email from error logs to avoid logging PII
Co-Authored-By: unknown <>
* fix: add isBookingAuditEnabled to removeAttendee handler
Align removeAttendee.handler.ts with the new onAttendeeRemoved interface
that requires isBookingAuditEnabled, following the same pattern used in
addGuests.handler.ts.
Co-Authored-By: bot_apk <apk@cognition.ai>
* style: apply biome formatting to conflict-resolved files
Co-Authored-By: bot_apk <apk@cognition.ai>
* chore: implement PR feedback
* fixup
* revert: biome formatting changes
* chore: implement feedback part 1
* chore: implement feedback part 2
* fix: await cancellation email flow to prevent uncaught promise rejections
The fire-and-forget .then() chain on prepareAttendeePerson() left
rejections from that promise uncaught. Await both prepareAttendeePerson()
and sendCancelledEmailToAttendee() so errors are properly handled.
sendCancelledEmailToAttendee() already has an internal try/catch, so
awaiting it will not cause the overall removeAttendee flow to fail on
email errors.
Addresses Cubic AI review (confidence 9/10).
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: implement feedback part 3
* chore: implement devin feedback
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* feat(unified-cal): connection-based unified calendar API with CRUD, freebusy, and list connections
- New GET /v2/calendars/connections endpoint returning all calendar connections with connectionId
- Connection-scoped CRUD: GET/POST/PATCH/DELETE /v2/calendars/connections/{connectionId}/events/*
- Connection-scoped free/busy: GET /v2/calendars/connections/{connectionId}/freebusy
- Legacy calendar-type endpoints: GET/POST/DELETE /v2/calendars/{calendar}/events, GET /{calendar}/freebusy
- Backward compat: dual @Patch decorators for singular /event/ (deprecated) and plural /events/
- ConnectedCalendarEntry interface to eliminate inline type annotations
- DRY service layer with shared private helpers (listEventsWithClient, createEventWithClient, etc.)
- Input validation: @IsDefined() on start/end, @IsTimeZone() on timezone fields, cross-field to >= from validation
- All-day event support: Google Calendar date-only events converted to midnight UTC
- New findCredentialByIdAndUserId method in CredentialsRepository for connection-scoped lookups
* style: apply biome formatting to unified calendar API files
* fix: use @IsTimeZone() validator for timeZone field in CreateEventDateTimeWithZone
* fix: add delegation auth support, extract freebusy service layer
- Comment 3: getCalendarClientForUser and getCalendarClientByCredentialId now
use getAuthorizedCalendarInstance with delegated-auth fallback instead of
requiring credential.key directly. Added findCredentialWithDelegationByTypeAndUserId
and expanded findCredentialByIdAndUserId to include delegationCredentialId.
- Comment 5: Extracted freebusy and connections logic from controller into
UnifiedCalendarsFreebusyService, keeping the controller thin (HTTP-only).
Moved ConnectedCalendarEntry type and INTEGRATION_TYPE_TO_API mapping into
the service layer.
- Biome auto-formatting applied to touched files.
* test: add unit and integration tests for unified calendar API
- GoogleCalendarService: 30 tests covering delegation auth, client creation, CRUD
- UnifiedCalendarsFreebusyService: 21 tests covering connections, busy times, filtering
- CalUnifiedCalendarsController: 31 tests covering all endpoints (connection-scoped + legacy)
- Pipe specs: 37 existing tests continue to pass
Total: 98 tests across 5 suites
* fix: address Devin Review feedback - fix JSDoc and validator pattern
- Fix incorrect JSDoc on listEventsForUser (all-day events ARE included, not skipped)
- Fix IsAfterFrom validator to return false instead of throwing BadRequestException
(preserves standard ValidationPipe error format)
* fix: revert IsAfterFrom to throw BadRequestException per team convention
Cubic AI (confidence 9/10, team feedback): validators should throw
BadRequestException to preserve the API's standard bad-request response
structure, per team convention.
* fix: add calendarId query param to createConnectionEvent for API consistency
All other connection-scoped endpoints accept calendarId; this was the
only one hardcoding 'primary'. Added @ApiQuery decorator and @Query
parameter with ?? 'primary' fallback, plus a test for custom calendarId.
* Update apps/api/v2/src/modules/cal-unified-calendars/controllers/cal-unified-calendars.controller.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Revert "Update apps/api/v2/src/modules/cal-unified-calendars/controllers/cal-unified-calendars.controller.ts"
This reverts commit e18e4621eff46d8ec49e4d03230783ce50b0c0e4.
* feat: enhance calendar service with connection-specific methods and improve API documentation
* test: complete delegation auth tests, document virtual mocks, fix key leak tests
- Item 3: Add 7 comprehensive delegation auth integration tests covering
JWT creation params, email cleaning, fallback scenarios, and error handling
- Item 7: Document why virtual mocks are necessary in all test files
(workspace packages with DB dependencies cannot resolve in Jest)
- Cubic #1: Document getCalendarsForConnection caching and upstream limitation
- Cubic #2+#3: Make credential key leak tests non-vacuous by including
actual key fields in mocks and verifying they don't leak
- Remove unused BadRequestException import from freebusy service
* fix: add defense-in-depth key stripping in listConnections controller
Controller now destructures only { connectionId, type, email } from each
connection before returning, so credential.key can never leak even if the
service layer has a future regression. Test updated to verify stripping.
* feat: add unified calendar API endpoints for connections and events management
* fix: add try/catch error handling to CRUD helper methods
Wrap Google Calendar API calls in listEventsWithClient, createEventWithClient,
getEventWithClient, updateEventWithClient, and deleteEventWithClient with
try/catch blocks matching the legacy getEventDetails/updateEventDetails pattern.
This ensures proper NestJS exceptions (NotFoundException, BadRequestException)
are returned instead of raw 500 errors when the Google API throws.
* fix: map Google API errors to correct HTTP status codes
Replace blanket NotFoundException/BadRequestException in CRUD catch blocks
with mapGoogleApiError() that inspects the GaxiosError status code and
returns the appropriate NestJS exception (404→NotFoundException,
401/403→UnauthorizedException, 400→BadRequestException, else→500).
* fix: preserve upstream Google API status codes in error mapping
Separate 403 (ForbiddenException) from 401 (UnauthorizedException) and
add 429 rate-limit handling. This ensures permission-denied and throttling
errors are not misreported to API clients.
* fix: distinguish Google quota/rate-limit 403 from permission 403
Check GaxiosError reason field for rateLimitExceeded, userRateLimitExceeded,
and dailyLimitExceeded before mapping 403 to ForbiddenException. Quota
errors are now correctly mapped to 429 (retriable) instead.
* fix: keep dailyLimitExceeded as 403 (non-retriable quota exhaustion)
dailyLimitExceeded is a daily quota cap, not transient throttling.
Only rateLimitExceeded and userRateLimitExceeded are remapped to 429.
* fix: add missing @ApiQuery decorators for calendarId on get/update/delete endpoints
getConnectionEvent, updateConnectionEvent, and deleteConnectionEvent were
missing @ApiQuery({ name: 'calendarId', required: false }) which caused
OpenAPI spec to incorrectly mark calendarId as required.
* ci: retry flaky vitest worker test
* fix: update calendarId query parameter to be optional in OpenAPI specification
* fix: swap dual decorator order so plural /events/ path appears in OpenAPI spec
NestJS Swagger only picks up the first HTTP method decorator. Swapping
the order ensures the preferred plural path (/events/:eventUid) is
generated in the OpenAPI spec, while the deprecated singular path
(/event/:eventUid) still works at runtime.
* fix: split dual decorators into separate methods so both paths appear in OpenAPI spec
NestJS Swagger only picks up the first HTTP method decorator per handler.
Split getCalendarEventDetails and updateCalendarEvent into separate
methods for the singular /event/ (deprecated) and plural /events/ paths,
each delegating to a shared private helper. Both routes now appear in
the generated OpenAPI spec.
* fix: update openapi.json with split dual-decorator paths for GET/PATCH event endpoints
* fix: mapGoogleApiError - coerce string code to number and read errors from response.data
* fix: mapGoogleApiError - guard against NaN from non-numeric error codes
* fix: use read replica for findCredentialWithDelegationByTypeAndUserId query
* refactor: address review comments - UnifiedCalendarService, ParseConnectionIdPipe, thin controller
- Comment 70 (Ryukemeister): Remove 'what' JSDoc from calendars.service.ts
- Comment 71 (Ryukemeister): Use array syntax for dual paths instead of separate methods
- Comments 73-78 (ThyMinimalDev): Create ParseConnectionIdPipe for connectionId validation
- Comments 79-84 (ThyMinimalDev): Create UnifiedCalendarService with strategy pattern
- Comment 85 (ThyMinimalDev): Move getConnections from freebusy to UnifiedCalendarService
- Controller now only handles HTTP concerns, delegates all logic to UnifiedCalendarService
- Updated all test specs to match refactored architecture
* chore: regenerate openapi.json after controller refactor to array syntax paths
---------
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: add missing vi.mock() calls to prevent vitest worker shutdown flakiness
Add vi.mock() calls for modules that trigger background network requests
or database connections during import. These transitive imports can cause
the vitest worker RPC to shut down while pending fetch/network operations
are still in flight, resulting in flaky test failures with:
Error: [vitest-worker]: Closing rpc while "fetch" was pending
The primary modules mocked are:
- @calcom/app-store/delegationCredential (triggers credential lookups)
- @calcom/prisma (triggers database initialization)
- @calcom/features/calendars/lib/CalendarManager (triggers calendar API calls)
- @calcom/features/auth/lib/verifyEmail (triggers email service)
- @calcom/lib/domainManager/organization (triggers domain lookups)
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: remove conflicting empty prisma mocks from files with prismock/prismaMock setups
- Remove vi.mock('@calcom/prisma', () => ({ default: {}, prisma: {} })) from 28 files
that already have prismock/prismaMock test doubles. Vitest hoists all vi.mock() calls
and the last one wins, so these empty mocks were overriding the functional test doubles.
- Fix CalendarSubscriptionService.test.ts to reuse the shared mock from
__mocks__/delegationCredential instead of creating a new unconfigured vi.fn()
- Remove DelegationCredentialRepository.test.ts empty prisma mock (different pattern)
- Remove vi.mock from inside beforeEach in intentToCreateOrg.handler.test.ts
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: add comprehensive delegationCredential mock exports to prevent CI test failures
The vi.mock blocks for @calcom/app-store/delegationCredential were missing
exports that the code under test transitively imports (e.g.
enrichUsersWithDelegationCredentials, enrichUserWithDelegationCredentialsIncludeServiceAccountKey,
buildAllCredentials, getFirstDelegationConferencingCredentialAppLocation).
Added all exports with passthrough implementations so the booking flow
works correctly without triggering real network requests.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: correct credential mock return shapes to match real module API
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: revert unintended yarn.lock changes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Sink url shortner for sms workflow reminders
* fix: remove hardcoded dub values
* update .env.example
* fix: unit tests
* chore: add tests for scheduleSmsReminder and utils
* review refactor
* fix: type check
* review refactor
* fix: update test to account for smsReminderNumber fallback from main
Co-Authored-By: unknown <>
* feat: add feature flag for sink and more tests to verify
* fix: type check
* use proper feature flags for sink
* Apply suggestion from @keithwillcode
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
* feat: add platform URL support for reschedule and cancel links in workflow emails
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* feat: pass platform URL data to CalendarEventBuilder in workflow emails
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* Revert "feat: pass platform URL data to CalendarEventBuilder in workflow emails"
This reverts commit 1d4d3623c93cd4eeeef18ffdad0597fe583b6a55.
* chore: provide platform metadat to workflow email task
* fixup! chore: provide platform metadat to workflow email task
* test: add unit tests for platform URL handling in EmailWorkflowService
Co-Authored-By: morgan@cal.com <morgan@cal.com>
* test: update WorkflowService tests to include platform params in tasker payload
Co-Authored-By: morgan@cal.com <morgan@cal.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: move useAppsData to features + replace useIsPlatform with isPlatform prop in DisconnectIntegrationModal
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* refactor: remove old useAppsData from web (moved to features)
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: align checkBookingAccessWithPBAC with listing logic for personal event types
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: align permission strings with doesUserIdHaveAccessToBooking granular permissions
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* change implementation
* remove unused code
* test: add getBookingDetails tests for personal event type booking access
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: remove unused MembershipRole import
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: convert BookingDetailsService tests to integration tests
Replace unit tests with mocks by integration tests using real database.
Tests verify org/team admin access to personal event type bookings.
- Org admin viewing team member's personal event booking (fails on main, passes on PR)
- Team admin viewing team member's personal event booking (fails on main, passes on PR)
- Non-admin denied access (passes on both)
- Owner viewing own booking (passes on both)
- Non-existent booking returns Forbidden (passes on both)
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: refactor integration tests to use repositories instead of direct prisma calls
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: replace direct prisma usage with TestXXXRepository pattern in integration tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: use production repositories (OrganizationRepository, BookingRepository, UserRepository) instead of test repos for setup
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: inline test repository logic into integration test file
The TestXXXRepository classes were thin wrappers around single prisma
calls with barely any logic, so the indirection wasn't justified.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Some OIDC providers like Keycloak (v18+) include an `iss` (issuer) parameter in the authentication response for security (as per RFC 9207). The oidc route was explicitly extracting only `code` and `state`, thus losing the `iss` parameter and other potential parameters like `session_state`. When Jackson passed the incomplete set of parameters to `openid-client`, the library failed validation with the error `invalid response encountered` because it expected the `iss` parameter to be present if the server supports it.
Fixes#22238.
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
The Dialpad conferencing integration's URL regex only accepted
alphanumeric characters in the meeting room path ([a-zA-Z0-9]+),
causing valid personal meeting URLs containing hyphens, dots, or
underscores to fail validation.
For example, https://meetings.dialpad.com/mmit-russ is rejected
despite being a valid Dialpad personal meeting link. The sample URL
shown in the error message (alphanumeric only) passes, but
real-world room names commonly use hyphens and other characters.
Updated the urlRegExp character class from [a-zA-Z0-9] to
[a-zA-Z0-9._-] to match all valid Dialpad meeting room slugs.
Fixes issue introduced in #15444.
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
* fix: correct admin password banner message to require both password length and 2FA
The banner message incorrectly used 'or' implying only one condition was needed,
but the code requires BOTH a password of at least 15 characters AND 2FA enabled.
Updated the message to clearly state both requirements and added a hint that
users need to log out and log back in after updating their security settings.
Fixes#9527
Co-Authored-By: unknown <>
* fix: auto-sign-out INACTIVE_ADMIN users after enabling 2FA
When an INACTIVE_ADMIN user enables 2FA, automatically sign them out so
they can log back in with refreshed session role, dismissing the banner.
This matches the existing behavior for password changes.
Co-Authored-By: unknown <>
* feat: add dynamic admin banner message based on inactiveAdminReason
Co-Authored-By: unknown <>
* Remove and add various localization strings
* Update common.json
* Add cookie consent checkbox message and remove entries
* test: add unit tests for AdminPasswordBanner and inactiveAdminReason logic
Co-Authored-By: unknown <>
* fix: add expires field to session mock to fix type check
Co-Authored-By: unknown <>
* fix: wrap CALENDSO_ENCRYPTION_KEY mutations in try/finally to prevent env state leaks
Addresses Cubic AI review feedback (confidence 9/10): when a test fails
early, the CALENDSO_ENCRYPTION_KEY env var was not being restored, which
could leak state into subsequent tests. Wrapped the env mutation in
try/finally blocks to guarantee cleanup.
Co-Authored-By: bot_apk <apk@cognition.ai>
* fix: add missing 'expires' field to buildSession in AdminPasswordBanner test
The Session type requires 'expires' to be a string, but buildSession was
not providing it, causing a type error caught by CI type-check.
Co-Authored-By: bot_apk <apk@cognition.ai>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* perf: add paginated host endpoints and delta-based host updates for event type editor
- New cursor-paginated tRPC endpoints: getHostsForAssignment, getHostsForAvailability, searchTeamMembers, getChildrenForAssignment, exportHostsForWeights, getHostsWithLocationOptions
- Delta-based host update support in update.handler.ts (pendingHostChanges, pendingChildrenChanges)
- Repository additions: EventTypeRepository.findChildrenByParentId, HostRepository pagination, MembershipRepository.searchMembers, UserRepository.findByIdsWithPagination
- Remove teamMembers from getEventTypeById initial load
- Shared types: PendingHostChangesInput, PendingChildrenChangesInput, HostUpdateInput
Co-Authored-By: unknown <>
* fix: revert getTranslation import path to @calcom/i18n/server
Co-Authored-By: unknown <>
* fix: guard findChildrenByParentId to only run when pendingChildrenChanges exists
Co-Authored-By: unknown <>
* refactor: remove delta-based saving logic from backend PR
Move pendingHostChanges/pendingChildrenChanges processing out of backend PR.
These changes belong in the frontend PR since they are tightly coupled
to the new frontend delta tracking components.
Backend PR now contains only read-side optimizations:
- Paginated host/children/member endpoints
- Repository methods
- getEventTypeById optimizations
Co-Authored-By: unknown <>
* refactor: move getEventTypeById changes to frontend PR for type safety
Reverts getEventTypeById.ts, eventTypeRepository.ts, API v2 atom service,
and platform libraries to main. The backend PR now only adds new
infrastructure (paginated endpoints, repository methods, findChildrenByParentId)
without changing existing return types. The getEventTypeById optimizations
will be in the frontend PR instead.
Co-Authored-By: unknown <>
* refactor: move findTeamMembersMatchingAttributeLogic pagination to frontend PR
The handler's return type change (adding nextCursor/total) breaks frontend
files on main that expect the old shape. Moving these changes to the
frontend PR keeps the backend PR purely additive.
Co-Authored-By: unknown <>
* fix: address Cubic review comments - empty array filter, stable total count, Set lookup
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Fix inifnite pagination loop
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: derive teamId from event type to prevent cross-team enumeration in exportHostsForWeights
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: restore doc comment to correct method hasAnyTeamMembershipByUserId
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use memberUserIds?.length to handle empty array filter in findHostsForAssignmentPaginated
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use explicit undefined check for memberUserIds to preserve empty array semantics
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: rename findChildrenByParentId to findChildrenByParentIdIncludeOwner
The method selects owner with user profile data, so the name should
reflect the included relation per Cal.com repository naming conventions.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: rename host repository methods to follow naming conventions
- findHostsForAvailabilityPaginated -> findHostsPaginatedIncludeUser
- findHostsForAssignmentPaginated -> findHostsPaginatedIncludeUserForAssignment
Repository methods should not be named after use-cases (Availability/Assignment)
but should describe what data they include, per Cal.com conventions.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: standardize slice(0, limit) across all pagination methods
Replace slice(0, -1) with slice(0, limit) in all HostRepository
pagination methods for consistency. slice(0, limit) is clearer about
intent since it directly references the limit parameter.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* perf: only run total count query on first page in findByIdsWithPagination
Wrap the count query in a !cursor guard so it only runs on the first
page request, avoiding an extra database query on every scroll.
Consistent with the hasFixedHosts optimization in HostRepository.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add integration tests for paginated host endpoints
Tests cover getHostsForAvailability and getHostsForAssignment handlers:
- Basic host retrieval
- Cursor-based pagination across multiple pages
- Host data fields (isFixed, priority, weight, name, email)
- Search filtering by name
- memberUserIds filtering (including empty array returning zero results)
- hasFixedHosts only present on first page
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract EventTypeHostService and make TRPC handlers thin
- Create EventTypeHostService at packages/features/host/services/ with all
DTO types and business logic for 5 event-type-host endpoints
- Refactor all 5 handlers to delegate to the service (thin handlers)
- Add 17 unit tests covering DTO mapping, authorization, segment filtering,
default values, and pagination pass-through
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* docs: add PR review context comments to EventTypeHostService
Reference key review decisions from PR #28156 as code comments:
- searchTeamMembers: membership check + repository delegation per @eunjae-lee
- exportHostsForWeights: cross-team enumeration security fix per @hariombalhara
- exportHostsForWeights: repository method instead of direct Prisma per @hariombalhara
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Revert "docs: add PR review context comments to EventTypeHostService"
This reverts commit 1a1596e012e971f349f01339ce7572516042f1b3.
* fix: use explicit undefined/null check for memberUserIds in searchMembers
Fixes empty array semantics so memberUserIds: [] correctly returns zero
results instead of all members. Now consistent with HostRepository pattern
which uses 'memberUserIds !== undefined' instead of 'memberUserIds?.length'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: replace TRPCError with ErrorWithCode in EventTypeHostService
Per AGENTS.md rules, services in packages/features/ should use
ErrorWithCode instead of TRPCError. The errorConversionMiddleware
will automatically convert it to the appropriate TRPCError at the
router layer.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Remove comment
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: remove unused teamId from exportHostsForWeights schema
teamId was originally accepted by the schema when the handler used it
directly. After the security fix to derive teamId server-side from the
event type, the field became dead code. Removing it to keep the API
contract accurate.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Abstract types
* Update imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* Integrate creation/rescheduling booking audit
* fix: add missing hostUserUuid to booking audit test data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix-ci
* feat: enhance booking audit with seat reference
- Added support for seat reference in booking audit actions.
- Updated localization for booking creation to include seat information.
- Modified relevant services to pass attendee seat ID during booking creation.
* fix: update test data to match schema requirements
- Add seatReferenceUid: null to default mock audit log data
- Add seatReferenceUid: null to multiple audit logs test case
- Convert SEAT_RESCHEDULED test data to use numeric timestamps instead of ISO strings
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Allow nullish seatReferenceUid
* feat: enhance booking audit to support rescheduledBy information
- Updated booking audit actions to include rescheduledBy details, allowing tracking of who rescheduled a booking.
- Refactored related services to accommodate the new rescheduledBy parameter in booking events.
- Adjusted type definitions and function signatures to reflect the changes in the booking audit context.
* Avoid possible run time issue
* Fix imoport path
* fix failing test due to merge from main\
* Pass useruuid
* chore: retrigger CI (flaky unit test)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add context parameter to bulk audit methods for impersonation support
- Add context parameter to queueBulkCreatedAudit and queueBulkRescheduledAudit in BookingAuditProducerService interface
- Add context parameter to BookingAuditTaskerProducerService implementation
- Add context parameter to onBulkBookingsCreated and onBulkBookingsRescheduled in BookingEventHandlerService
- Update RegularBookingService.fireBookingEvents to accept and pass impersonation context
- Update RecurringBookingService.fireBookingEvents to accept and pass impersonation context
- Update handleSeats to accept and pass impersonation context
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: Integrate mark-no-show booking audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Simplify host no-show audit and add comment for attendee actor
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make impersonatedByUserUuid required with explicit null for non-impersonation cases
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: integrate impersonatedByUserUuid into booking cancellation audit flow
- Add impersonatedByUserUuid to CancelBookingInput and CancelBookingMeta types
- Pass audit context with impersonatedBy to onBookingCancelled and onBulkBookingsCancelled
- Update all cancel booking call sites to pass impersonatedByUserUuid:
- Web app cancel route: uses session impersonatedBy
- API v1 and v2: explicitly set to null (no impersonation support)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: make impersonatedByUserUuid optional and remove explicit null assignments
- Changed impersonatedByUserUuid from required (string | null) to optional (string?)
- Removed explicit null assignments from API v1 and v2 endpoints
- Keep impersonatedByUserUuid only where impersonation actually occurs (web app)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Apply suggestions from code review
Rfemove unnecessary comment
* fix: restore bookingMeta variable in createBookingForApiV1
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Make actionSource required with ValidActionSource type and remove unnecessary comments
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove merge conflict markers from bookings.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: implement SYSTEM as a booking audit source for background tasks, including no-show triggers.
* refactor: Move prisma query to BookingRepository for audit logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Conflict resolution
* refactor: introduce `handleMarkHostNoShow` for public viewer and standardize actor and action source parameters for no-show actions.
* refactor: Combine HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService into NoShowUpdatedActionService
- Create new NoShowUpdatedAuditActionService with combined schema supporting both noShowHost and noShowAttendee as optional fields
- Update BookingAuditActionServiceRegistry to use combined service with NO_SHOW_UPDATED action type
- Update BookingAuditTaskerProducerService with single queueNoShowUpdatedAudit method
- Update BookingAuditProducerService.interface.ts with combined method
- Update BookingEventHandlerService with single onNoShowUpdated method
- Update handleMarkNoShow.ts to use combined audit service
- Update tasker tasks (triggerGuestNoShow, triggerHostNoShow) to use combined service
- Add NO_SHOW_UPDATED to Prisma BookingAuditAction enum
- Remove old separate HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService files
This allows a single API action (e.g., API V2 markAbsent) that updates both host and attendee no-show status to be logged as a single audit event.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: Add migration for NO_SHOW_UPDATED audit action enum value
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update no-show audit schema to use host and attendees array
- Rename noShowHost to host and noShowAttendee to attendees (remove redundant prefix)
- Change attendees from single value to array to support multiple attendees in single audit entry
- Update handleMarkNoShow.ts to create single audit entry for all attendees
- Update tasker tasks (triggerGuestNoShow, triggerHostNoShow) to use new schema
- Update display data types to reflect new schema structure
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Update schema to be more clear. Call onNoShowUpdated immediately after DB update as audit only cares about DB update, and this would avoid any accidental Audit update on DB change
* chore: accommodate schema changes and fix type errors for no-show audit
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update migration to remove old no-show enum values
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use updated attendees in webhook payload for guest no-show
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove duplicate imports and fix actor parameter in bookings.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Move booking query to BookingRepository and remove unused method
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: centralize no-show audit event firing and attendee fetching within `handleMarkNoShow` for improved clarity.
* fix: Build attendeesNoShow as Record with attendee IDs as keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix and add tests for handleMarkBoShow
* refactor: Move prisma queries to AttendeeRepository and BookingRepository
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Correct return type for updateNoShow method (noShow can be null)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update handleMarkNoShow tests to mock new repository methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix triggerGuestNoShow
* refactor: Move triggerGuestNoShow queries to AttendeeRepository and add unit tests
- Add new methods to AttendeeRepository:
- findByBookingId: Get attendees with id, email, noShow
- findByBookingIdWithDetails: Get full attendee details
- updateManyNoShowByBookingIdAndEmails: Update specific attendees
- updateManyNoShowByBookingIdExcludingEmails: Update all except specific emails
- Refactor triggerGuestNoShow.ts to use AttendeeRepository instead of direct Prisma calls
- Add comprehensive unit tests for triggerGuestNoShow following handleMarkNoShow.test.ts pattern:
- Mock repositories and external services
- In-memory DB simulation
- Test core functionality, audit logging, webhook payload, error handling, and edge cases
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Move triggerHostNoShow queries to repositories and delete unit test file
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Convert repository methods to use named parameters objects
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Enhance no-show handling by consolidating audit logging and updating repository interactions
- Refactor `buildResultPayload` to accept a structured argument for better clarity.
- Update `updateAttendees` to use a named parameter object for improved readability.
- Introduce `fireNoShowUpdatedEvent` to centralize no-show audit logging for both hosts and guests.
- Remove redundant methods and streamline attendee retrieval in `AttendeeRepository`.
- Add comprehensive unit tests for no-show event handling, ensuring accurate audit logging and webhook triggering.
* test: Add tests for handleMarkHostNoShow guest actor and unmarking host no-show
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: update attendeesNoShow validation to handle numeric keys
- Changed attendeesNoShow schema to use z.coerce.number() for key validation, ensuring string keys are correctly coerced to numbers.
- Updated test to validate attendeesNoShow data using numeric key comparison, improving robustness of the audit data checks.
* test: Add integration tests for NoShowUpdatedAuditActionService coerce fix
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add graceful error handling for audit log enrichment
- Add hasError field to EnrichedAuditLog type
- Create buildFallbackAuditLog() method for failed enrichments
- Wrap enrichAuditLog() in try-catch to handle errors gracefully
- Add booking_audit_action.error_processing translation key
- Update BookingHistory.tsx to show warning icon for error logs
- Hide 'Show details' button for logs with hasError
- Add comprehensive test cases for error handling scenarios
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Enhance booking audit action services with new display fields and improved validation
- Added "attendee_no_show_updated" and "no_show_updated" translations to common.json.
- Updated IAuditActionService to include new methods for handling display fields and migration.
- Enhanced NoShowUpdatedAuditActionService to differentiate between host and attendee no-show updates.
- Improved ReassignmentAuditActionService to ensure consistent handling of audit data.
- Refactored BookingAuditViewerService for better clarity and maintainability.
* refactor: Use attendeeEmail instead of attendeeId as key in audit data and store host's userUuid
- Updated schema to use attendee email as key instead of attendee ID because attendee records can be reused with different person's data
- Store host's userUuid in audit data with format: host: { userUuid, noShow: { old, new } }
- Updated fireNoShowUpdated to accept hostUserUuid parameter
- Added uuid field to BookingRepository.findByUidIncludeEventTypeAttendeesAndUser
- Updated NoShowUpdatedAuditActionService getDisplayFields to work with email-based keys
- Added attendeeRepository to DI modules for BookingAuditTaskConsumer
- Updated tests to match new schema format
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: Update integration tests to use new schema format with host.userUuid and email keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: correct audit schema for SYSTEM source and ensure attendee lookup by bookingId
- Fix schema inconsistency: use host.userUuid format instead of hostNoShow
- Add user.uuid to getBooking select for audit logging
- Log warning when hostUserUuid is undefined but host no-show is being updated
- Use email as key for attendeesNoShow (not attendee ID) to match schema
- Fetch attendees by bookingId before updating to ensure correct scoping
- Remove unused safeStringify import
* refactor: Change attendeesNoShow schema from Record to Array format
- Update NoShowUpdatedAuditActionService schema to use array format:
attendeesNoShow: Array<{attendeeEmail: string, noShow: {old, new}}>
- Update handleMarkNoShow.ts to build attendeesNoShow as array
- Update common.ts fireNoShowUpdatedEvent to convert Map to array
- Update all tests to use new array format with attendeeEmail property
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update attendeesNoShow schema to use array format in triggerNoShow tasks
- Refactor `triggerGuestNoShow` and `triggerHostNoShow` to replace Map with array for `attendeesNoShowAudit`.
- Update `fireNoShowUpdatedEvent` to handle the new array format for attendees.
- Ensure consistent data structure across no-show audit logging for better clarity and maintainability.
* fix: Update ReassignmentAuditActionService to use GetDisplayFieldsParams interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update ReassignmentAuditActionService tests to use GetDisplayFieldsParams interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Use handleMarkAttendeeNoShow for API v2 mark absent endpoint
- Export handleMarkAttendeeNoShow from platform-libraries
- Update API v2 bookings service to use handleMarkAttendeeNoShow with actionSource
- Add validation for userUuid before calling handleMarkAttendeeNoShow
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Enhance display fields structure in booking audit components
- Updated `BookingHistory.tsx` to allow for more flexible display fields, including optional raw values and arrays of values.
- Introduced `DisplayFieldValue` component for rendering display fields, improving clarity and maintainability.
- Adjusted `IAuditActionService` and `NoShowUpdatedAuditActionService` to align with the new display fields structure.
- Ensured consistent handling of display fields across the booking audit service for better data representation.
* fix: Remove debug code that duplicated first attendee in display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Update attendee repository methods to use structured parameters
- Modified `updateNoShow`, `updateManyNoShowByBookingIdAndEmails`, and `updateManyNoShowByBookingIdExcludingEmails` methods to accept structured `where` and `data` parameters for better clarity and maintainability.
- Updated calls to these methods throughout the codebase to reflect the new parameter structure.
- Removed unused `findByIdWithNoShow` method and adjusted related logic to streamline attendee data retrieval.
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update test mock to match new AttendeeRepository.updateNoShow signature
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: rename handleMarkAttendeeNoShow to handleMarkNoShow and update related types
- Renamed `handleMarkAttendeeNoShow` to `handleMarkNoShow` for clarity.
- Updated type definitions to reflect the new naming convention.
- Modified the `markNoShow` handler to require `userUuid` and adjusted related logic.
- Enhanced the `fireNoShowUpdatedEvent` to accommodate changes in event type structure.
- Updated references across various files to ensure consistency with the new function name.
* handle null value
* fix: add explicit parentheses for operator precedence clarity
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Fix cubic reported bug
* feat: add impersonation audit support to no-show flow (#27601)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add impersonatedByUserUuid context to all BookingEventHandler calls (#27602)
- Add context parameter to confirm.handler.ts (onBulkBookingsRejected, onBookingRejected)
- Add context parameter to requestReschedule.handler.ts (onRescheduleRequested)
- Add context parameter to editLocation.handler.ts (onLocationChanged)
- Add context parameter to addGuests.handler.ts (onAttendeeAdded)
- Add context parameter to handleConfirmation.ts (onBulkBookingsAccepted, onBookingAccepted)
- Update _router.tsx to pass impersonatedByUserUuid from session to handlers
This ensures all BookingEventHandler method calls that have loggedInUser context
now properly support impersonation tracking for audit logging.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: remove stray merge artifact in RegularBookingService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: remove duplicate imports in BookingRepository
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: resolve type errors and duplicate declarations from merge
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: remove duplicate test assertions and duplicate test from merge artifacts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: remove redundant ?? undefined from impersonatedByUserUuid
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: update impersonatedByUserUuid type to string | null for consistency
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: make impersonatedByUserUuid required in booking audit flows
Changes impersonatedByUserUuid from optional (?: string | null) to
required (: string | null) in all booking-related type definitions.
This ensures audit-critical parameters are never accidentally omitted.
Fixed all call sites to explicitly pass the value or null.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add impersonatedByUserUuid to remaining callers and tests
Adds impersonatedByUserUuid: null to API v1, API v2, and test callers
of handleCancelBooking that were missing the now-required parameter.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: require impersonatedByUserUuid in DTOs and pass it across all booking flows (coalesce to null)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add impersonatedByUserUuid to remaining call sites (confirm, markNoShow, webhook, tests)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add impersonatedByUserUuid to confirm handler test and API v2 confirm/decline calls
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: derive impersonatedByUserUuid from session in reportBooking handler instead of hardcoding null
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: reorder spread to ensure impersonatedByUserUuid null fallback is not overwritten
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add missing impersonatedByUserUuid to CalendarSyncService cancel and reschedule calls
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: update CalendarSyncService tests to expect impersonatedByUserUuid
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* ci: retrigger failed checks (attempt 1)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* ci: retrigger failed checks (attempt 2)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* ci: retrigger failed checks (attempt 3)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* chore: bust prisma cache with comment change
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* ci: retrigger after cache bust (attempt 2)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* ci: retrigger after cache bust (attempt 3)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Volnei Munhoz <volnei@cal.com>
* fix: resolve duplicate new-team-btn selector in teams E2E test
Remove TeamsCTA from the loading skeleton to prevent duplicate
data-testid='new-team-btn' elements in the DOM during Next.js
streaming SSR. The skeleton's Suspense fallback and the resolved
page content can briefly coexist, causing Playwright's strict
mode to fail when both contain the same test ID.
Also use .first() in the E2E test as a defensive measure.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* fix: assert single new-team-btn element instead of using .first()
Address Cubic AI review feedback: use toHaveCount(1) assertion
to ensure exactly one new-team-btn exists, rather than .first()
which could mask duplicate-element regressions.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* fix: revert test changes, only remove CTA from skeleton
Per user feedback: only change needed is removing TeamsCTA from
the loading skeleton to prevent users without a team plan from
bypassing the upgrade banner via the skeleton CTA.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: batch DB operations in performance test setup to avoid CI timeout
Replace ~5200 sequential DB inserts with batch createMany() operations
in createHugeAttributesOfTypeSingleSelect. The test was timing out at
60s on 2-vCPU CI runners because the setup phase alone (creating 400
users, 800 memberships, 4000 attribute assignments one-by-one) exceeded
the timeout before the actual logic under test even ran.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* perf: optimize createAttributesScenario with batch DB operations
Move batch DB operations (createMany) into the shared createAttributesScenario
helper so all tests benefit, not just the performance test. Simplify
createHugeAttributesOfTypeSingleSelect to delegate to the optimized helper.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* Revert "perf: optimize createAttributesScenario with batch DB operations"
This reverts commit c739edd3718796d61f6510ea3a9155065b69a1ce.
* Revert "perf: batch DB operations in performance test setup to avoid CI timeout"
This reverts commit ff6642ae49190c42dd8673a12ee11c8c971370b4.
* perf: optimize createAttributesScenario with batch DB operations
Replace sequential prisma.*.create() calls in the shared createAttributesScenario
helper with batch createMany() calls, reducing ~5,200 sequential DB operations to
~10 batch operations. Simplify createHugeAttributesOfTypeSingleSelect to delegate
to the optimized helper instead of duplicating batch logic.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* fix: preserve deterministic user ordering in createAttributesScenario
Map users by email after findMany to ensure the index-based attribute
assignment matches the original userDataList order, since findMany does
not guarantee return order.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* revert: make test timeout back to 1 min
* refactor: extract batch helpers createTestUsers, createTestMembershipsForUsers, createTestAttributeAssignments
Organize batch DB operations from createAttributesScenario into
reusable helper functions mirroring the existing single-record helpers.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* cleanup: remove unused single-record helpers and JSDoc comments
Remove createTestUser, createTestMemberships, createTestAttributeAssignment
(now replaced by batch versions). Remove added comments.
Co-Authored-By: romitgabani1 <romitgabani1.work@gmail.com>
* chore
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
The locale E2E tests (de->ar and de->pt-BR) were flaky because they used
a brittle CSS selector (.bg-default > div > div:nth-child(2)) to click
the language dropdown. This selector was susceptible to react-select's
internal Input wrapper element intercepting pointer events.
Changes:
- Add data-testid='locale-select' to the language Select in general settings
- Replace CSS path selectors with getByTestId('locale-select') in tests
- Replace text selector for pt-BR with getByTestId('select-option-pt-BR')
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: make source required on EventBusyDetails for Troubleshooter display
- Make source a required property on EventBusyDetails type
- Update LimitManager to accept and store source when adding busy times
- Add user-friendly source names for all busy time types:
- 'Booking Limit' for booking limit busy times
- 'Duration Limit' for duration limit busy times
- 'Team Booking Limit' for team booking limit busy times
- 'Buffer Time' for seated event buffer times
- 'Calendar' for external calendar busy times
- Ensure all entries in detailedBusyTimes have source set
- Cover includeManagedEventsInLimits and teamBookingLimits branches
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add limit value and unit metadata to busy time sources
- Include limit value and unit in source strings for Troubleshooter display
- Booking Limit: shows as 'Booking Limit: 5 per day'
- Duration Limit: shows as 'Duration Limit: 120 min per week'
- Team Booking Limit: shows as 'Team Booking Limit: 10 per month'
- Preserves existing calendar sources (e.g., 'google-calendar')
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: enhance busy time management with new limit sources
- Introduced new limit sources for busy times, including event booking and duration limits, with user-friendly titles for display.
- Updated LimitManager to accept and store detailed busy time information, including title and source.
- Refactored busy time addition logic across various services to utilize the new structure, improving clarity and maintainability.
* fixes
* feat: conditionally include source and translate busy time titles
- Add withSource parameter to conditionally include/exclude source from response
- Translate busy time titles on frontend using useLocale hook
- Source is only included when withSource=true (for Troubleshooter display)
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: enhance user availability service and busy time handling
- Updated LargeCalendar component to include event ID check for enabling busy times.
- Added translation for "busy" in common.json for better user experience.
- Refactored getUserAvailability service to include new method for fetching user availability with busy times from limits.
- Introduced parseLimits function to streamline booking and duration limit parsing.
- Improved error handling in user handler for better user feedback.
* refactor: remove unnecessary timeZone: undefined from addBusyTime calls
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add buffer_time and calendar translation keys for busy times
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add event source to calendar components and improve busy time handling
- Updated EventList component to include event source in data attributes for better tracking.
- Enhanced LargeCalendar component to pass event
* fix: add missing source property to Date Override calendar event
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use 'date-override' as source for Date Override calendar events
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Avoid type assertion
* fix: pass both bookingLimits and durationLimits to getStartEndDateforLimitCheck (#27898)
* test: add unit tests for getUserAvailabilityIncludingBusyTimesFromLimits
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: pass both bookingLimits and durationLimits to getStartEndDateforLimitCheck
- Fix bug where bookingLimits || durationLimits was passed as single param
- Skip getBusyTimesForLimitChecks when eventType has no limits
- Remove as never casts, use proper typing for mock dependencies
- Replace expect.any(String) with exact ISO date assertions
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: replace loose assertions with exact values in tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: address review feedback on busy time sources
- Fix t("busy") fallback to t("busy_time.busy") for correct translation lookup
- Add missing title property to buffer time entries for Troubleshooter display
- Use descriptive debug strings for buffer time source field
- Fix import ordering in LargeCalendar.tsx
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-Authored-By: unknown <>
* fix: preserve pre-existing busyTimesFromLimitsBookings from initialData
Address review feedback from @hariombalhara (comment #30, #31):
- Initialize busyTimesFromLimitsBookings from initialData instead of []
to avoid silently overwriting pre-existing data with an empty array
- Use conditional spread to only include busyTimesFromLimitsBookings
when it has a value
- Add test verifying pre-existing busyTimesFromLimitsBookings is
preserved and passed through to _getUserAvailability
- Add test verifying busyTimesFromLimitsBookings is not passed when
there are no limits and no initialData bookings
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
Co-Authored-By: bot_apk <apk@cognition.ai>
* fix: pass fetched eventType to _getUserAvailability to avoid duplicate DB query
Addresses Devin Review comment r2863593564: the wrapper method fetches
eventType but wasn't passing it through, causing _getUserAvailability to
re-fetch the same eventType from the database.
Also adds a test verifying eventType is forwarded correctly.
Co-Authored-By: bot_apk <apk@cognition.ai>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
Day.js requires the 'utc' plugin to be loaded before the 'timezone'
plugin to correctly apply timezone offsets during formatting.
This fixes an issue where the Bookings Dashboard displayed UTC time
but labeled it with the user's timezone.
Fixes#28193
Co-authored-by: Harshith Kumar <mharshithkumar6@gmail.com>
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
The deleteDomain function had a 'return false' statement after
'return isDomainDeleted && isDnsRecordDeleted' which could never
be reached.
Co-authored-by: Harshith Kumar <mharshithkumar6@gmail.com>
PostgreSQL 18+ stores data in /var/lib/postgresql/18/main instead of
/var/lib/postgresql/data. The volume mount needs to be at /var/lib/postgresql
to capture the new directory structure.
Fixes#28281
The 'should render the /teams page' test was failing because PR #27650
introduced a showHeader flag that hides the 'Teams' heading for users
without teams. Other tests in the file were updated to create users
with hasTeam: true, but this test was missed.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: use path.sep in redirect-apps template exclusion for Windows compatibility
* fix: normalize backslashes instead of using path.sep
---------
Co-authored-by: Romit <85230081+romitg2@users.noreply.github.com>
* feat: add booking attendees endpoint to API v2
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* feat: add rate limiting to booking attendees endpoint
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* refactor: simplify attendees output to id, bookingId, name, email, timeZone
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* test: add E2E tests for booking attendees endpoint
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* chore: update bookings repository
* fixup: add pbac guards and update service logic
* chore: update openapi spec
* test: add rate limiting E2E test for booking attendees endpoint
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: tests
* fix: return 404 instead of 403 for non-existent booking in BookingPbacGuard
The BookingPbacGuard was returning 403 (Forbidden) for non-existent bookings
because doesUserIdHaveAccessToBooking returns false when a booking doesn't
exist, which the guard treated as an access denial.
Added an explicit booking existence check in the guard before the access
check, so non-existent bookings now correctly return 404 (Not Found) as
documented in the PR description.
Updated the E2E test to expect 404 for non-existent booking UIDs.
Issue identified by cubic.
Co-Authored-By: unknown <>
* fixup
* fix: return 404 instead of 403 for non-existent booking in attendees endpoint
BookingPbacGuard now checks booking existence before the access check,
returning 404 (Not Found) instead of 403 (Forbidden) for non-existent
booking UIDs. Updated the E2E test assertion and description to match.
Issue identified by cubic (confidence 9/10).
Co-Authored-By: unknown <>
* chore: implement PR feedback
* chore: update tests
* fixup
* chore: update endpoint decsription
* feat: endpoint to retrieve specific attendee
* chore: update e2e tests
* chore: implement cubic feedback
* fix: update test to expect 403 for non-existent booking UID (BookingPbacGuard behavior)
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: merge conflicts
* feat: endpoint to get attendees
* chore: update findByUidIncludeEventTypeAttendeesAndUser method
* chore: implement PR feedback
* fix: e2e tests
* chore: update e2e tests
* fixup fixup
* fix: remove phoneNumber assertion since it's optional and not provided in test
* chore: implement PR feedback
* fix: keep the same output shape for get attendees and get attendee endpoint
* chore: update openapi spec
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* fix: trigger lingo.dev by removing duplicate value
* under progress
* wow this worked
* migrate schema
* fix types
* fix import for google login
* Add onboarding tests for Azure (Microsoft sign up)
* add comments back
* fix failing test
* fix: update Outlook login configuration and improve type safety in authentication adapter
- Set OUTLOOK_LOGIN_ENABLED to false in .env.example
- Refactor getServerSideProps to directly use samlTenantID and samlProductID
- Update linkAccount method in next-auth-custom-adapter for better type handling
- Remove redundant comment in next-auth-options related to Azure AD email verification
* remove log
* remove debug log from signin callback in next-auth options
* fixup
* chore: standardize naming
* chore: add primary calendar for outlook, verify email and auto link org for outlook
* chore: helper fns
* chore: implement cubic feedback
* cleanup
* chore: implement cubic feedback again
* WIP design#
* feat: login design
* fix: map identity provider names correctly
* 32px of mt
* fix: login UI
* fix: type check
* fix: fix type check again
* chore: update OAuth login tests
* fixup
* fix: bad import
* chore: update tests
* fixup
* fix: locales test
* chore: implement PR feedback and fix minor issues
* fix: revert token spreading change
* fix: merge conflicts
* chore: revert signup view changes
* fixup: bring back reverted changes because of merge conflicts
* fix: disable email input when microsoft sign in is in progress
* chore: implement cubic feedback
* cleanup: unused variables
* fix: address Cubic AI review feedback (confidence >= 9/10)
- Remove userId (PII) from log payloads in updateProfilePhotoMicrosoft.ts
- Replace text selectors with data-testid in locale.e2e.ts and oauth-provider.e2e.ts
- Restore callbackUrl redirect parameter in signup link in login-view.tsx
- Add data-testid='login-subtitle' to login page subtitle element
Co-Authored-By: unknown <>
* fix: use empty alt for decorative icon images in login view
MicrosoftIcon and GoogleIcon are decorative (adjacent to text labels),
so they should have empty alt attributes per accessibility best practices.
Co-Authored-By: unknown <>
* chore: implement cubic feedback
* cleanup
* fixup
* chore: implement PR feedback
* chore: implement feedback
* fix: address PR review feedback - type safety and centralize constants
- Replace non-null assertions (!) with proper null checks for OUTLOOK_CLIENT_ID/SECRET
- Replace `as any` casting with `Record<string, unknown>` for OAuth profile claims
- Remove non-null assertion on account.access_token by adding conditional check
- Centralize Outlook env constants in @calcom/lib/constants alongside MICROSOFT_CALENDAR_SCOPES
- Add explanatory comment for getNextAuthProviderName usage in get.handler.ts
Co-Authored-By: unknown <>
* Revert "fix: address PR review feedback - type safety and centralize constants"
This reverts commit 91ace141e6a28a23deea5897f7f9d6ad80319d84.
* chore: implement feedback
* chore: cleanup
* chore: implement feedback
* fix: merge conflicts
* fix: revert formatting-only changes in packages/lib/constants.ts
Co-Authored-By: unknown <>
* fix: revert IdentityProvider enum location change in schema.prisma
Co-Authored-By: unknown <>
* chore: implement more PR feedback
* fix: restore database-derived profileId from determineProfile in OAuth JWT
The profileId regression was identified by Cubic AI (confidence 9/10).
Previously, determineProfile's returned id was used to set profileId in the
JWT via 'profileResult.id ?? token.profileId ?? null'. A recent refactor
changed this to 'token.profileId ?? null', which drops the database-derived
profile ID. On first OAuth login (or when profile switcher is disabled),
token.profileId is likely null, so profileId would incorrectly be set to
null even though determineProfile returned a valid profile with an id.
This commit restores the correct priority chain:
visitorProfileId ?? token.profileId ?? null
Co-Authored-By: bot_apk <apk@cognition.ai>
* refactor: revert pure formatting and import reordering changes
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* fix: normalize determineProfile return type to use consistent 'id' field
The determineProfile function returned a union type where one branch used
'id' and the other used 'profileId'. This caused TS2339 when destructuring
'id' from the result. Normalize the token.upId branch to also return 'id'
(mapped from token.profileId) so the return type is consistent.
Co-Authored-By: bot_apk <apk@cognition.ai>
* chore: add tests
* reveret: profileId changes should be in a separate PR
* fix: avoid logging entire existingUser object in OAuth JWT callback
Revert to logging only { userId, upId } instead of the full existingUser
object, which contains PII (email, name, identity provider details).
This restores the previous safe logging pattern.
Co-Authored-By: bot_apk <apk@cognition.ai>
* chore: remove profileId related tests
---------
Co-authored-by: amrit <iamamrit27@gmail.com>
Co-authored-by: Devanshu Sharma <devanshusharma658@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Sean Brydon <sean@cal.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* chore: pull coss ui
* refactor: migrate webhooks page to coss/ui components
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor: migrate CreateNewWebhookButton to coss/ui Menu components
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor: adjust CreateNewWebhookButton variant and align based on context
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* feat(coss-ui): add list-item components and refactor WebhookListItem to use them
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor(webhooks): match prototype – remove docs link, flatten list, show user in item
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* update css vars
* refactor(webhooks): migrate WebhookForm.tsx to coss-ui components
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): add FormProvider for useFormContext in child components
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* restore prev form
* swap form components
* mc
* use coss button everywhere
* fix ts errors
* refactor(webhooks): replace SettingsHeaderWithBackButton with coss-ui CardFrame in edit/new views
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor(webhooks): split into two CardFrames with Card/CardPanel
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor(webhooks): wrap WebhookTestDisclosure in Card/CardPanel
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor(webhooks): migrate WebhookTestDisclosure to coss-ui CardFrame
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* mc
* mc
* refactor(webhooks): update skeletons to use coss-ui CardFrame pattern
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* mc
* refactor(webhooks): improve new webhook skeleton to match actual UI layout
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* imrpove form skeleton
* optimise card header
* refactor: improve base ui react-hook-form compatibility
* update string
* use coss ui alert dialog
* use coss toast
* feat(webhooks): add user filter to webhooks list
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): resolve lint warnings in filter component
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* refactor(webhooks): filter after new button, icon+badge only
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* improve switch
* remove unneeded toast
* refactor(webhooks): enhance WebhooksFilter component with anchor reference
* mc
* fix(webhooks): update Switch component to use checked prop instead of defaultChecked
* fix(webhooks): prevent Suspense boundary error on webhook edit save
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): add optimistic state for Switch toggle in WebhookListItem
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): disable switch during pending mutation to prevent rapid-toggle race
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): guard toggle with isPending check instead of disabled prop
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix toggle
* mc
* mc
* fix(webhooks): navigate before revalidation to prevent Suspense re-suspension
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* mc
* fix(webhooks): use render prop for Cancel button navigation
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): simplify Cancel button with explicit conditional rendering
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* fix(webhooks): sort list by id to prevent resorting on toggle
Co-Authored-By: pasquale@cal.com <pasquale@cal.com>
* mc
* mc
* show filter only if user is >1
* refactor(webhooks): move ListItem components to shared directory
* feat: reintroduce avatars
* remove unneeded translations
* remove unneeded useMemo
* add clickable +n badge
* mc
* mc
* add tooltips and docs link
* animated tooltips
* refactor(WebhookForm): replace hardcoded text with translation keys for better localization
* refactor(WebhookListItem): manage active state with useEffect and improve toggle error handling
* mc
* update toast position + re-pull coss ui components
* refactor: rename onEditWebhook to onEditWebhookAction to prevent warnings
* feat: enhance WebhookListItem with edit link and mobile support
* mc
* remove unneded component
* refactor: simplify webhook toggle logic in WebhookListItem component
* fix: update edit link logic in WebhookListItem and add editHref to webhooks-view
* wip
* feat: implement webhook version selection in WebhookForm
* update list item
* fix issue with delete button perrmission
* sort webhooks by ID in WebhooksList component
* mc
* refactor: enhance Webhook components with improved header and skeleton loading logic
* refactor: rename files for clarity
* display the edit button
* re-pull coss ui components
* fix ts error
* refactor: simplify permission checks in WebhookListItem component
* improve skeletons
* fix ts errors
* rename onEditWebhook
* fix: update SheetContent variant in ActiveUserBreakdown component
* fix e2e
* feat: add data-testid attribute to confirmation button in DeleteWebhookDialog
* remove unnecessary file
* refactor: allow passing the aria-label attribute to the button that removes a chip
* refactor: rename onEditWebhook to onEditWebhookAction to prevent warnings
* refactor: add webhook version select to event-type dialog and extract shared WebhookVersionCTA component
Co-Authored-By: pasquale@cal.com <pasqualevitiello@gmail.com>
* fix: restore toast notification on webhook toggle in event-types tab
Co-Authored-By: pasquale@cal.com <pasqualevitiello@gmail.com>
* implement the coss ui dialog
* refactor(webhooks): use shared WebhookListItem in event type tab and coss-ui Dialog
* remove unneeded lastItem prop
* re-pull coss ui components
* Merge remote-tracking branch 'origin/main' into style/improve-webhooks
Co-Authored-By: unknown <>
* refactor: extract AppHeader component and move page title outside CardFrame
Co-Authored-By: unknown <>
* refactor: show user header for every webhook group and remove CardFrame wrapper
Co-Authored-By: unknown <>
* fix: wrap each user webhook group in CardFrame with CardFrameHeader
Co-Authored-By: unknown <>
* fix: add dashed border to Empty state matching coss prototype
Co-Authored-By: unknown <>
* mc
* fix: only show New button in header when webhooks exist
Co-Authored-By: unknown <>
* refactor: move page title/description/back button out of CardFrame on new/edit webhook pages
Co-Authored-By: unknown <>
* refactor: render webhook form header outside flex-col gap wrapper
Co-Authored-By: unknown <>
* refactor: move header outside div wrapper in webhook form skeleton
Co-Authored-By: unknown <>
* mc
* skeleton improvements
* refactor: move AppHeader component to @coss/ui/shared/app-header
Co-Authored-By: unknown <>
* fix the webhook dialog on event-types settings
* make the webhook clickable for event types
* remove hardcoded string
---------
Co-authored-by: pasqualevitiello <pasqualevitiello@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: pasquale@cal.com <pasquale@cal.com>
Co-authored-by: Peer Richelsen <peeroke@gmail.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
The recent upgrade to PostgreSQL 18 in CI (commit 27515d4) caused the
Setup Database job to fail because the runner's host pg_dump (v16) refuses
to dump a Postgres 18 server.
Replace tj-actions/pg-dump and tj-actions/pg-restore with docker run
commands using the postgres:18 image, ensuring the client version always
matches the server. This aligns with the fix already applied in the
internal cal repo.
* fix: add phone mask overrides for Argentina and Finland to prevent digit truncation
Co-Authored-By: susan <susan@cal.com>
* refactor: extract CUSTOM_PHONE_MASKS to shared module to avoid duplication
Address review feedback from cubic-dev-ai: extract CUSTOM_PHONE_MASKS
into a shared phone-masks.ts module so both PhoneInput.tsx and the test
file import from the same source of truth instead of duplicating the
masks. Also fix non-null assertion lint warning in the test file.
Original PR by devin-ai-integration[bot].
Co-Authored-By: bot_apk <apk@cognition.ai>
* fix: move custom message to expect() for toBeGreaterThanOrEqual type compatibility
toBeGreaterThanOrEqual only accepts 1 argument. Move the custom error
message to the expect() call instead.
Co-Authored-By: bot_apk <apk@cognition.ai>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: susan <susan@cal.com>
Co-authored-by: bot_apk <apk@cognition.ai>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: admin wizard crash and prisma enum generator path resolution
1. Fix TypeError in WizardForm when license step is skipped:
- defaultStep was set to 3 (APPS) but only 2 steps existed when
hasValidLicense=true, causing steps[2] to be undefined
- Removed redundant double onNext() call in AdminUser onSuccess
- Extracted hasLicenseStep variable for clarity
2. Fix prisma enum generator not being directory-safe:
- Created run-enum-generator.js wrapper resolved by Prisma relative
to schema directory, so npx prisma commands work from any directory
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: use bin entry for prisma enum generator path resolution
Changed provider from './run-enum-generator.js' (which Prisma doesn't
resolve relative to schema dir) to 'prisma-enum-generator' bin registered
in package.json. This ensures the generator is found via PATH regardless
of CWD.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* chore: Also add new bin to the lockfile
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: susan <susan@cal.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
- Add await to unawaited bookingSeatsRepositoryFixture.create calls
- Clean up leftover selected slots before seated and variable length tests
- Add deleteAllByUserId method to SelectedSlotRepositoryFixture
The flakiness was caused by reserved slots from earlier tests leaking
into subsequent test groups. The availability calculation fetches all
unexpired reserved slots by userId (not eventTypeId), so non-seat
reserved slots from regular event type tests appeared as busy times
when computing slots for seated and variable length event types.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: strip avatar and profile from children payload in managed event type updates
When assigning users to managed event types with ~85+ users, the request
body exceeds the 1MB server limit. The root cause is that the full
ChildrenEventType objects (including avatar, profile, username, membership)
are sent in the update payload, even though the server schema (childSchema)
only needs owner.{id, name, email, eventTypeSlugs} and hidden.
Avatar data can be particularly large when stored as base64 data URLs.
With 85 users each having ~10KB+ avatars, the payload easily exceeds 1MB.
This fix strips the children array down to only server-required fields
before sending the mutation, while keeping the full data in form state
for UI display purposes.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* refactor: extract stripChildrenForPayload into shared utility instead of duplicating in test
Addresses Cubic AI review feedback: the test file was duplicating the
stripping logic instead of importing it from production code.
- Extracted stripChildrenForPayload() into childrenEventType.ts
- Updated useEventTypeForm.ts to import and use the shared function
- Updated test file to import from production code instead of defining its own copy
Co-Authored-By: bot_apk <apk@cognition.ai>
* fix: remove unused @ts-expect-error directive in useEventTypeForm
Co-Authored-By: bot_apk <apk@cognition.ai>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: bot_apk <apk@cognition.ai>
* feat: calendar cache and sync - wip
* Add env.example
* refactor on CalendarCacheEventService
* remove test console.log
* Fix type checks errors
* chore: remove pt comment
* add route.ts
* chore: fix tests
* Improve cache impl
* chore: update recurring event id
* chore: small improvements
* calendar cache improvements
* Fix remove dynamic imports
* Add cleanup stale cache
* Fix tests
* add event update
* type fixes
* feat: add comprehensive tests for new calendar subscription API routes
- Add tests for /api/cron/calendar-subscriptions-cleanup route (9 tests)
- Add tests for /api/cron/calendar-subscriptions route (10 tests)
- Add tests for /api/webhooks/calendar-subscription/[provider] route (11 tests)
- Add missing feature flags for calendar-subscription-cache and calendar-subscription-sync
- All 30 tests pass with comprehensive coverage of authentication, feature flags, error handling, and service instantiation
Tests cover:
- Authentication scenarios (API key validation, Bearer tokens, query parameters)
- Feature flag combinations (cache/sync enabled/disabled states)
- Success and error handling (including non-Error exceptions)
- Service instantiation with proper dependency injection
- Provider validation for webhook endpoints
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add comprehensive tests for calendar subscription services, repositories, and adapters
- Add unit tests for CalendarSubscriptionService with subscription, webhook, and event processing
- Add unit tests for CalendarCacheEventService with cache operations and cleanup
- Add unit tests for CalendarSyncService with Cal.com event filtering and booking operations
- Add unit tests for CalendarCacheEventRepository with CRUD operations
- Add unit tests for SelectedCalendarRepository with calendar selection management
- Add unit tests for GoogleCalendarSubscriptionAdapter with subscription and event fetching
- Add unit tests for Office365CalendarSubscriptionAdapter with placeholder implementation
- Add unit tests for AdaptersFactory with provider management and adapter creation
- Fix lint issues by removing explicit 'any' type casting and unused variables
- All tests follow Cal.com conventions using Vitest framework with proper mocking
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: improve calendar-subscriptions-cleanup test performance by adding missing mocks
- Add comprehensive mocks for defaultResponderForAppDir, logger, performance monitoring, and Sentry
- Fix slow test execution (933ms -> <100ms) caused by missing dependency mocks
- Ensure consistent test performance across different environments
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* Fix tests
* Fix tests
* type fix
* Fix coderabbit comments
* Fix types
* Fix test
* Update apps/web/app/api/cron/calendar-subscriptions/route.ts
Co-authored-by: Alex van Andel <me@alexvanandel.com>
* Fixes by first review
* feat: add database migrations for calendar cache and sync fields
- Add CalendarCacheEventStatus enum with confirmed, tentative, cancelled values
- Add new fields to SelectedCalendar: channelId, channelKind, channelResourceId, channelResourceUri, channelExpiration, syncSubscribedAt, syncToken, syncedAt, syncErrorAt, syncErrorCount
- Create CalendarCacheEvent table with foreign key to SelectedCalendar
- Add necessary indexes and constraints for performance and data integrity
Fixes database schema issues causing e2e test failures with 'column does not exist' errors.
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* only google-calendar for now
* docs: add Calendar Cache and Sync feature documentation
- Add comprehensive feature overview and motivation
- Document feature flags with SQL examples
- Include SQL examples for enabling features for users and teams
- Reference technical documentation files
Addresses PR #23876 documentation requirements
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* docs: update calendar subscription README with comprehensive documentation
- Undo incorrect changes to main README.md
- Update packages/features/calendar-subscription/README.md with:
- Feature overview and motivation
- Environment variables section
- Complete feature flags documentation with SQL examples
- SQL examples for enabling features for users and teams
- Detailed architecture documentation
Addresses PR #23876 documentation requirements
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix docs
* Fix test to available calendars
* Fix test to available calendars
* add migration and sync boilerplate
* fix typo
* remove double log
* sync boilerplate
* remove console.log
* only subscribe for google calendar
* adjust for 3 months fetch
* only subscribe for teams that have feature enabled
* adjust tests
* chore: safe increment error count
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* calendar sync
* test: add comprehensive tests for CalendarSyncService
- Add comprehensive test coverage for CalendarSyncService methods
- Test handleEvents, cancelBooking, and rescheduleBooking functionality
- Cover edge cases like missing UIDs, malformed UIDs, and error handling
- Fix dynamic import usage in CalendarSyncService to use .default
- Remove invalid properties from handleNewBooking call to fix type errors
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: add skipCalendarSyncTaskCreation flag to handleCancelBooking to prevent infinite loops
- Add skipCalendarSyncTaskCreation field to bookingCancelSchema in zod-utils.ts
- Update handleCancelBooking to skip EventManager.cancelEvent when flag is true
- Update CalendarSyncService.cancelBooking to pass skipCalendarSyncTaskCreation: true
- Update CalendarSyncService.rescheduleBooking to pass skipCalendarSyncTaskCreation: true
- Update tests to verify the flags are passed correctly
This prevents infinite loops when calendar events are cancelled/rescheduled from
external calendars (Google/Office365) which trigger webhooks to Cal.com, which
would otherwise try to update the external calendar again.
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: rename flag to skipCalendarSyncTaskCancellation and use static imports
- Rename skipCalendarSyncTaskCreation to skipCalendarSyncTaskCancellation in handleCancelBooking
- Convert dynamic imports to static imports in CalendarSyncService
- Update tests to use vi.hoisted for proper mock hoisting with static imports
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add error handling and static import for handleNewBooking
- Create index.ts entry point for handleNewBooking directory
- Add try-catch error handling to cancelBooking and rescheduleBooking
- Log errors but don't block calendar sync operations
- Update tests to verify errors are caught and logged, not thrown
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: use RegularBookingService directly and fix safeStringify usage
- Remove handleNewBooking/index.ts and call getRegularBookingService().createBooking() directly in CalendarSyncService
- Fix safeStringify usage: apply to error itself, not wrapper object
- Update tests to mock getRegularBookingService instead of handleNewBooking
Addresses PR comments from Volnei and Cubic-dev-ai
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: restore handleNewBooking/index.ts and fix bookingData structure for calendar sync
- Restore handleNewBooking/index.ts as entry point for static imports
- Fix CalendarSyncService.rescheduleBooking to use correct bookingData structure
with required fields (eventTypeId, start, end, timeZone, language, metadata)
- Use rescheduleUid to indicate this is a reschedule operation
- Fix safeStringify usage in error logging (wrap only the error, not the whole object)
- Update tests to match new bookingData structure
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* refactor: address PR comments - use RegularBookingService directly, remove unnecessary flags, fix booking reference update
- Remove handleNewBooking/index.ts wrapper and use getRegularBookingService().createBooking() directly in CalendarSyncService
- Remove allRemainingBookings and cancelSubsequentBookings flags from cancelBooking (only cancel the specific booking, not the entire series)
- Move bookingReference update outside skipCalendarSyncTaskCancellation block for data consistency
- Update tests to match new implementation
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* chore: remove unnecessary comment from zod-utils.ts
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* feat: add Sentry metrics telemetry and fix null assertions in CalendarSyncService
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: use dynamic import for getRegularBookingService to avoid RAQB import in server context
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* fix: use dynamic import for findTeamMembersMatchingAttributeLogic to avoid RAQB import in server context
Co-Authored-By: Volnei Munhoz <volnei.munhoz@gmail.com>
* improve booking update
* add more tests and edge cases
* fix: add required actionSource and Sentry mocks to calendar sync
Add actionSource: "SYSTEM" to handleCancelBooking call after it became
required, and mock @sentry/nextjs in test files.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
* fix: hide branding for teams
* fix: remove unused organizationId and username fields from profiles select
Addresses Cubic AI review feedback (confidence 9/10) to select only
the profile fields that are actually used. The organizationId and
username fields were fetched but never referenced in this function.
Co-Authored-By: unknown <>
* fix: unit tests
* fix: add prisma named export to test mock
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* Add tests: packages/features/profile/lib/hideBranding.test.ts
Generated by Paragon from proposal for PR #27643
* chore: implement cubic feedback
* fix: merge conflicts
* fix: unit tests
* fixup
* refactor: implement DI pattern for event type service
* fix: atoms build
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: workflow auto translation
* tests: add unit tests
* refactor: tests and workflow
* fix: type err
* fix: type err
* fix: remove redundant index on WorkflowStepTranslation
The @@index on [workflowStepId, field, targetLocale] duplicates the @@unique
constraint on the same columns. A unique index already provides efficient
lookups, so the separate @@index adds storage overhead and write latency
without benefit.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* fix: correct locale mapping when translation API returns null
Map translations with their corresponding locales before filtering to
preserve correct locale-to-translation associations. Previously, filtering
out null translations would reindex the array, causing incorrect locale
mappings when any translation in the batch failed.
Also fixes pre-existing lint warnings:
- Move exports to end of file
- Add explicit return type to processTranslations
- Replace ternary with if-else for upsertMany selection
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: address review feedback for workflow auto-translation
- Add change detection before creating translation tasks
- Rename userLocale to sourceLocale in task props for clarity
- Show source language in UI with new translation key
- Extract SUPPORTED_LOCALES to shared translationConstants.ts
- Fix locale mapping bug in translateEventTypeData.ts
- Add WhatsApp translation support
- Abstract translation lookup into shared translationLookup.ts helper
- Restore if-else readability for SCANNING_WORKFLOW_STEPS
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-Authored-By: unknown <>
* fix: update test to use sourceLocale instead of userLocale
Co-Authored-By: unknown <>
* refactor: feedback
* fix: handle first time
* fix: tests
* fix: tests
* fix: address Cubic AI review feedback (confidence 9/10 issues)
- WhatsApp translation: Apply variable substitution using getSMSMessageWithVariables
and clear contentSid when using translated body to ensure Twilio uses the
translated text instead of the original template
- update.handler.ts: Change sourceLocale assignment from ?? to || for consistency
with tasker payload behavior (line 481)
- ITranslationService.ts: Rename methods from plural to singular naming:
- getWorkflowStepTranslations -> getWorkflowStepTranslation
- getEventTypeTranslations -> getEventTypeTranslation
Updated all call sites and tests accordingly
Co-Authored-By: unknown <>
* fix: address Cubic AI review feedback (confidence 9/10+ issues)
- Fix getSMSMessageWithVariables to handle WHATSAPP_ATTENDEE action for
locale and timezone (confidence 9/10)
- Remove WhatsApp translation feature that set contentSid to undefined
since Twilio ignores body parameter for WhatsApp and requires
pre-approved Message Templates (confidence 10/10)
Co-Authored-By: unknown <>
* fix: translatio
* Add tests: packages/features/eventTypeTranslation/repositories/EventTypeTranslationRepository.test.ts
Generated by Paragon from proposal for PR #27087
* Add tests: packages/features/tasker/tasks/translateWorkflowStepData.test.ts
Generated by Paragon from proposal for PR #27087
* chore: nit
* chore: verfied atg
* fix: set sourceLocale for new steps, add shouldDirty to checkbox, remove spec docs
- Set sourceLocale fallback in addedSteps mapping to fix stale detection mismatch
- Add { shouldDirty: true } to autoTranslateEnabled checkbox onChange
- Remove specs/workflow-translation/ directory (planning docs, not for repo)
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
Co-Authored-By: unknown <>
* chore: add specs back
* fix: type error
* fix: type error
* fix: type err
* fix: tests
* refactor: feedback
* fix: type err
* refactor
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <udit.takkar@cal.com>
Co-authored-by: Udit Takkar <udit.07.takkar@gmail.com>
* perf: improve getTotalBookingDuration query
* perf: improve getTotalBookingDuration query
* test: add unit tests for getTotalBookingDurationForUsers and yearly duration pre-fetching
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: integration tests
* fix: update tests
* fix: use vi.stubEnv instead of direct process.env modification for test isolation
Replace direct deletion of process.env variables with vi.stubEnv() to ensure
automatic restoration after tests, preventing flaky tests and side effects
in parallel execution.
Issue identified by Cubic AI (confidence: 9/10)
Co-Authored-By: unknown <>
* chore: add log
* refactor: imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add no users found fallback action for routing forms
- Add NoUsersFoundFallbackActionType enum with CustomPageMessage and ExternalRedirectUrl options
- Add noUsersFoundFallbackAction field to zodNonRouterRoute schema
- Add NoUsersFoundFallback UI component in RouteBuilder for configuring fallback action
- Update handleResponse to return noUsersFoundFallbackAction when no team members match
- Add i18n translation keys for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: change fallback to use same action options as main route
- Replace noUsersFoundFallbackAction with fallbackAction that has same structure as main action
- Add action dropdown and input fields to fallback attributes query builder section
- Support Event redirect, External URL, and Custom page options in fallback
- Remove separate NoUsersFoundFallback toggle component
- Update handleResponse to return fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure fallbackAction type is always defined in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add guards for route.fallbackAction in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only return fallbackAction when no users are found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: restructure fallback section to mirror main route structure
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure backwards compatibility for existing routes with fallbackAttributesQueryValue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction with main event type for backwards compatibility
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include fallbackAction in getRoutesToSave to persist changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use fallbackAction in getRoutedUrl when no team members found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use i18n for fallback label and add tests for fallback action
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: do not trigger fallback action when CRM contact owner is found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: prioritize fallbackAction over fallbackAttributesQueryValue
When a route has a fallbackAction configured, skip the
fallbackAttributesQueryValue to ensure the fallbackAction is triggered
when no team members are found. This maintains backwards compatibility
by only using fallbackAttributesQueryValue when fallbackAction is not set.
Also treats teamMemberIdsMatchingAttributeLogic being null (routing couldn't
run, e.g., missing orgId) the same as an empty array for the purpose of
triggering the fallbackAction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract RouteActionSelector shared component for action selection UI
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add i18n string for default custom page message
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* revert: remove webhook trigger changes (to be moved to separate PR)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add ROUTING_FORM_FALLBACK_HIT to PayloadBuilderFactory trigger mapping
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* style: apply biome formatting fixes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: restore removed comments in RouteBuilder
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add prisma migration for ROUTING_FORM_FALLBACK_HIT enum value
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add ROUTING_FORM_FALLBACK_HIT to FormTriggerEvents type union
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: add test coverage for triggerFallbackWebhook function
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: consolidate fallback webhook firing with form submission webhooks
Move ROUTING_FORM_FALLBACK_HIT webhooks into the same Promise.all() batch
as FORM_SUBMITTED / FORM_SUBMITTED_NO_EVENT, eliminating redundant
getWebhooks/getWebhookTargetEntity/getOrgIdFromMemberOrTeamId calls.
- Add fallbackAction parameter to _onFormSubmission and onSubmissionOfFormResponse
- Remove triggerFallbackWebhook function and all its usages/imports
- Move getFallbackAction() earlier in handleResponse.ts and pass result through
- Update all tests to verify batched webhook behavior
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add UI option for trigger
* feat: persist fallbackAction on queued form responses
When a routing form response is queued, the fallbackAction determined
during routing is now stored on the QueuedFormResponse record. When
the queued response is later converted to a real FormResponse, the
stored fallbackAction is passed to onSubmissionOfFormResponse so the
ROUTING_FORM_FALLBACK_HIT webhook fires correctly.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* test: add coverage for fallbackAction in queued form response flow
Verify that fallbackAction is persisted when queuing and passed through
to onSubmissionOfFormResponse when the queued response is converted.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Replace request.url with WEBAPP_URL from @calcom/lib/constants as the base URL
for NextResponse.redirect() in booking confirmation API routes. Behind a reverse
proxy, request.url resolves to http://localhost:3000 instead of the public domain.
Fixes#20358
Co-Authored-By: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* SMS reminders not trigger
* addressed devin review
* Add tests: packages/features/ee/workflows/lib/service/scheduleLazySMSWorkflow.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/ee/workflows/lib/reminders/reminderScheduler.smsLazy.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/tasker/tasks/sendWorkflowSMS.test.ts
Generated by Paragon from proposal for PR #27942
* Add tests: packages/features/ee/workflows/lib/reminders/smsReminderManager.test.ts
Generated by Paragon from proposal for PR #27942
* Delete packages/features/ee/workflows/lib/reminders/reminderScheduler.smsLazy.test.ts
* fix: update sendWorkflowSMS tests to match refactored implementation
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: use smsReminderNumber fallback for attendee phone in SMS workflows
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* revert: keep only scheduleSMSReminders.ts changes, revert all other files
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: restore reminderScheduler.ts and smsReminderManager.ts to original branch state
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* fix: use smsReminderNumber fallback for attendee phone in WhatsApp reminders
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
- Add strategyName property to ISeatBillingStrategy interface and all implementations
- Log which strategy handled the invoice.upcoming webhook (not just whether it applied)
- Always log strategy result (not just when applied=true) for better observability
- Rename highWaterMarkApplied to strategyApplied since applied covers multiple strategies
Co-Authored-By: unknown <>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix(caldav): use consistent UIDs and inject VTIMEZONE in iCalendar output
Two remaining CalDAV interop issues from #9485:
1. UID consistency: use the booking's canonical UID (event.uid) instead
of always generating a new random UUID. CalDAV servers use UID as the
event identifier — different UIDs cause duplicate calendar entries.
2. VTIMEZONE injection: the ics library generates UTC times with no
VTIMEZONE block. CalDAV servers like Fastmail read this as UTC and
send scheduling emails with wrong times. Per RFC 5545 §3.6.5,
DTSTART with TZID requires a matching VTIMEZONE component. We now
build a proper VTIMEZONE using binary-searched DST transitions for
the event's year, handling Northern/Southern hemisphere correctly.
* fix: use pre-transition offset for VTIMEZONE DTSTART per RFC 5545
The DTSTART in VTIMEZONE components must represent the local time
interpreted with the pre-transition offset (TZOFFSETFROM), not the
post-transition offset. For example, US Eastern spring forward DTSTART
should be 02:00 (EST), not 03:00 (EDT).
* Remove comments on UID handling in createEvent
Removed comments about UID handling for calendar events.
* Revise injectVTimezone documentation
Update injectVTimezone function documentation to clarify UTC handling.
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: use import.meta.env instead of process.env in embed.ts
Using import.meta.env is Vite's native way to handle environment variables
in browser bundles. This prevents biome from auto-adding 'import process
from node:process' which breaks the embed.js in browsers.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Remove unncessar comment
* fix: migrate remaining process.env to import.meta.env in embed.ts
Complete the migration started by @hariombalhara by also converting
process.env usages at lines 1681, 1682, and 1700 to import.meta.env.
Update env.d.ts to include EMBED_PUBLIC_EMBED_FINGER_PRINT,
EMBED_PUBLIC_EMBED_VERSION, and INTEGRATION_TEST_MODE types.
This ensures no process.env references remain in embed.ts, fully
preventing Biome from auto-adding import process from node:process.
Co-Authored-By: unknown <>
* fix: add biome overrides for embed-core to prevent auto-import of node:process
Reverts import.meta.env back to process.env since API V2's tsconfig uses
module: commonjs which doesn't support import.meta. Instead, adds biome
overrides for embed-core to disable useNodejsImportProtocol, noProcessEnv,
and noProcessGlobal rules that were causing biome to auto-add
'import process from node:process' on save.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update routing form identifier hint text with learn more link
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* UI improvements
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* fix: prevent deselecting active option in ToggleGroup
Radix UI's single-type ToggleGroup allows deselection by default,
calling onValueChange("") when the active item is clicked.
Fix by converting to controlled mode with proper dual-mode support:
- Controlled (value prop): parent owns state, component just filters
empty values from onValueChange. Parent can still reject changes.
- Uncontrolled (defaultValue prop): internal useState prevents
deselection by only updating state for non-empty values.
* Update ToggleGroup.tsx
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
* fix: add CSRF protection to OAuth callback via HMAC-signed nonce
The OAuth state parameter was used only for passing application data
(returnTo, teamId) with no cryptographic binding to the user session.
An attacker could authorize their own account on a provider, capture the
authorization code, and trick a logged-in user into visiting the callback
URL to link the attacker's account to the victim's Cal.com profile.
Changes:
- encodeOAuthState: generate a random nonce and HMAC-sign it with
NEXTAUTH_SECRET + userId, injecting both into the OAuth state
- decodeOAuthState: verify the HMAC on callback using timingSafeEqual;
skip verification when nonce is absent (backwards compatible with apps
that don't yet use encodeOAuthState)
- Stripe callback: replace raw state.returnTo redirect with
getSafeRedirectUrl to prevent open redirect, remove redundant
getReturnToValueFromQueryState, add missing return on access_denied
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: make CSRF nonce verification mandatory with allowlist for exempt apps
Makes nonce/HMAC verification mandatory by default in decodeOAuthState,
preventing attackers from bypassing CSRF protection by omitting nonce
fields from the state parameter.
Apps not yet migrated to encodeOAuthState (stripe, basecamp3, dub,
webex, tandem) are explicitly allowlisted and pass their slug to
decodeOAuthState to skip verification.
Addresses review feedback (identified by cubic) about the conditional
check being trivially bypassable.
Co-Authored-By: unknown <>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: Add infrastructure for no-show audit integration
- Add Prisma migrations for SYSTEM source and NO_SHOW_UPDATED audit action
- Add NoShowUpdatedAuditActionService with array-based attendeesNoShow schema
- Update BookingAuditActionServiceRegistry to include NO_SHOW_UPDATED
- Update BookingAuditTaskConsumer and BookingAuditViewerService
- Add AttendeeRepository methods for no-show queries
- Update IAuditActionService interface with values array support
- Update locales with no-show audit translation keys
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add NO_SHOW_UPDATED to BookingAuditAction and SYSTEM to ActionSource types
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED from BookingAuditAction type to match Prisma schema
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Update BookingAuditActionSchema to use NO_SHOW_UPDATED instead of HOST_NO_SHOW_UPDATED and ATTENDEE_NO_SHOW_UPDATED
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: Remove deprecated no-show audit services and unify to NoShowUpdatedAuditActionService
- Delete HostNoShowUpdatedAuditActionService and AttendeeNoShowUpdatedAuditActionService
- Update BookingAuditProducerService.interface.ts to use queueNoShowUpdatedAudit
- Update BookingAuditTaskerProducerService.ts to use queueNoShowUpdatedAudit
- Update BookingEventHandlerService.ts to use onNoShowUpdated
- Add integration tests for NoShowUpdatedAuditActionService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Add data migration step for deprecated no-show enum values
Addresses Cubic AI review feedback (confidence 9/10): The migration now
includes an UPDATE statement to convert existing records using the
deprecated 'host_no_show_updated' or 'attendee_no_show_updated' enum
values to the new unified 'no_show_updated' value before the type cast.
This prevents migration failures if any existing data uses the old values.
Co-Authored-By: unknown <>
* fix: Use CASE expression in USING clause for enum migration
Fixes PostgreSQL error 'unsafe use of new value of enum type' by avoiding
the ADD VALUE statement and instead using a CASE expression in the ALTER
TABLE USING clause to convert deprecated enum values (host_no_show_updated,
attendee_no_show_updated) to the new unified value (no_show_updated) during
the type conversion.
Co-Authored-By: unknown <>
* fix: Replace hardcoded color with semantic text-success class
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: Remove color class completely from display fields
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: Add valuesWithParams support for translatable complex field values
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor(booking-audit): use discriminated union for displayFields and update consumers
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add explicit return type to getBookingHistoryHandler to bust stale tRPC build cache
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: replace $t() nested interpolation with separate translation keys and add translationsWithParams tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(meta-pixel): add trackingEvent to zod schema
* fix: do not allow trackingEvent to be optional
* feat(meta-pixel): add trackingEvent selection dropdown to UI
* feat(meta-pixel): add trackingEvent to Event Type App Data
* fix(meta-pixel): implement history API hook for SPA conversion tracking
* fix(meta-pixel): check for cancel in URL and prevent Re-wrapping of pushState
* Update EventTypeAppSettingsInterface.tsx
* fix: make trackingEvent optional in zod schema to fix type-check
---------
Co-authored-by: Sahitya Chandra <sahityajb@gmail.com>
* feat: add keyboard shortcuts and tooltips to booking slideover buttons
Co-Authored-By: peer@cal.com <peer@cal.com>
* fix: remove duplicate useBookingLocation import and fix import ordering
- Removed duplicate import of useBookingLocation from non-existent
@calcom/web/modules/bookings/hooks/useBookingLocation path
- Fixed import ordering to satisfy biome organizeImports rules
- Removed unnecessary code comment
- Original feature by @PeerRich via Devin AI
Co-Authored-By: unknown <>
* use same style arrows for both button and remove bydefault focus from them
* fix join button tooltip hover
* fix: disable keyboard shortcuts when overlays/dialogs are open on BookingDetailsSheet
Co-Authored-By: unknown <>
* fix: use focus-based detection instead of selector-based overlay detection for keyboard shortcuts
Co-Authored-By: unknown <>
* fix: allow keyboard shortcuts when focus is on sheet ancestors
Co-Authored-By: unknown <>
* fix: use capture phase for keyboard handler to prevent Enter from activating focused buttons
Co-Authored-By: unknown <>
* fix: allow shortcuts when focus is on page elements outside any Radix portal
Co-Authored-By: unknown <>
* fix: handle calendar event clicks in onInteractOutside to prevent sheet close/reopen
Co-Authored-By: unknown <>
* fix: stop arrow key propagation to prevent Radix dropdown from opening during booking navigation
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: always stop arrow key propagation when sheet is active, even at first/last booking
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: extract keyboard handler into testable utility with tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: resolve type errors in keyboard handler config and test mocks
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* test: add e2e tests for booking sheet keyboard shortcuts
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: replace text= locators with data-testid selectors in e2e tests
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* made 3 fixes: Fix 1 — Stabilize handleNext/handlePrevious/handleClose with useCallback
In BookingDetailsSheet.tsx, all three handler functions were plain arrow functions recreated on every render, causing the useEffect to tear down and re-attach the document keydown listener unnecessarily. Wrapped all three in useCallback with proper dependency arrays (the Zustand store functions they call).
Fix 2 — data-booking-list-item verified (no change needed)
Confirmed that data-booking-list-item is rendered on BookingListItem.tsx and data-booking-calendar-event is rendered on Event.tsx. The onInteractOutside handler in the final merged state correctly checks both selectors. No code change required.
Fix 3 — Removed dead code from JoinMeetingButton
Reverted JoinMeetingButton back to a plain function component:
Removed forwardRef wrapping (no caller passes a ref)
Removed showTooltip prop (unused — tooltip is handled by the parent BookingDetailsSheet)
Removed ref prop from the inner Button
Removed unused Tod forwardRef imports
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Co-authored-by: eunjae@cal.com <hey@eunjae.dev>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
* init
* wiring up
* fix type
* feat: implement DI pattern for webhook producer in API v2
- Export IWebhookProducerService and getWebhookProducer from platform-libraries
- Add WEBHOOK_PRODUCER token and useFactory provider in RegularBookingModule
- Inject webhookProducer in RegularBookingService and pass to base class
This follows the composition root pattern where only the NestJS module
knows about getWebhookProducer(), and all consumers depend only on the
IWebhookProducerService interface via constructor injection.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* test: migrate BOOKING_REQUESTED tests to new webhook architecture
- Remove failing BOOKING_REQUESTED tests from fresh-booking.test.ts (4 tests)
- Remove failing BOOKING_REQUESTED tests from reschedule.test.ts (2 tests)
- Remove failing BOOKING_REQUESTED test from collective-scheduling.test.ts (1 test)
- Replace WebhookTaskConsumer.test.ts with placeholder (constructor changed)
- Create new webhook architecture test suite:
- producer/WebhookTaskerProducerService.test.ts (14 tests)
- consumer/WebhookTaskConsumer.test.ts (8 tests)
- consumer/triggers/booking-requested.test.ts (8 tests)
The new test suite is organized by trigger type for extensibility as more
triggers are migrated to the producer/consumer pattern.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* test: remove paid events BOOKING_REQUESTED test (moved to new architecture)
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* wrap webhook in own try-catch
* wire datafetcher
* fix
* fix v2
* fix circular dependency
* --
* merge-conflict-resolve
* mreg-conflict-resolve
* remove early return
* test: add integration tests for BOOKING_REQUESTED webhook producer invocation
Cover all 8 scenarios verifying the booking flow correctly invokes
the webhook producer for BOOKING_REQUESTED:
1. Basic confirmation → queueBookingRequestedWebhook called
2. Booker-is-organizer + confirmation → still called
3. Confirmation threshold NOT met → not called (BOOKING_CREATED instead)
4. Confirmation threshold IS met → called
5. Paid event + confirmation → called after payment succeeds
6. Reschedule + confirmation (non-organizer) → called (not BOOKING_RESCHEDULED)
7. Reschedule + confirmation (organizer) → not called (BOOKING_RESCHEDULED instead)
8. Collective scheduling + confirmation → called
Adds reusable MockWebhookProducer helper in @calcom/testing for
extendable use as more webhook triggers migrate to the new architecture.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix bug
* fix conditional check
* remove unnecessary comment
* add missing expect
* remove empty test
* clean up
* tasker config
* --
* fix missing metadata
* remove faulty if else
* test: add payload content verification tests for BOOKING_REQUESTED webhook
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* remove unnecessary tests
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
* feat: replace admin API settings page with link to API docs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: remove unused AdminAPIView component
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: rename admin API tab to API Docs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: change URL prefill behavior to skip only invalid fields
Previously, when prefilling booking form fields from URL query parameters,
if any single field failed validation, all prefilling was skipped and an
empty object was returned.
This change modifies the behavior so that only invalid fields are skipped,
while valid fields are still prefilled. The implementation:
- Adds validateSingleFieldForPrefill() to validate individual fields
- Adds parseFieldByField() to collect valid fields when full parse fails
- Modifies getBookingResponsesPartialSchema to use field-by-field parsing
as a fallback when full schema validation fails
- Logs which specific fields were skipped and why
This improves user experience by preserving valid prefill data even when
some fields have invalid values.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: reduce code duplication in partial prefill validation
Simplify the implementation by integrating field-by-field error handling
directly into the existing preprocess() function instead of using separate
helper functions.
Changes:
- Remove validateSingleFieldForPrefill() and parseFieldByField() functions
- Simplify getBookingResponsesPartialSchema to directly use preprocess()
- Add try-catch wrapper in preprocess() when isPartialSchema=true
- Continue processing other fields even if one fails during preprocessing
- Log skipped fields with reasons at debug level
This reduces code duplication while maintaining the same behavior of
skipping only invalid fields during URL prefill.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* refactor: rename getRefinedValue to superRefineField and add edge case tests
- Rename getRefinedValue to superRefineField for clarity
- Remove duplicate multiselect test block
- Add test for empty object name field during partial prefill
- Add test for invalid URL format during partial prefill
- Add test for null responses during partial prefill
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Simplifu
* Simplif more
* Enhance booking responses schema tests to validate required fields and name variants. Updated error handling for empty names and added tests for first and last name requirements. Refactored schema validation logic to improve clarity and maintainability.
* fix types
* test: add test for email validation behavior during partial prefill
Documents that email validation is relaxed to z.string() during partial prefill,
consistent with phone field behavior where partial values like '+91' are accepted.
This addresses Cubic AI feedback (confidence 9/10) to verify email handling.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix
* Add tests: packages/features/bookings/lib/getBookingResponsesSchema.test.ts
Generated by Paragon from proposal for PR #26982
* Revert "Add tests: packages/features/bookings/lib/getBookingResponsesSchema.test.ts"
This reverts commit fc2b7ad7ca8099bc4f68e97b383ea16b41cdd772.
* Simplify
* fix: sanitize partial prefill log to avoid exposing user input data
Replaced raw Zod issues array in console.warn with a count of
validation errors to prevent sensitive user data from being logged.
Issue identified by cubic (confidence 9/10).
Co-Authored-By: unknown <>
* fix
* Remove throw Error
* Refactor booking responses schema tests to use helper functions for assertions
- Introduced `expectResponsesToBe` and `expectParsingToFail` helper functions to streamline test assertions.
- Replaced direct assertions with these helper functions for improved readability and maintainability.
- Removed redundant error handling code to simplify test logic.
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add duplicate functionality for managed event types
Co-Authored-By: peer@cal.com <peer@cal.com>
* feat: add disclaimer and redirect for managed event type duplication
- Add info alert disclaimer in duplicate modal for managed event types
- Pass schedulingType to duplicate dialog via query params
- Redirect to assignment tab after duplicating managed event types
- Add translation key for managed event type duplicate disclaimer
Co-Authored-By: peer@cal.com <peer@cal.com>
* test: add tests for managed event type duplication
Co-Authored-By: peer@cal.com <peer@cal.com>
* refactor: replace PrismaMock unit test with Prisma integration test for duplicate handler (#27973)
* refactor: replace PrismaMock unit test with Prisma integration test for duplicate handler
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: re-throw TRPCErrors in duplicate handler catch block and assert specific error codes in tests
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Hariom Balhara <1780212+hariombalhara@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
* fix: allow booking rejection via GET for email clients that do not support forms
Co-Authored-By: anik@cal.com <adhabal2002@gmail.com>
* Refactor test cases for GET handler in route.test.ts
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Replace 3 sequential queries (with expensive attendee email IN clauses)
with 2 parallel queries using indexed eventType.teamId filtering and
in-application user filtering with Sets for O(1) lookups.
Fix managed events booking-limit behavior: managed bookings now only
match by organizer userId (not attendee email), preserving the original
query semantics where managed bookings used userId-only matching.
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Fix exclusion filter - include all team members
* Fix display when members aren't saved in the DB
* Update tests
* test: add missing negation operator tests for TEXT, NUMBER, and compound rules
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert non-intentional changes to AddMembersWithSwitch.tsx
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Joe Au-Yeung <j.auyeung419@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: decouple large calendar from features
* fix: move calendar to features since this is the better approach
* chore: move OOO slots to features
* feat: add DefaultOutOfOfficeSlot fallback for calendar OOO rendering
Co-Authored-By: rajiv@cal.com <sahalrajiv6900@gmail.com>
* chore: cleanup unused LargeCalendar component
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Replaced dayjs with date-fns for better performance and consistency in date handling. Added Tooltip for displaying formatted timestamps and improved search functionality by translating field labels and values. Cleaned up code for better readability.
* fix: deep link reschedule audit log to booking drawer history tab
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: make booking drawer tab-agnostic for cross-tab deep links
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use status-agnostic /bookings URL for audit log deep links
- Update audit service URLs from /bookings/upcoming?uid=... to /bookings?uid=...
- Add /bookings/page.tsx redirect that routes to /bookings/upcoming preserving query params
- Update tests to expect new URL format
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* refactor: use client-side replaceState instead of server redirect for booking deep links
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use router.replace instead of replaceState to update tab and booking list
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* improvements
* refactor: extract deep link logic from BookingListContainer into usePreSelectedBooking hook
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use proper BookingOutput status type in test helper
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: decouple getTabForBooking from BookingOutput type for simpler testing
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: accept Date | string for endTime in BookingForTabResolution interface
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: eliminate initialBookingUid prop drilling and revert formatting-only changes
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use AuditDeepLink wrapper to preserve target=_blank through ServerTrans cloneElement
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Improve code organization
* refactor: rename usePreSelectedBooking to useSwitchToCorrectStatusTab
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: push preSelectedBooking into store so drawer opens on direct navigation
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fixes
* fixes
* fixes
* fix
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
* fix: revert assignmentReason breaking change in webhook payloads
Remove the new { category, details } format from EventPayloadType to
maintain backward compatibility for webhook consumers. The new format
is stripped at all webhook payload construction sites by destructuring
assignmentReason out of CalendarEvent before spreading into the payload.
A sanitizeAssignmentReasonForWebhook function provides an additional
safety net in sendPayload itself. Emails and booking single view
continue to use the new format via CalendarEvent.
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: strip assignmentReason from handlePaymentSuccess webhook payload
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* fix: strip assignmentReason from triggerWebhooks and handleSeats webhook payloads
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
* refactor: use zod safeParse instead of type assertion in sanitizeAssignmentReasonForWebhook
Co-Authored-By: ali@cal.com <alishahbaz7@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat(companion): add configurable landing page feature
- Add useUserPreferences hook for persistent storage of landing page preference
- Add LandingPagePicker component for both iOS and Android/web platforms
- Update tabs index to redirect based on user preference
- Update bookings index to accept initial filter from URL params
- Add App Settings section in More screen with landing page selector
- Clear user preferences on logout for fresh state
* fix(companion): remove try-finally for React Compiler compatibility
* fix(companion): use router.replace for dynamic landing page redirect
Replace Redirect component with router.replace() to fix TypeScript
strict typing issue with expo-router's Href type for dynamic routes.
* fix(companion): use literal route strings for TypeScript strict typing
Use switch statement with literal route strings instead of dynamic
string variable to satisfy expo-router's strict Href type checking.
* working fix
* better css
* refactor: apply biome formatting to small packages + packages/lib
Format packages/sms, packages/prisma, packages/platform/libraries,
packages/platform/examples, packages/platform/types, packages/emails,
and packages/lib.
Excludes packages/platform/examples/base/src/pages/[bookingUid].tsx
due to pre-existing lint errors.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* revert: remove packages/platform formatting changes
Revert biome formatting for packages/platform as requested.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* feat: enable onboarding-v3 globally and update e2e tests
- Add migration to set onboarding-v3 feature flag enabled=true
- Rewrite onboarding.e2e.ts for v3 flow (Plan Selection → Personal Settings → Calendar)
- Update URL assertions in signup, team-invitation, org-invitation, ab-tests-redirect, and auth tests to accept both old and new onboarding paths
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: use pathname-only matching in waitForURL to prevent false matches on callbackUrl query param
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* use test IDs
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* feat: add signup watchlist review feature flag and handler logic
- Add 'signup-watchlist-review' global feature flag
- Add SIGNUP to WatchlistSource enum in Prisma schema
- When flag enabled, lock new signups and add email to watchlist
- Show 'account under review' message on signup page
- Add i18n strings for review UI
- Create seed migration for the feature flag
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* test: add isAccountUnderReview tests to fetchSignup test suite
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: address Cubic AI review feedback (confidence >= 9/10)
- Remove 'import process from node:process' in signup-view.tsx (P0 bug in 'use client' component)
- Move watchlist review check before checkoutSessionId early return in calcomSignupHandler (P1 premium bypass)
- Revert selfHostedHandler to original state (out of scope per user request)
- Add test mocks for FeaturesRepository and GlobalWatchlistRepository
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: remove node:process import from useFlags.ts (client-side file)
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* fix: remove !token condition from watchlist review check
Token is present in normal email-verified signups, so the !token
condition was incorrectly skipping watchlist review for verified users.
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* Apply suggestion from @cubic-dev-ai[bot]
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* refactor: move user lock to UserRepository.lockByEmail
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: use cached getFeatureRepository() instead of deprecated FeaturesRepository
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* refactor: remove user locking, keep only watchlist addition on signup review
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* feat: lock user on signup review, remove watchlist entry on unlock
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* Pruned the 'License' section and Pushed it before 'Acknowledgement' to avoid confusion and Clarity
* Improves README file by pruning License Section and Aligning Properly
* Remove License section from README.md
---------
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* fix: update data-table hook imports after refactor (#27833)
The data-table hooks (useDataTable, useFilterValue) were moved from
@calcom/features/data-table to ~/data-table/hooks/ in #27833 but this
file was missed.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: update DataTableProvider import path in insights-wrong-routing-view
Another import missed in #27833 refactor — DataTableProvider moved from
@calcom/features/data-table/ to ~/data-table/.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* factory and statergie
* chore: use correct method of DI
* feat: add onchagne
* add logic to HWM stat
* add webhook resolver methods to each statergy
* move seat tracking + webhooks over to own statergy
* Move to factory base approach
* move logic to correct class
* rename create -> createByTeamId
* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED
Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.
Identified by cubic (https://cubic.dev)
Co-Authored-By: unknown <>
* feat: active user billing
* add tests
* UI side for users on active billing
* use correct period of stripe sub
* feat: claude feedback
* fix: skip Stripe sync for canceled/expired subscriptions to prevent repeated API calls
Co-Authored-By: unknown <>
* feat: feedback
* feat: only render when active users mode is set
* fix type error
* fix: constants + feature flags
* fix: default to null in tests
* chore: use seats in test
* fix: use node:crypto protocol for Node.js builtin imports
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
## What does this PR do?
Fixes footer text overlap in the Booking Details drawer where action buttons covered the last content items on smaller screens.
**Root cause:** `sm:-my-6` on `SheetFooter` applied both negative top and bottom margins. The negative top margin pulled the footer up into the body content area, causing the overlap.
**Fix:**
- Removed `-mb-4` and `sm:-my-6` from `SheetFooter`, keeping only horizontal negative margins (`-mx-4` / `sm:-mx-6`) for edge-to-edge stretch
- Added `pb-0` to `SheetContent` to remove bottom padding so the footer sits flush at the bottom on all breakpoints
<img width="1098" height="1362" alt="image" src="https://github.com/user-attachments/assets/0418df56-a432-4edf-8e5b-664a5e8c18ac" />
- Fixes #XXXX (GitHub issue number)
- Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)
## Visual Demo (For contributors especially)
#### Image Demo (if applicable):
See screenshot above showing the overlap bug. The footer actions ("..." button) overlap with the last booking history entry.
## Mandatory Tasks (DO NOT REMOVE)
- [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). If N/A, write N/A here and check the checkbox.
- [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Open any booking in the bookings list to open the details drawer
2. Scroll to the bottom of the drawer content
3. Verify the footer actions (buttons, dropdown) do not overlap with the last content item
4. Test on both mobile and desktop viewport sizes — footer should sit flush at the bottom with no extra gap on either
## Human Review Checklist
- [ ] Verify footer appearance on **mobile** (below `sm` breakpoint) — no gap below footer, no overlap with content
- [ ] Verify footer appearance on **desktop** (`sm+` breakpoint) — same checks
- [ ] Confirm the footer still stretches edge-to-edge horizontally on both breakpoints
## Checklist
- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my changes generate no new warnings
- My PR is too large (>500 lines or >10 files) and should be split into smaller PRs
---
Link to Devin run: https://app.devin.ai/sessions/b9d130b8a7a54f039b90b53921b19c78
Requested by: @hariombalhara
* Add DB table for wrong assignment reports
* When report is submitted write to the db
* Prevent duplicate reportings
* test: add migration and tests for WrongAssignmentReport table
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add unique constraint on bookingUid and booking access check for hasWrongAssignmentReport
- Add @unique constraint on bookingUid in WrongAssignmentReport model to prevent duplicate reports at DB level
- Add booking ownership check using BookingAccessService in hasWrongAssignmentReport endpoint
- Refactor hasWrongAssignmentReport into separate handler and schema files
Addresses Cubic AI review feedback on PR #27405
Co-Authored-By: unknown <>
* feat: add routingFormId to WrongAssignmentReport and fix Select clearing
- Add routingFormId field to WrongAssignmentReport model in schema.prisma
- Add relation to App_RoutingForms_Form with SetNull on delete
- Update WrongAssignmentReportRepository.createReport to accept routingFormId
- Update BookingRepository.findByUidIncludeEventTypeAndTeamAndAssignmentReason to include routedFromRoutingFormReponse
- Extract routingFormId from booking in reportWrongAssignment handler
- Fix Select clearing issue: handle null case when user clears team member selection
- Update tests to include routingFormId field
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add migration for routingFormId in WrongAssignmentReport
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add wrong assignment reports dashboard under routing tab
- Add reviewedById and reviewedAt fields to WrongAssignmentReport model
- Add repository methods for listing reports by status and updating status
- Create tRPC endpoints for fetching reports and updating status
- Create dashboard UI with pending/reviewed tabs showing routing form name
- Add translation keys for dashboard UI
- Integrate dashboard into routing insights page
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: move hooks before early return and fix indentation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address Udit's review comments
- hasWrongAssignmentReport: throw UNAUTHORIZED error instead of returning false
- reportWrongAssignment: add try-catch for Prisma P2002 unique constraint error
- WrongAssignmentReport: add Team relation to teamId field
- WrongAssignmentReportRepository: use findUnique instead of findFirst
- reportWrongAssignment: use i18n for error messages
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: revert hasWrongAssignmentReport to return false when user lacks access
Per PR checklist, hasWrongAssignmentReport should return { hasReport: false }
when user lacks access to booking, not throw an error. This allows the UI
to gracefully treat 'no access' as 'no report exists'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: update mocks for findUnique and i18n in unit tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: throw UNAUTHORIZED in hasWrongAssignmentReport and squash migrations
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: move wrong assignment reports to its own tab under Insights
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Use data table for wrong reports
* Add option to view routing trace
* feat: add view routing form submission action to wrong assignment reports
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add RoutingFormResponseSheet component for viewing form submissions
- Create slide-out sheet to display routing form responses
- Map option IDs to display labels for select/multiselect fields
- Handle both legacy and modern option formats
- Add i18n strings: form_submission, no_responses_found
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* feat: improve wrong assignment reports UX
- Integrate RoutingFormResponseSheet as slide-out panel instead of new tab
- Fix dropdown padding by using StartIcon prop instead of manual Icon
- Allow direct status changes for reviewed reports (no need to reopen first)
- Remove unused Icon import
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: change User relation onDelete from Cascade to SetNull in WrongAssignmentReport
Address Hariom's review feedback:
- Changed reportedById from Int to Int? (nullable)
- Changed reportedBy relation from onDelete: Cascade to onDelete: SetNull
- Updated migration SQL to reflect these changes
This preserves wrong assignment reports even when the reporting user is deleted,
as the data is still useful for analysis.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add missing i18n strings for wrong assignment reports dashboard
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: move form response display value resolution server-side
Replace client-side option ID to label resolution in
RoutingFormResponseSheet with a new lean tRPC endpoint
(getFormResponseDisplay) that resolves values server-side using
the existing getHumanReadableFieldResponseValue utility. This
enforces DTO boundaries by returning a clean pre-resolved payload
instead of leaking internal option format details to the client.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add report wrong assignment button to routing trace sheet
Wire up the WrongAssignmentDialog from the routing trace sheet header
so users can flag wrong assignments directly while viewing the trace.
The report button is disabled with a tooltip when a report already
exists.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: reuse existing WrongAssignmentDialog from parent
Replace the duplicate WrongAssignmentDialog in RoutingTraceSheet with
a callback to the existing instance in BookingActionsDropdown. This
reduces the prop surface from a 6-field reportContext object to an
onReport callback and hasExistingReport boolean.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use repository instead of direct Prisma in getFormResponseDisplay
Replace direct Prisma query with PrismaRoutingFormResponseRepository's
findByIdIncludeForm method. Extend the method to also select form name,
description, userId, and teamId needed for display and auth checks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: replace direct Prisma calls with repository methods
Use MembershipRepository.hasMembership() for auth checks and
TeamRepository.findAllByParentId() for child team queries instead
of direct Prisma calls. Replace direct user query with
UserRepository.getTimeZoneAndDefaultScheduleId(). Remove unused
seed script.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: use PBAC services for wrong assignment report auth
Replace manual MembershipRepository.hasMembership() checks with
PBAC-aware permission checking. getWrongAssignmentReports uses
createTeamPbacProcedure middleware since teamId is in input.
updateWrongAssignmentReportStatus uses PermissionCheckService
directly since teamId is discovered from the report entity.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: resolve type errors in RoutingFormResponseSheet and wrong-routing view
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add seed script for wrong assignment reports test data
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* Revert "feat: add seed script for wrong assignment reports test data"
This reverts commit 0bd60e9661858a59aab1573d14d57d81733b7991.
* Only update reviewed fields when not pending
Co-authored-by: devin-ai-integration[bot] <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: wrap handleStatusChange in useCallback to fix useMemo recalculation
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Fix routing sheet UI
* fix: use appropriate error message in getFormResponseDisplay handler
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract WrongAssignmentReportService from tRPC handlers
Move business logic (booking lookup, duplicate check, report creation,
webhook dispatch, org-level team resolution) into a dedicated service
in packages/features. Handlers become thin controllers that only handle
auth checks and delegate to the service.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: restrict report status updates to admin/owner roles
Remove MembershipRole.MEMBER from fallbackRoles in
updateWrongAssignmentReportStatus permission check. Updating report
status is an administrative action that should be limited to team
admins and owners.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: localize hard-coded success message in WrongAssignmentReportService
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: rename reviewed tab to handled and add missing translations
Rename the "Reviewed" tab to "Handled" since it groups three distinct
statuses (Reviewed, Resolved, Dismissed). Also add missing translation
keys for "resolved" and "dismissed" status badges.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* merge: resolve conflicts with main branch
Merge main into devin/1769747741-wrong-assignment-dashboard, resolving conflicts in:
- BookingActionsDropdown.tsx: use main's booking prop with PR's fragment structure
- reportWrongAssignment.handler.ts: keep PR's service-based approach
- reportWrongAssignment.handler.test.ts: use main's class-based mocks with PR's additions
- WrongAssignmentReportService.ts: align with main's repo method and field names
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Add guard when accessing assignmentReasonSortedByCreatedAt
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: address Cubic AI review feedback - select projection and useLocale refactor
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove stale TRPCError assertion in test (service throws ErrorWithCode)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: remove unused findByTeamIdAndStatus and findByTeamIdAndStatuses methods
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* perf: add composite index on WrongAssignmentReport and narrow findByIdIncludeForm select
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
* perf: use lightweight findTeamIdById in update-status handler instead of findById
Co-Authored-By: alex@cal.com <me@alexvanandel.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: hariom@cal.com <hariombalhara@gmail.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: alex@cal.com <me@alexvanandel.com>
## What does this PR do?
Fixes an issue where underscores in form field identifiers were being stripped when converting to workflow template variables. According to the documentation, users should be able to use variables like `{COMPANY_NAME}` or `{MONTHLY_INFRASTRUCTURE_SPEND}`, but the regex in `formatIdentifierToVariable` was removing underscores, causing these variables to not match.
**The problem:**
- Form field identifier: `Company_Name` or `Monthly_Infrastructure_Spend`
- Expected variable: `{COMPANY_NAME}` or `{MONTHLY_INFRASTRUCTURE_SPEND}`
- Actual variable (before fix): `{COMPANYNAME}` or `{MONTHLYINFRASTRUCTURESPEND}`
**The fix:**
- Updated the regex from `[^a-zA-Z0-9 ]` to `[^a-zA-Z0-9_ ]` to preserve underscores in `formatIdentifierToVariable`
- Added a private `formatIdentifierToVariableLegacy` function that maintains the old behavior (strips underscores)
- Added `getVariableFormats` helper that returns both current and legacy formats for backward compatibility
- Added `convertResponsesToVariableFormats` helper in `executeAIPhoneCall.ts` to convert form responses to both variable formats
- Updated `customTemplate.ts` matching logic to support both variable formats
- Updated `executeAIPhoneCall.ts` to include both variable formats when building dynamic variables
This ensures existing templates using `{COMPANYNAME}` continue to work while new templates can use the documented `{COMPANY_NAME}` format.
## Mandatory Tasks (DO NOT REMOVE)
- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a documentation change. N/A - no docs changes needed.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
1. Create a routing form with a field identifier containing underscores (e.g., `Company_Name`)
2. Create a workflow with a template using `{COMPANY_NAME}` (with underscore)
3. Submit the form and verify the variable is replaced correctly
4. Also test with `{COMPANYNAME}` (without underscore) to verify backward compatibility
**Automated tests added:**
- `customTemplate.test.ts` - 15 tests covering `formatIdentifierToVariable`, `getVariableFormats`, and template variable replacement
- `executeAIPhoneCall.test.ts` - 6 tests covering `convertResponsesToVariableFormats` function for form responses
## Human Review Checklist
- [ ] Verify the regex change `[^a-zA-Z0-9_ ]` correctly preserves underscores
- [ ] Verify backward compatibility: both `{COMPANY_NAME}` and `{COMPANYNAME}` should work for the same form field
- [ ] Verify `convertResponsesToVariableFormats` correctly generates both variable formats
- [ ] Review test coverage for edge cases (empty responses, undefined values)
---
Link to Devin run: https://app.devin.ai/sessions/9d5d90178714493086d94691865c3e07
Requested by: @hariombalhara
<!-- devin-review-badge-begin -->
---
<a href="https://app.devin.ai/review/calcom/cal.com/pull/27571">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="https://static.devin.ai/assets/gh-open-in-devin-review-dark.svg?v=1">
<img src="https://static.devin.ai/assets/gh-open-in-devin-review-light.svg?v=1" alt="Open with Devin">
</picture>
</a>
<!-- devin-review-badge-end -->
* feat: re-render bookings page after feature opt-in without full refresh
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: make onOptInSuccess a required prop
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: maintain uid parameter in BookingDetailsSheet URL
Move the isSyncedFromUrlToStoreRef guard from the effect level to inside
the Zustand subscription callback. This ensures the Store→URL subscription
is always created while still preventing premature URL overwrites during
URL→Store sync.
Also adds an E2E test verifying the uid parameter appears in the URL
after clicking a booking item in the v3 bookings view.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Update apps/web/playwright/bookings-list.e2e.ts
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* fix: clean up bookings-v3 feature flag after E2E test
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
* refactor: apply biome formatting to packages/features (batch 1 - small subdirs)
Format small subdirectories in packages/features: di, flags, holidays, oauth,
settings, users, assignment-reason, selectedCalendar, hashedLink, host, form,
form-builder, availability, data-table, pbac, schedules, troubleshooter,
eventtypes, calendar-subscription, and root-level files.
Also includes straggler apps/web BookEventForm.tsx.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 2 - medium subdirs)
Format medium subdirectories in packages/features: auth, credentials,
calendars, routing-forms, routing-trace, attributes, watchlist, calAIPhone,
tasker, and webhooks.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 3 - bookings + insights)
Format bookings and insights subdirectories in packages/features.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 4 - ee)
Format packages/features/ee subdirectory covering billing, workflows,
organizations, teams, managed-event-types, round-robin, dsync,
integration-attribute-sync, and payments.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 5 - booking-audit part 1)
Format booking-audit di, actions, common, dto, repository, and types
subdirectories in packages/features/booking-audit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: apply biome formatting to packages/features (batch 6 - booking-audit part 2)
Format booking-audit service subdirectory in packages/features/booking-audit.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* perf: use UNION ALL with DISTINCT for bookings query optimization
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add unionAll and distinct to kysely test mocks
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add integration tests for getBookings query
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: add totalCount deduplication test for multi-branch union scenario
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: fix totalCount assertion to be exact instead of gte
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Apply suggestion from @keithwillcode
* perf: restore COUNT(DISTINCT id) for totalCount query
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add tests: packages/trpc/server/routers/viewer/bookings/get.handler.integration-test.ts
Generated by Paragon from proposal for PR #27841
* Add tests: packages/trpc/server/routers/viewer/bookings/get.handler.test.ts
Generated by Paragon from proposal for PR #27841
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add no users found fallback action for routing forms
- Add NoUsersFoundFallbackActionType enum with CustomPageMessage and ExternalRedirectUrl options
- Add noUsersFoundFallbackAction field to zodNonRouterRoute schema
- Add NoUsersFoundFallback UI component in RouteBuilder for configuring fallback action
- Update handleResponse to return noUsersFoundFallbackAction when no team members match
- Add i18n translation keys for new UI strings
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: change fallback to use same action options as main route
- Replace noUsersFoundFallbackAction with fallbackAction that has same structure as main action
- Add action dropdown and input fields to fallback attributes query builder section
- Support Event redirect, External URL, and Custom page options in fallback
- Remove separate NoUsersFoundFallback toggle component
- Update handleResponse to return fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure fallbackAction type is always defined in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add guards for route.fallbackAction in onChange handlers
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only return fallbackAction when no users are found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: restructure fallback section to mirror main route structure
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: ensure backwards compatibility for existing routes with fallbackAttributesQueryValue
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction with main event type for backwards compatibility
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: include fallbackAction in getRoutesToSave to persist changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use fallbackAction in getRoutedUrl when no team members found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use i18n for fallback label and add tests for fallback action
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: do not trigger fallback action when CRM contact owner is found
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: prioritize fallbackAction over fallbackAttributesQueryValue
When a route has a fallbackAction configured, skip the
fallbackAttributesQueryValue to ensure the fallbackAction is triggered
when no team members are found. This maintains backwards compatibility
by only using fallbackAttributesQueryValue when fallbackAction is not set.
Also treats teamMemberIdsMatchingAttributeLogic being null (routing couldn't
run, e.g., missing orgId) the same as an empty array for the purpose of
triggering the fallbackAction.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* refactor: extract RouteActionSelector shared component for action selection UI
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add i18n string for default custom page message
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* feat: add webhook trigger when routing form fallback route is hit
- Add ROUTING_FORM_FALLBACK_HIT to WebhookTriggerEvents enum in Prisma schema
- Add new trigger to routing-forms webhook triggers in constants
- Add translation string for the new webhook trigger
- Implement triggerFallbackWebhook function in formSubmissionUtils.ts
- Call webhook trigger from handleResponse.ts when fallback action is used
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: remove webhook object from error log to avoid exposing secrets
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* revert: remove webhook trigger changes (to be moved to separate PR)
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback query builder when fallbackAction is explicitly set
The fallback attributes query builder was being shown for new routes
when fallbackAction was not set, causing an extra 'Add rule' button
to appear. This broke e2e tests that relied on the button index.
Now the fallback query builder is only shown when fallbackAction is
explicitly set to EventTypeRedirectUrl, which happens after the route
is saved and reloaded (via auto-initialization in useRoutes).
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: use data-testid for attribute query builder selectors in e2e tests
- Add data-testid='attributes-query-builder' to the main attributes query builder div
- Update addAttributeRoutingRule and addAttributeRoutingRuleWithOperator to use the data-testid selector instead of nth() indices
- This makes the tests more robust and independent of DOM structure changes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction for new routes in useCreateRoute hook
- New routes created via useCreateRoute now initialize fallbackAction
- This ensures new routes have a valid fallbackAction when saved
- Matches the initialization logic for existing routes in useRoutes
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction at save time when action is EventTypeRedirectUrl
- When saving routes, if fallbackAction is undefined but action is EventTypeRedirectUrl,
initialize fallbackAction from the main action
- This handles the case where user creates a new route and changes action to an event type
- Fixes e2e test failures where form save was failing due to undefined fallbackAction
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: initialize fallbackAction when main action changes to EventTypeRedirectUrl
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback section when main action has valid event type selected
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: check for valid fallbackAction event type before preserving existing value
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* Give more structure to main and fallback routes
* Update comment
* Rename fallbackAttributesQueryBuilder to matchingMembersFallbackRoute
* Display custom event redirect input
* fix: address Cubic AI review feedback for routing form fallback
- Simplify getFallbackAction logic to use checkedFallback flag instead of
manually parsing attributesQueryValue (confidence 9/10)
- Add i18n translation for 'And connect with specific team members' string
using t('connect_with_specific_team_members') (confidence 9/10)
Co-Authored-By: unknown <>
* fix: update getFallbackAction to use checkedFallback and handle missing orgId case
- Use checkedFallback flag as primary indicator for triggering fallback action
- Also handle edge case where attribute routing was configured but couldn't run
due to missing orgId (teamMemberIdsMatchingAttributeLogic is null)
- Update test to mock checkedFallback: true when expecting fallback action
Co-Authored-By: unknown <>
* Address UI comment
* fix: Use fallbackAttributesQueryValue when fallback action is event redirect
Only use fallbackAttributesQueryValue when:
1. fallbackAction is EventTypeRedirectUrl (attribute routing applies)
2. fallbackAction is not set (backwards compatibility)
Skip it for CustomPageMessage and ExternalRedirectUrl fallback actions
since attribute routing doesn't apply to those action types.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: correct JSX syntax error in matchingMembersFallbackRoute
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* refactor: extract getCustomSlug helper to deduplicate custom event type slug logic
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: only show fallback attribute filtering for teams under orgs
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: show fallback action for all team forms, gate attribute filtering on isOrganization
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Hariom Balhara <hariombalhara@gmail.com>
The Date.now() method returns time in epoch miliseconds, adding 60 * 60 = 3.6seconds leads to a bug that results in expiring the access token in 3.6 seconds, although it been valid. It creates unnecessary api calls to the zoho servers before most the api calls.
* feat: add UTM parameter data to insights bookings CSV download
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* test: add UTM data tests and update snapshots for csvDataTransformer
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* fix: use Tracking relation for UTM data instead of flat fields on Booking
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
When disableCancelling or disableRescheduling is enabled on an event type,
the confirmation email still showed non-functional cancel/reschedule links.
The ManageLink email component already had correct conditional logic to
hide these links, but two flows constructed the CalendarEvent manually
without including the flags: the booking confirmation handler and the
payment booking flow. This caused the flags to be undefined, which
ManageLink interpreted as enabled.
Added disableCancelling and disableRescheduling to the Prisma select and
CalendarEvent object in both confirm.handler.ts and getBooking.ts.
Closes#22906
* factory and statergie
* chore: use correct method of DI
* feat: add onchagne
* add logic to HWM stat
* add webhook resolver methods to each statergy
* move seat tracking + webhooks over to own statergy
* Move to factory base approach
* move logic to correct class
* rename create -> createByTeamId
* fix: remove debug `true ||` overrides from IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED
Remove accidentally committed debug overrides that short-circuited
IS_STRIPE_ENABLED and IS_TEAM_BILLING_ENABLED to always be true,
bypassing Stripe credential checks. This would break self-hosted
instances without Stripe configured.
Identified by cubic (https://cubic.dev)
Co-Authored-By: unknown <>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: optimize getBusyTimesForLimitChecks with batched parallel queries and raw SQL
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* test: add unit tests for getBusyTimesForLimitChecks batched query optimization
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* refactor: use Prisma findMany instead of raw SQL for better testability
The batching (50 user chunks) and parallel execution (Promise.all) provide
the main performance optimization. Using findMany instead of $queryRaw
maintains the same performance characteristics while being compatible
with prismock for testing.
Co-Authored-By: udit@cal.com <udit222001@gmail.com>
* fix: type error
* refactor: add integration test
* chore: remove log
* chore: remove log
* fix: avoid logging PII (userId) in busyTimes debug log
Replace JSON.stringify(busyTimes) with count to avoid exposing userId
in logs. Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* chore: undo
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: update booking page terms text to use APP_NAME instead of 'our'
Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>
* remove changes for platform booker
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
* inviteMemberUtils in featuers
* update imports
* wip
* wip
* fix: remove PII from log statements in inviteMemberUtils
- Remove email (usernameOrEmail) from error log in sendSignupToOrganizationEmail
- Replace full user object with userId in debug log in sendExistingUserTeamInviteEmails
Co-Authored-By: unknown <>
* revert: restore original code for pure relocation (no formatting/logic changes)
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* fix
* fix: update test mocks to use new import path for inviteMemberUtils
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
* revert: restore original formatting in proration test and handleGroupEvents
Co-Authored-By: benny@cal.com <sldisek783@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Add DB table for wrong assignment reports
* When report is submitted write to the db
* Prevent duplicate reportings
* test: add migration and tests for WrongAssignmentReport table
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: add unique constraint on bookingUid and booking access check for hasWrongAssignmentReport
- Add @unique constraint on bookingUid in WrongAssignmentReport model to prevent duplicate reports at DB level
- Add booking ownership check using BookingAccessService in hasWrongAssignmentReport endpoint
- Refactor hasWrongAssignmentReport into separate handler and schema files
Addresses Cubic AI review feedback on PR #27405
Co-Authored-By: unknown <>
* feat: add routingFormId to WrongAssignmentReport and fix Select clearing
- Add routingFormId field to WrongAssignmentReport model in schema.prisma
- Add relation to App_RoutingForms_Form with SetNull on delete
- Update WrongAssignmentReportRepository.createReport to accept routingFormId
- Update BookingRepository.findByUidIncludeEventTypeAndTeamAndAssignmentReason to include routedFromRoutingFormReponse
- Extract routingFormId from booking in reportWrongAssignment handler
- Fix Select clearing issue: handle null case when user clears team member selection
- Update tests to include routingFormId field
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* chore: add migration for routingFormId in WrongAssignmentReport
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: address Udit's review comments
- hasWrongAssignmentReport: throw UNAUTHORIZED error instead of returning false
- reportWrongAssignment: add try-catch for Prisma P2002 unique constraint error
- WrongAssignmentReport: add Team relation to teamId field
- WrongAssignmentReportRepository: use findUnique instead of findFirst
- reportWrongAssignment: use i18n for error messages
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: revert hasWrongAssignmentReport to return false when user lacks access
Per PR checklist, hasWrongAssignmentReport should return { hasReport: false }
when user lacks access to booking, not throw an error. This allows the UI
to gracefully treat 'no access' as 'no report exists'.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* test: update mocks for findUnique and i18n in unit tests
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: throw UNAUTHORIZED in hasWrongAssignmentReport and squash migrations
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
* fix: change User relation onDelete from Cascade to SetNull in WrongAssignmentReport
Address Hariom's review feedback:
- Changed reportedById from Int to Int? (nullable)
- Changed reportedBy relation from onDelete: Cascade to onDelete: SetNull
- Updated migration SQL to reflect these changes
This preserves wrong assignment reports even when the reporting user is deleted,
as the data is still useful for analysis.
Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: hide feature opt-in banner and feedback forms during impersonation
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: keep opt-in banner visible during impersonation, only hide feedback forms
Co-Authored-By: sean@cal.com <Sean@brydon.io>
* fix: allow feedback form during impersonation in development mode
Co-Authored-By: sean@cal.com <Sean@brydon.io>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* feat: add activeFilters validator to DataTableProvider for filter segment validation
- Add validateActiveFilters prop to DataTableProvider to filter out inaccessible values
- Create useActiveFiltersValidator hook in bookings module to validate filter values
- Integrate validator in bookings-view.tsx to validate userId, eventTypeId, teamId filters
- Add comprehensive tests for the filter validation logic
This provides a scalable solution for handling stale filter segment data by validating
filters at the caller level using already-fetched accessible resource IDs.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: add type guard for eventTypeIds to ensure number[] type
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: delay segment filter application until validator is ready
- Change useActiveFiltersValidator to return 'loading' state while data is fetching
- Add pending segment handling in DataTableProvider to delay filter application
- Expose isValidatorPending in context for consumers to disable queries
- Update BookingListContainer to disable bookings query while validator is pending
This prevents both errors from invalid filters and flash from fetching without filters.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: extract useSegmentManagement hook from DataTableProvider
- Extract segment management logic into dedicated useSegmentManagement hook
- Reduces DataTableProvider from ~500 lines to ~330 lines
- Use NuqsSetter type to match nuqs useQueryState setter signatures
- Maintains all existing functionality and type safety
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: export ActiveFiltersValidator types from data-table lib/types
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: import ActiveFiltersValidatorState from lib/types instead of hook
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* Revert "refactor: extract useSegmentManagement hook from DataTableProvider"
This reverts commit dd588ac4cfb1d709c71d1f621994a7e8f3118f33.
* refactor: split DataTableProvider into fine-grained context providers
- Create DataTableStateContext for raw nuqs state management
- Create DataTableSegmentContext for segment management
- Create DataTableFiltersContext for filter manipulation
- DataTableProvider now composes all providers with a bridge component
- Maintains backward compatibility via combined DataTableContext
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* fix: apply segment filters when navigating back to page with existing URL state
When navigating back to a page with a segment already selected in the URL,
the segment was being marked as selected but its filters were not being
applied. This caused non-deterministic behavior where the filter segment
would sometimes not take effect.
The fix ensures that when segmentIdRaw exists in the URL and segments are
fetched, we also call applySegmentFilters() (with validator timing support)
instead of just setting the selectedSegment state.
Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>
* refactor: simplify filter validator to only handle multi-select
These filters are always multi-select, so remove unnecessary
single-select handling code and tests.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
* fix: cancel debounced search term on unmount
Cancel the debounced search term on unmount/dependency change to avoid
queued updates firing after the provider unmounts.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Users with personal emails (e.g. Gmail) could enter the org creation flow
at /settings/organizations/new but only hit the company email error at
checkout, with no way to fix it from the form. After changing their email
in settings, the cached store email was still used.
- Add server-side isCompanyEmail check in page.tsx to redirect early
- Add client-side check in CreateANewOrganizationForm with clear messaging
- Always use current session email for non-admin users (not cached store)
- Add tests for isCompanyEmail utility
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* perf: add composite index on (formId, createdAt) for App_RoutingForms_FormResponse
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: add migration for formId, createdAt composite index
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Update migration.sql
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: migrate IP-based rate limiting from Unkey to Cloudflare
Remove Unkey rate limiting for IP-based endpoints that will now be
handled by Cloudflare Enterprise Advanced Rate Limiting:
- Regular booking creation (createBooking:{hashedIP})
- Recurring booking creation (createRecurringBooking:{hashedIP})
- Instant meeting creation (instant.event-{hashedIP})
- Booking cancellation for unauthenticated users (api:cancel-ip:{hashedIP})
- Forgot password (forgotPassword:{hashedIP})
- Reset password (api:reset-password:{hashedIP})
- Signup (api:signup:{hashedIP})
- API v1 requests ({userId} with auto-lock)
- Global proxy rate limiting (common namespace)
Rate limiting that remains in Unkey (user/entity-based):
- Login (hashedEmail)
- Booking cancellation for authenticated users (api:cancel-user:{userId})
- 2FA setup/enable/disable (api:totp-*:{userId})
- SMS sending (team/org/user based)
- Email verification (various patterns)
- Team member operations (userId based)
- Routing forms (formId:responseHash)
- AI phone calls (userId based)
Also includes Cloudflare configuration proposal document with
recommended rules using JA4 fingerprinting for enhanced protection.
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove cloudflare proposal doc from PR
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* chore: remove cloudflare comments and keep common rate limiting type
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: revert global middleware from PR #25080 and restore core rate limits
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: restore instantMeeting rate limiting
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: restore email fallback in forgot-password rate limiting
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Remove the static file check
* refactor: add POST_METHODS_ALLOWED_API_ROUTES to proxy matcher
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore API v1 rate limiting to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore reset-password, cancel, book/event, book/recurring-event to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* refactor: use POST_METHODS_ALLOWED_API_ROUTES spread in matcher instead of hardcoded routes
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* revert: restore signup route to original state
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* test: update proxy matcher tests for POST_METHODS_ALLOWED_API_ROUTES spread
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* Rename var
* refactor: revert matcher to static strings, add sync-check test for POST_METHODS_ALLOWED_API_ROUTES
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* removed dead routing forms rewrite code
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: remove IS_PRODUCTION gate from BookingAuditProducer
Remove the IS_PRODUCTION check that was preventing booking audits from
being queued in production. Audits are still properly gated by:
1. Organization check: Audits are skipped for non-organization bookings
(organizationId === null)
2. Feature flag: The BookingAuditTaskConsumer checks if the 'booking-audit'
feature is enabled for the organization via featuresRepository.checkIfTeamHasFeature()
The IS_PRODUCTION gate was intentionally added to prevent logs from being
created in production while the action data versioning was being actively
reviewed and finalized. Without proper versioning handling, the Booking
History UI could crash when encountering unversioned data. Now that the
versioning system is in place, this gate can be safely removed.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert formatting, keep only IS_PRODUCTION removal
Reverts the unintended formatting changes from the previous commit.
Only removes the IS_PRODUCTION gate without changing indentation.
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: add booking-audit feature flag check in producer to avoid unnecessary task creation
- Query booking-audit and booking-email-sms-tasker flags in parallel before fireBookingEvents
- Pass isBookingAuditEnabled through BookingEventHandler to producer's queueTask method
- Add conditional check in queueTask with debug log when skipping audit
- Reuse pre-queried isBookingEmailSmsTaskerEnabled flag instead of querying again
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make isBookingAuditEnabled required and pass it through all booking audit flows
- Make isBookingAuditEnabled a required property in BookingAuditProducerService interface
- Update all BookingEventHandler methods to require isBookingAuditEnabled
- Add feature flag check in all flows that call booking audit:
- handleSeats (seat booking/rescheduling)
- RecurringBookingService (bulk bookings)
- handleCancelBooking (booking cancellation)
- handleConfirmation (booking acceptance)
- roundRobinReassignment (automatic reassignment)
- roundRobinManualReassignment (manual reassignment)
- trpc handlers: addGuests, confirm, editLocation, requestReschedule
- Skip queueing audit tasks when feature is disabled with debug logging
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add featuresRepository dependency to RecurringBookingService DI module
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: make isBookingAuditEnabled optional for non-main flows
Keep feature flag check only in main flows (handleSeats, RegularBookingService, RecurringBookingService) which are frequently triggered. For other flows (handleCancelBooking, handleConfirmation, roundRobinReassignment, etc.), rely on the existing consumer-level check.
Changes:
- Revert feature flag check from non-main flows
- Make isBookingAuditEnabled optional in interface for non-main flow methods
- Keep isBookingAuditEnabled required for main flow methods (queueCreatedAudit, queueRescheduledAudit, queueSeatBookedAudit, queueSeatRescheduledAudit, queueBulkCreatedAudit, queueBulkRescheduledAudit)
- Update BookingEventHandlerService to use required params for main flows and optional for non-main flows
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* refactor: remove isBookingAuditEnabled from non-main flow methods
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* feat: make isBookingAuditEnabled required in all BookingEventHandlerService methods
- Add isBookingAuditEnabled as required parameter in all BookingAuditProducerService interface methods
- Update BookingAuditTaskerProducerService to use simplified check (!params.isBookingAuditEnabled)
- Update BookingEventHandlerService to require isBookingAuditEnabled in all methods
- Update all callers to query booking-audit feature flag and pass isBookingAuditEnabled:
- handleCancelBooking
- handleConfirmation
- roundRobinReassignment
- roundRobinManualReassignment
- addGuests.handler
- confirm.handler
- editLocation.handler
- requestReschedule.handler
- Inject featuresRepository in API V2's booking-location.service.ts
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: update roundRobinReassignment tests to include isBookingAuditEnabled
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* test: update roundRobinManualReassignment tests to include isBookingAuditEnabled
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: inject featuresRepository in API V2 RecurringBookingService
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: import PrismaWorkerModule for PrismaFeaturesRepository dependency
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use booking's organization for feature flag check in addGuests handler
Use booking.user?.profiles?.[0]?.organizationId instead of user.organizationId
to check the booking-audit feature flag. This ensures the feature flag is
checked against the booking's organization rather than the actor's organization,
which is consistent with other handlers in this PR.
Addresses Cubic AI review feedback (confidence 9/10).
Co-Authored-By: unknown <>
* Add comment
* fix: use user.organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: use booking's organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: revert to user.organizationId for feature flag check in addGuests handler
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add isBookingAuditEnabled to onNoShowUpdated calls after main merge
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
* fix: add isBookingAuditEnabled to onNoShowUpdated calls and update tests
Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>
---------
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2026-02-09 09:25:33 -03:00
5330 changed files with 245946 additions and 650906 deletions
# - The codebase has to stay open source, whether it was modified or not
# - You can not repackage or sell the codebase
# - Acquire a commercial license to remove these terms by visiting: cal.com/sales
#
# To enable enterprise-only features please add your environment variable to the .env file then make your way to /auth/setup to select your license and follow instructions.
CALCOM_LICENSE_KEY=
# Signature token for the Cal.com License API (used for self-hosted integrations)
# We will give you a token when we provide you with a license key this ensure you and only you can communicate with the Cal.com License API for your license key
- 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)
- Follow Best Practices lined out in our [Contributor Docs](https://github.com/calcom/cal.diy/blob/main/CONTRIBUTING.md)
<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change. -->
<!-- Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
Note: Cal.diy is a community-maintained open-source project. Contributions here do NOT flow to Cal.com's production service. -->
- Fixes #XXXX (GitHub issue number)
- Fixes CAL-XXXX (Linear issue number - should be visible at the bottom of the GitHub issue description)
## Visual Demo (For contributors especially)
@@ -22,7 +23,7 @@ A visual demonstration is strongly recommended, for both the original and new ch
## Mandatory Tasks (DO NOT REMOVE)
- [ ] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [ ] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). If N/A, write N/A here and check the checkbox.
- [ ] I have updated the developer docs if this PR makes changes that would require a documentation change. If N/A, write N/A here and check the checkbox.
- [ ] I confirm automated tests are in place that prove my fix is effective or that my feature works.
## How should this be tested?
@@ -38,7 +39,7 @@ A visual demonstration is strongly recommended, for both the original and new ch
<!-- Remove bullet points below that don't apply to you -->
- I haven't read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- I haven't read the [contributing guide](https://github.com/calcom/cal.diy/blob/main/CONTRIBUTING.md)
- My code doesn't follow the style guidelines of this project
- I haven't commented my code, particularly in hard-to-understand areas
- I haven't checked if my changes generate no new warnings
MESSAGE="New Cubic AI review feedback has been submitted on PR #${{ steps.extract-prompt.outputs.pr-number }}.
IMPORTANT: Before fixing any issue, check the individual Cubic comment on the PR for that issue's confidence score. The confidence score appears as 'Fix confidence (alpha): X/10' in each comment. Note that the (alpha) may or not be there. Don't be picky about that text. But only fix issues where the confidence score is 9/10 or higher. If you cannot find a confidence score for an issue, skip it.
Here are the issues identified by Cubic AI:
${CUBIC_PROMPT}
Continue working on the same PR branch and push your fixes."
HTTP_CODE=$(curl -s -o /tmp/devin-response.json -w "%{http_code}" -X POST "https://api.devin.ai/v1/sessions/${EXISTING_SESSION_ID}/message" \
FULL_PROMPT="You are addressing code review feedback from Cubic AI on PR #${{ steps.extract-prompt.outputs.pr-number }} in repository ${{ github.repository }}.
Your tasks:
1. Clone the repository ${{ github.repository }} locally.
2. Check out the PR branch and review the current code.
3. Read the Cubic AI review feedback below AND check each individual Cubic comment on the PR for the confidence score.
4. IMPORTANT: Only fix issues where the confidence score is 9/10 or higher. The confidence score appears as 'Fix confidence (alpha): X/10' in each Cubic comment. If you cannot find a confidence score for an issue, skip it.
5. Commit your changes with clear commit messages referencing the issues fixed.
6. Push your changes to the PR branch.
Cubic AI Review Feedback:
${CUBIC_PROMPT}
Rules and Guidelines:
1. Make minimal, focused changes that directly address the feedback.
2. Follow the existing code style and conventions in the repository.
3. Test your changes if possible before pushing.
4. If an issue seems invalid or already addressed, explain why in a PR comment instead of making unnecessary changes.
5. Never ask for user confirmation. Never wait for user messages."
HTTP_CODE=$(curl -s -o /tmp/devin-response.json -w "%{http_code}" -X POST "https://api.devin.ai/v1/sessions" \
-H "Authorization: Bearer ${DEVIN_API_KEY}" \
-H "Content-Type: application/json" \
-d "$(jq -n \
--arg prompt "$FULL_PROMPT" \
--arg title "Cubic AI Review: PR #${{ steps.extract-prompt.outputs.pr-number }}" \
? `### Devin AI is addressing Cubic AI's review feedback\n\nA Devin session has been created to address the issues identified by Cubic AI.\n\n[View Devin Session](${sessionUrl})`
: `### Devin AI is addressing Cubic AI's review feedback\n\nNew feedback has been sent to the existing Devin session.\n\n[View Devin Session](${sessionUrl})`;
// Use the PR number from the extract-prompt step output instead of reading the file
MESSAGE="IMPORTANT: When you have finished addressing Cubic AI's feedback (whether you pushed changes or not), you MUST update the GitHub comment to show completion status.
Use the GitHub API to update comment ID ${COMMENT_ID} on PR #${{ steps.extract-prompt.outputs.pr-number }} in repository ${{ github.repository }}.
Add a completion status section at the bottom of the comment:
- If you pushed changes: Add '\n\n---\n:white_check_mark: Pushed commit [\`{SHORT_HASH}\`](https://github.com/${{ github.repository }}/commit/{FULL_HASH})' where {SHORT_HASH} is the first 7 characters of the commit hash and {FULL_HASH} is the full commit hash.
- If no changes were needed: Add '\n\n---\n:white_check_mark: No changes pushed'
To update the comment, use: gh api repos/${{ github.repository }}/issues/comments/${COMMENT_ID} -X PATCH -f body='<updated comment body>'"
HTTP_CODE=$(curl -s -o /tmp/devin-instructions-response.json -w "%{http_code}" -X POST "https://api.devin.ai/v1/sessions/${SESSION_ID}/message" \
console.log(`Requesting maintainer access for PR #${prNumber}`);
const commentBody = [
'### Maintainer Access Needed',
'',
`Hi @${forkAuthor}! Thanks for your contribution to Cal.com.`,
'',
'We noticed that this PR doesn\'t have "Allow edits from maintainers" enabled. We\'d love to help keep your PR up to date by resolving merge conflicts and making small fixes when needed.',
'',
'**Could you please enable this setting?** Here\'s how:',
'1. Scroll down to the bottom of this PR page',
'2. In the right sidebar, check the box that says **"Allow edits and access to secrets by maintainers"**',
'',
'This allows us to push commits directly to your PR branch, which helps us:',
'- Resolve merge conflicts automatically',
'- Make small adjustments to help get your PR merged faster',
'',
'Once you enable this setting, please remove and re-add the `devin-conflict-resolution` label to trigger conflict resolution.',
'',
'If you have any concerns about enabling this setting, feel free to let us know!'
].join('\n');
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: commentBody
});
// Add label to track that we've requested access
await github.rest.issues.addLabels({
owner,
repo,
issue_number: prNumber,
labels: ['maintainer-access-requested']
});
// Remove the devin-conflict-resolution label since we can't proceed
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: prNumber,
name: 'devin-conflict-resolution'
});
console.log(`Posted comment and updated labels for PR #${prNumber}`);
core.setFailed('Cannot proceed without maintainer access on fork PR');
console.log(`PR #${prNumber} does not have conflicts (status: ${mergeableStatus})`);
let message;
if (mergeableStatus === 'MERGEABLE') {
message = `### No Conflicts Detected\n\nThis PR does not have any merge conflicts with the base branch. The \`devin-conflict-resolution\` label has been removed.\n\nIf you believe this PR should have conflicts, please check the PR status and try again.`;
} else {
message = `### Mergeable Status Unknown\n\nGitHub is still computing the mergeable status for this PR (current status: \`${mergeableStatus}\`). The \`devin-conflict-resolution\` label has been removed.\n\nPlease wait a moment and re-add the label if the PR has conflicts.`;
}
await github.rest.issues.createComment({
owner,
repo,
issue_number: prNumber,
body: message
});
try {
await github.rest.issues.removeLabel({
owner,
repo,
issue_number: prNumber,
name: 'devin-conflict-resolution'
});
} catch (e) {
console.log(`Label may have already been removed: ${e.message}`);
}
console.log(`Posted comment and removed label for PR #${prNumber}`);
- Clone the FORK repository: ${pr.head_repo_owner}/${pr.head_repo_name}
- The branch to work on is: ${pr.head_ref}
- Add the upstream remote for ${owner}/${repo}
- Fetch and merge the upstream ${pr.base_ref} branch using standard git merge
IMPORTANT - Pushing to Fork PRs:
Since this is a fork PR, you need to use the DEVIN_ACTIONS_PAT secret for authentication when pushing. Configure git to use this PAT for authentication before pushing your changes to the fork.` : `
- Clone the repository: ${owner}/${repo}
- Check out the PR branch: ${pr.head_ref}
- Merge the base branch (${pr.base_ref}) using standard git merge`;
const conflictResolutionInstructions = `You are resolving merge conflicts on PR #${pr.number} in repository ${owner}/${repo}.
IMPORTANT WARNING: If your merge commit shows significantly more files than the original PR, DO NOT PUSH. This indicates the merge was done incorrectly. Abort and leave a comment instead.
Your tasks:
${forkInstructions}
Then:
1. Resolve all merge conflicts carefully:
- Review the conflicting changes from both branches
- Make intelligent decisions about how to combine the changes
- Preserve the intent of both the PR changes and the base branch updates
- If unsure about a conflict, prefer keeping both changes where possible
2. Test that the code still works after resolving conflicts (run lint/type checks).
3. Commit the merge resolution with a clear commit message.
4. CRITICAL VALIDATION BEFORE PUSHING - YOU MUST RUN THESE EXACT COMMANDS:
Step A: Get original PR file count and count files in your merge commit:
Step B: If MERGE_FILES > THRESHOLD, STOP IMMEDIATELY. DO NOT PUSH.
- A proper merge commit should show approximately the same files as the original PR
- If you see significantly more files, the merge was done INCORRECTLY
- This usually means you accidentally reproduced changes from ${pr.base_ref} instead of just resolving conflicts
Step C: Compare your merge to the original PR:
\`\`\`
git diff --stat HEAD^2...HEAD
\`\`\`
- This should show approximately the same files as the original PR (the PR's contribution to the base branch)
- If you see many more files or files unrelated to the original PR, STOP
Step D: If validation fails:
- DO NOT PUSH under any circumstances
- DO NOT attempt to fix it
- Leave a comment on the PR explaining that the merge validation failed
- ABORT THE TASK IMMEDIATELY
- It is better to leave the PR with conflicts than to push a broken merge commit that pollutes git history
5. Push the resolved changes to the PR branch only after validation passes.
6. After successfully pushing the resolved changes, remove the \`devin-conflict-resolution\` label from the PR using the GitHub API.
Rules and Guidelines:
1. Be careful when resolving conflicts - understand the context of both changes.
To help with this, try to find the recent PRs associated with the conflicting files to gain more context.
2. Follow the existing code style and conventions in the repository.
3. Run lint and type checks before pushing to ensure the code is valid.
4. If a conflict seems too complex or risky to resolve automatically, explain the situation in a PR comment instead.
5. Never ask for user confirmation. Never wait for user messages.
6. CRITICAL: If this is a fork PR and you encounter ANY error when pushing (permission denied, authentication failure, etc.) even after using the DEVIN_ACTIONS_PAT, you MUST fail the task immediately. Do NOT attempt to push to a new branch in the main ${owner}/${repo} repository as a workaround. Simply report the error and stop.
7. CRITICAL: Never reproduce or recreate changes from the target branch. Your merge commit should ONLY contain conflict resolutions. If you find yourself manually copying file contents from ${pr.base_ref} or creating changes that mirror what's already in ${pr.base_ref}, you are doing it wrong. Use git's merge functionality properly - it handles bringing in changes automatically.
8. CRITICAL: If your merge commit shows significantly more files than the original PR (more than original PR files + 10), DO NOT PUSH UNDER ANY CIRCUMSTANCES. This is a sign that the merge was done incorrectly. Abort the task and leave a comment explaining the issue. A bad merge commit in the git history is worse than leaving the PR with conflicts.`;
try {
let sessionUrl;
let isNewSession = false;
if (hasExistingSession) {
console.log(`Sending message to existing session ${existingSessionId} for PR #${pr.number}`);
const message = `PR #${pr.number} has merge conflicts that need to be resolved.
${conflictResolutionInstructions}
Continue working on the same PR branch and push your fixes.`;
`Welcome to **Cal.diy**, @${pr.user.login}! Thanks for opening this pull request.`,
"",
"A few things to keep in mind:",
"",
"- **This is Cal.diy, not Cal.com.** Cal.diy is a community-driven, fully open-source fork of Cal.com licensed under MIT. Your changes here will be part of Cal.diy — they will **not** be deployed to the Cal.com production app.",
"- Please review our [Contributing Guidelines](https://github.com/calcom/cal.diy/blob/main/CONTRIBUTING.md) if you haven't already.",
"- Make sure your PR title follows the [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/) format.",
"",
"A maintainer will review your PR soon. Thanks for contributing!",
`Hi @${prAuthor}! Thanks for your contribution to Cal.com.`,
'',
`We'd like to help complete this stale PR, but we noticed that "Allow edits from maintainers" isn't enabled. We need this setting to push updates to your PR branch.`,
'',
'**Could you please enable this setting?** Here\'s how:',
'1. Scroll down to the bottom of this PR page',
'2. In the right sidebar, check the box that says **"Allow edits and access to secrets by maintainers"**',
'',
'This allows us to push commits directly to your PR branch, which helps us:',
'- Complete any remaining work on your PR',
'- Fix merge conflicts and make small adjustments',
'- Get your contribution merged faster',
'',
'Once you\'ve enabled this setting, we\'ll be able to help finish up this PR. If you have any concerns about enabling this setting, feel free to let us know!'
].join('\n');
await github.rest.issues.createComment({
owner,
repo,
issue_number:prNumber,
body:commentBody
});
console.log(`Posted maintainer access request comment on PR#${prNumber}`);
}else {
console.log(`Maintainer access already requested on PR#${prNumber}`);
}
core.setFailed(`Cannot complete this fork PR:the fork owner has not enabled "Allow edits from maintainers". A comment has been posted requesting access.`);
CLONE_INSTRUCTIONS="This is a fork PR. Clone from the fork repository: ${HEAD_REPO_FULL_NAME}
To check out this PR:
1. Clone the fork:git clone https://github.com/${HEAD_REPO_FULL_NAME}.git
2. Check out the branch:git checkout ${PR_BRANCH}
3. Add the upstream remote:git remote add upstream https://github.com/${REPO_NAME}.git
IMPORTANT - Pushing to Fork PRs:
Since this is a fork PR, you need to use the DEVIN_ACTIONS_PAT secret for authentication when pushing. Configure git to use this PAT for authentication before pushing your changes to the fork."
else
CLONE_INSTRUCTIONS="Clone the repository ${REPO_NAME} and check out the branch: ${PR_BRANCH}"
fi
FULL_PROMPT="You are completing a stale community PR#${PR_NUMBER} in repository ${REPO_NAME}.
This PR was started by @${PR_AUTHOR} but has become stale. Your job is to complete it.
PR Title:${PR_TITLE}
PR Branch:${PR_BRANCH}
${CLONE_INSTRUCTIONS}
Your tasks:
1. Clone the repository as described above.
2. Check out the PR branch:${PR_BRANCH}
3. Review the current state of the PR and understand what it's trying to accomplish.
4. Read the PR description and any comments/review feedback on the PR.
5. Complete any unfinished work on the PR:
- Fix any issues mentioned in review comments
- Ensure the code follows the repository's coding standards
- Add any missing tests if applicable
- Fix any linting or type errors
6. Run the appropriate checks (lint, type-check, tests) to ensure the PR is ready for review.
7. Commit your changes with clear commit messages.
8. Push your changes to the PR branch.
9. After successfully pushing, remove the 'Stale' or 'devin-finish-pr' label from the PR (whichever was used to trigger this workflow).
10. Post a summary comment on the PR explaining what you completed.
11. Mark the PR as ready for review if applicable.
Rules and Guidelines:
1. Respect the original author's intent and approach - don't rewrite the PR from scratch.
2. Make minimal, focused changes that complete the PR's original goal.
3. Follow the existing code style and conventions in the repository.
4. If the PR's original approach seems fundamentally flawed, explain why in a comment instead of making major changes.
5. Never ask for user confirmation. Never wait for user messages.
6. Credit the original author in your commit messages where appropriate.
7. CRITICAL:If this is a fork PR and you encounter ANY error when pushing (permission denied, authentication failure, etc.) even after using the DEVIN_ACTIONS_PAT, you MUST fail the task immediately. Do NOT attempt to push to a new branch in the main repository as a workaround. Simply report the error and stop."
if [ "$HAS_EXISTING_SESSION" = "true" ]; then
echo "Sending message to existing session ${EXISTING_SESSION_ID}"
MESSAGE="This PR has been marked as stale and needs to be completed.
${FULL_PROMPT}
Continue working on the same PR branch and push your fixes."
HTTP_CODE=$(curl -s -o /tmp/devin-response.json -w "%{http_code}" -X POST "https://api.devin.ai/v1/sessions/${EXISTING_SESSION_ID}/message" \
?`### Devin AI is completing this stale PR\n\nThis PR by @${prAuthor} has been marked as stale. A Devin session has been created to complete the remaining work.\n\n[View Devin Session](${sessionUrl})\n\n---\n*Devin will review the PR, address any feedback, and push updates to complete this PR.*`
:`### Devin AI is completing this stale PR\n\nThis PR by @${prAuthor} has been marked as stale. The existing Devin session has been notified to complete the remaining work.\n\n[View Devin Session](${sessionUrl})\n\n---\n*Devin will review the PR, address any feedback, and push updates to complete this PR.*`;
> **Important:** Cal.diy is a community-driven, open-source fork of Cal.com. Contributions made here **do not** get merged into Cal.com's production service — Cal.com is now closed-source. This repo is maintained independently by the community under the MIT license.
Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**.
@@ -6,7 +8,7 @@ Contributions are what make the open source community such an amazing place to l
### 👥 Prevent Work Duplication
Before submitting a new issue or PR, check if it already exists in the [Issues](https://github.com/calcom/cal.com/issues) or [Pull Requests](https://github.com/calcom/cal.com/pulls).
Before submitting a new issue or PR, check if it already exists in the [Issues](https://github.com/calcom/cal.diy/issues) or [Pull Requests](https://github.com/calcom/cal.diy/pulls).
### ✅ Work Only on Approved Issues
@@ -64,7 +66,7 @@ Write with the future in mind. If there are trade-offs, edge cases, or temporary
Portions of this software are licensed as follows:
* All content that resides under https://github.com/calcom/cal.com/tree/main/packages/features/ee and
https://github.com/calcom/cal.com/tree/main/apps/api/v2/src/ee directory of this repository (Commercial License) is licensed under the license defined in "ee/LICENSE".
* All third party components incorporated into the Cal.com Software are licensed under the original license provided by the owner of the applicable component.
* Content outside of the above mentioned directories or restrictions above is available under the "AGPLv3" license as defined below.
GNU AFFERO GENERAL PUBLIC LICENSE
Version 3, 19 November 2007
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
Everyone is permitted to copy and distribute verbatim copies
of this license document, but changing it is not allowed.
Preamble
The GNU Affero General Public License is a free, copyleft license for
software and other kinds of works, specifically designed to ensure
cooperation with the community in the case of network server software.
The licenses for most software and other practical works are designed
to take away your freedom to share and change the works. By contrast,
our General Public Licenses are intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.
When we speak of free software, we are referring to freedom, not
price. Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.
Developers that use our General Public Licenses protect your rights
with two steps: (1) assert copyright on the software, and (2) offer
you this License which gives you legal permission to copy, distribute
and/or modify the software.
A secondary benefit of defending all users' freedom is that
improvements made in alternate versions of the program, if they
receive widespread use, become available for other developers to
incorporate. Many developers of free software are heartened and
encouraged by the resulting cooperation. However, in the case of
software used on network servers, this result may fail to come about.
The GNU General Public License permits making a modified version and
letting the public access it on a server without ever releasing its
source code to the public.
The GNU Affero General Public License is designed specifically to
ensure that, in such cases, the modified source code becomes available
to the community. It requires the operator of a network server to
provide the source code of the modified version running there to the
users of that server. Therefore, public use of a modified version, on
a publicly accessible server, gives the public access to the source
code of the modified version.
An older license, called the Affero General Public License and
published by Affero, was designed to accomplish similar goals. This is
a different license, not a version of the Affero GPL, but Affero has
released a new version of the Affero GPL which permits relicensing under
this license.
The precise terms and conditions for copying, distribution and
modification follow.
TERMS AND CONDITIONS
0. Definitions.
"This License" refers to version 3 of the GNU Affero General Public License.
"Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.
"The Program" refers to any copyrightable work licensed under this
License. Each licensee is addressed as "you". "Licensees" and
"recipients" may be individuals or organizations.
To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy. The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.
A "covered work" means either the unmodified Program or a work based
on the Program.
To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy. Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.
To "convey" a work means any kind of propagation that enables other
parties to make or receive copies. Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.
An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License. If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.
1. Source Code.
The "source code" for a work means the preferred form of the work
for making modifications to it. "Object code" means any non-source
form of a work.
A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.
The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form. A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.
The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities. However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work. For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.
The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.
The Corresponding Source for a work in source code form is that
same work.
2. Basic Permissions.
All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met. This License explicitly affirms your unlimited
permission to run the unmodified Program. The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work. This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.
You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force. You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright. Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.
Conveying under any other circumstances is permitted solely under
the conditions stated below. Sublicensing is not allowed; section 10
makes it unnecessary.
3. Protecting Users' Legal Rights From Anti-Circumvention Law.
No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.
When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.
4. Conveying Verbatim Copies.
You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.
You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.
5. Conveying Modified Source Versions.
You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:
a) The work must carry prominent notices stating that you modified
it, and giving a relevant date.
b) The work must carry prominent notices stating that it is
released under this License and any conditions added under section
7. This requirement modifies the requirement in section 4 to
"keep intact all notices".
c) You must license the entire work, as a whole, under this
License to anyone who comes into possession of a copy. This
License will therefore apply, along with any applicable section 7
additional terms, to the whole of the work, and all its parts,
regardless of how they are packaged. This License gives no
permission to license the work in any other way, but it does not
invalidate such permission if you have separately received it.
d) If the work has interactive user interfaces, each must display
Appropriate Legal Notices; however, if the Program has interactive
interfaces that do not display Appropriate Legal Notices, your
work need not make them do so.
A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit. Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.
6. Conveying Non-Source Forms.
You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:
a) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by the
Corresponding Source fixed on a durable physical medium
customarily used for software interchange.
b) Convey the object code in, or embodied in, a physical product
(including a physical distribution medium), accompanied by a
written offer, valid for at least three years and valid for as
long as you offer spare parts or customer support for that product
model, to give anyone who possesses the object code either (1) a
copy of the Corresponding Source for all the software in the
product that is covered by this License, on a durable physical
medium customarily used for software interchange, for a price no
more than your reasonable cost of physically performing this
conveying of source, or (2) access to copy the
Corresponding Source from a network server at no charge.
c) Convey individual copies of the object code with a copy of the
written offer to provide the Corresponding Source. This
alternative is allowed only occasionally and noncommercially, and
only if you received the object code with such an offer, in accord
with subsection 6b.
d) Convey the object code by offering access from a designated
place (gratis or for a charge), and offer equivalent access to the
Corresponding Source in the same way through the same place at no
further charge. You need not require recipients to copy the
Corresponding Source along with the object code. If the place to
copy the object code is a network server, the Corresponding Source
may be on a different server (operated by you or a third party)
that supports equivalent copying facilities, provided you maintain
clear directions next to the object code saying where to find the
Corresponding Source. Regardless of what server hosts the
Corresponding Source, you remain obligated to ensure that it is
available for as long as needed to satisfy these requirements.
e) Convey the object code using peer-to-peer transmission, provided
you inform other peers where the object code and Corresponding
Source of the work are being offered to the general public at no
charge under subsection 6d.
A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.
A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling. In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage. For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product. A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.
"Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source. The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.
If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information. But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).
The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed. Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.
Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.
7. Additional Terms.
"Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law. If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.
When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it. (Additional permissions may be written to require their own
removal in certain cases when you modify the work.) You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.
Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:
a) Disclaiming warranty or limiting liability differently from the
terms of sections 15 and 16 of this License; or
b) Requiring preservation of specified reasonable legal notices or
author attributions in that material or in the Appropriate Legal
Notices displayed by works containing it; or
c) Prohibiting misrepresentation of the origin of that material, or
requiring that modified versions of such material be marked in
reasonable ways as different from the original version; or
d) Limiting the use for publicity purposes of names of licensors or
authors of the material; or
e) Declining to grant rights under trademark law for use of some
trade names, trademarks, or service marks; or
f) Requiring indemnification of licensors and authors of that
material by anyone who conveys the material (or modified versions of
it) with contractual assumptions of liability to the recipient, for
any liability that these contractual assumptions directly impose on
those licensors and authors.
All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10. If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term. If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.
If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.
Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.
8. Termination.
You may not propagate or modify a covered work except as expressly
provided under this License. Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).
However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.
Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.
Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License. If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.
9. Acceptance Not Required for Having Copies.
You are not required to accept this License in order to receive or
run a copy of the Program. Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance. However,
nothing other than this License grants you permission to propagate or
modify any covered work. These actions infringe copyright if you do
not accept this License. Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.
10. Automatic Licensing of Downstream Recipients.
Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License. You are not responsible
for enforcing compliance by third parties with this License.
An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations. If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.
You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License. For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.
11. Patents.
A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based. The
work thus licensed is called the contributor's "contributor version".
A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version. For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.
Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.
In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement). To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.
If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients. "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.
If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.
A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License. You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.
Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.
12. No Surrender of Others' Freedom.
If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License. If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all. For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.
13. Remote Network Interaction; Use with the GNU General Public License.
Notwithstanding any other provision of this License, if you modify the
Program, your modified version must prominently offer all users
interacting with it remotely through a computer network (if your version
supports such interaction) an opportunity to receive the Corresponding
Source of your version by providing access to the Corresponding Source
from a network server at no charge, through some standard or customary
means of facilitating copying of software. This Corresponding Source
shall include the Corresponding Source for any work covered by version 3
of the GNU General Public License that is incorporated pursuant to the
following paragraph.
Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU General Public License into a single
combined work, and to convey the resulting work. The terms of this
License will continue to apply to the part which is the covered work,
but the work with which it is combined will remain governed by version
3 of the GNU General Public License.
14. Revised Versions of this License.
The Free Software Foundation may publish revised and/or new versions of
the GNU Affero General Public License from time to time. Such new versions
will be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.
Each version is given a distinguishing version number. If the
Program specifies that a certain numbered version of the GNU Affero General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation. If the Program does not specify a version number of the
GNU Affero General Public License, you may choose any version ever published
by the Free Software Foundation.
If the Program specifies that a proxy can decide which future
versions of the GNU Affero General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.
Later license versions may give you additional or different
permissions. However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.
15. Disclaimer of Warranty.
THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
16. Limitation of Liability.
IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.
17. Interpretation of Sections 15 and 16.
If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.
END OF TERMS AND CONDITIONS
How to Apply These Terms to Your New Programs
If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.
To do so, attach the following notices to the program. It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
<one line to give the program's name and a brief idea of what it does.>
Copyright (C) <year> <name of author>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
Also add information on how to contact you by electronic and paper mail.
If your software can interact with users remotely through a computer
network, you should also make sure that it provides a way for users to
get its source. For example, if your program is a web application, its
interface could display a "Source" link that leads users to an archive
of the code. There are many ways you could offer source, and different
solutions will be better for different programs; see section 13 for the
specific requirements.
You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU AGPL, see
<https://www.gnu.org/licenses/>.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
This document maps existing role-based permission checks to the new PBAC (Permission-Based Access Control) system's permission strings in the format `resource.action`.
# Knowledge Base - Domain & Product-Specific Information
This file contains domain knowledge about the Cal.com product and codebase. For coding guidelines and rules, see [`rules/`](rules/).
This file contains domain knowledge about the Cal.diy product and codebase. For coding guidelines and rules, see [`rules/`](rules/).
## When working with managed event types
@@ -52,9 +52,9 @@ Control logging verbosity by setting `NEXT_PUBLIC_LOGGER_LEVEL` in .env:
- 5: error
- 6: fatal
### Cal.com Event Identification
### Cal.diy Event Identification
Cal.com events in Google Calendar can be identified by checking if the iCalUID ends with `@Cal.com` (e.g., `2GBXSdEixretciJfKVmYN8@Cal.com`). This identifier is used to distinguish Cal.com bookings from other calendar events for data storage and privacy purposes.
Cal.diy events in Google Calendar can be identified by checking if the iCalUID ends with `@Cal.diy` (e.g., `2GBXSdEixretciJfKVmYN8@Cal.diy`). This identifier is used to distinguish Cal.diy bookings from other calendar events for data storage and privacy purposes.
### UI Component Locations
@@ -87,7 +87,7 @@ To make persistent changes to API documentation, use NestJS decorators (`@ApiQue
### Workflows vs Webhooks
Workflows and webhooks are two completely separate features in Cal.com with different implementations and file structures:
Workflows and webhooks are two completely separate features in Cal.diy with different implementations and file structures:
This directory contains modular, machine-readable engineering rules derived from [Cal.com's Engineering Standards for 2026 and Beyond](https://cal.com/blog/engineering-in-2026-and-beyond).
This directory contains modular, machine-readable engineering rules derived from [Cal.diy's Engineering Standards for 2026 and Beyond](https://cal.com/blog/engineering-in-2026-and-beyond).
impactDescription: Enables technology-agnostic business logic
@@ -70,4 +72,4 @@ export async function POST(request: Request) {
**The principle:**
We must detach HTTP technology from our application. The way we transfer data between client and server (whether REST, tRPC, etc.) should not influence how our core application works. HTTP is a delivery mechanism, not an architectural driver.
Circular dependencies between packages cause build failures, type errors, and make the codebase unmaintainable. The dependency graph must be acyclic, with clear layers from low-level utilities up to high-level features.
The dependency hierarchy (from lowest to highest):
```
packages/lib (lowest - no feature dependencies)
↓
packages/app-store
↓
packages/features
↓
packages/trpc
↓
apps/web (highest - can depend on all packages)
```
**Benefits:**
- **Honest implementation**: The dependency graph accurately reflects what code depends on what, with no hidden or circular relationships that create false expectations
- **Predictable behavior**: Code behaves as the dependency structure suggests - no surprises from circular imports
- **Clear mental model**: Developers can reason about the system without tracking complex circular relationships
- **Build reliability**: No circular dependency errors during compilation or bundling
- **Easier testing**: Lower-level packages can be tested independently without pulling in the entire codebase
### `packages/lib`
**Rules:**
1. No files in `packages/lib` import from `@calcom/app-store` or `../app-store/**`
2. No files in `packages/lib` import from `@calcom/features` or `../features/**`
3. No files in `packages/lib` import from `@calcom/trpc` or `../trpc/**`
4. No files in `packages/lib` import from `@trpc/server`
5. **BONUS:** No repository files allowed in `packages/lib` (repositories belong in features or app-store)
**Incorrect:**
```typescript
// Bad - lib importing from features
import { getBooking } from "@calcom/features/bookings";
import { EventRepository } from "@calcom/features/events/repositories";
// Bad - lib importing from app-store
import { googleCalendarHandler } from "@calcom/app-store/googlecalendar";
// Bad - lib importing from trpc
import { router } from "@calcom/trpc/server/trpc";
```
**Correct:**
```typescript
// Good - lib only imports from other lib files or external packages
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
import type { User } from "@prisma/client";
```
### `packages/app-store`
**Rules:**
6. No files in `packages/app-store` import from `@calcom/features` or `../features/**`
7. No files in `packages/app-store` import from `@calcom/trpc` or `../trpc/**`
**Incorrect:**
```typescript
// Bad - app-store importing from features
import { BookingService } from "@calcom/features/bookings/services";
// Bad - app-store importing from trpc
import { publicProcedure } from "@calcom/trpc/server/trpc";
```
**Correct:**
```typescript
// Good - app-store imports from lib and prisma
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
import type { Credential } from "@prisma/client";
```
### `packages/features`
**Rules:**
8. No files in `packages/features` import from `@calcom/trpc` or `../trpc/**`
9. No files in `packages/features` import from `@calcom/web` or `@calcom/web/**`
10. No files in `packages/features` import from `../../apps/web/**`
**Incorrect:**
```typescript
// Bad - features importing from trpc
import { router } from "@calcom/trpc/server/trpc";
// Bad - features importing from web app
import { getServerSession } from "@calcom/web/lib/auth";
import { WebComponent } from "../../apps/web/components/WebComponent";
```
**Correct:**
```typescript
// Good - features import from lib, app-store, and prisma
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
import { googleCalendarHandler } from "@calcom/app-store/googlecalendar";
```
### `packages/trpc`
**Rules:**
11. No files in `packages/trpc` import from `../apps/web/**`
**Incorrect:**
```typescript
// Bad - trpc importing from web app
import { getServerSession } from "../../apps/web/lib/auth";
```
**Correct:**
```typescript
// Good - trpc imports from features, lib, and prisma
import { BookingService } from "@calcom/features/bookings/services";
import { logger } from "@calcom/lib/logger";
import { prisma } from "@calcom/prisma";
```
### `packages/testing`
**Rules:**
12. No files in `packages/testing` import from `@calcom/web` or `@calcom/web/**`
13. No files in `packages/testing` import from `@calcom/features` or `@calcom/features/**`
14. No files in `packages/testing` import from `../../apps/web/**`
15. No files in `packages/testing` import from `../features/**`
**Incorrect:**
```typescript
// Bad - testing importing from web or features
import { WebComponent } from "@calcom/web/components";
import { BookingService } from "@calcom/features/bookings";
```
**Correct:**
```typescript
// Good - testing imports from lib and prisma
import { prisma } from "@calcom/prisma";
import { logger } from "@calcom/lib/logger";
```
### `packages/platform/atoms`
**Rules:**
16. No files in `packages/platform/atoms` import from `@calcom/trpc` or `@calcom/trpc/**`
17. No files in `packages/platform/atoms` import from `../../trpc` or `../../trpc/**`
18. No files in `packages/platform/atoms` import from `@calcom/web`
**Incorrect:**
```typescript
// Bad - platform/atoms importing from trpc or web
import { router } from "@calcom/trpc/server/trpc";
import { trpc } from "../../trpc";
import { WebComponent } from "@calcom/web";
```
**Correct:**
```typescript
// Good - platform/atoms imports from lib, features, and ui
import { logger } from "@calcom/lib/logger";
import { Button } from "@calcom/ui/components/button";
import type { Booking } from "@calcom/features/bookings/types";
@@ -39,4 +39,4 @@ Domain boundaries are enforced automatically through linting. If `packages/featu
- Easier testing: Test the entire feature as a unit with all pieces in one place
- Clearer dependencies: When you see `import { getAvailability } from '@calcom/features/availability'`, you know exactly which feature you're depending on
The Cal.com repository uses generated files (`*.generated.ts`) for app-store integrations. These files are created by the app-store-cli tool.
The Cal.diy repository uses generated files (`*.generated.ts`) for app-store integrations. These files are created by the app-store-cli tool.
**Do not manually modify** `*.generated.ts` files. If you need structural changes to how integrations are imported or used, update the CLI code that generates these files.
We use `@evyweb/ioctopus` to manage service and repository dependencies. The **moduleLoader pattern** provides type-safe dependency injection, ensuring that if a service adds a new dependency, TypeScript will catch missing dependencies at build time rather than runtime.
@@ -277,4 +277,4 @@ export function getMyService(): MyService {
- **Automatic dependency resolution**: The moduleLoader automatically loads all dependencies recursively
- **Self-documenting**: Each module declares its own dependencies explicitly
@@ -55,4 +55,4 @@ async function precomputeTeamAvailability(teamId: number) {
This is why performance isn't just a nice-to-have in scheduling software. It's the foundation that determines whether your system can scale to enterprise needs.
When working with imports in the Cal.com codebase, particularly in app-store integrations, pay attention to whether modules use named exports or default exports.
When working with imports in the Cal.diy codebase, particularly in app-store integrations, pay attention to whether modules use named exports or default exports.
Many services like VideoApiAdapter, CalendarService, and PaymentService are exported as named exports, but the actual export name may differ from the generic service type.
@@ -26,7 +26,7 @@ import CalendarService from "./applecalendar/lib/CalendarService";
## Generated Files
When fixing imports in Cal.com's generated files (like `packages/app-store/apps.browser-*.generated.tsx`), always check the actual exports in the source files first.
When fixing imports in Cal.diy's generated files (like `packages/app-store/apps.browser-*.generated.tsx`), always check the actual exports in the source files first.
For EventTypeAppCardInterface components, they likely use named exports rather than default exports, requiring:
The "slowness" of doing things right is an investment, not a cost. Code that's architected correctly from the start doesn't need massive refactors later.
Simple doesn't mean lacking in features. Just because our goal is to create simple systems, this doesn't mean they should feel anemic and lacking obvious functionality.
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.