Skip to content

Conversation

@kussberg
Copy link
Owner

What does this PR do?

  • 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)

A visual demonstration is strongly recommended, for both the original and new change (video / image - any one).

Video Demo (if applicable):

  • Show screen recordings of the issue or feature.
  • Demonstrate how to reproduce the issue, the behavior before and after the change.

Image Demo (if applicable):

  • Add side-by-side screenshots of the original and updated change.
  • Highlight any significant change(s).

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. 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?

  • Are there environment variables that should be set?
  • What are the minimal test data to have?
  • What is expected (happy path) to have (input and output)?
  • Any other important info that could help to test that PR

Checklist

  • I haven't read the contributing guide
  • 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

@vercel
Copy link

vercel bot commented Aug 27, 2025

@emrysal is attempting to deploy a commit to the Kussberg Team on Vercel.

A member of the Team first needs to authorize it.

@github-actions
Copy link

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

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

Details:

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

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

@vercel
Copy link

vercel bot commented Aug 27, 2025

Deployment failed with the following error:

The value for maxDuration must be between 1 second and 300 seconds, in order to increase this limit upgrade your plan: https://vercel.com/pricing

Learn More: https://vercel.com/docs/functions/limitations

@kussberg kussberg changed the title Update feat: update to latest Aug 27, 2025
dhairyashiil and others added 23 commits November 28, 2025 02:26
* fix: Add PBAC permission checks for insights access

- Add checkInsightsPermission() helper that properly checks insights.read permission with PBAC support
- Update userBelongsToTeamProcedure to use PBAC-aware permission check for org-level access
- Update teamListForUser query to filter teams based on insights.read permission instead of only checking base ADMIN/OWNER roles
- Maintain backward compatibility with fallback to traditional role checks (ADMIN/OWNER) when PBAC is disabled
- Org admins (base role ADMIN/OWNER) continue to have automatic insights access as a privileged position
- Team-level admins with custom roles now properly checked for insights.read permission

Fixes issue where users with custom PBAC roles couldn't access insights even if they had insights.read permission.

Related: CAL-XXXX

* perf: Optimize team permission checks to avoid N+1 queries

Replace individual permission checks per team with bulk query using getTeamIdsWithPermission().
This reduces database queries from N (one per team) to a single optimized query.

- Use PermissionCheckService.getTeamIdsWithPermission() for bulk permission checking
- Filter teams based on returned team IDs instead of individual checks
- Maintains same functionality with significantly better performance for users with many teams

* perf: Fetch only teams with insights access instead of filtering after

Move permission check before team query to filter at database level.
Previously fetched all teams then filtered in JavaScript.
Now only fetches teams the user has insights access to.

- Check permissions first using getTeamIdsWithPermission()
- Add teamId filter to membership query (teamId: { in: teamIdsWithAccess })
- Remove JavaScript filter step (done at DB level)
- Reduces data transfer and improves query efficiency
…#25114)

* refactor: consolidate error handlers to use getServerErrorFromUnknown

- Migrate server-only code to use getServerErrorFromUnknown for better error handling
- Add JSDoc documentation to both getErrorFromUnknown and getServerErrorFromUnknown
- Update webhook handlers, payment services, email service, and booking service
- Keep getErrorFromUnknown for client-side and isomorphic code
- Improve error message extraction by using err.cause?.stack instead of err.stack
- Fix ESLint warnings: replace 'any' with 'unknown' types, fix hasOwnProperty usage

Co-Authored-By: benny@cal.com <sldisek783@gmail.com>

* Update packages/app-store/paypal/api/webhook.ts

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>

* refactor

---------

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>
* posthog version upgrade and calai banner tracking

* disable posthog for EU

* bunch more posthog tracking

* Revert yarn.lock changes

* add posthog package yarn changes

* fix: add missing posthog import and fix lint warning in MemberInvitationModal

Co-Authored-By: amit@cal.com <samit91848@gmail.com>

* fix: type check

* cubic fixes

* refactor

* remove ui playground

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* wip

* wip

* feature: Booking Tasker without DI yet

* feature: Booking Tasker with DI

* fix type check 1

* fix type check 2

* fix

