* feat: add holidays feature for automatic availability blocking- Add UserHolidaySettings model for storing user preferences- Generate static holiday data for 20 countries using date-holidays- Create HolidayService for runtime holiday queries- Add TRPC router with endpoints for country selection and holiday toggles- Create Holidays tab UI in Availability page with conflict warnings- Integrate holiday blocking into getUserAvailability calculation- Show holiday indicator on blocked dates in booker page- Add warning in OOO modal when dates overlap with holidays * feat: add optimizations, tests, and code quality improvements - Add memoization/caching to HolidayService for better performance - Optimize checkConflicts DB query with OR conditions for specific dates - Add HolidayService unit tests (10 tests) - Add error handling with Alert component for failed queries - Memoize HolidayListItem component to prevent unnecessary re-renders - Extract magic numbers into constants.ts - Use TRPCError consistently in handlers - Add missing i18n keys for error messages - Update handlers to follow Cal.com patterns (default exports, minimal comments) - Add regeneration instructions in constants * refactor: replace static JSON with Google Calendar API integration - Add GoogleHolidayService to fetch holidays from Google Calendar public calendars - Add HolidayCache Prisma model for caching API responses - Add GOOGLE_CALENDAR_API_KEY and HOLIDAY_CACHE_DAYS env variables - Support 38 countries via Google Calendar holiday calendars - Update HolidayService methods to async with database caching - Update all TRPC handlers for async holiday methods - Fix UI to display holiday dates correctly - Remove static holidays.json and generate script * use we instead of calcom in i18n message * address cubics comments * move holidays from availability to ooo * public holidays filter for holidays * follow i18n _one and _other pattern * remove holiday feature flag * revert lint command code change * revert lint command code change 2.0 * revert lint command code change 3.0 * bye bye my christmas emoji :crying-emoji * remove comments * refactor(holidays): add repository pattern, split services, and add tests - Create HolidayRepository for database operations - Split GoogleHolidayService into GoogleCalendarClient and HolidayCacheService - Add dependency injection to HolidayService and HolidayCacheService - Update TRPC handlers to use HolidayRepository - Add tests for HolidayRepository and calculateHolidayBlockedDates - Update calendar IDs to use official holiday format (244 countries + religions) * fix: address PR review feedback - Remove unused date-holidays package - Add pluralization for and_more_holidays_with_conflicts translation - DRY: spread GOOGLE_RELIGIOUS_HOLIDAY_CALENDARS into GOOGLE_HOLIDAY_CALENDARS - Add select to userHolidaySettings query in getUserAvailability - Optimize checkConflicts with pre-computed timestamps * refactor(holidays): apply proxy pattern and move logic to service - Rename HolidayCacheService to HolidayServiceCachingProxy (proxy pattern) - Remove HOLIDAY_CACHE_DAYS env var, use constant directly - Add isSupportedCountry() method to HolidayService - Add getUserSettings() and updateSettings() to HolidayService - Move toggleHoliday logic from handler to service - Move checkConflicts logic from handler to service - Add findBookingsInDateRanges() to HolidayRepository - Simplify all handlers to just call service methods * feat(bookings): add backend validation to prevent booking on holidays Adds explicit holiday conflict validation during booking creation to handle the race condition where a host enables a holiday after a guest selects a date but before they submit the booking. Changes: - Add checkHolidayConflict validation with HolidayRepository integration - Integrate ensureNoHolidayConflict in RegularBookingService - Add BookingOnHoliday error code with proper HTTP 400 response - Handle holiday error display in BookEventForm with name interpolation - Hide trace ID for expected validation errors - Add unit tests for holiday conflict validation * fix failing type check * fix: address PR review comments for holiday feature - Change error code from BAD_REQUEST to INTERNAL_SERVER_ERROR in toggleHoliday handler (errors are internal failures, not bad input) - Refactor ensureNoHolidayConflict to use Promise.all for parallel user checking instead of sequential loop * refactor: use Promise.all with logging in loop for holiday check - Check all users in parallel using Promise.all - Log conflicts inside the loop as they are detected - Wait for all checks to complete before throwing error * fix(holidays): use host timezone for holiday conflict checks The holiday feature was checking booking dates against holidays using server/local timezone instead of the host's timezone. This caused bookings near midnight boundaries to incorrectly pass or fail the holiday check. Example: A booking at Dec 24th 8PM UTC (which is Dec 25th in IST) was not being blocked for an Indian host with Christmas as a holiday. Changes: - Add .utc() to holiday date formatting for consistency - Fetch host's timezone in checkHolidayConflict - Convert booking time to host's timezone before comparison - Add findUserSettingsWithTimezone to HolidayRepository - Update error message to clarify it's the host's local time - Add timezone edge case tests * fix(holidays): align holiday blocking with OOO pattern for consistent timezone handling - Simplify calculateHolidayBlockedDates to match OOO pattern using dayjs.utc() - Fix date range query to use full day bounds (startOfDay/endOfDay) so holidays stored at midnight UTC are correctly found during booking validation - Remove separate checkHolidayConflict booking-time validation - holidays now block through oooExcludedDateRanges like OOO does - Remove getHolidayOnDate from HolidayService (no longer needed) - Remove findUserSettingsWithTimezone from HolidayRepository (no longer needed) - Clean up related tests Holiday blocking now works exactly like OOO: 1. Holidays are added to datesOutOfOffice in calculateHolidayBlockedDates 2. buildDateRanges processes them via processOOO with .tz(timeZone, true) 3. oooExcludedDateRanges excludes those dates from availability 4. ensureAvailableUsers uses oooExcludedDateRanges to block bookings This ensures consistent timezone handling where Dec 25th blocks all hours of Dec 25th in the host's timezone, regardless of booker's timezone. * update test * update .env.example file
Organizations feature
From the Original RFC:
We want to create organisations within Cal.com to enable people to easily and effectively manage multiple teams. An organisation will live above the current teams layer.
App setup
-
Log in as admin and in Settings, turn on Organizations feature flag under Features section. Organizations feature has an operational feature flag in order to turn on the entire feature.
-
Set the following environment variables as described:
CALCOM_LICENSE_KEY: Since Organizations is an EE feature, a license key should be present, either as this environment variable or visiting as an Admin/auth/setup. To get a license key, please contact salesNEXT_PUBLIC_WEBAPP_URL: In case of local development, this variable should be set tohttp://app.cal.local:3000to be able to handle subdomains correctly in terms of authentication and cookiesNEXTAUTH_URL: Should be equal toNEXT_PUBLIC_WEBAPP_URLwhich ishttp://app.cal.local:3000NEXTAUTH_COOKIE_DOMAIN: In case of local development, this variable should be set to.cal.localto be able to accept session cookies in subdomains as well otherwise it should be set to the corresponding environment such as.cal.dev,.cal.qaor.cal.com. If you choose another subdomain, the value for this should match the apex domain ofNEXT_PUBLIC_WEBAPP_URLwith a leading dot (.)ORGANIZATIONS_ENABLED: Should be set to1ortrueSTRIPE_ORG_MONTHLY_PRICE_ID: For dev and all testing should be set to your own testing key. Or ask for the shared key if you're a core member.ORGANIZATIONS_AUTOLINK: Optional. Set to1ortrueto let new signed-up users using Google external provider join the corresponding organization based on the email domain name.
-
Add
app.cal.localto your host file, either:sudo npx hostile set localhost app.cal.local- Add it yourself
-
Add
acme.cal.localto host file given that the org create for it will beacme, otherwise do this for whatever slug will be assigned to the org. This is needed to test org-related public URLs, such as sub-teams, members and event-types. -
Be sure to be logged in with any type of user and visit
/settings/organizations/newand follow setup steps with the slug matching the org slug from step 3 -
Log in as admin and go to Settings and under Organizations you will need to accept the newly created organization in order to be operational
-
After finishing the org creation, you will be automatically logged in as the owner of the organization, and the app will be shown in organization mode
Note
Browsers do not allow camera/mic access on any non-HTTPS hosts except for localhost. To test cal video organization meeting links locally (app.cal.local/video/[uid]). You can access the meeting link by replacing app.cal.local with localhost in the URL.
For eg:- Use http://localhost:3000/video/nAjnkjejuzis99NhN72rGt instead of http://app.cal.local:3000/video/nAjnkjejuzis99NhN72rGt.
To get an HTTPS URL for localhost, you can use a tunneling tool such as ngrok or Tunnelmole . Alternatively, you can generate an SSL certificate for your local domain using mkcert. Turn off any SSL certificate validation in your HTTPS client (be sure to do this for local only, otherwise its a security risk).
Tunnelmole - Open Source Tunnelling Tool:
To install Tunnelmole, execute the command:
curl -O https://install.tunnelmole.com/8dPBw/install && sudo bash install
After a successful installation, you can run Tunnelmole using the following command, replacing 8000 with your actual port number if it is different:
tmole 8000
In the output, you'll see two URLs, one HTTP and an HTTPS URL. For privacy and security reasons, it is recommended to use the HTTPS URL.
View the Tunnelmole README for additional information and other installation methods such as npm or building your own binaries from source.
ngrok - Closed Source Tunnelling Tool:
ngrok is a popular closed source tunneling tool. You can run ngrok using the same port, using the format ngrok http <port> replacing <port> with your actual port number. For example:
ngrok http 8000
This will generate a public URL that you can use to access your localhost server.
DNS setup
When a new organization is created, other than not being verified up until the admin accepts it in settings as explained in step 6, a flag gets created that marks the organization as missing DNS setup. That flag get auto-checked by the system upon organization creation when the Cal instance is deployed in Vercel and the subdomain registration was successful. Logging in as admin and going to Settings > Organizations section, you will see that flag as a badge, designed to give admins a glimpe on what is pending in terms of making an organization work. Alongside the mentioned badge, an email gets sent to admins in order to warn them there is a pending action about setting up DNS for the newly created organization to work. Read more about the Organization Setup here