Compare commits

..
Author SHA1 Message Date
github-actions[bot] b84405c5eb chore: version packages (#22418)
Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-07-11 18:53:35 +00:00
Morgan 2743b32536 fix: third part token auth strategy (#22374) 2025-07-11 17:27:51 +00:00
Omar López a251422f7a fix: locks eslint to v8 across monorepo (#22424) 2025-07-11 17:10:12 +00:00
Alex van Andel d7c9bd6ed8 perf: Tasker performance improvements (#22395)
* perf: Tasker performance improvements

* Remove fields from expected tasker create call, test fix

* Fix the tests, needs validation
2025-07-11 10:09:24 -07:00
8b3ff0b789 fix: ensure isBookingDryRun prop works for booking creation (#22182)
* fix: ensure isBookingDryRun prop works for booking creation

- Add isBookingDryRun prop to useHandleBookEvent hook
- Pass prop through BookerPlatformWrapper and useBookings
- Override _isDryRun flag when prop is provided
- Fixes issue where dry run banner showed but booking creation ignored prop

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

* fixup

* if dry run prop is pass we use that, otherwise we use params

* add changesets for PR

* replace minor with patch changeset

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rajiv@cal.com <rajiv@cal.com>
Co-authored-by: Ryukemeister <sahalrajiv6900@gmail.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-07-11 16:45:30 +00:00
Lauris Skraucis 94a537bfda feat: v2 bookings return rescheduledToUid (#22289) 2025-07-11 13:17:55 -03:00
Calcom BotandLingo.dev 64305ffee1 feat: update translations via @LingoDotDev (#22416)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-07-11 15:55:52 +00:00
45698db0b4 fix: validate Round Robin host availability in handleNewBooking (#22253)
* fix: validate Round Robin host availability in handleNewBooking

- Add validation to ensure Round Robin events have at least one available non-fixed host
- Throw NoAvailableUsersFound error when no Round Robin hosts are available
- Add unit test to verify the fix works correctly
- Fixes issue where Round Robin events with fixed hosts could be booked without Round Robin hosts

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* test: fix host configuration in Round Robin test

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* test: improve Round Robin test to only make RR host busy

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* fix: correct Round Robin validation to only check when RR hosts assigned

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* fix: refine Round Robin validation to only apply when both fixed and RR hosts present

- Add fixedUserPool.length > 0 condition to validation
- Ensures validation only triggers for specific bug scenario: events with fixed hosts + RR hosts but no available RR hosts
- Prevents blocking normal Round Robin events that only have RR hosts
- Updates test description to reflect more precise validation logic

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* fix: simplify Round Robin validation to resolve E2E test failures

Remove overly restrictive fixedUserPool.length > 0 condition that was
blocking valid Round Robin booking scenarios in E2E tests. The validation
now only checks if Round Robin hosts are assigned but none are available,
which is the intended behavior for the bug fix.

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* fix: restore fixedUserPool condition to Round Robin validation

- Add back fixedUserPool.length > 0 condition to make validation specific to bug scenario
- Only triggers when Round Robin events have both fixed hosts AND Round Robin hosts but no available Round Robin hosts
- Prevents blocking normal Round Robin events that only have Round Robin hosts (like in organization settings E2E test)
- Updates test description to reflect more precise validation logic

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

Co-Authored-By: carina@cal.com <c.wollendorfer@me.com>

* throw error if no RR user is available

* add tests

* fix comments

* revert change

* remove comments

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: CarinaWolli <wollencarina@gmail.com>
2025-07-11 16:47:21 +01:00
Anik Dhabal Babuandcubic-dev-ai[bot] 14e14289f0 fix: e2e tests flakes (#22415)
* fix: e2e flakes

* Update apps/web/playwright/dynamic-booking-pages.e2e.ts

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

* Update delete-account.e2e.ts

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-07-11 16:45:06 +01:00
Omar López 18650b1215 fix: local eslint runs (#22402) 2025-07-11 16:44:35 +01:00
d2bbbf3f45 feat: expose AvailabilitySettingsProps type in @calcom/atoms (#22246)
* feat: expose AvailabilitySettingsProps type in @calcom/atoms

- Export AvailabilitySettingsPlatformWrapperProps as AvailabilitySettingsProps
- Add type export to main atoms package index for direct import
- Enables external consumers to import type directly from @calcom/atoms

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

* feat: also export AtomScheduleType for atomSchedule data

- Export FindDetailedScheduleByIdReturnType as AtomScheduleType
- Enables direct import of atomSchedule type from @calcom/atoms
- Covers both AvailabilitySettingsProps and AtomScheduleType exports

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

* fix: update AtomScheduleType to include null and undefined

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

* refactor: move AtomScheduleType to AvailabilitySettingsPlatformWrapper for cleaner organization

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

* feat: rename AtomScheduleType to AvailabilitySettingsAtomScheduleType for better clarity

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

* feat: rename to AvailabilitySettingsScheduleType for better naming consistency

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

* fix: use proper import from @calcom/platform-libraries/schedules instead of dynamic import

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

* add changelogs

* feat: extract AvailabilitySettingsSchedule type from AvailabilitySettingsProps

- Extract schedule property structure into separate AvailabilitySettingsSchedule type
- Update AvailabilitySettingsProps to reuse the extracted schedule type
- Export AvailabilitySettingsSchedule from @calcom/atoms package index
- Enables direct import of schedule type for external consumers

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

* Revert "feat: extract AvailabilitySettingsSchedule type from AvailabilitySettingsProps"

This reverts commit 79239e4b18925db3fcc1d8df7e9f37770ec4f116.

* fixup: extract schedule for AvailabilitySettingsProps

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: rajiv@cal.com <rajiv@cal.com>
Co-authored-by: Rajiv Sahal <sahalrajiv-extc@atharvacoe.ac.in>
Co-authored-by: Ryukemeister <sahalrajiv6900@gmail.com>
2025-07-11 16:53:40 +02:00
devin-ai-integration[bot]andmorgan@cal.com 13cb99b7fe feat: inject required repositories into AvailableSlotsService using dependency injection (#22413)
* feat: inject all repositories into AvailableSlotsService using dependency injection

- Create DI modules for SelectedSlotsRepository, TeamRepository, UserRepository, BookingRepository, EventTypeRepository, RoutingFormResponseRepository
- Update available-slots DI module to bind all 6 repositories
- Update DI container to load all new repository modules
- Replace all 10 direct repository instantiations with injected dependencies
- Add missing DI tokens for repository modules
- Expand IAvailableSlotsService interface to include all repository dependencies

This follows the same dependency injection pattern established in previous repository refactoring PRs and enables better testability and modularity by making all dependencies explicit and configurable.

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

* chore: DI available slots repo on api v2

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
2025-07-11 17:37:24 +03:00
a1571d01bd docs: Update Organization Readme (#22060)
* Update org readme

* Update packages/features/ee/organizations/README.md

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

* Apply suggestions from code review

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

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Co-authored-by: Syed Ali Shahbaz <52925846+alishaz-polymath@users.noreply.github.com>
2025-07-11 18:30:28 +05:30
Kartik Saini bbd8af074e chore: update i18n workflow to use Node.js 20 (#22403) 2025-07-11 12:17:12 +00:00
c9a47dd0ce refactor: convert RoutingFormResponseRepository to use dependency injection pattern (#22408)
* refactor: convert RoutingFormResponseRepository to use dependency injection pattern

- Add constructor with PrismaClient dependency injection
- Convert all 5 static methods to instance methods using this.prismaClient
- Convert private static helper method generateCreateFormResponseData to instance method
- Update all usage sites to use two-step instantiation pattern:
  const formResponseRepo = new RoutingFormResponseRepository(prisma); formResponseRepo.method(...)
- Update test files to use proper mock implementation with vi.mocked pattern
- Reuse repository instances within same function scope where multiple calls are made
- Follow same dependency injection pattern as UserRepository, TeamRepository, SelectedSlotsRepository, BookingRepository, and EventTypeRepository

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

* fix: resolve inconsistent dependency injection in AvailableSlotsService

- Use two-step instantiation pattern for UserRepository calls
- Continue fixing remaining repository instantiation inconsistencies

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

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-07-11 10:03:25 +00:00
sean-brydon 1c9778e667 fix members page crash with pbac feature flag (#22409) 2025-07-11 09:59:56 +01:00
devin-ai-integration[bot]andmorgan@cal.com 758143ef70 refactor: convert EventTypeRepository to use dependency injection pattern (#22397)
- Add constructor with PrismaClient dependency injection
- Convert all 14 static methods to instance methods using this.prismaClient
- Update all usage sites to use two-step instantiation pattern:
  const eventTypeRepo = new EventTypeRepository(prisma); eventTypeRepo.method(...)
- Keep getSelectedCalendarsFromUser as static utility method
- Follow same pattern as UserRepository, TeamRepository, SelectedSlotsRepository, and BookingRepository
- Maintain all existing method signatures and functionality

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
2025-07-11 10:39:22 +03:00
sean-brydon b5911a9a2d feat: add method for getting teamId permissions PBAC (#22375)
* feat:add method for getting teamId permissions

* add logic and test for zero permsiion strings

* fix test by updating mock
2025-07-11 08:34:33 +01:00
keithwillcode 260f4e1b51 chore: release v5.4.19
Post release / release (push) Canceled after 0s
2025-07-11 01:25:57 +00:00
Keith Williams e6c36af3b4 revert: "fix: Add new route to create team routing-form response (#22347)" (#22399)
This reverts commit 89e7fca183.
2025-07-11 01:02:11 +00:00
Omar López 2fb2458eee Revert "feat: add race condition test for calendar-cache-serve booking flow (…" (#22400) 2025-07-10 17:43:41 -07:00
keithwillcode b340b0edbe chore: release v5.4.18 2025-07-10 22:59:01 +00:00
f7fd1c4402 feat: remove unused x-cal-timezone header and middleware matcher (#22398)
* feat: remove unused x-cal-timezone header and middleware matcher

- Remove x-cal-timezone header setting from middleware.ts
- Remove x-cal-timezone header setting from createNextApiHandler.ts
- Remove /api/trpc/:path* from middleware matcher
- Eliminates ~50M edge requests per month for unused functionality
- All timezone handling uses browser detection and localStorage instead

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

* update POST_METHODS_ALLOWED_API_ROUTES

* test: remove api/trpc POST validation test

- Remove test for /api/trpc/book/event POST requests since /api/trpc/:path* was removed from middleware matcher
- Keep test for /api/auth/signup which is still processed by middleware

Co-Authored-By: keith@cal.com <keithwillcode@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: hbjORbj <sldisek783@gmail.com>
2025-07-10 22:16:25 +00:00
devin-ai-integration[bot]andmorgan@cal.com c316721051 refactor: convert BookingRepository to use dependency injection pattern (#22389)
* refactor: convert BookingRepository to use dependency injection pattern

- Add constructor injection for PrismaClient to BookingRepository
- Convert all static methods to instance methods using this.prismaClient
- Update all usage sites to use two-step instantiation pattern:
  const bookingRepo = new BookingRepository(prisma); bookingRepo.method(...)
- Apply same dependency injection pattern as UserRepository, TeamRepository, and SelectedSlotsRepository
- Update test files to use correct prismaMock import paths
- Maintain all existing functionality and type safety

Files updated:
- BookingRepository class structure and all 15 static methods
- Video meeting pages and booking views
- Booking utilities and services (handleNewBooking, originalRescheduledBookingUtils)
- Round robin handlers and reassignment logic
- Interval limits and booking limits checking
- Test files with proper mocking setup

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

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
2025-07-10 18:06:57 +00:00
80ac118242 refactor: convert SelectedSlotsRepository to use dependency injection pattern (#22387)
* refactor: convert TeamRepository to use dependency injection pattern

- Add constructor injection for PrismaClient in TeamRepository
- Convert all static methods to instance methods using this.prismaClient
- Update all usage sites to use two-step pattern: const teamRepo = new TeamRepository(prisma); teamRepo.method(...)
- Optimize instance reuse within same function scopes where possible
- Update test files to work with new instance pattern
- Preserve all existing functionality and method signatures

Follows the same dependency injection pattern as UserRepository refactoring

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

* refactor: convert SelectedSlotsRepository to use dependency injection pattern

- Add constructor injection for PrismaClient/PrismaTransaction
- Convert all static methods to instance methods using this.prismaClient
- Update usage sites to use two-step instantiation pattern:
  const slotsRepo = new SelectedSlotsRepository(prisma); slotsRepo.method(...)
- Optimize instance reuse in util.ts for multiple method calls
- Update test mocks to handle new constructor-based pattern
- Add explicit type annotations to resolve TypeScript inference issues

Follows same dependency injection pattern as UserRepository and TeamRepository refactorings.

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

* chore: bump platform libs

* fix: devin mistake prisma client

* fix: devin typing mistakes

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-07-10 15:14:26 +00:00
efd15c5a3e refactor: convert TeamRepository to use dependency injection pattern (#22383)
* refactor: convert TeamRepository to use dependency injection pattern

- Add constructor injection for PrismaClient in TeamRepository
- Convert all static methods to instance methods using this.prismaClient
- Update all usage sites to use two-step pattern: const teamRepo = new TeamRepository(prisma); teamRepo.method(...)
- Optimize instance reuse within same function scopes where possible
- Update test files to work with new instance pattern
- Preserve all existing functionality and method signatures

Follows the same dependency injection pattern as UserRepository refactoring

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

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-07-10 14:03:50 +00:00
devin-ai-integration[bot]andBailey 7d02d1bb23 fix: improve layout in copy times popup (#22368)
- Remove justify-between from checkbox labels to fix uneven padding
- Use CheckboxField with descriptionAsLabel for consistent spacing
- Add flex justify-end to button container for right alignment
- Maintain existing keyboard navigation functionality

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: Bailey <bailey@pumfleet.co.uk>
2025-07-10 13:21:38 +00:00
Dhairyashil ShindeandAnik Dhabal Babu 0d877e7be3 fix: Always return cancellationReason and cancelledByEmail for cancelled bookings (#22082)
* fix: always return cancellationReason and cancelledByEmail for cancelled bookings

* Implemented reviewed changes

* Updated ternary conditional logic for cancelled booking

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-07-10 18:45:46 +05:30
e4c47640fc refactor: convert UserRepository to use dependency injection pattern (#22360)
* refactor: convert UserRepository to use dependency injection pattern

- Convert all static methods to public instance methods
- Add constructor that takes PrismaClient parameter
- Update all usage sites to use new instantiation pattern: new UserRepository(prisma).method()
- Follow same pattern as PrismaOOORepository for consistency
- Maintain all existing method logic and signatures unchanged
- Update 125+ files across the codebase to adapt to new pattern

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

* optimize: reuse UserRepository instances within same function scope

- Create single UserRepository instance per function scope
- Reuse instance for multiple method calls within same function
- Reduces object instantiation overhead and improves performance
- Apply optimization pattern consistently across codebase

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

* fix: repository

* fixup! fix: repository

* fixup! fixup! fix: repository

* fixup! fixup! fixup! fix: repository

* fix: update test mocking strategies for UserRepository dependency injection

- Convert static method mocks to instance method mocks in userCreationService.test.ts
- Update vi.spyOn calls to work with constructor injection pattern in getAllCredentials.test.ts
- Fix UserRepository mocking in getRoutedUrl.test.ts to use constructor injection
- Ensure consistent mocking approach across all test files
- Fix 'UserRepository is not a constructor' errors in tests

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

* feat: optimize UserRepository instance reuse and add SessionUser type

- Reuse UserRepository instance in OrganizationRepository.createWithNonExistentOwner
- Add comprehensive SessionUser type definition for type safety
- Improve type constraints in enrichUserWithTheProfile and enrichUserWithItsProfile
- Ensure proper return types with profile information

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

* fix: make UserRepository mocking strategy more robust for CI environments

- Add defensive checks for vi.mocked() to handle CI environment differences
- Ensure mockImplementation is available before calling it
- Maintain consistent mocking pattern across all test files
- Fix 'Cannot read properties of undefined' error in CI

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

* fixup! fix: make UserRepository mocking strategy more robust for CI environments

* refactor: convert direct UserRepository instantiations to two-step pattern

- Change await new UserRepository(prisma).method(...) to const userRepo = new UserRepository(prisma); await userRepo.method(...)
- Optimize instance reuse within same function scopes
- Apply pattern consistently across all modified files in PR
- Fix type errors in organization.ts and sessionMiddleware.ts

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

* refactor: complete two-step UserRepository pattern for remaining files

- Apply two-step instantiation pattern to all remaining modified files in PR
- Ensure consistent UserRepository usage across entire codebase
- Maintain instance reuse optimization within function scopes

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

* chore: bump platform libs

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-07-10 12:11:14 +00:00
Hariom Balhara 89e7fca183 fix: Add new route to create team routing-form response (#22347)
* fix: Add formId ownership with org guard

* Don't allow user routing form endpoint to create response for team
routing form

* rename to or guard
2025-07-10 11:56:45 +00:00
sean-brydon 33b5bf1db8 fix: Move pbac to prisma from kysley (#22318)
* fixes

* fix prisma mock to include transaction callbacj

* remove redudant transactions

* remove hard requirement on transactions in services

* remove mocktrx

* fix cubic feedback

* correct permission updating logic of roles. Implement diff service + domain types

* remove role mapper
2025-07-10 10:24:50 +00:00
Devanshu Sharma f69c4a900e refactor: enhance UI spacing and text styling in Locations component and Switch component (#22328) 2025-07-10 08:34:32 +00:00
Sarthak Kapila 1da25cc85f docs: booking attendee absent (#22094)
* refactor: booking attendee absent

* docs: swagger generate
2025-07-10 07:52:10 +00:00
Akash Kinkar Pandey 3a65b372c5 perf:Remove Redundant Keys in common.json for english (#22362) 2025-07-10 13:19:38 +05:30
sean-brydon 9892d521fb fix: use domain errors instead of trpc (#22341) 2025-07-10 08:23:50 +01:00
Alex van Andel 3285b252c2 chore: Add High-Load warning for user availability checks with many users (#22369) 2025-07-10 01:09:21 +00:00
emrysal 97af2fc0f9 chore: release v5.4.17
Post release / release (push) Canceled after 0s
2025-07-09 21:19:55 +00:00
Morgan 5c508bce6d chore: DI repositories in AvailableSlotsService class (#22356)
* chore: DI available slots service repositories

* remove configService from worker module

* register scheduleRepo in availableSlotsModule

* chore: bump platform libs

* remove useless comment from prisma module

* fix: availableSlotsModule to class deps

* fix: repositoriesModule deps

* chore: container pattern

* refactor: move modules in DI folder

* fixup! refactor: move modules in DI folder
2025-07-09 19:55:55 +00:00
Morgan db84343581 chore: new auth strategy for third party access tokens (#21867)
* chore: new auth strategy for third party access tokens

* chore: throw error earlier if Bearer token is invalid

* fix: check apikey and access token before third party token
2025-07-09 22:47:07 +03:00
Shaunak DasandUdit Takkar 2b44d09191 fix: 🐛 guests no show insights bug (#22045)
* fix: guests no show insights bug

* feat: fix feature failure introduced by conflict resolve

* feat: address cubic comments

* fix: add no show guests count

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2025-07-09 19:17:19 +00:00
Anik Dhabal Babu 10d67aee86 Revert "fix: issue #9523 limited scroll range on mobile device (#21935)" (#22363)
This reverts commit f611c3d767.
2025-07-09 19:09:46 +00:00
Anik Dhabal Babuandcubic-dev-ai[bot] b33bbb8af0 fix: manage Seo index for embed page (#22348)
* fix: manage seo index

* Update apps/web/app/(booking-page-wrapper)/[user]/[type]/embed/page.tsx

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

* update

* tweak

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-07-09 16:03:46 +00:00
Joe Au-Yeung 3fe9327724 fix: Add unique constraint to team and slug for attributes (#22357)
* Add unique constraint to team and slug

* Add migration file
2025-07-09 15:52:48 +00:00
Omar LópezandDevin AI 179a547bd0 feat: add race condition test for calendar-cache-serve booking flow (#22170)
* feat: add race condition test for calendar-cache-serve booking flow

- Reproduces double-booking when two guests book same timeslot 1-2s apart
- Enables both calendar-cache and calendar-cache-serve feature flags
- Uses parallel browser contexts to simulate concurrent booking attempts
- Verifies both bookings succeed and create double-booking scenario

Co-Authored-By: zomars@cal.com <zomars@me.com>

* fix: correct page reference in race condition test timeout

- Change page.waitForTimeout(1500) to use correct page context
- Fix timeslot selection to ensure both contexts book same slot
- Remove incorrect host assertion and add logging to demonstrate race condition
- Test now successfully reproduces double-booking bug locally
- Fixes 'end of central directory record signature not found' error

Co-Authored-By: zomars@cal.com <zomars@me.com>

* Update booking-race-condition.e2e.ts

* Update booking-race-condition.e2e.ts

* Update booking-race-condition.e2e.ts

* Update booking-race-condition.e2e.ts

* Create scratchpad.md

* Update booking-race-condition.e2e.ts

* Delete scratchpad.md

* Update booking-race-condition.e2e.ts

* Update booking-race-condition.e2e.ts

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-07-09 10:33:13 -04:00
Morganandcubic-dev-ai[bot] 7ada02177d refactor: get available slots service (#22340)
* refactor: GetAvailableSlotsService

* chore: add todo comments where DI is needed

* chore: bump platform libs

* chore: fix unit tests

* fix: only instantiate AvailableSlotsService once per service

* Update packages/trpc/server/routers/viewer/slots/util.ts

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

* fix: bind this on all functions with reporting

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-07-09 12:15:09 +01:00
59b55e40b5 fix: allow enter key to create new list items in editor (#22001)
* fix: allow enter key to create new list items in editor

* remove: unnecessary priority set to low

* remove: unnecessary priority set to low

---------

Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
Co-authored-by: Eunjae Lee <hey@eunjae.dev>
2025-07-09 09:39:08 +00:00
19563aa697 feat: Self hosted onboarding (#22102)
* intro work

* update wixard form to have content callback to remove preset navigation

* more fixes to deployment

* fix calling static service

* fix save license key text

* ensure default steps work as expected

* fix conditional for rendering step

* skip step

* add on next step for free license

* refactor wizard form to use nuqs

* fix styles

* merge base param with step config

* fix next stepo text

* use deployment Signature token

* decrypt signature token

* fix: resolve type errors and test failures from wizard form refactor

- Fix signatureToken field name to signatureTokenEncrypted in deployment repository
- Add missing getSignatureToken method to verifyApiKey test mock
- Fix WizardForm import from default to named export in test file
- Add missing nextStep prop to Steps component in WizardForm

Resolves TypeScript type check errors and unit test failures without changing functionality.

Co-Authored-By: sean@cal.com <Sean@brydon.io>

* fix: add missing getDeploymentSignatureToken mock in LicenseKeyService test

Co-Authored-By: sean@cal.com <Sean@brydon.io>

* fix: add nuqs library mock for WizardForm test

Co-Authored-By: sean@cal.com <Sean@brydon.io>

* fix: add missing nav prop to AdminAppsList component with eslint disable

Co-Authored-By: sean@cal.com <Sean@brydon.io>

* Apply suggestions from code review

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

* Update apps/web/modules/auth/setup-view.tsx

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

* fix license schema changes

* revret schema generation

* fix eslint errors

* remove required nav type + add use client

* fix types

* Update packages/ui/components/form/wizard/useWizardState.ts

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

* fix controller issue

* add checks for deployment key being null - add more tests

* fix tests

* add deployment key tests

* fix: resolve crypto mock to handle empty encryption keys gracefully

- Updated symmetricDecrypt mock to return null instead of throwing 'Invalid key' error when encryption key is empty
- All getDeploymentKey tests now pass including the previously failing 'should return null when decryption fails due to missing encryption key' test
- Fixes mocking issues in PR 22102 self-hosted onboarding wizard form refactor

Co-Authored-By: sean@cal.com <Sean@brydon.io>

* fix label

* add i18n to error

* use enum for steps

* add as const

* fix test env issues

---------

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>
2025-07-09 09:26:01 +01:00
emrysal 6410687fb6 chore: release v5.4.16
Post release / release (push) Canceled after 0s
2025-07-09 00:00:28 +00:00
Bandhan Majumder 4e4c8f5f49 fix: fixes type issue in theme and password property of newUser seed data (#22232)
Signed-off-by: bandhan-majumder <bandhanmajumder16@gmail.com>
2025-07-08 23:53:48 +00:00
abhix4 416b17cf69 fix: center align dropdown indicator when menu is open (#22262)
* fix: vertical misalignment of dropdown indicator icon in select

* fix: vertical misalignment of dropdown indicator icon in select
2025-07-08 23:37:24 +00:00
Anwar SadathandAnik Dhabal Babu 27cd7c8496 fix: Hosts are able to cancel an event without providing any reason but with whitepsace characters (#22282)
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-07-09 00:17:13 +01:00
Benny Joo 02321c33b2 Revert "refactor: GetAvailableSlotsService (#22321)" (#22334) 2025-07-08 14:28:59 -07:00
Morgan c3dab3e4f8 refactor: GetAvailableSlotsService (#22321)
* refactor: GetAvailableSlotsService

* chore: add todo comments where DI is needed

* chore: bump platform libs
2025-07-08 16:54:45 -04:00
Joe Au-Yeung 0c182e2813 Allow admins to update users (#22332) 2025-07-08 20:14:44 +00:00
Hariom BalharaandUdit Takkar 51a076212b fix: Do not wait for slots request before removing the (#22323)
default(non-skeleton) loader

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
2025-07-08 16:52:37 -03:00
Akash Kinkar Pandey 27dce7374b feat: Add Bengali Language Support. This fixes #17717 (#20567) 2025-07-08 13:15:11 +00:00
Lauris Skraucis 52f2c18800 refactor: attendee scheduled email timezone (#22301) 2025-07-08 10:16:22 +00:00
Eunjae LeeandDevin AI 35a8918ea9 feat: add expandable submenu for sidebar (#22268)
* feat: add dropdown submenu for Insights navigation

- Add dropdown functionality for desktop Insights menu to show submenu instead of immediate navigation
- Add expandable submenu for mobile Insights in More navigation
- Users can now select specific insights destination (Bookings, Routing, Router Position)
- Maintains existing navigation patterns and accessibility

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

* fix: use expansion state instead of dropdown for insights navigation

- Remove dropdown components and use existing child rendering pattern
- Add state management for expansion tracking
- Change items with children to render as buttons instead of links
- Preserve existing behavior when currently on page

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

* feat: keep parent expanded and hide chevron when sub-item is active

- Add hasActiveChild logic to detect when any child item is currently active
- Keep parent menu expanded when any sub-item is selected
- Hide chevron icon when any sub-item is active
- Maintain existing expansion behavior when no sub-item is active

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

* feat: add localStorage persistence for navigation expansion state

- Replace useState with usePersistedExpansionState hook in both NavigationItem and MobileNavigationMoreItem
- Expansion state now persists across page navigations using localStorage
- Uses nav-expansion-{itemName} keys following existing localStorage patterns

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

* chore: update yarn.lock from dev server startup

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

* feat: change navigation expansion persistence from localStorage to sessionStorage

- Expansion state now persists within browser session but resets when tab closes
- Maintains same error handling and key naming patterns

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

* Revert "chore: update yarn.lock from dev server startup"

This reverts commit 832f417da96f79979942c99400d2a7b26f889616.

* clean up

* add aria attributes

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-07-08 09:39:38 +00:00
b39eab72e7 feat: added option to lock maxactivebookingsperbooker for managed events (#21892)
* added option to disable maxactivebookingsperbooker for managed events

* chore

* update

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
Co-authored-by: unknown <adhabal2002@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-07-08 08:25:37 +00:00
Peer Richelsenandcubic-dev-ai[bot] 928b2477be chore: added platform link to api key settings (#21807)
* added platform link to api key settings

* Update packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx

* Update packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx

* nit

* Update packages/features/ee/api-keys/components/ApiKeyDialogForm.tsx

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

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-07-08 08:05:29 +00:00
Lauris Skraucis dc13251bcc fix: flakey v2 assign-all-team-members test (#22300) 2025-07-08 08:03:10 +00:00
Hariom Balhara 4843658dfb Allow scroll when user selects a date (#22313) 2025-07-08 07:00:30 +00:00
Anik Dhabal Babuandcubic-dev-ai[bot] 076afdc00e fix: Unable to cancel a team event if any event host is an attendee (#22233)
* fix

* Update handleCancelBooking.ts

* Update handleCancelBooking.ts

* update comment

* fix type error

* added test

* Update packages/features/bookings/lib/handleCancelBooking.ts

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

* Update handleCancelBooking.test.ts

* refactor

---------

Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
2025-07-07 19:33:33 +00:00
Deepak guptaandKartik Saini 4404699fa6 fix: improve redirect parameter handling for success page (#20764)
* improve redirect parameter

* test

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-07-08 00:23:38 +05:30
Morgan 52940ce254 refactor: getAvailableSlots use repositories (#22290) 2025-07-07 10:57:01 -07:00
Balaji KrishnamurthyandKartik Saini cd6293d220 fix: sort the availabilities based on start time for each day (#21656)
* sort the availabilities based on start time for each day

* move the sorting outside the reduce

* add guard variable for safe access

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-07-07 23:11:47 +05:30
Romit 108f95723b fix: Changes to Managed Event Types Reset Assignee Availability (#21980)
* fixed

* chore
2025-07-07 17:03:50 +00:00
Fábio Galiano f611c3d767 fix: issue #9523 limited scroll range on mobile device (#21935) 2025-07-07 21:45:26 +05:30
Lauris Skraucis 6091208f8a refactor: standardize v2 docs (#22299) 2025-07-07 14:47:31 +00:00
Morgan 585a12fa48 fix: unit tests failing on destination calendar missing timestamps (#22292) 2025-07-07 11:40:13 +00:00
349 changed files with 16996 additions and 7465 deletions
+4
View File
@@ -17,6 +17,10 @@ jobs:
pull-requests: write
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
cache: 'npm'
- uses: lingodotdev/lingo.dev@main
env:
GH_TOKEN: ${{ secrets.GH_ACCESS_TOKEN }}
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
View File
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+1 -6
View File
@@ -488,13 +488,8 @@ Don't code but still want to contribute? Join our [Discussions](https://github.c
- Set CSP_POLICY="non-strict" env variable, which enables [Strict CSP](https://web.dev/strict-csp/) except for unsafe-inline in style-src . If you have some custom changes in your instance, you might have to make some code change to make your instance CSP compatible. Right now it enables strict CSP only on login page and on other SSR pages it is enabled in Report only mode to detect possible issues. On, SSG pages it is still not supported.
## Single Org Mode
If you want to have booker.yourcompany.com to be the domain used for both dashboard(e.g. https://booker.yourcompany.com/event-types) and booking pages(e.g. https://booker.yourcompany.com/john.joe/15min).
- Set the `NEXT_PUBLIC_SINGLE_ORG_SLUG` environment variable to the slug of the organization you want to use. `NEXT_PUBLIC_SINGLE_ORG_SLUG=booker`
- Set the `NEXT_PUBLIC_WEBAPP_URL` environment variable to the URL of the Cal.com self-hosted instance e.g. `NEXT_PUBLIC_WEBAPP_URL=https://booker.yourcompany.com`.
- Set the `NEXT_PUBLIC_WEBSITE_URL` environment variable to the URL of the Cal.com self-hosted instance e.g. `NEXT_PUBLIC_WEBSITE_URL=https://booker.yourcompany.com`.
- Set the `NEXTAUTH_URL` environment variable to the URL of the Cal.com self-hosted instance e.g. `NEXTAUTH_URL=https://booker.yourcompany.com`.
Refer to docs [here](./docs/self-hosting/guides/organization/single-organization-setup) for a detailed documentation with screenshots.
Note: It causes root to serve the dashboard and not the organization profile page which shows all bookable users in the organization.
## Integrations
@@ -23,6 +23,7 @@ afterEach(() => {
const mockDeploymentRepository: IDeploymentRepository = {
getLicenseKeyWithId: vi.fn().mockResolvedValue("mockLicenseKey"), // Mocked return value
getSignatureToken: vi.fn().mockResolvedValue("mockSignatureToken"),
};
// TODO: Fix the skip condition for this test suite
+1
View File
@@ -43,6 +43,7 @@ enum locales {
SR = "sr",
SV = "sv",
VI = "vi",
BN = "bn",
}
enum theme {
DARK = "dark",
@@ -105,7 +105,9 @@ async function getHandler(req: NextApiRequest) {
const userIds = req.query.userId ? extractUserIdsFromQuery(req) : [userId];
const usersWithCalendars = await UserRepository.findManyByIdsIncludeDestinationAndSelectedCalendars({
const usersWithCalendars = await new UserRepository(
prisma
).findManyByIdsIncludeDestinationAndSelectedCalendars({
ids: userIds,
});
+8 -2
View File
@@ -4,11 +4,11 @@ import type { NextApiRequest, NextApiResponse } from "next";
import dayjs from "@calcom/dayjs";
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import { createContext } from "@calcom/trpc/server/createContext";
import { getScheduleSchema } from "@calcom/trpc/server/routers/viewer/slots/types";
import { getAvailableSlots } from "@calcom/trpc/server/routers/viewer/slots/util";
import { TRPCError } from "@trpc/server";
import { getHTTPStatusCodeFromError } from "@trpc/server/http";
@@ -27,7 +27,13 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
}
const input = getScheduleSchema.parse({ usernameList: slugs, isTeamEvent: parsedIsTeamEvent, ...rest });
const timeZoneSupported = input.timeZone ? isSupportedTimeZone(input.timeZone) : false;
const availableSlots = await getAvailableSlots({ ctx: await createContext({ req, res }), input });
const availableSlotsService = getAvailableSlotsService();
const availableSlots = await availableSlotsService.getAvailableSlots({
ctx: await createContext({ req, res }),
input,
});
const slotsInProvidedTimeZone = timeZoneSupported
? Object.keys(availableSlots.slots).reduce(
(acc: Record<string, { time: string; attendees?: number; bookingUid?: string }[]>, date) => {
+1 -1
View File
@@ -38,7 +38,7 @@
"@axiomhq/winston": "^1.2.0",
"@calcom/platform-constants": "*",
"@calcom/platform-enums": "*",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.239",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.257",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
"@calcom/prisma": "*",
@@ -443,7 +443,7 @@ export class BookingsController_2024_08_13 {
@Permissions([BOOKING_READ])
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary: "Get 'Booking References' for a booking",
summary: "Get booking references",
})
@HttpCode(HttpStatus.OK)
async getBookingReferences(
@@ -1425,8 +1425,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
// @ts-ignore
const data: BookingOutput_2024_08_13 = responseBody.data;
expect(data.status).toEqual("cancelled");
createdBooking = data;
expect(data.rescheduledToUid).toEqual(rescheduledBooking.uid);
});
});
@@ -596,9 +596,9 @@ export class BookingsService_2024_08_13 {
} else if (isRecurring && isSeated) {
formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted));
} else if (isSeated) {
formattedBookings.push(this.outputService.getOutputSeatedBooking(formatted));
formattedBookings.push(await this.outputService.getOutputSeatedBooking(formatted));
} else {
formattedBookings.push(this.outputService.getOutputBooking(formatted));
formattedBookings.push(await this.outputService.getOutputBooking(formatted));
}
}
@@ -190,9 +190,20 @@ export class InputBookingsService_2024_08_13 {
};
}
private getRoutingFormData(routing: { teamMemberIds?: number[]; responseId?: number; teamMemberEmail?: string; skipContactOwner?: boolean; crmAppSlug?: string; crmOwnerRecordType?: string } | undefined) {
private getRoutingFormData(
routing:
| {
teamMemberIds?: number[];
responseId?: number;
teamMemberEmail?: string;
skipContactOwner?: boolean;
crmAppSlug?: string;
crmOwnerRecordType?: string;
}
| undefined
) {
if (!routing) return null;
return {
routedTeamMemberIds: routing.teamMemberIds,
routingFormResponseId: routing.responseId,
@@ -90,7 +90,7 @@ type DatabaseMetadata = z.infer<typeof bookingMetadataSchema>;
export class OutputBookingsService_2024_08_13 {
constructor(private readonly bookingsRepository: BookingsRepository_2024_08_13) {}
getOutputBooking(databaseBooking: DatabaseBooking) {
async getOutputBooking(databaseBooking: DatabaseBooking) {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes;
@@ -101,6 +101,9 @@ export class OutputBookingsService_2024_08_13 {
);
const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata);
const location = metadata?.videoCallUrl || databaseBooking.location;
const rescheduledToUid = databaseBooking.rescheduled
? await this.getRescheduledToUid(databaseBooking.uid)
: undefined;
const booking = {
id: databaseBooking.id,
@@ -109,8 +112,9 @@ export class OutputBookingsService_2024_08_13 {
description: databaseBooking.description,
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined,
cancelledByEmail: databaseBooking.cancelledBy || undefined,
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
cancelledByEmail: databaseBooking.status === "CANCELLED" ? databaseBooking.cancelledBy : undefined,
reschedulingReason: bookingResponses?.rescheduledReason,
rescheduledByEmail: databaseBooking.rescheduledBy || undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
@@ -137,6 +141,7 @@ export class OutputBookingsService_2024_08_13 {
updatedAt: databaseBooking.updatedAt,
rating: databaseBooking.rating,
icsUid: databaseBooking.iCalUID,
rescheduledToUid,
};
const bookingTransformed = plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
@@ -146,6 +151,11 @@ export class OutputBookingsService_2024_08_13 {
return bookingTransformed;
}
async getRescheduledToUid(bookingUid: string) {
const rescheduledTo = await this.bookingsRepository.getByFromReschedule(bookingUid);
return rescheduledTo?.uid;
}
getUserDefinedMetadata(databaseMetadata: DatabaseMetadata) {
if (databaseMetadata === null) return {};
@@ -204,8 +214,9 @@ export class OutputBookingsService_2024_08_13 {
description: databaseBooking.description,
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined,
cancelledByEmail: databaseBooking.cancelledBy || undefined,
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
cancelledByEmail: databaseBooking.status === "CANCELLED" ? databaseBooking.cancelledBy : undefined,
reschedulingReason: bookingResponses?.rescheduledReason,
rescheduledByEmail: databaseBooking.rescheduledBy || undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
@@ -244,20 +255,23 @@ export class OutputBookingsService_2024_08_13 {
return bookingTransformed;
}
getOutputCreateSeatedBooking(
async getOutputCreateSeatedBooking(
databaseBooking: DatabaseBooking,
seatUid: string
): CreateSeatedBookingOutput_2024_08_13 {
const getSeatedBookingOutput = this.getOutputSeatedBooking(databaseBooking);
): Promise<CreateSeatedBookingOutput_2024_08_13> {
const getSeatedBookingOutput = await this.getOutputSeatedBooking(databaseBooking);
return { ...getSeatedBookingOutput, seatUid };
}
getOutputSeatedBooking(databaseBooking: DatabaseBooking) {
async getOutputSeatedBooking(databaseBooking: DatabaseBooking) {
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
const duration = dateEnd.diff(dateStart, "minutes").minutes;
const metadata = safeParse(bookingMetadataSchema, databaseBooking.metadata, defaultBookingMetadata);
const location = metadata?.videoCallUrl || databaseBooking.location;
const rescheduledToUid = databaseBooking.rescheduled
? await this.getRescheduledToUid(databaseBooking.uid)
: undefined;
const booking = {
id: databaseBooking.id,
@@ -266,7 +280,8 @@ export class OutputBookingsService_2024_08_13 {
description: databaseBooking.description,
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined,
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
start: databaseBooking.startTime,
end: databaseBooking.endTime,
@@ -283,6 +298,7 @@ export class OutputBookingsService_2024_08_13 {
updatedAt: databaseBooking.updatedAt,
rating: databaseBooking.rating,
icsUid: databaseBooking.iCalUID,
rescheduledToUid,
};
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
@@ -375,7 +391,8 @@ export class OutputBookingsService_2024_08_13 {
description: databaseBooking.description,
hosts: [this.getHost(databaseBooking.user)],
status: databaseBooking.status.toLowerCase(),
cancellationReason: databaseBooking.cancellationReason || undefined,
cancellationReason:
databaseBooking.status === "CANCELLED" ? databaseBooking.cancellationReason : undefined,
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
start: databaseBooking.startTime,
end: databaseBooking.endTime,
@@ -124,7 +124,9 @@ export class EventTypesController_2024_04_15 {
let orgSlug = queryParams.org;
if (clientId && !orgSlug && username.includes(`-${clientId}`)) {
const org = await this.organizationsRepository.findTeamIdAndSlugFromClientId(clientId).catch(() => null);
const org = await this.organizationsRepository
.findTeamIdAndSlugFromClientId(clientId)
.catch(() => null);
if (org) {
orgSlug = org.slug;
}
@@ -3,4 +3,5 @@ export enum AuthMethods {
"ACCESS_TOKEN" = "access-token",
"OAUTH_CLIENT" = "oauth-client",
"NEXT_AUTH" = "next-auth",
"THIRD_PARTY_ACCESS_TOKEN" = "third_party_access_token",
}
+1
View File
@@ -41,4 +41,5 @@ export enum Locales {
TA = "ta",
UK = "uk",
ZH_TW = "zh-TW",
BN = "bn",
}
@@ -0,0 +1,28 @@
import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository";
import { PrismaEventTypeRepository } from "@/lib/repositories/prisma-event-type.repository";
import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository";
import { PrismaRoutingFormResponseRepository } from "@/lib/repositories/prisma-routing-form-response.repository";
import { PrismaScheduleRepository } from "@/lib/repositories/prisma-schedule.repository";
import { PrismaSelectedSlotsRepository } from "@/lib/repositories/prisma-selected-slots.repository";
import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository";
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
import { AvailableSlotsService } from "@/lib/services/available-slots.service";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { Module } from "@nestjs/common";
@Module({
imports: [PrismaModule],
providers: [
PrismaOOORepository,
PrismaScheduleRepository,
PrismaBookingRepository,
PrismaSelectedSlotsRepository,
PrismaUserRepository,
PrismaEventTypeRepository,
PrismaRoutingFormResponseRepository,
PrismaTeamRepository,
AvailableSlotsService,
],
exports: [AvailableSlotsService],
})
export class AvailableSlotsModule {}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaBookingRepository as PrismaBookingRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaBookingRepository extends PrismaBookingRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaEventTypeRepository as PrismaEventTypeRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaEventTypeRepository extends PrismaEventTypeRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaOOORepository as PrismaOOORepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaOOORepository extends PrismaOOORepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaRoutingFormResponseRepository as PrismaRoutingFormResponseRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaRoutingFormResponseRepository extends PrismaRoutingFormResponseRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaScheduleRepository as PrismaScheduleRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaScheduleRepository extends PrismaScheduleRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaSelectedSlotsRepository as PrismaSelectedSlotsRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaSelectedSlotsRepository extends PrismaSelectedSlotsRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaTeamRepository as PrismaTeamRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaTeamRepository extends PrismaTeamRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,12 @@
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
import { PrismaUserRepository as PrismaUserRepositoryLib } from "@calcom/platform-libraries/repositories";
import { PrismaClient } from "@calcom/prisma";
@Injectable()
export class PrismaUserRepository extends PrismaUserRepositoryLib {
constructor(private readonly dbWrite: PrismaWriteService) {
super(dbWrite.prisma as unknown as PrismaClient);
}
}
@@ -0,0 +1,36 @@
import { PrismaBookingRepository } from "@/lib/repositories/prisma-booking.repository";
import { PrismaEventTypeRepository } from "@/lib/repositories/prisma-event-type.repository";
import { PrismaOOORepository } from "@/lib/repositories/prisma-ooo.repository";
import { PrismaRoutingFormResponseRepository } from "@/lib/repositories/prisma-routing-form-response.repository";
import { PrismaScheduleRepository } from "@/lib/repositories/prisma-schedule.repository";
import { PrismaSelectedSlotsRepository } from "@/lib/repositories/prisma-selected-slots.repository";
import { PrismaTeamRepository } from "@/lib/repositories/prisma-team.repository";
import { PrismaUserRepository } from "@/lib/repositories/prisma-user.repository";
import { Injectable } from "@nestjs/common";
import { AvailableSlotsService as BaseAvailableSlotsService } from "@calcom/platform-libraries/slots";
@Injectable()
export class AvailableSlotsService extends BaseAvailableSlotsService {
constructor(
private readonly oooRepoDependency: PrismaOOORepository,
private readonly scheduleRepoDependency: PrismaScheduleRepository,
private readonly teamRepository: PrismaTeamRepository,
private readonly routingFormResponseRepository: PrismaRoutingFormResponseRepository,
private readonly bookingRepository: PrismaBookingRepository,
private readonly selectedSlotsRepository: PrismaSelectedSlotsRepository,
private readonly eventTypeRepository: PrismaEventTypeRepository,
private readonly userRepository: PrismaUserRepository
) {
super({
oooRepo: oooRepoDependency,
scheduleRepo: scheduleRepoDependency,
teamRepo: teamRepository,
routingFormResponseRepo: routingFormResponseRepository,
bookingRepo: bookingRepository,
selectedSlotsRepo: selectedSlotsRepository,
eventTypeRepo: eventTypeRepository,
userRepo: userRepository,
});
}
}
@@ -1,5 +1,10 @@
import { Reflector } from "@nestjs/core";
export type AllowedAuthMethod = "OAUTH_CLIENT_CREDENTIALS" | "API_KEY" | "ACCESS_TOKEN" | "NEXT_AUTH";
export type AllowedAuthMethod =
| "OAUTH_CLIENT_CREDENTIALS"
| "API_KEY"
| "ACCESS_TOKEN"
| "NEXT_AUTH"
| "THIRD_PARTY_ACCESS_TOKEN";
export const ApiAuthGuardOnlyAllow = Reflector.createDecorator<AllowedAuthMethod[]>();
@@ -10,16 +10,30 @@ import { ProfilesRepository } from "@/modules/profiles/profiles.repository";
import { TokensRepository } from "@/modules/tokens/tokens.repository";
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile, UsersRepository } from "@/modules/users/users.repository";
import { Injectable, InternalServerErrorException, UnauthorizedException } from "@nestjs/common";
import {
HttpException,
Injectable,
InternalServerErrorException,
UnauthorizedException,
} from "@nestjs/common";
import { Logger } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { PassportStrategy } from "@nestjs/passport";
import type { Request } from "express";
import * as jwt from "jsonwebtoken";
import { getToken } from "next-auth/jwt";
import { INVALID_ACCESS_TOKEN, X_CAL_CLIENT_ID, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
import type { AllowedAuthMethod } from "../../decorators/api-auth-guard-only-allow.decorator";
interface OAuthTokenPayload {
userId?: number;
teamId?: number;
scope: string[];
token_type: string;
}
export type ApiAuthGuardUser = UserWithProfile & { isSystemAdmin: boolean };
export type ApiAuthGuardRequest = Request & {
authMethod: AuthMethods;
@@ -29,8 +43,11 @@ export type ApiAuthGuardRequest = Request & {
};
export const NO_AUTH_PROVIDED_MESSAGE =
"No authentication method provided. Either pass an API key as 'Bearer' header or OAuth client credentials as 'x-cal-secret-key' and 'x-cal-client-id' headers";
@Injectable()
export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth") {
private readonly logger = new Logger("ApiAuthStrategy");
constructor(
private readonly deploymentsService: DeploymentsService,
private readonly config: ConfigService,
@@ -54,22 +71,53 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
const allowedMethods = request.allowedAuthMethods;
const noSpecificAuthExpected = !allowedMethods || !allowedMethods.length;
const oAuthAllowed = noSpecificAuthExpected || allowedMethods.includes("OAUTH_CLIENT_CREDENTIALS");
const apiKeyAllowed = noSpecificAuthExpected || allowedMethods.includes("API_KEY");
const accessTokenAllowed = noSpecificAuthExpected || allowedMethods.includes("ACCESS_TOKEN");
const nextAuthAllowed = noSpecificAuthExpected || allowedMethods.includes("NEXT_AUTH");
const thirdPartyAccessTokenAllowed =
noSpecificAuthExpected || allowedMethods.includes("THIRD_PARTY_ACCESS_TOKEN");
if (oAuthClientId && oAuthClientSecret && oAuthAllowed) {
request.authMethod = AuthMethods["OAUTH_CLIENT"];
return await this.authenticateOAuthClient(oAuthClientId, oAuthClientSecret, request);
}
if (bearerToken && (apiKeyAllowed || accessTokenAllowed)) {
const requestOrigin = request.get("Origin");
request.authMethod = isApiKey(bearerToken, this.config.get<string>("api.apiKeyPrefix") ?? "cal_")
? AuthMethods["API_KEY"]
: AuthMethods["ACCESS_TOKEN"];
return await this.authenticateBearerToken(bearerToken, request, requestOrigin);
if (bearerToken) {
if (!apiKeyAllowed && !accessTokenAllowed && thirdPartyAccessTokenAllowed) {
request.authMethod = AuthMethods["THIRD_PARTY_ACCESS_TOKEN"];
const result = await this.validateThirdPartyAccessToken(bearerToken, request);
if (result.success) {
return this.success(this.getSuccessUser(result.data));
}
}
if (apiKeyAllowed || accessTokenAllowed) {
try {
const requestOrigin = request.get("Origin");
request.authMethod = isApiKey(bearerToken, this.config.get<string>("api.apiKeyPrefix") ?? "cal_")
? AuthMethods["API_KEY"]
: AuthMethods["ACCESS_TOKEN"];
return await this.authenticateBearerToken(bearerToken, request, requestOrigin);
} catch (err) {
// failed to validate access token, try to validate third party token
if (thirdPartyAccessTokenAllowed && request.authMethod === AuthMethods["ACCESS_TOKEN"]) {
request.authMethod = AuthMethods["THIRD_PARTY_ACCESS_TOKEN"];
const result = await this.validateThirdPartyAccessToken(bearerToken, request);
if (result.success) {
return this.success(this.getSuccessUser(result.data));
}
}
// token was not third party token, rethrow error from authenticateBearerToken
if (err instanceof Error) {
return this.error(err);
}
}
}
throw new UnauthorizedException(`ApiAuthStrategy - Invalid Bearer token`);
}
const nextAuthSecret = this.config.get("next.authSecret", { infer: true });
@@ -83,8 +131,11 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
if (noAuthProvided) {
throw new UnauthorizedException(`ApiAuthStrategy - ${NO_AUTH_PROVIDED_MESSAGE}`);
}
throw new UnauthorizedException(
`ApiAuthStrategy - Invalid authentication method. Please provide one of the allowed methods: ${allowedMethods}`
`ApiAuthStrategy - Invalid authentication method. Please provide one of the allowed methods: ${
allowedMethods && allowedMethods.length > 0 ? allowedMethods.join(", ") : "Any supported method"
}`
);
} catch (err) {
if (err instanceof Error) {
@@ -164,21 +215,17 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
: await this.accessTokenStrategy(authString, request, requestOrigin);
if (!user) {
return this.error(
new UnauthorizedException(
"ApiAuthStrategy - bearer token - No user associated with the provided token"
)
throw new UnauthorizedException(
"ApiAuthStrategy - bearer token - No user associated with the provided token"
);
}
return this.success(this.getSuccessUser(user));
} catch (err) {
if (err instanceof Error) {
return this.error(err);
throw err;
}
return this.error(
new InternalServerErrorException("An error occurred while authenticating the request")
);
throw new InternalServerErrorException("An error occurred while authenticating the request");
}
}
@@ -271,4 +318,55 @@ export class ApiAuthStrategy extends PassportStrategy(BaseStrategy, "api-auth")
return user;
}
async validateThirdPartyAccessToken(
token: string,
request: ApiAuthGuardRequest
): Promise<{ success: true; data: UserWithProfile } | { success: false }> {
// Removed requiredScopes parameter
const encryptionKey = this.config.get<string>("CALENDSO_ENCRYPTION_KEY");
if (!encryptionKey) {
throw new InternalServerErrorException("CALENDSO_ENCRYPTION_KEY environment variable is not set.");
}
let decodedToken: OAuthTokenPayload;
try {
decodedToken = jwt.verify(token, encryptionKey) as OAuthTokenPayload;
} catch (e) {
return { success: false };
}
if (!decodedToken || decodedToken.token_type !== "Access Token") {
return { success: false };
}
let user: UserWithProfile | null = null;
let organizationId: number | null = null;
if (decodedToken.userId) {
user = await this.userRepository.findByIdWithProfile(decodedToken.userId);
if (user) {
organizationId = this.usersService.getUserMainOrgId(user) as number;
}
} else if (decodedToken.teamId) {
const teamOwner = await this.userRepository.findOwnerByTeamIdWithProfile(decodedToken.teamId);
if (!teamOwner) {
throw new UnauthorizedException(
"ApiAuthStrategy - third-party token - No owner found for the associated team."
);
}
user = teamOwner;
organizationId =
teamOwner.profiles?.find((p) => p.organizationId === decodedToken.teamId)?.organizationId ?? null;
}
if (!user) {
throw new UnauthorizedException(
"ApiAuthStrategy - third-party token - No user or team owner associated with the token."
);
}
request.organizationId = organizationId;
return { success: true, data: user };
}
}
@@ -34,7 +34,7 @@ export class CalUnifiedCalendarsController {
@UseGuards(ApiAuthGuard, PermissionsGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary: "Get meeting Details from calendar",
summary: "Get meeting details from calendar",
description: "Returns detailed information about a meeting including attendance metrics",
})
async getCalendarEventDetails(
@@ -304,8 +304,10 @@ describe("Assign all team members", () => {
expect(data.title).toEqual(body.title);
expect(data.hosts.length).toEqual(2);
expect(data.schedulingType).toEqual("collective");
evaluateHost({ userId: firstManagedUser.user.id }, data.hosts[0]);
evaluateHost({ userId: secondManagedUser.user.id }, data.hosts[1]);
const dataFirstHost = data.hosts.find((host) => host.userId === firstManagedUser.user.id);
const dataSecondHost = data.hosts.find((host) => host.userId === secondManagedUser.user.id);
evaluateHost({ userId: firstManagedUser.user.id }, dataFirstHost);
evaluateHost({ userId: secondManagedUser.user.id }, dataSecondHost);
const eventTypeHosts = await hostsRepositoryFixture.getEventTypeHosts(data.id);
expect(eventTypeHosts.length).toEqual(2);
@@ -341,13 +343,15 @@ describe("Assign all team members", () => {
expect(data.title).toEqual(body.title);
expect(data.hosts.length).toEqual(2);
expect(data.schedulingType).toEqual("roundRobin");
const dataFirstHost = data.hosts.find((host) => host.userId === firstManagedUser.user.id);
const dataSecondHost = data.hosts.find((host) => host.userId === secondManagedUser.user.id);
evaluateHost(
{ userId: firstManagedUser.user.id, mandatory: false, priority: "medium" },
data.hosts[0]
dataFirstHost
);
evaluateHost(
{ userId: secondManagedUser.user.id, mandatory: false, priority: "medium" },
data.hosts[1]
dataSecondHost
);
const eventTypeHosts = await hostsRepositoryFixture.getEventTypeHosts(data.id);
@@ -381,13 +385,15 @@ describe("Assign all team members", () => {
expect(data.title).toEqual(body.title);
expect(data.hosts.length).toEqual(2);
expect(data.schedulingType).toEqual("roundRobin");
const dataFirstHost = data.hosts.find((host) => host.userId === firstManagedUser.user.id);
const dataSecondHost = data.hosts.find((host) => host.userId === secondManagedUser.user.id);
evaluateHost(
{ userId: firstManagedUser.user.id, mandatory: false, priority: "medium" },
data.hosts[0]
dataFirstHost
);
evaluateHost(
{ userId: secondManagedUser.user.id, mandatory: false, priority: "medium" },
data.hosts[1]
dataSecondHost
);
const eventTypeHosts = await hostsRepositoryFixture.getEventTypeHosts(data.id);
@@ -43,7 +43,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
teamId: number | null;
userId: number | null;
title: string;
}
};
let membershipsRepositoryFixture: MembershipRepositoryFixture;
beforeAll(async () => {
const moduleRef = await Test.createTestingModule({
@@ -130,24 +130,24 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
operator: "equal",
value: ["answer1"],
valueSrc: ["value"],
valueType: ["text"]
}
}
}
valueType: ["text"],
},
},
},
},
action: {
type: "eventTypeRedirectUrl",
eventTypeId: routingEventType.id,
value: `team/${team.slug}/${routingEventType.slug}`
value: `team/${team.slug}/${routingEventType.slug}`,
},
isFallback: false
isFallback: false,
},
{
id: "fallback-route",
action: { type: "customPageMessage", value: "Fallback Message" },
isFallback: true,
queryValue: { id: "fallback-route", type: "group" }
}
queryValue: { id: "fallback-route", type: "group" },
},
],
fields: [
{
@@ -155,18 +155,18 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
type: "text",
label: "Question 1",
required: true,
identifier: "question1"
identifier: "question1",
},
{
id: "question2",
id: "question2",
type: "text",
label: "Question 2",
required: false,
identifier: "question2"
}
identifier: "question2",
},
],
settings: {
emailOwnerOnSubmission: false
emailOwnerOnSubmission: false,
},
teamId: team.id,
userId: user.id,
@@ -245,7 +245,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
describe(`POST /v2/organizations/:orgId/routing-forms/:routingFormId/responses`, () => {
it("should return 403 when organization does not exist", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/99999/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/99999/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1",
@@ -255,7 +257,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should return 404 when routing form does not exist", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/non-existent-id/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/non-existent-id/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1",
@@ -265,7 +269,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should return 401 when authentication token is missing", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.send({
question1: "answer1",
})
@@ -274,7 +280,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should create response and return available slots when routing to event type", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1", // This matches the route condition
@@ -296,7 +304,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should return 400 when required form fields are missing", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question2: "answer2", // Missing required question1
@@ -306,7 +316,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should create response and return custom message if the routing is to custom page", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "different-answer", // This won't match any route
@@ -334,7 +346,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
})
.expect(400);
// Missing end parameter
// Missing end parameter
await request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05`)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
@@ -347,7 +359,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should return 400 when date parameters have invalid format", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=invalid-date&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=invalid-date&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1",
@@ -357,7 +371,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should return 400 when end date is before start date", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-10&end=2050-09-05`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-10&end=2050-09-05`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1",
@@ -371,10 +387,15 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
email: `unauthorized-user-${randomString()}@api.com`,
});
const { keyString: unauthorizedApiKey } = await apiKeysRepositoryFixture.createApiKey(unauthorizedUser.id, null);
const { keyString: unauthorizedApiKey } = await apiKeysRepositoryFixture.createApiKey(
unauthorizedUser.id,
null
);
const response = await request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${unauthorizedApiKey}` })
.send({
question1: "answer1",
@@ -390,7 +411,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
it("should handle queued response creation", async () => {
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06&queueResponse=true`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06&queueResponse=true`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1",
@@ -426,24 +449,24 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
operator: "equal",
value: ["answer1"],
valueSrc: ["value"],
valueType: ["text"]
}
}
}
valueType: ["text"],
},
},
},
},
action: {
type: "eventTypeRedirectUrl",
eventTypeId: 99999, // Invalid event type ID
value: `team/${team.slug}/non-existent-event-type`
value: `team/${team.slug}/non-existent-event-type`,
},
isFallback: false
isFallback: false,
},
{
id: "fallback-route",
action: { type: "customPageMessage", value: "Fallback Message" },
isFallback: true,
queryValue: { id: "fallback-route", type: "group" }
}
queryValue: { id: "fallback-route", type: "group" },
},
],
fields: [
{
@@ -451,11 +474,11 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
type: "text",
label: "Question 1",
required: true,
identifier: "question1"
}
identifier: "question1",
},
],
settings: {
emailOwnerOnSubmission: false
emailOwnerOnSubmission: false,
},
teamId: team.id,
userId: user.id,
@@ -464,7 +487,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
// Try to create a response for the form with invalid event type
const response = await request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingFormWithInvalidEventType.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingFormWithInvalidEventType.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1",
@@ -474,7 +499,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
// Clean up the form
await prismaWriteService.prisma.app_RoutingForms_Form.delete({
where: { id: routingFormWithInvalidEventType.id }
where: { id: routingFormWithInvalidEventType.id },
});
});
@@ -482,7 +507,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
// This test verifies that routing forms can handle team member assignments
// and that the routing returns the correct team member information
return request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${routingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "answer1", // This matches the route condition
@@ -524,23 +551,23 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
operator: "equal",
value: ["external"],
valueSrc: ["value"],
valueType: ["text"]
}
}
}
valueType: ["text"],
},
},
},
},
action: {
type: "externalRedirectUrl",
value: "https://example.com/external-booking"
value: "https://example.com/external-booking",
},
isFallback: false
isFallback: false,
},
{
id: "fallback-route",
action: { type: "customPageMessage", value: "Fallback Message" },
isFallback: true,
queryValue: { id: "fallback-route", type: "group" }
}
queryValue: { id: "fallback-route", type: "group" },
},
],
fields: [
{
@@ -548,11 +575,11 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
type: "text",
label: "Question 1",
required: true,
identifier: "question1"
}
identifier: "question1",
},
],
settings: {
emailOwnerOnSubmission: false
emailOwnerOnSubmission: false,
},
teamId: team.id,
userId: user.id,
@@ -560,7 +587,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
});
const response = await request(app.getHttpServer())
.post(`/v2/organizations/${org.id}/routing-forms/${externalRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06`)
.post(
`/v2/organizations/${org.id}/routing-forms/${externalRoutingForm.id}/responses?start=2050-09-05&end=2050-09-06`
)
.set({ Authorization: `Bearer cal_test_${apiKeyString}` })
.send({
question1: "external", // This matches the route condition for external redirect
@@ -574,7 +603,7 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
expect(data.routingExternalRedirectUrl).toBeDefined();
expect(data.routingExternalRedirectUrl).toContain("https://example.com/external-booking");
expect(data.routingExternalRedirectUrl).toContain("cal.action=externalRedirectUrl");
// Verify that it doesn't contain event type routing data
expect(data.eventTypeId).toBeUndefined();
expect(data.slots).toBeUndefined();
@@ -583,10 +612,9 @@ describe("OrganizationsRoutingFormsResponsesController", () => {
// Clean up the external routing form
await prismaWriteService.prisma.app_RoutingForms_Form.delete({
where: { id: externalRoutingForm.id }
where: { id: externalRoutingForm.id },
});
});
});
describe(`PATCH /v2/organizations/:orgId/routing-forms/:routingFormId/responses/:responseId`, () => {
@@ -6,9 +6,9 @@ import { GetAvailableSlotsInput_2024_09_04 } from "@calcom/platform-types";
export class CreateRoutingFormResponseInput extends GetAvailableSlotsInput_2024_09_04 {
@Transform(({ value }: { value: string | boolean }) => {
if (typeof value === 'boolean') return value;
if (typeof value === 'string') {
return value.toLowerCase() === 'true';
if (typeof value === "boolean") return value;
if (typeof value === "string") {
return value.toLowerCase() === "true";
}
return undefined;
})
@@ -20,4 +20,4 @@ export class CreateRoutingFormResponseInput extends GetAvailableSlotsInput_2024_
example: true,
})
queueResponse?: boolean;
}
}
@@ -2,7 +2,12 @@ import { ApiProperty, ApiPropertyOptional, getSchemaPath } from "@nestjs/swagger
import { Type } from "class-transformer";
import { IsArray, IsBoolean, IsInt, IsNumber, IsOptional, IsString, ValidateNested } from "class-validator";
import { ApiResponseWithoutData, SlotsOutput_2024_09_04, RangeSlotsOutput_2024_09_04 } from "@calcom/platform-types";
import {
ApiResponseWithoutData,
SlotsOutput_2024_09_04,
RangeSlotsOutput_2024_09_04,
} from "@calcom/platform-types";
class Routing {
@ApiProperty({
type: String,
@@ -133,4 +138,4 @@ export class CreateRoutingFormResponseOutput extends ApiResponseWithoutData {
@ApiProperty({ type: CreateRoutingFormResponseOutputData })
@Type(() => CreateRoutingFormResponseOutputData)
data!: CreateRoutingFormResponseOutputData;
}
}
@@ -2,7 +2,12 @@ import { OrganizationsRoutingFormsRepository } from "@/modules/organizations/rou
import { OrganizationsTeamsRoutingFormsResponsesOutputService } from "@/modules/organizations/teams/routing-forms/services/organizations-teams-routing-forms-responses-output.service";
import { SlotsService_2024_09_04 } from "@/modules/slots/slots-2024-09-04/services/slots.service";
import { TeamsEventTypesRepository } from "@/modules/teams/event-types/teams-event-types.repository";
import { BadRequestException, Injectable, NotFoundException, InternalServerErrorException } from "@nestjs/common";
import {
BadRequestException,
Injectable,
NotFoundException,
InternalServerErrorException,
} from "@nestjs/common";
import { Request } from "express";
import { getRoutedUrl } from "@calcom/platform-libraries";
@@ -71,10 +76,14 @@ export class OrganizationsRoutingFormsResponsesService {
request: Request
): Promise<CreateRoutingFormResponseOutputData> {
const { queueResponse, ...slotsQuery } = query;
this.validateDateRange(slotsQuery.start, slotsQuery.end);
const { redirectUrl, customMessage } = await this.getRoutingUrl(request, routingFormId, queueResponse ?? false);
const { redirectUrl, customMessage } = await this.getRoutingUrl(
request,
routingFormId,
queueResponse ?? false
);
// If there is no redirect URL, then we have to show the message as that would be custom page message to be shown as per the route chosen
if (!redirectUrl) {
@@ -86,7 +95,7 @@ export class OrganizationsRoutingFormsResponsesService {
if (!this.isEventTypeRedirectUrl(redirectUrl)) {
return {
routingExternalRedirectUrl: redirectUrl.toString(),
}
};
}
// Extract event type information from the routed URL
@@ -123,9 +132,11 @@ export class OrganizationsRoutingFormsResponsesService {
slots,
};
}
if (!queuedResponseId) {
throw new InternalServerErrorException("No routing form response ID or queued form response ID could be found.");
throw new InternalServerErrorException(
"No routing form response ID or queued form response ID could be found."
);
}
return {
@@ -145,7 +156,7 @@ export class OrganizationsRoutingFormsResponsesService {
private validateDateRange(start: string, end: string) {
const startDate = new Date(start);
const endDate = new Date(end);
if (endDate < startDate) {
throw new BadRequestException("End date cannot be before start date.");
}
@@ -153,10 +164,13 @@ export class OrganizationsRoutingFormsResponsesService {
private async getRoutingUrl(request: Request, formId: string, queueResponse: boolean) {
const params = Object.fromEntries(new URLSearchParams(request.body));
const routedUrlData = await getRoutedUrl({
req: request,
query: { ...params, form: formId, ...(queueResponse && { "cal.queueFormResponse": "true" }) },
}, true);
const routedUrlData = await getRoutedUrl(
{
req: request,
query: { ...params, form: formId, ...(queueResponse && { "cal.queueFormResponse": "true" }) },
},
true
);
if (routedUrlData.notFound) {
throw new NotFoundException("Routing form not found.");
@@ -186,8 +200,6 @@ export class OrganizationsRoutingFormsResponsesService {
}
private async extractEventTypeAndCrmParams(routingUrl: URL) {
// Extract team and event type information
const { teamId, eventTypeSlug } = this.extractTeamIdAndEventTypeSlugFromRedirectUrl(routingUrl);
const eventType = await this.teamsEventTypesRepository.getEventTypeByTeamIdAndSlug(teamId, eventTypeSlug);
@@ -53,7 +53,7 @@ export type OAuthCallbackState = {
path: "/v2/organizations/:orgId/teams/:teamId/stripe",
version: API_VERSIONS_VALUES,
})
@DocsTags("Organizations/Teams Stripe")
@DocsTags("Orgs / Teams / Stripe")
export class OrganizationsStripeController {
constructor(
private readonly organizationsStripeService: OrganizationsStripeService,
@@ -80,7 +80,7 @@ export class OrganizationsTeamsBookingsController {
IsAdminAPIEnabledGuard
)
@ApiOperation({
summary: "Get 'Booking References' for a booking",
summary: "Get booking references",
})
@HttpCode(HttpStatus.OK)
async getBookingReferences(
@@ -20,7 +20,14 @@ import { OrganizationsTeamsRoutingFormsResponsesService } from "./services/organ
import { OrganizationsTeamsRoutingFormsService } from "./services/organizations-teams-routing-forms.service";
@Module({
imports: [PrismaModule, StripeModule, RedisModule, RoutingFormsModule, SlotsModule_2024_09_04, TeamsEventTypesModule],
imports: [
PrismaModule,
StripeModule,
RedisModule,
RoutingFormsModule,
SlotsModule_2024_09_04,
TeamsEventTypesModule,
],
providers: [
OrganizationsTeamsRoutingFormsService,
OrganizationsTeamsRoutingFormsResponsesService,
@@ -52,8 +52,8 @@ import { SkipTakePagination } from "@calcom/platform-types";
export class OrgTeamsVerifiedResourcesController {
constructor(private readonly verifiedResourcesService: VerifiedResourcesService) {}
@ApiOperation({
summary: "Request Email Verification Code",
description: `Sends a verification code to the email.`,
summary: "Request email verification code",
description: `Sends a verification code to the email`,
})
@Roles("TEAM_ADMIN")
@Throttle({
@@ -82,8 +82,8 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Request Phone Number Verification Code",
description: `Sends a verification code to the phone number.`,
summary: "Request phone number verification code",
description: `Sends a verification code to the phone number`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -108,7 +108,7 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Verify an email for an org team.",
summary: "Verify an email for an org team",
description: `Use code to verify an email`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -134,7 +134,7 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Verify a phone number for an org team.",
summary: "Verify a phone number for an org team",
description: `Use code to verify a phone number`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -166,7 +166,7 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get list of verified emails of an org team.",
summary: "Get list of verified emails of an org team",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -189,7 +189,7 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get list of verified phone numbers of an org team.",
summary: "Get list of verified phone numbers of an org team",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@PlatformPlan("ESSENTIALS")
@@ -214,7 +214,7 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get verified email of an org team by id.",
summary: "Get verified email of an org team by id",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -233,7 +233,7 @@ export class OrgTeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get verified phone number of an org team by id.",
summary: "Get verified phone number of an org team by id",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -25,7 +25,6 @@ export class RouterController {
@Param("formId") formId: string,
@Body() body?: Record<string, string>
): Promise<void | (ApiResponse<unknown> & { redirect: boolean })> {
const params = Object.fromEntries(new URLSearchParams(body ?? {}));
const routedUrlData = await getRoutedUrl({ req: request, query: { ...params, form: formId } });
if (routedUrlData?.notFound) {
@@ -1,4 +1,5 @@
import { TRPC_ERROR_CODE, TRPC_ERROR_MAP, TRPCErrorCode } from "@/filters/trpc-exception.filter";
import { AvailableSlotsService } from "@/lib/services/available-slots.service";
import { SlotsOutputService_2024_04_15 } from "@/modules/slots/slots-2024-04-15/services/slots-output.service";
import type { RangeSlots, TimeSlots } from "@/modules/slots/slots-2024-04-15/services/slots-output.service";
import { SlotsWorkerService_2024_04_15 } from "@/modules/slots/slots-2024-04-15/services/slots-worker.service";
@@ -17,7 +18,6 @@ import {
VERSION_2024_08_13,
} from "@calcom/platform-constants";
import { TRPCError } from "@calcom/platform-libraries";
import { getAvailableSlots } from "@calcom/platform-libraries/slots";
import { RemoveSelectedSlotInput_2024_04_15, ReserveSlotInput_2024_04_15 } from "@calcom/platform-types";
import { ApiResponse, GetAvailableSlotsInput_2024_04_15 } from "@calcom/platform-types";
@@ -31,7 +31,8 @@ export class SlotsController_2024_04_15 {
private readonly slotsService: SlotsService_2024_04_15,
private readonly config: ConfigService,
private readonly slotsOutputService: SlotsOutputService_2024_04_15,
private readonly slotsWorkerService: SlotsWorkerService_2024_04_15
private readonly slotsWorkerService: SlotsWorkerService_2024_04_15,
private readonly availableSlotsService: AvailableSlotsService
) {}
@Post("/reserve")
@@ -170,8 +171,10 @@ export class SlotsController_2024_04_15 {
: query.isTeamEvent;
// Do not use workers in E2E, not supported by TS-JEST
const availableSlots = this.config.get<boolean>("e2e")
? await getAvailableSlots({
const availableSlotsService = this.config.get<boolean>("e2e") ? this.availableSlotsService : null;
const availableSlots = availableSlotsService
? await availableSlotsService.getAvailableSlots({
input: {
...query,
isTeamEvent,
@@ -1,4 +1,5 @@
import { EventTypesModule_2024_04_15 } from "@/ee/event-types/event-types_2024_04_15/event-types.module";
import { AvailableSlotsModule } from "@/lib/modules/available-slots.module";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { SlotsController_2024_04_15 } from "@/modules/slots/slots-2024-04-15/controllers/slots.controller";
import { SlotsOutputService_2024_04_15 } from "@/modules/slots/slots-2024-04-15/services/slots-output.service";
@@ -8,7 +9,7 @@ import { SlotsRepository_2024_04_15 } from "@/modules/slots/slots-2024-04-15/slo
import { Module } from "@nestjs/common";
@Module({
imports: [PrismaModule, EventTypesModule_2024_04_15],
imports: [PrismaModule, EventTypesModule_2024_04_15, AvailableSlotsModule],
providers: [
SlotsRepository_2024_04_15,
SlotsService_2024_04_15,
@@ -0,0 +1,23 @@
// apps/api/v2/src/modules/slots/slots-2024-04-15/workers/slots-worker.module.ts
// Needed if ConfigService is used anywhere
import appConfig from "@/config/app";
import { AvailableSlotsModule } from "@/lib/modules/available-slots.module";
import { PrismaModule } from "@/modules/prisma/prisma.module";
import { Module, Logger } from "@nestjs/common";
import { ConfigModule } from "@nestjs/config";
import { SentryModule } from "@sentry/nestjs/setup";
@Module({
imports: [
SentryModule.forRoot(),
ConfigModule.forRoot({
ignoreEnvFile: true,
isGlobal: true,
load: [appConfig],
}),
PrismaModule,
AvailableSlotsModule,
],
providers: [Logger],
})
export class SlotsWorkerModule {}
@@ -1,16 +1,65 @@
import { parentPort } from "worker_threads";
import { AvailableSlotsService } from "@/lib/services/available-slots.service";
import { Logger } from "@nestjs/common";
import { NestFactory } from "@nestjs/core";
import { IncomingMessage } from "http";
import { parentPort, isMainThread } from "worker_threads";
import { getAvailableSlots } from "@calcom/platform-libraries/slots";
import { SlotsWorkerModule } from "./slots.worker.module";
parentPort?.on("message", async (data) => {
try {
const { input, ctx } = data;
const result = await getAvailableSlots({ input, ctx });
parentPort?.postMessage({ success: true, data: result });
} catch (error) {
parentPort?.postMessage({
success: false,
error: error instanceof Error ? error : new Error("Unknown error"),
});
interface ContextForGetSchedule extends Record<string, unknown> {
req?: (IncomingMessage & { cookies: Partial<{ [key: string]: string }> }) | undefined;
}
interface WorkerMessage {
input: any; // Use a more specific type if available, e.g., TGetScheduleInputSchema
ctx?: ContextForGetSchedule;
}
interface WorkerResult {
success: boolean;
data?: any; // Use TimeSlots
error?: {
message: string;
code?: string;
stack?: string;
};
}
async function bootstrapWorkerApp() {
if (isMainThread) {
console.error("slots-worker-main.ts should only be run in a worker thread.");
process.exit(1);
}
});
const app = await NestFactory.createApplicationContext(SlotsWorkerModule);
const logger = app.get(Logger); // Get the NestJS Logger instance
logger.log("NestJS worker application bootstrapped successfully.");
const availableSlotsService = app.get(AvailableSlotsService);
parentPort?.on("message", async (data: WorkerMessage) => {
try {
const { input, ctx } = data;
const result = await availableSlotsService.getAvailableSlots({ input, ctx });
parentPort?.postMessage({ success: true, data: result } as WorkerResult);
} catch (error: any) {
parentPort?.postMessage({
success: false,
error: error instanceof Error ? error : new Error("Unknown error"),
});
}
});
parentPort?.on("close", async () => {
logger.log("Worker port closed. Shutting down NestJS worker application.");
await app.close();
});
parentPort?.on("disconnect", async () => {
logger.log("Worker port disconnected. Shutting down NestJS worker application.");
await app.close();
});
}
bootstrapWorkerApp();
@@ -1,4 +1,5 @@
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
import { AvailableSlotsService } from "@/lib/services/available-slots.service";
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
import { MembershipsService } from "@/modules/memberships/services/memberships.service";
import { TimeSlots } from "@/modules/slots/slots-2024-04-15/services/slots-output.service";
@@ -17,7 +18,6 @@ import {
import { DateTime } from "luxon";
import { z } from "zod";
import { getAvailableSlots } from "@calcom/platform-libraries/slots";
import { GetSlotsInput_2024_09_04, ReserveSlotInput_2024_09_04 } from "@calcom/platform-types";
import { Booking, EventType } from "@calcom/prisma/client";
@@ -38,13 +38,14 @@ export class SlotsService_2024_09_04 {
private readonly slotsInputService: SlotsInputService_2024_09_04,
private readonly membershipsService: MembershipsService,
private readonly membershipsRepository: MembershipsRepository,
private readonly teamsRepository: TeamsRepository
private readonly teamsRepository: TeamsRepository,
private readonly availableSlotsService: AvailableSlotsService
) {}
async getAvailableSlots(query: GetSlotsInput_2024_09_04) {
try {
const queryTransformed = await this.slotsInputService.transformGetSlotsQuery(query);
const availableSlots: TimeSlots = await getAvailableSlots({
const availableSlots: TimeSlots = await this.availableSlotsService.getAvailableSlots({
input: {
...queryTransformed,
routingFormResponseId: queryTransformed.routingFormResponseId ?? undefined,
@@ -1,4 +1,5 @@
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
import { AvailableSlotsModule } from "@/lib/modules/available-slots.module";
import { MembershipsModule } from "@/modules/memberships/memberships.module";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { OrganizationsTeamsRepository } from "@/modules/organizations/teams/index/organizations-teams.repository";
@@ -23,6 +24,7 @@ import { Module } from "@nestjs/common";
TeamsModule,
MembershipsModule,
TeamsEventTypesModule,
AvailableSlotsModule,
],
providers: [
SlotsRepository_2024_09_04,
@@ -47,8 +47,8 @@ import { SkipTakePagination } from "@calcom/platform-types";
export class TeamsVerifiedResourcesController {
constructor(private readonly verifiedResourcesService: VerifiedResourcesService) {}
@ApiOperation({
summary: "Request Email Verification Code",
description: `Sends a verification code to the Email.`,
summary: "Request email verification code",
description: `Sends a verification code to the Email`,
})
@Roles("TEAM_ADMIN")
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -71,8 +71,8 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Request Phone Number Verification Code",
description: `Sends a verification code to the phone number.`,
summary: "Request phone number verification code",
description: `Sends a verification code to the phone number`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -92,7 +92,7 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Verify an email for a team.",
summary: "Verify an email for a team",
description: `Use code to verify an email`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -117,7 +117,7 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Verify a phone number for an org team.",
summary: "Verify a phone number for an org team",
description: `Use code to verify a phone number`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -143,7 +143,7 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get list of verified emails of a team.",
summary: "Get list of verified emails of a team",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -165,7 +165,7 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get list of verified phone numbers of a team.",
summary: "Get list of verified phone numbers of a team",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/phones")
@@ -189,7 +189,7 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get verified email of a team by id.",
summary: "Get verified email of a team by id",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -207,7 +207,7 @@ export class TeamsVerifiedResourcesController {
}
@ApiOperation({
summary: "Get verified phone number of a team by id.",
summary: "Get verified phone number of a team by id",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Roles("TEAM_ADMIN")
@@ -85,6 +85,27 @@ export class UsersRepository {
});
}
async findOwnerByTeamIdWithProfile(teamId: number): Promise<UserWithProfile | null> {
return this.dbRead.prisma.user.findFirst({
where: {
teams: {
some: {
teamId,
role: "OWNER",
},
},
},
include: {
movedToProfile: {
include: { organization: { select: { isPlatform: true, name: true, slug: true, id: true } } },
},
profiles: {
include: { organization: { select: { isPlatform: true, name: true, slug: true, id: true } } },
},
},
});
}
async findByIdsWithEventTypes(userIds: number[]) {
return this.dbRead.prisma.user.findMany({
where: {
@@ -26,6 +26,7 @@ export class LocaleValidator implements ValidatorConstraintInterface {
"sr",
"sv",
"vi",
"bn",
];
if (localeValues.includes(locale)) return true;
@@ -34,8 +34,8 @@ import { SkipTakePagination } from "@calcom/platform-types";
export class UserVerifiedResourcesController {
constructor(private readonly verifiedResourcesService: VerifiedResourcesService) {}
@ApiOperation({
summary: "Request Email Verification Code",
description: `Sends a verification code to the email.`,
summary: "Request email verification code",
description: `Sends a verification code to the email`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Post("/emails/verification-code/request")
@@ -57,8 +57,8 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Request Phone Number Verification Code",
description: `Sends a verification code to the phone number.`,
summary: "Request phone number verification code",
description: `Sends a verification code to the phone number`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Post("/phones/verification-code/request")
@@ -77,7 +77,7 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Verify an email.",
summary: "Verify an email",
description: `Use code to verify an email`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -96,7 +96,7 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Verify a phone number.",
summary: "Verify a phone number",
description: `Use code to verify a phone number`,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@@ -114,7 +114,7 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Get list of verified emails.",
summary: "Get list of verified emails",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/emails")
@@ -135,7 +135,7 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Get list of verified phone numbers.",
summary: "Get list of verified phone numbers",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/phones")
@@ -158,7 +158,7 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Get verified email by id.",
summary: "Get verified email by id",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/emails/:id")
@@ -175,7 +175,7 @@ export class UserVerifiedResourcesController {
}
@ApiOperation({
summary: "Get verified phone number by id.",
summary: "Get verified phone number by id",
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/phones/:id")
+328 -192
View File
@@ -3447,7 +3447,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/bookings/{bookingUid}/references": {
"get": {
"operationId": "OrganizationsTeamsBookingsController_getBookingReferences",
"summary": "Get 'Booking References' for a booking",
"summary": "Get booking references",
"parameters": [
{
"name": "Authorization",
@@ -5133,6 +5133,147 @@
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/stripe/connect": {
"get": {
"operationId": "OrganizationsStripeController_getTeamStripeConnectUrl",
"summary": "Get Stripe connect URL for a team",
"parameters": [
{
"name": "Authorization",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "teamId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "orgId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "returnTo",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "onErrorReturnTo",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StripConnectOutputResponseDto"
}
}
}
}
},
"tags": [
"Orgs / Teams / Stripe"
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/stripe/check": {
"get": {
"operationId": "OrganizationsStripeController_checkTeamStripeConnection",
"summary": "Check team Stripe connection",
"parameters": [
{
"name": "teamId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StripCredentialsCheckOutputResponseDto"
}
}
}
}
},
"tags": [
"Orgs / Teams / Stripe"
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/stripe/save": {
"get": {
"operationId": "OrganizationsStripeController_save",
"summary": "Save Stripe credentials",
"parameters": [
{
"name": "state",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "code",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "teamId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StripCredentialsSaveOutputResponseDto"
}
}
}
}
},
"tags": [
"Orgs / Teams / Stripe"
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/users/{userId}/schedules": {
"get": {
"operationId": "OrganizationsTeamsSchedulesController_getUserSchedules",
@@ -8176,7 +8317,7 @@
"/v2/bookings/{bookingUid}/references": {
"get": {
"operationId": "BookingsController_2024_08_13_getBookingReferences",
"summary": "Get 'Booking References' for a booking",
"summary": "Get booking references",
"parameters": [
{
"name": "cal-api-version",
@@ -8244,7 +8385,7 @@
"/v2/calendars/{calendar}/event/{eventUid}": {
"get": {
"operationId": "CalUnifiedCalendarsController_getCalendarEventDetails",
"summary": "Get meeting Details from calendar",
"summary": "Get meeting details from calendar",
"description": "Returns detailed information about a meeting including attendance metrics",
"parameters": [
{
@@ -10194,8 +10335,8 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails/verification-code/request": {
"post": {
"operationId": "OrgTeamsVerifiedResourcesController_requestEmailVerificationCode",
"summary": "Request Email Verification Code",
"description": "Sends a verification code to the email.",
"summary": "Request email verification code",
"description": "Sends a verification code to the email",
"parameters": [
{
"name": "Authorization",
@@ -10237,8 +10378,8 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones/verification-code/request": {
"post": {
"operationId": "OrgTeamsVerifiedResourcesController_requestPhoneVerificationCode",
"summary": "Request Phone Number Verification Code",
"description": "Sends a verification code to the phone number.",
"summary": "Request phone number verification code",
"description": "Sends a verification code to the phone number",
"parameters": [
{
"name": "Authorization",
@@ -10280,7 +10421,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails/verification-code/verify": {
"post": {
"operationId": "OrgTeamsVerifiedResourcesController_verifyEmail",
"summary": "Verify an email for an org team.",
"summary": "Verify an email for an org team",
"description": "Use code to verify an email",
"parameters": [
{
@@ -10331,7 +10472,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones/verification-code/verify": {
"post": {
"operationId": "OrgTeamsVerifiedResourcesController_verifyPhoneNumber",
"summary": "Verify a phone number for an org team.",
"summary": "Verify a phone number for an org team",
"description": "Use code to verify a phone number",
"parameters": [
{
@@ -10382,7 +10523,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails": {
"get": {
"operationId": "OrgTeamsVerifiedResourcesController_getVerifiedEmails",
"summary": "Get list of verified emails of an org team.",
"summary": "Get list of verified emails of an org team",
"parameters": [
{
"name": "teamId",
@@ -10447,7 +10588,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones": {
"get": {
"operationId": "OrgTeamsVerifiedResourcesController_getVerifiedPhoneNumbers",
"summary": "Get list of verified phone numbers of an org team.",
"summary": "Get list of verified phone numbers of an org team",
"parameters": [
{
"name": "teamId",
@@ -10512,7 +10653,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/emails/{id}": {
"get": {
"operationId": "OrgTeamsVerifiedResourcesController_getVerifiedEmailById",
"summary": "Get verified email of an org team by id.",
"summary": "Get verified email of an org team by id",
"parameters": [
{
"name": "id",
@@ -10560,7 +10701,7 @@
"/v2/organizations/{orgId}/teams/{teamId}/verified-resources/phones/{id}": {
"get": {
"operationId": "OrgTeamsVerifiedResourcesController_getVerifiedPhoneById",
"summary": "Get verified phone number of an org team by id.",
"summary": "Get verified phone number of an org team by id",
"parameters": [
{
"name": "teamId",
@@ -10605,147 +10746,6 @@
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/stripe/connect": {
"get": {
"operationId": "OrganizationsStripeController_getTeamStripeConnectUrl",
"summary": "Get Stripe connect URL for a team",
"parameters": [
{
"name": "Authorization",
"required": true,
"in": "header",
"schema": {
"type": "string"
}
},
{
"name": "teamId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "orgId",
"required": true,
"in": "path",
"schema": {
"type": "string"
}
},
{
"name": "returnTo",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "onErrorReturnTo",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StripConnectOutputResponseDto"
}
}
}
}
},
"tags": [
"Organizations/Teams Stripe"
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/stripe/check": {
"get": {
"operationId": "OrganizationsStripeController_checkTeamStripeConnection",
"summary": "Check team Stripe connection",
"parameters": [
{
"name": "teamId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StripCredentialsCheckOutputResponseDto"
}
}
}
}
},
"tags": [
"Organizations/Teams Stripe"
]
}
},
"/v2/organizations/{orgId}/teams/{teamId}/stripe/save": {
"get": {
"operationId": "OrganizationsStripeController_save",
"summary": "Save Stripe credentials",
"parameters": [
{
"name": "state",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "code",
"required": true,
"in": "query",
"schema": {
"type": "string"
}
},
{
"name": "teamId",
"required": true,
"in": "path",
"schema": {
"type": "number"
}
}
],
"responses": {
"200": {
"description": "",
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/StripCredentialsSaveOutputResponseDto"
}
}
}
}
},
"tags": [
"Organizations/Teams Stripe"
]
}
},
"/v2/routing-forms/{routingFormId}/calculate-slots": {
"post": {
"operationId": "RoutingFormsController_calculateSlotsBasedOnRoutingFormResponse",
@@ -12519,8 +12519,8 @@
"/v2/teams/{teamId}/verified-resources/emails/verification-code/request": {
"post": {
"operationId": "TeamsVerifiedResourcesController_requestEmailVerificationCode",
"summary": "Request Email Verification Code",
"description": "Sends a verification code to the Email.",
"summary": "Request email verification code",
"description": "Sends a verification code to the Email",
"parameters": [
{
"name": "Authorization",
@@ -12562,8 +12562,8 @@
"/v2/teams/{teamId}/verified-resources/phones/verification-code/request": {
"post": {
"operationId": "TeamsVerifiedResourcesController_requestPhoneVerificationCode",
"summary": "Request Phone Number Verification Code",
"description": "Sends a verification code to the phone number.",
"summary": "Request phone number verification code",
"description": "Sends a verification code to the phone number",
"parameters": [
{
"name": "Authorization",
@@ -12605,7 +12605,7 @@
"/v2/teams/{teamId}/verified-resources/emails/verification-code/verify": {
"post": {
"operationId": "TeamsVerifiedResourcesController_verifyEmail",
"summary": "Verify an email for a team.",
"summary": "Verify an email for a team",
"description": "Use code to verify an email",
"parameters": [
{
@@ -12656,7 +12656,7 @@
"/v2/teams/{teamId}/verified-resources/phones/verification-code/verify": {
"post": {
"operationId": "TeamsVerifiedResourcesController_verifyPhoneNumber",
"summary": "Verify a phone number for an org team.",
"summary": "Verify a phone number for an org team",
"description": "Use code to verify a phone number",
"parameters": [
{
@@ -12707,7 +12707,7 @@
"/v2/teams/{teamId}/verified-resources/emails": {
"get": {
"operationId": "TeamsVerifiedResourcesController_getVerifiedEmails",
"summary": "Get list of verified emails of a team.",
"summary": "Get list of verified emails of a team",
"parameters": [
{
"name": "teamId",
@@ -12772,7 +12772,7 @@
"/v2/teams/{teamId}/verified-resources/phones": {
"get": {
"operationId": "TeamsVerifiedResourcesController_getVerifiedPhoneNumbers",
"summary": "Get list of verified phone numbers of a team.",
"summary": "Get list of verified phone numbers of a team",
"parameters": [
{
"name": "teamId",
@@ -12837,7 +12837,7 @@
"/v2/teams/{teamId}/verified-resources/emails/{id}": {
"get": {
"operationId": "TeamsVerifiedResourcesController_getVerifiedEmailById",
"summary": "Get verified email of a team by id.",
"summary": "Get verified email of a team by id",
"parameters": [
{
"name": "id",
@@ -12885,7 +12885,7 @@
"/v2/teams/{teamId}/verified-resources/phones/{id}": {
"get": {
"operationId": "TeamsVerifiedResourcesController_getVerifiedPhoneById",
"summary": "Get verified phone number of a team by id.",
"summary": "Get verified phone number of a team by id",
"parameters": [
{
"name": "teamId",
@@ -12933,8 +12933,8 @@
"/v2/verified-resources/emails/verification-code/request": {
"post": {
"operationId": "UserVerifiedResourcesController_requestEmailVerificationCode",
"summary": "Request Email Verification Code",
"description": "Sends a verification code to the email.",
"summary": "Request email verification code",
"description": "Sends a verification code to the email",
"parameters": [
{
"name": "Authorization",
@@ -12976,8 +12976,8 @@
"/v2/verified-resources/phones/verification-code/request": {
"post": {
"operationId": "UserVerifiedResourcesController_requestPhoneVerificationCode",
"summary": "Request Phone Number Verification Code",
"description": "Sends a verification code to the phone number.",
"summary": "Request phone number verification code",
"description": "Sends a verification code to the phone number",
"parameters": [
{
"name": "Authorization",
@@ -13019,7 +13019,7 @@
"/v2/verified-resources/emails/verification-code/verify": {
"post": {
"operationId": "UserVerifiedResourcesController_verifyEmail",
"summary": "Verify an email.",
"summary": "Verify an email",
"description": "Use code to verify an email",
"parameters": [
{
@@ -13062,7 +13062,7 @@
"/v2/verified-resources/phones/verification-code/verify": {
"post": {
"operationId": "UserVerifiedResourcesController_verifyPhoneNumber",
"summary": "Verify a phone number.",
"summary": "Verify a phone number",
"description": "Use code to verify a phone number",
"parameters": [
{
@@ -13105,7 +13105,7 @@
"/v2/verified-resources/emails": {
"get": {
"operationId": "UserVerifiedResourcesController_getVerifiedEmails",
"summary": "Get list of verified emails.",
"summary": "Get list of verified emails",
"parameters": [
{
"name": "take",
@@ -13162,7 +13162,7 @@
"/v2/verified-resources/phones": {
"get": {
"operationId": "UserVerifiedResourcesController_getVerifiedPhoneNumbers",
"summary": "Get list of verified phone numbers.",
"summary": "Get list of verified phone numbers",
"parameters": [
{
"name": "take",
@@ -13219,7 +13219,7 @@
"/v2/verified-resources/emails/{id}": {
"get": {
"operationId": "UserVerifiedResourcesController_getVerifiedEmailById",
"summary": "Get verified email by id.",
"summary": "Get verified email by id",
"parameters": [
{
"name": "id",
@@ -13259,7 +13259,7 @@
"/v2/verified-resources/phones/{id}": {
"get": {
"operationId": "UserVerifiedResourcesController_getVerifiedPhoneById",
"summary": "Get verified phone number by id.",
"summary": "Get verified phone number by id",
"parameters": [
{
"name": "id",
@@ -13614,7 +13614,8 @@
"sk",
"ta",
"uk",
"zh-TW"
"zh-TW",
"bn"
],
"type": "string",
"example": "en"
@@ -13750,7 +13751,8 @@
"sk",
"ta",
"uk",
"zh-TW"
"zh-TW",
"bn"
],
"type": "string",
"example": "en"
@@ -13930,7 +13932,8 @@
"sk",
"ta",
"uk",
"zh-TW"
"zh-TW",
"bn"
],
"type": "string",
"example": "en"
@@ -23972,7 +23975,7 @@
"type": "object",
"properties": {}
},
"Attendee": {
"CreateBookingAttendee": {
"type": "object",
"properties": {
"name": {
@@ -24039,7 +24042,8 @@
"sk",
"ta",
"uk",
"zh-TW"
"zh-TW",
"bn"
],
"description": "The preferred language of the attendee. Used for booking confirmation.",
"example": "it",
@@ -24108,7 +24112,7 @@
"description": "The attendee's details.",
"allOf": [
{
"$ref": "#/components/schemas/Attendee"
"$ref": "#/components/schemas/CreateBookingAttendee"
}
]
},
@@ -24235,7 +24239,7 @@
"description": "The attendee's details.",
"allOf": [
{
"$ref": "#/components/schemas/Attendee"
"$ref": "#/components/schemas/CreateBookingAttendee"
}
]
},
@@ -24368,7 +24372,7 @@
"description": "The attendee's details.",
"allOf": [
{
"$ref": "#/components/schemas/Attendee"
"$ref": "#/components/schemas/CreateBookingAttendee"
}
]
},
@@ -24537,6 +24541,86 @@
"slug"
]
},
"BookingAttendee": {
"type": "object",
"properties": {
"name": {
"type": "string",
"example": "John Doe"
},
"email": {
"type": "string",
"example": "john@example.com"
},
"timeZone": {
"type": "string",
"example": "America/New_York"
},
"language": {
"type": "string",
"enum": [
"ar",
"ca",
"de",
"es",
"eu",
"he",
"id",
"ja",
"lv",
"pl",
"ro",
"sr",
"th",
"vi",
"az",
"cs",
"el",
"es-419",
"fi",
"hr",
"it",
"km",
"nl",
"pt",
"ru",
"sv",
"tr",
"zh-CN",
"bg",
"da",
"en",
"et",
"fr",
"hu",
"iw",
"ko",
"no",
"pt-BR",
"sk",
"ta",
"uk",
"zh-TW",
"bn"
],
"example": "en"
},
"absent": {
"type": "boolean",
"example": false
},
"phoneNumber": {
"type": "string",
"example": "+1234567890"
}
},
"required": [
"name",
"email",
"timeZone",
"absent"
]
},
"BookingOutput_2024_08_13": {
"type": "object",
"properties": {
@@ -24590,7 +24674,13 @@
},
"rescheduledFromUid": {
"type": "string",
"example": "previous_uid_123"
"example": "previous_uid_123",
"description": "UID of the previous booking from which this booking was rescheduled."
},
"rescheduledToUid": {
"type": "string",
"example": "new_uid_456",
"description": "UID of the new booking to which this booking was rescheduled."
},
"start": {
"type": "string",
@@ -24653,7 +24743,7 @@
"attendees": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Attendee"
"$ref": "#/components/schemas/BookingAttendee"
}
},
"guests": {
@@ -24747,7 +24837,13 @@
},
"rescheduledFromUid": {
"type": "string",
"example": "previous_uid_123"
"example": "previous_uid_123",
"description": "UID of the previous booking from which this booking was rescheduled."
},
"rescheduledToUid": {
"type": "string",
"example": "new_uid_456",
"description": "UID of the new booking to which this booking was rescheduled."
},
"start": {
"type": "string",
@@ -24810,7 +24906,7 @@
"attendees": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Attendee"
"$ref": "#/components/schemas/BookingAttendee"
}
},
"guests": {
@@ -24915,7 +25011,8 @@
"sk",
"ta",
"uk",
"zh-TW"
"zh-TW",
"bn"
],
"example": "en"
},
@@ -25007,7 +25104,13 @@
},
"rescheduledFromUid": {
"type": "string",
"example": "previous_uid_123"
"example": "previous_uid_123",
"description": "UID of the previous booking from which this booking was rescheduled."
},
"rescheduledToUid": {
"type": "string",
"example": "new_uid_456",
"description": "UID of the new booking to which this booking was rescheduled."
},
"start": {
"type": "string",
@@ -25151,7 +25254,13 @@
},
"rescheduledFromUid": {
"type": "string",
"example": "previous_uid_123"
"example": "previous_uid_123",
"description": "UID of the previous booking from which this booking was rescheduled."
},
"rescheduledToUid": {
"type": "string",
"example": "new_uid_456",
"description": "UID of the new booking to which this booking was rescheduled."
},
"start": {
"type": "string",
@@ -25340,7 +25449,13 @@
},
"rescheduledFromUid": {
"type": "string",
"example": "previous_uid_123"
"example": "previous_uid_123",
"description": "UID of the previous booking from which this booking was rescheduled."
},
"rescheduledToUid": {
"type": "string",
"example": "new_uid_456",
"description": "UID of the new booking to which this booking was rescheduled."
},
"start": {
"type": "string",
@@ -25479,7 +25594,13 @@
},
"rescheduledFromUid": {
"type": "string",
"example": "previous_uid_123"
"example": "previous_uid_123",
"description": "UID of the previous booking from which this booking was rescheduled."
},
"rescheduledToUid": {
"type": "string",
"example": "new_uid_456",
"description": "UID of the new booking to which this booking was rescheduled."
},
"start": {
"type": "string",
@@ -25921,6 +26042,21 @@
"data"
]
},
"MarkAbsentAttendee": {
"type": "object",
"properties": {
"email": {
"type": "string"
},
"absent": {
"type": "boolean"
}
},
"required": [
"email",
"absent"
]
},
"MarkAbsentBookingInput_2024_08_13": {
"type": "object",
"properties": {
@@ -25932,7 +26068,7 @@
"attendees": {
"type": "array",
"items": {
"$ref": "#/components/schemas/Attendee"
"$ref": "#/components/schemas/MarkAbsentAttendee"
}
}
}
+2 -1
View File
@@ -25,7 +25,8 @@
"@calcom/platform-libraries/schedules": ["../../../packages/platform/libraries/schedules.ts"],
"@calcom/platform-libraries/app-store": ["../../../packages/platform/libraries/app-store.ts"],
"@calcom/platform-libraries/workflows": ["../../../packages/platform/libraries/workflows.ts"],
"@calcom/platform-libraries/conferencing": ["../../../packages/platform/libraries/conferencing.ts"]
"@calcom/platform-libraries/conferencing": ["../../../packages/platform/libraries/conferencing.ts"],
"@calcom/platform-libraries/repositories": ["../../../packages/platform/libraries/repositories.ts"]
},
"incremental": true,
"skipLibCheck": true,
+1 -1
View File
@@ -30,7 +30,7 @@
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^9",
"eslint": "^8.34.0",
"eslint-config-next": "15.1.6",
"postcss": "^8",
"tailwindcss": "^3.4.1",
@@ -11,6 +11,15 @@ import { getServerSideProps } from "@server/lib/[user]/[type]/getServerSideProps
import TypePage, { type PageProps as ClientPageProps } from "~/users/views/users-type-public-view";
export const generateMetadata = async () => {
return {
robots: {
follow: false,
index: false,
},
};
};
const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
@@ -8,6 +8,15 @@ import { getServerSideProps } from "@server/lib/[user]/getServerSideProps";
import User, { type PageProps as ClientPageProps } from "~/users/views/users-public-view";
export const generateMetadata = async () => {
return {
robots: {
follow: false,
index: false,
},
};
};
const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
@@ -17,6 +17,15 @@ const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
export type ClientPageProps = UserTypePageProps | TeamTypePageProps;
export const generateMetadata = async () => {
return {
robots: {
follow: false,
index: false,
},
};
};
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
const context = buildLegacyCtx(await headers(), await cookies(), await params, await searchParams);
const props = await getData(context);
@@ -14,6 +14,15 @@ const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
export type ClientPageProps = UserPageProps | TeamPageProps;
export const generateMetadata = async () => {
return {
robots: {
follow: false,
index: false,
},
};
};
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
const context = buildLegacyCtx(await headers(), await cookies(), await params, await searchParams);
const props = await getData(context);
@@ -10,6 +10,15 @@ import { getServerSideProps } from "@lib/team/[slug]/[type]/getServerSideProps";
import TypePage, { type PageProps as ClientPageProps } from "~/team/type-view";
export const generateMetadata = async () => {
return {
robots: {
follow: false,
index: false,
},
};
};
const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
@@ -7,6 +7,15 @@ import { getServerSideProps } from "@lib/team/[slug]/getServerSideProps";
import TeamPage, { type PageProps as ClientPageProps } from "~/team/team-view";
export const generateMetadata = async () => {
return {
robots: {
follow: false,
index: false,
},
};
};
const getData = withEmbedSsrAppDir<ClientPageProps>(getServerSideProps);
const ServerPage = async ({ params, searchParams }: ServerPageProps) => {
@@ -6,6 +6,7 @@ import { unstable_cache } from "next/cache";
import { TeamsListing } from "@calcom/features/ee/teams/components/TeamsListing";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import { TeamService } from "@calcom/lib/server/service/team";
import prisma from "@calcom/prisma";
import { meRouter } from "@calcom/trpc/server/routers/viewer/me/_router";
import { TRPCError } from "@trpc/server";
@@ -14,7 +15,8 @@ import { TeamsCTA } from "./CTA";
const getCachedTeams = unstable_cache(
async (userId: number) => {
return await TeamRepository.findTeamsByUserId({
const teamRepo = new TeamRepository(prisma);
return await teamRepo.findTeamsByUserId({
userId,
includeOrgs: true,
});
@@ -4,6 +4,7 @@ import { cookies, headers } from "next/headers";
import { getAppRegistry, getAppRegistryWithCredentials } from "@calcom/app-store/_appRegistry";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma from "@calcom/prisma";
import type { AppCategories } from "@calcom/prisma/enums";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
@@ -25,7 +26,8 @@ const ServerPage = async () => {
const session = await getServerSession({ req });
let appStore, userAdminTeamsIds: number[];
if (session?.user?.id) {
const userAdminTeams = await UserRepository.getUserAdminTeams(session.user.id);
const userRepo = new UserRepository(prisma);
const userAdminTeams = await userRepo.getUserAdminTeams({ userId: session.user.id });
userAdminTeamsIds = userAdminTeams?.teams?.map(({ team }) => team.id) ?? [];
appStore = await getAppRegistryWithCredentials(session.user.id, userAdminTeamsIds);
} else {
@@ -6,6 +6,7 @@ import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequir
import { UsersEditView } from "@calcom/features/ee/users/pages/users-edit-view";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma from "@calcom/prisma";
const userIdSchema = z.object({ id: z.coerce.number() });
@@ -21,7 +22,8 @@ export const generateMetadata = async ({ params }: { params: Params }) => {
);
}
const user = await UserRepository.adminFindById(input.data.id);
const userRepo = new UserRepository(prisma);
const user = await userRepo.adminFindById(input.data.id);
return await _generateMetadata(
(t) => `${t("editing_user")}: ${user.username}`,
@@ -37,7 +39,8 @@ const Page = async ({ params }: { params: Params }) => {
if (!input.success) throw new Error("Invalid access");
const user = await UserRepository.adminFindById(input.data.id);
const userRepo = new UserRepository(prisma);
const user = await userRepo.adminFindById(input.data.id);
const t = await getTranslate();
return (
@@ -6,6 +6,7 @@ import { notFound } from "next/navigation";
import { z } from "zod";
import { BookingRepository } from "@calcom/lib/server/repository/booking";
import { prisma } from "@calcom/prisma";
import { buildLegacyCtx } from "@lib/buildLegacyCtx";
import { getServerSideProps } from "@lib/video/meeting-not-started/[uid]/getServerSideProps";
@@ -22,7 +23,8 @@ export const generateMetadata = async ({ params }: ServerPageProps) => {
if (!parsed.success) {
notFound();
}
const booking = await BookingRepository.findBookingByUid({
const bookingRepo = new BookingRepository(prisma);
const booking = await bookingRepo.findBookingByUid({
bookingUid: parsed.data.uid,
});
@@ -11,6 +11,7 @@ import { HttpError } from "@calcom/lib/http-error";
import notEmpty from "@calcom/lib/notEmpty";
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/selectedCalendar";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma from "@calcom/prisma";
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
@@ -29,7 +30,10 @@ async function authMiddleware() {
throw new HttpError({ statusCode: 401, message: "Not authenticated" });
}
const userWithCredentials = await UserRepository.findUserWithCredentials({ id: session.user.id });
const userRepo = new UserRepository(prisma);
const userWithCredentials = await userRepo.findUserWithCredentials({
id: session.user.id,
});
if (!userWithCredentials) {
throw new HttpError({ statusCode: 401, message: "Not authenticated" });
+4 -2
View File
@@ -8,6 +8,7 @@ import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { timeZoneSchema } from "@calcom/lib/dayjs/timeZone.schema";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma from "@calcom/prisma";
import { userMetadata } from "@calcom/prisma/zod-utils";
import { CardComponent } from "@lib/plain/card-components";
@@ -474,7 +475,8 @@ async function handler(request: NextRequest) {
// Validate request body
const { cardKeys, customer } = inputSchema.parse(requestBody);
const user = await UserRepository.findByEmail({ email: customer.email });
const userRepo = new UserRepository(prisma);
const user = await userRepo.findByEmail({ email: customer.email });
if (!user) {
return NextResponse.json({
@@ -502,7 +504,7 @@ async function handler(request: NextRequest) {
}
// Fetch team details including userId and team name
const teamMemberships = await UserRepository.findTeamsByUserId({ userId: user.id });
const teamMemberships = await userRepo.findTeamsByUserId({ userId: user.id });
const firstTeam = teamMemberships.teams[0] ?? null;
// Parse user metadata
@@ -1,4 +1,4 @@
import { describe, it, expect, vi } from "vitest";
import { beforeEach, describe, it, expect, vi } from "vitest";
import { onSubmissionOfFormResponse } from "@calcom/app-store/routing-forms/lib/formSubmissionUtils";
import { getResponseToStore } from "@calcom/app-store/routing-forms/lib/getResponseToStore";
@@ -8,6 +8,11 @@ import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/for
import { queuedResponseHandler } from "../route";
vi.mock("@calcom/lib/server/repository/formResponse");
const mockRoutingFormResponseRepository = {
getQueuedFormResponseFromId: vi.fn(),
recordFormResponse: vi.fn(),
};
vi.mock("@calcom/app-store/routing-forms/lib/getSerializableForm");
vi.mock("@calcom/app-store/routing-forms/lib/getResponseToStore");
vi.mock("@calcom/app-store/routing-forms/lib/formSubmissionUtils");
@@ -43,8 +48,14 @@ const mockQueuedFormResponse = {
};
describe("queuedResponseHandler", () => {
beforeEach(() => {
vi.mocked(RoutingFormResponseRepository).mockImplementation(
() => mockRoutingFormResponseRepository as any
);
});
it("should process a queued form response", async () => {
vi.mocked(RoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(
vi.mocked(mockRoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(
mockQueuedFormResponse
);
@@ -90,7 +101,7 @@ describe("queuedResponseHandler", () => {
team: null,
} as unknown as Awaited<ReturnType<typeof onSubmissionOfFormResponse>>);
vi.mocked(RoutingFormResponseRepository.recordFormResponse).mockResolvedValue({
vi.mocked(mockRoutingFormResponseRepository.recordFormResponse).mockResolvedValue({
id: "mock-form-id",
name: "Test Form",
description: "Test Form Description",
@@ -102,7 +113,7 @@ describe("queuedResponseHandler", () => {
},
team: null,
chosenRouteId: "mock-chosen-route-id",
} as unknown as Awaited<ReturnType<typeof RoutingFormResponseRepository.recordFormResponse>>);
} as unknown as Awaited<ReturnType<typeof mockRoutingFormResponseRepository.recordFormResponse>>);
const response = await queuedResponseHandler({
queuedFormResponseId: "1",
@@ -115,7 +126,7 @@ describe("queuedResponseHandler", () => {
});
it("if no queued form response is found, should return early", async () => {
vi.mocked(RoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(null);
vi.mocked(mockRoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(null);
const response = await queuedResponseHandler({
queuedFormResponseId: "1",
params: {},
@@ -127,7 +138,7 @@ describe("queuedResponseHandler", () => {
});
it("should throw if form has no fields", async () => {
vi.mocked(RoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(
vi.mocked(mockRoutingFormResponseRepository.getQueuedFormResponseFromId).mockResolvedValue(
mockQueuedFormResponse
);
vi.mocked(getSerializableForm).mockResolvedValue({
@@ -8,6 +8,7 @@ import { getSerializableForm } from "@calcom/app-store/routing-forms/lib/getSeri
import logger from "@calcom/lib/logger";
import { safeStringify } from "@calcom/lib/safeStringify";
import { RoutingFormResponseRepository } from "@calcom/lib/server/repository/formResponse";
import prisma from "@calcom/prisma";
import { defaultResponderForAppDir } from "../../defaultResponderForAppDir";
@@ -23,10 +24,10 @@ export const queuedResponseHandler = async ({
queuedFormResponseId: string;
params: Record<string, string | string[]>;
}) => {
const formResponseRepo = new RoutingFormResponseRepository(prisma);
// Get the queued response
const queuedFormResponse = await RoutingFormResponseRepository.getQueuedFormResponseFromId(
queuedFormResponseId
);
const queuedFormResponse = await formResponseRepo.getQueuedFormResponseFromId(queuedFormResponseId);
if (!queuedFormResponse) {
return {
@@ -48,7 +49,7 @@ export const queuedResponseHandler = async ({
fieldsResponses: params,
});
const formResponse = await RoutingFormResponseRepository.recordFormResponse({
const formResponse = await formResponseRepo.recordFormResponse({
formId: queuedFormResponse.formId,
queuedFormResponseId: queuedFormResponse.id,
// We record new response here as that might be different from the queued response depending on if the user changed something in b/w before clicking CTA and that something wasn't prerendered
@@ -199,7 +199,7 @@ export default function CancelBooking(props: Props) {
data-testid="confirm_cancel"
disabled={
props.isHost &&
(!cancellationReason || (props.internalNotePresets.length > 0 && !internalNote?.id))
(!cancellationReason?.trim() || (props.internalNotePresets.length > 0 && !internalNote?.id))
}
onClick={async () => {
setLoading(true);
@@ -440,7 +440,11 @@ describe("RerouteDialog", () => {
render(
<SessionProvider session={mockSession}>
<TooltipProvider>
<RerouteDialog isOpenDialog={true} setIsOpenDialog={mockSetIsOpenDialog} booking={mockBooking} />
<RerouteDialog
isOpenDialog={true}
setIsOpenDialog={mockSetIsOpenDialog}
booking={mockBooking}
/>
</TooltipProvider>
</SessionProvider>
);
@@ -483,7 +487,11 @@ describe("RerouteDialog", () => {
render(
<SessionProvider session={mockSession}>
<TooltipProvider>
<RerouteDialog isOpenDialog={true} setIsOpenDialog={mockSetIsOpenDialog} booking={mockBooking} />
<RerouteDialog
isOpenDialog={true}
setIsOpenDialog={mockSetIsOpenDialog}
booking={mockBooking}
/>
</TooltipProvider>
</SessionProvider>
);
+17 -2
View File
@@ -9,6 +9,7 @@ import { isPasswordValid } from "@calcom/features/auth/lib/isPasswordValid";
import { WEBSITE_URL } from "@calcom/lib/constants";
import { emailRegex } from "@calcom/lib/emailSchema";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Button } from "@calcom/ui/components/button";
import { EmptyScreen } from "@calcom/ui/components/empty-screen";
import { EmailField, Label, TextField, PasswordField } from "@calcom/ui/components/form";
@@ -34,7 +35,12 @@ export const AdminUserContainer = (props: React.ComponentProps<typeof AdminUser>
return <AdminUser {...props} />;
};
export const AdminUser = (props: { onSubmit: () => void; onError: () => void; onSuccess: () => void }) => {
export const AdminUser = (props: {
onSubmit: () => void;
onError: () => void;
onSuccess: () => void;
nav: { onNext: () => void; onPrev: () => void };
}) => {
const { t } = useLocale();
const formSchema = z.object({
@@ -118,7 +124,7 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
<TextField
addOnLeading={
!longWebsiteUrl && (
<span className="text-subtle inline-flex items-center rounded-none px-3 text-sm">
<span className="text-subtle inline-flex items-center rounded-none text-sm">
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
</span>
)
@@ -187,6 +193,15 @@ export const AdminUser = (props: { onSubmit: () => void; onError: () => void; on
)}
/>
</div>
<div className="flex justify-end gap-2">
<Button
type="submit"
color="primary"
loading={formMethods.formState.isSubmitting}
disabled={!formMethods.formState.isValid || formMethods.formState.isSubmitting}>
{t("next")}
</Button>
</div>
</form>
</FormProvider>
);
@@ -0,0 +1,255 @@
"use client";
import { zodResolver } from "@hookform/resolvers/zod";
import * as RadioGroup from "@radix-ui/react-radio-group";
import classNames from "classnames";
import Link from "next/link";
import { useCallback, useState } from "react";
import { Controller, FormProvider, useForm, useFormState } from "react-hook-form";
import { z } from "zod";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { RouterInputs, RouterOutputs } from "@calcom/trpc/react";
import { trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui/components/button";
import { TextField } from "@calcom/ui/components/form";
import { Icon } from "@calcom/ui/components/icon";
type LicenseSelectionFormValues = {
licenseKey: string;
signatureToken?: string;
};
type LicenseOption = "FREE" | "EXISTING";
const LicenseSelection = (
props: {
value: LicenseOption;
onChange: (value: LicenseOption) => void;
onSubmit: (value: LicenseSelectionFormValues) => void;
onSuccess?: (
data: RouterOutputs["viewer"]["deploymentSetup"]["update"],
variables: RouterInputs["viewer"]["deploymentSetup"]["update"]
) => void;
onPrevStep: () => void;
onNextStep: () => void;
} & Omit<JSX.IntrinsicElements["form"], "onSubmit" | "onChange">
) => {
const {
value: initialValue = "FREE",
onChange,
onSubmit,
onSuccess,
onPrevStep,
onNextStep,
...rest
} = props;
const [value, setValue] = useState<LicenseOption>(initialValue);
const { t } = useLocale();
const mutation = trpc.viewer.deploymentSetup.update.useMutation({
onSuccess,
});
const [licenseKeyInput, setLicenseKeyInput] = useState("");
const [licenseTouched, setLicenseTouched] = useState(false);
// Use TRPC query for validation
const { data: licenseValidation, isLoading: checkLicenseLoading } =
trpc.viewer.deploymentSetup.validateLicense.useQuery(
{ licenseKey: licenseKeyInput },
{
enabled: licenseTouched && licenseKeyInput.trim().length > 0,
}
);
const schemaLicenseKey = useCallback(
() =>
z.object({
licenseKey: z
.string()
.min(1, t("license_key_required"))
.refine(() => !licenseTouched || (licenseValidation ? licenseValidation.valid : true), {
message: licenseValidation?.message || t("invalid_license_key"),
}),
signatureToken: z.string().optional(),
}),
[licenseValidation, licenseTouched, t]
);
const formMethods = useForm<LicenseSelectionFormValues>({
defaultValues: {
licenseKey: "",
signatureToken: "",
},
resolver: zodResolver(schemaLicenseKey()),
});
const handleSubmit = formMethods.handleSubmit((values) => {
onSubmit(values);
if (value === "EXISTING" && values.licenseKey) {
mutation.mutate(values);
}
});
const { isDirty, errors } = useFormState(formMethods);
const handleRadioChange = (newValue: LicenseOption) => {
setValue(newValue);
onChange(newValue);
};
return (
<form
{...rest}
className="space-y-6"
onSubmit={(e) => {
e.preventDefault();
if (value === "FREE") {
onSubmit({ licenseKey: "", signatureToken: "" });
} else {
handleSubmit();
}
}}>
<RadioGroup.Root
defaultValue={initialValue}
value={value}
aria-label={t("choose_a_license")}
className="grid grid-rows-2 gap-4 md:grid-cols-2 md:grid-rows-1"
onValueChange={(value) => handleRadioChange(value as LicenseOption)}>
<RadioGroup.Item value="FREE" className="h-full">
<div
className={classNames(
"bg-default h-full cursor-pointer space-y-2 rounded-md border p-4 hover:border-black",
value === "FREE" && "ring-2 ring-black"
)}>
<h2 className="font-cal text-emphasis text-xl">{t("agplv3_license")}</h2>
<p className="font-medium text-green-800">{t("free_license_fee")}</p>
<p className="text-subtle">{t("forever_open_and_free")}</p>
<ul className="text-subtle ml-4 list-disc text-left text-xs">
<li>{t("required_to_keep_your_code_open_source")}</li>
<li>{t("cannot_repackage_and_resell")}</li>
<li>{t("no_enterprise_features")}</li>
</ul>
</div>
</RadioGroup.Item>
<RadioGroup.Item value="EXISTING" className="h-full">
<div
className={classNames(
"bg-default h-full cursor-pointer space-y-2 rounded-md border p-4 hover:border-black",
value === "EXISTING" && "ring-2 ring-black"
)}>
<h2 className="font-cal text-emphasis text-xl">{t("enter_license_key")}</h2>
<p className="font-medium text-green-800">{t("enter_existing_license")}</p>
<p className="text-subtle">{t("enter_your_license_key")}</p>
<p className="text-subtle text-xs">
{t("need_a_license")}{" "}
<Link
href="https://go.cal.com/self-hosted"
target="_blank"
rel="noreferrer noopener"
className="text-blue-600 hover:underline">
{t("purchase_license")}
</Link>
</p>
</div>
</RadioGroup.Item>
</RadioGroup.Root>
{value === "EXISTING" && (
<FormProvider {...formMethods}>
<div className="bg-muted space-y-4 rounded-md px-4 py-3">
<div className="space-y-4">
<div>
<Controller
name="licenseKey"
control={formMethods.control}
render={({ field: { onBlur, onChange, value } }) => (
<TextField
name="licenseKey"
label={t("license_key")}
className={classNames(
"group-hover:border-emphasis mb-0",
(checkLicenseLoading || (errors.licenseKey === undefined && isDirty)) && "border-r-0"
)}
placeholder="cal_live_XXXXXXXXXXX"
value={licenseKeyInput}
addOnClassname={classNames(
"hover:border-default",
errors.licenseKey === undefined && isDirty && "group-hover:border-emphasis"
)}
addOnSuffix={
checkLicenseLoading ? (
<Icon name="loader" className="h-5 w-5 animate-spin" />
) : licenseValidation?.valid && licenseTouched ? (
<Icon name="check" className="h-5 w-5 text-green-700" />
) : undefined
}
color={errors.licenseKey ? "warn" : ""}
onBlur={(e) => {
setLicenseTouched(true);
onBlur();
}}
onChange={(e) => {
setLicenseKeyInput(e.target.value);
onChange(e.target.value);
}}
/>
)}
/>
{errors.licenseKey && (
<p className="mt-1 text-sm text-red-600">{errors.licenseKey.message}</p>
)}
</div>
<div>
<Controller
name="signatureToken"
control={formMethods.control}
render={({ field: { onBlur, onChange, value } }) => (
<TextField
name="signatureToken"
label={t("signature_token_optional")}
placeholder="cal_sk_XXXXXXXXXXX"
value={value || ""}
onBlur={onBlur}
onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
onChange(e.target.value);
formMethods.setValue("signatureToken", e.target.value);
}}
/>
)}
/>
<p className="text-subtle mt-1 text-sm">{t("signature_token_description")}</p>
</div>
</div>
</div>
</FormProvider>
)}
<div className="flex justify-end gap-2">
<Button type="button" color="secondary" onClick={onPrevStep}>
{t("prev_step")}
</Button>
{value === "EXISTING" ? (
<Button
type="submit"
color="primary"
loading={checkLicenseLoading || mutation.isPending}
disabled={
value === "EXISTING" &&
(!formMethods.formState.isValid || checkLicenseLoading || mutation.isPending)
}>
{t("save_license_key")}
</Button>
) : (
<Button color="primary" onClick={onNextStep} type="button">
{t("next_step")}
</Button>
)}
</div>
</form>
);
};
export default LicenseSelection;
@@ -18,7 +18,8 @@ import { STEPS } from "~/apps/installation/[[...step]]/constants";
import type { OnboardingPageProps, TEventTypeGroup } from "~/apps/installation/[[...step]]/step-view";
const getUser = async (userId: number) => {
const userAdminTeams = await UserRepository.getUserAdminTeams(userId);
const userRepo = new UserRepository(prisma);
const userAdminTeams = await userRepo.getUserAdminTeams({ userId });
if (!userAdminTeams?.id) {
return null;
@@ -101,7 +101,8 @@ async function getUserPageProps(context: GetServerSidePropsContext) {
name = profileUsername || username;
const [user] = await UserRepository.findUsersByUsername({
const userRepo = new UserRepository(prisma);
const [user] = await userRepo.findUsersByUsername({
usernameList: [name],
orgSlug: org,
});
@@ -2,6 +2,7 @@ import type { GetServerSidePropsContext } from "next";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import { UserRepository } from "@calcom/lib/server/repository/user";
import prisma from "@calcom/prisma";
export const getServerSideProps = async (context: GetServerSidePropsContext) => {
const { req } = context;
@@ -12,7 +13,8 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
return { redirect: { permanent: false, destination: "/auth/login" } };
}
const user = await UserRepository.findUserTeams({
const userRepo = new UserRepository(prisma);
const user = await userRepo.findUserTeams({
id: session.user.id,
});
@@ -101,8 +101,9 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
const eventType = booking.eventType ? booking.eventType : getDefaultEvent(dynamicEventSlugRef);
const userRepo = new UserRepository(prisma);
const enrichedBookingUser = booking.user
? await UserRepository.enrichUserWithItsProfile({ user: booking.user })
? await userRepo.enrichUserWithItsProfile({ user: booking.user })
: null;
const eventUrl = await buildEventUrlFromBooking({
@@ -94,7 +94,8 @@ const checkIfUserIsHost = async ({
return booking.user?.id === sessionUserId;
}
const eventType = await EventTypeRepository.findByIdWithUserAccess({
const eventTypeRepo = new EventTypeRepository(prisma);
const eventType = await eventTypeRepo.findByIdWithUserAccess({
id: booking.eventTypeId,
userId: sessionUserId,
});
@@ -106,7 +107,8 @@ const checkIfUserIsHost = async ({
export async function getServerSideProps(context: GetServerSidePropsContext) {
const { req } = context;
const booking = await BookingRepository.findBookingForMeetingPage({
const bookingRepo = new BookingRepository(prisma);
const booking = await bookingRepo.findBookingForMeetingPage({
bookingUid: context.query.uid as string,
});
@@ -140,9 +142,10 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
})
: false;
const userRepo = new UserRepository(prisma);
const profile = booking.user
? (
await UserRepository.enrichUserWithItsProfile({
await userRepo.enrichUserWithItsProfile({
user: booking.user,
})
).profile
@@ -1,12 +1,14 @@
import type { GetServerSidePropsContext } from "next";
import { BookingRepository } from "@calcom/lib/server/repository/booking";
import { prisma } from "@calcom/prisma";
import { type inferSSRProps } from "@lib/types/inferSSRProps";
export type PageProps = inferSSRProps<typeof getServerSideProps>;
export async function getServerSideProps(context: GetServerSidePropsContext) {
const booking = await BookingRepository.findBookingForMeetingEndedPage({
const bookingRepo = new BookingRepository(prisma);
const booking = await bookingRepo.findBookingForMeetingEndedPage({
bookingUid: context.query.uid as string,
});
@@ -1,10 +1,12 @@
import type { GetServerSidePropsContext } from "next";
import { BookingRepository } from "@calcom/lib/server/repository/booking";
import { prisma } from "@calcom/prisma";
// change the type
export async function getServerSideProps(context: GetServerSidePropsContext) {
const booking = await BookingRepository.findBookingByUid({
const bookingRepo = new BookingRepository(prisma);
const booking = await bookingRepo.findBookingByUid({
bookingUid: context.query.uid as string,
});
-4
View File
@@ -18,10 +18,6 @@ describe("Middleware - POST requests restriction", () => {
const req1 = createRequest("/api/auth/signup", "POST");
const res1 = checkPostMethod(req1);
expect(res1).toBeNull();
const req2 = createRequest("/api/trpc/book/event", "POST");
const res2 = checkPostMethod(req2);
expect(res2).toBeNull();
});
it("should block POST requests to not-allowed app routes", async () => {
+1 -6
View File
@@ -15,7 +15,7 @@ const safeGet = async <T = any>(key: string): Promise<T | undefined> => {
}
};
export const POST_METHODS_ALLOWED_API_ROUTES = ["/api/auth/signup", "/api/trpc/"];
export const POST_METHODS_ALLOWED_API_ROUTES = ["/api/auth/signup"];
export function checkPostMethod(req: NextRequest) {
const pathname = req.nextUrl.pathname;
if (!POST_METHODS_ALLOWED_API_ROUTES.some((route) => pathname.startsWith(route)) && req.method === "POST") {
@@ -69,10 +69,6 @@ const middleware = async (req: NextRequest): Promise<NextResponse<unknown>> => {
return responseWithHeaders({ url, res: routingFormRewriteResponse, req });
}
if (url.pathname.startsWith("/api/trpc/")) {
requestHeaders.set("x-cal-timezone", req.headers.get("x-vercel-ip-timezone") ?? "");
}
if (url.pathname.startsWith("/api/auth/signup")) {
const isSignupDisabled = await safeGet<boolean>("isSignupDisabled");
// If is in maintenance mode, point the url pathname to the maintenance page
@@ -178,7 +174,6 @@ export const config = {
"/:path*/embed",
// API routes
"/api/auth/signup",
"/api/trpc/:path*",
],
};
+62 -63
View File
@@ -1,104 +1,102 @@
"use client";
import { usePathname, useRouter } from "next/navigation";
import { useState } from "react";
import { useRouter } from "next/navigation";
import { useMemo, useState } from "react";
import AdminAppsList from "@calcom/features/apps/AdminAppsList";
import { APP_NAME } from "@calcom/lib/constants";
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { inferSSRProps } from "@calcom/types/inferSSRProps";
import { WizardForm } from "@calcom/ui/components/form";
import type { WizardStep } from "@calcom/ui/components/form/wizard/WizardForm";
import { AdminUserContainer as AdminUser } from "@components/setup/AdminUser";
import ChooseLicense from "@components/setup/ChooseLicense";
import EnterpriseLicense from "@components/setup/EnterpriseLicense";
import LicenseSelection from "@components/setup/LicenseSelection";
import type { getServerSideProps } from "@server/lib/setup/getServerSideProps";
function useSetStep() {
const router = useRouter();
const searchParams = useCompatSearchParams();
const pathname = usePathname();
const setStep = (newStep = 1) => {
const _searchParams = new URLSearchParams(searchParams ?? undefined);
_searchParams.set("step", newStep.toString());
router.replace(`${pathname}?${_searchParams.toString()}`);
};
return setStep;
}
const SETUP_VIEW_SETPS = {
ADMIN_USER: 1,
LICENSE: 2,
APPS: 3,
} as const;
export type PageProps = inferSSRProps<typeof getServerSideProps>;
export function Setup(props: PageProps) {
const [hasPickedAGPLv3, setHasPickedAGPLv3] = useState(false);
const { t } = useLocale();
const router = useRouter();
const [value, setValue] = useState(props.isFreeLicense ? "FREE" : "EE");
const isFreeLicense = value === "FREE";
const [isEnabledEE, setIsEnabledEE] = useState(!props.isFreeLicense);
const setStep = useSetStep();
const [licenseOption, setLicenseOption] = useState<"FREE" | "EXISTING">(
props.hasValidLicense ? "EXISTING" : "FREE"
);
const steps: React.ComponentProps<typeof WizardForm>["steps"] = [
const defaultStep = useMemo(() => {
if (props.userCount > 0) {
if (!props.hasValidLicense && !hasPickedAGPLv3) {
return SETUP_VIEW_SETPS.LICENSE;
} else {
return SETUP_VIEW_SETPS.APPS;
}
}
return SETUP_VIEW_SETPS.ADMIN_USER;
}, [props.userCount, props.hasValidLicense, hasPickedAGPLv3]);
const steps: WizardStep[] = [
{
title: t("administrator_user"),
description: t("lets_create_first_administrator_user"),
content: (setIsPending) => (
customActions: true,
content: (setIsPending, nav) => (
<AdminUser
onSubmit={() => {
setIsPending(true);
}}
onSuccess={() => {
setStep(2);
// If there's already a valid license or user picked AGPLv3, skip to apps step
if (props.hasValidLicense || hasPickedAGPLv3) {
nav.onNext();
nav.onNext(); // Skip license step
} else {
nav.onNext();
}
}}
onError={() => {
setIsPending(false);
}}
userCount={props.userCount}
nav={nav}
/>
),
},
{
title: t("choose_a_license"),
description: t("choose_license_description"),
content: (setIsPending) => {
return (
<ChooseLicense
id="wizard-step-2"
name="wizard-step-2"
value={value}
onChange={setValue}
onSubmit={() => {
setIsPending(true);
setStep(3);
}}
/>
);
},
},
];
if (!isFreeLicense) {
// Only show license selection step if there's no valid license already and AGPLv3 wasn't picked
if (!props.hasValidLicense && !hasPickedAGPLv3) {
steps.push({
title: t("step_enterprise_license"),
description: t("step_enterprise_license_description"),
content: (setIsPending) => {
const currentStep = 3;
title: t("choose_a_license"),
description: t("choose_license_description"),
customActions: true,
content: (setIsPending, nav) => {
return (
<EnterpriseLicense
id={`wizard-step-${currentStep}`}
name={`wizard-step-${currentStep}`}
onSubmit={() => {
<LicenseSelection
id="wizard-step-2"
name="wizard-step-2"
value={licenseOption}
onChange={setLicenseOption}
onSubmit={(values) => {
setIsPending(true);
if (licenseOption === "FREE") {
setHasPickedAGPLv3(true);
nav.onNext();
} else if (licenseOption === "EXISTING" && values.licenseKey) {
nav.onNext();
}
}}
onSuccess={() => {
setStep(currentStep + 1);
}}
onSuccessValidate={() => {
setIsEnabledEE(true);
}}
onPrevStep={nav.onPrev}
onNextStep={nav.onNext}
/>
);
},
isEnabled: isEnabledEE,
});
}
@@ -106,23 +104,24 @@ export function Setup(props: PageProps) {
title: t("enable_apps"),
description: t("enable_apps_description", { appName: APP_NAME }),
contentClassname: "!pb-0 mb-[-1px]",
content: (setIsPending) => {
const currentStep = isFreeLicense ? 3 : 4;
customActions: true,
content: (setIsPending, nav) => {
return (
<AdminAppsList
id={`wizard-step-${currentStep}`}
name={`wizard-step-${currentStep}`}
id={`wizard-step-${steps.length}`}
name={`wizard-step-${steps.length}`}
classNames={{
form: "mb-4 rounded-md bg-default px-0 pt-0 md:max-w-full",
appCategoryNavigationContainer: "max-h-[400px] overflow-y-auto md:p-4",
verticalTabsItem: "!w-48 md:p-4",
}}
baseURL={`/auth/setup?step=${currentStep}`}
baseURL={`/auth/setup?step=${steps.length}`}
useQueryParam={true}
onSubmit={() => {
setIsPending(true);
router.replace("/");
}}
nav={nav}
/>
);
},
@@ -131,7 +130,7 @@ export function Setup(props: PageProps) {
return (
<main className="bg-subtle flex items-center print:h-full md:h-screen">
<WizardForm
href="/auth/setup"
defaultStep={defaultStep}
steps={steps}
nextLabel={t("next_step_text")}
finishLabel={t("finish")}
@@ -77,7 +77,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
let rescheduledToUid: string | null = null;
if (bookingInfo.rescheduled) {
const rescheduledTo = await BookingRepository.findFirstBookingByReschedule({
const bookingRepo = new BookingRepository(prisma);
const rescheduledTo = await bookingRepo.findFirstBookingByReschedule({
originalBookingUid: bookingInfo.uid,
});
rescheduledToUid = rescheduledTo?.uid ?? null;
@@ -89,7 +90,8 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
} | null = null;
if (bookingInfo.fromReschedule) {
previousBooking = await BookingRepository.findReschedulerByUid({
const bookingRepo = new BookingRepository(prisma);
previousBooking = await bookingRepo.findReschedulerByUid({
uid: bookingInfo.fromReschedule,
});
}
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "5.4.15",
"version": "5.4.19",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -5,10 +5,10 @@ import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { ZGetRetellLLMSchema } from "@calcom/features/ee/cal-ai-phone/zod-utils";
import type { TGetRetellLLMSchema } from "@calcom/features/ee/cal-ai-phone/zod-utils";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/available-slots";
import { fetcher } from "@calcom/lib/retellAIFetcher";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import prisma from "@calcom/prisma";
import { getAvailableSlots } from "@calcom/trpc/server/routers/viewer/slots/util";
dayjs.extend(advancedFormat);
@@ -86,8 +86,9 @@ async function handler(req: NextApiRequest, res: NextApiResponse) {
const startTime = now.startOf("month").toISOString();
const endTime = now.add(2, "month").endOf("month").toISOString();
const orgSlug = eventType?.team?.parent?.slug ?? null;
const availableSlotsService = getAvailableSlotsService();
const availableSlots = await getAvailableSlots({
const availableSlots = await availableSlotsService.getAvailableSlots({
input: {
startTime,
endTime,
+4
View File
@@ -1,5 +1,9 @@
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("AppListCard", async () => {
test("should remove the highlight from the URL", async ({ page, users }) => {
const user = await users.create({});
@@ -2,6 +2,10 @@ import { expectPageToBeNotFound } from "playwright/lib/testUtils";
import { test } from "./lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("App Router - error handling", () => {
test("404s must render App Router's not-found page regardless of whether pages router or app router is used", async ({
page,
@@ -3,6 +3,8 @@ import { expect } from "@playwright/test";
import { test } from "../lib/fixtures";
import { submitAndWaitForResponse } from "../lib/testUtils";
test.describe.configure({ mode: "parallel" });
test.describe("Can signup from a team invite", async () => {
test.beforeEach(async ({ users }) => {
const proUser = await users.create();
@@ -6,6 +6,8 @@ import prisma from "@calcom/prisma";
import { test } from "../lib/fixtures";
test.describe.configure({ mode: "parallel" });
test.afterEach(({ users }) => users.deleteAll());
test.describe("Forgot password", async () => {
+4 -1
View File
@@ -461,10 +461,13 @@ test.describe("Bookings", () => {
await addFilter(page, "userId");
await openFilter(page, "userId");
const bookingsGetResponse2 = page.waitForResponse((response) =>
/\/api\/trpc\/bookings\/get.*/.test(response.url())
);
await page
.locator(`[data-testid="select-filter-options-userId"] [role="option"]:has-text("${anotherUser}")`)
.click();
await page.waitForResponse((response) => /\/api\/trpc\/bookings\/get.*/.test(response.url()));
await bookingsGetResponse2;
await expect(page.locator('[data-testid="booking-item"]')).toHaveCount(0);
});
@@ -19,6 +19,8 @@ test("dynamic booking", async ({ page, users }) => {
const free = await users.create({ username: "free.example" });
await page.goto(`/${pro.username}+${free.username}`);
//fix race condition
await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).waitFor({ state: "visible" });
await test.step("book an event first day in next month", async () => {
await selectFirstAvailableTimeSlotNextMonth(page);
+168 -17
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "تخصيص إعدادات Cal Video",
"calvideo_settings_description": "قم بتكوين تفضيلات اجتماعات الفيديو بما في ذلك أذونات التسجيل للمضيفين والحاضرين.",
"disable_recording_for_guests": "تعطيل التسجيل للضيوف",
"enable_automatic_transcription": "تمكين النسخ التلقائي بعد الانضمام إلى الاجتماع",
"video_options": "خيارات الفيديو",
"get_meeting_session_details": "الحصول على تفاصيل جلسة الاجتماع",
"meeting_session_details": "تفاصيل جلسة الاجتماع",
"meeting_session": "جلسة الاجتماع",
"session_id": "معرف الجلسة",
"max_participants": "الحد الأقصى للمشاركين",
"unlimited": "غير محدود",
"ongoing": "جارية",
"ended": "انتهت",
"participants": "المشاركون",
"anonymous_user": "مستخدم مجهول",
"session_duration": "مدة الجلسة",
"no_meeting_sessions_found": "لم يتم العثور على جلسات اجتماع",
"not_available": "غير متاح",
"disable_recording_for_organizer": "تعطيل التسجيل للمنظم",
"disable_transcription_for_guests": "تعطيل النسخ للضيوف",
"disable_transcription_for_organizer": "تعطيل النسخ للمنظم",
"event_request_declined_recurring": "تم رفض طلب الحدث المتكرر الخاص بك",
"event_request_cancelled": "تم إلغاء حدثك المُجَدْوَل",
"event_request_reassigned": "تم إعادة تعيين الحدث المجدول الخاص بك",
@@ -264,6 +281,8 @@
"guests": "الضيوف",
"guest": "الضيف",
"web_conferencing_details_to_follow": "تفاصيل مؤتمرات الويب القادمة.",
"confirmation": "تأكيد",
"what_booker_should_provide": "ما يجب أن يقدمه الحاجز للحصول على تأكيدات",
"404_the_user": "اسم المستخدم",
"username": "اسم المستخدم",
"is_still_available": "لا يزال متاحًا.",
@@ -331,7 +350,7 @@
"organization_general_description": "إدارة الإعدادات للغة فريقك ومنطقته الزمنية",
"few_sentences_about_yourself": "اكتب بضع جمل عن نفسك. سيظهر هذا على صفحة عنوان URL الشخصية لديك.",
"nearly_there": "أوشكت على الانتهاء!",
"nearly_there_instructions": "وأخيرًا، يساعدك الوصف الموجز عنك والصورة في الحصول على الحجوزات والسماح للأشخاص بمعرفة الشخص الذين يحجزون معه.",
"nearly_there_instructions": "آخر شيء، وصف موجز عنك وصورة يساعدان حقًا في الحصول على الحجوزات ويتيحان للأشخاص معرفة مع من يقومون بالحجز.",
"set_availability_instructions": "حدد الفترات الزمنية التي تكون متاحًا فيها بشكل متكرر. يمكنك لاحقًا تحديد المزيد منها وربطها مع تقاويم مختلفة.",
"set_availability": "تحديد الوقت الذي تكون فيه متاحًا",
"set_availbility_description": "حدد الجداول الزمنية للأوقات التي ترغب بأن يتم حجزك فيها.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "ربط التقويم لديك",
"connect_your_video_app": "اربط تطبيقات الفيديو لديك",
"connect_your_video_app_instructions": "قم بتوصيل تطبيقات الفيديو لديك لاستخدامها في أنواع الأحداث الخاصة بك.",
"connect_your_calendar_instructions": "اربط التقويم لديك للتحقق تلقائيًا من الأوقات المشغولة والأحداث الجديدة أثناء جدولتها.",
"connect_your_calendar_instructions": "قم بربط تقويمك للتحقق تلقائيًا من الأوقات المشغولة والأحداث الجديدة عند جدولتها.",
"set_up_later": "الإعداد لاحقًا",
"current_time": "الوقت الحالي",
"details": "التفاصيل",
"welcome": "مرحبًا",
"welcome_back": "أهلاً بك من جديد",
"welcome_to_calcom": "مرحبًا بك في {{appName}}",
"welcome_instructions": "أخبرنا باسمك وبالمنطقة الزمنية التي توجد فيها. ستتمكن لاحقًا من تعديل هذا.",
"welcome_instructions": "أخبرنا بما نناديك به وأعلمنا بالمنطقة الزمنية التي تتواجد فيها. ستتمكن من تعديل هذا لاحقًا.",
"connect_caldav": "الاتصال بخادم CalDav",
"connect_ics_feed": "اربط بـICS",
"connect": "الاتصال",
@@ -361,7 +380,7 @@
"when": "متى",
"where": "أين",
"add_to_calendar": "إضافة إلى التقويم",
"add_to_calendar_description": "اختر أين ستُضاف الأحداث الجديدة عندما يتم الحجز لديك.",
"add_to_calendar_description": "حدد أين تريد إضافة الأحداث عندما يتم حجزها.",
"add_events_to": "إضافة أحداث إلى",
"add_another_calendar": "إضافة تقويم آخر",
"other": "آخر",
@@ -1153,6 +1172,7 @@
"test_failed": "فشل الاختبار",
"provide_api_key": "توفير مفتاح API",
"api_key_modal_subtitle": "تتيح لك مفاتيح API إجراء مكالمات API لحسابك الخاص.",
"api_key_modal_subtitle_platform": "إنشاء سوق أو منصة بدلاً من ذلك.",
"api_keys_subtitle": "إنشاء مفاتيح API لاستخدامها للوصول إلى حسابك الخاص.",
"create_api_key": "إنشاء مفتاح API",
"personal_note": "تسمية هذا المفتاح",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "إلغاء مفتاح API هذا",
"disable_rescheduling": "تعطيل إعادة الجدولة",
"description_disable_rescheduling": "لم يعد بإمكان الضيوف إعادة جدولة الحدث من خلال دعوة التقويم أو البريد الإلكتروني",
"allow_rescheduling_cancelled_bookings": "السماح بالحجز من خلال رابط إعادة الجدولة",
"description_allow_rescheduling_cancelled_bookings": "عند التفعيل، سيتمكن المستخدمون من إنشاء حجز جديد عند محاولة إعادة جدولة حجز ملغى",
"disable_cancelling": "تعطيل الإلغاء",
"description_disable_cancelling": "لم يعد بإمكان الضيوف إلغاء الحدث من خلال دعوة التقويم أو البريد الإلكتروني",
"revoke_api_key": "إلغاء مفتاح API",
@@ -1675,7 +1697,7 @@
"create_first_api_key_description": "تسمح مفاتيح API بتواصل التطبيقات الأخرى مع {{appName}}",
"back_to_signin": "العودة لتسجيل الدخول",
"reset_link_sent": "تم إرسال رابط إعادة الضبط",
"password_reset_email": "تم إرسال بريد إلكتروني إلى {{email}} يحتوي على تعليمات لإعادة تعيين كلمة المرور.",
"password_reset_email": "تم إرسال بريد إلكتروني إلى {{email}} مع تعليمات لإعادة تعيين كلمة المرور الخاصة بك.",
"password_reset_leading": "إذا لم تستلم رسالة بريد إلكتروني قريبًا، تحقق من أن عنوان البريد الإلكتروني الذي أدخلته صحيح، وتحقق من مجلد البريد المزعج أو اتصل بالدعم إذا استمرت المشكلة.",
"password_updated": "تم تحديث كلمة المرور!",
"pending_payment": "في انتظار الدفع",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "سيؤدي ذلك إلى تقييد توفر هذا النوع من الأحداث إلى فتحة واحدة في اليوم، مجدولة في أقرب وقت متاح.",
"limit_total_booking_duration": "الحد من مدة الحجز الإجمالية",
"limit_total_booking_duration_description": "الحد من إجمالي الوقت المخصص الذي يمكن حجزه لهذا الحدث",
"booker_booking_limit": "حد الحجوزات النشطة للحاجز",
"booker_booking_limit_description": "تحديد عدد الحجوزات النشطة التي يمكن للحاجز إجراؤها لهذا النوع من الفعاليات",
"recurring_event_doesnt_support_booker_booking_limit": "الفعاليات المتكررة لا تدعم حد حجوزات الحاجز",
"add_limit": "إضافة حد",
"team_name_required": "اسم الفريق مطلوب",
"show_attendees": "مشاركة معلومات الحضور مع الضيوف",
"show_available_seats_count": "إظهار عدد المقاعد المتاحة",
"how_booking_questions_as_variables": "كيف يمكن استخدام أسئلة الحجز كمتغيرات؟",
"format": "التنسيق",
"questions": "الأسئلة",
"all_info_your_booker_provide": "جميع المعلومات التي يجب على الحاجز تقديمها قبل الحجز معك.",
"uppercase_for_letters": "استخدم الحروف الكبيرة لجميع الأحرف",
"replace_whitespaces_underscores": "استبدل المساحات البيضاء بالشرطات السفلية",
"platform_members": "أعضاء المنصة",
@@ -1915,7 +1942,7 @@
"new_attendee": "حضور جديد",
"awaiting_approval": "في انتظار الموافقة",
"requires_google_calendar": "يتطلب هذا التطبيق اتصال تقويم Google",
"event_type_requires_google_calendar": "يحتاج \"إضافة إلى التقويم\" لهذا النوع من الأحداث أن يكون تقويم Google حتى يعمل Meet. قم بتوصيله <0>هنا</0>.",
"event_type_requires_google_calendar": "يحتاج خيار \"إضافة إلى التقويم\" لهذا النوع من الفعاليات أن يكون تقويم Google حتى يعمل Meet. قم بربطه <0>هنا</0>.",
"connected_google_calendar": "لقد قمت بتوصيل حساب تقويم Google.",
"using_meet_requires_calendar": "يتطلب استخدام Google Meet تقويم Google متصل",
"continue_to_install_google_calendar": "متابعة تثبيت تقويم Google",
@@ -1967,12 +1994,12 @@
"delete_sso_configuration_confirmation_description": "هل تريد بالتأكيد حذف تكوين {{connectionType}}؟ لن يتمكن أعضاء فريقك الذين يستخدمون معلومات تسجيل الدخول إلى {{connectionType}} من الوصول إلى Cal.com بعد الآن.",
"organizer_timezone": "منظم المناطق الزمنية",
"email_user_cta": "عرض الدعوة",
"email_no_user_invite_heading_subteam": "لقد تمت دعوتك للانضمام إلى فريق من منظمة {{parentTeamName}}",
"email_no_user_invite_heading_subteam": "تمت دعوتك للانضمام إلى فريق من منظمة {{parentTeamName}}",
"email_no_user_invite_heading_org": "تمت دعوتك للانضمام إلى منظمة {{appName}}",
"email_no_user_invite_subheading": "دعاك {{invitedBy}} للانضمام إلى فريقه على {{appName}}. إن {{appName}} هو برنامج جدولة الأحداث الذي يمكّنك أنت وفريقك من جدولة الاجتماعات دون الحاجة إلى المراسلة عبر البريد الإلكتروني.",
"email_user_invite_subheading_subteam": "{{invitedBy}} دعاك للانضمام إلى الفريق {{teamName}} في منظمتهم {{parentTeamName}} على {{appName}}. {{appName}} هو منظم الأحداث الذي يمكّنك أنت وفريقك من جدولة الاجتماعات دون تبادل الرسائل الإلكترونية.",
"email_user_invite_subheading_org": "لقد دعاك {{invitedBy}} للانضمام إلى فريق {{teamName}} التابع لمنظمتهم على {{appName}}. إن {{appName}} هو برنامج جدولة للفعاليات يمنحك القدرة أنت ومؤسستك على جدولة الاجتماعات من دون الحاجة إلى التراسل الكثيف عبر البريد الإلكتروني.",
"email_no_user_invite_steps_intro": "سنرشدك خلال بضع خطوات قصيرة وستستمتع بجدول زمني خالٍ من التوتر مع {{entity}} في لمح البصر.",
"email_no_user_invite_steps_intro": "سنرشدك خلال بعض الخطوات القصيرة وستستمتع بجدولة خالية من التوتر مع {{entity}} الخاص بك في أقرب وقت.",
"email_no_user_step_one": "اختر اسم المستخدم الخاص بك",
"email_no_user_step_two": "ربط حساب التقويم الخاص بك",
"email_no_user_step_three": "تعيين أوقاتك المتاحة",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "اتخذ قرارات مستنيرة باستخدام Insights",
"make_informed_decisions_description": "تُظهر لوحة معلومات Insights جميع أنشطة فريقك وتعرض لك الاتجاهات الرائجة التي تتيح جدولة الفريق واتخاذ القرارات بشكل أفضل.",
"view_bookings_across": "عرض حجوزات جميع الأعضاء",
"view_bookings_across_description": "تعرف على من يتلقى أكبر عدد من الحجوزات وتأكد من التوزيع الأفضل بين أعضاء فريقك",
"view_bookings_across_description": "معرفة من يتلقى أكبر عدد من الحجوزات وضمان أفضل توزيع عبر فريقك",
"identify_booking_trends": "تحديد اتجاهات الحجز الرائجة",
"identify_booking_trends_description": "شاهد أي الأوقات في الأسبوع وخلال اليوم تحظى بشعبية لدى من يقومون بالحجز لديك",
"spot_popular_event_types": "اكتشف أنواع الأحداث الشائعة",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "لم يتم العثور على بيانات لعامل التصفية المحدد أو التواريخ المحددة.",
"acknowledge_booking_no_show_fee": "أقر بأنه إذا لم أحضر هذا الحدث فسيتم سحب رسوم عدم حضور من بطاقتي تبلغ {{amount, currency}}.",
"card_details": "تفاصيل البطاقة",
"something_went_wrong_on_our_end": "حدث خطأ ما من طرفنا. اتصل بفريق الدعم لدينا، وسنُصلحه لك فوراً.",
"something_went_wrong_on_our_end": "حدث خطأ ما من جانبنا. تواصل مع فريق الدعم لدينا، وسنقوم بإصلاحه فورًا لك.",
"please_provide_following_text_to_suppport": "يرجى تقديم النص التالي عند الاتصال بالدعم لمساعدتك بشكل أفضل",
"seats_and_no_show_fee_error": "لا يمكن حاليًا تمكين المقاعد وفرض رسوم عدم الحضور",
"complete_your_booking": "أكمل الحجز",
@@ -2276,7 +2303,7 @@
"messaging": "المراسلة",
"sender_id_info": "الاسم أو الرقم الظاهر على أنه مرسل الرسالة النصية القصيرة (بعض البلدان لا تسمح بمعرفات المرسل الأبجدية الرقمية)",
"org_admins_can_create_new_teams": "يمكن فقط لمسؤول منظمتك إنشاء فرق جديدة",
"google_new_spam_policy": "يمكن أن تمنعك سياسة Google الجديدة للرسائل غير المرغوب فيها من تلقي أي إشعارات بالبريد الإلكتروني والتقويم حول هذا الحجز.",
"google_new_spam_policy": "قد تمنعك سياسة Google الجديدة للبريد المزعج من تلقي أي إشعارات بريد إلكتروني وتقويم حول هذا الحجز.",
"resolve": "حل",
"no_organization_slug": "حدث خطأ أثناء إنشاء فرق لهذه المنظمة. الكلمة اللطيفة للرابط مفقودة.",
"copy_link_org": "نسخ الرابط إلى المنظمة",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "تسمح بيانات اعتماد التفويض بإدارة الوصول إلى تقويمات Google Workspace لمؤسستك.",
"add_client_id_in_google_workspace_with_below_scope": "أضف معرف العميل هذا في Google Workspace مع النطاق أدناه",
"delegation_credential_enabled": "تم تفعيل بيانات اعتماد التفويض",
"delegation_credential_disabled": "تم تعطيل بيانات اعتماد التفويض",
"add_delegation_credential": "إضافة بيانات اعتماد التفويض",
"edit_delegation_credential": "تعديل بيانات اعتماد التفويض",
"domain": "النطاق",
@@ -2582,7 +2608,7 @@
"date": "التاريخ",
"overlaps_with_existing_schedule": "يتداخل هذا مع جدول موجود. يرجى اختيار تاريخ مختلف.",
"org_admin_no_slots|heading": "لم يتم العثور على توافر لـ {{name}}",
"org_admin_no_slots|content": "مرحبًا مسؤولي المنظمة،<br /><br />يرجى ملاحظة: لقد تم إبلاغنا بأن {{username}} ليس لديه أي توافر عندما زار مستخدم {{username}}/{{slug}}<br /><br />هناك بعض الأسباب التي قد تؤدي إلى ذلك<br />المستخدم ليس لديه أي تقاويم متصلة<br />جداولهم المرتبطة بهذا الحدث غير مفعلة<br /> <br />نوصي بالتحقق من توافرهم لحل هذه المشكلة.",
"org_admin_no_slots|content": "مرحبًا مسؤولي المنظمة،<br /><br />يرجى ملاحظة: لقد تم لفت انتباهنا إلى أن {{username}} لم يكن لديه أي توفر عندما قام مستخدم بزيارة {{username}}/{{slug}}<br /><br />هناك عدة أسباب قد تؤدي إلى حدوث ذلك<br />المستخدم ليس لديه أي تقويمات متصلة<br />جداولهم المرتبطة بهذا الحدث غير مفعلة<br /> <br />نوصي بالتحقق من توفرهم لحل هذه المشكلة.",
"org_admin_no_slots|cta": "افتح توافر المستخدمين",
"organization_no_slots_notification_switch_title": "احصل على إشعارات عندما لا يكون لدى فريقك توافر",
"organization_no_slots_notification_switch_description": "سيحصل المسؤولون على إشعارات عبر البريد الإلكتروني عندما يحاول مستخدم حجز عضو في الفريق ويواجه 'عدم التوافر'. نقوم بإرسال هذا البريد الإلكتروني بعد حدوث حالتين ونذكركم كل 7 أيام لكل مستخدم.",
@@ -2592,10 +2618,10 @@
"email_team_invite|subject|invited_to_org": "{{user}} دعاك للانضمام إلى المنظمة {{team}} على {{appName}}",
"email_team_invite|subject|added_to_subteam": "{{user}} أضافك إلى الفريق {{team}} من المنظمة {{parentTeamName}} على {{appName}}",
"email_team_invite|subject|invited_to_regular_team": "قام {{user}} بدعوتك للانضمام إلى فريق {{team}} على {{appName}}",
"email_team_invite|heading|added_to_org": "تمت إضافتك إلى منظمة {{appName}}",
"email_team_invite|heading|invited_to_org": "تمت دعوتك إلى منظمة {{appName}}",
"email_team_invite|heading|added_to_subteam": "تمت إضافتك إلى فريق من منظمة {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "تمت دعوتك إلى فريق من منظمة {{parentTeamName}}",
"email_team_invite|heading|added_to_org": "تمت إضافتك إلى مؤسسة {{appName}}",
"email_team_invite|heading|invited_to_org": "تمت دعوتك إلى مؤسسة {{appName}}",
"email_team_invite|heading|added_to_subteam": "تمت إضافتك إلى فريق في مؤسسة {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "تمت دعوتك إلى فريق في مؤسسة {{parentTeamName}}",
"email_team_invite|heading|invited_to_regular_team": "تمت دعوتك للانضمام إلى فريق {{appName}}",
"email_team_invite|content|added_to_org": "{{invitedBy}} أضافك إلى منظمة <strong>{{teamName}}</strong>.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} دعاك للانضمام إلى منظمة {{teamName}}.",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "تحديد عدد المرات التي يمكن فيها حجز الأعضاء عبر جميع أنواع فعاليات الفريق",
"booking_limits_updated_successfully": "تم تحديث حدود الحجز بنجاح",
"you_are_unauthorized_to_make_this_change_to_the_booking": "أنت غير مخول لإجراء هذا التغيير على الحجز",
"delegation_credential_disabled": "تم تعطيل بيانات اعتماد التفويض",
"matching_members": "الأعضاء المطابقون",
"x_matching_members": "{{x}} عضو مطابق",
"matching_members_queue_using_attribute_weights": "قائمة انتظار مطابقة الأعضاء (باستخدام أوزان السمات)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "نعم، أعد التعيين",
"filter_by_attributes": "تصفية حسب السمات",
"reassign_reason": "سبب إعادة التعيين",
"field_type_change_suggestion": "قد يؤدي تغيير نوع الحقل إلى فقدان البيانات. يرجى إنشاء حقل جديد بدلاً من ذلك.",
"reassigned_by": "تمت إعادة التعيين بواسطة",
"row": "صف",
"rows": "صفوف",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "ليس لديك الصلاحية لإنشاء مؤسسة لهذا البريد الإلكتروني",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "ليس لديك الصلاحية لنقل فريق واحد أو أكثر",
"organization_has_subscription_already": "المؤسسة لديها اشتراك بالفعل",
"reset_interval_weighted_rr": "إعادة تعيين الفاصل الزمني للتناوب الدائري المرجح",
"interval": "الفترة",
"rr_reset_interval_description": "تحدد مدى تكرار إعادة تعيين عدد الحجوزات في الترتيب الدوري لضمان توزيع متوازن.",
"daily": "يوميًا",
@@ -3075,6 +3104,7 @@
"user_has_no_team_yet": "ليس لديك فريق بعد",
"no_team_members": "ليس لديك أعضاء في الفريق بعد",
"recurring_event_doesnt_support_seats": "الفعالية المتكررة لا تدعم ميزة المقاعد. اجعلها غير متكررة لتمكين المقاعد.",
"booking_limit_per_booker_doesnt_support_recurring": "لا يدعم الحدث المتكرر ميزة حد الحجز لكل حاجز. قم بتعطيل حد الحجز لكل حاجز لتمكين الحدث المتكرر.",
"seats_doesnt_support_recurring": "ميزة المقاعد لا تدعم الفعاليات المتكررة. قم بتعطيل ميزة المقاعد لجعلها متكررة.",
"recurring_event_seats_error": "الفعالية المتكررة لا تدعم ميزة المقاعد. قم بتعطيل ميزة المقاعد أو اجعل الفعالية غير متكررة.",
"segment": "شريحة",
@@ -3203,10 +3233,131 @@
"matching": "المطابقة",
"event_redirect": "إعادة توجيه الحدث",
"reset_form": "إعادة تعيين النموذج",
"rr_settings_successfully_updated": "تم تحديث إعدادات التناوب الدائري بنجاح",
"distribution_basis_weighted_rr": "أساس التوزيع للتناوب الدائري المرجح",
"timestamp_basis_description": "يحدد أي طابع زمني للحدث يُستخدم كأساس لتوزيع التناوب الدائري المرجح.",
"requires_credits": "يتطلب رصيداً",
"requires_credits_tooltip": "تحتاج إلى رصيد كافٍ في حسابك لاستخدام هذه الميزة",
"available_credits": "الرصيد المتاح",
"create_role": "إنشاء دور",
"roles_and_permissions": "الأدوار والصلاحيات",
"roles_and_permissions_description": "إدارة الأدوار والصلاحيات لمؤسستك",
"learn_more_permissions": "تعرف على المزيد حول كيفية عمل الصلاحيات",
"role_name": "اسم الدور",
"role_name_placeholder": "مثال: فريق المبيعات، موظفو الدعم",
"role_description": "وصف الدور",
"role_description_placeholder": "صف الغرض من هذا الدور والصلاحيات التي يحتاجها",
"default_role": "الدور الافتراضي",
"custom_role": "دور مخصص",
"advanced": "متقدم",
"search_permissions": "البحث في الصلاحيات",
"all_permissions": "جميع الأذونات",
"none": "لا شيء",
"read_only": "للقراءة فقط",
"pbac_desc_all_actions_all_resources": "جميع الإجراءات على جميع الموارد",
"pbac_resource_all": "جميع الموارد",
"pbac_resource_event_type": "أنواع الحدث",
"pbac_resource_team": "الفرق",
"pbac_resource_organization": "المنظمة",
"pbac_resource_booking": "الحجوزات",
"pbac_resource_insights": "الرؤى",
"pbac_resource_role": "الأدوار",
"pbac_action_all": "جميع الإجراءات",
"pbac_action_create": "إنشاء",
"pbac_action_read": "عرض",
"pbac_action_update": "تعديل",
"pbac_action_delete": "حذف",
"pbac_action_manage": "إدارة",
"pbac_action_invite": "دعوة",
"pbac_action_remove": "إزالة",
"pbac_action_change_member_role": "تغيير دور العضو",
"pbac_action_list_members": "سرد الأعضاء",
"pbac_action_manage_billing": "إدارة الفواتير",
"pbac_action_read_team_bookings": "عرض حجوزات الفريق",
"pbac_action_read_org_bookings": "عرض حجوزات المنظمة",
"pbac_action_read_recordings": "عرض التسجيلات",
"role_created_successfully": "تم إنشاء الدور بنجاح",
"role_updated_successfully": "تم تحديث الدور بنجاح",
"delete_role": "حذف الدور",
"confirm_delete_role": "هل أنت متأكد من رغبتك في حذف هذا الدور؟ لا يمكن التراجع عن هذا الإجراء.",
"role_deleted_successfully": "تم حذف الدور بنجاح",
"error_creating_role": "خطأ في إنشاء الدور",
"error_updating_role": "خطأ في تحديث الدور",
"pbac_desc_create_roles": "إنشاء الأدوار",
"pbac_desc_view_roles": "عرض الأدوار",
"pbac_desc_update_roles": "تحديث الأدوار",
"pbac_desc_delete_roles": "حذف الأدوار",
"pbac_desc_manage_roles": "جميع الإجراءات على الأدوار",
"pbac_desc_create_event_types": "إنشاء أنواع الأحداث",
"pbac_desc_view_event_types": "عرض أنواع الأحداث",
"pbac_desc_update_event_types": "تحديث أنواع الأحداث",
"pbac_desc_delete_event_types": "حذف أنواع الأحداث",
"pbac_desc_manage_event_types": "جميع الإجراءات على أنواع الأحداث",
"pbac_desc_create_teams": "إنشاء الفرق",
"pbac_desc_view_team_details": "عرض تفاصيل الفريق",
"pbac_desc_update_team_settings": "تحديث إعدادات الفريق",
"pbac_desc_delete_team": "حذف الفريق",
"pbac_desc_invite_team_members": "دعوة أعضاء الفريق",
"pbac_desc_remove_team_members": "إزالة أعضاء الفريق",
"pbac_desc_change_team_member_role": "تغيير دور أعضاء الفريق",
"pbac_desc_manage_teams": "جميع الإجراءات على الفرق",
"pbac_desc_create_organization": "إنشاء مؤسسة",
"pbac_desc_view_organization_details": "عرض تفاصيل المؤسسة",
"pbac_desc_list_organization_members": "سرد أعضاء المؤسسة",
"pbac_desc_invite_organization_members": "دعوة أعضاء المؤسسة",
"pbac_desc_remove_organization_members": "إزالة أعضاء المؤسسة",
"pbac_desc_manage_organization_billing": "إدارة فواتير المؤسسة",
"pbac_desc_change_organization_member_role": "تغيير دور أعضاء المؤسسة",
"pbac_desc_edit_organization_settings": "تعديل إعدادات المؤسسة",
"pbac_desc_manage_organizations": "جميع الإجراءات على المؤسسات",
"pbac_desc_view_bookings": "عرض الحجوزات",
"pbac_desc_view_team_bookings": "عرض حجوزات الفريق",
"pbac_desc_view_organization_bookings": "عرض حجوزات المؤسسة",
"pbac_desc_view_booking_recordings": "عرض تسجيلات الحجز",
"pbac_desc_update_bookings": "تحديث الحجوزات",
"pbac_desc_manage_bookings": "جميع الإجراءات على الحجوزات",
"pbac_desc_view_team_insights": "عرض رؤى الفريق",
"pbac_desc_manage_team_insights": "إدارة رؤى الفريق",
"read_permission_auto_enabled_tooltip": "يتم تمكين إذن القراءة تلقائيًا عند إنشاء أو تحديث أو حذف مورد",
"choose_restriction_schedule": "إضافة جدول القيود",
"choose_restriction_schedule_description": "قم بتمكين هذا إذا كنت ترغب في تقييد توفر المضيفين لهذا النوع من الأحداث بناءً على جدول القيود. هذا يزيل فقط الفترات التي ليست في جدول القيود، ولا يضيف أي فترات جديدة.",
"use_booker_timezone": "استخدام المنطقة الزمنية للحاجز",
"use_booker_timezone_info": "تطبيق جدول القيود المحدد في المنطقة الزمنية للحاجز",
"load_balancing_warning": "سيؤدي هذا إلى تعطيل توزيع الحمل على جميع أنواع أحداث round robin الخاصة بك",
"rr_load_balancing_disabled": "توزيع الحمل متاح فقط عندما يتم تعيين أساس الطابع الزمني على وقت إنشاء الحجز",
"round_robin_settings_description": "تخصيص إعدادات round robin الافتراضية لهذا الفريق",
"round_robin_settings_updated_successfully": "تم تحديث إعدادات التناوب الدائري بنجاح",
"booking_creation_time": "وقت إنشاء الحجز",
"meeting_start_time": "وقت بدء الاجتماع",
"members_affected_by_disabling_delegation_credential": "الأعضاء المتأثرون",
"no_members_affected_by_disabling_delegation_credential": "لا يوجد أعضاء متأثرون بتعطيل بيانات اعتماد التفويض",
"download_expense_log": "تنزيل سجل النفقات",
"error_downloading_expense_log": "خطأ في تنزيل سجل النفقات",
"offer_to_reschedule_last_booking": "عرض إعادة جدولة آخر حجز نشط إلى الفترة الزمنية المختارة",
"booker_limit_exceeded_error": "تم تجاوز الحد الأقصى للحجوزات النشطة للحاجز",
"booker_limit_exceeded_error_reschedule": "لديك بالفعل حجز لهذا الحدث في {{date}}. هل ترغب في إعادة الجدولة إلى الوقت الجديد المحدد؟",
"duration_min_error": "يجب أن تكون المدة على الأقل {{min}} دقيقة (دقائق)",
"duration_max_error": "لا يمكن أن تتجاوز المدة {{max}} دقيقة (24 ساعة)",
"booking_not_allowed_by_restriction_schedule_error": "الحجز خارج توفر جدول القيود.",
"restriction_schedule_not_found_error": "لم يتم العثور على جدول القيود",
"converted_image_size_limit_exceed": "تم تجاوز حد حجم الصورة، يرجى استخدام صورة أصغر يفضل أن تكون بتنسيق JPEG",
"license_key": "مفتاح الترخيص",
"license_selection_title": "اختر ترخيصك",
"license_selection_description": "حدد كيف تريد ترخيص نشر Cal.com الخاص بك",
"agplv3_license_description": "استخدم Cal.com تحت ترخيص AGPLv3 مفتوح المصدر",
"enter_license_key": "أدخل مفتاح الترخيص",
"enter_your_license_key": "الوصول إلى جميع ميزات المؤسسات",
"enter_existing_license": "ابتداءً من 15 دولارًا/مستخدم شهريًا",
"enter_license_key_description": "الوصول إلى جميع ميزات المؤسسات",
"license_key_placeholder": "أدخل مفتاح الترخيص الخاص بك",
"save_license_key": "حفظ مفتاح الترخيص",
"signature_token_optional": "رمز التوقيع (اختياري)",
"signature_token_description": "يستخدم رمز التوقيع للتحقق من أن مفتاح الترخيص هو ملكك عند زيادة الفوترة المستندة إلى الاستخدام. يمكنك العثور عليه في بريد تأكيد الشراء. إذا كنت تستخدم ترخيصًا قديمًا، يمكنك ترك هذا الحقل فارغًا.",
"need_a_license": "هل تحتاج إلى رخصة؟",
"license_key_required": "مفتاح الترخيص مطلوب",
"invalid_license_key": "مفتاح الترخيص غير صالح",
"license_validation_failed": "فشل التحقق من صحة مفتاح الترخيص",
"license_key_saved": "تم حفظ مفتاح الترخيص بنجاح",
"timezone_mismatch_tooltip": "أنت تشاهد التقرير بناءً على المنطقة الزمنية لملفك الشخصي ({{userTimezone}})، بينما متصفحك مضبوط على المنطقة الزمنية ({{browserTimezone}})",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
+174 -22
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "Cal Video Parametrlərini Fərdiləşdirin",
"calvideo_settings_description": "Təşkilatçılar və iştirakçılar üçün yazı icazələri də daxil olmaqla video görüş tərcihlərinizi konfiqurasiya edin.",
"disable_recording_for_guests": "Qonaqlar üçün yazını deaktiv edin",
"enable_automatic_transcription": "Görüşə qoşulduqdan sonra avtomatik transkripsiya funksiyasını aktivləşdir",
"video_options": "Video Seçimləri",
"get_meeting_session_details": "Görüş Sessiyasının Detallarını Əldə Et",
"meeting_session_details": "Görüş Sessiyasının Detalları",
"meeting_session": "Görüş Sessiyası",
"session_id": "Sessiya ID",
"max_participants": "Maksimum İştirakçı Sayı",
"unlimited": "Limitsiz",
"ongoing": "Davam edir",
"ended": "Bitib",
"participants": "İştirakçılar",
"anonymous_user": "Anonim İstifadəçi",
"session_duration": "Sessiya Müddəti",
"no_meeting_sessions_found": "Heç bir görüş sessiyası tapılmadı",
"not_available": "Mövcud deyil",
"disable_recording_for_organizer": "Təşkilatçı üçün yazını deaktiv edin",
"disable_transcription_for_guests": "Qonaqlar üçün transkripsiya funksiyasını deaktiv et",
"disable_transcription_for_organizer": "Təşkilatçı üçün transkripsiya funksiyasını deaktiv et",
"event_request_declined_recurring": "Təkrarlanan tədbir sorğunuz rədd edildi",
"event_request_cancelled": "Planlaşdırılmış tədbiriniz ləğv edildi",
"event_request_reassigned": "Planlaşdırılmış tədbir yenidən təyin edildi",
@@ -264,6 +281,8 @@
"guests": "Qonaqlar",
"guest": "Qonaq",
"web_conferencing_details_to_follow": "Veb konfransın təfərrüatları təsdiq e-poçtunda göndəriləcək.",
"confirmation": "Təsdiq",
"what_booker_should_provide": "Təsdiq almaq üçün rezervasiya edən şəxs nə təqdim etməlidir",
"404_the_user": "İstifadəçi adı",
"username": "İstifadəçi adı",
"is_still_available": "hələ də mövcuddur.",
@@ -331,7 +350,7 @@
"organization_general_description": "Komandanızın dil və saat qurşağı parametrlərini idarə edin",
"few_sentences_about_yourself": "Özünüz haqqında bir neçə cümlə. Bu, şəxsi URL səhifənizdə görünəcək.",
"nearly_there": "Demək olar ki, hazır!",
"nearly_there_instructions": "Son addım, qısa bir təsvir və fotoşəkil sizin rezervasiyalarınızı artırmağa və kimlə görüşəcəklərini bilməyə kömək edir.",
"nearly_there_instructions": "Son olaraq, özünüz haqqında qısa məlumat və bir foto rezervasiyaların alınmasına kömək edir və insanlara kiminlə görüşəcəklərini bildirir.",
"set_availability_instructions": "Təkrarlanan əsasda mövcud olduğunuz vaxt aralıqlarını təyin edin. Daha sonra bunlardan daha çoxunu yarada və müxtəlif təqvimlərə təyin edə bilərsiniz.",
"set_availability": "Mövcudluğunuzu təyin edin",
"set_availbility_description": "Rezervasiya olunmaq istədiyiniz vaxtlar üçün cədvəllər təyin edin.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "Təqviminizi bağlayın",
"connect_your_video_app": "Video tətbiqlərinizi bağlayın",
"connect_your_video_app_instructions": "Tədbir növlərinizdə istifadə etmək üçün video tətbiqlərinizi bağlayın.",
"connect_your_calendar_instructions": "Məşğul vaxtları və yeni tədbirləri avtomatik yoxlamaq üçün təqviminizi bağlayın.",
"connect_your_calendar_instructions": "Məşğul vaxtları və planlaşdırılan yeni tədbirləri avtomatik yoxlamaq üçün təqviminizi qoşun.",
"set_up_later": "Daha sonra qur",
"current_time": "Cari vaxt",
"details": "Detallar",
"welcome": "Xoş gəlmisiniz",
"welcome_back": "Yenidən xoş gəldiniz",
"welcome_to_calcom": "{{appName}}-a xoş gəldiniz",
"welcome_instructions": "Sizi necə çağıracağımızı və hansı zaman zonasındasınızsa, bizə bildirin. Bunu sonradan redaktə edə biləcəksiniz.",
"welcome_instructions": "Bizə sizə necə müraciət edəcəyimizi və hansı saat qurşağında olduğunuzu bildirin. Bunu sonradan redaktə edə biləcəksiniz.",
"connect_caldav": "CalDav-a qoşulun (Beta)",
"connect_ics_feed": "ICS Feed-ə qoşulun",
"connect": "Qoşulun",
@@ -361,7 +380,7 @@
"when": "Nə vaxt",
"where": "Harada",
"add_to_calendar": "Təqvimə əlavə edin",
"add_to_calendar_description": "Rezervasiya edildikdə hadisələri hara əlavə edəcəyinizi seçin.",
"add_to_calendar_description": "Rezervasiya olunduğunuzda tədbirləri hara əlavə edəcəyinizi seçin.",
"add_events_to": "Hadisələri əlavə edin",
"add_another_calendar": "Başqa bir təqvim əlavə edin",
"other": "Digər",
@@ -1153,6 +1172,7 @@
"test_failed": "Test uğursuz oldu",
"provide_api_key": "API açarını təmin edin",
"api_key_modal_subtitle": "API açarları öz hesabınız üçün API zəngləri etməyə imkan verir.",
"api_key_modal_subtitle_platform": "Bunun əvəzinə bazar yeri və ya platforma yaradın.",
"api_keys_subtitle": "Öz hesabınıza daxil olmaq üçün istifadə etmək üçün API açarları yaradın.",
"create_api_key": "API açarı yaradın",
"personal_note": "Bu açarı adlandırın",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "Bu API açarını ləğv edin",
"disable_rescheduling": "Yenidən planlaşdırmanı deaktiv et",
"description_disable_rescheduling": "Qonaqlar artıq təqvim dəvəti və ya e-poçt vasitəsilə tədbiri yenidən planlaşdıra bilməzlər",
"allow_rescheduling_cancelled_bookings": "Yenidən planlaşdırma linki vasitəsilə rezervasiyaya icazə verin",
"description_allow_rescheduling_cancelled_bookings": "Aktiv olduqda, istifadəçilər ləğv edilmiş rezervasiyanı yenidən planlaşdırmağa çalışarkən yeni rezervasiya yarada biləcəklər",
"disable_cancelling": "Ləğv etməni deaktiv et",
"description_disable_cancelling": "Qonaqlar artıq təqvim dəvəti və ya e-poçt vasitəsilə tədbiri ləğv edə bilməzlər",
"revoke_api_key": "API açarını ləğv et",
@@ -1675,7 +1697,7 @@
"create_first_api_key_description": "API açarları digər tətbiqlərin {{appName}} ilə əlaqə qurmasına imkan verir",
"back_to_signin": "Girişə geri dön",
"reset_link_sent": "Sıfırlama linki göndərildi",
"password_reset_email": "{{email}} ünvanına parolunuzu sıfırlamaq üçün təlimatlarla bir e-poçt göndərildi.",
"password_reset_email": "{{email}} ünvanına şifrənizi sıfırlamaq üçün təlimatlarla bir e-poçt göndərilir.",
"password_reset_leading": "Tezliklə e-poçt almırsınızsa, daxil etdiyiniz e-poçt ünvanının düzgün olduğundan əmin olun, spam qovluğunu yoxlayın və ya problem davam edərsə dəstək ilə əlaqə saxlayın.",
"password_updated": "Parol yeniləndi!",
"pending_payment": "Gözləyən ödəniş",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "Bu, bu hadisə növü üçün mövcudluğunuzu hər gün üçün bir vaxta məhdudlaşdıracaq, ən erkən mövcud vaxtda planlaşdırılacaq.",
"limit_total_booking_duration": "Ümumi rezervasiya müddətini məhdudlaşdırın",
"limit_total_booking_duration_description": "Bu hadisənin rezervasiya edilə biləcəyi ümumi vaxtı məhdudlaşdırın",
"booker_booking_limit": "Rezervasiya edənin aktiv rezervasiya limiti",
"booker_booking_limit_description": "Bu tədbir növü üçün rezervasiya edənin edə biləcəyi aktiv rezervasiyaların sayını məhdudlaşdırın",
"recurring_event_doesnt_support_booker_booking_limit": "Təkrarlanan tədbirlər rezervasiya edənin rezervasiya limitini dəstəkləmir",
"add_limit": "Məhdudiyyət əlavə edin",
"team_name_required": "Komanda adı tələb olunur",
"show_attendees": "Qonaqlar arasında iştirakçı məlumatlarını paylaş",
"show_available_seats_count": "Mövcud yerlərin sayını göstər",
"how_booking_questions_as_variables": "Rezervasiya suallarını dəyişənlər kimi necə istifadə etmək olar?",
"format": "Format",
"questions": "Suallar",
"all_info_your_booker_provide": "Rezervasiya edənin sizinlə rezervasiya etməzdən əvvəl təqdim etməli olduğu bütün məlumatlar.",
"uppercase_for_letters": "Bütün hərfləri böyük hərflərlə yaz",
"replace_whitespaces_underscores": "Boşluqları alt xətlərlə əvəz et",
"platform_members": "Platforma üzvləri",
@@ -1915,7 +1942,7 @@
"new_attendee": "Yeni İştirakçı",
"awaiting_approval": "Təsdiq gözləyir",
"requires_google_calendar": "Bu tətbiq Google Calendar bağlantısı tələb edir",
"event_type_requires_google_calendar": "Bu tədbir növü üçün \"Təqvimə əlavə et\" funksiyasının işləməsi üçün Google Calendar olmalıdır. Onu <0>burada</0> qoşun.",
"event_type_requires_google_calendar": "Bu tədbir növü üçün \"Təqvimə əlavə et\" funksiyasının Meet-in işləməsi üçün Google Calendar olması lazımdır. Onu <0>burada</0> qoşun.",
"connected_google_calendar": "Google Calendar hesabı bağlanıb.",
"using_meet_requires_calendar": "Google Meet istifadə etmək üçün bağlı Google Calendar tələb olunur",
"continue_to_install_google_calendar": "Google Calendar quraşdırmağa davam edin",
@@ -1967,12 +1994,12 @@
"delete_sso_configuration_confirmation_description": "{{connectionType}} konfiqurasiyasını silmək istədiyinizə əminsinizmi? {{connectionType}} girişi istifadə edən komanda üzvləriniz artıq Cal.com-a daxil ola bilməyəcəklər.",
"organizer_timezone": "Təşkilatçının saat qurşağı",
"email_user_cta": "Dəvəti Görüntülə",
"email_no_user_invite_heading_subteam": "Siz {{parentTeamName}} təşkilatının komandasına qoşulmağa dəvət olunmusunuz",
"email_no_user_invite_heading_org": "Siz {{appName}} təşkilatına qoşulmağa dəvət olunmusunuz",
"email_no_user_invite_heading_subteam": "{{parentTeamName}} təşkilatının komandasına qoşulmağa dəvət olunmusunuz",
"email_no_user_invite_heading_org": "{{appName}} təşkilatına qoşulmağa dəvət olunmusunuz",
"email_no_user_invite_subheading": "{{invitedBy}} sizi {{appName}} üzərindəki komandasına qoşulmağa dəvət edib. {{appName}} sizə və komandanıza görüşləri e-poçt mübadiləsi olmadan planlaşdırmağa imkan verən tədbir idarəetmə planlayıcısıdır.",
"email_user_invite_subheading_subteam": "{{invitedBy}} sizi {{appName}} üzərindəki {{parentTeamName}} təşkilatının {{teamName}} komandasına qoşulmağa dəvət edib. {{appName}} sizə və komandanıza görüşləri e-poçt mübadiləsi olmadan planlaşdırmağa imkan verən tədbir idarəetmə planlayıcısıdır.",
"email_user_invite_subheading_org": "{{invitedBy}} sizi {{appName}} üzərindəki {{teamName}} təşkilatına qoşulmağa dəvət edib. {{appName}} sizə və təşkilatınıza görüşləri e-poçt mübadiləsi olmadan planlaşdırmağa imkan verən tədbir idarəetmə planlayıcısıdır.",
"email_no_user_invite_steps_intro": "Sizi bir neçə qısa addımla tanış edəcəyik və tezliklə {{entity}} ilə stresssiz planlaşdırmadan həzz alacaqsınız.",
"email_no_user_invite_steps_intro": "Biz sizi bir neçə qısa addımla yönləndirəcəyik və qısa müddətdə {{entity}} ilə stressiz planlaşdırmadan həzz alacaqsınız.",
"email_no_user_step_one": "İstifadəçi adınızı seçin",
"email_no_user_step_two": "Təqvim hesabınızı bağlayın",
"email_no_user_step_three": "Mövcudluğunuzu təyin edin",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "İnsaytlarla məlumatlı qərarlar verin",
"make_informed_decisions_description": "İnsaytlar panelimiz komandanızın bütün fəaliyyətini göstərir və daha yaxşı komanda planlaşdırması və qərar qəbul etməyə imkan verən meylləri göstərir.",
"view_bookings_across": "Bütün üzvlərin rezervasiyalarına baxın",
"view_bookings_across_description": "Kimlərin ən çox rezervasiya aldığını görün və komandanız arasında ən yaxşı paylanmanı təmin edin",
"view_bookings_across_description": "Kimin ən çox rezervasiya qəbul etdiyini görün və komandanız arasında ən yaxşı paylanmanı təmin edin",
"identify_booking_trends": "Rezervasiya meyllərini müəyyən edin",
"identify_booking_trends_description": "Həftənin hansı günləri və günün hansı saatlarının rezervasiya edənlər üçün populyar olduğunu görün",
"spot_popular_event_types": "Populyar tədbir növlərini müəyyən edin",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "Seçilmiş filtr və ya tarixlər üçün məlumat tapılmadı.",
"acknowledge_booking_no_show_fee": "Bu tədbirə qatılmasam, kartıma {{amount, currency}} gəlməmə haqqı tətbiq olunacağını qəbul edirəm.",
"card_details": "Kart məlumatları",
"something_went_wrong_on_our_end": "Bizim tərəfimizdə bir şey səhv getdi. Dəstək komandamızla əlaqə saxlayın, və biz bunu dərhal düzəldəcəyik.",
"something_went_wrong_on_our_end": "Bizim tərəfimizdə bir şey səhv getdi. Dəstək komandamızla əlaqə saxlayın və biz bunu sizin üçün dərhal həll edəcəyik.",
"please_provide_following_text_to_suppport": "Dəstək ilə əlaqə saxlayarkən aşağıdakı mətni təqdim edin ki, sizə daha yaxşı kömək edə bilək",
"seats_and_no_show_fee_error": "Hazırda yerləri aktivləşdirmək və gəlməmə haqqını tutmaq mümkün deyil",
"complete_your_booking": "Rezervasiyanızı tamamlayın",
@@ -2276,7 +2303,7 @@
"messaging": "Mesajlaşma",
"sender_id_info": "SMS göndərən kimi göstərilən ad və ya nömrə (bəzi ölkələrdə alfasayısal göndərən ID-lərinə icazə verilmir)",
"org_admins_can_create_new_teams": "Yalnız təşkilatınızın administratoru yeni komandalar yarada bilər",
"google_new_spam_policy": "Google-un yeni spam siyasəti bu rezervasiya ilə bağlı hər hansı bir email və təqvim bildirişlərini almağınıza mane ola bilər.",
"google_new_spam_policy": "Google-un yeni spam siyasəti bu rezervasiya haqqında hər hansı e-poçt və təqvim bildirişləri almanızın qarşısını ala bilər.",
"resolve": "Həll et",
"no_organization_slug": "Bu təşkilat üçün komandalar yaradılarkən səhv baş verdi. URL slug çatışmır.",
"copy_link_org": "Təşkilatın linkini kopyalayın",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "Nümayəndəlik etimadnaməsi təşkilatınız üçün Google Workspace təqvimlərinə girişi idarə etməyə imkan verir.",
"add_client_id_in_google_workspace_with_below_scope": "Bu Client Id-ni aşağıdakı əhatə dairəsi ilə Google Workspace-ə əlavə edin",
"delegation_credential_enabled": "Nümayəndəlik etimadnaməsi aktivləşdirildi",
"delegation_credential_disabled": "Nümayəndəlik etimadnaməsi deaktiv edildi",
"add_delegation_credential": "Nümayəndəlik etimadnaməsi əlavə et",
"edit_delegation_credential": "Nümayəndəlik etimadnaməsini redaktə et",
"domain": "Domen",
@@ -2454,7 +2480,7 @@
"view_overlay_calendar_events": "Təqvim hadisələrinizi görərək toqquşmaların qarşısını alın.",
"join_event_location": "{{eventLocationType}}-ə qoşulun",
"troubleshooting": "Problemlərin həlli",
"calendars_were_checking_for_conflicts": "Toqquşmalar üçün yoxladığımız təqvimlər",
"calendars_were_checking_for_conflicts": "Münaqişələri yoxladığımız təqvimlər",
"availabilty_schedules": "Mövcudluq cədvəlləri",
"manage_calendars": "Təqvimləri idarə edin",
"manage_availability_schedules": "Mövcudluq cədvəllərini idarə edin",
@@ -2582,7 +2608,7 @@
"date": "Tarix",
"overlaps_with_existing_schedule": "Bu, mövcud cədvəllə üst-üstə düşür. Zəhmət olmasa fərqli bir tarix seçin.",
"org_admin_no_slots|heading": "{{name}} üçün mövcudluq tapılmadı",
"org_admin_no_slots|content": "Salam Təşkilat Administratorları,<br /><br />Xahiş edirik nəzərə alın: {{username}} istifadəçisinin {{username}}/{{slug}} ziyarət edildikdə heç bir mövcudluğu olmadığı diqqətimizə çatdırılıb.<br /><br />Bu bir neçə səbəbdən baş verə bilər:<br />İstifadəçinin heç bir təqvimi qoşulmayıb<br />Bu tədbirə bağlı cədvəlləri aktiv deyil<br /><br />Mövcudluqlarını yoxlamağı tövsiyə edirik.",
"org_admin_no_slots|content": "Salam Təşkilat Administratorları,<br /><br />Diqqətinizə çatdırırıq ki, istifadəçi {{username}}/{{slug}} səhifəsinə daxil olduqda {{username}} istifadəçisinin heç bir əlçatanlığı olmadığı diqqətimizə çatdırılıb<br /><br />Bunun baş verməsinin bir neçə səbəbi ola bilər<br />İstifadəçinin qoşulmuş təqvimləri yoxdur<br />Bu tədbirə əlavə edilmiş cədvəlləri aktiv deyil<br /> <br />Bu problemi həll etmək üçün onların əlçatanlığını yoxlamağı tövsiyə edirik.",
"org_admin_no_slots|cta": "İstifadəçilərin mövcudluğunu açın",
"organization_no_slots_notification_switch_title": "Komandanızın mövcudluğu olmadıqda bildirişlər alın",
"organization_no_slots_notification_switch_description": "İstifadəçi komanda üzvünü rezervasiya etməyə çalışdıqda və 'Mövcudluq yoxdur' ilə qarşılaşdıqda administratorlar e-poçt bildirişləri alacaqlar. Bu e-poçtu iki hadisədən sonra tetikleyirik və hər istifadəçi üçün hər 7 gündə bir xatırladırıq.",
@@ -2592,11 +2618,11 @@
"email_team_invite|subject|invited_to_org": "{{user}} sizi {{appName}} üzərində {{team}} təşkilatına qoşulmağa dəvət etdi",
"email_team_invite|subject|added_to_subteam": "{{user}} sizi {{appName}} üzərində {{parentTeamName}} təşkilatının {{team}} komandasına əlavə etdi",
"email_team_invite|subject|invited_to_regular_team": "{{user}} sizi {{appName}} platformasında {{team}} komandasına dəvət etdi",
"email_team_invite|heading|added_to_org": "{{appName}} təşkilatına əlavə edildiniz",
"email_team_invite|heading|invited_to_org": "{{appName}} təşkilatına dəvət edildiniz",
"email_team_invite|heading|added_to_subteam": "{{parentTeamName}} təşkilatının komandasına əlavə edildiniz",
"email_team_invite|heading|invited_to_subteam": "{{parentTeamName}} təşkilatının komandasına dəvət edildiniz",
"email_team_invite|heading|invited_to_regular_team": "{{appName}} komandasına dəvət edildiniz",
"email_team_invite|heading|added_to_org": "Siz {{appName}} təşkilatına əlavə edilmisiniz",
"email_team_invite|heading|invited_to_org": "Siz {{appName}} təşkilatına dəvət edilmisiniz",
"email_team_invite|heading|added_to_subteam": "Siz {{parentTeamName}} təşkilatının komandasına əlavə edilmisiniz",
"email_team_invite|heading|invited_to_subteam": "Siz {{parentTeamName}} təşkilatının komandasına dəvət edilmisiniz",
"email_team_invite|heading|invited_to_regular_team": "Siz {{appName}} komandasına qoşulmağa dəvət edilmisiniz",
"email_team_invite|content|added_to_org": "{{invitedBy}} sizi <strong>{{teamName}}</strong> təşkilatına əlavə etdi.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} sizi {{teamName}} təşkilatına qoşulmağa dəvət etdi.",
"email_team_invite|subject|invited_to_subteam": "{{user}} sizi {{appName}} üzərində {{parentTeamName}} təşkilatının {{team}} komandasına qoşulmağa dəvət etdi",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "Üzvlərin bütün komanda tədbir növləri üzrə neçə dəfə rezervasiya edilə biləcəyini məhdudlaşdırın",
"booking_limits_updated_successfully": "Rezervasiya məhdudiyyətləri uğurla yeniləndi",
"you_are_unauthorized_to_make_this_change_to_the_booking": "Bu dəyişikliyi etmək üçün səlahiyyətiniz yoxdur",
"delegation_credential_disabled": "Nümayəndəlik etimadnaməsi deaktiv edildi",
"matching_members": "Uyğun üzvlər",
"x_matching_members": "{{x}} uyğun üzv",
"matching_members_queue_using_attribute_weights": "Üzvlərin uyğunlaşdırılması növbəsi (atribut çəkilərindən istifadə edərək)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "Bəli, yenidən təyin et",
"filter_by_attributes": "Atributlara görə filtrlə",
"reassign_reason": "Yenidən təyin etmə səbəbi",
"field_type_change_suggestion": "Sahə növünün dəyişdirilməsi məlumat itkisinə səbəb ola bilər. Bunun əvəzinə yeni sahə yaradın.",
"reassigned_by": "Yenidən təyin edən",
"row": "sətir",
"rows": "sətirlər",
@@ -2902,7 +2930,7 @@
"routed_to_per_period": "Dövr üzrə yönləndirilmə",
"per_day": "Gün üzrə",
"per_week": "Həftə üzrə",
"per_month": "aylıq",
"per_month": "Ay üzrə",
"routing_form_insights_assignment_reason": "Təyin edilmə səbəbi",
"access_denied": "Giriş qadağandır",
"salesforce_route_to_owner": "Əlaqə sahibi mövcud olduqda Round Robin ev sahibi olacaq",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "Bu e-poçt üçün təşkilat yaratmağa icazəniz yoxdur",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "Bir və ya bir neçə komandanı köçürməyə icazəniz yoxdur",
"organization_has_subscription_already": "Təşkilatın artıq abunəliyi var",
"reset_interval_weighted_rr": "Çəkili Növbəli Bölüşdürmə üçün interval sıfırlama",
"interval": "İnterval",
"rr_reset_interval_description": "Tarazlı paylaşımı təmin etmək üçün round robin rezervasiya sayının nə qədər tez-tez sıfırlanacağını müəyyən edir.",
"daily": "Gündəlik",
@@ -3059,6 +3088,7 @@
"ooo_team_redirect_infinite_not_allowed": "Bu istifadəçilər arasında artıq əks yönləndirmə mövcuddur.",
"previous_ooo_empty_title": "Əvvəlki OOO tapılmadı",
"previous": "Əvvəlki",
"rr_interval_successfully_updated": "Növbəli Bölüşdürmə intervalı uğurla yeniləndi",
"trial_mode": "Sınaq rejimi",
"inactive_team_plan": "Qeyri-aktiv komanda planı",
"inactive_team_plan_description": "Komanda planınız qeyri-aktivdir. Abunəliyinizi yoxlayın və ya müştəri dəstəyi ilə əlaqə saxlayın",
@@ -3074,6 +3104,7 @@
"user_has_no_team_yet": "Hələ komandanız yoxdur",
"no_team_members": "Hələ komanda üzvləriniz yoxdur",
"recurring_event_doesnt_support_seats": "Təkrarlanan tədbir yerlər funksiyasını dəstəkləmir. Yerləri aktivləşdirmək üçün onu təkrarlanmayan edin.",
"booking_limit_per_booker_doesnt_support_recurring": "Təkrarlanan tədbir rezervasiya edən şəxs üçün rezervasiya limiti xüsusiyyətini dəstəkləmir. Təkrarlanan tədbiri aktivləşdirmək üçün rezervasiya edən şəxs üçün rezervasiya limitini deaktiv edin.",
"seats_doesnt_support_recurring": "Yerlər funksiyası təkrarlanan tədbiri dəstəkləmir. Təkrarlanma funksiyasını aktivləşdirmək üçün yerlər funksiyasını deaktiv edin.",
"recurring_event_seats_error": "Təkrarlanan tədbir yerlər funksiyasını dəstəkləmir. Yerlər funksiyasını deaktiv edin və ya tədbiri təkrarlanmayan edin.",
"segment": "Seqment",
@@ -3114,7 +3145,7 @@
"enable_delegation_credential": "Nümayəndəlik etimadnaməsini aktivləşdirin",
"enable_delegation_credential_description": "Nümayəndəlik etimadnaməsini aktivləşdirməklə Cal.com-a bütün təşkilat üzvlərinin təqvimlərinə avtomatik giriş imkanı verin.",
"disable_delegation_credential": "Nümayəndəlik etimadnaməsini deaktiv edin",
"disable_delegation_credential_description": "Nümayəndəlik etimadnaməsi deaktiv edildikdən sonra, təqvimlərini qoşmamış təşkilat üzvləri bunu əl ilə etməli olacaqlar.",
"disable_delegation_credential_description": "Nümayəndəlik etimadnaməsi deaktiv edildikdən sonra, təqvimlərini qoşmayan təşkilat üzvləri bunu əl ilə etməli olacaqlar.",
"last_active": "Son aktivlik",
"member_since": "Üzvlük tarixi",
"last_updated": "Son yenilənmə",
@@ -3202,10 +3233,131 @@
"matching": "Uyğunlaşdırma",
"event_redirect": "Tədbir yönləndirməsi",
"reset_form": "Formanı sıfırlayın",
"rr_settings_successfully_updated": "Növbəli Bölüşdürmə parametrləri uğurla yeniləndi",
"distribution_basis_weighted_rr": "Çəkili Növbəli Bölüşdürmə üçün paylanma əsası",
"timestamp_basis_description": "Çəkili növbəli bölüşdürmə paylanması üçün hansı tədbir vaxt damğasının əsas kimi istifadə olunduğunu müəyyən edir.",
"requires_credits": "Kreditlər tələb olunur",
"requires_credits_tooltip": "Bu xüsusiyyətdən istifadə etmək üçün hesabınızda kifayət qədər kredit olmalıdır",
"available_credits": "Mövcud kreditlər",
"create_role": "Rol yarat",
"roles_and_permissions": "Rollar və icazələr",
"roles_and_permissions_description": "Təşkilatınız üçün rolları və icazələri idarə edin",
"learn_more_permissions": "İcazələrin necə işlədiyini daha ətraflı öyrənin",
"role_name": "Rol adı",
"role_name_placeholder": "məs. Satış Komandası, Dəstək İşçiləri",
"role_description": "Rol təsviri",
"role_description_placeholder": "Bu rolun nə üçün olduğunu və hansı icazələrə ehtiyacı olduğunu təsvir edin",
"default_role": "Standart rol",
"custom_role": "Xüsusi rol",
"advanced": "İrəli",
"search_permissions": "İcazələri axtar",
"all_permissions": "Bütün icazələr",
"none": "Heç biri",
"read_only": "Yalnız oxuma",
"pbac_desc_all_actions_all_resources": "Bütün resurslarda bütün fəaliyyətlər",
"pbac_resource_all": "Bütün Resurslar",
"pbac_resource_event_type": "Tədbir Növləri",
"pbac_resource_team": "Komandalar",
"pbac_resource_organization": "Təşkilat",
"pbac_resource_booking": "Rezervasiyalar",
"pbac_resource_insights": "Görüşlər",
"pbac_resource_role": "Rollar",
"pbac_action_all": "Bütün Fəaliyyətlər",
"pbac_action_create": "Yaratmaq",
"pbac_action_read": "Baxmaq",
"pbac_action_update": "Redaktə etmək",
"pbac_action_delete": "Silmək",
"pbac_action_manage": "İdarə etmək",
"pbac_action_invite": "Dəvət etmək",
"pbac_action_remove": "Silmək",
"pbac_action_change_member_role": "Üzv Rolunu Dəyişmək",
"pbac_action_list_members": "Üzvləri Siyahılamaq",
"pbac_action_manage_billing": "Ödənişləri İdarə etmək",
"pbac_action_read_team_bookings": "Komanda Rezervasiyalarına Baxmaq",
"pbac_action_read_org_bookings": "Təşkilat Rezervasiyalarına Baxmaq",
"pbac_action_read_recordings": "Qeydlərə Baxın",
"role_created_successfully": "Rol uğurla yaradıldı",
"role_updated_successfully": "Rol uğurla yeniləndi",
"delete_role": "Rolu sil",
"confirm_delete_role": "Bu rolu silmək istədiyinizə əminsiniz? Bu əməliyyat geri qaytarıla bilməz.",
"role_deleted_successfully": "Rol uğurla silindi",
"error_creating_role": "Rol yaradılarkən xəta baş verdi",
"error_updating_role": "Rol yenilənərkən xəta baş verdi",
"pbac_desc_create_roles": "Rollar yaratmaq",
"pbac_desc_view_roles": "Rollara baxmaq",
"pbac_desc_update_roles": "Rolları yeniləmək",
"pbac_desc_delete_roles": "Rolları silmək",
"pbac_desc_manage_roles": "Rollar üzərində bütün əməliyyatlar",
"pbac_desc_create_event_types": "Tədbir növləri yaratmaq",
"pbac_desc_view_event_types": "Tədbir növlərinə baxmaq",
"pbac_desc_update_event_types": "Tədbir növlərini yeniləmək",
"pbac_desc_delete_event_types": "Tədbir növlərini silmək",
"pbac_desc_manage_event_types": "Tədbir növləri üzərində bütün əməliyyatlar",
"pbac_desc_create_teams": "Komandalar yaratmaq",
"pbac_desc_view_team_details": "Komanda məlumatlarına baxmaq",
"pbac_desc_update_team_settings": "Komanda parametrlərini yeniləmək",
"pbac_desc_delete_team": "Komandanı silmək",
"pbac_desc_invite_team_members": "Komanda üzvlərini dəvət etmək",
"pbac_desc_remove_team_members": "Komanda üzvlərini silmək",
"pbac_desc_change_team_member_role": "Komanda üzvlərinin rolunu dəyişmək",
"pbac_desc_manage_teams": "Komandalar üzərində bütün əməliyyatlar",
"pbac_desc_create_organization": "Təşkilat yaratmaq",
"pbac_desc_view_organization_details": "Təşkilat məlumatlarına baxmaq",
"pbac_desc_list_organization_members": "Təşkilat üzvlərinin siyahısı",
"pbac_desc_invite_organization_members": "Təşkilat üzvlərini dəvət etmək",
"pbac_desc_remove_organization_members": "Təşkilat üzvlərini silmək",
"pbac_desc_manage_organization_billing": "Təşkilat fakturasını idarə etmək",
"pbac_desc_change_organization_member_role": "Təşkilat üzvlərinin rolunu dəyişmək",
"pbac_desc_edit_organization_settings": "Təşkilat parametrlərini redaktə etmək",
"pbac_desc_manage_organizations": "Təşkilatlar üzərində bütün əməliyyatlar",
"pbac_desc_view_bookings": "Rezervasiyalara baxmaq",
"pbac_desc_view_team_bookings": "Komanda rezervasiyalarına baxmaq",
"pbac_desc_view_organization_bookings": "Təşkilat rezervasiyalarına baxmaq",
"pbac_desc_view_booking_recordings": "Rezervasiya qeydlərinə baxmaq",
"pbac_desc_update_bookings": "Rezervasiyaları yeniləmək",
"pbac_desc_manage_bookings": "Rezervasiyalar üzərində bütün əməliyyatlar",
"pbac_desc_view_team_insights": "Komanda analitikasına baxmaq",
"pbac_desc_manage_team_insights": "Komanda analitikasını idarə etmək",
"read_permission_auto_enabled_tooltip": "Resurs yaradarkən, yeniləyərkən və ya silənərkən oxuma icazəsi avtomatik olaraq aktivləşdirilir",
"choose_restriction_schedule": "Məhdudiyyət qrafiki əlavə edin",
"choose_restriction_schedule_description": "Bu tədbir növü üçün ev sahiblərinin əlçatanlığını məhdudiyyət qrafikinə əsasən məhdudlaşdırmaq istəyirsinizsə, bunu aktivləşdirin. Bu, yalnız məhdudiyyət qrafikində olmayan slotları silir, heç bir yeni slot əlavə etmir.",
"use_booker_timezone": "Rezervasiya edənin saat qurşağından istifadə edin",
"use_booker_timezone_info": "Seçilmiş məhdudiyyət qrafikini rezervasiya edənin saat qurşağında tətbiq edin",
"load_balancing_warning": "Bu, bütün round robin tədbir növlərinizdə yük balanslaşdırmasını deaktiv edəcək",
"rr_load_balancing_disabled": "Yük balanslaşdırması yalnız vaxt əsası Rezervasiya Yaradılma Vaxtına təyin edildikdə mövcuddur",
"round_robin_settings_description": "Bu komanda üçün standart növbəli təqvim parametrlərini fərdiləşdirin",
"round_robin_settings_updated_successfully": "Növbəli təqvim parametrləri uğurla yeniləndi",
"booking_creation_time": "Rezervasiya yaradılma vaxtı",
"meeting_start_time": "Görüş başlama vaxtı",
"members_affected_by_disabling_delegation_credential": "Təsir edilən üzvlər",
"no_members_affected_by_disabling_delegation_credential": "Nümayəndəlik etimadnaməsinin deaktiv edilməsindən təsirlənən üzv yoxdur",
"download_expense_log": "Xərc jurnalını yüklə",
"error_downloading_expense_log": "Xərc jurnalını yükləyərkən xəta baş verdi",
"offer_to_reschedule_last_booking": "Seçilmiş vaxt aralığına son aktiv rezervasiyanı yenidən planlaşdırmağı təklif edin",
"booker_limit_exceeded_error": "Rezervasiya edənin maksimum aktiv rezervasiya limiti aşıldı",
"booker_limit_exceeded_error_reschedule": "Artıq {{date}} tarixində bu tədbir üçün rezervasiyanız var. Yeni seçilmiş vaxta yenidən planlaşdırmaq istəyirsiniz?",
"duration_min_error": "Müddət ən azı {{min}} dəqiqə olmalıdır",
"duration_max_error": "Müddət {{max}} dəqiqəni (24 saat) keçə bilməz",
"booking_not_allowed_by_restriction_schedule_error": "Məhdudiyyət cədvəli xaricində rezervasiya mümkün deyil.",
"restriction_schedule_not_found_error": "Məhdudiyyət cədvəli tapılmadı",
"converted_image_size_limit_exceed": "Şəkil ölçüsü limiti aşıldı, lütfən daha kiçik şəkil istifadə edin, tərcihən JPEG formatında",
"license_key": "Lisenziya açarı",
"license_selection_title": "Lisenziyanızı seçin",
"license_selection_description": "Cal.com yerləşdirməsini necə lisenziyalaşdırmaq istədiyinizi seçin",
"agplv3_license_description": "Cal.com-u AGPLv3 açıq mənbə lisenziyası altında istifadə edin",
"enter_license_key": "Lisenziya açarını daxil edin",
"enter_your_license_key": "Bütün müəssisə xüsusiyyətlərinə giriş",
"enter_existing_license": "Hər istifadəçi üçün ayda $15-dan başlayaraq",
"enter_license_key_description": "Bütün müəssisə xüsusiyyətlərinə giriş",
"license_key_placeholder": "Lisenziya açarınızı daxil edin",
"save_license_key": "Lisenziya açarını saxla",
"signature_token_optional": "İmza tokeni (istəyə bağlı)",
"signature_token_description": "İmza tokeni istifadəyə əsaslanan ödənişləri artırarkən lisenziya açarının sizə aid olduğunu təsdiqləmək üçün istifadə olunur. Bunu alış təsdiq e-poçtunuzda tapa bilərsiniz. Əgər köhnə lisenziyadan istifadə edirsinizsə, bunu boş saxlaya bilərsiniz.",
"need_a_license": "Lisenziyaya ehtiyacınız var?",
"license_key_required": "Lisenziya açarı tələb olunur",
"invalid_license_key": "Yanlış lisenziya açarı",
"license_validation_failed": "Lisenziya açarının təsdiqlənməsi uğursuz oldu",
"license_key_saved": "Lisenziya açarı uğurla saxlanıldı",
"timezone_mismatch_tooltip": "Siz hesabatı profil saat qurşağınıza ({{userTimezone}}) əsasən görürsünüz, brauzeriniz isə başqa saat qurşağına ({{browserTimezone}}) təyin edilib",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
+168 -16
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "Персонализиране на настройките на Cal Video",
"calvideo_settings_description": "Конфигурирайте предпочитания за видео срещи, включително разрешения за запис за домакини и участници.",
"disable_recording_for_guests": "Деактивиране на записа за гости",
"enable_automatic_transcription": "Активиране на автоматична транскрипция след присъединяване към срещата",
"video_options": "Видео опции",
"get_meeting_session_details": "Получаване на детайли за сесията на срещата",
"meeting_session_details": "Детайли за сесията на срещата",
"meeting_session": "Сесия на срещата",
"session_id": "ID на сесията",
"max_participants": "Максимален брой участници",
"unlimited": "Неограничено",
"ongoing": "В процес",
"ended": "Приключила",
"participants": "Участници",
"anonymous_user": "Анонимен потребител",
"session_duration": "Продължителност на сесията",
"no_meeting_sessions_found": "Не са намерени сесии на срещи",
"not_available": "Не е налично",
"disable_recording_for_organizer": "Деактивиране на записа за организатора",
"disable_transcription_for_guests": "Деактивиране на транскрипцията за гости",
"disable_transcription_for_organizer": "Деактивиране на транскрипцията за организатора",
"event_request_declined_recurring": "Вашата заявка за повтарящо се събитие беше отказана",
"event_request_cancelled": "Вашето планирано събитие беше отменено",
"event_request_reassigned": "Планираното ви събитие беше преразпределено",
@@ -264,6 +281,8 @@
"guests": "Гости",
"guest": "Гост",
"web_conferencing_details_to_follow": "Детайлите за уеб конференцията ще бъдат изпратени в имейла за потвърждение.",
"confirmation": "Потвърждение",
"what_booker_should_provide": "Какво трябва да предостави резервиращият, за да получи потвърждения",
"404_the_user": "Потребителското име",
"username": "Потребителско име",
"is_still_available": "все още е свободно.",
@@ -331,7 +350,7 @@
"organization_general_description": "Управлявайте настройките за език и часова зона на вашия екип",
"few_sentences_about_yourself": "Няколко изречения за вас. Това ще се появи на вашата персонална URL страница.",
"nearly_there": "Почти сте готови!",
"nearly_there_instructions": "И последно, кратко описание за вас и снимка наистина помагат да получавате резервации и хората да знаят с кого си запазват час.",
"nearly_there_instructions": "Последно нещо, кратко описание за вас и снимка наистина помагат да получите резервации и да информирате хората с кого правят резервация.",
"set_availability_instructions": "Определете периодите от време, в които сте свободни на повтаряща се база. Можете да създадете още такива по-късно и да ги присвоите към различни календари.",
"set_availability": "Задайте своята наличност",
"set_availbility_description": "Задайте график за времето, в което искате да бъдете резервиран.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "Свържете календара си",
"connect_your_video_app": "Свържете видео приложенията си",
"connect_your_video_app_instructions": "Свържете видео приложенията си, за да ги използвате в типовете събития.",
"connect_your_calendar_instructions": "Свържете календара си, за да проверявате автоматично за заети часове и нови събития при планирането им.",
"connect_your_calendar_instructions": "Свържете календара си, за да проверявате автоматично за заети часове и нови събития, когато бъдат планирани.",
"set_up_later": "Настройте по-късно",
"current_time": "Текущо време",
"details": "Детайли",
"welcome": "Добре дошли",
"welcome_back": "Добре дошли отново",
"welcome_to_calcom": "Добре дошли в {{appName}}",
"welcome_instructions": "Кажете ни как да се обръщаме към вас и в коя часова зона се намирате. Можете да редактирате това по-късно.",
"welcome_instructions": "Кажете ни как да ви наричаме и ни уведомете в коя часова зона се намирате. Ще можете да редактирате това по-късно.",
"connect_caldav": "Свържете се с CalDav (Бета)",
"connect_ics_feed": "Свържете се с ICS поток",
"connect": "Свържете се",
@@ -1153,6 +1172,7 @@
"test_failed": "Тестът е неуспешен",
"provide_api_key": "Въведете API ключ",
"api_key_modal_subtitle": "API ключовете ви позволяват да правите API заявки за собствения си акаунт.",
"api_key_modal_subtitle_platform": "Създайте пазар или платформа вместо това.",
"api_keys_subtitle": "Генерирайте API ключове за достъп до собствения си акаунт.",
"create_api_key": "Създаване на API ключ",
"personal_note": "Именувайте този ключ",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "Отмени този API ключ",
"disable_rescheduling": "Деактивиране на пренасрочването",
"description_disable_rescheduling": "Гостите вече не могат да пренасрочват събитието чрез календарна покана или имейл",
"allow_rescheduling_cancelled_bookings": "Разрешаване на резервации чрез връзка за пренасрочване",
"description_allow_rescheduling_cancelled_bookings": "Когато е активирано, потребителите ще могат да създадат нова резервация, когато се опитват да пренасрочат отменена резервация",
"disable_cancelling": "Деактивиране на отказването",
"description_disable_cancelling": "Гостите вече не могат да отказват събитието чрез календарна покана или имейл",
"revoke_api_key": "Отмени API ключ",
@@ -1572,7 +1594,7 @@
"location_info": "Локацията на събитието",
"additional_notes_info": "Допълнителните бележки към резервацията",
"attendee_name_info": "Името на човека, който прави резервацията",
"organizer_name_info": "Името на организатора",
"organizer_name_info": "Име на организатора",
"to": "До",
"workflow_turned_on_successfully": "Работният процес {{workflowName}} е {{offOn}} успешно",
"download_responses": "Изтегли отговорите",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "Това ще ограничи вашата наличност за този тип събитие до един слот на ден, планиран за най-ранното възможно време.",
"limit_total_booking_duration": "Ограничаване на общата продължителност на резервациите",
"limit_total_booking_duration_description": "Ограничете общото време, за което това събитие може да бъде резервирано",
"booker_booking_limit": "Лимит на активни резервации за резервиращия",
"booker_booking_limit_description": "Ограничете броя на активните резервации, които резервиращият може да направи за този тип събитие",
"recurring_event_doesnt_support_booker_booking_limit": "Повтарящите се събития не поддържат лимит на резервации за резервиращия",
"add_limit": "Добави ограничение",
"team_name_required": "Изисква се име на екипа",
"show_attendees": "Споделяне на информация за участниците между гостите",
"show_available_seats_count": "Показване на броя свободни места",
"how_booking_questions_as_variables": "Как да използвате въпросите за резервация като променливи?",
"format": "Формат",
"questions": "Въпроси",
"all_info_your_booker_provide": "Цялата информация, която резервиращият трябва да предостави преди да резервира при вас.",
"uppercase_for_letters": "Използвайте главни букви за всички букви",
"replace_whitespaces_underscores": "Заменете интервалите с долни черти",
"platform_members": "Членове на платформата",
@@ -1915,7 +1942,7 @@
"new_attendee": "Нов участник",
"awaiting_approval": "Очаква одобрение",
"requires_google_calendar": "Това приложение изисква връзка с Google Calendar",
"event_type_requires_google_calendar": "\"Добавяне към календар\" за този тип събитие трябва да бъде Google Calendar, за да работи Meet. Свържете го <0>тук</0>.",
"event_type_requires_google_calendar": "\"Добавяне към календара\" за този тип събитие трябва да бъде Google Calendar, за да работи Meet. Свържете го <0>тук</0>.",
"connected_google_calendar": "Свързали сте акаунт в Google Calendar.",
"using_meet_requires_calendar": "Използването на Google Meet изисква свързан Google Calendar",
"continue_to_install_google_calendar": "Продължете към инсталиране на Google Calendar",
@@ -1968,11 +1995,11 @@
"organizer_timezone": "Часова зона на организатора",
"email_user_cta": "Преглед на поканата",
"email_no_user_invite_heading_subteam": "Поканени сте да се присъедините към екип от организацията {{parentTeamName}}",
"email_no_user_invite_heading_org": "Поканени сте да се присъедините към {{appName}} организация",
"email_no_user_invite_heading_org": "Поканени сте да се присъедините към организация в {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} ви покани да се присъедините към техния екип в {{appName}}. {{appName}} е платформа за управление на събития, която позволява на вас и вашия екип да планирате срещи без безкрайна размяна на имейли.",
"email_user_invite_subheading_subteam": "{{invitedBy}} ви покани да се присъедините към екипа {{teamName}} в тяхната организация {{parentTeamName}} в {{appName}}. {{appName}} е платформа за управление на събития, която позволява на вас и вашия екип да планирате срещи без безкрайна размяна на имейли.",
"email_user_invite_subheading_org": "{{invitedBy}} ви покани да се присъедините към тяхната организация {{teamName}} в {{appName}}. {{appName}} е платформа за управление на събития, която позволява на вас и вашата организация да планирате срещи без безкрайна размяна на имейли.",
"email_no_user_invite_steps_intro": "Ще ви преведем през няколко кратки стъпки и скоро ще се наслаждавате на безпроблемно планиране с вашия {{entity}}.",
"email_no_user_invite_steps_intro": "Ще ви преведем през няколко кратки стъпки и скоро ще се наслаждавате на планиране без стрес с вашия {{entity}}.",
"email_no_user_step_one": "Изберете потребителско име",
"email_no_user_step_two": "Свържете календарния си акаунт",
"email_no_user_step_three": "Задайте своята достъпност",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "Вземайте информирани решения с Insights",
"make_informed_decisions_description": "Нашото табло за управление Insights показва цялата активност в екипа ви и ви представя тенденции, които позволяват по-добро планиране и вземане на решения за екипа.",
"view_bookings_across": "Преглед на резервациите във всички членове",
"view_bookings_across_description": "Вижте кой получава най-много резервации и осигурете най-доброто разпределение в екипа си",
"view_bookings_across_description": "Вижте кой получава най-много резервации и осигурете най-доброто разпределение в екипа ви",
"identify_booking_trends": "Идентифицирайте тенденциите в резервациите",
"identify_booking_trends_description": "Вижте кои дни от седмицата и кои часове през деня са популярни сред вашите клиенти",
"spot_popular_event_types": "Открийте популярните видове събития",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "Не са намерени данни за избрания филтър или избраните дати.",
"acknowledge_booking_no_show_fee": "Потвърждавам, че ако не присъствам на това събитие, ще бъде начислена такса за неявяване в размер на {{amount, currency}} на моята карта.",
"card_details": "Данни на картата",
"something_went_wrong_on_our_end": "Възникна проблем от наша страна. Свържете се с екипа ни за поддръжка и ще го отстраним веднага.",
"something_went_wrong_on_our_end": "Нещо се обърка от наша страна. Свържете се с нашия екип за поддръжка и ние ще го оправим веднага за вас.",
"please_provide_following_text_to_suppport": "Моля, предоставете следния текст при контакт с поддръжката за по-добра помощ",
"seats_and_no_show_fee_error": "В момента не е възможно да се активират места и да се начислява такса за неявяване",
"complete_your_booking": "Завършете резервацията си",
@@ -2276,7 +2303,7 @@
"messaging": "Съобщения",
"sender_id_info": "Име или номер, показвани като подател на SMS (някои държави не позволяват буквено-цифрови идентификатори на подателя)",
"org_admins_can_create_new_teams": "Само администраторът на вашата организация може да създава нови екипи",
"google_new_spam_policy": "Новата политика на Google за спам може да попречи на получаването на имейли и календарни известия за тази резервация.",
"google_new_spam_policy": "Новата политика на Google за спам може да ви попречи да получавате имейли и известия в календара за тази резервация.",
"resolve": "Разреши",
"no_organization_slug": "Възникна грешка при създаването на екипи за тази организация. Липсва URL адрес.",
"copy_link_org": "Копирай линк към организацията",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "Делегираните идентификационни данни ви позволяват да управлявате достъпа до календарите на Google Workspace за вашата организация.",
"add_client_id_in_google_workspace_with_below_scope": "Добавете този Client Id в Google Workspace със следния обхват",
"delegation_credential_enabled": "Делегираните идентификационни данни са активирани",
"delegation_credential_disabled": "Делегираните идентификационни данни са деактивирани",
"add_delegation_credential": "Добавяне на делегирани идентификационни данни",
"edit_delegation_credential": "Редактиране на делегирани идентификационни данни",
"domain": "Домейн",
@@ -2582,7 +2608,7 @@
"date": "Дата",
"overlaps_with_existing_schedule": "Това се припокрива със съществуващ график. Моля, изберете друга дата.",
"org_admin_no_slots|heading": "No availability found for {{name}}",
"org_admin_no_slots|content": "Hello Organization Admins,<br /><br />Please note: It has been brought to our attention that {{username}} has not had any availability when a user has visited {{username}}/{{slug}}<br /><br />There’s a few reasons why this could be happening<br />The user does not have any calendars connected<br />Their schedules attached to this event are not enabled<br /> <br />We recommend checking their availability to resolve this.",
"org_admin_no_slots|content": "Здравейте, администратори на организацията,<br /><br />Моля, обърнете внимание: Беше ни съобщено, че {{username}} не е имал наличност, когато потребител е посетил {{username}}/{{slug}}<br /><br />Има няколко причини, поради които това може да се случва<br />Потребителят няма свързани календари<br />Графиците, прикрепени към това събитие, не са активирани<br /> <br />Препоръчваме да проверите наличността им, за да разрешите този проблем.",
"org_admin_no_slots|cta": "Open users availability",
"organization_no_slots_notification_switch_title": "Получавайте известия, когато вашият екип няма свободна заетост",
"organization_no_slots_notification_switch_description": "Администраторите ще получават имейл известия, когато потребител се опита да резервира член на екипа и се сблъска с 'Няма свободна заетост'. Изпращаме този имейл след две появявания и ви напомняме на всеки 7 дни за потребител.",
@@ -2594,8 +2620,8 @@
"email_team_invite|subject|invited_to_regular_team": "{{user}} ви покани да се присъедините към екипа {{team}} в {{appName}}",
"email_team_invite|heading|added_to_org": "Добавени сте към организация в {{appName}}",
"email_team_invite|heading|invited_to_org": "Поканени сте в организация в {{appName}}",
"email_team_invite|heading|added_to_subteam": "Добавени сте към екип от организацията {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "Поканени сте в екип от организацията {{parentTeamName}}",
"email_team_invite|heading|added_to_subteam": "Добавени сте към екип в организацията {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "Поканени сте в екип в организацията {{parentTeamName}}",
"email_team_invite|heading|invited_to_regular_team": "Поканени сте да се присъедините към екип в {{appName}}",
"email_team_invite|content|added_to_org": "{{invitedBy}} ви добави към организацията <strong>{{teamName}}</strong>.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} ви покани да се присъедините към организацията {{teamName}}.",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "Ограничете колко пъти членовете могат да бъдат резервирани във всички типове екипни събития",
"booking_limits_updated_successfully": "Ограниченията за резервации са актуализирани успешно",
"you_are_unauthorized_to_make_this_change_to_the_booking": "Нямате разрешение да правите тази промяна в резервацията",
"delegation_credential_disabled": "Делегираните идентификационни данни са деактивирани",
"matching_members": "Съвпадащи членове",
"x_matching_members": "{{x}} съвпадащи членове",
"matching_members_queue_using_attribute_weights": "Опашка на съвпадащи членове (използвайки тежест на атрибутите)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "Да, преназначи",
"filter_by_attributes": "Филтриране по атрибути",
"reassign_reason": "Причина за преназначаване",
"field_type_change_suggestion": "Промяната на типа поле може да доведе до загуба на данни. Моля, създайте ново поле вместо това.",
"reassigned_by": "Преназначено от",
"row": "ред",
"rows": "реда",
@@ -2902,7 +2930,7 @@
"routed_to_per_period": "Пренасочени към за период",
"per_day": "За ден",
"per_week": "За седмица",
"per_month": "на месец",
"per_month": "На месец",
"routing_form_insights_assignment_reason": "Причина за назначаване",
"access_denied": "Достъпът е отказан",
"salesforce_route_to_owner": "Собственикът на контакта ще бъде Round Robin домакин, ако е наличен",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "Нямате разрешение да създадете организация за този имейл",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "Нямате разрешение да мигрирате един или повече от екипите",
"organization_has_subscription_already": "Организацията вече има абонамент",
"reset_interval_weighted_rr": "Нулиране на интервала за претеглен кръгов ред",
"interval": "Интервал",
"rr_reset_interval_description": "Определя колко често се нулира броят на резервациите в round robin, за да се осигури балансирано разпределение.",
"daily": "Ежедневно",
@@ -3059,6 +3088,7 @@
"ooo_team_redirect_infinite_not_allowed": "Вече съществува обратно пренасочване между тези потребители.",
"previous_ooo_empty_title": "Не са намерени предишни OOO",
"previous": "Предишен",
"rr_interval_successfully_updated": "Интервалът на кръговия ред е успешно актуализиран",
"trial_mode": "Пробен режим",
"inactive_team_plan": "Неактивен план за екип",
"inactive_team_plan_description": "Вашият план за екип е неактивен. Проверете абонамента си или се свържете с отдела за обслужване на клиенти",
@@ -3074,6 +3104,7 @@
"user_has_no_team_yet": "Все още нямате екип",
"no_team_members": "Все още нямате членове на екипа",
"recurring_event_doesnt_support_seats": "Повтарящото се събитие не поддържа функцията за места. Направете го неповтарящо се, за да активирате места.",
"booking_limit_per_booker_doesnt_support_recurring": "Повтарящото се събитие не поддържа функцията за ограничаване на резервациите за всеки резервиращ. Деактивирайте ограничението за резервации за всеки резервиращ, за да активирате повтарящото се събитие.",
"seats_doesnt_support_recurring": "Функцията за места не поддържа повтарящи се събития. Деактивирайте функцията за места, за да го направите повтарящо се.",
"recurring_event_seats_error": "Повтарящото се събитие не поддържа функцията за места. Деактивирайте функцията за места или направете събитието неповтарящо се.",
"segment": "Сегмент",
@@ -3114,7 +3145,7 @@
"enable_delegation_credential": "Активиране на делегирани идентификационни данни",
"enable_delegation_credential_description": "Предоставете на Cal.com автоматичен достъп до календарите на всички членове на организацията чрез активиране на делегирани идентификационни данни.",
"disable_delegation_credential": "Деактивиране на делегирани идентификационни данни",
"disable_delegation_credential_description": "След деактивиране на делегираните идентификационни данни, членовете на организацията, които не са свързали календарите си, ще трябва да го направят ръчно.",
"disable_delegation_credential_description": "След като пълномощното за делегиране бъде деактивирано, членовете на организацията, които не са свързали календарите си, ще трябва да го направят ръчно.",
"last_active": "Последна активност",
"member_since": "Член от",
"last_updated": "Последно обновено",
@@ -3202,10 +3233,131 @@
"matching": "Съвпадение",
"event_redirect": "Пренасочване на събитие",
"reset_form": "Нулиране на формуляра",
"rr_settings_successfully_updated": "Настройките на кръговия ред са успешно актуализирани",
"distribution_basis_weighted_rr": "Основа за разпределение за претеглен кръгов ред",
"timestamp_basis_description": "Определя кой времеви печат на събитието се използва като основа за разпределение на претегления кръгов ред.",
"requires_credits": "Изисква кредити",
"requires_credits_tooltip": "Трябва да имате достатъчно кредити в акаунта си, за да използвате тази функция",
"available_credits": "Налични кредити",
"create_role": "Създаване на роля",
"roles_and_permissions": "Роли и разрешения",
"roles_and_permissions_description": "Управлявайте роли и разрешения за вашата организация",
"learn_more_permissions": "Научете повече за това как работят разрешенията",
"role_name": "Име на ролята",
"role_name_placeholder": "напр. Търговски екип, Екип за поддръжка",
"role_description": "Описание на ролята",
"role_description_placeholder": "Опишете за какво е тази роля и какви разрешения са ѝ необходими",
"default_role": "Роля по подразбиране",
"custom_role": "Персонализирана роля",
"advanced": "Разширени",
"search_permissions": "Търсене на разрешения",
"all_permissions": "Всички разрешения",
"none": "Няма",
"read_only": "Само за четене",
"pbac_desc_all_actions_all_resources": "Всички действия върху всички ресурси",
"pbac_resource_all": "Всички ресурси",
"pbac_resource_event_type": "Типове събития",
"pbac_resource_team": "Екипи",
"pbac_resource_organization": "Организация",
"pbac_resource_booking": "Резервации",
"pbac_resource_insights": "Анализи",
"pbac_resource_role": "Роли",
"pbac_action_all": "Всички действия",
"pbac_action_create": "Създаване",
"pbac_action_read": "Преглед",
"pbac_action_update": "Редактиране",
"pbac_action_delete": "Изтриване",
"pbac_action_manage": "Управление",
"pbac_action_invite": "Поканване",
"pbac_action_remove": "Премахване",
"pbac_action_change_member_role": "Промяна на ролята на член",
"pbac_action_list_members": "Списък с членове",
"pbac_action_manage_billing": "Управление на таксуването",
"pbac_action_read_team_bookings": "Преглед на резервациите на екипа",
"pbac_action_read_org_bookings": "Преглед на резервациите на организацията",
"pbac_action_read_recordings": "Преглед на записи",
"role_created_successfully": "Ролята е създадена успешно",
"role_updated_successfully": "Ролята е актуализирана успешно",
"delete_role": "Изтрий роля",
"confirm_delete_role": "Сигурни ли сте, че искате да изтриете тази роля? Това действие не може да бъде отменено.",
"role_deleted_successfully": "Ролята е изтрита успешно",
"error_creating_role": "Грешка при създаване на роля",
"error_updating_role": "Грешка при актуализиране на роля",
"pbac_desc_create_roles": "Създаване на роли",
"pbac_desc_view_roles": "Преглед на роли",
"pbac_desc_update_roles": "Актуализиране на роли",
"pbac_desc_delete_roles": "Изтриване на роли",
"pbac_desc_manage_roles": "Всички действия върху роли",
"pbac_desc_create_event_types": "Създаване на типове събития",
"pbac_desc_view_event_types": "Преглед на типове събития",
"pbac_desc_update_event_types": "Актуализиране на типове събития",
"pbac_desc_delete_event_types": "Изтриване на типове събития",
"pbac_desc_manage_event_types": "Всички действия върху типове събития",
"pbac_desc_create_teams": "Създаване на екипи",
"pbac_desc_view_team_details": "Преглед на детайли за екипа",
"pbac_desc_update_team_settings": "Актуализиране на настройки на екипа",
"pbac_desc_delete_team": "Изтриване на екип",
"pbac_desc_invite_team_members": "Поканване на членове на екипа",
"pbac_desc_remove_team_members": "Премахване на членове на екипа",
"pbac_desc_change_team_member_role": "Промяна на ролята на членове на екипа",
"pbac_desc_manage_teams": "Всички действия върху екипи",
"pbac_desc_create_organization": "Създаване на организация",
"pbac_desc_view_organization_details": "Преглед на детайли за организацията",
"pbac_desc_list_organization_members": "Списък на членовете на организацията",
"pbac_desc_invite_organization_members": "Покана на членове в организацията",
"pbac_desc_remove_organization_members": "Премахване на членове от организацията",
"pbac_desc_manage_organization_billing": "Управление на фактурирането на организацията",
"pbac_desc_change_organization_member_role": "Промяна на ролята на членовете на организацията",
"pbac_desc_edit_organization_settings": "Редактиране на настройките на организацията",
"pbac_desc_manage_organizations": "Всички действия върху организации",
"pbac_desc_view_bookings": "Преглед на резервации",
"pbac_desc_view_team_bookings": "Преглед на резервации на екипа",
"pbac_desc_view_organization_bookings": "Преглед на резервации на организацията",
"pbac_desc_view_booking_recordings": "Преглед на записи от резервации",
"pbac_desc_update_bookings": "Актуализиране на резервации",
"pbac_desc_manage_bookings": "Всички действия върху резервации",
"pbac_desc_view_team_insights": "Преглед на анализи на екипа",
"pbac_desc_manage_team_insights": "Управление на анализи на екипа",
"read_permission_auto_enabled_tooltip": "Разрешението за четене се активира автоматично при създаване, актуализиране или изтриване на ресурс",
"choose_restriction_schedule": "Добавяне на график с ограничения",
"choose_restriction_schedule_description": "Активирайте това, ако искате да ограничите наличността на домакините за този тип събитие въз основа на график с ограничения. Това само премахва слотовете, които не са в графика с ограничения, но не добавя нови слотове.",
"use_booker_timezone": "Използване на часовата зона на резервиращия",
"use_booker_timezone_info": "Прилагане на избрания график с ограничения в часовата зона на резервиращия",
"load_balancing_warning": "Това ще деактивира балансирането на натоварването за всички ваши round robin типове събития",
"rr_load_balancing_disabled": "Балансирането на натоварването е налично само когато основата на времевия печат е зададена на време на създаване на резервацията",
"round_robin_settings_description": "Персонализирайте настройките по подразбиране за кръговото разпределение за този екип",
"round_robin_settings_updated_successfully": "Настройките за кръгово разпределение са актуализирани успешно",
"booking_creation_time": "Време на създаване на резервацията",
"meeting_start_time": "Начален час на срещата",
"members_affected_by_disabling_delegation_credential": "Засегнати членове",
"no_members_affected_by_disabling_delegation_credential": "Няма членове, засегнати от деактивирането на пълномощията за делегиране",
"download_expense_log": "Изтеглете регистър на разходите",
"error_downloading_expense_log": "Грешка при изтегляне на регистъра на разходите",
"offer_to_reschedule_last_booking": "Предложете пренасрочване на последната активна резервация за избрания времеви интервал",
"booker_limit_exceeded_error": "Превишен е максималният лимит за активни резервации на резервиращия",
"booker_limit_exceeded_error_reschedule": "Вече имате резервация за това събитие на {{date}}. Искате ли да пренасрочите за новоизбраното време?",
"duration_min_error": "Продължителността трябва да бъде поне {{min}} минута(и)",
"duration_max_error": "Продължителността не може да надвишава {{max}} минути (24 часа)",
"booking_not_allowed_by_restriction_schedule_error": "Резервация извън наличността на ограничителния график.",
"restriction_schedule_not_found_error": "Ограничителният график не е намерен",
"converted_image_size_limit_exceed": "Превишен е лимитът за размер на изображението, моля, използвайте по-малко изображение, за предпочитане в JPEG формат",
"license_key": "Лицензионен ключ",
"license_selection_title": "Изберете вашия лиценз",
"license_selection_description": "Изберете как искате да лицензирате вашето внедряване на Cal.com",
"agplv3_license_description": "Използвайте Cal.com под AGPLv3 лиценз с отворен код",
"enter_license_key": "Въведете лицензионен ключ",
"enter_your_license_key": "Достъп до всички корпоративни функции",
"enter_existing_license": "Започва от $15/потребител на месец",
"enter_license_key_description": "Достъп до всички корпоративни функции",
"license_key_placeholder": "Въведете вашия лицензионен ключ",
"save_license_key": "Запазете лицензионния ключ",
"signature_token_optional": "Токен за подпис (по избор)",
"signature_token_description": "Токенът за подпис се използва за проверка дали лицензионният ключ е ваш при увеличаване на таксуването според използването. Можете да го намерите в имейла за потвърждение на покупката. Ако използвате стар лиценз, можете да оставите това поле празно.",
"need_a_license": "Нуждаете се от лиценз?",
"license_key_required": "Изисква се лицензионен ключ",
"invalid_license_key": "Невалиден лицензионен ключ",
"license_validation_failed": "Неуспешна валидация на лицензионния ключ",
"license_key_saved": "Лицензионният ключ е успешно записан",
"timezone_mismatch_tooltip": "Преглеждате отчета според часовата зона на вашия профил ({{userTimezone}}), докато браузърът ви е настроен на часова зона ({{browserTimezone}})",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавете новите си низове над този ред ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
File diff suppressed because it is too large Load Diff
+169 -17
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "Personalitza la configuració de Cal Video",
"calvideo_settings_description": "Configura les preferències de reunions de vídeo, incloent-hi els permisos d'enregistrament per a amfitrions i assistents.",
"disable_recording_for_guests": "Desactiva l'enregistrament per als convidats",
"enable_automatic_transcription": "Activar la transcripció automàtica després d'unir-se a la reunió",
"video_options": "Opcions de vídeo",
"get_meeting_session_details": "Obtenir detalls de la sessió de reunió",
"meeting_session_details": "Detalls de la sessió de reunió",
"meeting_session": "Sessió de reunió",
"session_id": "ID de sessió",
"max_participants": "Màxim de participants",
"unlimited": "Il·limitat",
"ongoing": "En curs",
"ended": "Finalitzada",
"participants": "Participants",
"anonymous_user": "Usuari anònim",
"session_duration": "Durada de la sessió",
"no_meeting_sessions_found": "No s'han trobat sessions de reunió",
"not_available": "No disponible",
"disable_recording_for_organizer": "Desactiva l'enregistrament per a l'organitzador",
"disable_transcription_for_guests": "Desactivar la transcripció per als convidats",
"disable_transcription_for_organizer": "Desactivar la transcripció per a l'organitzador",
"event_request_declined_recurring": "La teva sol·licitud d'esdeveniment recurrent ha estat rebutjada",
"event_request_cancelled": "El teu esdeveniment programat ha estat cancel·lat",
"event_request_reassigned": "El teu esdeveniment programat ha estat reassignat",
@@ -264,6 +281,8 @@
"guests": "Convidats",
"guest": "Convidat",
"web_conferencing_details_to_follow": "Els detalls de la videoconferència s'enviaran al correu de confirmació.",
"confirmation": "Confirmació",
"what_booker_should_provide": "Què ha de proporcionar la persona que reserva per rebre confirmacions",
"404_the_user": "El nom d'usuari",
"username": "Nom d'usuari",
"is_still_available": "encara està disponible.",
@@ -331,7 +350,7 @@
"organization_general_description": "Gestiona la configuració d'idioma i zona horària del teu equip",
"few_sentences_about_yourself": "Unes quantes frases sobre tu. Això apareixerà a la teva pàgina d'URL personal.",
"nearly_there": "Ja quasi hi som!",
"nearly_there_instructions": "Última cosa, una breu descripció sobre tu i una foto realment ajuden a aconseguir reserves i permeten que la gent sàpiga amb qui estan reservant.",
"nearly_there_instructions": "Última cosa, una breu descripció sobre tu i una foto realment ajuda a aconseguir reserves i permet a les persones saber amb qui estan reservant.",
"set_availability_instructions": "Defineix rangs de temps en què estàs disponible de manera recurrent. Pots crear-ne més després i assignar-los a diferents calendaris.",
"set_availability": "Estableix la teva disponibilitat",
"set_availbility_description": "Estableix horaris per als moments en què vols estar disponible per a reserves.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "Connecta el teu calendari",
"connect_your_video_app": "Connecta les teves apps de vídeo",
"connect_your_video_app_instructions": "Connecta les teves apps de vídeo per utilitzar-les als teus tipus d'esdeveniments.",
"connect_your_calendar_instructions": "Connecta el teu calendari per comprovar automàticament les hores ocupades i els nous esdeveniments a mesura que es programin.",
"connect_your_calendar_instructions": "Connecta el teu calendari per comprovar automàticament els horaris ocupats i els nous esdeveniments a mesura que es programen.",
"set_up_later": "Configura-ho més tard",
"current_time": "Hora actual",
"details": "Detalls",
"welcome": "Benvingut/da",
"welcome_back": "Benvingut/da de nou",
"welcome_to_calcom": "Benvingut/da a {{appName}}",
"welcome_instructions": "Digue'ns com vols que t'anomenem i indica'ns la teva zona horària. Ho podràs editar més endavant.",
"welcome_instructions": "Digue'ns com vols que t'anomenem i fes-nos saber en quina zona horària et trobes. Podràs editar això més endavant.",
"connect_caldav": "Connecta amb CalDav (Beta)",
"connect_ics_feed": "Connecta amb un feed ICS",
"connect": "Connecta",
@@ -361,7 +380,7 @@
"when": "Quan",
"where": "On",
"add_to_calendar": "Afegeix al calendari",
"add_to_calendar_description": "Selecciona on vols afegir els esdeveniments quan et reservin.",
"add_to_calendar_description": "Selecciona on afegir esdeveniments quan et reservin.",
"add_events_to": "Afegeix esdeveniments a",
"add_another_calendar": "Afegeix un altre calendari",
"other": "Altres",
@@ -1153,6 +1172,7 @@
"test_failed": "Prova fallida",
"provide_api_key": "Proporciona la clau API",
"api_key_modal_subtitle": "Les claus API et permeten fer crides a l'API pel teu propi compte.",
"api_key_modal_subtitle_platform": "Crea un mercat o plataforma en lloc d'això.",
"api_keys_subtitle": "Genera claus API per accedir al teu propi compte.",
"create_api_key": "Crea una clau API",
"personal_note": "Anomena aquesta clau",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "Revocar aquesta clau API",
"disable_rescheduling": "Desactivar reprogramació",
"description_disable_rescheduling": "Els convidats ja no poden reprogramar l'esdeveniment amb la invitació del calendari o el correu electrònic",
"allow_rescheduling_cancelled_bookings": "Permetre reserves a través de l'enllaç de reprogramació",
"description_allow_rescheduling_cancelled_bookings": "Quan està activat, els usuaris podran crear una nova reserva quan intentin reprogramar una reserva cancel·lada",
"disable_cancelling": "Desactivar cancel·lació",
"description_disable_cancelling": "Els convidats ja no poden cancel·lar l'esdeveniment amb la invitació del calendari o el correu electrònic",
"revoke_api_key": "Revocar clau API",
@@ -1675,7 +1697,7 @@
"create_first_api_key_description": "Les claus API permeten que altres aplicacions es comuniquin amb {{appName}}",
"back_to_signin": "Torna a l'inici de sessió",
"reset_link_sent": "Enllaç de restabliment enviat",
"password_reset_email": "S'ha enviat un correu a {{email}} amb instruccions per restablir la teva contrasenya",
"password_reset_email": "S'ha enviat un correu electrònic a {{email}} amb instruccions per restablir la teva contrasenya.",
"password_reset_leading": "Si no reps un correu aviat, comprova que l'adreça de correu que has introduït és correcta, revisa la carpeta de spam o contacta amb suport si el problema persisteix",
"password_updated": "Contrasenya actualitzada!",
"pending_payment": "Pagament pendent",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "Això limitarà la teva disponibilitat per a aquest tipus d'esdeveniment a un espai per dia, programat en el primer horari disponible.",
"limit_total_booking_duration": "Limitar la durada total de les reserves",
"limit_total_booking_duration_description": "Limita el temps total que es pot reservar aquest esdeveniment",
"booker_booking_limit": "Límit de reserves actives per reservant",
"booker_booking_limit_description": "Limita el nombre de reserves actives que un reservant pot fer per a aquest tipus d'esdeveniment",
"recurring_event_doesnt_support_booker_booking_limit": "Els esdeveniments recurrents no admeten el límit de reserves per reservant",
"add_limit": "Afegir límit",
"team_name_required": "El nom de l'equip és obligatori",
"show_attendees": "Compartir informació dels assistents entre els convidats",
"show_available_seats_count": "Mostrar el nombre de places disponibles",
"how_booking_questions_as_variables": "Com utilitzar les preguntes de reserva com a variables?",
"format": "Format",
"questions": "Preguntes",
"all_info_your_booker_provide": "Tota la informació que el teu reservant hauria de proporcionar abans de reservar amb tu.",
"uppercase_for_letters": "Utilitza majúscules per a totes les lletres",
"replace_whitespaces_underscores": "Substitueix els espais en blanc per guions baixos",
"platform_members": "Membres de la plataforma",
@@ -1972,7 +1999,7 @@
"email_no_user_invite_subheading": "{{invitedBy}} t'ha convidat a unir-te al seu equip a {{appName}}. {{appName}} és el programador de reunions que us permet a tu i al teu equip programar trobades sense el ping-pong dels correus electrònics.",
"email_user_invite_subheading_subteam": "{{invitedBy}} t'ha convidat a unir-te a l'equip {{teamName}} de la seva organització {{parentTeamName}} a {{appName}}. {{appName}} és el programador de reunions que us permet a tu i al teu equip programar trobades sense el ping-pong dels correus electrònics.",
"email_user_invite_subheading_org": "{{invitedBy}} t'ha convidat a unir-te a la seva organització {{teamName}} a {{appName}}. {{appName}} és el programador de reunions que us permet a tu i a la teva organització programar trobades sense el ping-pong dels correus electrònics.",
"email_no_user_invite_steps_intro": "Et guiarem a través d'uns breus passos i podràs gaudir de la programació sense estrès amb el teu {{entity}} en poc temps.",
"email_no_user_invite_steps_intro": "Et guiarem a través d'uns breus passos i aviat podràs gaudir de programació sense estrès amb la teva {{entity}}.",
"email_no_user_step_one": "Tria el teu nom d'usuari",
"email_no_user_step_two": "Connecta el teu compte de calendari",
"email_no_user_step_three": "Estableix la teva disponibilitat",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "Pren decisions informades amb Insights",
"make_informed_decisions_description": "El nostre tauler d'Insights mostra tota l'activitat del teu equip i et presenta tendències que permeten una millor programació i presa de decisions en equip.",
"view_bookings_across": "Visualitza les reserves de tots els membres",
"view_bookings_across_description": "Comprova qui rep més reserves i assegura la millor distribució entre el teu equip",
"view_bookings_across_description": "Mira qui està rebent més reserves i assegura't de la millor distribució entre el teu equip",
"identify_booking_trends": "Identifica tendències de reserves",
"identify_booking_trends_description": "Descobreix quins dies de la setmana i quines hores del dia són més populars per als teus clients",
"spot_popular_event_types": "Detecta els tipus d'esdeveniments populars",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "No s'han trobat dades per al filtre seleccionat o les dates seleccionades.",
"acknowledge_booking_no_show_fee": "Reconec que si no assisteixo a aquest esdeveniment, s'aplicarà una penalització de {{amount, currency}} a la meva targeta.",
"card_details": "Detalls de la targeta",
"something_went_wrong_on_our_end": "Hi ha hagut un error per la nostra part. Contacta amb el nostre equip de suport i ho solucionarem de seguida.",
"something_went_wrong_on_our_end": "Alguna cosa ha anat malament per la nostra part. Contacta amb el nostre equip de suport i ho solucionarem de seguida per a tu.",
"please_provide_following_text_to_suppport": "Si us plau, proporciona el següent text quan contactis amb suport per ajudar-te millor",
"seats_and_no_show_fee_error": "Actualment no es poden habilitar seients i cobrar una penalització per incompareixença",
"complete_your_booking": "Completa la teva reserva",
@@ -2276,7 +2303,7 @@
"messaging": "Missatgeria",
"sender_id_info": "Nom o número que es mostra com a remitent d'un SMS (alguns països no permeten IDs alfanumèrics)",
"org_admins_can_create_new_teams": "Només l'administrador de la vostra organització pot crear nous equips",
"google_new_spam_policy": "La nova política de spam de Google podria impedir que rebeu correus electrònics i notificacions de calendari sobre aquesta reserva.",
"google_new_spam_policy": "La nova política de spam de Google podria impedir que rebis correus electrònics i notificacions de calendari sobre aquesta reserva.",
"resolve": "Resoldre",
"no_organization_slug": "Hi ha hagut un error en crear els equips per a aquesta organització. Falta l'slug de l'URL.",
"copy_link_org": "Copiar l'enllaç de l'organització",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "La credencial de delegació us permet gestionar l'accés als calendaris de Google Workspace per a la vostra organització.",
"add_client_id_in_google_workspace_with_below_scope": "Afegiu aquest ID de client a Google Workspace amb l'abast següent",
"delegation_credential_enabled": "Credencial de delegació activada",
"delegation_credential_disabled": "Credencial de delegació desactivada",
"add_delegation_credential": "Afegir credencial de delegació",
"edit_delegation_credential": "Editar credencial de delegació",
"domain": "Domini",
@@ -2582,7 +2608,7 @@
"date": "Data",
"overlaps_with_existing_schedule": "Això es solapa amb un horari existent. Si us plau, selecciona una data diferent.",
"org_admin_no_slots|heading": "No s'ha trobat disponibilitat per a {{name}}",
"org_admin_no_slots|content": "Hola administradors de l'organització,<br /><br />Tingueu en compte: Hem detectat que {{username}} no ha tingut disponibilitat quan un usuari ha visitat {{username}}/{{slug}}<br /><br />Hi ha diversos motius pels quals això pot passar:<br />L'usuari no té cap calendari connectat<br />Els seus horaris vinculats a aquest esdeveniment no estan activats<br /><br />Recomanem revisar la seva disponibilitat per resoldre-ho.",
"org_admin_no_slots|content": "Hola Administradors de l'Organització,<br /><br />Tingueu en compte: S'ha portat a la nostra atenció que {{username}} no ha tingut disponibilitat quan un usuari ha visitat {{username}}/{{slug}}<br /><br />Hi ha algunes raons per les quals això podria estar passant<br />L'usuari no té cap calendari connectat<br />Els seus horaris adjunts a aquest esdeveniment no estan activats<br /> <br />Recomanem comprovar la seva disponibilitat per resoldre això.",
"org_admin_no_slots|cta": "Obre la disponibilitat dels usuaris",
"organization_no_slots_notification_switch_title": "Rep notificacions quan el teu equip no tingui disponibilitat",
"organization_no_slots_notification_switch_description": "Els administradors rebran notificacions per correu electrònic quan un usuari intenti reservar amb un membre de l'equip i es trobi amb 'Sense disponibilitat'. Activem aquest correu després de dues ocurrències i t'ho recordem cada 7 dies per usuari.",
@@ -2592,11 +2618,11 @@
"email_team_invite|subject|invited_to_org": "{{user}} t'ha convidat a unir-te a l'organització {{team}} a {{appName}}",
"email_team_invite|subject|added_to_subteam": "{{user}} t'ha afegit a l'equip {{team}} de l'organització {{parentTeamName}} a {{appName}}",
"email_team_invite|subject|invited_to_regular_team": "{{user}} t'ha convidat a unir-te a l'equip {{team}} a {{appName}}",
"email_team_invite|heading|added_to_org": "T'han afegit a una organització de {{appName}}",
"email_team_invite|heading|invited_to_org": "T'han convidat a una organització de {{appName}}",
"email_team_invite|heading|added_to_subteam": "T'han afegit a un equip de l'organització {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "T'han convidat a un equip de l'organització {{parentTeamName}}",
"email_team_invite|heading|invited_to_regular_team": "T'han convidat a unir-te a un equip de {{appName}}",
"email_team_invite|heading|added_to_org": "Has estat afegit a una organització de {{appName}}",
"email_team_invite|heading|invited_to_org": "Has estat convidat a una organització de {{appName}}",
"email_team_invite|heading|added_to_subteam": "Has estat afegit a un equip de l'organització {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "Has estat convidat a un equip de l'organització {{parentTeamName}}",
"email_team_invite|heading|invited_to_regular_team": "Has estat convidat a unir-te a un equip de {{appName}}",
"email_team_invite|content|added_to_org": "{{invitedBy}} t'ha afegit a l'organització <strong>{{teamName}}</strong>.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} t'ha convidat a unir-te a l'organització {{teamName}}.",
"email_team_invite|subject|invited_to_subteam": "{{user}} t'ha convidat a unir-te a l'equip {{team}} de l'organització {{parentTeamName}} a {{appName}}",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "Limita quantes vegades els membres poden ser reservats en tots els tipus d'esdeveniments d'equip",
"booking_limits_updated_successfully": "Límits de reserva actualitzats correctament",
"you_are_unauthorized_to_make_this_change_to_the_booking": "No estàs autoritzat per fer aquest canvi a la reserva",
"delegation_credential_disabled": "Credencial de delegació desactivada",
"matching_members": "Membres coincidents",
"x_matching_members": "{{x}} membres coincidents",
"matching_members_queue_using_attribute_weights": "Cua de membres coincidents (utilitzant pesos d'atributs)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "Sí, reassignar",
"filter_by_attributes": "Filtra per atributs",
"reassign_reason": "Motiu de la reassignació",
"field_type_change_suggestion": "Canviar el tipus de camp pot causar pèrdua de dades. Si us plau, crea un nou camp en lloc d'això.",
"reassigned_by": "Reassignat per",
"row": "fila",
"rows": "files",
@@ -2902,7 +2930,7 @@
"routed_to_per_period": "Redirigit a per període",
"per_day": "Per dia",
"per_week": "Per setmana",
"per_month": "al mes",
"per_month": "Per mes",
"routing_form_insights_assignment_reason": "Motiu d'assignació",
"access_denied": "Accés denegat",
"salesforce_route_to_owner": "El propietari del contacte serà l'amfitrió del Round Robin si està disponible",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "No teniu permís per crear una organització per a aquest correu electrònic",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "No teniu permís per migrar un o més dels equips",
"organization_has_subscription_already": "L'organització ja té una subscripció",
"reset_interval_weighted_rr": "Restableix l'interval per a Round Robin ponderat",
"interval": "Interval",
"rr_reset_interval_description": "Determina amb quina freqüència es reinicia el comptador de reserves del round robin per assegurar una distribució equilibrada.",
"daily": "Diàriament",
@@ -3059,6 +3088,7 @@
"ooo_team_redirect_infinite_not_allowed": "Ja hi ha una redirecció inversa entre aquests usuaris.",
"previous_ooo_empty_title": "No s'ha trobat cap OOO anterior",
"previous": "Anterior",
"rr_interval_successfully_updated": "Interval de Round Robin actualitzat correctament",
"trial_mode": "Mode de prova",
"inactive_team_plan": "Pla d'equip inactiu",
"inactive_team_plan_description": "El teu pla d'equip està inactiu. Comprova la teva subscripció o contacta amb el suport al client",
@@ -3074,6 +3104,7 @@
"user_has_no_team_yet": "Encara no tens cap equip",
"no_team_members": "Encara no tens membres a l'equip",
"recurring_event_doesnt_support_seats": "Els esdeveniments recurrents no admeten la funció de seients. Feu-lo no recurrent per habilitar els seients.",
"booking_limit_per_booker_doesnt_support_recurring": "L'esdeveniment recurrent no admet la funció de límit de reserves per reservant. Desactiva el límit de reserves per reservant per habilitar l'esdeveniment recurrent.",
"seats_doesnt_support_recurring": "La funció de seients no admet esdeveniments recurrents. Desactiveu la funció de seients per fer-lo recurrent.",
"recurring_event_seats_error": "Els esdeveniments recurrents no admeten la funció de seients. Desactiveu la funció de seients o feu que l'esdeveniment no sigui recurrent.",
"segment": "Segment",
@@ -3202,10 +3233,131 @@
"matching": "Coincidència",
"event_redirect": "Redirecció d'esdeveniments",
"reset_form": "Restablir formulari",
"rr_settings_successfully_updated": "Configuració de Round Robin actualitzada correctament",
"distribution_basis_weighted_rr": "Base de distribució per a Round Robin ponderat",
"timestamp_basis_description": "Determina quina marca de temps de l'esdeveniment s'utilitza com a base per a la distribució ponderada de round robin.",
"requires_credits": "Requereix crèdits",
"requires_credits_tooltip": "Necessites tenir suficients crèdits al teu compte per utilitzar aquesta funcionalitat",
"available_credits": "Crèdits disponibles",
"create_role": "Crea un rol",
"roles_and_permissions": "Rols i permisos",
"roles_and_permissions_description": "Gestiona els rols i permisos per a la teva organització",
"learn_more_permissions": "Aprèn més sobre com funcionen els permisos",
"role_name": "Nom del rol",
"role_name_placeholder": "p. ex. Equip de vendes, Personal de suport",
"role_description": "Descripció del rol",
"role_description_placeholder": "Descriu per a què serveix aquest rol i quins permisos necessita",
"default_role": "Rol predeterminat",
"custom_role": "Rol personalitzat",
"advanced": "Avançat",
"search_permissions": "Cerca permisos",
"all_permissions": "Tots els permisos",
"none": "Cap",
"read_only": "Només lectura",
"pbac_desc_all_actions_all_resources": "Totes les accions en tots els recursos",
"pbac_resource_all": "Tots els recursos",
"pbac_resource_event_type": "Tipus d'esdeveniments",
"pbac_resource_team": "Equips",
"pbac_resource_organization": "Organització",
"pbac_resource_booking": "Reserves",
"pbac_resource_insights": "Estadístiques",
"pbac_resource_role": "Rols",
"pbac_action_all": "Totes les accions",
"pbac_action_create": "Crear",
"pbac_action_read": "Veure",
"pbac_action_update": "Editar",
"pbac_action_delete": "Eliminar",
"pbac_action_manage": "Gestionar",
"pbac_action_invite": "Convidar",
"pbac_action_remove": "Eliminar",
"pbac_action_change_member_role": "Canviar rol de membre",
"pbac_action_list_members": "Llistar membres",
"pbac_action_manage_billing": "Gestionar facturació",
"pbac_action_read_team_bookings": "Veure reserves d'equip",
"pbac_action_read_org_bookings": "Veure reserves d'organització",
"pbac_action_read_recordings": "Veure enregistraments",
"role_created_successfully": "Rol creat correctament",
"role_updated_successfully": "Rol actualitzat correctament",
"delete_role": "Eliminar rol",
"confirm_delete_role": "Estàs segur que vols eliminar aquest rol? Aquesta acció no es pot desfer.",
"role_deleted_successfully": "Rol eliminat correctament",
"error_creating_role": "Error en crear el rol",
"error_updating_role": "Error en actualitzar el rol",
"pbac_desc_create_roles": "Crear rols",
"pbac_desc_view_roles": "Veure rols",
"pbac_desc_update_roles": "Actualitzar rols",
"pbac_desc_delete_roles": "Eliminar rols",
"pbac_desc_manage_roles": "Totes les accions sobre rols",
"pbac_desc_create_event_types": "Crear tipus d'esdeveniments",
"pbac_desc_view_event_types": "Veure tipus d'esdeveniments",
"pbac_desc_update_event_types": "Actualitzar tipus d'esdeveniments",
"pbac_desc_delete_event_types": "Eliminar tipus d'esdeveniments",
"pbac_desc_manage_event_types": "Totes les accions sobre tipus d'esdeveniments",
"pbac_desc_create_teams": "Crear equips",
"pbac_desc_view_team_details": "Veure detalls de l'equip",
"pbac_desc_update_team_settings": "Actualitzar configuració de l'equip",
"pbac_desc_delete_team": "Eliminar equip",
"pbac_desc_invite_team_members": "Convidar membres a l'equip",
"pbac_desc_remove_team_members": "Eliminar membres de l'equip",
"pbac_desc_change_team_member_role": "Canviar el rol dels membres de l'equip",
"pbac_desc_manage_teams": "Totes les accions sobre equips",
"pbac_desc_create_organization": "Crear organització",
"pbac_desc_view_organization_details": "Veure detalls de l'organització",
"pbac_desc_list_organization_members": "Llistar membres de l'organització",
"pbac_desc_invite_organization_members": "Convidar membres a l'organització",
"pbac_desc_remove_organization_members": "Eliminar membres de l'organització",
"pbac_desc_manage_organization_billing": "Gestionar la facturació de l'organització",
"pbac_desc_change_organization_member_role": "Canviar el rol dels membres de l'organització",
"pbac_desc_edit_organization_settings": "Editar configuració de l'organització",
"pbac_desc_manage_organizations": "Totes les accions sobre organitzacions",
"pbac_desc_view_bookings": "Veure reserves",
"pbac_desc_view_team_bookings": "Veure reserves d'equip",
"pbac_desc_view_organization_bookings": "Veure reserves de l'organització",
"pbac_desc_view_booking_recordings": "Veure enregistraments de reserves",
"pbac_desc_update_bookings": "Actualitzar reserves",
"pbac_desc_manage_bookings": "Totes les accions sobre reserves",
"pbac_desc_view_team_insights": "Veure estadístiques d'equip",
"pbac_desc_manage_team_insights": "Gestionar estadístiques d'equip",
"read_permission_auto_enabled_tooltip": "El permís de lectura s'activa automàticament quan es crea, actualitza o elimina un recurs",
"choose_restriction_schedule": "Afegir horari de restricció",
"choose_restriction_schedule_description": "Activeu aquesta opció si voleu restringir la disponibilitat dels amfitrions per a aquest tipus d'esdeveniment segons un horari de restricció. Això només elimina els espais que no estan a l'horari de restricció, no afegeix cap espai nou.",
"use_booker_timezone": "Utilitzar la zona horària del reservant",
"use_booker_timezone_info": "Aplicar l'horari de restricció seleccionat a la zona horària del reservant",
"load_balancing_warning": "Això desactivarà l'equilibri de càrrega en tots els vostres tipus d'esdeveniments round robin",
"rr_load_balancing_disabled": "L'equilibri de càrrega només està disponible quan la base de temps està configurada com a Moment de creació de la reserva",
"round_robin_settings_description": "Personalitza la configuració predeterminada de round robin per a aquest equip",
"round_robin_settings_updated_successfully": "Configuració de Round Robin actualitzada correctament",
"booking_creation_time": "Hora de creació de la reserva",
"meeting_start_time": "Hora d'inici de la reunió",
"members_affected_by_disabling_delegation_credential": "Membres afectats",
"no_members_affected_by_disabling_delegation_credential": "Cap membre afectat per la desactivació de la credencial de delegació",
"download_expense_log": "Descarrega el registre de despeses",
"error_downloading_expense_log": "Error en descarregar el registre de despeses",
"offer_to_reschedule_last_booking": "Ofereix reprogramar l'última reserva activa a la franja horària escollida",
"booker_limit_exceeded_error": "S'ha excedit el límit màxim de reserves actives del reservant",
"booker_limit_exceeded_error_reschedule": "Ja tens una reserva per a aquest esdeveniment el {{date}}. Vols reprogramar-la a la nova hora seleccionada?",
"duration_min_error": "La durada ha de ser d'almenys {{min}} minut(s)",
"duration_max_error": "La durada no pot superar els {{max}} minuts (24 hores)",
"booking_not_allowed_by_restriction_schedule_error": "Reserva fora de la disponibilitat de l'horari de restricció.",
"restriction_schedule_not_found_error": "No s'ha trobat l'horari de restricció",
"converted_image_size_limit_exceed": "S'ha excedit el límit de mida de la imatge, si us plau, utilitza una imatge més petita preferiblement en format JPEG",
"license_key": "Clau de llicència",
"license_selection_title": "Tria la teva llicència",
"license_selection_description": "Selecciona com vols llicenciar la teva implementació de Cal.com",
"agplv3_license_description": "Utilitza Cal.com sota la llicència de codi obert AGPLv3",
"enter_license_key": "Introdueix la clau de llicència",
"enter_your_license_key": "Accés a totes les funcionalitats empresarials",
"enter_existing_license": "A partir de 15 $/usuari al mes",
"enter_license_key_description": "Accés a totes les funcionalitats empresarials",
"license_key_placeholder": "Introdueix la teva clau de llicència",
"save_license_key": "Desa la clau de llicència",
"signature_token_optional": "Token de signatura (opcional)",
"signature_token_description": "El token de signatura s'utilitza per verificar que la clau de llicència és vostra quan s'incrementa la facturació basada en l'ús. El podeu trobar al correu electrònic de confirmació de compra. Si teniu una llicència antiga, podeu deixar-ho en blanc.",
"need_a_license": "Necessiteu una llicència?",
"license_key_required": "Es requereix una clau de llicència",
"invalid_license_key": "Clau de llicència no vàlida",
"license_validation_failed": "No s'ha pogut validar la clau de llicència",
"license_key_saved": "Clau de llicència desada correctament",
"timezone_mismatch_tooltip": "Esteu veient l'informe basat en la zona horària del vostre perfil ({{userTimezone}}), mentre que el vostre navegador està configurat a la zona horària ({{browserTimezone}})",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Afegiu les vostres noves cadenes a dalt ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
+171 -19
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "Přizpůsobit nastavení Cal Video",
"calvideo_settings_description": "Nakonfigurujte preference videoschůzek včetně oprávnění k nahrávání pro hostitele a účastníky.",
"disable_recording_for_guests": "Zakázat nahrávání pro hosty",
"enable_automatic_transcription": "Povolit automatický přepis po připojení k jednání",
"video_options": "Možnosti videa",
"get_meeting_session_details": "Získat podrobnosti o jednání",
"meeting_session_details": "Podrobnosti o jednání",
"meeting_session": "Jednání",
"session_id": "ID jednání",
"max_participants": "Maximální počet účastníků",
"unlimited": "Neomezeno",
"ongoing": "Probíhající",
"ended": "Ukončeno",
"participants": "Účastníci",
"anonymous_user": "Anonymní uživatel",
"session_duration": "Délka jednání",
"no_meeting_sessions_found": "Nebyla nalezena žádná jednání",
"not_available": "Není k dispozici",
"disable_recording_for_organizer": "Zakázat nahrávání pro organizátora",
"disable_transcription_for_guests": "Zakázat přepis pro hosty",
"disable_transcription_for_organizer": "Zakázat přepis pro organizátora",
"event_request_declined_recurring": "Žádost o opakovanou událost byla zamítnuta",
"event_request_cancelled": "Vaše plánovaná událost byla zrušena",
"event_request_reassigned": "Vaše naplánovaná událost byla přeřazena",
@@ -264,6 +281,8 @@
"guests": "Hosté",
"guest": "Host",
"web_conferencing_details_to_follow": "Podrobnosti k videohovoru, které je třeba následovat.",
"confirmation": "Potvrzení",
"what_booker_should_provide": "Co by měl váš rezervující poskytnout pro získání potvrzení",
"404_the_user": "Uživatelské jméno",
"username": "Uživatelské jméno",
"is_still_available": "je stále dostupný.",
@@ -331,7 +350,7 @@
"organization_general_description": "Správa nastavení jazyka a časového pásma vašeho týmu",
"few_sentences_about_yourself": "Pár vět o vás. Budou se zobrazovat na URL vaší osobní stránky.",
"nearly_there": "Téměř hotovo!",
"nearly_there_instructions": "Poslední věc... pár slov o vás a fotografie vám pomůžou získat rezervace a lidi aspoň budou vědět, koho si rezervují.",
"nearly_there_instructions": "Poslední věc, stručný popis o vás a fotografie opravdu pomáhají získat rezervace a dát lidem vědět, s kým si schůzku domlouvají.",
"set_availability_instructions": "Určete si časové intervaly, kdy jste pravidelně dostupní. Později můžete vytvořit další a přiřadit je různým kalendářům.",
"set_availability": "Nastavte, jak jste dostupní",
"set_availbility_description": "Nastavte si rozvrhy pro časy, kdy chcete být rezervováni.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "Připojit svůj kalendář",
"connect_your_video_app": "Propojte své video aplikace",
"connect_your_video_app_instructions": "Propojte své video aplikace a používejte je ve svých typech událostí.",
"connect_your_calendar_instructions": "Připojit kalendář pro automatickou kontrolu časů, kdy nejste dostupní, a nově naplánovaných událostí.",
"connect_your_calendar_instructions": "Připojte svůj kalendář pro automatickou kontrolu obsazených časů a nových událostí, jakmile jsou naplánovány.",
"set_up_later": "Zprovoznit později",
"current_time": "Aktuální čas",
"details": "Podrobnosti",
"welcome": "Vítejte",
"welcome_back": "Vítejte zpět",
"welcome_to_calcom": "Vítejte na webu {{appName}}",
"welcome_instructions": "Řekněte nám vaše jméno a v jaké časové zóně se pohybujete. Později můžete tyto informace změnit.",
"welcome_instructions": "Řekněte nám, jak vás máme oslovovat a v jakém časovém pásmu se nacházíte. Tyto údaje budete moci později upravit.",
"connect_caldav": "Připojit se k CalDav (Beta)",
"connect_ics_feed": "Připojit k ICS feedu",
"connect": "Připojit",
@@ -361,7 +380,7 @@
"when": "Kdy",
"where": "Kde",
"add_to_calendar": "Přidat do kalendáře",
"add_to_calendar_description": "Vyberte, kam se mají přidávat události, pokud jste zarezervováni.",
"add_to_calendar_description": "Vyberte, kam přidávat události, když jste rezervováni.",
"add_events_to": "Přidat události do",
"add_another_calendar": "Přidat další kalendář",
"other": "Ostatní",
@@ -1153,6 +1172,7 @@
"test_failed": "Test se nezdařil",
"provide_api_key": "Zadejte klíč API",
"api_key_modal_subtitle": "Klíče API vám umožňují provádět volání API pro váš vlastní účet.",
"api_key_modal_subtitle_platform": "Vytvořte místo toho tržiště nebo platformu.",
"api_keys_subtitle": "Generovat API klíče pro přístup k vašemu vlastnímu účtu.",
"create_api_key": "Vytvořit klíč API",
"personal_note": "Pojmenovat tento klíč",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "Odvolat tento klíč API",
"disable_rescheduling": "Zakázat změnu termínu",
"description_disable_rescheduling": "Hosté již nemohou změnit termín události pomocí pozvánky v kalendáři nebo e-mailu",
"allow_rescheduling_cancelled_bookings": "Povolit rezervaci prostřednictvím odkazu na změnu termínu",
"description_allow_rescheduling_cancelled_bookings": "Pokud je povoleno, uživatelé budou moci vytvořit novou rezervaci při pokusu o přeplánování zrušené rezervace",
"disable_cancelling": "Zakázat rušení",
"description_disable_cancelling": "Hosté již nemohou zrušit událost pomocí pozvánky v kalendáři nebo e-mailu",
"revoke_api_key": "Odvolat klíč API",
@@ -1613,7 +1635,7 @@
"workflow_example_6": "Odeslat hostiteli vlastní SMS, pokud je rezervována nová událost",
"count_managed_to_limit": "Zahrnout počty rezervací ze spravovaných typů událostí",
"welcome_to_cal_header": "Vítá vás {{appName}}!",
"edit_form_later_subtitle": "Toto budete moci upravit později.",
"edit_form_later_subtitle": "Toto budete moci později upravit.",
"connect_calendar_later": "Připojím svůj kalendář později",
"problem_saving_user_profile": "Při ukládání vašich údajů došlo k problému. Zkuste to prosím znovu, případně se obraťte na zákaznickou podporu.",
"purchase_missing_seats": "Koupit chybějící místa",
@@ -1675,7 +1697,7 @@
"create_first_api_key_description": "Klíče API umožňují ostatním aplikacím komunikovat se službou {{appName}}",
"back_to_signin": "Zpět na přihlášení",
"reset_link_sent": "Odkaz pro obnovení byl odeslán",
"password_reset_email": "E-mail s pokyny k obnovení hesla byl zaslán na adresu {{email}}.",
"password_reset_email": "E-mail s pokyny k obnovení hesla byl odeslán na adresu {{email}}.",
"password_reset_leading": "Pokud brzy neobdržíte e-mail, zkontrolujte, zda je zadaná e-mailová adresa správná, dále složku nevyžádané pošty nebo se obraťte na podporu, pokud problém přetrvává.",
"password_updated": "Heslo bylo úspěšně změněno!",
"pending_payment": "Čeká se na platbu",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "Tímto omezíte svou dostupnost pro tento typ události na jeden časový slot denně, naplánovaný na nejbližší dostupný čas.",
"limit_total_booking_duration": "Omezení celkové doby trvání rezervace",
"limit_total_booking_duration_description": "Omezení celkové doby, po kterou je možné tuto událost rezervovat",
"booker_booking_limit": "Limit aktivních rezervací pro rezervujícího",
"booker_booking_limit_description": "Omezit počet aktivních rezervací, které může rezervující provést pro tento typ události",
"recurring_event_doesnt_support_booker_booking_limit": "Opakující se události nepodporují limit rezervací pro rezervujícího",
"add_limit": "Přidat limit",
"team_name_required": "Je vyžadován název týmu",
"show_attendees": "Sdílet informace o účastnících",
"show_available_seats_count": "Zobrazení počtu volných míst",
"how_booking_questions_as_variables": "Jak používat rezervační otázky jako proměnné?",
"format": "Formát",
"questions": "Otázky",
"all_info_your_booker_provide": "Všechny informace, které by měl rezervující poskytnout před rezervací u vás.",
"uppercase_for_letters": "Použít velká písmena pro všechna písmena",
"replace_whitespaces_underscores": "Nahradit mezery podtržítky",
"platform_members": "Členové platformy",
@@ -1915,7 +1942,7 @@
"new_attendee": "Nový účastník",
"awaiting_approval": "Čeká na schválení",
"requires_google_calendar": "Tato aplikace vyžaduje připojení ke Kalendáři Google",
"event_type_requires_google_calendar": "Pro fungování Meet musí být \"Přidat do kalendáře\" pro tento typ události nastaven na Google Kalendář. Připojte jej <0>zde</0>.",
"event_type_requires_google_calendar": "Pro fungování služby Meet musí být \"Přidat do kalendáře\" pro tento typ události Kalendář Google. Připojte jej <0>zde</0>.",
"connected_google_calendar": "Připojili jste účet Kalendáře Google.",
"using_meet_requires_calendar": "Používání služby Google Meet vyžaduje připojení Kalendáře Google",
"continue_to_install_google_calendar": "Pokračovat v instalaci Kalendáře Google",
@@ -1967,12 +1994,12 @@
"delete_sso_configuration_confirmation_description": "Opravdu chcete odstranit konfiguraci {{connectionType}}? Členové vašeho týmu, kteří používají přihlášení {{connectionType}}, ztratí přístup k webu Cal.com.",
"organizer_timezone": "Časové pásmo organizátora",
"email_user_cta": "Zobrazit pozvánku",
"email_no_user_invite_heading_subteam": "Byli jste pozváni, abyste se připojili k týmu organizace {{parentTeamName}}",
"email_no_user_invite_heading_org": "Byli jste pozváni do organizace {{appName}}",
"email_no_user_invite_heading_subteam": "Byli jste pozváni k připojení do týmu organizace {{parentTeamName}}",
"email_no_user_invite_heading_org": "Byli jste pozváni k připojení do organizace {{appName}}",
"email_no_user_invite_subheading": "Uživatel {{invitedBy}} vás pozval, abyste se připojili k jeho týmu v aplikaci {{appName}}. {{appName}} je plánovač událostí, který vám a vašemu týmu umožňuje plánovat schůzky bez e-mailového ping-pongu.",
"email_user_invite_subheading_subteam": "{{invitedBy}} vás pozval(a) k připojení k týmu {{teamName}} v jejich organizaci {{parentTeamName}} na {{appName}}. {{appName}} je plánovač událostí, který vám a vašemu týmu umožňuje plánovat schůzky bez zbytečných e-mailů.",
"email_user_invite_subheading_org": "Uživatel {{invitedBy}} vás pozval, abyste se připojili k týmu {{teamName}} jeho organizace v aplikaci {{appName}}. {{appName}} je plánovač událostí, který vám a vaší organizaci umožňuje plánovat schůzky bez e-mailového pingpongu.",
"email_no_user_invite_steps_intro": "Provedeme vás několika krátkými kroky a za malou chvíli si budete se svým subjektem {{entity}} užívat plánování bez stresu.",
"email_no_user_invite_steps_intro": "Provedeme vás několika krátkými kroky a brzy si budete užívat plánování bez stresu se svým {{entity}}.",
"email_no_user_step_one": "Vyberte si uživatelské jméno",
"email_no_user_step_two": "Připojte svůj účet kalendáře",
"email_no_user_step_three": "Nastavte si dostupnost",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "Rozhodujte se na základě informací pomocí funkce Insights",
"make_informed_decisions_description": "Náš panel Insights zobrazuje všechny aktivity vašeho týmu a trendy, které v rámci týmu umožňují lepší plánování a rozhodování.",
"view_bookings_across": "Zobrazení rezervací všech členů",
"view_bookings_across_description": "Podívejte se, kdo přijímá nejvíce rezervací, a zajistěte co nejlepší rozložení v rámci celého týmu",
"view_bookings_across_description": "Zjistěte, kdo získává nejvíce rezervací, a zajistěte nejlepší distribuci napříč vaším týmem",
"identify_booking_trends": "Identifikujte trendy v rezervacích",
"identify_booking_trends_description": "Zjistěte, které části týdne a které časy během dne jsou u vašich rezervací oblíbené",
"spot_popular_event_types": "Identifikujte oblíbené typy událostí",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "Pro vybraný filtr nebo vybraná data nebyla nalezena žádná data.",
"acknowledge_booking_no_show_fee": "Beru na vědomí, že pokud se této události nezúčastním, bude z mé karty stržen poplatek za nedostavení se ve výši {{amount, currency}}.",
"card_details": "Údaje o kartě",
"something_went_wrong_on_our_end": "Na naší straně se něco pokazilo. Spojte se s naší podporou a hned to napravíme.",
"something_went_wrong_on_our_end": "Na naší straně došlo k chybě. Kontaktujte náš tým podpory a my to pro vás co nejdříve opravíme.",
"please_provide_following_text_to_suppport": "Při kontaktování podpory uveďte v zájmu lepší efektivity následující",
"seats_and_no_show_fee_error": "V současné době nelze povolit místa a strhnout poplatek za nedostavení se",
"complete_your_booking": "Dokončete svou rezervaci",
@@ -2276,7 +2303,7 @@
"messaging": "Zasílání zpráv",
"sender_id_info": "Jméno nebo číslo zobrazené jako odesílatel SMS (některé země nepovolují alfanumerické ID odesílatele)",
"org_admins_can_create_new_teams": "Nové týmy může vytvářet pouze správce vaší organizace",
"google_new_spam_policy": "Nové zásady společnosti Google týkající se nevyžádané pošty by mohly zabránit tomu, abyste dostávali e-mailová oznámení a oznámení kalendáře o této rezervaci.",
"google_new_spam_policy": "Nová politika Googlu proti spamu by vám mohla zabránit v přijímání e-mailových a kalendářových oznámení o této rezervaci.",
"resolve": "Vyřešit",
"no_organization_slug": "Při vytváření týmů pro tuto organizaci došlo k chybě. Chybějící slug adresy URL.",
"copy_link_org": "Zkopírovat odkaz na organizaci",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "Delegované pověření vám umožňuje spravovat přístup ke kalendářům Google Workspace pro vaši organizaci.",
"add_client_id_in_google_workspace_with_below_scope": "Přidejte toto Client ID do Google Workspace s následujícím rozsahem oprávnění",
"delegation_credential_enabled": "Delegované pověření povoleno",
"delegation_credential_disabled": "Delegované pověření zakázáno",
"add_delegation_credential": "Přidat delegované pověření",
"edit_delegation_credential": "Upravit delegované pověření",
"domain": "Doména",
@@ -2582,7 +2608,7 @@
"date": "Datum",
"overlaps_with_existing_schedule": "Toto se překrývá s existujícím rozvrhem. Vyberte prosím jiné datum.",
"org_admin_no_slots|heading": "Nebyly nalezeny žádné dostupné termíny pro {{name}}",
"org_admin_no_slots|content": "Dobrý den, administrátoři organizace,<br /><br />Upozorňujeme, že {{username}} neměl žádné dostupné termíny, když uživatel navštívil {{username}}/{{slug}}<br /><br />Existuje několik důvodů, proč se to může stát:<br />Uživatel nemá připojené žádné kalendáře<br />Jejich rozvrhy připojené k této události nejsou povoleny<br /> <br />Doporučujeme zkontrolovat jejich dostupnost, aby se tento problém vyřešil.",
"org_admin_no_slots|content": "Dobrý den, administrátoři organizace,<br /><br />Upozorňujeme: Bylo nám oznámeno, že {{username}} neměl žádnou dostupnost, když uživatel navštívil {{username}}/{{slug}}<br /><br />Může to být způsobeno několika důvody<br />Uživatel nemá připojené žádné kalendáře<br />Jejich rozvrhy připojené k této události nejsou povoleny<br /> <br />Doporučujeme zkontrolovat jejich dostupnost k vyřešení tohoto problému.",
"org_admin_no_slots|cta": "Otevřít dostupnost uživatelů",
"organization_no_slots_notification_switch_title": "Dostávejte upozornění, když váš tým nemá žádnou dostupnost",
"organization_no_slots_notification_switch_description": "Administrátoři budou dostávat e-mailová upozornění, když se uživatel pokusí rezervovat člena týmu a zjistí, že není žádná dostupnost. Tento e-mail spustíme po dvou výskytech a připomeneme vám to každých 7 dní na uživatele.",
@@ -2594,8 +2620,8 @@
"email_team_invite|subject|invited_to_regular_team": "{{user}} vás pozval, abyste se připojili k týmu {{team}} na {{appName}}",
"email_team_invite|heading|added_to_org": "Byli jste přidáni do organizace {{appName}}",
"email_team_invite|heading|invited_to_org": "Byli jste pozváni do organizace {{appName}}",
"email_team_invite|heading|added_to_subteam": "Byli jste přidáni do týmu v organizaci {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "Byli jste pozváni do týmu v organizaci {{parentTeamName}}",
"email_team_invite|heading|added_to_subteam": "Byli jste přidáni do týmu organizace {{parentTeamName}}",
"email_team_invite|heading|invited_to_subteam": "Byli jste pozváni do týmu organizace {{parentTeamName}}",
"email_team_invite|heading|invited_to_regular_team": "Byli jste pozváni do týmu {{appName}}",
"email_team_invite|content|added_to_org": "{{invitedBy}} vás přidal(a) do organizace <strong>{{teamName}}</strong>.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} vás pozval(a) k připojení do organizace {{teamName}}.",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "Omezit, kolikrát mohou být členové rezervováni napříč všemi týmovými typy událostí",
"booking_limits_updated_successfully": "Limity rezervací byly úspěšně aktualizovány",
"you_are_unauthorized_to_make_this_change_to_the_booking": "Nemáte oprávnění provést tuto změnu v rezervaci",
"delegation_credential_disabled": "Delegované pověření zakázáno",
"matching_members": "Odpovídající členové",
"x_matching_members": "{{x}} odpovídajících členů",
"matching_members_queue_using_attribute_weights": "Fronta přiřazování členů (podle vah atributů)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "Ano, přeřadit",
"filter_by_attributes": "Filtrovat podle atributů",
"reassign_reason": "Důvod přeřazení",
"field_type_change_suggestion": "Změna typu pole může způsobit ztrátu dat. Místo toho prosím vytvořte nové pole.",
"reassigned_by": "Přeřazeno kým",
"row": "řádek",
"rows": "řádky",
@@ -2902,7 +2930,7 @@
"routed_to_per_period": "Přesměrováno za období",
"per_day": "Za den",
"per_week": "Za týden",
"per_month": "za měsíc",
"per_month": "Za měsíc",
"routing_form_insights_assignment_reason": "Důvod přiřazení",
"access_denied": "Přístup odepřen",
"salesforce_route_to_owner": "Vlastník kontaktu bude hostem Round Robin, pokud je k dispozici",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "Nemáte oprávnění vytvořit organizaci pro tento e-mail",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "Nemáte oprávnění migrovat jeden nebo více týmů",
"organization_has_subscription_already": "Organizace již má předplatné",
"reset_interval_weighted_rr": "Interval resetování pro vážený Round Robin",
"interval": "Interval",
"rr_reset_interval_description": "Určuje, jak často se resetuje počet rezervací v systému round robin pro zajištění vyváženého rozdělení.",
"daily": "Denně",
@@ -3059,6 +3088,7 @@
"ooo_team_redirect_infinite_not_allowed": "Mezi těmito uživateli již existuje zpětné přesměrování.",
"previous_ooo_empty_title": "Nenalezeno žádné předchozí OOO",
"previous": "Předchozí",
"rr_interval_successfully_updated": "Interval Round Robin byl úspěšně aktualizován",
"trial_mode": "Zkušební režim",
"inactive_team_plan": "Neaktivní týmový plán",
"inactive_team_plan_description": "Váš týmový plán je neaktivní. Zkontrolujte své předplatné nebo kontaktujte zákaznickou podporu",
@@ -3074,6 +3104,7 @@
"user_has_no_team_yet": "Zatím nemáte žádný tým",
"no_team_members": "Zatím nemáte žádné členy týmu",
"recurring_event_doesnt_support_seats": "Opakující se událost nepodporuje funkci míst. Změňte ji na neopakující se pro povolení míst.",
"booking_limit_per_booker_doesnt_support_recurring": "Opakující se událost nepodporuje funkci limitu rezervací na jednoho rezervujícího. Zakažte limit rezervací na jednoho rezervujícího pro povolení opakujících se událostí.",
"seats_doesnt_support_recurring": "Funkce míst nepodporuje opakující se události. Zakažte funkci míst pro vytvoření opakující se události.",
"recurring_event_seats_error": "Opakující se událost nepodporuje funkci míst. Zakažte funkci míst nebo změňte událost na neopakující se.",
"segment": "Segment",
@@ -3114,7 +3145,7 @@
"enable_delegation_credential": "Povolit delegované pověření",
"enable_delegation_credential_description": "Udělte Cal.com automatický přístup ke kalendářům všech členů organizace povolením delegovaného pověření.",
"disable_delegation_credential": "Zakázat delegované pověření",
"disable_delegation_credential_description": "Jakmile je delegované pověření zakázáno, členové organizace, kteří nepropojili své kalendáře, to budou muset udělat ručně.",
"disable_delegation_credential_description": "Jakmile je pověření k delegování zakázáno, členové organizace, kteří nepropojili své kalendáře, to budou muset udělat ručně.",
"last_active": "Naposledy aktivní",
"member_since": "Členem od",
"last_updated": "Naposledy aktualizováno",
@@ -3202,10 +3233,131 @@
"matching": "Odpovídající",
"event_redirect": "Přesměrování události",
"reset_form": "Resetovat formulář",
"rr_settings_successfully_updated": "Nastavení Round Robin bylo úspěšně aktualizováno",
"distribution_basis_weighted_rr": "Základ distribuce pro vážený Round Robin",
"timestamp_basis_description": "Určuje, který časový údaj události se použije jako základ pro distribuci váženého round robin.",
"requires_credits": "Vyžaduje kredity",
"requires_credits_tooltip": "Pro použití této funkce potřebujete mít na účtu dostatek kreditů",
"available_credits": "Dostupné kredity",
"create_role": "Vytvořit roli",
"roles_and_permissions": "Role a oprávnění",
"roles_and_permissions_description": "Správa rolí a oprávnění pro vaši organizaci",
"learn_more_permissions": "Zjistěte více o tom, jak fungují oprávnění",
"role_name": "Název role",
"role_name_placeholder": "např. Prodejní tým, Podpora",
"role_description": "Popis role",
"role_description_placeholder": "Popište, k čemu tato role slouží a jaká oprávnění potřebuje",
"default_role": "Výchozí role",
"custom_role": "Vlastní role",
"advanced": "Upřesnit",
"search_permissions": "Hledat oprávnění",
"all_permissions": "Všechna oprávnění",
"none": "Žádné",
"read_only": "Pouze pro čtení",
"pbac_desc_all_actions_all_resources": "Všechny akce na všech zdrojích",
"pbac_resource_all": "Všechny zdroje",
"pbac_resource_event_type": "Typy událostí",
"pbac_resource_team": "Týmy",
"pbac_resource_organization": "Organizace",
"pbac_resource_booking": "Rezervace",
"pbac_resource_insights": "Přehledy",
"pbac_resource_role": "Role",
"pbac_action_all": "Všechny akce",
"pbac_action_create": "Vytvořit",
"pbac_action_read": "Zobrazit",
"pbac_action_update": "Upravit",
"pbac_action_delete": "Smazat",
"pbac_action_manage": "Spravovat",
"pbac_action_invite": "Pozvat",
"pbac_action_remove": "Odebrat",
"pbac_action_change_member_role": "Změnit roli člena",
"pbac_action_list_members": "Seznam členů",
"pbac_action_manage_billing": "Spravovat fakturaci",
"pbac_action_read_team_bookings": "Zobrazit rezervace týmu",
"pbac_action_read_org_bookings": "Zobrazit rezervace organizace",
"pbac_action_read_recordings": "Zobrazit nahrávky",
"role_created_successfully": "Role byla úspěšně vytvořena",
"role_updated_successfully": "Role byla úspěšně aktualizována",
"delete_role": "Odstranit roli",
"confirm_delete_role": "Opravdu chcete tuto roli odstranit? Tuto akci nelze vrátit zpět.",
"role_deleted_successfully": "Role byla úspěšně odstraněna",
"error_creating_role": "Chyba při vytváření role",
"error_updating_role": "Chyba při aktualizaci role",
"pbac_desc_create_roles": "Vytvářet role",
"pbac_desc_view_roles": "Zobrazit role",
"pbac_desc_update_roles": "Aktualizovat role",
"pbac_desc_delete_roles": "Odstraňovat role",
"pbac_desc_manage_roles": "Všechny akce s rolemi",
"pbac_desc_create_event_types": "Vytvářet typy událostí",
"pbac_desc_view_event_types": "Zobrazit typy událostí",
"pbac_desc_update_event_types": "Aktualizovat typy událostí",
"pbac_desc_delete_event_types": "Odstraňovat typy událostí",
"pbac_desc_manage_event_types": "Všechny akce s typy událostí",
"pbac_desc_create_teams": "Vytvářet týmy",
"pbac_desc_view_team_details": "Zobrazit detaily týmu",
"pbac_desc_update_team_settings": "Aktualizovat nastavení týmu",
"pbac_desc_delete_team": "Odstranit tým",
"pbac_desc_invite_team_members": "Pozvat členy týmu",
"pbac_desc_remove_team_members": "Odebrat členy týmu",
"pbac_desc_change_team_member_role": "Změnit roli členů týmu",
"pbac_desc_manage_teams": "Všechny akce v týmech",
"pbac_desc_create_organization": "Vytvořit organizaci",
"pbac_desc_view_organization_details": "Zobrazit detaily organizace",
"pbac_desc_list_organization_members": "Seznam členů organizace",
"pbac_desc_invite_organization_members": "Pozvat členy organizace",
"pbac_desc_remove_organization_members": "Odebrat členy organizace",
"pbac_desc_manage_organization_billing": "Spravovat fakturaci organizace",
"pbac_desc_change_organization_member_role": "Změnit roli členů organizace",
"pbac_desc_edit_organization_settings": "Upravit nastavení organizace",
"pbac_desc_manage_organizations": "Všechny akce v organizacích",
"pbac_desc_view_bookings": "Zobrazit rezervace",
"pbac_desc_view_team_bookings": "Zobrazit týmové rezervace",
"pbac_desc_view_organization_bookings": "Zobrazit rezervace organizace",
"pbac_desc_view_booking_recordings": "Zobrazit záznamy rezervací",
"pbac_desc_update_bookings": "Aktualizovat rezervace",
"pbac_desc_manage_bookings": "Všechny akce s rezervacemi",
"pbac_desc_view_team_insights": "Zobrazit přehledy týmu",
"pbac_desc_manage_team_insights": "Spravovat přehledy týmu",
"read_permission_auto_enabled_tooltip": "Oprávnění ke čtení je automaticky povoleno při vytváření, aktualizaci nebo mazání zdroje",
"choose_restriction_schedule": "Přidat plán omezení",
"choose_restriction_schedule_description": "Povolte tuto možnost, pokud chcete omezit dostupnost hostitelů pro tento typ události na základě plánu omezení. Tím se pouze odstraní časové úseky, které nejsou v plánu omezení, nepřidávají se žádné nové úseky.",
"use_booker_timezone": "Použít časové pásmo rezervujícího",
"use_booker_timezone_info": "Aplikovat vybraný plán omezení v časovém pásmu rezervujícího",
"load_balancing_warning": "Tímto se vypne vyvažování zátěže u všech vašich typů událostí round robin",
"rr_load_balancing_disabled": "Vyvažování zátěže je k dispozici pouze když je základ časového razítka nastaven na Čas vytvoření rezervace",
"round_robin_settings_description": "Přizpůsobte výchozí nastavení kolečka pro tento tým",
"round_robin_settings_updated_successfully": "Nastavení kolečka bylo úspěšně aktualizováno",
"booking_creation_time": "Čas vytvoření rezervace",
"meeting_start_time": "Čas začátku schůzky",
"members_affected_by_disabling_delegation_credential": "Dotčení členové",
"no_members_affected_by_disabling_delegation_credential": "Žádní členové nejsou dotčeni vypnutím pověření k delegování",
"download_expense_log": "Stáhnout protokol výdajů",
"error_downloading_expense_log": "Chyba při stahování protokolu výdajů",
"offer_to_reschedule_last_booking": "Nabídnout přeplánování poslední aktivní rezervace na vybraný časový úsek",
"booker_limit_exceeded_error": "Překročen maximální limit aktivních rezervací",
"booker_limit_exceeded_error_reschedule": "Již máte rezervaci pro tuto událost dne {{date}}. Chcete ji přeplánovat na nově vybraný čas?",
"duration_min_error": "Doba trvání musí být alespoň {{min}} minut",
"duration_max_error": "Doba trvání nesmí překročit {{max}} minut (24 hodin)",
"booking_not_allowed_by_restriction_schedule_error": "Rezervace mimo dostupnost omezujícího rozvrhu.",
"restriction_schedule_not_found_error": "Omezující rozvrh nebyl nalezen",
"converted_image_size_limit_exceed": "Překročen limit velikosti obrázku, použijte prosím menší obrázek, nejlépe ve formátu JPEG",
"license_key": "Licenční klíč",
"license_selection_title": "Vyberte si licenci",
"license_selection_description": "Vyberte, jak chcete licencovat vaše nasazení Cal.com",
"agplv3_license_description": "Používejte Cal.com pod open source licencí AGPLv3",
"enter_license_key": "Zadejte licenční klíč",
"enter_your_license_key": "Přístup ke všem podnikovým funkcím",
"enter_existing_license": "Od 15 $/uživatele za měsíc",
"enter_license_key_description": "Přístup ke všem podnikovým funkcím",
"license_key_placeholder": "Zadejte svůj licenční klíč",
"save_license_key": "Uložit licenční klíč",
"signature_token_optional": "Token podpisu (volitelné)",
"signature_token_description": "Podpisový token se používá k ověření, že licenční klíč je váš, při navyšování využití založeného na fakturaci. Najdete jej v e-mailu s potvrzením nákupu. Pokud používáte starší licenci, můžete toto pole nechat prázdné.",
"need_a_license": "Potřebujete licenci?",
"license_key_required": "Licenční klíč je povinný",
"invalid_license_key": "Neplatný licenční klíč",
"license_validation_failed": "Ověření licenčního klíče selhalo",
"license_key_saved": "Licenční klíč byl úspěšně uložen",
"timezone_mismatch_tooltip": "Prohlížíte si report na základě časového pásma vašeho profilu ({{userTimezone}}), zatímco váš prohlížeč je nastaven na časové pásmo ({{browserTimezone}})",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
+172 -20
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "Tilpas Cal Video-indstillinger",
"calvideo_settings_description": "Konfigurer præferencer for videomøder, herunder optagelsestilladelser for værter og deltagere.",
"disable_recording_for_guests": "Deaktiver optagelse for gæster",
"enable_automatic_transcription": "Aktivér automatisk transskription efter at have deltaget i mødet",
"video_options": "Videoindstillinger",
"get_meeting_session_details": "Hent mødesessiondetaljer",
"meeting_session_details": "Mødesessiondetaljer",
"meeting_session": "Mødesession",
"session_id": "Sessions-ID",
"max_participants": "Maks. deltagere",
"unlimited": "Ubegrænset",
"ongoing": "I gang",
"ended": "Afsluttet",
"participants": "Deltagere",
"anonymous_user": "Anonym bruger",
"session_duration": "Sessionsvarighed",
"no_meeting_sessions_found": "Ingen mødesessioner fundet",
"not_available": "Ikke tilgængelig",
"disable_recording_for_organizer": "Deaktiver optagelse for arrangør",
"disable_transcription_for_guests": "Deaktiver transskription for gæster",
"disable_transcription_for_organizer": "Deaktiver transskription for arrangør",
"event_request_declined_recurring": "Din tilbagevendende begivenhedsanmodning er blevet afvist",
"event_request_cancelled": "Dit planlagte arrangement blev annulleret",
"event_request_reassigned": "Din planlagte begivenhed er blevet omfordelt",
@@ -264,6 +281,8 @@
"guests": "Gæster",
"guest": "Gæst",
"web_conferencing_details_to_follow": "Webkonference detaljer følger i bekræftelses-e-mailen.",
"confirmation": "Bekræftelse",
"what_booker_should_provide": "Hvad din booker skal give for at modtage bekræftelser",
"404_the_user": "Brugernavnet",
"username": "Brugernavn",
"is_still_available": "er stadig tilgængeligt.",
@@ -331,7 +350,7 @@
"organization_general_description": "Administrer indstillinger for dit teams sprog og tidszone",
"few_sentences_about_yourself": "Et par sætninger om dig selv. Dette vil blive vist på din personlige url side.",
"nearly_there": "Du er der næsten!",
"nearly_there_instructions": "Sidste trin, en kort beskrivelse af dig og et foto hjælper virkelig med at få bookinger og lade folk vide, hvem de booker med.",
"nearly_there_instructions": "Sidste ting, en kort beskrivelse af dig og et billede hjælper virkelig med at få bookinger og lade folk vide, hvem de booker med.",
"set_availability_instructions": "Definér tidsintervaller hvor du er tilgængelig på en tilbagevendende basis. Du kan oprette flere af disse senere og tildele dem til forskellige kalendere.",
"set_availability": "Angiv din tilgængelighed",
"set_availbility_description": "Indstil tidsplaner for de tidspunkter, du vil kunne bookes.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "Tilslut din kalender",
"connect_your_video_app": "Forbind dine videoapps",
"connect_your_video_app_instructions": "Forbind dine videoapps for at bruge dem på dine begivenhedstyper.",
"connect_your_calendar_instructions": "Tilslut din kalender for automatisk at tjekke for travle tider og nye begivenheder, som de bliver planlagt.",
"connect_your_calendar_instructions": "Forbind din kalender for automatisk at tjekke for optagede tider og nye begivenheder, når de planlægges.",
"set_up_later": "Opsæt senere",
"current_time": "Aktuel tid",
"details": "Detaljer",
"welcome": "Velkommen",
"welcome_back": "Velkommen tilbage",
"welcome_to_calcom": "Velkommen til {{appName}}",
"welcome_instructions": "Fortæl os hvad du vil kaldes, og lad os vide hvilken tidszone du er i. Du vil kunne redigere dette senere.",
"welcome_instructions": "Fortæl os, hvad vi skal kalde dig, og lad os vide, hvilken tidszone du er i. Du kan redigere dette senere.",
"connect_caldav": "Forbind til CalDav (Beta)",
"connect_ics_feed": "Forbind til en ICS-feed",
"connect": "Tilslut",
@@ -361,7 +380,7 @@
"when": "Hvornår",
"where": "Hvor",
"add_to_calendar": "Føj til kalender",
"add_to_calendar_description": "Vælg, hvor du vil tilføje begivenheder, når du bliver booket.",
"add_to_calendar_description": "Vælg, hvor begivenheder skal tilføjes, når du bliver booket.",
"add_events_to": "Tilføj begivenheder til",
"add_another_calendar": "Tilføj endnu en kalender",
"other": "Flere",
@@ -1153,6 +1172,7 @@
"test_failed": "Test mislykkedes",
"provide_api_key": "Angiv API-nøgle",
"api_key_modal_subtitle": "API-nøgler giver dig mulighed for at foretage API-opkald for din egen konto.",
"api_key_modal_subtitle_platform": "Opret en markedsplads eller platform i stedet.",
"api_keys_subtitle": "Generer API-nøgler til brug for adgang til din egen konto.",
"create_api_key": "Opret en API-nøgle",
"personal_note": "Navngiv denne nøgle",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "Tilbagekald denne API-nøgle",
"disable_rescheduling": "Deaktiver ombooking",
"description_disable_rescheduling": "Gæster kan ikke længere ombooke begivenheden via kalenderinvitation eller e-mail",
"allow_rescheduling_cancelled_bookings": "Tillad booking via ombookingslink",
"description_allow_rescheduling_cancelled_bookings": "Når aktiveret, vil brugere kunne oprette en ny booking, når de forsøger at omlægge en aflyst booking.",
"disable_cancelling": "Deaktiver aflysning",
"description_disable_cancelling": "Gæster kan ikke længere aflyse begivenheden via kalenderinvitation eller e-mail",
"revoke_api_key": "Tilbagekald API-nøgle",
@@ -1675,7 +1697,7 @@
"create_first_api_key_description": "API-nøgler tillader andre apps at kommunikere med {{appName}}",
"back_to_signin": "Tilbage til log ind",
"reset_link_sent": "Link til nulstilling blev sendt",
"password_reset_email": "En e-mail er på vej til {{email}} med instruktioner om at nulstille din adgangskode.",
"password_reset_email": "En e-mail er på vej til {{email}} med instruktioner til at nulstille din adgangskode.",
"password_reset_leading": "Hvis du ikke modtager en e-mail snart, skal du kontrollere, at den e-mail-adresse, du har indtastet, er korrekt, tjekke din spam-mappe eller kontakte support, hvis problemet fortsætter.",
"password_updated": "Adgangskode opdateret!",
"pending_payment": "Afventer betaling",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "Dette vil begrænse din tilgængelighed for denne begivenhedstype til én tid pr. dag, planlagt til det tidligst tilgængelige tidspunkt.",
"limit_total_booking_duration": "Begræns den samlede bookingvarighed",
"limit_total_booking_duration_description": "Begræns den samlede tid, som denne begivenhed kan bookes",
"booker_booking_limit": "Grænse for aktive bookinger pr. booker",
"booker_booking_limit_description": "Begræns antallet af aktive bookinger, en booker kan foretage for denne begivenhedstype.",
"recurring_event_doesnt_support_booker_booking_limit": "Tilbagevendende begivenheder understøtter ikke grænse for booker-bookinger.",
"add_limit": "Tilføj Begrænsning",
"team_name_required": "Teamnavn påkrævet",
"show_attendees": "Del deltagerinformation mellem gæster",
"show_available_seats_count": "Vis antallet af ledige pladser",
"how_booking_questions_as_variables": "Hvordan bruger man booking-spørgsmål som variabler?",
"format": "Formatér",
"questions": "Spørgsmål",
"all_info_your_booker_provide": "Alle de oplysninger, din booker skal give, før de kan booke hos dig.",
"uppercase_for_letters": "Brug store bogstaver for alle bogstaver",
"replace_whitespaces_underscores": "Erstat blanke tegn med understregninger",
"platform_members": "Platformmedlemmer",
@@ -1915,7 +1942,7 @@
"new_attendee": "Ny Deltager",
"awaiting_approval": "Afventer Godkendelse",
"requires_google_calendar": "Denne app kræver en Google Calendar-forbindelse",
"event_type_requires_google_calendar": "\"Tilføj til kalender\" for denne begivenhedstype skal være en Google Kalender for at Meet kan fungere. Forbind den <0>her</0>.",
"event_type_requires_google_calendar": "\"Tilføj til kalender\" for denne begivenhedstype kræver en Google Kalender for at Meet kan fungere. Forbind den <0>her</0>.",
"connected_google_calendar": "Du har tilsluttet en Google Calendar-konto.",
"using_meet_requires_calendar": "Brug af Google Meet kræver en tilsluttet Google Kalender",
"continue_to_install_google_calendar": "Fortsæt med at installere Google Calendar",
@@ -1967,8 +1994,8 @@
"delete_sso_configuration_confirmation_description": "Er du sikker på, at du vil slette {{connectionType}} konfigurationen? Dine teammedlemmer, der bruger {{connectionType}} login, vil ikke længere kunne få adgang til Cal.com.",
"organizer_timezone": "Arrangørens tidszone",
"email_user_cta": "Vis Invitation",
"email_no_user_invite_heading_subteam": "Du er blevet inviteret til at deltage i et team i {{parentTeamName}} organisationen",
"email_no_user_invite_heading_org": "Du er blevet inviteret til at deltage i en {{appName}} organisation",
"email_no_user_invite_heading_subteam": "Du er blevet inviteret til at blive medlem af et team i {{parentTeamName}}-organisationen.",
"email_no_user_invite_heading_org": "Du er blevet inviteret til at blive medlem af en {{appName}}-organisation.",
"email_no_user_invite_subheading": "{{invitedBy}} har inviteret dig til at deltage i deres team på {{appName}}. {{appName}} er den begivenheds-jonglerende planlægger, der gør det muligt for dig og dit team at planlægge møder uden e-mail tennis.",
"email_user_invite_subheading_subteam": "{{invitedBy}} har inviteret dig til at deltage i teamet {{teamName}} i deres organisation {{parentTeamName}} på {{appName}}. {{appName}} er den begivenhedsplanlægger, der gør det muligt for dig og dit team at planlægge møder uden e-mail tennis.",
"email_user_invite_subheading_org": "{{invitedBy}} har inviteret dig til at deltage i deres organisation {{teamName}} på {{appName}}. {{appName}} er den event-jonglerende planlægger, der gør det muligt for dig og din organisation at planlægge møder uden e-mailtennis.",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "Træf informerede beslutninger med Insights",
"make_informed_decisions_description": "Vores Insights-dashboard viser al aktivitet på tværs af dit team og viser dig tendenser, der muliggør bedre teamplanlægning og beslutningstagning.",
"view_bookings_across": "Se bookinger på tværs af alle medlemmer",
"view_bookings_across_description": "Se hvem der modtager flest bookinger og sørg for den bedste fordeling i dit team",
"view_bookings_across_description": "Se, hvem der modtager flest bookinger, og sørg for den bedste fordeling på tværs af dit team.",
"identify_booking_trends": "Identificer bookingtrends",
"identify_booking_trends_description": "Se hvilke tidspunkter på ugen og hvilke tidspunkter på dagen der er populære for dine bookere",
"spot_popular_event_types": "Find populære begivenhedstyper",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "Ingen data fundet for det valgte filter eller de valgte datoer.",
"acknowledge_booking_no_show_fee": "Jeg anerkender, at hvis jeg ikke deltager i denne begivenhed, vil et gebyr for udeblivelse på {{amount, currency}} blive opkrævet på mit kort.",
"card_details": "Kortoplysninger",
"something_went_wrong_on_our_end": "Noget gik galt på vores side. Kontakt vores supportteam, så løser vi det med det samme for dig.",
"something_went_wrong_on_our_end": "Noget gik galt hos os. Kontakt vores supportteam, så løser vi det straks for dig.",
"please_provide_following_text_to_suppport": "Angiv venligst følgende tekst, når du kontakter support, for at hjælpe os bedre",
"seats_and_no_show_fee_error": "Kan i øjeblikket ikke aktivere pladser og opkræve gebyr for udeblivelse",
"complete_your_booking": "Fuldfør din booking",
@@ -2276,7 +2303,7 @@
"messaging": "Beskeder",
"sender_id_info": "Navn eller nummer vist som afsender af en SMS (nogle lande tillader ikke alfanumeriske afsender-ID'er)",
"org_admins_can_create_new_teams": "Kun administratoren af din organisation kan oprette nye teams",
"google_new_spam_policy": "Googles nye spam-politik kan forhindre dig i at modtage e-mail- og kalendernotifikationer om denne booking.",
"google_new_spam_policy": "Googles nye spam-politik kan forhindre dig i at modtage e-mails og kalendernotifikationer om denne booking.",
"resolve": "Løs",
"no_organization_slug": "Der opstod en fejl ved oprettelse af teams for denne organisation. Mangler URL-slug.",
"copy_link_org": "Kopiér link til organisation",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "Delegationslegitimation giver dig mulighed for at administrere adgang til Google Workspace-kalendere for din organisation.",
"add_client_id_in_google_workspace_with_below_scope": "Tilføj dette klient-id i Google Workspace med nedenstående scope",
"delegation_credential_enabled": "Delegationslegitimation aktiveret",
"delegation_credential_disabled": "Delegationslegitimation deaktiveret",
"add_delegation_credential": "Tilføj delegationslegitimation",
"edit_delegation_credential": "Rediger delegationslegitimation",
"domain": "Domæne",
@@ -2454,7 +2480,7 @@
"view_overlay_calendar_events": "Se dine kalenderbegivenheder for at undgå dobbeltbookinger.",
"join_event_location": "Deltag i {{eventLocationType}}",
"troubleshooting": "Fejlfinding",
"calendars_were_checking_for_conflicts": "Kalendere vi tjekker for konflikter",
"calendars_were_checking_for_conflicts": "Kalendere, vi tjekker for konflikter",
"availabilty_schedules": "Tilgængelighedsplaner",
"manage_calendars": "Administrer kalendere",
"manage_availability_schedules": "Administrer tilgængelighedsplaner",
@@ -2582,7 +2608,7 @@
"date": "Dato",
"overlaps_with_existing_schedule": "Dette overlapper med en eksisterende tidsplan. Vælg venligst en anden dato.",
"org_admin_no_slots|heading": "Ingen tilgængelighed fundet for {{name}}",
"org_admin_no_slots|content": "Hej organisationsadministratorer,<br /><br />Bemærk venligst: Det er blevet gjort opmærksom på, at {{username}} ikke har haft nogen tilgængelighed, når en bruger har besøgt {{username}}/{{slug}}<br /><br />Der er nogle få grunde til, at dette kan ske<br />Brugeren har ikke nogen kalendere forbundet<br />Deres tidsplaner knyttet til denne begivenhed er ikke aktiveret<br /> <br />Vi anbefaler at tjekke deres tilgængelighed for at løse dette.",
"org_admin_no_slots|content": "Hej organisationsadministratorer,<br /><br />Bemærk venligst: Det er blevet gjort opmærksom på, at {{username}} ikke har haft nogen tilgængelighed, når en bruger har besøgt {{username}}/{{slug}}<br /><br />Der kan være flere grunde til, at dette sker:<br />Brugeren har ikke nogen kalendere forbundet<br />Deres tidsplaner knyttet til denne begivenhed er ikke aktiveret<br /><br />Vi anbefaler at tjekke deres tilgængelighed for at løse dette.",
"org_admin_no_slots|cta": "Åbn brugernes tilgængelighed",
"organization_no_slots_notification_switch_title": "Få besked, når dit team ikke har nogen tilgængelighed",
"organization_no_slots_notification_switch_description": "Administratorer vil få e-mailbeskeder, når en bruger forsøger at booke et teammedlem og møder 'Ingen tilgængelighed'. Vi udløser denne e-mail efter to forekomster og minder dig om det hver 7. dag pr. bruger.",
@@ -2592,10 +2618,10 @@
"email_team_invite|subject|invited_to_org": "{{user}} har inviteret dig til at deltage i organisationen {{team}} på {{appName}}",
"email_team_invite|subject|added_to_subteam": "{{user}} har tilføjet dig til teamet {{team}} i organisationen {{parentTeamName}} på {{appName}}",
"email_team_invite|subject|invited_to_regular_team": "{{user}} inviterede dig til at deltage i teamet {{team}} på {{appName}}",
"email_team_invite|heading|added_to_org": "Du er blevet tilføjet til en {{appName}} organisation",
"email_team_invite|heading|invited_to_org": "Du er blevet inviteret til en {{appName}} organisation",
"email_team_invite|heading|added_to_subteam": "Du er blevet tilføjet til et team i {{parentTeamName}} organisationen",
"email_team_invite|heading|invited_to_subteam": "Du er blevet inviteret til et team i {{parentTeamName}} organisationen",
"email_team_invite|heading|added_to_org": "Du er blevet tilføjet til en {{appName}}-organisation",
"email_team_invite|heading|invited_to_org": "Du er blevet inviteret til en {{appName}}-organisation",
"email_team_invite|heading|added_to_subteam": "Du er blevet tilføjet til et team i {{parentTeamName}}-organisationen",
"email_team_invite|heading|invited_to_subteam": "Du er blevet inviteret til et team i {{parentTeamName}}-organisationen",
"email_team_invite|heading|invited_to_regular_team": "Du er blevet inviteret til at deltage i et {{appName}}-team",
"email_team_invite|content|added_to_org": "{{invitedBy}} har tilføjet dig til <strong>{{teamName}}</strong>-organisationen.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} har inviteret dig til at deltage i {{teamName}}-organisationen.",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "Begræns, hvor mange gange medlemmer kan bookes på tværs af alle team-begivenhedstyper",
"booking_limits_updated_successfully": "Bookinggrænser opdateret med succes",
"you_are_unauthorized_to_make_this_change_to_the_booking": "Du har ikke tilladelse til at foretage denne ændring i bookingen",
"delegation_credential_disabled": "Delegationslegitimation deaktiveret",
"matching_members": "Matchende medlemmer",
"x_matching_members": "{{x}} matchende medlemmer",
"matching_members_queue_using_attribute_weights": "Matchende medlemskø (bruger attributvægte)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "Ja, omfordel",
"filter_by_attributes": "Filtrer efter attributter",
"reassign_reason": "Årsag til omfordeling",
"field_type_change_suggestion": "Ændring af felttypen kan medføre tab af data. Opret venligst et nyt felt i stedet.",
"reassigned_by": "Omfordelt af",
"row": "række",
"rows": "rækker",
@@ -2902,7 +2930,7 @@
"routed_to_per_period": "Dirigeret til pr. periode",
"per_day": "Pr. dag",
"per_week": "Pr. uge",
"per_month": "pr. måned",
"per_month": "Pr. måned",
"routing_form_insights_assignment_reason": "Tildelingsårsag",
"access_denied": "Adgang nægtet",
"salesforce_route_to_owner": "Kontaktens ejer vil være Round Robin-vært, hvis tilgængelig",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "Du har ikke tilladelse til at oprette en organisation for denne e-mail",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "Du har ikke tilladelse til at migrere et eller flere af teamene",
"organization_has_subscription_already": "Organisationen har allerede et abonnement",
"reset_interval_weighted_rr": "Nulstil interval for vægtet Round Robin",
"interval": "Interval",
"rr_reset_interval_description": "Bestemmer hvor ofte round robin-bookingtællingen nulstilles for at sikre en balanceret fordeling.",
"daily": "Dagligt",
@@ -3059,6 +3088,7 @@
"ooo_team_redirect_infinite_not_allowed": "Der er allerede en omvendt videresendelse mellem disse brugere.",
"previous_ooo_empty_title": "Ingen tidligere fravær fundet",
"previous": "Forrige",
"rr_interval_successfully_updated": "Round Robin-intervallet blev opdateret",
"trial_mode": "Prøveperiode",
"inactive_team_plan": "Inaktiv teamplan",
"inactive_team_plan_description": "Din teamplan er inaktiv. Tjek dit abonnement eller kontakt kundesupport",
@@ -3074,6 +3104,7 @@
"user_has_no_team_yet": "Du har ikke et team endnu",
"no_team_members": "Du har ikke nogen teammedlemmer endnu",
"recurring_event_doesnt_support_seats": "Tilbagevendende begivenheder understøtter ikke sædefunktionen. Gør den ikke-tilbagevendende for at aktivere sæder.",
"booking_limit_per_booker_doesnt_support_recurring": "Gentagende begivenheder understøtter ikke funktionen for bookinggrænse pr. booker. Deaktiver bookinggrænse pr. booker for at aktivere gentagende begivenheder.",
"seats_doesnt_support_recurring": "Sædefunktionen understøtter ikke tilbagevendende begivenheder. Deaktiver sædefunktionen for at gøre den tilbagevendende.",
"recurring_event_seats_error": "Tilbagevendende begivenheder understøtter ikke sædefunktionen. Deaktiver sædefunktionen eller gør begivenheden ikke-tilbagevendende.",
"segment": "Segment",
@@ -3114,7 +3145,7 @@
"enable_delegation_credential": "Aktivér delegationslegitimation",
"enable_delegation_credential_description": "Giv Cal.com automatisk adgang til kalendere for alle organisationsmedlemmer ved at aktivere delegationslegitimation.",
"disable_delegation_credential": "Deaktiver delegationslegitimation",
"disable_delegation_credential_description": "Når delegationslegitimation er deaktiveret, skal organisationsmedlemmer, der ikke har forbundet deres kalendere, gøre det manuelt.",
"disable_delegation_credential_description": "Når delegationslegitimation deaktiveres, skal organisationsmedlemmer, der ikke har forbundet deres kalendere, gøre det manuelt.",
"last_active": "Sidst aktiv",
"member_since": "Medlem siden",
"last_updated": "Sidst opdateret",
@@ -3202,10 +3233,131 @@
"matching": "Matchning",
"event_redirect": "Begivenheds-omdirigering",
"reset_form": "Nulstil formular",
"rr_settings_successfully_updated": "Round Robin-indstillinger blev opdateret",
"distribution_basis_weighted_rr": "Fordelingsgrundlag for vægtet Round Robin",
"timestamp_basis_description": "Bestemmer hvilken tidsstempel for begivenheden, der bruges som grundlag for vægtet Round Robin-fordeling.",
"requires_credits": "Kræver credits",
"requires_credits_tooltip": "Du skal have nok credits på din konto for at bruge denne funktion",
"available_credits": "Tilgængelige credits",
"create_role": "Opret rolle",
"roles_and_permissions": "Roller og tilladelser",
"roles_and_permissions_description": "Administrer roller og tilladelser for din organisation",
"learn_more_permissions": "Lær mere om, hvordan tilladelser fungerer",
"role_name": "Rollenavn",
"role_name_placeholder": "f.eks. Salgsteam, Supportpersonale",
"role_description": "Rollebekskrivelse",
"role_description_placeholder": "Beskriv, hvad denne rolle er til, og hvilke tilladelser den har brug for",
"default_role": "Standardrolle",
"custom_role": "Brugerdefineret rolle",
"advanced": "Avanceret",
"search_permissions": "Søg tilladelser",
"all_permissions": "Alle tilladelser",
"none": "Ingen",
"read_only": "Kun læseadgang",
"pbac_desc_all_actions_all_resources": "Alle handlinger på alle ressourcer",
"pbac_resource_all": "Alle ressourcer",
"pbac_resource_event_type": "Begivenhedstyper",
"pbac_resource_team": "Teams",
"pbac_resource_organization": "Organisation",
"pbac_resource_booking": "Bookinger",
"pbac_resource_insights": "Indsigter",
"pbac_resource_role": "Roller",
"pbac_action_all": "Alle handlinger",
"pbac_action_create": "Opret",
"pbac_action_read": "Vis",
"pbac_action_update": "Redigér",
"pbac_action_delete": "Slet",
"pbac_action_manage": "Administrér",
"pbac_action_invite": "Invitér",
"pbac_action_remove": "Fjern",
"pbac_action_change_member_role": "Skift medlemsrolle",
"pbac_action_list_members": "Vis medlemmer",
"pbac_action_manage_billing": "Håndtér fakturering",
"pbac_action_read_team_bookings": "Vis teambookinger",
"pbac_action_read_org_bookings": "Vis organisationsbookinger",
"pbac_action_read_recordings": "Vis optagelser",
"role_created_successfully": "Rolle oprettet med succes",
"role_updated_successfully": "Rolle opdateret med succes",
"delete_role": "Slet rolle",
"confirm_delete_role": "Er du sikker på, at du vil slette denne rolle? Denne handling kan ikke fortrydes.",
"role_deleted_successfully": "Rolle slettet med succes",
"error_creating_role": "Fejl ved oprettelse af rolle",
"error_updating_role": "Fejl ved opdatering af rolle",
"pbac_desc_create_roles": "Opret roller",
"pbac_desc_view_roles": "Vis roller",
"pbac_desc_update_roles": "Opdater roller",
"pbac_desc_delete_roles": "Slet roller",
"pbac_desc_manage_roles": "Alle handlinger på roller",
"pbac_desc_create_event_types": "Opret begivenhedstyper",
"pbac_desc_view_event_types": "Vis begivenhedstyper",
"pbac_desc_update_event_types": "Opdater begivenhedstyper",
"pbac_desc_delete_event_types": "Slet begivenhedstyper",
"pbac_desc_manage_event_types": "Alle handlinger på begivenhedstyper",
"pbac_desc_create_teams": "Opret teams",
"pbac_desc_view_team_details": "Vis teamdetaljer",
"pbac_desc_update_team_settings": "Opdater teamindstillinger",
"pbac_desc_delete_team": "Slet team",
"pbac_desc_invite_team_members": "Invitér teammedlemmer",
"pbac_desc_remove_team_members": "Fjern teammedlemmer",
"pbac_desc_change_team_member_role": "Skift rolle for teammedlemmer",
"pbac_desc_manage_teams": "Alle handlinger på teams",
"pbac_desc_create_organization": "Opret organisation",
"pbac_desc_view_organization_details": "Se organisationsdetaljer",
"pbac_desc_list_organization_members": "Vis organisationsmedlemmer",
"pbac_desc_invite_organization_members": "Inviter organisationsmedlemmer",
"pbac_desc_remove_organization_members": "Fjern organisationsmedlemmer",
"pbac_desc_manage_organization_billing": "Administrer organisationsfakturering",
"pbac_desc_change_organization_member_role": "Ændr rolle for organisationsmedlemmer",
"pbac_desc_edit_organization_settings": "Rediger organisationsindstillinger",
"pbac_desc_manage_organizations": "Alle handlinger på organisationer",
"pbac_desc_view_bookings": "Se bookinger",
"pbac_desc_view_team_bookings": "Se team-bookinger",
"pbac_desc_view_organization_bookings": "Se organisationsbookinger",
"pbac_desc_view_booking_recordings": "Se bookingoptagelser",
"pbac_desc_update_bookings": "Opdater bookinger",
"pbac_desc_manage_bookings": "Alle handlinger på bookinger",
"pbac_desc_view_team_insights": "Se teamindsigter",
"pbac_desc_manage_team_insights": "Administrer teamindsigter",
"read_permission_auto_enabled_tooltip": "Læs tilladelse aktiveres automatisk, når en ressource oprettes, opdateres eller slettes",
"choose_restriction_schedule": "Tilføj restriktionsskema",
"choose_restriction_schedule_description": "Aktivér dette, hvis du vil begrænse værternes tilgængelighed for denne begivenhedstype baseret på et restriktionsskema. Dette fjerner kun de tidsrum, der ikke er i restriktionsskemaet, det tilføjer ikke nye tidsrum.",
"use_booker_timezone": "Brug bookerens tidszone",
"use_booker_timezone_info": "Anvend det valgte restriktionsskema i bookerens tidszone",
"load_balancing_warning": "Dette vil deaktivere Load Balancing på alle dine Round Robin-begivenhedstyper",
"rr_load_balancing_disabled": "Load Balancing er kun tilgængelig, når tidsstempelgrundlaget er indstillet til bookingens oprettelsestidspunkt",
"round_robin_settings_description": "Tilpas standardindstillingerne for round robin for dette team",
"round_robin_settings_updated_successfully": "Round robin-indstillinger opdateret med succes",
"booking_creation_time": "Bookingoprettelsestid",
"meeting_start_time": "Mødestarttid",
"members_affected_by_disabling_delegation_credential": "Berørte medlemmer",
"no_members_affected_by_disabling_delegation_credential": "Ingen medlemmer berørt af deaktivering af delegationslegitimation",
"download_expense_log": "Download udgiftslog",
"error_downloading_expense_log": "Fejl ved download af udgiftslog",
"offer_to_reschedule_last_booking": "Tilbyd at omlægge den sidste aktive booking til den valgte tidsperiode",
"booker_limit_exceeded_error": "Bookerens maksimale aktive bookinggrænse er overskredet",
"booker_limit_exceeded_error_reschedule": "Du har allerede en booking for denne begivenhed den {{date}}. Vil du omlægge til det nye valgte tidspunkt?",
"duration_min_error": "Varigheden skal være mindst {{min}} minut(ter)",
"duration_max_error": "Varigheden må ikke overstige {{max}} minutter (24 timer)",
"booking_not_allowed_by_restriction_schedule_error": "Booking uden for tilgængeligheden i restriktionsplanen.",
"restriction_schedule_not_found_error": "Restriktionsplan ikke fundet",
"converted_image_size_limit_exceed": "Billedstørrelsesgrænsen er overskredet, brug venligst et mindre billede, helst i JPEG-format",
"license_key": "Licensnøgle",
"license_selection_title": "Vælg din licens",
"license_selection_description": "Vælg, hvordan du vil licensere din Cal.com-implementering",
"agplv3_license_description": "Brug Cal.com under AGPLv3 open source-licensen",
"enter_license_key": "Indtast licensnøgle",
"enter_your_license_key": "Adgang til alle virksomhedsfunktioner",
"enter_existing_license": "Fra $15/bruger pr. måned",
"enter_license_key_description": "Adgang til alle virksomhedsfunktioner",
"license_key_placeholder": "Indtast din licensnøgle",
"save_license_key": "Gem licensnøgle",
"signature_token_optional": "Signaturtoken (valgfrit)",
"signature_token_description": "Signaturtoken bruges til at verificere, at licensnøglen tilhører dig, når du øger forbrugsbaseret fakturering. Du kan finde den i din købsbekræftelsesmail. Hvis du har en ældre licens, kan du lade dette felt være tomt.",
"need_a_license": "Har du brug for en licens?",
"license_key_required": "Licensnøgle er påkrævet",
"invalid_license_key": "Ugyldig licensnøgle",
"license_validation_failed": "Validering af licensnøgle mislykkedes",
"license_key_saved": "Licensnøgle blev gemt med succes",
"timezone_mismatch_tooltip": "Du ser rapporten baseret på din profils tidszone ({{userTimezone}}), mens din browser er indstillet til tidszonen ({{browserTimezone}})",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Tilføj dine nye strenge ovenfor her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
+176 -24
View File
@@ -47,7 +47,24 @@
"customize_calvideo_settings": "Cal Video-Einstellungen anpassen",
"calvideo_settings_description": "Konfigurieren Sie Videokonferenz-Präferenzen einschließlich Aufnahmeberechtigungen für Gastgeber und Teilnehmer.",
"disable_recording_for_guests": "Aufnahme für Gäste deaktivieren",
"enable_automatic_transcription": "Automatische Transkription nach Beitritt zum Meeting aktivieren",
"video_options": "Videooptionen",
"get_meeting_session_details": "Meeting-Sitzungsdetails abrufen",
"meeting_session_details": "Meeting-Sitzungsdetails",
"meeting_session": "Meeting-Sitzung",
"session_id": "Sitzungs-ID",
"max_participants": "Maximale Teilnehmerzahl",
"unlimited": "Unbegrenzt",
"ongoing": "Laufend",
"ended": "Beendet",
"participants": "Teilnehmer",
"anonymous_user": "Anonymer Benutzer",
"session_duration": "Sitzungsdauer",
"no_meeting_sessions_found": "Keine Meeting-Sitzungen gefunden",
"not_available": "Nicht verfügbar",
"disable_recording_for_organizer": "Aufnahme für Organisator deaktivieren",
"disable_transcription_for_guests": "Transkription für Gäste deaktivieren",
"disable_transcription_for_organizer": "Transkription für Organisator deaktivieren",
"event_request_declined_recurring": "Ihre wiederkehrende Terminanfrage wurde abgelehnt",
"event_request_cancelled": "Ihr geplanter Termin wurde abgesagt",
"event_request_reassigned": "Ihr geplanter Termin wurde neu zugewiesen",
@@ -264,6 +281,8 @@
"guests": "Gäste",
"guest": "Gast",
"web_conferencing_details_to_follow": "Web-Konferenzdetails folgen.",
"confirmation": "Bestätigung",
"what_booker_should_provide": "Was Ihr Bucher angeben sollte, um Bestätigungen zu erhalten",
"404_the_user": "Der Benutzername",
"username": "Benutzername",
"is_still_available": "ist noch verfügbar.",
@@ -331,7 +350,7 @@
"organization_general_description": "Einstellungen für die Sprache und Zeitzone Ihres Teams verwalten",
"few_sentences_about_yourself": "Schreiben Sie eine kurze Beschreibung, welche auf Ihrer persönlichen URL-Seite erscheinen wird.",
"nearly_there": "Fast fertig",
"nearly_there_instructions": "Zu guter Letzt: Ein Foto und eine Kurzbeschreibung zu Ihnen helfen, öfter gebucht zu werden, da Kunden dann genauer wissen, bei wem sie einen Termin buchen.",
"nearly_there_instructions": "Zum Schluss: Eine kurze Beschreibung über Sie und ein Foto helfen wirklich, Buchungen zu erhalten und lassen andere wissen, mit wem sie einen Termin buchen.",
"set_availability_instructions": "Definieren Sie Zeiträume, wenn Sie wiederkehrend verfügbar sind. Sie können später weitere Zeiträume erstellen und verschiedenen Kalendern zuordnen.",
"set_availability": "Verfügbarkeit festlegen",
"set_availbility_description": "Legen Sie Zeitpläne für die Zeiten fest, in denen Sie gebucht werden möchten.",
@@ -343,14 +362,14 @@
"connect_your_calendar": "Kalender verbinden",
"connect_your_video_app": "Verbinden Sie Ihre Video-Apps",
"connect_your_video_app_instructions": "Verbinden Sie Ihre Video-Apps, um sie für Ihre Termintypen zu verwenden.",
"connect_your_calendar_instructions": "Verbinden Sie Ihren Kalender, um automatisch zu prüfen, ob Termine verfügbar sind.",
"connect_your_calendar_instructions": "Verbinden Sie Ihren Kalender, um automatisch nach belegten Zeiten zu suchen und neue Termine zu erfassen, sobald sie geplant werden.",
"set_up_later": "Später einrichten",
"current_time": "Aktuelle Zeit",
"details": "Details",
"welcome": "Willkommen",
"welcome_back": "Willkommen zurück",
"welcome_to_calcom": "Willkommen bei {{appName}}",
"welcome_instructions": "Wie ist Ihr Name und in welcher Zeitzone sind Sie? Dies kann später bearbeitet werden.",
"welcome_instructions": "Teilen Sie uns mit, wie wir Sie nennen sollen und in welcher Zeitzone Sie sich befinden. Sie können dies später bearbeiten.",
"connect_caldav": "Mit CalDav-Server verbinden",
"connect_ics_feed": "Mit einem ICS-Feed verbinden",
"connect": "Verbinden",
@@ -361,7 +380,7 @@
"when": "Wann",
"where": "Wo",
"add_to_calendar": "Zum Kalender hinzufügen",
"add_to_calendar_description": "Legen Sie fest, wo neue Termine hinzugefügt werden sollen, wenn Sie gebucht werden.",
"add_to_calendar_description": "Wählen Sie aus, wo Termine hinzugefügt werden sollen, wenn Sie gebucht werden.",
"add_events_to": "Termine hinzufügen zu",
"add_another_calendar": "Einen weiteren Kalender hinzufügen",
"other": "Sonstige",
@@ -1132,7 +1151,7 @@
"24_hour_short": "24 Std",
"redirect_success_booking": "Weiterleitung bei Buchung ",
"you_are_being_redirected": "Sie werden in $t(second, {\"count\": {{seconds}} }) auf {{ url }} weitergeleitet.",
"external_redirect_url": "Externe Umleitungs-URL - Beginnt mit https://",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "Nach erfolgreicher Buchung auf eine benutzerdefinierte URL weiterleiten",
"duplicate": "Duplizieren",
"offer_seats": "Plätze anbieten",
@@ -1153,6 +1172,7 @@
"test_failed": "Test fehlgeschlagen",
"provide_api_key": "API-Schlüssel bereitstellen",
"api_key_modal_subtitle": "Mit API-Schlüsseln können Sie API-Aufrufe für Ihren eigenen Account durchführen.",
"api_key_modal_subtitle_platform": "Erstellen Sie stattdessen einen Marktplatz oder eine Plattform.",
"api_keys_subtitle": "Generieren Sie API-Schlüssel für den Zugriff auf Ihren eigenen Account.",
"create_api_key": "API-Schlüssel erstellen",
"personal_note": "Diesen Schlüssel benennen",
@@ -1171,6 +1191,8 @@
"confirm_delete_api_key": "Diesen API-Schlüssel widerrufen",
"disable_rescheduling": "Umbuchung deaktivieren",
"description_disable_rescheduling": "Gäste können die Veranstaltung nicht mehr über Kalendereinladung oder E-Mail umbuchen",
"allow_rescheduling_cancelled_bookings": "Buchung über Umbuchungslink erlauben",
"description_allow_rescheduling_cancelled_bookings": "Wenn aktiviert, können Benutzer eine neue Buchung erstellen, wenn sie versuchen, eine stornierte Buchung umzuplanen",
"disable_cancelling": "Stornierung deaktivieren",
"description_disable_cancelling": "Gäste können die Veranstaltung nicht mehr über Kalendereinladung oder E-Mail stornieren",
"revoke_api_key": "API-Schlüssel widerrufen",
@@ -1613,7 +1635,7 @@
"workflow_example_6": "Benutzerdefinierte SMS senden, wenn der Termin zum Moderieren gebucht wird",
"count_managed_to_limit": "Buchungszahlen von verwalteten Ereignistypen einbeziehen",
"welcome_to_cal_header": "Willkommen bei {{appName}}!",
"edit_form_later_subtitle": "Sie können dies später bearbeiten.",
"edit_form_later_subtitle": "Du kannst dies später bearbeiten.",
"connect_calendar_later": "Ich verbinde meinen Kalender später",
"problem_saving_user_profile": "Beim Speichern Ihrer Daten ist ein Problem aufgetreten. Bitte versuchen Sie es erneut oder wenden Sie sich an den Support.",
"purchase_missing_seats": "Fehlende Plätze kaufen",
@@ -1675,7 +1697,7 @@
"create_first_api_key_description": "API-Schlüssel erlauben es anderen Apps, mit {{appName}} zu kommunizieren",
"back_to_signin": "Zurück zur Anmeldung",
"reset_link_sent": "Zurücksetzungslink gesendet",
"password_reset_email": "Eine E-Mail mit Anweisungen zum Zurücksetzen Ihres Passworts ist auf dem Weg zu {{email}}.",
"password_reset_email": "Eine E-Mail mit Anweisungen zum Zurücksetzen deines Passworts ist unterwegs an {{email}}.",
"password_reset_leading": "Falls Sie nicht bald eine E-Mail erhalten, überprüfen Sie, ob die eingegebene E-Mail-Adresse korrekt ist. Überprüfen Sie außerdem Ihren Spam-Ordner oder wenden Sie sich an den Support, wenn das Problem weiterhin besteht.",
"password_updated": "Passwort aktualisiert!",
"pending_payment": "Zahlung ausstehend",
@@ -1696,12 +1718,17 @@
"only_show_first_available_slot_description": "Dies beschränkt Ihre Verfügbarkeit für diesen Ereignistyp auf einen Zeitslot pro Tag, der zum frühestmöglichen Zeitpunkt geplant wird.",
"limit_total_booking_duration": "Gesamtbuchungsdauer beschränken",
"limit_total_booking_duration_description": "Begrenzt die Gesamtanzahl, die dieser Termin gebucht werden kann",
"booker_booking_limit": "Limit für aktive Buchungen pro Bucher",
"booker_booking_limit_description": "Begrenze die Anzahl der aktiven Buchungen, die ein Bucher für diesen Termintyp vornehmen kann",
"recurring_event_doesnt_support_booker_booking_limit": "Wiederkehrende Termine unterstützen keine Buchungslimits pro Bucher",
"add_limit": "Begrenzung hinzufügen",
"team_name_required": "Teamname erforderlich",
"show_attendees": "Teilnehmerinformationen zwischen Gästen teilen",
"show_available_seats_count": "Anzahl der verfügbaren Plätze anzeigen",
"how_booking_questions_as_variables": "Wie verwendet man Buchungsfragen als Variablen?",
"format": "Format",
"questions": "Fragen",
"all_info_your_booker_provide": "Alle Informationen, die dein Bucher vor der Buchung angeben sollte.",
"uppercase_for_letters": "Großbuchstaben für alle Buchstaben verwenden",
"replace_whitespaces_underscores": "Leerzeichen durch Unterstriche ersetzen",
"platform_members": "Plattform-Mitglieder",
@@ -1915,7 +1942,7 @@
"new_attendee": "Neuer Teilnehmer",
"awaiting_approval": "Wartet auf Genehmigung",
"requires_google_calendar": "Diese App erfordert eine Verbindung mit Google Calendar",
"event_type_requires_google_calendar": "Das \"Zum Kalender hinzufügen\" für diesen Ereignistyp muss ein Google Kalender sein, damit Meet funktioniert. Verbinde ihn <0>hier</0>.",
"event_type_requires_google_calendar": "Für die Funktion \"Zum Kalender hinzufügen\" für diesen Termintyp wird ein Google Kalender benötigt, damit Meet funktioniert. Verbinde ihn <0>hier</0>.",
"connected_google_calendar": "Sie haben ein Google Calendar-Konto verbunden.",
"using_meet_requires_calendar": "Die Nutzung von Google Meet erfordert einen verbundenen Google Calendar",
"continue_to_install_google_calendar": "Installation von Google Calender fortsetzen",
@@ -1967,12 +1994,12 @@
"delete_sso_configuration_confirmation_description": "Sind Sie sicher, dass Sie die {{connectionType}}-Konfiguration löschen möchten? Ihre Teammitglieder, die sich über {{connectionType}} anmelden, werden keinen Zugriff mehr auf Cal.com haben.",
"organizer_timezone": "Zeitzone des Veranstalters",
"email_user_cta": "Einladung anzeigen",
"email_no_user_invite_heading_subteam": "Sie wurden eingeladen, einem Team der Organisation {{parentTeamName}} beizutreten",
"email_no_user_invite_heading_org": "Sie wurden eingeladen, einer {{appName}}-Organisation beizutreten",
"email_no_user_invite_heading_subteam": "Du wurdest eingeladen, einem Team der Organisation {{parentTeamName}} beizutreten",
"email_no_user_invite_heading_org": "Du wurdest eingeladen, einer {{appName}}-Organisation beizutreten",
"email_no_user_invite_subheading": "{{invitedBy}} hat Sie eingeladen, dem Team in {{appName}} beizutreten. {{appName}} ist der Event-Planer, der es Ihnen und Ihrem Team ermöglicht, Meetings ohne ständiges hin und her zu planen.",
"email_user_invite_subheading_subteam": "{{invitedBy}} hat Sie eingeladen, dem Team „{{teamName}}“ in seiner bzw. ihrer Organisation {{parentTeamName}} bei {{appName}} beizutreten. {{appName}} ist der Terminplaner, der es Ihnen und Ihrem Team ermöglicht, Meetings ohne Hin und Her zu planen.",
"email_user_invite_subheading_org": "{{invitedBy}} hat Sie eingeladen, ihrer Organisation „{{teamName}}“ in {{appName}} beizutreten. {{appName}} ist der Terminplaner, der es Ihnen und Ihrer Organisation ermöglicht, Meetings ohne ständiges hin und her zu planen.",
"email_no_user_invite_steps_intro": "Wir begleiten Sie durch ein paar Schritte, danach können Sie in kürzester Zeit stressfreie Planung mit Ihrer {{entity}} genießen.",
"email_no_user_invite_steps_intro": "Wir führen dich durch einige kurze Schritte und du wirst in kürzester Zeit stressfreie Terminplanung mit deinem {{entity}} genießen.",
"email_no_user_step_one": "Wählen Sie Ihren Benutzernamen",
"email_no_user_step_two": "Verbinden Sie Ihr Kalenderkonto",
"email_no_user_step_three": "Legen Sie Ihre Verfügbarkeit fest",
@@ -2048,7 +2075,7 @@
"make_informed_decisions": "Treffen Sie mit Insights fundierte Entscheidungen",
"make_informed_decisions_description": "Unser Insights-Dashboard deckt alle Aktivitäten in Ihrem Team ab und zeigt Ihnen Trends, die eine bessere Teamplanung und Entscheidungsfindung ermöglichen.",
"view_bookings_across": "Buchungen für alle Mitglieder anzeigen",
"view_bookings_across_description": "Sehen Sie, wer am häufigsten gebucht wird und sorgen Sie für die beste Verteilung in Ihrem Team",
"view_bookings_across_description": "Sieh, wer die meisten Buchungen erhält und stelle die beste Verteilung in deinem Team sicher",
"identify_booking_trends": "Buchungstrends erkennen",
"identify_booking_trends_description": "Sehen Sie, welche Wochenzeiten und welche Tageszeiten bei Ihren Buchern beliebt sind",
"spot_popular_event_types": "Machen Sie beliebte Ereignistypen ausfindig",
@@ -2171,7 +2198,7 @@
"insights_no_data_found_for_filter": "Keine Daten für den ausgewählten Filter oder das ausgewählte Datum gefunden.",
"acknowledge_booking_no_show_fee": "Ich bestätige, dass meiner Karte eine Gebühr von {{amount, currency}} berechnet wird, sofern ich nicht an diesem Ereignis teilnehme.",
"card_details": "Karteninformationen",
"something_went_wrong_on_our_end": "Etwas ist auf unserer Seite schief gelaufen. Kontaktieren Sie unser Support-Team, wir werden es sofort für Sie reparieren.",
"something_went_wrong_on_our_end": "Bei uns ist etwas schiefgelaufen. Kontaktiere unseren Support und wir werden das Problem umgehend für dich beheben.",
"please_provide_following_text_to_suppport": "Bitte geben Sie den folgenden Text an, wenn Sie den Support kontaktieren, damit wir Ihnen besser helfen können",
"seats_and_no_show_fee_error": "Aktuell können keine Plätze aktiviert oder eine Gebühr für das Nichterscheinen berechnet werden",
"complete_your_booking": "Schließen Sie Ihre Buchung ab",
@@ -2276,7 +2303,7 @@
"messaging": "Nachrichten",
"sender_id_info": "Name oder Nummer, welcher bzw. welche als Absender einer SMS angezeigt wird (einige Länder erlauben keine alphanumerischen Absender-IDs)",
"org_admins_can_create_new_teams": "Nur der Administrator Ihrer Organization kann neue Teams erstellen",
"google_new_spam_policy": "Die neuen Spam-Richtlinien von Google könnten verhindern, dass Sie E-Mail- und Kalenderbenachrichtigungen zu dieser Buchung erhalten.",
"google_new_spam_policy": "Googles neue Spam-Richtlinie könnte verhindern, dass du E-Mail- und Kalenderbenachrichtigungen über diese Buchung erhältst.",
"resolve": "Problem lösen",
"no_organization_slug": "Beim Erstellen von Teams für diese Organization ist ein Fehler aufgetreten. URL-Slug fehlt.",
"copy_link_org": "Link zur Organization kopieren",
@@ -2311,7 +2338,6 @@
"delegation_credential_description": "Der Delegierungsnachweis ermöglicht es Ihnen, den Zugriff auf Google Workspace-Kalender für Ihre Organisation zu verwalten.",
"add_client_id_in_google_workspace_with_below_scope": "Fügen Sie diese Client-ID in Google Workspace mit dem folgenden Bereich hinzu",
"delegation_credential_enabled": "Delegierungsnachweis aktiviert",
"delegation_credential_disabled": "Delegierungsnachweis deaktiviert",
"add_delegation_credential": "Delegierungsnachweis hinzufügen",
"edit_delegation_credential": "Delegierungsnachweis bearbeiten",
"domain": "Domain",
@@ -2454,7 +2480,7 @@
"view_overlay_calendar_events": "Sehen Sie sich Ihre Kalendertermine an, um Buchungskonflikte zu vermeiden.",
"join_event_location": "{{eventLocationType}} beitreten",
"troubleshooting": "Problembehandlung",
"calendars_were_checking_for_conflicts": "Kalender, die wir auf Konflikten überprüfen",
"calendars_were_checking_for_conflicts": "Kalender, die wir auf Konflikte prüfen",
"availabilty_schedules": "Verfügbarkeitspläne",
"manage_calendars": "Kalender verwalten",
"manage_availability_schedules": "Verfügbarkeitspläne verwalten",
@@ -2582,7 +2608,7 @@
"date": "Datum",
"overlaps_with_existing_schedule": "Dies überlappt sich mit einem bestehenden Zeitplan. Bitte wählen Sie ein anderes Datum.",
"org_admin_no_slots|heading": "Keine Verfügbarkeit für {{name}} gefunden",
"org_admin_no_slots|content": "Hallo Organisationsadmins,<br /><br />Bitte beachtet: Wir wurden darauf hingewiesen, dass {{username}} keine Verfügbarkeit hatte, als ein Benutzer {{username}}/{{slug}} besucht hat<br /><br />Das könnte einige Gründe haben<br />Der Benutzer hat keine verbundenen Kalender<br />Seine Zeitpläne, die an dieses Ereignis angeschlossen sind, sind nicht aktiviert<br /> <br />Wir empfehlen, seine Verfügbarkeit zu überprüfen, um das Problem zu beheben.",
"org_admin_no_slots|content": "Hallo Organisationsadministratoren,<br /><br />Bitte beachten: Es wurde uns mitgeteilt, dass {{username}} keine Verfügbarkeit hatte, als ein Benutzer {{username}}/{{slug}} besucht hat<br /><br />Es gibt einige Gründe, warum dies passieren könnte<br />Der Benutzer hat keine Kalender verbunden<br />Die mit diesem Termin verknüpften Zeitpläne sind nicht aktiviert<br /> <br />Wir empfehlen, die Verfügbarkeit zu überprüfen, um dieses Problem zu lösen.",
"org_admin_no_slots|cta": "Benutzerverfügbarkeit öffnen",
"organization_no_slots_notification_switch_title": "Erhalten Sie Benachrichtigungen, wenn Ihr Team keine Verfügbarkeiten hat",
"organization_no_slots_notification_switch_description": "Admins erhalten E-Mail-Benachrichtigungen, wenn ein Benutzer versucht, ein Teammitglied zu buchen, und „Keine Verfügbarkeit“ sieht. Wir lösen diese E-Mail nach zwei Vorfällen aus und erinnern Sie alle 7 Tage pro Benutzer. ",
@@ -2592,11 +2618,11 @@
"email_team_invite|subject|invited_to_org": "{{user}} hat Sie eingeladen, der Organisation {{team}} auf {{appName}} beizutreten",
"email_team_invite|subject|added_to_subteam": "{{user}} hat Sie zum Team {{team}} der Organisation {{parentTeamName}} auf {{appName}} hinzugefügt",
"email_team_invite|subject|invited_to_regular_team": "{{user}} hat Sie eingeladen, dem Team {{team}} auf {{appName}} beizutreten",
"email_team_invite|heading|added_to_org": "Sie wurden zu einer {{appName}}-Organisation hinzugefügt",
"email_team_invite|heading|invited_to_org": "Sie wurden zu einer {{appName}}-Organisation eingeladen",
"email_team_invite|heading|added_to_subteam": "Sie wurden zu einem Team einer {{parentTeamName}}-Organisation hinzugefügt",
"email_team_invite|heading|invited_to_subteam": "Sie wurden zu einem Team einer {{parentTeamName}}-Organisation eingeladen",
"email_team_invite|heading|invited_to_regular_team": "Sie wurden eingeladen, einem {{appName}}-Team beizutreten",
"email_team_invite|heading|added_to_org": "Du wurdest zu einer {{appName}}-Organisation hinzugefügt",
"email_team_invite|heading|invited_to_org": "Du wurdest zu einer {{appName}}-Organisation eingeladen",
"email_team_invite|heading|added_to_subteam": "Du wurdest zu einem Team der {{parentTeamName}}-Organisation hinzugefügt",
"email_team_invite|heading|invited_to_subteam": "Du wurdest zu einem Team der {{parentTeamName}}-Organisation eingeladen",
"email_team_invite|heading|invited_to_regular_team": "Du wurdest eingeladen, einem {{appName}}-Team beizutreten",
"email_team_invite|content|added_to_org": "{{invitedBy}} hat Sie zur <strong>{{teamName}}</strong>-Organisation hinzugefügt.",
"email_team_invite|content|invited_to_org": "{{invitedBy}} hat Sie zur {{teamName}}-Organisation eingeladen.",
"email_team_invite|subject|invited_to_subteam": "{{user}} hat Sie eingeladen, dem Team {{team}} der Organisation {{parentTeamName}} auf {{appName}} beizutreten",
@@ -2781,6 +2807,7 @@
"limit_team_booking_frequency_description": "Begrenzen Sie, wie oft Mitglieder über alle Team-Ereignistypen hinweg gebucht werden können",
"booking_limits_updated_successfully": "Buchungslimits erfolgreich aktualisiert",
"you_are_unauthorized_to_make_this_change_to_the_booking": "Sie sind nicht berechtigt, diese Änderung an der Buchung vorzunehmen",
"delegation_credential_disabled": "Delegierungsnachweis deaktiviert",
"matching_members": "Passende Mitglieder",
"x_matching_members": "{{x}} passende Mitglieder",
"matching_members_queue_using_attribute_weights": "Warteschlange für passende Mitglieder (mit Attributgewichtung)",
@@ -2861,6 +2888,7 @@
"yes_reassign": "Ja, neu zuweisen",
"filter_by_attributes": "Nach Attributen filtern",
"reassign_reason": "Grund für die Neuzuweisung",
"field_type_change_suggestion": "Das Ändern des Feldtyps kann zu Datenverlust führen. Bitte erstelle stattdessen ein neues Feld.",
"reassigned_by": "Neu zugewiesen von",
"row": "Zeile",
"rows": "Zeilen",
@@ -2902,7 +2930,7 @@
"routed_to_per_period": "Weitergeleitet pro Zeitraum",
"per_day": "Pro Tag",
"per_week": "Pro Woche",
"per_month": "pro Monat",
"per_month": "Pro Monat",
"routing_form_insights_assignment_reason": "Zuweisungsgrund",
"access_denied": "Zugriff verweigert",
"salesforce_route_to_owner": "Der Kontaktinhaber wird der Round Robin Host sein, falls verfügbar",
@@ -3043,6 +3071,7 @@
"you_do_not_have_permission_to_create_an_organization_for_this_email": "Sie haben keine berechtigung, eine organisation für diese e-mail-adresse zu erstellen",
"you_do_not_have_permission_to_migrate_one_or_more_of_the_teams": "Sie haben keine berechtigung, eines oder mehrere der teams zu migrieren",
"organization_has_subscription_already": "Die organisation hat bereits ein abonnement",
"reset_interval_weighted_rr": "Intervall für gewichtetes Round Robin zurücksetzen",
"interval": "Intervall",
"rr_reset_interval_description": "Legt fest, wie oft der Round Robin Buchungszähler zurückgesetzt wird, um eine ausgewogene Verteilung zu gewährleisten.",
"daily": "Täglich",
@@ -3059,6 +3088,7 @@
"ooo_team_redirect_infinite_not_allowed": "Es gibt bereits eine umgekehrte Weiterleitung zwischen diesen Benutzern.",
"previous_ooo_empty_title": "Keine vorherige Abwesenheit gefunden",
"previous": "Vorherige",
"rr_interval_successfully_updated": "Round Robin-Intervall erfolgreich aktualisiert",
"trial_mode": "Testmodus",
"inactive_team_plan": "Inaktiver Team-Plan",
"inactive_team_plan_description": "Ihr Team-Plan ist inaktiv. Überprüfen Sie Ihr Abonnement oder wenden Sie sich an den Kundensupport",
@@ -3074,6 +3104,7 @@
"user_has_no_team_yet": "Sie haben noch kein Team",
"no_team_members": "Sie haben noch keine Teammitglieder",
"recurring_event_doesnt_support_seats": "Wiederkehrende Events unterstützen die Sitzplatzfunktion nicht. Machen Sie es zu einem einmaligen Event, um Sitzplätze zu aktivieren.",
"booking_limit_per_booker_doesnt_support_recurring": "Wiederkehrende Ereignisse unterstützen die Funktion zur Buchungsbegrenzung pro Bucher nicht. Deaktiviere die Buchungsbegrenzung pro Bucher, um wiederkehrende Ereignisse zu aktivieren.",
"seats_doesnt_support_recurring": "Die Sitzplatzfunktion unterstützt keine wiederkehrenden Events. Deaktivieren Sie die Sitzplatzfunktion, um es wiederkehrend zu machen.",
"recurring_event_seats_error": "Wiederkehrende Events unterstützen die Sitzplatzfunktion nicht. Deaktivieren Sie die Sitzplatzfunktion oder machen Sie das Event nicht wiederkehrend.",
"segment": "Segment",
@@ -3114,7 +3145,7 @@
"enable_delegation_credential": "Delegierungsnachweis aktivieren",
"enable_delegation_credential_description": "Gewähren Sie Cal.com automatischen Zugriff auf die Kalender aller Organisationsmitglieder, indem Sie den Delegierungsnachweis aktivieren.",
"disable_delegation_credential": "Delegierungsnachweis deaktivieren",
"disable_delegation_credential_description": "Sobald der Delegierungsnachweis deaktiviert ist, müssen Organisationsmitglieder, die ihre Kalender noch nicht verbunden haben, dies manuell tun.",
"disable_delegation_credential_description": "Sobald die Delegationsanmeldedaten deaktiviert sind, müssen Organisationsmitglieder, die ihre Kalender noch nicht verbunden haben, dies manuell tun.",
"last_active": "Zuletzt aktiv",
"member_since": "Mitglied seit",
"last_updated": "Zuletzt aktualisiert",
@@ -3202,10 +3233,131 @@
"matching": "Übereinstimmung",
"event_redirect": "Event-Weiterleitung",
"reset_form": "Formular zurücksetzen",
"rr_settings_successfully_updated": "Round Robin-Einstellungen erfolgreich aktualisiert",
"distribution_basis_weighted_rr": "Verteilungsgrundlage für gewichtetes Round Robin",
"timestamp_basis_description": "Bestimmt, welcher Ereigniszeitstempel als Grundlage für die gewichtete Round Robin-Verteilung verwendet wird.",
"requires_credits": "Erfordert Credits",
"requires_credits_tooltip": "Du benötigst genügend Credits in deinem Konto, um diese Funktion zu nutzen",
"available_credits": "Verfügbare Credits",
"create_role": "Rolle erstellen",
"roles_and_permissions": "Rollen & Berechtigungen",
"roles_and_permissions_description": "Verwalte Rollen und Berechtigungen für deine Organisation",
"learn_more_permissions": "Erfahre mehr darüber, wie Berechtigungen funktionieren",
"role_name": "Rollenname",
"role_name_placeholder": "z.B. Vertriebsteam, Support-Mitarbeiter",
"role_description": "Rollenbeschreibung",
"role_description_placeholder": "Beschreibe, wofür diese Rolle gedacht ist und welche Berechtigungen sie benötigt",
"default_role": "Standardrolle",
"custom_role": "Benutzerdefinierte Rolle",
"advanced": "Erweitert",
"search_permissions": "Berechtigungen suchen",
"all_permissions": "Alle Berechtigungen",
"none": "Keine",
"read_only": "Nur lesen",
"pbac_desc_all_actions_all_resources": "Alle Aktionen für alle Ressourcen",
"pbac_resource_all": "Alle Ressourcen",
"pbac_resource_event_type": "Ereignistypen",
"pbac_resource_team": "Teams",
"pbac_resource_organization": "Organization",
"pbac_resource_booking": "Buchungen",
"pbac_resource_insights": "Erkenntnisse",
"pbac_resource_role": "Rollen",
"pbac_action_all": "Alle Aktionen",
"pbac_action_create": "Erstellen",
"pbac_action_read": "Ansehen",
"pbac_action_update": "Bearbeiten",
"pbac_action_delete": "Löschen",
"pbac_action_manage": "Verwalten",
"pbac_action_invite": "Einladen",
"pbac_action_remove": "Entfernen",
"pbac_action_change_member_role": "Mitgliedsrolle ändern",
"pbac_action_list_members": "Mitglieder auflisten",
"pbac_action_manage_billing": "Abrechnung verwalten",
"pbac_action_read_team_bookings": "Team-Buchungen ansehen",
"pbac_action_read_org_bookings": "Organizations-Buchungen ansehen",
"pbac_action_read_recordings": "Aufnahmen anzeigen",
"role_created_successfully": "Rolle erfolgreich erstellt",
"role_updated_successfully": "Rolle erfolgreich aktualisiert",
"delete_role": "Rolle löschen",
"confirm_delete_role": "Sind Sie sicher, dass Sie diese Rolle löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"role_deleted_successfully": "Rolle erfolgreich gelöscht",
"error_creating_role": "Fehler beim Erstellen der Rolle",
"error_updating_role": "Fehler beim Aktualisieren der Rolle",
"pbac_desc_create_roles": "Rollen erstellen",
"pbac_desc_view_roles": "Rollen anzeigen",
"pbac_desc_update_roles": "Rollen aktualisieren",
"pbac_desc_delete_roles": "Rollen löschen",
"pbac_desc_manage_roles": "Alle Aktionen für Rollen",
"pbac_desc_create_event_types": "Ereignistypen erstellen",
"pbac_desc_view_event_types": "Ereignistypen anzeigen",
"pbac_desc_update_event_types": "Ereignistypen aktualisieren",
"pbac_desc_delete_event_types": "Ereignistypen löschen",
"pbac_desc_manage_event_types": "Alle Aktionen für Ereignistypen",
"pbac_desc_create_teams": "Teams erstellen",
"pbac_desc_view_team_details": "Teamdetails anzeigen",
"pbac_desc_update_team_settings": "Teameinstellungen aktualisieren",
"pbac_desc_delete_team": "Team löschen",
"pbac_desc_invite_team_members": "Teammitglieder einladen",
"pbac_desc_remove_team_members": "Teammitglieder entfernen",
"pbac_desc_change_team_member_role": "Rolle von Teammitgliedern ändern",
"pbac_desc_manage_teams": "Alle Aktionen für Teams",
"pbac_desc_create_organization": "Organisation erstellen",
"pbac_desc_view_organization_details": "Organisationsdetails anzeigen",
"pbac_desc_list_organization_members": "Organisationsmitglieder auflisten",
"pbac_desc_invite_organization_members": "Organisationsmitglieder einladen",
"pbac_desc_remove_organization_members": "Organisationsmitglieder entfernen",
"pbac_desc_manage_organization_billing": "Organisationsabrechnung verwalten",
"pbac_desc_change_organization_member_role": "Rolle von Organisationsmitgliedern ändern",
"pbac_desc_edit_organization_settings": "Organisationseinstellungen bearbeiten",
"pbac_desc_manage_organizations": "Alle Aktionen für Organisationen",
"pbac_desc_view_bookings": "Buchungen anzeigen",
"pbac_desc_view_team_bookings": "Team-Buchungen anzeigen",
"pbac_desc_view_organization_bookings": "Organisationsbuchungen anzeigen",
"pbac_desc_view_booking_recordings": "Buchungsaufzeichnungen anzeigen",
"pbac_desc_update_bookings": "Buchungen aktualisieren",
"pbac_desc_manage_bookings": "Alle Aktionen für Buchungen",
"pbac_desc_view_team_insights": "Team-Insights anzeigen",
"pbac_desc_manage_team_insights": "Team-Insights verwalten",
"read_permission_auto_enabled_tooltip": "Leseberechtigung wird automatisch aktiviert, wenn eine Ressource erstellt, aktualisiert oder gelöscht wird",
"choose_restriction_schedule": "Einschränkungszeitplan hinzufügen",
"choose_restriction_schedule_description": "Aktivieren Sie dies, wenn Sie die Verfügbarkeit von Hosts für diesen Ereignistyp basierend auf einem Einschränkungszeitplan begrenzen möchten. Dies entfernt nur die Zeitfenster, die nicht im Einschränkungszeitplan enthalten sind, es werden keine neuen Zeitfenster hinzugefügt.",
"use_booker_timezone": "Zeitzone des Buchenden verwenden",
"use_booker_timezone_info": "Den ausgewählten Einschränkungszeitplan in der Zeitzone des Buchenden anwenden",
"load_balancing_warning": "Dies deaktiviert die Lastverteilung für alle Ihre Round-Robin-Ereignistypen",
"rr_load_balancing_disabled": "Lastverteilung ist nur verfügbar, wenn die Zeitstempelbasis auf Buchungserstellungszeit eingestellt ist",
"round_robin_settings_description": "Passen Sie die Standard-Round-Robin-Einstellungen für dieses Team an",
"round_robin_settings_updated_successfully": "Round-Robin-Einstellungen erfolgreich aktualisiert",
"booking_creation_time": "Zeitpunkt der Buchungserstellung",
"meeting_start_time": "Beginn des Meetings",
"members_affected_by_disabling_delegation_credential": "Betroffene Mitglieder",
"no_members_affected_by_disabling_delegation_credential": "Keine Mitglieder von der Deaktivierung der Delegationsanmeldedaten betroffen",
"download_expense_log": "Ausgabenprotokoll herunterladen",
"error_downloading_expense_log": "Fehler beim Herunterladen des Ausgabenprotokolls",
"offer_to_reschedule_last_booking": "Angebot zur Umbuchung der letzten aktiven Buchung auf den gewählten Zeitraum",
"booker_limit_exceeded_error": "Maximale Anzahl aktiver Buchungen des Buchers überschritten",
"booker_limit_exceeded_error_reschedule": "Sie haben bereits eine Buchung für dieses Event am {{date}}. Möchten Sie auf die neu gewählte Zeit umbuchen?",
"duration_min_error": "Die Dauer muss mindestens {{min}} Minute(n) betragen",
"duration_max_error": "Die Dauer darf {{max}} Minuten (24 Stunden) nicht überschreiten",
"booking_not_allowed_by_restriction_schedule_error": "Buchung außerhalb der Verfügbarkeit des Einschränkungsplans.",
"restriction_schedule_not_found_error": "Einschränkungsplan nicht gefunden",
"converted_image_size_limit_exceed": "Bildgrößenlimit überschritten, bitte verwenden Sie ein kleineres Bild, vorzugsweise im JPEG-Format",
"license_key": "Lizenzschlüssel",
"license_selection_title": "Wählen Sie ihre Lizenz",
"license_selection_description": "Wählen Sie, wie Sie Ihre Cal.com-Bereitstellung lizenzieren möchten",
"agplv3_license_description": "Verwenden Sie Cal.com unter der AGPLv3 Open-Source-Lizenz",
"enter_license_key": "Lizenzschlüssel eingeben",
"enter_your_license_key": "Zugang zu allen Enterprise-Funktionen",
"enter_existing_license": "Ab 15 $/Benutzer pro Monat",
"enter_license_key_description": "Zugang zu allen Enterprise-Funktionen",
"license_key_placeholder": "Geben Sie Ihren Lizenzschlüssel ein",
"save_license_key": "Lizenzschlüssel speichern",
"signature_token_optional": "Signatur-Token (optional)",
"signature_token_description": "Das Signatur-Token wird verwendet, um zu überprüfen, ob der Lizenzschlüssel Ihnen gehört, wenn nutzungsbasierte Abrechnungen erhöht werden. Sie finden es in Ihrer Kaufbestätigungs-E-Mail. Wenn Sie eine ältere Lizenz haben, können Sie dieses Feld leer lassen.",
"need_a_license": "Benötigen Sie eine Lizenz?",
"license_key_required": "Lizenzschlüssel ist erforderlich",
"invalid_license_key": "Ungültiger Lizenzschlüssel",
"license_validation_failed": "Validierung des Lizenzschlüssels fehlgeschlagen",
"license_key_saved": "Lizenzschlüssel erfolgreich gespeichert",
"timezone_mismatch_tooltip": "Sie sehen den Bericht basierend auf Ihrer Profil-Zeitzone ({{userTimezone}}), während Ihr Browser auf die Zeitzone ({{browserTimezone}}) eingestellt ist",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Fügen Sie Ihre neuen Code-Zeilen über dieser hinzu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}

Some files were not shown because too many files have changed in this diff Show More