* comment booking tasker for now

* fix: DI regularBookingService api v2

* fix: convert trigger.dev SDK imports to dynamic imports to fix unit tests

The unit tests were failing because BookingEmailAndSmsTriggerTasker.ts had static imports of trigger files that depend on @trigger.dev/sdk. This caused Vitest to try to resolve the SDK at module load time, even though it should be optional.

Changed all imports in BookingEmailAndSmsTriggerTasker.ts from static to dynamic (using await import()) so the trigger files are only loaded when the tasker methods are actually called, not at module load time during tests.

This fixes the 'Failed to load url @trigger.dev/sdk' errors that were causing 28+ test failures.

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix unit tests

* keep inline smsAndEmailHandler.send calls

* chore: add team feature flag

* add satisfies ModuleLoader

* fix type check app flags

* move trigger in feature

* fix: add trigger.dev prisma  generator

* fix: email app statuses

* fix: CalEvtBuilder unit test

* chore: improvements, schema, config, retry

* fixup! chore: improvements, schema, config, retry

* chore: cleanup code

* chore: cleanup code

* chore: clean code and give full payload

* remove log

* add booking notifications queue

* add attendee phone number for sms

* bump trigger to 4.1.0

* add missing booking seat data in attendee

* update config

* fix logger regular booking service

* fix: prisma as external deps of trigger

* fix yarn.lock

* revert change to example app booking page

* fix: resolve circular dependencies and improve cold start performance in trigger tasks

- Convert BookingRepository import to type-only in CalendarEventBuilder.ts to eliminate circular dependency risk
- Convert EventNameObjectType, CalendarEvent, and JsonObject imports to type-only in BookingEmailAndSmsTaskService.ts
- Use dynamic imports in all trigger notification tasks (confirm, request, reschedule, rr-reschedule) to reduce cold start time
- Move heavy imports (BookingEmailSmsHandler, BookingRepository, prisma, TriggerDevLogger, BookingEmailAndSmsTaskService) inside run functions
- Eliminates module-level prisma import which violates repo guidelines and adds cold start overhead
- Reduces initial module dependency graph by deferring heavy imports (email templates, workflows, large repositories) until task execution

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: improve cold start performance in reminderScheduler with dynamic imports

- Remove module-level prisma import (violates 'No prisma outside repositories' guideline)
- Use dynamic imports for UserRepository (1,168 lines) - only loaded when needed in EMAIL_ATTENDEE action
- Use dynamic imports for twilio provider (386 lines) - only loaded in cancelScheduledMessagesAndScheduleEmails
- Use dynamic imports for all manager functions by action type:
  - scheduleSMSReminder (387 lines) - loaded only for SMS actions
  - scheduleEmailReminder (459 lines) - loaded only for Email actions
  - scheduleWhatsappReminder (266 lines) - loaded only for WhatsApp actions
  - scheduleAIPhoneCall (478 lines) - loaded only for AI phone call actions
- Use dynamic imports for sendOrScheduleWorkflowEmails in cancelScheduledMessagesAndScheduleEmails
- Significantly reduces cold start time by deferring heavy module loading until execution paths need them
- Eliminates module-level prisma import that violated repository pattern guidelines

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: improve cold start performance in BookingEmailSmsHandler with dynamic imports

- Remove module-level imports of all email-manager functions (653 LOC + 30+ email templates)
- Add dynamic imports in each method (_handleRescheduled, _handleRoundRobinRescheduled, _handleConfirmed, _handleRequested, handleAddGuests)
- Defer heavy email-manager loading until method execution
- Verified no circular dependencies between email-manager and bookings
- Significantly reduces cold start time for RegularBookingService and BookingEmailAndSmsTaskService

Co-Authored-By: morgan@cal.com <morgan@cal.com>

* fix: use dynamic imports

* update yarn lock

* code review

* trigger config project ref in env

* update yarn lock

* add .env.example trigger variables

* add .env.example trigger variables

* fix: cleanup error handling and loggin

* fix: trigger config from env

* fix: small typo fix

* fix: ai review comments

* fix: ai review comments

* ai review

* prettier

---------

Co-authored-by: hbjORbj <sldisek783@gmail.com>
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: google connect broken for iframe
* chore: add changesets
* chore: update atoms exports
* chore: update atoms exports
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
* fix: stabilize date range calculation in column view

* fix: restore eslint-disable for scrollIntoViewSmooth

---------

Co-authored-by: Pallav <90088723+Pallava-Joshi@users.noreply.github.com>
* remove min seat usage

* update constants and also turbo json

* Remove comments

* remove redundant tests as no min exists
…#25414)

## What does this PR do?

Fixes a bug in the seed-insights script where `AWAITING_HOST` status was randomly assigned to bookings along with a `userId`, which violates the correct behavior of instant meetings.

**Problem:**
- The seed script randomly picked from all 5 booking statuses (including `AWAITING_HOST`)
- When `AWAITING_HOST` was selected, the script still assigned a `userId`
- Real instant meetings with `AWAITING_HOST` status should have `userId = NULL` until a host joins
- This created incorrect test data that doesn't match production behavior

**Solution:**
- Filter out `AWAITING_HOST` from `BookingStatus` values using `Object.values(BookingStatus).filter()`
- This approach is more maintainable than listing valid statuses explicitly - if new statuses are added in the future, they will automatically be included
- Moved `validStatusesForSeed` to module level so it's calculated once at load time instead of on every shuffle call
- Added comments explaining why this status requires special handling

## Mandatory Tasks (DO NOT REMOVE)

- [x] I have self-reviewed the code (A decent size PR without self-review might be rejected).
- [x] I have updated the developer docs in /docs if this PR makes changes that would require a [documentation change](https://cal.com/docs). N/A - seed script only.
- [x] I confirm automated tests are in place that prove my fix is effective or that my feature works. N/A - this is a seed script for test data generation.

## How should this be tested?

1. Run the seed script: `yarn seed-insights`
2. Verify no `AWAITING_HOST` bookings are created with a `userId`:
```sql
SELECT COUNT(*) FROM "Booking" 
WHERE status = 'awaiting_host' AND "userId" IS NOT NULL 
AND "createdAt" > NOW() - INTERVAL '1 day';
-- Should return 0 for newly seeded data
```
3. Verify other statuses are still generated:
```sql
SELECT status, COUNT(*) FROM "Booking" 
WHERE "createdAt" > NOW() - INTERVAL '1 day' 
GROUP BY status;
-- Should show ACCEPTED, PENDING, CANCELLED, REJECTED (but not AWAITING_HOST)
```

## Checklist

- [x] I have read the [contributing guide](https://github.com/calcom/cal.com/blob/main/CONTRIBUTING.md)
- [x] My code follows the style guidelines of this project
- [x] I have commented my code, particularly in hard-to-understand areas
- [x] I have checked if my changes generate no new warnings

---

> **Link to Devin run:** https://app.devin.ai/sessions/08f634e7210d415982d71c8f687b4468
> **Requested by:** eunjae@cal.com (@eunjae-lee)
* refactor: hide duplicate translations warning

* Apply suggestion from @keithwillcode

---------

Co-authored-by: Keith Williams <keithwillcode@gmail.com>
* fix: made playground page use same layout as other pages

* update: added feature key

* Change link display from table to list format

Refactor page layout to use a list instead of a table for links.

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* init

* --

* update dialog

* reassignment

* further changes

* add unit test

* fix

* type fix??

* fix type??

* emails

* workflows

* reason recorder

* refactor reassigned email

* fix reassigned reason

* fix type

* improve dialog

* add integration tests

* -

* remove unnecessary comments --1

* removed unnecessary comments

* fix type?

* address cubic

* address feedback

* --

* fix type?

* address cubic

* type-fix?

* fix type

* further fixes

* fix location update

* type fix

* propagate error to top

* fix mocking

* fix reported bugs

* fix

* fix success page video URL

* remove PII from logs

* persist video URL

* better audit trail

* revert email function name change

* fix test

* fix flake

* di

* fixes

* extract logic and other repo access from BookingRepository

* fixes

* (☞゚∀゚)☞ udit

* integration test fixes

* mroe fixes

* extract to repo

* extract to repo --2

* fix type

* cubic

* address feedback --1

* --2

* wip

* addressed feedback

* type fixes

* type fixes

* fix issues

* feedback --1

* feedback --2

* BookingAccessService DI

* fix

* break down function into small functions

* fixes --1

* fixes

* typefix

* addressing feedback

* fix merge conflict lost change
…25489)

* refactor: use Button component instead of plain button in CopyButton

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* add shrink-0 to StartIcon and EndIcon of Button

* remove unused class names

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
* fix: integer to text comparison in routing insights query

Add explicit integer array cast to prevent PostgreSQL type mismatch error when comparing bookingUserId (integer) with user_id array values in getRoutedToPerPeriodData query

* add e2e tests

* refactor: remove LoadingInsight component and handle loading in ChartCard

- Enhanced ChartCard to render default loading UI when isPending is true
- Replaced all LoadingInsight usages with ChartCard that accepts isPending/isError props
- Removed LoadingInsight component and updated exports
- Updated 16 chart components to use the new pattern
- ChartCard now shows spinner and skeleton title during loading state

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix: make children prop optional in ChartCard when isPending is true

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove unused loadingState prop from ChartCard

- Remove loadingState prop and ChartLoadingState type export
- Simplify computedLoadingState to derive state only from isPending/isError
- No functional changes - loadingState was not being used by any components
- data-loading-state attribute behavior remains unchanged for E2E tests

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove duplicate isPending early returns from chart components

- Update all 16 chart components to use single ChartCard return pattern
- Gate children rendering with !isPending && isSuccess && data checks
- Prevents data processing code from executing during loading state
- Improves code consistency and maintainability across all charts

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* refactor: remove redundant !isPending check from chart conditionals

- Simplify conditional rendering to use just 'isSuccess && data' or 'isSuccess'
- In TanStack Query, isSuccess and isPending are mutually exclusive
- The !isPending check was redundant since isSuccess already implies !isPending
- Applied to all 16 chart components for consistency
- Components with safe defaults (data ?? []) use just 'isSuccess'
- Components requiring data check use 'isSuccess && data'

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* revert the mistake

* apply feedback

* apply feedback

* fix e2e

---------

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>
* Fixed variable dropdown

* Radix fix

* addressed coderabits comments

* Adjust styles and structure in AddVariablesDropdown

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
Co-authored-by: Dhairyashil <dhairyashil10101010@gmail.com>
Co-authored-by: Dhairyashil Shinde <93669429+dhairyashiil@users.noreply.github.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
hbjORbj and others added 29 commits December 14, 2025 08:45
…layer (#25816)

* move users admin router to trpc package

* users admin router

* update import path

* fix
* fix: checkout session completed

* refactor: code quality
* chore: Cleanup some outdated pages files

* revert: endpoint

* revert: endpoint

---------

Co-authored-by: Udit Takkar <udit222001@gmail.com>
Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
…25840)

* add prisma agent repository to features

* add

* wip

* wip

* wip

* Update apps/web/app/api/webhooks/retell-ai/route.ts

Co-authored-by: Volnei Munhoz <volnei@cal.com>

* ts fix

* ts fix

---------

Co-authored-by: Volnei Munhoz <volnei@cal.com>
…#25827)

- Update weekly view to display OOO when showNotePublicly is true
- Update column view to include days with public notes in schedule
- Add notes and showNotePublicly props to weekly view calendar
- Disable show note publicly checkbox when notes field is empty
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: lauris@cal.com <lauris@cal.com>
* feat: distributed tracing 2

* feat: distributed tracing 2

* refactor: feedback

* refactor: feedback

* fix: type error

* fix: trpc error

* feat: distributed tracing - 3

* chore: translation

* refactor: improvements

* fix: feedback

* chore: remove

* feat: dist tracing 4

* fix: type errors

* fix: type errors

* fix: remove string type

* fix: type errors
* feat: extract core booking audit infrastructure from PR 25125

This PR contains only the core booking audit infrastructure changes from PR 25125,
excluding integration changes with booking flows.

Included:
- All packages/features/booking-audit/* (core audit services, actions, repository)
- packages/features/di/containers/BookingAuditViewerService.container.ts
- packages/features/tasker/tasker.ts (audit task types)
- packages/features/bookings/lib/types/actor.ts (actor types for audit)
- packages/features/bookings/repositories/BookingRepository.ts (getFromRescheduleUid method)
- apps/web/modules/booking/logs/views/booking-logs-view.tsx (UI for viewing audit logs)
- apps/web/public/static/locales/en/common.json (translations)

Excluded (integration changes):
- packages/trpc/server/* (tRPC handlers)
- packages/features/ee/round-robin/* (round-robin integration)
- packages/features/bookings/lib/handleCancelBooking.ts
- packages/features/bookings/lib/handleConfirmation.ts
- packages/features/bookings/lib/onBookingEvents/BookingEventHandlerService.ts
- packages/features/bookings/lib/service/RegularBookingService.ts
- apps/api/v2/* (API v2 integration)

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix: make booking audit interfaces backwards-compatible with main

- Add queueAudit method back to BookingAuditProducerService interface for backwards compatibility
- Implement queueAudit method in BookingAuditTaskerProducerService
- Make userTimeZone parameter optional in BookingAuditViewerService
- Add BookingAuditTaskProducerActionData type for legacy queueAudit method
- Use any generics in BookingAuditActionServiceRegistry (matching PR 25125)
- Fix type assertions in BookingAuditTaskConsumer

Co-Authored-By: hariom@cal.com <hariombalhara@gmail.com>

* fix switch eslint and ts

* feat: enhance BookingAuditViewerService with logging and type improvements

- Added ISimpleLogger dependency to BookingAuditViewerService for better error handling.
- Updated actor type in enriched audit logs to use AuditActorType for improved type safety.
- Replaced console.error with logger for error reporting when no rescheduled log is found.

---------

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: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
* feat: add gradient indicator for scrollable event descriptions

- Create EventDescription component with dynamic gradient overlay
- Show gradient only when description content overflows (180px max-height)
- Use Tailwind v4 gradient utilities for optimal styling
- Replace inline description rendering in EventMeta with new component
- Minimal implementation: 28 lines, no useEffect, single state variable

* add top gradient and optimize ScrollableWithGradients component

* add top gradient and optimize ScrollableWithGradients component

* refactor: extract reusable ScrollableWithGradients component with top/bottom gradients

* adjust gradient fades
* refactor: prisma N+1 Queries

* addressed review
* fix: atoms vite config

* fixup! fix: atoms vite config

* fixup! fixup! fix: atoms vite config
…s sheet with view persistence (#25545)

* display UTM parameters

* persist view (list | calendar) in localStorage

* move to next page on clicking "next" of last item in the page

improve navigation

^ Conflicts:
^	apps/web/modules/bookings/components/BookingCalendarContainer.tsx

* fix navigation on calendar view

* Delete apps/web/modules/bookings/NAVIGATION_IMPLEMENTATION.md

* avoid page size being 0

* check feature flag on user level

* use map to improve useBookingListData

* address feedback

* feat(bookings): add smart navigation for calendar view

- Add sort option to tRPC bookings.get schema and handler
- Create NAVIGATION_PROBE_WINDOW_MONTHS constant (3 months)
- Create useNearestFutureBooking hook to find nearest future booking
- Create useNearestPastBooking hook to find nearest past booking
- Update useCalendarNavigationCapabilities to use probe results
- Update BookingCalendarContainer to wire up probe hooks

This enables the booking details sheet to:
- Disable next/prev buttons when no bookings exist in that direction
- Jump directly to the week containing the nearest booking

Co-Authored-By: eunjae@cal.com <hey@eunjae.dev>

* fix type error

* feat: add booking selection state when using slideover (#25637)

* implement booking selection when using slideover

* remove pixel shift

* fix

* auto scroll to selected event on calendar

* make DateValues header sticky when scrolling

---------

Co-authored-by: Eunjae Lee <hey@eunjae.dev>

* prefetch previous / next weeks on calendar view

* clean up classes

* handle "fetched & but no data" situation more correctly in useCalendarAutoSelector

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: sean-brydon <55134778+sean-brydon@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
…25561)

* 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
@kussberg kussberg closed this Dec 15, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.