Compare commits

..
18 Commits
Author SHA1 Message Date
GitHub Actions 3711e01a77 chore: release v5.1.12
Post release / release (push) Canceled after 0s
2025-03-31 22:17:14 +00:00
Anik Dhabal Babu 63c9cb6b92 test: embed e2e tests for mobile viewport and hideEventTypeDetails=true (#20445)
* test: embed e2e test

* update

* tweak

* Update fixtures.ts

* update

* something new

* Update playwright.config.ts
2025-03-31 23:05:53 +01:00
Eunjae Lee f039e116d7 fix: eventTypeId filter on Download at /insights (#20463)
* fix: eventTypeId filter on Download at /insights

* show toast for error
2025-03-31 22:00:15 +00:00
Joe Au-Yeung 93621baea5 Add users to event types when provisioned (#20465) 2025-03-31 21:49:08 +00:00
Tushar BhattandEunjae Lee eaa17fa48d fix: booking filtering queryParams (#20447)
* fix: bookng-filtering-queryParams

* adding comments

* feedback changes

* fix: typo

* minor-change

* revert Pagination.tsx

* minor-change

* minor-changes

* Update packages/features/data-table/DataTableProvider.tsx

* fix: type-check

* fix: test

---------

Co-authored-by: Eunjae Lee <hey@eunjae.dev>
2025-03-31 17:19:28 +01:00
Tushar Bhatt 9ce94fbbfc fix:theme-flakiness (#20460) 2025-03-31 21:18:06 +05:30
Lauris Skraucis 69548e6ed0 docs: v2 authorization headers (#20453)
* refactor: client secret header for oauth users and webhooks

* fix: platform introduction link to api reference

* regenerate docs for commits above

* refactor: specify request headers

* refactor: specify request headers
2025-03-31 14:28:05 +03:00
sean-brydon 5cd2ba1362 perf: move stripe to billing service out of app store (#20376)
* add more methods to billingService

* update profile handler

* stripe customer handler

* verify email

* move imports + move generic methods to billing service

* move to strip ebilling service class

* push changes to mocks

* fix enum

* fix typo

* correctly update customer metadata

* fix userId type to number

* fix types on billing services

* fix type errors due to return methods differing

* fix return types
2025-03-31 09:20:27 +00:00
Eunjae Lee f15d347804 feat: implement filter segment (#20066)
* feat: add filter segment

* add tests

* add ui

* provide tableIdentifier from context

* fix components

* update save button

* try to fix router

* add proper implementation

* update icon

* remove segmentId if invalid

* fix tests

* fix type errors

* fix type errors

* fix unit test

* clean up

* minor fixes

* clean up types

* avoid barrel imports

* fix imports

* minor improvements

* fix type error

* fix radio import

* fix little issues

* menu permission

* update icon snapshot

* fix: remember last used filter segment (#20261)

* fix: remember last used filter segment

* extract segments logic as a separate file

* remove unnecessary query

* apply feedback

* deep comparison
2025-03-31 08:53:48 +00:00
Morgan c95118715a chore: don't log the whole delegated credential (#20452) 2025-03-31 08:25:35 +00:00
Benny Joo 21e7ac3cbe fix: middleware config matcher (#20450)
* fix: middleware config matcher - ending slash

* improve comment
2025-03-30 23:02:20 -03:00
Amit SharmaandKeith Williams 81de95bd28 feat: allow plain ask a question for paid users only (#20390)
Co-authored-by: Keith Williams <keithwillcode@gmail.com>
2025-03-30 04:17:12 +00:00
Omar López d80ed07093 fix: calendar cache date mismtach (#20446)
* fix: calendar cache date mismtach

* Update tests
2025-03-29 17:00:26 -07:00
Anik Dhabal Babu b2ac86c759 fix: send email to is not being displayed in routing forms (#20444)
* feat: ability to set locked timezone

* fix: send email to is not being displayed in routing forms

* update

* Update EventAdvancedTab.tsx
2025-03-29 14:49:05 +00:00
Vachmara 295cf40c6e fix(intercom app): team / org url validation (#20431) 2025-03-29 10:56:07 +05:30
Omar López 92827b2c9d fix: atom builds (#20437) 2025-03-28 23:18:56 +00:00
Omar López e1076690c1 chore: bump platform (#20436) 2025-03-28 21:10:46 +00:00
Calcom BotandLingo.dev 70dd9b9c2c feat: update translations via @lingodotdev (#20432)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-03-28 20:20:38 +00:00
162 changed files with 8832 additions and 2066 deletions
+2
View File
@@ -0,0 +1,2 @@
undecided:
- "@calcom/prisma"
+1 -1
View File
@@ -1,8 +1,8 @@
import type { NextApiRequest } from "next";
import { getStripeCustomerIdFromUserId } from "@calcom/app-store/stripepayment/lib/customer";
import stripe from "@calcom/app-store/stripepayment/lib/server";
import { getDubCustomer } from "@calcom/features/auth/lib/dub";
import stripe from "@calcom/features/ee/payments/server/stripe";
import { IS_PRODUCTION } from "@calcom/lib/constants";
import { IS_TEAM_BILLING_ENABLED, WEBAPP_URL } from "@calcom/lib/constants";
import { HttpError } from "@calcom/lib/http-error";
+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.156",
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.160",
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
"@calcom/platform-types": "*",
"@calcom/platform-utils": "*",
@@ -7,6 +7,7 @@ import { ReassignBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outpu
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service";
import { VERSION_2024_08_13_VALUE, VERSION_2024_08_13 } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -155,12 +156,7 @@ export class BookingsController_2024_08_13 {
@Get("/")
@UseGuards(ApiAuthGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Permissions([BOOKING_READ])
@ApiOperation({ summary: "Get all bookings" })
async getBookings(
@@ -245,13 +241,10 @@ export class BookingsController_2024_08_13 {
@HttpCode(HttpStatus.OK)
@Permissions([BOOKING_WRITE])
@UseGuards(ApiAuthGuard, BookingUidGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary: "Mark a booking absence - provided authorization header refers to owner of the booking.",
})
@ApiOperation({ summary: "Mark a booking absence" })
async markNoShow(
@Param("bookingUid") bookingUid: string,
@Body() body: MarkAbsentBookingInput_2024_08_13,
@@ -269,13 +262,11 @@ export class BookingsController_2024_08_13 {
@HttpCode(HttpStatus.OK)
@Permissions([BOOKING_WRITE])
@UseGuards(ApiAuthGuard, BookingUidGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary:
"Automatically reassign booking to a new host automatically - provided authorization header refers to person who reassigned the booking.",
})
@ApiOperation({ summary: "Automatically reassign booking to a new host" })
async reassignBooking(
@Param("bookingUid") bookingUid: string,
@GetUser() user: UserWithProfile
@@ -292,13 +283,11 @@ export class BookingsController_2024_08_13 {
@HttpCode(HttpStatus.OK)
@Permissions([BOOKING_WRITE])
@UseGuards(ApiAuthGuard, BookingUidGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary:
"Reassign a booking to a specific user specified by the :userId - provided authorization header refers to person who reassigned the booking.",
})
@ApiOperation({ summary: "Reassign a booking to a specific user" })
async reassignBookingToUser(
@Param("bookingUid") bookingUid: string,
@Param("userId") userId: number,
@@ -322,13 +311,11 @@ export class BookingsController_2024_08_13 {
@HttpCode(HttpStatus.OK)
@Permissions([BOOKING_WRITE])
@UseGuards(ApiAuthGuard, BookingUidGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary:
"Confirm booking that requires a confirmation - provided authorization header refers to owner of the booking.",
})
@ApiOperation({ summary: "Confirm booking that requires a confirmation" })
async confirmBooking(
@Param("bookingUid") bookingUid: string,
@GetUser() user: UserWithProfile
@@ -345,13 +332,11 @@ export class BookingsController_2024_08_13 {
@HttpCode(HttpStatus.OK)
@Permissions([BOOKING_WRITE])
@UseGuards(ApiAuthGuard, BookingUidGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary:
"Decline booking that requires a confirmation - provided authorization header refers to owner of the booking.",
})
@ApiOperation({ summary: "Decline booking that requires a confirmation" })
async declineBooking(
@Param("bookingUid") bookingUid: string,
@Body() body: DeclineBookingInput_2024_08_13,
@@ -368,6 +353,7 @@ export class BookingsController_2024_08_13 {
@Get("/:bookingUid/calendar-links")
@UseGuards(ApiAuthGuard, BookingUidGuard)
@Permissions([BOOKING_READ])
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({
summary: "Get 'Add to Calendar' links for a booking",
description:
@@ -14,6 +14,7 @@ import { GoogleCalendarService } from "@/ee/calendars/services/gcal.service";
import { IcsFeedService } from "@/ee/calendars/services/ics-feed.service";
import { OutlookService } from "@/ee/calendars/services/outlook.service";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -34,7 +35,7 @@ import {
Post,
Body,
} from "@nestjs/common";
import { ApiOperation, ApiParam, ApiQuery, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiParam, ApiQuery, ApiTags as DocsTags } from "@nestjs/swagger";
import { User } from "@prisma/client";
import { plainToClass } from "class-transformer";
import { Request } from "express";
@@ -68,6 +69,7 @@ export class CalendarsController {
@Post("/ics-feed/save")
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Save an ICS feed" })
async createIcsFeed(
@GetUser("id") userId: number,
@@ -79,12 +81,14 @@ export class CalendarsController {
@Get("/ics-feed/check")
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Check an ICS feed" })
async checkIcsFeed(@GetUser("id") userId: number): Promise<ApiResponse> {
return await this.icsFeedService.check(userId);
}
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/busy-times")
@ApiOperation({
summary: "Get busy times",
@@ -113,6 +117,7 @@ export class CalendarsController {
@Get("/")
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Get all calendars" })
async getCalendars(@GetUser("id") userId: number): Promise<ConnectedCalendarsOutput> {
const calendars = await this.calendarsService.getCalendars(userId);
@@ -129,6 +134,7 @@ export class CalendarsController {
name: "calendar",
})
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Get("/:calendar/connect")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Get oAuth connect URL" })
@@ -199,6 +205,7 @@ export class CalendarsController {
name: "calendar",
})
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Post("/:calendar/credentials")
@ApiOperation({ summary: "Sync credentials" })
async syncCredentials(
@@ -227,6 +234,7 @@ export class CalendarsController {
@Get("/:calendar/check")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard, PermissionsGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Permissions([APPS_READ])
@ApiOperation({ summary: "Check a calendar connection" })
async check(@GetUser("id") userId: number, @Param("calendar") calendar: string): Promise<ApiResponse> {
@@ -251,6 +259,7 @@ export class CalendarsController {
name: "calendar",
})
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@Post("/:calendar/disconnect")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Disconnect a calendar" })
@@ -7,6 +7,7 @@ import { EventTypeResponseTransformPipe } from "@/ee/event-types/event-types_202
import { EventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/event-types.service";
import { InputEventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/input-event-types.service";
import { VERSION_2024_06_14_VALUE } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -67,12 +68,7 @@ export class EventTypesController_2024_06_14 {
@Post("/")
@Permissions([EVENT_TYPE_WRITE])
@UseGuards(ApiAuthGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Create an event type" })
async createEventType(
@Body() body: CreateEventTypeInput_2024_06_14,
@@ -94,12 +90,7 @@ export class EventTypesController_2024_06_14 {
@Get("/:eventTypeId")
@Permissions([EVENT_TYPE_READ])
@UseGuards(ApiAuthGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Get an event type" })
async getEventTypeById(
@Param("eventTypeId") eventTypeId: string,
@@ -149,12 +140,7 @@ export class EventTypesController_2024_06_14 {
@Patch("/:eventTypeId")
@Permissions([EVENT_TYPE_WRITE])
@UseGuards(ApiAuthGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Update an event type" })
async updateEventType(
@@ -179,12 +165,7 @@ export class EventTypesController_2024_06_14 {
@Delete("/:eventTypeId")
@Permissions([EVENT_TYPE_WRITE])
@UseGuards(ApiAuthGuard)
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Delete an event type" })
async deleteEventType(
@Param("eventTypeId") eventTypeId: number,
+3 -1
View File
@@ -2,6 +2,7 @@ import { GetMeOutput } from "@/ee/me/outputs/get-me.output";
import { UpdateMeOutput } from "@/ee/me/outputs/update-me.output";
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -10,7 +11,7 @@ import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-us
import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile, UsersRepository } from "@/modules/users/users.repository";
import { Controller, UseGuards, Get, Patch, Body } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { PROFILE_READ, PROFILE_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
import { userSchemaResponse } from "@calcom/platform-types";
@@ -21,6 +22,7 @@ import { userSchemaResponse } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, PermissionsGuard)
@DocsTags("Me")
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
export class MeController {
constructor(
private readonly usersRepository: UsersRepository,
@@ -1,6 +1,7 @@
import { ProviderVerifyAccessTokenOutput } from "@/ee/provider/outputs/verify-access-token.output";
import { ProviderVerifyClientOutput } from "@/ee/provider/outputs/verify-client.output";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
@@ -16,7 +17,7 @@ import {
UnauthorizedException,
UseGuards,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiExcludeController, ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -24,6 +25,9 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants";
path: "/v2/provider",
version: API_VERSIONS_VALUES,
})
// note(Lauris): this controller is used by useOAuthClient and useOAuthFlow internal hooks and if customer
// wants to know if access token is expired then the flow is to make request and then receive message it expired.
@ApiExcludeController()
@DocsTags("Platform / Cal Provider")
export class CalProviderController {
constructor(private readonly oauthClientRepository: OAuthClientRepository) {}
@@ -53,6 +57,7 @@ export class CalProviderController {
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiOperation({ summary: "Verify an access token" })
@ApiHeader(ACCESS_TOKEN_HEADER)
async verifyAccessToken(
@Param("clientId") clientId: string,
@GetUser() user: UserWithProfile
@@ -1,5 +1,6 @@
import { SchedulesService_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/services/schedules.service";
import { VERSION_2024_06_14, VERSION_2024_06_11 } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Permissions } from "@/modules/auth/decorators/permissions/permissions.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -46,12 +47,7 @@ import {
default: VERSION_2024_06_11,
},
})
@ApiHeader({
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` either managed user access token or api key prefixed with cal_",
required: true,
})
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
export class SchedulesController_2024_06_11 {
constructor(private readonly schedulesService: SchedulesService_2024_06_11) {}
+60
View File
@@ -0,0 +1,60 @@
import { ApiHeaderOptions } from "@nestjs/swagger";
import { X_CAL_CLIENT_ID, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
export const OPTIONAL_X_CAL_CLIENT_ID_HEADER: ApiHeaderOptions = {
name: X_CAL_CLIENT_ID,
description: "For platform customers - OAuth client ID",
required: false,
};
export const OPTIONAL_X_CAL_SECRET_KEY_HEADER: ApiHeaderOptions = {
name: X_CAL_SECRET_KEY,
description: "For platform customers - OAuth client secret key",
required: false,
};
export const X_CAL_CLIENT_ID_HEADER: ApiHeaderOptions = {
name: X_CAL_CLIENT_ID,
description: "For platform customers - OAuth client ID",
required: false,
};
export const X_CAL_SECRET_KEY_HEADER: ApiHeaderOptions = {
name: X_CAL_SECRET_KEY,
description: "For platform customers - OAuth client secret key",
required: false,
};
export const OPTIONAL_API_KEY_HEADER: ApiHeaderOptions = {
name: "Authorization",
description:
"For non-platform customers - value must be `Bearer <token>` where `<token>` is api key prefixed with cal_",
required: false,
};
export const API_KEY_HEADER: ApiHeaderOptions = {
name: "Authorization",
description: "value must be `Bearer <token>` where `<token>` is api key prefixed with cal_",
required: true,
};
export const API_KEY_OR_ACCESS_TOKEN_HEADER: ApiHeaderOptions = {
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` is api key prefixed with cal_ or managed user access token",
required: true,
};
export const OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER: ApiHeaderOptions = {
name: "Authorization",
description:
"value must be `Bearer <token>` where `<token>` is api key prefixed with cal_ or managed user access token",
required: false,
};
export const ACCESS_TOKEN_HEADER: ApiHeaderOptions = {
name: "Authorization",
description: "value must be `Bearer <token>` where `<token>` is managed user access token",
required: true,
};
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { RefreshApiKeyInput } from "@/modules/api-keys/inputs/refresh-api-key.input";
import { RefreshApiKeyOutput } from "@/modules/api-keys/outputs/refresh-api-key.output";
import { ApiKeysService } from "@/modules/api-keys/services/api-keys.service";
@@ -23,12 +24,7 @@ export class ApiKeysController {
summary: "Refresh API Key",
description: `Generate a new API key and delete the current one. Provide API key to refresh as a Bearer token in the Authorization header (e.g. "Authorization: Bearer <apiKey>").`,
})
@ApiHeader({
name: "Authorization",
description: "Bearer <apiKey>",
required: true,
schema: { type: "string" },
})
@ApiHeader(API_KEY_HEADER)
@Post("/refresh")
@HttpCode(HttpStatus.OK)
async refresh(
@@ -9,8 +9,7 @@ import { UsersService } from "@/modules/users/services/users.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Injectable, NotFoundException, ForbiddenException } from "@nestjs/common";
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
import { getClientSecretFromPayment } from "@calcom/platform-libraries";
import { checkAdminOrOwner, getClientSecretFromPayment } from "@calcom/platform-libraries";
import type { TeamQuery } from "@calcom/platform-libraries";
import { enrichUserWithDelegationConferencingCredentialsWithoutOrgId } from "@calcom/platform-libraries/app-store";
import { getEnabledAppsFromCredentials, getAppFromSlug } from "@calcom/platform-libraries/app-store";
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import {
@@ -37,7 +38,7 @@ import {
Req,
HttpException,
} from "@nestjs/common";
import { ApiOperation, ApiParam, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiParam, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToInstance } from "class-transformer";
import { Request } from "express";
@@ -70,6 +71,7 @@ export class ConferencingController {
@Post("/:app/connect")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Connect your conferencing application" })
@ApiParam({
name: "app",
@@ -98,6 +100,7 @@ export class ConferencingController {
@Get("/:app/oauth/auth-url")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Get OAuth conferencing app auth url" })
@ApiParam({
name: "app",
@@ -204,6 +207,7 @@ export class ConferencingController {
@Get("/")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "List your conferencing applications" })
async listInstalledConferencingApps(
@GetUser("id") userId: number
@@ -220,6 +224,7 @@ export class ConferencingController {
@Post("/:app/default")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Set your default conferencing application" })
@ApiParam({
name: "app",
@@ -238,6 +243,7 @@ export class ConferencingController {
@Get("/default")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Get your default conferencing application" })
async getDefault(@GetUser("id") userId: number): Promise<GetDefaultConferencingAppOutputResponseDto> {
const defaultconferencingApp = await this.conferencingService.getUserDefaultConferencingApp(userId);
@@ -247,6 +253,7 @@ export class ConferencingController {
@Delete("/:app/disconnect")
@HttpCode(HttpStatus.OK)
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Disconnect your conferencing application" })
@ApiParam({
name: "app",
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { DestinationCalendarsInputBodyDto } from "@/modules/destination-calendars/inputs/destination-calendars.input";
@@ -9,7 +10,7 @@ import {
import { DestinationCalendarsService } from "@/modules/destination-calendars/services/destination-calendars.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Body, Controller, Put, UseGuards } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -19,11 +20,12 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants";
version: API_VERSIONS_VALUES,
})
@DocsTags("Destination Calendars")
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@UseGuards(ApiAuthGuard)
export class DestinationCalendarsController {
constructor(private readonly destinationCalendarsService: DestinationCalendarsService) {}
@Put("/")
@UseGuards(ApiAuthGuard)
@ApiOperation({ summary: "Update destination calendars" })
async updateDestinationCalendars(
@Body() input: DestinationCalendarsInputBodyDto,
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { GetWebhook } from "@/modules/webhooks/decorators/get-webhook-decorator";
import { IsUserEventTypeWebhookGuard } from "@/modules/webhooks/guards/is-user-event-type-webhook-guard";
@@ -25,7 +26,7 @@ import {
Patch,
ParseIntPipe,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { Webhook } from "@prisma/client";
import { plainToClass } from "class-transformer";
@@ -38,6 +39,7 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, IsUserEventTypeWebhookGuard)
@DocsTags("Event Types / Webhooks")
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
export class EventTypeWebhooksController {
constructor(
private readonly webhooksService: WebhooksService,
@@ -31,11 +31,10 @@ import {
Query,
NotFoundException,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiOperation, ApiTags as DocsTags, ApiHeader } from "@nestjs/swagger";
import { User, MembershipRole } from "@prisma/client";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { Pagination } from "@calcom/platform-types";
import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
@Controller({
path: "/v2/oauth-clients/:clientId/users",
@@ -43,6 +42,11 @@ import { Pagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, OAuthClientGuard, OrganizationRolesGuard)
@DocsTags("Platform / Managed Users")
@ApiHeader({
name: X_CAL_SECRET_KEY,
description: "OAuth client secret key",
required: true,
})
export class OAuthClientUsersController {
private readonly logger = new Logger("UserController");
@@ -16,11 +16,11 @@ import { WebhookOutputPipe } from "@/modules/webhooks/pipes/WebhookOutputPipe";
import { OAuthClientWebhooksService } from "@/modules/webhooks/services/oauth-clients-webhooks.service";
import { WebhooksService } from "@/modules/webhooks/services/webhooks.service";
import { Controller, Post, Body, UseGuards, Get, Param, Query, Delete, Patch } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { Webhook, MembershipRole } from "@prisma/client";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { SUCCESS_STATUS, X_CAL_SECRET_KEY } from "@calcom/platform-constants";
import { SkipTakePagination } from "@calcom/platform-types";
import { OAuthClientGuard } from "../../guards/oauth-client-guard";
@@ -31,6 +31,11 @@ import { OAuthClientGuard } from "../../guards/oauth-client-guard";
})
@UseGuards(NextAuthGuard, OrganizationRolesGuard, OAuthClientGuard)
@DocsTags("Platform / Webhooks")
@ApiHeader({
name: X_CAL_SECRET_KEY,
description: "OAuth client secret key",
required: true,
})
export class OAuthClientWebhooksController {
constructor(
private readonly webhooksService: WebhooksService,
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { GetOrgId } from "@/modules/auth/decorators/get-org-id/get-org-id.decorator";
import { MembershipRoles } from "@/modules/auth/decorators/roles/membership-roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -9,7 +10,6 @@ import { CreateOAuthClientResponseDto } from "@/modules/oauth-clients/controller
import { GetOAuthClientResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientResponse.dto";
import { GetOAuthClientsResponseDto } from "@/modules/oauth-clients/controllers/oauth-clients/responses/GetOAuthClientsResponse.dto";
import { OAuthClientGuard } from "@/modules/oauth-clients/guards/oauth-client-guard";
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
import { OAuthClientsService } from "@/modules/oauth-clients/services/oauth-clients/oauth-clients.service";
import { OrganizationsRepository } from "@/modules/organizations/index/organizations.repository";
import { UsersRepository } from "@/modules/users/users.repository";
@@ -26,10 +26,15 @@ import {
HttpStatus,
Logger,
UseGuards,
NotFoundException,
BadRequestException,
} from "@nestjs/common";
import { ApiCreatedResponse as DocsCreatedResponse, ApiTags } from "@nestjs/swagger";
import {
ApiCreatedResponse as DocsCreatedResponse,
ApiTags,
ApiOperation,
ApiExcludeEndpoint,
ApiHeader,
} from "@nestjs/swagger";
import { User, MembershipRole } from "@prisma/client";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -41,11 +46,11 @@ import { CreateOAuthClientInput, UpdateOAuthClientInput, Pagination } from "@cal
})
@ApiTags("OAuth Clients")
@UseGuards(ApiAuthGuard, OrganizationRolesGuard)
@ApiHeader(API_KEY_HEADER)
export class OAuthClientsController {
private readonly logger = new Logger("OAuthClientController");
constructor(
private readonly oauthClientRepository: OAuthClientRepository,
private readonly oAuthClientsService: OAuthClientsService,
private readonly userRepository: UsersRepository,
private readonly teamsRepository: OrganizationsRepository
@@ -58,6 +63,7 @@ export class OAuthClientsController {
description: "Create an OAuth client",
type: CreateOAuthClientResponseDto,
})
@ApiOperation({ summary: "Create an OAuth client" })
async createOAuthClient(
@GetOrgId() organizationId: number,
@Body() body: CreateOAuthClientInput
@@ -68,7 +74,9 @@ export class OAuthClientsController {
const organization = await this.teamsRepository.findByIdIncludeBilling(organizationId);
if (!organization?.platformBilling || !organization?.platformBilling?.subscriptionId) {
throw new BadRequestException("Team is not subscribed, cannot create an OAuth Client.");
throw new BadRequestException(
"Team is not subscribed to platform plan, cannot create an OAuth Client."
);
}
const oAuthClientCredentials = await this.oAuthClientsService.createOAuthClient(organizationId, body);
@@ -81,6 +89,7 @@ export class OAuthClientsController {
@Get("/")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Get all OAuth clients" })
@MembershipRoles([MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER])
async getOAuthClients(@GetOrgId() organizationId: number): Promise<GetOAuthClientsResponseDto> {
const clients = await this.oAuthClientsService.getOAuthClients(organizationId);
@@ -91,6 +100,7 @@ export class OAuthClientsController {
@HttpCode(HttpStatus.OK)
@MembershipRoles([MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER])
@UseGuards(OAuthClientGuard)
@ApiOperation({ summary: "Get an OAuth client" })
async getOAuthClientById(@Param("clientId") clientId: string): Promise<GetOAuthClientResponseDto> {
const client = await this.oAuthClientsService.getOAuthClientById(clientId);
return { status: SUCCESS_STATUS, data: client };
@@ -100,6 +110,9 @@ export class OAuthClientsController {
@HttpCode(HttpStatus.OK)
@MembershipRoles([MembershipRole.ADMIN, MembershipRole.OWNER, MembershipRole.MEMBER])
@UseGuards(OAuthClientGuard)
// note(Lauris): Excluding this endpoint because we have oauth-clients-users.controller to fetch oAuth users and I am leaving this endpoint
// just in case someone is using it.
@ApiExcludeEndpoint()
async getOAuthClientManagedUsersById(
@Param("clientId") clientId: string,
@Query() queryParams: Pagination
@@ -118,6 +131,7 @@ export class OAuthClientsController {
@HttpCode(HttpStatus.OK)
@MembershipRoles([MembershipRole.ADMIN, MembershipRole.OWNER])
@UseGuards(OAuthClientGuard)
@ApiOperation({ summary: "Update an OAuth client" })
async updateOAuthClient(
@Param("clientId") clientId: string,
@Body() body: UpdateOAuthClientInput
@@ -131,6 +145,7 @@ export class OAuthClientsController {
@HttpCode(HttpStatus.OK)
@MembershipRoles([MembershipRole.ADMIN, MembershipRole.OWNER])
@UseGuards(OAuthClientGuard)
@ApiOperation({ summary: "Delete an OAuth client" })
async deleteOAuthClient(@Param("clientId") clientId: string): Promise<GetOAuthClientResponseDto> {
this.logger.log(`Deleting OAuth Client with ID: ${clientId}`);
const client = await this.oAuthClientsService.deleteOAuthClient(clientId);
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -28,7 +29,7 @@ import {
Query,
UseGuards,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { SkipTakePagination } from "@calcom/platform-types";
@@ -39,6 +40,7 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Attributes")
@ApiHeader(API_KEY_HEADER)
export class OrganizationsAttributesController {
constructor(private readonly organizationsAttributesService: OrganizationAttributesService) {}
// Gets all attributes for an organization
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -20,7 +21,7 @@ import { GetAllAttributeOptionOutput } from "@/modules/organizations/attributes/
import { UpdateAttributeOptionOutput } from "@/modules/organizations/attributes/options/outputs/update-option.output";
import { OrganizationAttributeOptionService } from "@/modules/organizations/attributes/options/services/organization-attributes-option.service";
import { Body, Controller, Delete, Get, Param, ParseIntPipe, Patch, Post, UseGuards } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -30,6 +31,7 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Attributes / Options")
@ApiHeader(API_KEY_HEADER)
export class OrganizationsAttributesOptionsController {
constructor(private readonly organizationsAttributesOptionsService: OrganizationAttributeOptionService) {}
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
OPTIONAL_API_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
@@ -23,7 +28,7 @@ import {
HttpStatus,
Patch,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { User } from "@prisma/client";
import { plainToClass } from "class-transformer";
@@ -37,6 +42,9 @@ import { CreateDelegationCredentialInput } from "./inputs/create-delegation-cred
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Delegation Credentials")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsDelegationCredentialController {
constructor(private readonly delegationCredentialService: OrganizationsDelegationCredentialService) {}
@@ -1,6 +1,11 @@
import { CreatePhoneCallInput } from "@/ee/event-types/event-types_2024_06_14/inputs/create-phone-call.input";
import { CreatePhoneCallOutput } from "@/ee/event-types/event-types_2024_06_14/outputs/create-phone-call.output";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
@@ -35,7 +40,7 @@ import {
NotFoundException,
Query,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
import { handleCreatePhoneCall } from "@calcom/platform-libraries";
@@ -56,7 +61,10 @@ export type EventTypeHandlerResponse = {
path: "/v2/organizations/:orgId",
version: API_VERSIONS_VALUES,
})
@DocsTags("Orgs / Event Types")
@DocsTags("Orgs / Teams / Event Types")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsEventTypesController {
constructor(
private readonly organizationsEventTypesService: OrganizationsEventTypesService,
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -30,7 +35,7 @@ import {
HttpCode,
HttpStatus,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -42,6 +47,9 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Memberships")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsMembershipsController {
constructor(private organizationsMembershipService: OrganizationsMembershipService) {}
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { X_CAL_CLIENT_ID_HEADER, X_CAL_SECRET_KEY_HEADER } from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
@@ -28,7 +29,7 @@ import {
HttpStatus,
Delete,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -40,6 +41,8 @@ const SCALE = "SCALE";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Orgs")
@ApiHeader(X_CAL_CLIENT_ID_HEADER)
@ApiHeader(X_CAL_SECRET_KEY_HEADER)
export class OrganizationsOrganizationsController {
constructor(private readonly managedOrganizationsService: ManagedOrganizationsService) {}
@@ -1,5 +1,10 @@
import { SchedulesService_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/services/schedules.service";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -23,7 +28,7 @@ import {
HttpStatus,
Query,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import {
@@ -42,7 +47,9 @@ import { SkipTakePagination } from "@calcom/platform-types";
version: API_VERSIONS_VALUES,
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Schedules")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsSchedulesController {
constructor(
private schedulesService: SchedulesService_2024_06_11,
@@ -52,6 +59,7 @@ export class OrganizationsSchedulesController {
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@Get("/schedules")
@DocsTags("Orgs / Schedules")
@ApiOperation({ summary: "Get all schedules" })
async getOrganizationSchedules(
@Param("orgId", ParseIntPipe) orgId: number,
@@ -1,6 +1,11 @@
import { BookingUidGuard } from "@/ee/bookings/2024-08-13/guards/booking-uid.guard";
import { BookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/services/bookings.service";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
OPTIONAL_API_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
@@ -13,7 +18,7 @@ import { IsTeamInOrg } from "@/modules/auth/guards/teams/is-team-in-org.guard";
import { GetOrganizationsTeamsBookingsInput_2024_08_13 } from "@/modules/organizations/teams/bookings/inputs/get-organizations-teams-bookings.input";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Controller, UseGuards, Get, Param, ParseIntPipe, Query, HttpStatus, HttpCode } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { GetBookingsOutput_2024_08_13 } from "@calcom/platform-types";
@@ -24,6 +29,9 @@ import { GetBookingsOutput_2024_08_13 } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, IsTeamInOrg, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Teams / Bookings")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsTeamsBookingsController {
constructor(private readonly bookingsService: BookingsService_2024_08_13) {}
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetTeam } from "@/modules/auth/decorators/get-team/get-team.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
@@ -30,10 +35,11 @@ import {
Patch,
Post,
Body,
Headers,
Req,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { Request } from "express";
import { SUCCESS_STATUS, X_CAL_CLIENT_ID } from "@calcom/platform-constants";
import { OrgTeamOutputDto } from "@calcom/platform-types";
@@ -46,11 +52,13 @@ import { Team } from "@calcom/prisma/client";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Teams")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsTeamsController {
constructor(private organizationsTeamsService: OrganizationsTeamsService) {}
@Get()
@DocsTags("Teams")
@ApiOperation({ summary: "Get all teams" })
@Roles("ORG_ADMIN")
@PlatformPlan("ESSENTIALS")
@@ -148,8 +156,9 @@ export class OrganizationsTeamsController {
@Param("orgId", ParseIntPipe) orgId: number,
@Body() body: CreateOrgTeamDto,
@GetUser() user: UserWithProfile,
@Headers(X_CAL_CLIENT_ID) oAuthClientId?: string
@Req() req: Request
): Promise<OrgTeamOutputResponseDto> {
const oAuthClientId = req.headers[X_CAL_CLIENT_ID] as string | undefined;
const team = oAuthClientId
? await this.organizationsTeamsService.createPlatformOrgTeam(orgId, oAuthClientId, body, user)
: await this.organizationsTeamsService.createOrgTeam(orgId, body, user);
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -33,7 +38,7 @@ import {
UnprocessableEntityException,
Logger,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -46,6 +51,9 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, IsTeamInOrg, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Teams / Memberships")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsTeamsMembershipsController {
private logger = new Logger("OrganizationsTeamsMembershipsController");
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -8,11 +9,9 @@ import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
import { IsRoutingFormInTeam } from "@/modules/auth/guards/routing-forms/is-routing-form-in-team.guard";
import { IsTeamInOrg } from "@/modules/auth/guards/teams/is-team-in-org.guard";
import { Controller, Get, Param, UseGuards } from "@nestjs/common";
import { ApiOperation, ApiTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { ApiHeader, ApiOperation, ApiTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { RoutingFormResponseOutput } from "@calcom/platform-types";
import { RoutingFormsResponsesService } from "../../../../routing-forms-responses/services/routing-forms-responses.service";
import { GetRoutingFormResponsesOutput } from "../outputs/get-routing-form-responses.output";
@@ -30,6 +29,7 @@ import { GetRoutingFormResponsesOutput } from "../outputs/get-routing-form-respo
PlatformPlanGuard,
IsAdminAPIEnabledGuard
)
@ApiHeader(API_KEY_HEADER)
export class OrganizationsTeamsRoutingFormsResponsesController {
constructor(private readonly routingFormsResponsesService: RoutingFormsResponsesService) {}
@@ -1,5 +1,10 @@
import { SchedulesService_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/services/schedules.service";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -10,7 +15,7 @@ import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
import { IsTeamInOrg } from "@/modules/auth/guards/teams/is-team-in-org.guard";
import { IsUserInOrgTeam } from "@/modules/auth/guards/users/is-user-in-org-team.guard";
import { Controller, UseGuards, Get, Param, ParseIntPipe } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { GetSchedulesOutput_2024_06_11 } from "@calcom/platform-types";
@@ -20,7 +25,9 @@ import { GetSchedulesOutput_2024_06_11 } from "@calcom/platform-types";
version: API_VERSIONS_VALUES,
})
@UseGuards(ApiAuthGuard, IsOrgGuard, IsTeamInOrg, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Teams / Schedules")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsTeamsSchedulesController {
constructor(private schedulesService: SchedulesService_2024_06_11) {}
@@ -29,7 +36,9 @@ export class OrganizationsTeamsSchedulesController {
@UseGuards(IsUserInOrgTeam)
@Get("/users/:userId/schedules")
@DocsTags("Orgs / Teams / Users / Schedules")
@ApiOperation({ summary: "Get schedules of a team member" })
@ApiOperation({
summary: "Get schedules of a team member",
})
async getUserSchedules(
@Param("userId", ParseIntPipe) userId: number
): Promise<GetSchedulesOutput_2024_06_11> {
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetOrg } from "@/modules/auth/decorators/get-org/get-org.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
@@ -33,7 +38,7 @@ import {
Query,
} from "@nestjs/common";
import { ClassSerializerInterceptor } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToInstance } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -47,6 +52,9 @@ import { Team } from "@calcom/prisma/client";
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@UseGuards(IsOrgGuard)
@DocsTags("Orgs / Users")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsUsersController {
constructor(private readonly organizationsUsersService: OrganizationsUsersService) {}
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -35,7 +40,7 @@ import {
Query,
} from "@nestjs/common";
import { ClassSerializerInterceptor } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToInstance } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -48,6 +53,9 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants";
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@UseGuards(IsOrgGuard)
@DocsTags("Orgs / Users / OOO")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsUsersOOOController {
constructor(
private readonly userOOOService: UserOOOService,
@@ -1,4 +1,9 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import {
OPTIONAL_API_KEY_HEADER,
OPTIONAL_X_CAL_CLIENT_ID_HEADER,
OPTIONAL_X_CAL_SECRET_KEY_HEADER,
} from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -32,7 +37,7 @@ import {
HttpCode,
HttpStatus,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -44,6 +49,9 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, IsOrgGuard, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
@DocsTags("Orgs / Webhooks")
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_SECRET_KEY_HEADER)
@ApiHeader(OPTIONAL_API_KEY_HEADER)
export class OrganizationsWebhooksController {
constructor(
private organizationsWebhooksService: OrganizationsWebhooksService,
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import {
@@ -12,7 +13,7 @@ import {
import { SelectedCalendarsService } from "@/modules/selected-calendars/services/selected-calendars.service";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Body, Controller, Post, UseGuards, Delete, Query } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -22,11 +23,12 @@ import { SUCCESS_STATUS } from "@calcom/platform-constants";
version: API_VERSIONS_VALUES,
})
@DocsTags("Selected Calendars")
@UseGuards(ApiAuthGuard)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
export class SelectedCalendarsController {
constructor(private readonly selectedCalendarsService: SelectedCalendarsService) {}
@Post("/")
@UseGuards(ApiAuthGuard)
@ApiOperation({ summary: "Add a selected calendar" })
async addSelectedCalendar(
@Body() input: SelectedCalendarsInputDto,
@@ -41,7 +43,6 @@ export class SelectedCalendarsController {
}
@Delete("/")
@UseGuards(ApiAuthGuard)
@ApiOperation({ summary: "Delete a selected calendar" })
async deleteSelectedCalendar(
@Query() queryParams: SelectedCalendarsQueryParamsInputDto,
@@ -1,4 +1,5 @@
import { VERSION_2024_09_04 } from "@/lib/api-versions";
import { OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER, OPTIONAL_X_CAL_CLIENT_ID_HEADER } from "@/lib/docs/headers";
import { GetOptionalUser } from "@/modules/auth/decorators/get-optional-user/get-optional-user.decorator";
import { OptionalApiAuthGuard } from "@/modules/auth/guards/optional-api-auth/optional-api-auth.guard";
import { GetReservedSlotOutput_2024_09_04 } from "@/modules/slots/slots-2024-09-04/outputs/get-reserved-slot.output";
@@ -13,12 +14,10 @@ import {
Delete,
Post,
Param,
Res,
HttpCode,
HttpStatus,
Patch,
UseGuards,
BadRequestException,
} from "@nestjs/common";
import {
ApiOperation,
@@ -223,8 +222,12 @@ export class SlotsController_2024_09_04 {
@UseGuards(OptionalApiAuthGuard)
@ApiOperation({
summary: "Reserve a slot",
description: "Make a slot not available for others to book for a certain period of time.",
description: `Make a slot not available for others to book for a certain period of time. If you authenticate using oAuth credentials, api key or access token
then you can also specify custom duration for how long the slot should be reserved for (defaults to 5 minutes).`,
})
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_X_CAL_CLIENT_ID_HEADER)
@ApiHeader(OPTIONAL_API_KEY_OR_ACCESS_TOKEN_HEADER)
async reserveSlot(
@Body() body: ReserveSlotInput_2024_09_04,
@GetOptionalUser() user: User
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_OR_ACCESS_TOKEN_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import {
@@ -23,7 +24,7 @@ import {
Headers,
Param,
} from "@nestjs/common";
import { ApiTags as DocsTags, ApiOperation } from "@nestjs/swagger";
import { ApiTags as DocsTags, ApiOperation, ApiHeader } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { Request } from "express";
import { stringify } from "querystring";
@@ -42,6 +43,7 @@ export class StripeController {
@UseGuards(ApiAuthGuard)
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Get stripe connect URL" })
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
async redirect(
@Req() req: Request,
@Headers("Authorization") authorization: string,
@@ -100,6 +102,7 @@ export class StripeController {
@Get("/check")
@UseGuards(ApiAuthGuard)
@HttpCode(HttpStatus.OK)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Check stripe connection" })
async check(@GetUser() user: UserWithProfile): Promise<StripCredentialsCheckOutputResponseDto> {
return await this.stripeService.checkIfIndividualStripeAccountConnected(user.id);
@@ -108,6 +111,7 @@ export class StripeController {
@Get("/check/:teamId")
@UseGuards(ApiAuthGuard)
@HttpCode(HttpStatus.OK)
@ApiHeader(API_KEY_OR_ACCESS_TOKEN_HEADER)
@ApiOperation({ summary: "Check team stripe connection" })
async checkTeamStripeConnection(
@Param("teamId") teamId: string
@@ -1,6 +1,7 @@
import { CreatePhoneCallInput } from "@/ee/event-types/event-types_2024_06_14/inputs/create-phone-call.input";
import { CreatePhoneCallOutput } from "@/ee/event-types/event-types_2024_06_14/outputs/create-phone-call.output";
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { PlatformPlan } from "@/modules/auth/decorators/billing/platform-plan.decorator";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
@@ -31,7 +32,7 @@ import {
NotFoundException,
Query,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
import { handleCreatePhoneCall } from "@calcom/platform-libraries";
@@ -63,6 +64,7 @@ export class TeamsEventTypesController {
@Roles("TEAM_ADMIN")
@PlatformPlan("ESSENTIALS")
@UseGuards(ApiAuthGuard, RolesGuard)
@ApiHeader(API_KEY_HEADER)
@Post("/")
@ApiOperation({ summary: "Create an event type" })
async createTeamEventType(
@@ -86,6 +88,7 @@ export class TeamsEventTypesController {
@Roles("TEAM_ADMIN")
@UseGuards(ApiAuthGuard, RolesGuard)
@ApiHeader(API_KEY_HEADER)
@Get("/:eventTypeId")
@ApiOperation({ summary: "Get an event type" })
async getTeamEventType(
@@ -109,6 +112,7 @@ export class TeamsEventTypesController {
@Roles("TEAM_ADMIN")
@Post("/:eventTypeId/create-phone-call")
@UseGuards(ApiAuthGuard, RolesGuard)
@ApiHeader(API_KEY_HEADER)
@ApiOperation({ summary: "Create a phone call" })
async createPhoneCall(
@Param("eventTypeId") eventTypeId: number,
@@ -162,6 +166,7 @@ export class TeamsEventTypesController {
@Roles("TEAM_ADMIN")
@UseGuards(ApiAuthGuard, RolesGuard)
@ApiHeader(API_KEY_HEADER)
@Patch("/:eventTypeId")
@ApiOperation({ summary: "Update a team event type" })
async updateTeamEventType(
@@ -193,6 +198,7 @@ export class TeamsEventTypesController {
@Roles("TEAM_ADMIN")
@UseGuards(ApiAuthGuard, RolesGuard)
@ApiHeader(API_KEY_HEADER)
@Delete("/:eventTypeId")
@HttpCode(HttpStatus.OK)
@ApiOperation({ summary: "Delete a team event type" })
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
@@ -27,7 +28,7 @@ import {
HttpStatus,
Logger,
} from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -40,6 +41,7 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard, RolesGuard)
@DocsTags("Teams / Memberships")
@ApiHeader(API_KEY_HEADER)
export class TeamsMembershipsController {
private logger = new Logger("TeamsMembershipsController");
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
@@ -14,7 +15,7 @@ import { TeamsService } from "@/modules/teams/teams/services/teams.service";
import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
import { UserWithProfile } from "@/modules/users/users.repository";
import { Controller, UseGuards, Get, Param, ParseIntPipe, Delete, Patch, Post, Body } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
@@ -26,6 +27,7 @@ import { TeamOutputDto } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard)
@DocsTags("Teams")
@ApiHeader(API_KEY_HEADER)
export class TeamsController {
constructor(private teamsService: TeamsService, private teamsRepository: TeamsRepository) {}
@@ -1,7 +1,7 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { TimezonesService } from "@/modules/timezones/services/timezones.service";
import { Controller, Get } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiExcludeController, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import type { CityTimezones } from "@calcom/platform-libraries";
@@ -11,6 +11,8 @@ import { ApiResponse } from "@calcom/platform-types";
path: "/v2/timezones",
version: API_VERSIONS_VALUES,
})
// note(Lauris): controller used only internally by atoms
@ApiExcludeController()
@DocsTags("Timezones")
export class TimezonesController {
constructor(private readonly timezonesService: TimezonesService) {}
@@ -1,4 +1,5 @@
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
import { API_KEY_HEADER } from "@/lib/docs/headers";
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { UserWithProfile } from "@/modules/users/users.repository";
@@ -15,7 +16,7 @@ import { WebhookOutputPipe } from "@/modules/webhooks/pipes/WebhookOutputPipe";
import { UserWebhooksService } from "@/modules/webhooks/services/user-webhooks.service";
import { WebhooksService } from "@/modules/webhooks/services/webhooks.service";
import { Controller, Post, Body, UseGuards, Get, Param, Query, Delete, Patch } from "@nestjs/common";
import { ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { ApiHeader, ApiOperation, ApiTags as DocsTags } from "@nestjs/swagger";
import { Webhook } from "@prisma/client";
import { plainToClass } from "class-transformer";
@@ -28,6 +29,7 @@ import { SkipTakePagination } from "@calcom/platform-types";
})
@UseGuards(ApiAuthGuard)
@DocsTags("Webhooks")
@ApiHeader(API_KEY_HEADER)
export class WebhooksController {
constructor(
private readonly webhooksService: WebhooksService,
File diff suppressed because it is too large Load Diff
@@ -8,7 +8,8 @@ import { DubReferralsPage } from "./DubReferralsPage";
export const metadata: Metadata = {
title: "Cal.com referral program - Earn money by sharing your link",
description: "Earn 20% recurring commissions for a full year by referring others to Cal.com, while giving your referrals 20% off for 12 months. Share your link and start earning today!",
description:
"Earn 20% recurring commissions for a full year by referring others to Cal.com, while giving your referrals 20% off for 12 months. Share your link and start earning today!",
};
// Export the appropriate component based on the feature flag
+12 -8
View File
@@ -87,6 +87,7 @@ const PlainChat = IS_PLAIN_CHAT_ENABLED
const shouldOpenPlain = pathname === "/event-types" && searchParams?.has("openPlain");
const userEmail = session?.user?.email;
const isPaidUser = session?.user.belongsToActiveTeam || !!session?.user.org;
const isAppDomain = useMemo(() => {
const restrictedPathsSet = new Set(
@@ -166,14 +167,6 @@ const PlainChat = IS_PLAIN_CHAT_ENABLED
},
],
chatButtons: [
{
icon: "chat",
text: "Ask a question",
threadDetails: {
labelTypeIds: ["lt_01JFJWNWAC464N8DZ6YE71YJRF"],
tierIdentifier: { externalId: data.userTier },
},
},
{
icon: "bulb",
text: "Send feedback",
@@ -238,6 +231,17 @@ const PlainChat = IS_PLAIN_CHAT_ENABLED
},
};
if (isPaidUser) {
plainChatConfig.chatButtons.push({
icon: "chat",
text: "Ask a question",
threadDetails: {
labelTypeIds: ["lt_01JFJWNWAC464N8DZ6YE71YJRF"],
tierIdentifier: { externalId: data.userTier },
},
});
}
if (process.env.NODE_ENV === "development" || process.env.NODE_ENV === "test") {
window.__PLAIN_CONFIG__ = plainChatConfig;
}
+12 -4
View File
@@ -174,6 +174,8 @@ function responseWithHeaders({ url, res, req }: { url: URL; res: NextResponse; r
export const config = {
// Next.js Doesn't support spread operator in config matcher, so, we must list all paths explicitly here.
// https://github.com/vercel/next.js/discussions/42458
// WARNING: DO NOT ADD AN ENDING SLASH "/" TO THE PATHS BELOW
// THIS WILL MAKE THEM NOT MATCH AND HENCE NOT HIT MIDDLEWARE
matcher: [
"/403",
"/500",
@@ -204,10 +206,16 @@ export const config = {
"/booking/:path*",
"/payment/:path*",
"/routing-forms/:path*",
"/team/:path*",
"/org/:path*",
"/:user/:type/",
"/:user/",
"/org/:orgSlug/instant-meeting/team/:slug/:type",
"/org/:orgSlug/team/:slug/:type",
"/org/:orgSlug/team/:slug",
"/org/:orgSlug/:user/:type",
"/org/:orgSlug/:user",
"/org/:orgSlug",
"/team/:slug/:type",
"/team/:slug",
"/:user/:type",
"/:user",
],
};
+1 -1
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "5.1.11",
"version": "5.1.12",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
+4 -2
View File
@@ -1,8 +1,8 @@
import type { NextApiRequest, NextApiResponse } from "next";
import { z } from "zod";
import stripe from "@calcom/app-store/stripepayment/lib/server";
import dayjs from "@calcom/dayjs";
import { StripeBillingService } from "@calcom/features/ee/billing/stripe-billling-service";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { IS_STRIPE_ENABLED } from "@calcom/lib/constants";
import { OrganizationRepository } from "@calcom/lib/server/repository/organization";
@@ -43,6 +43,7 @@ export async function moveUserToMatchingOrg({ email }: { email: string }) {
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
const { token } = verifySchema.parse(req.query);
const billingService = new StripeBillingService();
const foundToken = await prisma.verificationToken.findFirst({
where: {
@@ -130,7 +131,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
});
if (IS_STRIPE_ENABLED && userMetadataParsed.stripeCustomerId) {
await stripe.customers.update(userMetadataParsed.stripeCustomerId, {
await billingService.updateCustomer({
customerId: userMetadataParsed.stripeCustomerId,
email: updatedEmail,
});
}
@@ -2,7 +2,7 @@ import { buffer } from "micro";
import type { NextApiRequest, NextApiResponse } from "next";
import type Stripe from "stripe";
import stripe from "@calcom/app-store/stripepayment/lib/server";
import stripe from "@calcom/features/ee/payments/server/stripe";
import { IS_PRODUCTION } from "@calcom/lib/constants";
import { getErrorFromUnknown } from "@calcom/lib/errors";
import { HttpError as HttpCode } from "@calcom/lib/http-error";
@@ -0,0 +1,4 @@
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
import { filterSegmentsRouter } from "@calcom/trpc/server/routers/viewer/filterSegments/_router";
export default createNextApiHandler(filterSegmentsRouter);
+1 -1
View File
@@ -57,7 +57,7 @@ test.describe("Change App Theme Test", () => {
await page.waitForTimeout(3000);
const themeValue = await page.evaluate(() => localStorage.getItem("app-theme"));
expect(themeValue).toBe("system");
expect(themeValue).toBe("light");
const systemTheme = await page.evaluate(() => {
return window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
Binary file not shown.

Before

Width:  |  Height:  |  Size: 227 KiB

After

Width:  |  Height:  |  Size: 239 KiB

+12
View File
@@ -80,6 +80,9 @@
<path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H19a1 1 0 0 1 1 1v18a1 1 0 0 1-1 1H6.5a1 1 0 0 1 0-5H20" ></path>
<circle cx="12" cy="8" r="2" ></circle>
</symbol>
<symbol class="lucide lucide-bookmark" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="bookmark">
<path d="m19 21-7-4-7 4V5a2 2 0 0 1 2-2h10a2 2 0 0 1 2 2v16z" ></path>
</symbol>
<symbol class="lucide lucide-building" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="building">
<rect width="16" height="20" x="4" y="2" rx="2" ry="2" ></rect>
<path d="M9 22v-4h6v4" ></path>
@@ -418,6 +421,11 @@
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" ></path>
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" ></path>
</symbol>
<symbol class="lucide lucide-list-filter" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="list-filter">
<path d="M3 6h18" ></path>
<path d="M7 12h10" ></path>
<path d="M10 18h4" ></path>
</symbol>
<symbol class="lucide lucide-loader" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="loader">
<path d="M12 2v4" ></path>
<path d="m16.2 7.8 2.9-2.9" ></path>
@@ -605,6 +613,10 @@
<rect width="18" height="18" x="3" y="3" rx="2" ></rect>
<path d="m9 12 2 2 4-4" ></path>
</symbol>
<symbol class="lucide lucide-square-pen" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="square-pen">
<path d="M12 3H5a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h14a2 2 0 0 0 2-2v-7" ></path>
<path d="M18.375 2.625a1 1 0 0 1 3 3l-9.013 9.014a2 2 0 0 1-.853.505l-2.873.84a.5.5 0 0 1-.62-.62l.84-2.873a2 2 0 0 1 .506-.852z" ></path>
</symbol>
<symbol class="lucide lucide-star" viewBox="0 0 24 24" fill="inherit" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" id="star">
<polygon points="12 2 15.09 8.26 22 9.27 17 14.14 18.18 21.02 12 17.77 5.82 21.02 7 14.14 2 9.27 8.91 8.26 12 2" ></polygon>
</symbol>

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 51 KiB

@@ -2452,7 +2452,6 @@
"published": "منشور",
"unpublished": "غير منشور",
"publish": "نشر",
"org_publish_error": "لم يتمكن نشر المنظمة",
"troubleshooter_tooltip": "افتح أداة استكشاف الأخطاء وإصلاحها واكتشف ما هو الخطأ في جدولك",
"need_help": "هل تحتاج إلى مساعدة؟",
"troubleshooter": "أداة استكشاف الأخطاء وإصلاحها",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "خطة الفريق غير نشطة",
"inactive_team_plan_description": "خطة فريقك غير نشطة. تحقق من اشتراكك أو تواصل مع دعم العملاء",
"limited_access_trial_mode": "وصول محدود خلال فترة التجربة. الميزة متاحة بعد انتهاء فترة التجربة.",
"earn_20_percent_affiliate": "اكسب 20% كشريك تسويقي",
"referral_program": "برنامج الإحالة",
"dub_disabled_error_message": "تحتاج إلى مفتاح واجهة برمجة تطبيقات Dub ومعرف البرنامج لاستخدام هذه الصفحة.",
"uid": "المعرف الفريد",
"link": "رابط",
"rows_per_page": "صفوف في الصفحة",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "نصيحة: يمكنك إضافة '+' بين أسماء المستخدمين: cal.com/anna+brian لإنشاء اجتماع جماعي ديناميكي",
"user_has_no_team_yet": "ليس لديك فريق بعد",
"no_team_members": "ليس لديك أعضاء في الفريق بعد",
"hide_team_profile_link": "إخفاء رابط ملف الفريق",
"hide_team_profile_link_description": "إخفاء رابط ملف الفريق على صفحات الحجز",
"display": "عرض",
"could_not_find_slug_to_publish_org": "تعذر العثور على الاسم المختصر لنشر المؤسسة",
"picklist": "قائمة الاختيار",
"name_or_email": "الاسم أو البريد الإلكتروني",
"salesforce_round_robin_skip_fallback_to_lead_owner": "إذا لم يتم العثور على جهة اتصال، يتم الرجوع إلى مالك العميل المحتمل إذا كان موجودًا",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Dərc Edildi",
"unpublished": "Dərc Edilmədi",
"publish": "Dərc Et",
"org_publish_error": "Təşkilat dərc edilə bilmədi",
"troubleshooter_tooltip": "Problemləri həll edən vasitəni açın və cədvəlinizdə nə səhv olduğunu öyrənin",
"need_help": "Köməyə ehtiyacınız var?",
"troubleshooter": "Problemləri həll edən vasitə",
@@ -3009,6 +3008,9 @@
"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",
"limited_access_trial_mode": "Sınaq müddətində məhdud giriş. Xüsusiyyət sınaq müddəti bitdikdən sonra əlçatan olacaq.",
"earn_20_percent_affiliate": "20% Tərəfdaş Qazancı",
"referral_program": "Referral Proqramı",
"dub_disabled_error_message": "Bu səhifəni istifadə etmək üçün Dub API Açarı və Proqram İD-si lazımdır.",
"uid": "UID",
"link": "Link",
"rows_per_page": "səhifə başına sətir",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Məsləhət: Dinamik qrup görüşü yaratmaq üçün istifadəçi adları arasında '+' işarəsi istifadə edə bilərsiniz: cal.com/anna+brian",
"user_has_no_team_yet": "Hələ komandanız yoxdur",
"no_team_members": "Hələ komanda üzvləriniz yoxdur",
"hide_team_profile_link": "Komanda Profil Linkini Gizlət",
"hide_team_profile_link_description": "Rezervasiya səhifələrində komanda profil linkini gizlət",
"display": "Göstər",
"could_not_find_slug_to_publish_org": "Təşkilatı dərc etmək üçün slug tapılmadı",
"picklist": "Seçim siyahısı",
"name_or_email": "Ad və ya E-poçt",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Əgər əlaqə tapılmazsa, mövcud olduğu təqdirdə potensial müştəri sahibinə qayıt",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Публикувано",
"unpublished": "Непубликувано",
"publish": "Публикувай",
"org_publish_error": "Организацията не можа да бъде публикувана",
"troubleshooter_tooltip": "Отворете инструмента за отстраняване на проблеми и разберете какво не е наред с вашия график",
"need_help": "Нужда от помощ?",
"troubleshooter": "Инструмент за отстраняване на проблеми",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Неактивен план за екип",
"inactive_team_plan_description": "Вашият план за екип е неактивен. Проверете абонамента си или се свържете с отдела за обслужване на клиенти",
"limited_access_trial_mode": "Ограничен достъп по време на пробния период. Функцията ще бъде достъпна след края на пробния период.",
"earn_20_percent_affiliate": "Печелете 20% като партньор",
"referral_program": "Програма за препоръки",
"dub_disabled_error_message": "Нуждаете се от Dub API ключ и идентификатор на програма, за да използвате тази страница.",
"uid": "UID",
"link": "Връзка",
"rows_per_page": "реда на страница",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Съвет: Можете да добавите '+' между потребителските имена: cal.com/anna+brian, за да създадете динамична групова среща",
"user_has_no_team_yet": "Все още нямате екип",
"no_team_members": "Все още нямате членове на екипа",
"hide_team_profile_link": "Скриване на връзката към профила на екипа",
"hide_team_profile_link_description": "Скриване на връзката към профила на екипа на страниците за резервации",
"display": "Показване",
"could_not_find_slug_to_publish_org": "Не може да се намери слъг за публикуване на организацията",
"picklist": "Списък за избор",
"name_or_email": "Име или имейл",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ако не е намерен контакт, връщане към собственика на лийда, ако съществува",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавете новите си низове над този ред ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publicat",
"unpublished": "No publicat",
"publish": "Publica",
"org_publish_error": "No s'ha pogut publicar l'organització",
"troubleshooter_tooltip": "Obre el solucionador de problemes i descobreix què passa amb el teu calendari",
"need_help": "Necessites ajuda?",
"troubleshooter": "Solucionador de problemes",
@@ -3009,6 +3008,9 @@
"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",
"limited_access_trial_mode": "Accés limitat durant el període de prova. Funció disponible després de finalitzar la prova.",
"earn_20_percent_affiliate": "Guanya un 20% d'afiliació",
"referral_program": "Programa de recomanació",
"dub_disabled_error_message": "Necessites una clau API de Dub i un ID de programa per utilitzar aquesta pàgina.",
"uid": "UID",
"link": "Enllaç",
"rows_per_page": "files per pàgina",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Consell: Pots afegir un '+' entre noms d'usuari: cal.com/anna+brian per crear una reunió de grup dinàmica",
"user_has_no_team_yet": "Encara no tens cap equip",
"no_team_members": "Encara no tens membres a l'equip",
"hide_team_profile_link": "Amaga l'enllaç del perfil d'equip",
"hide_team_profile_link_description": "Amaga l'enllaç del perfil d'equip a les pàgines de reserva",
"display": "Visualització",
"could_not_find_slug_to_publish_org": "No s'ha pogut trobar l'slug per publicar l'organització",
"picklist": "Llista de selecció",
"name_or_email": "Nom o correu electrònic",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no es troba cap contacte, recorre al propietari del lead si existeix",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Afegiu les vostres noves cadenes a dalt ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publikováno",
"unpublished": "Nepublikováno",
"publish": "Publikovat",
"org_publish_error": "Organizaci se nepodařilo publikovat",
"troubleshooter_tooltip": "Otevřete nástroj pro řešení problémů a zjistěte, co je špatně s vaším rozvrhem",
"need_help": "Potřebujete pomoc?",
"troubleshooter": "Nástroj pro řešení problémů",
@@ -3009,6 +3008,9 @@
"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",
"limited_access_trial_mode": "Omezený přístup během zkušební doby. Funkce bude dostupná po skončení zkušební doby.",
"earn_20_percent_affiliate": "Získejte 20% jako partner",
"referral_program": "Doporučovací program",
"dub_disabled_error_message": "Pro použití této stránky potřebujete Dub API klíč a ID programu.",
"uid": "UID",
"link": "Odkaz",
"rows_per_page": "řádků na stránku",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tip: Mezi uživatelská jména můžete vložit '+': cal.com/anna+brian pro vytvoření dynamické skupinové schůzky",
"user_has_no_team_yet": "Zatím nemáte žádný tým",
"no_team_members": "Zatím nemáte žádné členy týmu",
"hide_team_profile_link": "Skrýt odkaz na profil týmu",
"hide_team_profile_link_description": "Skrýt odkaz na profil týmu na stránkách rezervací",
"display": "Zobrazení",
"could_not_find_slug_to_publish_org": "Nelze najít slug pro publikování organizace",
"picklist": "Výběrový seznam",
"name_or_email": "Jméno nebo e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Pokud není nalezen žádný kontakt, použít vlastníka leadu, pokud existuje",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Udgivet",
"unpublished": "Ikke udgivet",
"publish": "Udgiv",
"org_publish_error": "Organisationen kunne ikke udgives",
"troubleshooter_tooltip": "Åbn fejlfindingen og find ud af, hvad der er galt med din tidsplan",
"need_help": "Brug for hjælp?",
"troubleshooter": "Fejlfinding",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Inaktiv teamplan",
"inactive_team_plan_description": "Din teamplan er inaktiv. Tjek dit abonnement eller kontakt kundesupport",
"limited_access_trial_mode": "Begrænset adgang under prøveperioden. Funktionen er tilgængelig efter prøveperioden slutter.",
"earn_20_percent_affiliate": "Tjen 20% som partner",
"referral_program": "Henvisningsprogram",
"dub_disabled_error_message": "Du skal have en Dub API-nøgle og program-id for at bruge denne side.",
"uid": "UID",
"link": "Link",
"rows_per_page": "rækker pr. side",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tip: Du kan tilføje et '+' mellem brugernavne: cal.com/anna+brian for at lave et dynamisk gruppemøde",
"user_has_no_team_yet": "Du har ikke et team endnu",
"no_team_members": "Du har ikke nogen teammedlemmer endnu",
"hide_team_profile_link": "Skjul teamprofillink",
"hide_team_profile_link_description": "Skjul teamprofillinket på bookingsider",
"display": "Vis",
"could_not_find_slug_to_publish_org": "Kunne ikke finde slug til at publicere organisationen",
"picklist": "Valgliste",
"name_or_email": "Navn eller e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hvis ingen kontakt findes, fald tilbage til lead-ejeren, hvis denne eksisterer",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Tilføj dine nye strenge ovenfor her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Veröffentlicht",
"unpublished": "Unveröffentlicht",
"publish": "Veröffentlichen",
"org_publish_error": "Organisation konnte nicht veröffentlicht werden",
"troubleshooter_tooltip": "Öffnen Sie die Problembehandlung und finden Sie heraus, was mit Ihrem Zeitplan nicht stimmt",
"need_help": "Brauchst du Hilfe?",
"troubleshooter": "Problembehandlung",
@@ -3009,6 +3008,9 @@
"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",
"limited_access_trial_mode": "Eingeschränkter Zugriff während der Testphase. Funktion nach Ablauf der Testphase verfügbar.",
"earn_20_percent_affiliate": "20% Partnerprovision verdienen",
"referral_program": "Empfehlungsprogramm",
"dub_disabled_error_message": "Du benötigst einen Dub API-Schlüssel und eine Programm-ID, um diese Seite zu nutzen.",
"uid": "UID",
"link": "Link",
"rows_per_page": "Zeilen pro Seite",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tipp: Sie können ein '+' zwischen Benutzernamen setzen: cal.com/anna+brian, um ein dynamisches Gruppenmeeting zu erstellen",
"user_has_no_team_yet": "Sie haben noch kein Team",
"no_team_members": "Sie haben noch keine Teammitglieder",
"hide_team_profile_link": "Team-Profillink ausblenden",
"hide_team_profile_link_description": "Den Team-Profillink auf Buchungsseiten ausblenden",
"display": "Anzeige",
"could_not_find_slug_to_publish_org": "Konnte keinen Slug finden, um die Organisation zu veröffentlichen",
"picklist": "Auswahlliste",
"name_or_email": "Name oder E-Mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Wenn kein Kontakt gefunden wird, auf den Lead-Eigentümer zurückgreifen, falls vorhanden",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Fügen Sie Ihre neuen Code-Zeilen über dieser hinzu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Δημοσιευμένο",
"unpublished": "Μη δημοσιευμένο",
"publish": "Δημοσίευση",
"org_publish_error": "Ο οργανισμός δεν μπόρεσε να δημοσιευτεί",
"troubleshooter_tooltip": "Ανοίξτε τον αντιμετωπιστή προβλημάτων και βρείτε τι δεν πάει καλά με το πρόγραμμά σας",
"need_help": "Χρειάζεστε βοήθεια;",
"troubleshooter": "Αντιμετωπιστής προβλημάτων",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Ανενεργό πλάνο ομάδας",
"inactive_team_plan_description": "Το πλάνο της ομάδας σας είναι ανενεργό. Ελέγξτε τη συνδρομή σας ή επικοινωνήστε με την εξυπηρέτηση πελατών",
"limited_access_trial_mode": "Περιορισμένη πρόσβαση κατά τη διάρκεια της δοκιμαστικής περιόδου. Η λειτουργία θα είναι διαθέσιμη μετά το τέλος της δοκιμαστικής περιόδου.",
"earn_20_percent_affiliate": "Κέρδισε 20% ως συνεργάτης",
"referral_program": "Πρόγραμμα παραπομπών",
"dub_disabled_error_message": "Χρειάζεστε ένα κλειδί API Dub και ένα αναγνωριστικό προγράμματος για να χρησιμοποιήσετε αυτή τη σελίδα.",
"uid": "UID",
"link": "Σύνδεσμος",
"rows_per_page": "γραμμές ανά σελίδα",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Συμβουλή: Μπορείτε να προσθέσετε ένα '+' μεταξύ των ονομάτων χρήστη: cal.com/anna+brian για να δημιουργήσετε μια δυναμική ομαδική συνάντηση",
"user_has_no_team_yet": "Δεν έχετε ακόμη ομάδα",
"no_team_members": "Δεν έχετε ακόμη μέλη ομάδας",
"hide_team_profile_link": "Απόκρυψη συνδέσμου προφίλ ομάδας",
"hide_team_profile_link_description": "Απόκρυψη του συνδέσμου προφίλ ομάδας στις σελίδες κράτησης",
"display": "Εμφάνιση",
"could_not_find_slug_to_publish_org": "Δεν βρέθηκε το slug για τη δημοσίευση του οργανισμού",
"picklist": "Λίστα επιλογών",
"name_or_email": "Όνομα ή Email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Αν δεν βρεθεί επαφή, επιστροφή στον ιδιοκτήτη του lead εάν υπάρχει",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Προσθέστε τις νέες συμβολοσειρές σας πάνω από εδώ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -3038,6 +3038,29 @@
"tip_username_plus": "Tip: You can a '+' between usernames: cal.com/anna+brian to make a dynamic group meeting",
"user_has_no_team_yet": "You don't have a team yet",
"no_team_members": "You don't have team members yet",
"segment": "Segment",
"rename": "Rename",
"rename_segment": "Rename Segment",
"delete_segment": "Delete Segment",
"duplicate_segment": "Duplicate Segment",
"delete_segment_confirmation": "Are you sure you want to delete this segment? This action cannot be undone.",
"filter_segment_updated": "Filter segment updated",
"error_updating_filter_segment": "Error updating filter segment",
"filter_segment_duplicated": "Filter segment duplicated",
"error_duplicating_filter_segment": "Error duplicating filter segment",
"filter_segment_deleted": "Filter segment deleted",
"error_deleting_filter_segment": "Error deleting filter segment",
"save_segment": "Save Segment",
"segment_not_found": "Segment not found",
"filter_segment_saved": "Filter segment saved",
"no_segments": "No segments",
"error_saving_filter_segment": "Error saving filter segment",
"override_segment": "Save overrides to \"{{name}}\"",
"create_new_segment": "Create new segment",
"save_for_team": "Save for team",
"select_team": "Select team",
"please_select_team": "Please select a team",
"personal": "Personal",
"hide_team_profile_link": "Hide Team Profile Link",
"hide_team_profile_link_description": "Hide the team profile link on booking pages",
"display": "Display",
@@ -2452,7 +2452,6 @@
"published": "Publicado",
"unpublished": "No Publicado",
"publish": "Publicar",
"org_publish_error": "No se pudo publicar la organización",
"troubleshooter_tooltip": "Abre el solucionador de problemas y descubre qué está mal con tu horario",
"need_help": "¿Necesitas ayuda?",
"troubleshooter": "Solucionador de Problemas",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Plan de equipo inactivo",
"inactive_team_plan_description": "Tu plan de equipo está inactivo. Revisa tu suscripción o comunícate con el soporte al cliente",
"limited_access_trial_mode": "Acceso limitado durante la prueba. Función disponible después de que termine la prueba.",
"earn_20_percent_affiliate": "Gana 20% como afiliado",
"referral_program": "Programa de referidos",
"dub_disabled_error_message": "Necesitas una clave API de Dub y un ID de programa para usar esta página.",
"uid": "UID",
"link": "Enlace",
"rows_per_page": "filas por página",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Consejo: Puedes usar un '+' entre nombres de usuario: cal.com/anna+brian para crear una reunión grupal dinámica",
"user_has_no_team_yet": "Aún no tienes un equipo",
"no_team_members": "Aún no tienes miembros en tu equipo",
"hide_team_profile_link": "Ocultar enlace del perfil del equipo",
"hide_team_profile_link_description": "Ocultar el enlace del perfil del equipo en las páginas de reserva",
"display": "Mostrar",
"could_not_find_slug_to_publish_org": "No se pudo encontrar el slug para publicar la organización",
"picklist": "Lista de selección",
"name_or_email": "Nombre o correo electrónico",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no se encuentra ningún contacto, recurrir al propietario del lead si existe",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agrega tus nuevas cadenas arriba de esta línea ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publicado",
"unpublished": "No Publicado",
"publish": "Publicar",
"org_publish_error": "No se pudo publicar la organización",
"troubleshooter_tooltip": "Abre el solucionador de problemas y averigua qué está mal con tu horario",
"need_help": "¿Necesita ayuda?",
"troubleshooter": "Solucionador de Problemas",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Plan de equipo inactivo",
"inactive_team_plan_description": "Tu plan de equipo está inactivo. Revisa tu suscripción o contacta con el soporte al cliente",
"limited_access_trial_mode": "Acceso limitado durante la prueba. Función disponible después de que termine la prueba.",
"earn_20_percent_affiliate": "Gana 20% de afiliado",
"referral_program": "Programa de referidos",
"dub_disabled_error_message": "Necesitas una clave API de Dub y un ID de programa para usar esta página.",
"uid": "UID",
"link": "Enlace",
"rows_per_page": "filas por página",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Consejo: Puedes usar un '+' entre nombres de usuario: cal.com/anna+brian para crear una reunión de grupo dinámica",
"user_has_no_team_yet": "Aún no tienes un equipo",
"no_team_members": "Aún no tienes miembros en tu equipo",
"hide_team_profile_link": "Ocultar enlace del perfil del equipo",
"hide_team_profile_link_description": "Ocultar el enlace del perfil del equipo en las páginas de reserva",
"display": "Mostrar",
"could_not_find_slug_to_publish_org": "No se pudo encontrar el slug para publicar la organización",
"picklist": "Lista de selección",
"name_or_email": "Nombre o correo electrónico",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no se encuentra ningún contacto, recurrir al propietario del lead si existe",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agregue sus nuevas cadenas arriba ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Avaldatud",
"unpublished": "Avaldamata",
"publish": "Avalda",
"org_publish_error": "Organisatsiooni ei saanud avaldada",
"troubleshooter_tooltip": "Avage tõrkeotsing ja selgitage välja, mis teie ajakavas valesti on",
"need_help": "Abi vajama?",
"troubleshooter": "Veaotsing",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Mitteaktiivne meeskonnaplaan",
"inactive_team_plan_description": "Teie meeskonnaplaan on mitteaktiivne. Kontrollige oma tellimust või võtke ühendust klienditoega",
"limited_access_trial_mode": "Piiratud juurdepääs prooviperioodi ajal. Funktsioon on saadaval pärast prooviperioodi lõppu.",
"earn_20_percent_affiliate": "Teeni 20% partnertasu",
"referral_program": "Soovitusprogramm",
"dub_disabled_error_message": "Selle lehe kasutamiseks vajad Dub API võtit ja programmi ID-d.",
"uid": "UID",
"link": "Link",
"rows_per_page": "rida lehel",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Nõuanne: Saate kasutajanimede vahele lisada '+': cal.com/anna+brian, et luua dünaamiline grupikohtumise",
"user_has_no_team_yet": "Teil pole veel meeskonda",
"no_team_members": "Teil pole veel meeskonnaliikmeid",
"hide_team_profile_link": "Peida meeskonna profiili link",
"hide_team_profile_link_description": "Peida meeskonna profiili link broneerimislehtedel",
"display": "Kuva",
"could_not_find_slug_to_publish_org": "Organisatsiooni avaldamiseks ei leitud lühinime",
"picklist": "Valikloend",
"name_or_email": "Nimi või e-post",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Kui kontakti ei leita, kasuta võimalusel liidi omanikku",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Argitaratuta",
"unpublished": "Argitaratu gabe",
"publish": "Argitaratu",
"org_publish_error": "Ezin izan da erakundea argitaratu",
"troubleshooter_tooltip": "Ireki arazoen konpontzailea eta ikusi zer dagoen gaizki zure ordutegian",
"need_help": "Laguntza behar?",
"troubleshooter": "Arazoen konpontzailea",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Talde plan inaktiboa",
"inactive_team_plan_description": "Zure talde plana inaktiboa dago. Egiaztatu zure harpidetza edo jarri harremanetan bezeroen laguntzarekin",
"limited_access_trial_mode": "Sarbide mugatua proba aldian. Eginbidea proba amaitu ondoren erabilgarri.",
"earn_20_percent_affiliate": "Irabazi %20 afiliatuen bidez",
"referral_program": "Erreferentzia programa",
"dub_disabled_error_message": "Dub API gako bat eta programa ID bat behar dituzu orrialde hau erabiltzeko.",
"uid": "UID",
"link": "Esteka",
"rows_per_page": "errenkada orrialdeko",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Aholkua: '+' bat jar dezakezu erabiltzaile-izenen artean: cal.com/anna+brian talde-bilera dinamiko bat sortzeko",
"user_has_no_team_yet": "Oraindik ez duzu talderik",
"no_team_members": "Oraindik ez dituzu taldekiderik",
"hide_team_profile_link": "Ezkutatu taldearen profil esteka",
"hide_team_profile_link_description": "Ezkutatu taldearen profil esteka erreserba orrialdeetan",
"display": "Bistaratu",
"could_not_find_slug_to_publish_org": "Ezin izan da erakundea argitaratzeko slug-a aurkitu",
"picklist": "Hautaketa-zerrenda",
"name_or_email": "Izena edo helbide elektronikoa",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Kontakturik aurkitzen ez bada, itzuli bezeroen arduradunera existitzen bada",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Gehitu zure kate berriak honen gainean ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Julkaistu",
"unpublished": "Julkaisematon",
"publish": "Julkaise",
"org_publish_error": "Organisaatiota ei voitu julkaista",
"troubleshooter_tooltip": "Avaa vianmääritys ja selvitä mikä aikataulussasi on vialla",
"need_help": "Tarvitsetko apua?",
"troubleshooter": "Vianmääritys",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Ei-aktiivinen tiimitilaus",
"inactive_team_plan_description": "Tiimitilauksesi ei ole aktiivinen. Tarkista tilauksesi tai ota yhteyttä asiakastukeen",
"limited_access_trial_mode": "Rajoitettu pääsy kokeilun aikana. Ominaisuus käytettävissä kokeilun päätyttyä.",
"earn_20_percent_affiliate": "Ansaitse 20% kumppanina",
"referral_program": "Suositteluohjelma",
"dub_disabled_error_message": "Tarvitset Dub API -avaimen ja ohjelmatunnuksen käyttääksesi tätä sivua.",
"uid": "UID",
"link": "Linkki",
"rows_per_page": "riviä per sivu",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Vinkki: Voit lisätä '+'-merkin käyttäjänimien väliin: cal.com/anna+brian luodaksesi dynaamisen ryhmätapaamisen",
"user_has_no_team_yet": "Sinulla ei ole vielä tiimiä",
"no_team_members": "Sinulla ei ole vielä tiimin jäseniä",
"hide_team_profile_link": "Piilota tiimin profiililinkki",
"hide_team_profile_link_description": "Piilota tiimin profiililinkki varaussivuilta",
"display": "Näyttö",
"could_not_find_slug_to_publish_org": "Organisaation julkaisemiseen tarvittavaa lyhytosoitetta ei löytynyt",
"picklist": "Valintalistaus",
"name_or_email": "Nimi tai sähköposti",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Jos yhteyshenkilöä ei löydy, käytä liidin omistajaa, jos sellainen on olemassa",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Lisää uudet merkkijonot tämän yläpuolelle ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publiée",
"unpublished": "Non publiée",
"publish": "Publier",
"org_publish_error": "L'organisation n'a pas pu être publiée",
"troubleshooter_tooltip": "Ouvrez l'outil de dépannage et déterminez ce qui ne va pas avec votre planning",
"need_help": "Besoin d'aide ?",
"troubleshooter": "Dépannage",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Plan d'équipe inactif",
"inactive_team_plan_description": "Votre plan d'équipe est inactif. Vérifiez votre abonnement ou contactez le support client",
"limited_access_trial_mode": "Accès limité pendant l'essai. Fonctionnalité disponible après la fin de l'essai.",
"earn_20_percent_affiliate": "Gagnez 20 % d'affiliation",
"referral_program": "Programme de parrainage",
"dub_disabled_error_message": "Vous avez besoin d'une clé API Dub et d'un identifiant de programme pour utiliser cette page.",
"uid": "UID",
"link": "Lien",
"rows_per_page": "lignes par page",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Astuce : Vous pouvez ajouter un '+' entre les noms d'utilisateur : cal.com/anna+brian pour créer une réunion de groupe dynamique",
"user_has_no_team_yet": "Vous n'avez pas encore d'équipe",
"no_team_members": "Vous n'avez pas encore de membres d'équipe",
"hide_team_profile_link": "Masquer le lien du profil d'équipe",
"hide_team_profile_link_description": "Masquer le lien du profil d'équipe sur les pages de réservation",
"display": "Affichage",
"could_not_find_slug_to_publish_org": "Impossible de trouver le slug pour publier l'organisation",
"picklist": "Liste de sélection",
"name_or_email": "Nom ou e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si aucun contact n'est trouvé, se rabattre sur le propriétaire du lead s'il existe",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Ajoutez vos nouvelles chaînes ci-dessus ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "מפורסם",
"unpublished": "לא מפורסם",
"publish": "פרסום",
"org_publish_error": "אי אפשר לפרסם את הארגון",
"troubleshooter_tooltip": "אפשר לפתוח את פותר הבעיות ולנסות לפענח מה לא תקין בלוח הזמנים שלך",
"need_help": "צריך עזרה?",
"troubleshooter": "פותר בעיות",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "תוכנית צוות לא פעילה",
"inactive_team_plan_description": "תוכנית הצוות שלך אינה פעילה. בדוק את המנוי שלך או פנה לתמיכת הלקוחות",
"limited_access_trial_mode": "גישה מוגבלת במהלך תקופת הניסיון. התכונה תהיה זמינה לאחר סיום תקופת הניסיון.",
"earn_20_percent_affiliate": "הרווח 20% שותפים",
"referral_program": "תוכנית הפניות",
"dub_disabled_error_message": "אתה צריך מפתח API של Dub ומזהה תוכנית כדי להשתמש בדף זה.",
"uid": "מזהה ייחודי",
"link": "קישור",
"rows_per_page": "שורות לעמוד",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "טיפ: ניתן להוסיף '+' בין שמות משתמשים: cal.com/anna+brian ליצירת פגישה קבוצתית דינמית",
"user_has_no_team_yet": "עדיין אין לך צוות",
"no_team_members": "עדיין אין לך חברי צוות",
"hide_team_profile_link": "הסתר קישור לפרופיל הצוות",
"hide_team_profile_link_description": "הסתר את קישור פרופיל הצוות בדפי ההזמנות",
"display": "תצוגה",
"could_not_find_slug_to_publish_org": "לא ניתן למצוא מזהה URL לפרסום הארגון",
"picklist": "רשימת בחירה",
"name_or_email": "שם או אימייל",
"salesforce_round_robin_skip_fallback_to_lead_owner": "אם לא נמצא איש קשר, חזור לבעל הליד אם הוא קיים",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Közzétett",
"unpublished": "Kiadatlan",
"publish": "Közzététel",
"org_publish_error": "A szervezetet nem lehetett közzétenni",
"troubleshooter_tooltip": "Nyissa meg a hibaelhárítót, és derítse ki, mi a probléma az ütemezéssel",
"need_help": "Segítségre van szükséged?",
"troubleshooter": "Hibakereső",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Inaktív csapat előfizetés",
"inactive_team_plan_description": "Az Ön csapat előfizetése inaktív. Ellenőrizze előfizetését vagy vegye fel a kapcsolatot az ügyfélszolgálattal",
"limited_access_trial_mode": "Korlátozott hozzáférés a próbaidőszak alatt. A funkció a próbaidőszak végén lesz elérhető.",
"earn_20_percent_affiliate": "Keress 20% jutalékot",
"referral_program": "Ajánlói program",
"dub_disabled_error_message": "Dub API kulcsra és Program azonosítóra van szükséged az oldal használatához.",
"uid": "UID",
"link": "Link",
"rows_per_page": "sor oldalanként",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tipp: Használhat '+' jelet a felhasználónevek között: cal.com/anna+brian dinamikus csoportos találkozó létrehozásához",
"user_has_no_team_yet": "Még nincs csapatod",
"no_team_members": "Még nincsenek csapattagjaid",
"hide_team_profile_link": "Csapatprofil link elrejtése",
"hide_team_profile_link_description": "Csapatprofil link elrejtése a foglalási oldalakon",
"display": "Megjelenítés",
"could_not_find_slug_to_publish_org": "Nem található slug a szervezet közzétételéhez",
"picklist": "Választólista",
"name_or_email": "Név vagy e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ha nem található kapcsolat, visszalépés a lead tulajdonoshoz, ha létezik",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adja hozzá az új karakterláncokat fent ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Pubblicato",
"unpublished": "Pubblicazione annullata",
"publish": "Pubblica",
"org_publish_error": "Non è stato possibile pubblicare l'organizzazione",
"troubleshooter_tooltip": "Apri lo strumento di soluzione dei problemi per individuare il problema del tuo programma",
"need_help": "Hai bisogno di aiuto?",
"troubleshooter": "Risoluzione problemi",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Piano team inattivo",
"inactive_team_plan_description": "Il tuo piano team è inattivo. Controlla il tuo abbonamento o contatta l'assistenza clienti",
"limited_access_trial_mode": "Accesso limitato durante la prova. Funzionalità disponibile al termine del periodo di prova.",
"earn_20_percent_affiliate": "Guadagna il 20% come affiliato",
"referral_program": "Programma di referral",
"dub_disabled_error_message": "Hai bisogno di una chiave API Dub e di un ID programma per utilizzare questa pagina.",
"uid": "UID",
"link": "Link",
"rows_per_page": "righe per pagina",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Suggerimento: Puoi aggiungere un '+' tra i nomi utente: cal.com/anna+brian per creare un incontro di gruppo dinamico",
"user_has_no_team_yet": "Non hai ancora un team",
"no_team_members": "Non hai ancora membri del team",
"hide_team_profile_link": "Nascondi link al profilo del team",
"hide_team_profile_link_description": "Nascondi il link al profilo del team nelle pagine di prenotazione",
"display": "Visualizzazione",
"could_not_find_slug_to_publish_org": "Impossibile trovare lo slug per pubblicare l'organizzazione",
"picklist": "Lista di selezione",
"name_or_email": "Nome o email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se non viene trovato alcun contatto, passa al proprietario del lead se esiste",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Aggiungi le tue nuove stringhe qui sopra ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "公開済み",
"unpublished": "未公開",
"publish": "公開する",
"org_publish_error": "組織を公開できませんでした",
"troubleshooter_tooltip": "トラブルシューティングを開いて、スケジュールの問題を解決する",
"need_help": "サポートが必要ですか?",
"troubleshooter": "トラブルシューティング",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "非アクティブなチームプラン",
"inactive_team_plan_description": "チームプランが非アクティブです。サブスクリプションを確認するか、カスタマーサポートにお問い合わせください",
"limited_access_trial_mode": "トライアル期間中はアクセスが制限されます。この機能はトライアル終了後に利用可能になります。",
"earn_20_percent_affiliate": "20%のアフィリエイト報酬を獲得",
"referral_program": "紹介プログラム",
"dub_disabled_error_message": "このページを使用するには、Dub APIキーとプログラムIDが必要です。",
"uid": "UID",
"link": "リンク",
"rows_per_page": "ページあたりの行数",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "ヒント:ユーザー名の間に '+'を入れることで動的なグループミーティングを作成できます:cal.com/anna+brian",
"user_has_no_team_yet": "まだチームがありません",
"no_team_members": "まだチームメンバーがいません",
"hide_team_profile_link": "チームプロフィールリンクを非表示",
"hide_team_profile_link_description": "予約ページでチームプロフィールリンクを非表示にする",
"display": "表示",
"could_not_find_slug_to_publish_org": "組織を公開するためのスラッグが見つかりませんでした",
"picklist": "選択リスト",
"name_or_email": "名前またはメールアドレス",
"salesforce_round_robin_skip_fallback_to_lead_owner": "連絡先が見つからない場合、リード所有者が存在すればそちらにフォールバックする",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ この上に新しい文字列を追加してください ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "បានបោះពុម្ពផ្សាយ",
"unpublished": "មិនបានបោះពុម្ពផ្សាយ",
"publish": "បោះពុម្ពផ្សាយ",
"org_publish_error": "អង្គការមិនអាចបោះពុម្ពផ្សាយបានទេ",
"troubleshooter_tooltip": "បើកឧបករណ៍ដោះស្រាយបញ្ហា និងស្វែងរកអ្វីដែលខុសក្នុងកាលវិភាគរបស់អ្នក",
"need_help": "ត្រូវការជំនួយ?",
"troubleshooter": "ឧបករណ៍ដោះស្រាយបញ្ហា",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "ផែនការក្រុមដែលមិនសកម្ម",
"inactive_team_plan_description": "ផែនការក្រុមរបស់អ្នកមិនសកម្មទេ។ សូមពិនិត្យមើលការជាវរបស់អ្នក ឬទាក់ទងសេវាកម្មអតិថិជន",
"limited_access_trial_mode": "ការចូលប្រើមានកំណត់ក្នុងអំឡុងពេលសាកល្បង។ មុខងារនេះនឹងមានបន្ទាប់ពីការសាកល្បងបញ្ចប់។",
"earn_20_percent_affiliate": "ទទួលបាន 20% ពីសម្ព័ន្ធមិត្ត",
"referral_program": "កម្មវិធីណែនាំ",
"dub_disabled_error_message": "អ្នកត្រូវការ Dub API Key និង Program Id ដើម្បីប្រើទំព័រនេះ។",
"uid": "UID",
"link": "តំណភ្ជាប់",
"rows_per_page": "ជួរដេកក្នុងមួយទំព័រ",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "គន្លឹះ៖ អ្នកអាចដាក់សញ្ញា '+' រវាងឈ្មោះអ្នកប្រើប្រាស់៖ cal.com/anna+brian ដើម្បីបង្កើតការប្រជុំក្រុមដែលមានការផ្លាស់ប្តូរ",
"user_has_no_team_yet": "អ្នកមិនទាន់មានក្រុមនៅឡើយទេ",
"no_team_members": "អ្នកមិនទាន់មានសមាជិកក្រុមនៅឡើយទេ",
"hide_team_profile_link": "លាក់តំណភ្ជាប់ទម្រង់ក្រុម",
"hide_team_profile_link_description": "លាក់តំណភ្ជាប់ទម្រង់ក្រុមនៅលើទំព័រកក់",
"display": "បង្ហាញ",
"could_not_find_slug_to_publish_org": "មិនអាចរកឃើញ slug ដើម្បីផ្សព្វផ្សាយអង្គភាពទេ",
"picklist": "បញ្ជីជម្រើស",
"name_or_email": "ឈ្មោះ ឬអ៊ីមែល",
"salesforce_round_robin_skip_fallback_to_lead_owner": "ប្រសិនបើមិនមានទំនាក់ទំនងត្រូវបានរកឃើញទេ ត្រឡប់ទៅម្ចាស់ Lead ប្រសិនបើមាន",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ បន្ថែមខ្សែអក្សរថ្មីរបស់អ្នកនៅខាងលើនេះ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "게시됨",
"unpublished": "게시되지 않음",
"publish": "게시",
"org_publish_error": "조직을 게시할 수 없습니다",
"troubleshooter_tooltip": "문제 해결 도구를 열고 일정에 무엇이 잘못되었는지 확인하세요",
"need_help": "도움이 필요하세요?",
"troubleshooter": "문제 해결 도구",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "비활성 팀 플랜",
"inactive_team_plan_description": "팀 플랜이 비활성 상태입니다. 구독을 확인하거나 고객 지원에 문의하세요",
"limited_access_trial_mode": "체험 기간 동안 제한된 액세스. 체험 종료 후 기능 사용 가능.",
"earn_20_percent_affiliate": "20% 제휴 수익 창출",
"referral_program": "추천 프로그램",
"dub_disabled_error_message": "이 페이지를 사용하려면 Dub API 키와 프로그램 ID가 필요합니다.",
"uid": "UID",
"link": "링크",
"rows_per_page": "페이지당 행 수",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "팁: 사용자 이름 사이에 '+'를 추가할 수 있습니다: cal.com/anna+brian으로 동적 그룹 미팅을 만들 수 있습니다",
"user_has_no_team_yet": "아직 팀이 없습니다",
"no_team_members": "아직 팀원이 없습니다",
"hide_team_profile_link": "팀 프로필 링크 숨기기",
"hide_team_profile_link_description": "예약 페이지에서 팀 프로필 링크 숨기기",
"display": "표시",
"could_not_find_slug_to_publish_org": "조직을 게시할 슬러그를 찾을 수 없습니다",
"picklist": "선택 목록",
"name_or_email": "이름 또는 이메일",
"salesforce_round_robin_skip_fallback_to_lead_owner": "연락처를 찾을 수 없는 경우, 리드 소유자가 있으면 대체",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 여기에 새 문자열을 추가하세요 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Gepubliceerd",
"unpublished": "Niet gepubliceerd",
"publish": "Publiceren",
"org_publish_error": "Organisatie kon niet worden gepubliceerd",
"troubleshooter_tooltip": "Open de probleemoplosser en ontdek wat er mis is met je schema",
"need_help": "Hulp nodig?",
"troubleshooter": "Probleemoplosser",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Inactief teamabonnement",
"inactive_team_plan_description": "Uw teamabonnement is inactief. Controleer uw abonnement of neem contact op met de klantenondersteuning",
"limited_access_trial_mode": "Beperkte toegang tijdens proefperiode. Functie beschikbaar na afloop van de proefperiode.",
"earn_20_percent_affiliate": "Verdien 20% als affiliate",
"referral_program": "Verwijzingsprogramma",
"dub_disabled_error_message": "Je hebt een Dub API-sleutel en programma-ID nodig om deze pagina te gebruiken.",
"uid": "UID",
"link": "Link",
"rows_per_page": "rijen per pagina",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tip: U kunt een '+' tussen gebruikersnamen plaatsen: cal.com/anna+brian om een dynamische groepsbijeenkomst te maken",
"user_has_no_team_yet": "U heeft nog geen team",
"no_team_members": "U heeft nog geen teamleden",
"hide_team_profile_link": "Teamprofiellink verbergen",
"hide_team_profile_link_description": "Verberg de teamprofiellink op boekingspagina's",
"display": "Weergave",
"could_not_find_slug_to_publish_org": "Kon geen slug vinden om de organisatie te publiceren",
"picklist": "Keuzelijst",
"name_or_email": "Naam of e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Als er geen contactpersoon wordt gevonden, val dan terug op de lead-eigenaar als deze bestaat",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Voeg uw nieuwe strings hierboven toe ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publisert",
"unpublished": "Upublisert",
"publish": "Publiser",
"org_publish_error": "Organisasjonen kunne ikke publiseres",
"troubleshooter_tooltip": "Åpne feilsøkeren og finn ut hva som er galt med timeplanen din",
"need_help": "Trenger du hjelp?",
"troubleshooter": "Feilsøker",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Inaktiv teamplan",
"inactive_team_plan_description": "Din teamplan er inaktiv. Sjekk abonnementet ditt eller ta kontakt med kundeservice",
"limited_access_trial_mode": "Begrenset tilgang under prøveperioden. Funksjonen blir tilgjengelig etter at prøveperioden er over.",
"earn_20_percent_affiliate": "Tjen 20% som partner",
"referral_program": "Henvisningsprogram",
"dub_disabled_error_message": "Du trenger en Dub API-nøkkel og program-ID for å bruke denne siden.",
"uid": "UID",
"link": "Lenke",
"rows_per_page": "rader per side",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tips: Du kan legge til '+' mellom brukernavn: cal.com/anna+brian for å lage et dynamisk gruppemøte",
"user_has_no_team_yet": "Du har ikke et team ennå",
"no_team_members": "Du har ingen teammedlemmer ennå",
"hide_team_profile_link": "Skjul teamprofillenke",
"hide_team_profile_link_description": "Skjul teamprofillenken på bestillingssider",
"display": "Visning",
"could_not_find_slug_to_publish_org": "Kunne ikke finne slug for å publisere organisasjonen",
"picklist": "Valgliste",
"name_or_email": "Navn eller e-post",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hvis ingen kontakt blir funnet, fall tilbake til leadeier hvis den eksisterer",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Legg til dine nye strenger over her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Opublikowane",
"unpublished": "Nieopublikowane",
"publish": "Opublikuj",
"org_publish_error": "Nie udało się opublikować organizacji",
"troubleshooter_tooltip": "Otwórz narzędzie do rozwiązywania problemów i sprawdź, co jest nie tak z Twoim harmonogramem",
"need_help": "Potrzebujesz pomocy?",
"troubleshooter": "Narzędzie do rozwiązywania problemów",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Nieaktywny plan zespołowy",
"inactive_team_plan_description": "Twój plan zespołowy jest nieaktywny. Sprawdź swoją subskrypcję lub skontaktuj się z obsługą klienta",
"limited_access_trial_mode": "Ograniczony dostęp w trybie próbnym. Funkcja dostępna po zakończeniu okresu próbnego.",
"earn_20_percent_affiliate": "Zarabiaj 20% jako partner afiliacyjny",
"referral_program": "Program poleceń",
"dub_disabled_error_message": "Potrzebujesz klucza API Dub i identyfikatora programu, aby korzystać z tej strony.",
"uid": "UID",
"link": "Link",
"rows_per_page": "wierszy na stronę",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Wskazówka: Możesz dodać '+' między nazwami użytkowników: cal.com/anna+brian, aby utworzyć dynamiczne spotkanie grupowe",
"user_has_no_team_yet": "Nie masz jeszcze zespołu",
"no_team_members": "Nie masz jeszcze członków zespołu",
"hide_team_profile_link": "Ukryj link do profilu zespołu",
"hide_team_profile_link_description": "Ukryj link do profilu zespołu na stronach rezerwacji",
"display": "Wyświetlanie",
"could_not_find_slug_to_publish_org": "Nie można znaleźć sluga do opublikowania organizacji",
"picklist": "Lista wyboru",
"name_or_email": "Imię lub email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Jeśli nie znaleziono kontaktu, powróć do właściciela leada, jeśli istnieje",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodaj nowe ciągi powyżej ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publicado",
"unpublished": "Não publicado",
"publish": "Publicar",
"org_publish_error": "Não foi possível publicar a organização",
"troubleshooter_tooltip": "Abra o solucionador de problemas e descubra o que há de errado com sua programação",
"need_help": "Precisa de ajuda?",
"troubleshooter": "Solucionador de problemas",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Plano de equipe inativo",
"inactive_team_plan_description": "Seu plano de equipe está inativo. Verifique sua assinatura ou entre em contato com o suporte ao cliente",
"limited_access_trial_mode": "Acesso limitado durante o período de teste. Recurso disponível após o término do teste.",
"earn_20_percent_affiliate": "Ganhe 20% como afiliado",
"referral_program": "Programa de indicação",
"dub_disabled_error_message": "Você precisa de uma chave de API Dub e um ID de programa para usar esta página.",
"uid": "UID",
"link": "Link",
"rows_per_page": "linhas por página",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Dica: Você pode usar um '+' entre nomes de usuário: cal.com/anna+brian para criar uma reunião de grupo dinâmica",
"user_has_no_team_yet": "Você ainda não tem uma equipe",
"no_team_members": "Você ainda não tem membros na equipe",
"hide_team_profile_link": "Ocultar link do perfil da equipe",
"hide_team_profile_link_description": "Ocultar o link do perfil da equipe nas páginas de agendamento",
"display": "Exibir",
"could_not_find_slug_to_publish_org": "Não foi possível encontrar o slug para publicar a organização",
"picklist": "Lista de seleção",
"name_or_email": "Nome ou e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se nenhum contato for encontrado, recorrer ao proprietário do lead, se existir",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adicione suas novas strings aqui em cima ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publicada",
"unpublished": "Não publicada",
"publish": "Publicar",
"org_publish_error": "Não foi possível publicar a organização",
"troubleshooter_tooltip": "Abra o solucionador de problemas e descubra o que está errado com o seu agendamento",
"need_help": "Precisa de ajuda?",
"troubleshooter": "Solucionador de problemas",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Plano de equipa inativo",
"inactive_team_plan_description": "O seu plano de equipa está inativo. Verifique a sua subscrição ou contacte o apoio ao cliente",
"limited_access_trial_mode": "Acesso limitado durante o período de teste. Funcionalidade disponível após o término do teste.",
"earn_20_percent_affiliate": "Ganhe 20% como afiliado",
"referral_program": "Programa de indicação",
"dub_disabled_error_message": "Você precisa de uma chave de API Dub e um ID de programa para usar esta página.",
"uid": "UID",
"link": "Link",
"rows_per_page": "linhas por página",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Dica: Pode adicionar um '+' entre nomes de utilizador: cal.com/anna+brian para criar uma reunião de grupo dinâmica",
"user_has_no_team_yet": "Ainda não tem uma equipa",
"no_team_members": "Ainda não tem membros na equipa",
"hide_team_profile_link": "Ocultar link do perfil da equipe",
"hide_team_profile_link_description": "Ocultar o link do perfil da equipe nas páginas de agendamento",
"display": "Exibir",
"could_not_find_slug_to_publish_org": "Não foi possível encontrar o slug para publicar a organização",
"picklist": "Lista de seleção",
"name_or_email": "Nome ou e-mail",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se nenhum contato for encontrado, recorrer ao proprietário do lead, se existir",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publicat",
"unpublished": "Nepublicat",
"publish": "Publicați",
"org_publish_error": "Organizația nu a putut fi publicată",
"troubleshooter_tooltip": "Deschideți instrumentul de depanare și aflați ce este în neregulă cu programul dvs.",
"need_help": "Aveți nevoie de ajutor?",
"troubleshooter": "Instrument de Depanare",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Plan de echipă inactiv",
"inactive_team_plan_description": "Planul dvs. de echipă este inactiv. Verificați-vă abonamentul sau contactați serviciul de asistență pentru clienți",
"limited_access_trial_mode": "Acces limitat în timpul perioadei de probă. Funcționalitatea va fi disponibilă după încheierea perioadei de probă.",
"earn_20_percent_affiliate": "Câștigă 20% afiliat",
"referral_program": "Program de recomandare",
"dub_disabled_error_message": "Ai nevoie de o cheie API Dub și un ID de program pentru a utiliza această pagină.",
"uid": "UID",
"link": "Link",
"rows_per_page": "rânduri pe pagină",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Sfat: Puteți adăuga un '+' între numele de utilizator: cal.com/anna+brian pentru a crea o întâlnire de grup dinamică",
"user_has_no_team_yet": "Nu aveți încă o echipă",
"no_team_members": "Nu aveți încă membri în echipă",
"hide_team_profile_link": "Ascunde link-ul profilului echipei",
"hide_team_profile_link_description": "Ascunde link-ul profilului echipei pe paginile de programare",
"display": "Afișare",
"could_not_find_slug_to_publish_org": "Nu s-a putut găsi slug-ul pentru publicarea organizației",
"picklist": "Listă de selecție",
"name_or_email": "Nume sau email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Dacă nu este găsit niciun contact, se revine la proprietarul lead-ului dacă acesta există",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adăugați stringurile noi deasupra acestui rând ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Опубликовано",
"unpublished": "Не опубликовано",
"publish": "Опубликовать",
"org_publish_error": "Не удалось опубликовать организацию",
"troubleshooter_tooltip": "Откройте средство устранения неполадок и выясните, что не так с вашим расписанием",
"need_help": "Нужна помощь?",
"troubleshooter": "Средство устранения неполадок",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Неактивный тарифный план команды",
"inactive_team_plan_description": "Ваш тарифный план команды неактивен. Проверьте вашу подписку или обратитесь в службу поддержки",
"limited_access_trial_mode": "Ограниченный доступ во время пробного периода. Функция будет доступна после окончания пробного периода.",
"earn_20_percent_affiliate": "Получайте 20% партнерских отчислений",
"referral_program": "Реферальная программа",
"dub_disabled_error_message": "Для использования этой страницы вам нужен API-ключ Dub и идентификатор программы.",
"uid": "UID",
"link": "Ссылка",
"rows_per_page": "строк на странице",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Совет: Вы можете добавить '+' между именами пользователей: cal.com/anna+brian для создания динамической групповой встречи",
"user_has_no_team_yet": "У вас пока нет команды",
"no_team_members": "У вас пока нет участников команды",
"hide_team_profile_link": "Скрыть ссылку на профиль команды",
"hide_team_profile_link_description": "Скрыть ссылку на профиль команды на страницах бронирования",
"display": "Отображение",
"could_not_find_slug_to_publish_org": "Не удалось найти slug для публикации организации",
"picklist": "Список выбора",
"name_or_email": "Имя или электронная почта",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Если контакт не найден, использовать владельца лида, если он существует",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавьте строки выше ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publikované",
"unpublished": "Nepublikované",
"publish": "Publikovať",
"org_publish_error": "Organizáciu sa nepodarilo publikovať",
"troubleshooter_tooltip": "Otvorte nástroj na riešenie problémov a zistite, čo je zle s vaším plánom",
"need_help": "Potrebujete pomoc?",
"troubleshooter": "Nástroj na riešenie problémov",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Neaktívny tímový plán",
"inactive_team_plan_description": "Váš tímový plán je neaktívny. Skontrolujte svoje predplatné alebo kontaktujte zákaznícku podporu",
"limited_access_trial_mode": "Obmedzený prístup počas skúšobnej doby. Funkcia bude dostupná po skončení skúšobnej doby.",
"earn_20_percent_affiliate": "Získajte 20% ako partner",
"referral_program": "Partnerský program",
"dub_disabled_error_message": "Na použitie tejto stránky potrebujete Dub API kľúč a ID programu.",
"uid": "UID",
"link": "Odkaz",
"rows_per_page": "riadkov na stránku",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tip: Medzi používateľské mená môžete vložiť '+': cal.com/anna+brian na vytvorenie dynamického skupinového stretnutia",
"user_has_no_team_yet": "Zatiaľ nemáte žiadny tím",
"no_team_members": "Zatiaľ nemáte žiadnych členov tímu",
"hide_team_profile_link": "Skryť odkaz na profil tímu",
"hide_team_profile_link_description": "Skryť odkaz na profil tímu na stránkach rezervácií",
"display": "Zobrazenie",
"could_not_find_slug_to_publish_org": "Nepodarilo sa nájsť slug na zverejnenie organizácie",
"picklist": "Zoznam na výber",
"name_or_email": "Meno alebo email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ak sa nenájde žiadny kontakt, použije sa vlastník leadu, ak existuje",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Pridajte svoje nové reťazce nad túto líniu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Objavljeno",
"unpublished": "Neobjavljeno",
"publish": "Objavi",
"org_publish_error": "Organizacija nije mogla biti objavljena",
"troubleshooter_tooltip": "Otvorite alat za rešavanje problema i saznajte šta nije u redu sa vašim rasporedom",
"need_help": "Potrebna vam je pomoć?",
"troubleshooter": "Alat za rešavanje problema",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Neaktivan timski plan",
"inactive_team_plan_description": "Vaš timski plan je neaktivan. Proverite vašu pretplatu ili se obratite korisničkoj podršci",
"limited_access_trial_mode": "Ograničen pristup tokom probnog perioda. Funkcija dostupna nakon završetka probnog perioda.",
"earn_20_percent_affiliate": "Zaradite 20% kao afiliejt",
"referral_program": "Program preporuka",
"dub_disabled_error_message": "Potreban vam je Dub API ključ i ID programa da biste koristili ovu stranicu.",
"uid": "UID",
"link": "Link",
"rows_per_page": "redova po stranici",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Savet: Možete dodati '+' između korisničkih imena: cal.com/anna+brian da napravite dinamički grupni sastanak",
"user_has_no_team_yet": "Još uvek nemate tim",
"no_team_members": "Još uvek nemate članove tima",
"hide_team_profile_link": "Sakrij link profila tima",
"hide_team_profile_link_description": "Sakrij link profila tima na stranicama za zakazivanje",
"display": "Prikaz",
"could_not_find_slug_to_publish_org": "Nije moguće pronaći slug za objavljivanje organizacije",
"picklist": "Lista za izbor",
"name_or_email": "Ime ili email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ako kontakt nije pronađen, vrati se na vlasnika potencijalnog klijenta ako postoji",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodajte svoje nove stringove iznad ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Publicerad",
"unpublished": "Opublicerad",
"publish": "Publicera",
"org_publish_error": "Organisationen kunde inte publiceras",
"troubleshooter_tooltip": "Öppna felsökaren och ta reda på vad som är fel med ditt schema",
"need_help": "Behöver du hjälp?",
"troubleshooter": "Felsökare",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Inaktiv teamplan",
"inactive_team_plan_description": "Din teamplan är inaktiv. Kontrollera din prenumeration eller kontakta kundsupport",
"limited_access_trial_mode": "Begränsad åtkomst under provperioden. Funktionen blir tillgänglig efter att provperioden avslutas.",
"earn_20_percent_affiliate": "Tjäna 20% som affiliate",
"referral_program": "Referensprogram",
"dub_disabled_error_message": "Du behöver en Dub API-nyckel och program-ID för att använda denna sida.",
"uid": "UID",
"link": "Länk",
"rows_per_page": "rader per sida",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Tips: Du kan lägga till ett '+' mellan användarnamn: cal.com/anna+brian för att skapa ett dynamiskt gruppmöte",
"user_has_no_team_yet": "Du har inget team ännu",
"no_team_members": "Du har inga teammedlemmar ännu",
"hide_team_profile_link": "Dölj teamprofilslänk",
"hide_team_profile_link_description": "Dölj teamprofilslänken på bokningssidor",
"display": "Visa",
"could_not_find_slug_to_publish_org": "Kunde inte hitta slug för att publicera organisationen",
"picklist": "Urvalslista",
"name_or_email": "Namn eller e-post",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Om ingen kontakt hittas, återgå till lead-ägaren om sådan finns",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Yayınlandı",
"unpublished": "Yayınlanmadı",
"publish": "Yayınla",
"org_publish_error": "Organizasyon yayınlanamadı",
"troubleshooter_tooltip": "Sorun gidericiyi açın ve programınızda neyin yanlış olduğunu öğrenin",
"need_help": "Yardıma mı ihtiyacınız var?",
"troubleshooter": "Sorun Giderici",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Aktif olmayan ekip planı",
"inactive_team_plan_description": "Ekip planınız aktif değil. Aboneliğinizi kontrol edin veya müşteri desteğiyle iletişime geçin",
"limited_access_trial_mode": "Deneme süresi boyunca sınırlı erişim. Özellik deneme süresi sona erdikten sonra kullanılabilir.",
"earn_20_percent_affiliate": "Yüzde 20 Ortaklık Kazancı",
"referral_program": "Referans Programı",
"dub_disabled_error_message": "Bu sayfayı kullanmak için bir Dub API Anahtarı ve Program Kimliği'ne ihtiyacınız var.",
"uid": "UID",
"link": "Bağlantı",
"rows_per_page": "sayfa başına satır",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "İpucu: Dinamik bir grup toplantısı oluşturmak için kullanıcı adları arasına '+' ekleyebilirsiniz: cal.com/anna+brian",
"user_has_no_team_yet": "Henüz bir ekibiniz yok",
"no_team_members": "Henüz ekip üyeniz yok",
"hide_team_profile_link": "Takım Profil Bağlantısını Gizle",
"hide_team_profile_link_description": "Rezervasyon sayfalarında takım profil bağlantısını gizle",
"display": "Görüntüleme",
"could_not_find_slug_to_publish_org": "Organizasyonu yayınlamak için kısa ad bulunamadı",
"picklist": "Seçim listesi",
"name_or_email": "İsim veya E-posta",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hiçbir kişi bulunamazsa, eğer varsa müşteri adayı sahibine geri dön",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni dizelerinizi yukarıya ekleyin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Опубліковано",
"unpublished": "Неопубліковано",
"publish": "Опублікувати",
"org_publish_error": "Не вдалося опублікувати організацію",
"troubleshooter_tooltip": "Відкрийте засіб усунення несправностей і з'ясуйте, що не так з вашим розкладом",
"need_help": "Потрібна допомога?",
"troubleshooter": "Засіб усунення несправностей",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Неактивний план команди",
"inactive_team_plan_description": "Ваш командний план неактивний. Перевірте вашу підписку або зверніться до служби підтримки",
"limited_access_trial_mode": "Обмежений доступ під час пробного періоду. Функція буде доступна після завершення пробного періоду.",
"earn_20_percent_affiliate": "Отримайте 20% партнерської винагороди",
"referral_program": "Реферальна програма",
"dub_disabled_error_message": "Вам потрібен API-ключ Dub та ідентифікатор програми, щоб використовувати цю сторінку.",
"uid": "UID",
"link": "Посилання",
"rows_per_page": "рядків на сторінку",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Порада: Ви можете додати '+' між іменами користувачів: cal.com/anna+brian, щоб створити динамічну групову зустріч",
"user_has_no_team_yet": "У вас ще немає команди",
"no_team_members": "У вас ще немає учасників команди",
"hide_team_profile_link": "Приховати посилання на профіль команди",
"hide_team_profile_link_description": "Приховати посилання на профіль команди на сторінках бронювання",
"display": "Відображення",
"could_not_find_slug_to_publish_org": "Не вдалося знайти слаг для публікації організації",
"picklist": "Список вибору",
"name_or_email": "Ім'я або електронна пошта",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Якщо контакт не знайдено, використовувати власника ліда, якщо він існує",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "Đã xuất bản",
"unpublished": "Chưa xuất bản",
"publish": "Xuất bản",
"org_publish_error": "Không thể xuất bản tổ chức",
"troubleshooter_tooltip": "Mở trình khắc phục sự cố và tìm hiểu điều gì sai với lịch trình của bạn",
"need_help": "Cần hỗ trợ?",
"troubleshooter": "Trình khắc phục sự cố",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "Gói nhóm không hoạt động",
"inactive_team_plan_description": "Gói nhóm của bạn đang không hoạt động. Kiểm tra đăng ký của bạn hoặc liên hệ với bộ phận hỗ trợ khách hàng",
"limited_access_trial_mode": "Truy cập hạn chế trong thời gian dùng thử. Tính năng sẽ khả dụng sau khi kết thúc thời gian dùng thử.",
"earn_20_percent_affiliate": "Kiếm 20% tiền giới thiệu",
"referral_program": "Chương trình giới thiệu",
"dub_disabled_error_message": "Bạn cần Khóa API Dub và ID Chương trình để sử dụng trang này.",
"uid": "UID",
"link": "Liên kết",
"rows_per_page": "hàng mỗi trang",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "Mẹo: Bạn có thể thêm dấu '+' giữa các tên người dùng: cal.com/anna+brian để tạo cuộc họp nhóm động",
"user_has_no_team_yet": "Bạn chưa có nhóm nào",
"no_team_members": "Bạn chưa có thành viên nhóm nào",
"hide_team_profile_link": "Ẩn liên kết hồ sơ nhóm",
"hide_team_profile_link_description": "Ẩn liên kết hồ sơ nhóm trên trang đặt lịch",
"display": "Hiển thị",
"could_not_find_slug_to_publish_org": "Không tìm thấy slug để xuất bản tổ chức",
"picklist": "Danh sách chọn",
"name_or_email": "Tên hoặc email",
"salesforce_round_robin_skip_fallback_to_lead_owner": "Nếu không tìm thấy liên hệ, chuyển sang chủ sở hữu khách hàng tiềm năng nếu tồn tại",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "已发布",
"unpublished": "未发布",
"publish": "发布",
"org_publish_error": "无法发布组织",
"troubleshooter_tooltip": "打开问题排查器并找出您的日程安排有什么问题",
"need_help": "需要帮助?",
"troubleshooter": "问题排查器",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "非活跃团队计划",
"inactive_team_plan_description": "您的团队计划处于非活跃状态。请检查您的订阅或联系客户支持",
"limited_access_trial_mode": "试用期间访问受限。此功能将在试用结束后可用。",
"earn_20_percent_affiliate": "赚取20%联盟佣金",
"referral_program": "推荐计划",
"dub_disabled_error_message": "您需要Dub API密钥和程序ID才能使用此页面。",
"uid": "用户ID",
"link": "链接",
"rows_per_page": "每页行数",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "提示:您可以在用户名之间添加'+':cal.com/anna+brian 来创建动态群组会议",
"user_has_no_team_yet": "您还没有团队",
"no_team_members": "您还没有团队成员",
"hide_team_profile_link": "隐藏团队资料链接",
"hide_team_profile_link_description": "在预约页面上隐藏团队资料链接",
"display": "显示",
"could_not_find_slug_to_publish_org": "找不到要发布组织的短链接",
"picklist": "选择列表",
"name_or_email": "姓名或电子邮箱",
"salesforce_round_robin_skip_fallback_to_lead_owner": "如果未找到联系人,则在存在潜在客户所有者的情况下回退到潜在客户所有者",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 在此上方添加您的新字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
@@ -2452,7 +2452,6 @@
"published": "已發佈",
"unpublished": "未發佈",
"publish": "發佈",
"org_publish_error": "無法發佈組織",
"troubleshooter_tooltip": "打開故障排除工具,找出您的日程安排有什麼問題",
"need_help": "需要協助?",
"troubleshooter": "故障排除工具",
@@ -3009,6 +3008,9 @@
"inactive_team_plan": "未啟用的團隊方案",
"inactive_team_plan_description": "您的團隊方案未啟用。請檢查您的訂閱或聯繫客戶支援",
"limited_access_trial_mode": "試用期間功能受限。試用結束後即可使用完整功能。",
"earn_20_percent_affiliate": "賺取20%聯盟佣金",
"referral_program": "推薦計劃",
"dub_disabled_error_message": "您需要Dub API金鑰和程式ID才能使用此頁面。",
"uid": "UID",
"link": "連結",
"rows_per_page": "每頁行數",
@@ -3016,6 +3018,12 @@
"tip_username_plus": "提示:您可以在使用者名稱之間加上 '+':cal.com/anna+brian 來建立動態群組會議",
"user_has_no_team_yet": "您還沒有團隊",
"no_team_members": "您還沒有團隊成員",
"hide_team_profile_link": "隱藏團隊資料連結",
"hide_team_profile_link_description": "在預約頁面上隱藏團隊資料連結",
"display": "顯示",
"could_not_find_slug_to_publish_org": "找不到要發布組織的短網址",
"picklist": "選項清單",
"name_or_email": "姓名或電子郵件",
"salesforce_round_robin_skip_fallback_to_lead_owner": "如果找不到聯絡人,則回退到潛在客戶擁有者(如果存在)",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 請在此處新增您的字串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
+15 -9
View File
@@ -3,6 +3,20 @@ title: 'Introduction to API v2'
description: 'Introduction to Cal.com API v2 endpoints'
---
## Platform endpoints
Platform customers have the following endpoints available:
1. Endpoints prefixed with "Platform".
2. Endpoints with no prefix e.g "Bookings", "Event Types".
3. If you are at least on the ESSENTIALS plan, then all endpoints prefixed with "Orgs" except "Orgs / Attributes", "Orgs / Attributes / Options" and "Orgs / Teams / Routing forms / Responses".
## Organizations endpoints
Organizations customers have all the endpoints except the ones prefixed with "Platform" and "Teams" and "Orgs / Orgs" because
children organizations are only allowed in the platform plan right now.
## Teams endpoints
Teams customers have all the endpoints except the ones prefixed with "Platform" and "Orgs".
## Authentication
The Cal.com API has 3 authentication methods:
@@ -40,7 +54,7 @@ You need to use OAuth credentials when:
1. Managing managed users [API reference](https://cal.com/docs/api-reference/v2/platform-managed-users/create-a-managed-user)
2. Creating OAuth client webhooks [API reference](https://cal.com/docs/api-reference/v2/platform-webhooks/create-a-webhook)
3. Refreshing tokens of a managed user [API reference](https://cal.com/docs/api-reference/v2/oauth/post-v2oauth-refresh)
3. Refreshing tokens of a managed user [API reference](https://cal.com/docs/api-reference/v2/platform-managed-users/refresh-managed-user-tokens)
4. Teams related endpoints: Managing organization teams [API reference](https://cal.com/docs/api-reference/v2/orgs-teams/create-a-team), adding managed users as members to teams [API reference](https://cal.com/docs/api-reference/v2/orgs-teams-memberships/create-a-membership), creating team event types [API reference](https://cal.com/docs/api-reference/v2/orgs-event-types/create-an-event-type).
OAuth credentials can be accessed in the platform dashboard https://app.cal.com/settings/platform after you have created an OAuth client. Each one has an ID and secret. You then need to pass them as request headers:
@@ -63,11 +77,3 @@ Validity period: access tokens are valid for 60 minutes and refresh tokens for 1
Recovering tokens: if you ever lose managed user's access or refresh tokens, you can force refresh them using the OAuth client credentials and store them in your database [API reference](https://cal.com/docs/api-reference/v2/platform-managed-users/force-refresh-tokens).
## Navigating endpoints
Platform customers have the following endpoints available:
1. Endpoints prefixed with "Platform".
2. Endpoints with no prefix e.g "Bookings", "Event Types".
3. If you are at least on the ESSENTIALS plan, then all endpoints prefixed with "Orgs" except "Orgs / Attributes".
Non-platform customers have all the endpoints except the ones prefixed with "Platform".
File diff suppressed because it is too large Load Diff
+9 -1
View File
@@ -2449,7 +2449,6 @@ checksums:
published: ad5e035c44f71ed32d41e47c7643f32f
unpublished: 7239558b647feca27aac552977234c50
publish: 4aa95ba4793bb293e771bd73b4f87c0f
org_publish_error: d41336ed09d192987160a1f5f0440773
troubleshooter_tooltip: 09454c97ca0c80bd8d0485dd79ba0c86
need_help: 04e7322f2d3ffb2d73ff2f64b71637c8
troubleshooter: ac4a51cd3ebfacdd646583bf158235f5
@@ -3006,6 +3005,9 @@ checksums:
inactive_team_plan: 25f9f6f4b074eeec440bc61aa8638cf7
inactive_team_plan_description: 15de96578b521a59f65b7fa69e9a4888
limited_access_trial_mode: c501618906970336bf51e5d715efed8d
earn_20_percent_affiliate: 8805417839624540ef151441c68d8b02
referral_program: bc858f75f37326cc8cb8645d3bca6e4c
dub_disabled_error_message: 5f77315bfefcd9e3268a54ea96c9a7de
uid: 379cc5edfadebfc2d792e341bf7a190b
link: 60f0aa33e70ca6df3b6659802b16f433
rows_per_page: c95a0b385cad1e73b95619fe92a61cb6
@@ -3013,5 +3015,11 @@ checksums:
tip_username_plus: d3279ca90f8abdd965844998dd29bebc
user_has_no_team_yet: a8d58d1e2b62cfb97b61afd1b72385c4
no_team_members: a8329bca21c7026eb28cde53c0d2911a
hide_team_profile_link: e21a893cc987056b6fbdf83a4e3813a9
hide_team_profile_link_description: 8070f11216814e43b8b72c24eb8f9cfd
display: 9e4db8e7b3d215db7148240466ed796a
could_not_find_slug_to_publish_org: 86399c898effaf4d2b6113e91ffebb95
picklist: 5a3d44808605be31190c9956ecc5e412
name_or_email: d6e1cc7612211a2f7080b5096510f282
salesforce_round_robin_skip_fallback_to_lead_owner: 95ba66cdc5f1422c48d907e9d4d442b9
ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS: 18323f2f3fabb169a7cb50ff62433850
@@ -1,5 +1,4 @@
import { CAL_URL } from "@calcom/lib/constants";
import prisma from "@calcom/prisma";
import type { TextComponent } from "../lib";
@@ -27,91 +26,14 @@ export async function isValidCalURL(url: string) {
error,
};
const urlWithoutCal = url.replace(regex, "");
const response = await fetch(url);
const urlParts = urlWithoutCal.split("/");
const usernameOrTeamSlug = urlParts[0];
const eventTypeSlug = urlParts[1];
if (!usernameOrTeamSlug || !eventTypeSlug)
if (response.status !== 200)
return {
isValid: false,
error,
};
// Find all potential users with the given username
const potentialUsers = await prisma.user.findMany({
where: {
username: usernameOrTeamSlug,
},
include: {
eventTypes: {
where: {
slug: eventTypeSlug,
hidden: false,
},
},
},
});
// Find all potential teams with the given slug
const potentialTeams = await prisma.team.findMany({
where: {
slug: usernameOrTeamSlug,
},
include: {
eventTypes: {
where: {
slug: eventTypeSlug,
hidden: false,
},
},
},
});
// Check if any user has the matching eventTypeSlug
const matchingUser = potentialUsers.find((user) => user.eventTypes.length > 0);
// Check if any team has the matching eventTypeSlug
const matchingTeam = potentialTeams.find((team) => team.eventTypes.length > 0);
if (!matchingUser && !matchingTeam)
return {
isValid: false,
error: {
...error,
text: `${usernameOrTeamSlug} team or user is not valid.`,
},
};
const userOrTeam = matchingUser || matchingTeam;
if (!userOrTeam)
return {
isValid: false,
error,
};
// Retrieve the correct user or team
const userOrTeamId = userOrTeam.id;
const eventType = await prisma.eventType.findFirst({
where: {
userId: userOrTeamId,
slug: eventTypeSlug,
hidden: false,
},
});
if (!eventType)
return {
isValid: false,
error: {
...error,
text: `The event ${eventTypeSlug} doesn't exist.`,
},
};
return {
isValid: true,
};
@@ -1,7 +1,8 @@
import stripe from "@calcom/app-store/stripepayment/lib/server";
import { StripeBillingService } from "@calcom/features/ee/billing/stripe-billling-service";
export async function getCustomerAndCheckoutSession(checkoutSessionId: string) {
const checkoutSession = await stripe.checkout.sessions.retrieve(checkoutSessionId);
const billingService = new StripeBillingService();
const checkoutSession = await billingService.getCheckoutSession(checkoutSessionId);
const customerOrCustomerId = checkoutSession.customer;
let customerId = null;
@@ -16,7 +17,7 @@ export async function getCustomerAndCheckoutSession(checkoutSessionId: string) {
} else {
customerId = customerOrCustomerId.id;
}
const stripeCustomer = await stripe.customers.retrieve(customerId);
const stripeCustomer = await billingService.getCustomer(customerId);
if (stripeCustomer.deleted) {
return { checkoutSession, stripeCustomer: null };
}
@@ -274,6 +274,24 @@ test.describe("Popup Tests", () => {
pathname: configuredLink,
});
});
test("should open embed iframe on click - Configured with hide eventType details", async ({
page,
embeds,
}) => {
await deleteAllBookingsByEmail("embed-user@example.com");
const calNamespace = "popupHideEventTypeDetails";
await embeds.gotoPlayground({ calNamespace, url: "/" });
await page.click(`[data-cal-namespace="${calNamespace}"]`);
const embedIframe = await getEmbedIframe({ calNamespace, page, pathname: "/free/30min" });
await expect(embedIframe).toBeEmbedCalLink(calNamespace, embeds.getActionFiredDetails, {
pathname: "/free/30min",
});
});
});
async function expectPrerenderedIframe({
@@ -1,11 +1,11 @@
import type { NextApiResponse } from "next";
import stripe from "@calcom/app-store/stripepayment/lib/server";
import { getPremiumMonthlyPlanPriceId } from "@calcom/app-store/stripepayment/lib/utils";
import { hashPassword } from "@calcom/features/auth/lib/hashPassword";
import { sendEmailVerification } from "@calcom/features/auth/lib/verifyEmail";
import { createOrUpdateMemberships } from "@calcom/features/auth/signup/utils/createOrUpdateMemberships";
import { prefillAvatar } from "@calcom/features/auth/signup/utils/prefillAvatar";
import { StripeBillingService } from "@calcom/features/ee/billing/stripe-billling-service";
import { checkIfEmailIsBlockedInWatchlistController } from "@calcom/features/watchlist/operations/check-if-email-in-watchlist.controller";
import { WEBAPP_URL } from "@calcom/lib/constants";
import { getLocaleFromRequest } from "@calcom/lib/getLocaleFromRequest";
@@ -40,6 +40,8 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
})
.parse(req.body);
const billingService = new StripeBillingService();
const shouldLockByDefault = await checkIfEmailIsBlockedInWatchlistController(_email);
log.debug("handler", { email: _email });
@@ -96,7 +98,8 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
}
// Create the customer in Stripe
const customer = await stripe.customers.create({
const customer = await billingService.createCustomer({
email,
metadata: {
email /* Stripe customer email can be changed, so we add this to keep track of which email was used to signup */,
@@ -108,22 +111,18 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
// Pro username, must be purchased
if (req.usernameStatus.statusCode === 402) {
const checkoutSession = await stripe.checkout.sessions.create({
const checkoutSession = await billingService.createSubscriptionCheckout({
mode: "subscription",
customer: customer.id,
line_items: [
{
price: getPremiumMonthlyPlanPriceId(),
quantity: 1,
},
],
success_url: returnUrl,
cancel_url: returnUrl,
allow_promotion_codes: true,
customerId: customer.stripeCustomerId,
successUrl: returnUrl,
cancelUrl: returnUrl,
priceId: getPremiumMonthlyPlanPriceId(),
quantity: 1,
allowPromotionCodes: true,
});
/** We create a username-less user until he pays */
checkoutSessionId = checkoutSession.id;
checkoutSessionId = checkoutSession.sessionId;
username = null;
}
@@ -168,7 +167,7 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
},
});
// Wrapping in a transaction as if one fails we want to rollback the whole thing to preventa any data inconsistencies
const { membership } = await createOrUpdateMemberships({
await createOrUpdateMemberships({
user,
team,
});
@@ -190,14 +189,14 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
});
} else {
// Create the user
const user = await prisma.user.create({
await prisma.user.create({
data: {
username,
email,
locked: shouldLockByDefault,
password: { create: { hash: hashedPassword } },
metadata: {
stripeCustomerId: customer.id,
stripeCustomerId: customer.stripeCustomerId,
checkoutSessionId,
},
creationSource: CreationSource.WEBAPP,
@@ -221,7 +220,7 @@ async function handler(req: RequestWithUsernameStatus, res: NextApiResponse) {
});
}
return res.status(201).json({ message: "Created user", stripeCustomerId: customer.id });
return res.status(201).json({ message: "Created user", stripeCustomerId: customer.stripeCustomerId });
}
export default usernameHandler(handler);
@@ -11,30 +11,21 @@ import { getTimeMax, getTimeMin } from "./lib/datesForCache";
const log = logger.getSubLogger({ prefix: ["CalendarCacheRepository"] });
/** Enable or disable the expanded cache. Enabled by default. */
const ENABLE_EXPANDED_CACHE = process.env.ENABLE_EXPANDED_CACHE !== "0";
const MS_PER_DAY = 24 * 60 * 60 * 1000;
const ONE_MONTH_IN_MS = 30 * MS_PER_DAY;
const CACHING_TIME = ONE_MONTH_IN_MS;
export function parseKeyForCache(args: FreeBusyArgs): string {
const { timeMin: _timeMin, timeMax: _timeMax, items } = args;
function parseKeyForCache(args: FreeBusyArgs): string {
// Ensure that calendarIds are unique
const uniqueItems = uniqueBy(items, ["id"]);
const { timeMin, timeMax } = handleMinMax(_timeMin, _timeMax);
const key = JSON.stringify({ timeMin, timeMax, items: uniqueItems });
const uniqueItems = uniqueBy(args.items, ["id"]);
const key = JSON.stringify({
timeMin: getTimeMin(args.timeMin),
timeMax: getTimeMax(args.timeMax),
items: uniqueItems,
});
return key;
}
/**
* By expanding the cache to whole months, we can save round trips to the third party APIs.
* In this case we already have the data in the database, so we can just return it.
*/
function handleMinMax(min: string, max: string) {
if (!ENABLE_EXPANDED_CACHE) return { timeMin: min, timeMax: max };
return { timeMin: getTimeMin(min), timeMax: getTimeMax(max) };
}
type FreeBusyArgs = { timeMin: string; timeMax: string; items: { id: string }[] };
export class CalendarCacheRepository implements ICalendarCacheRepository {
@@ -0,0 +1,77 @@
import { describe, expect, it } from "vitest";
import { getTimeMin, getTimeMax } from "./datesForCache";
describe("getTimeMin", () => {
// Tested on multiple dates
// vi.setSystemTime("2030-04-21T00:00:13Z");
it("should return start of current month when no date is passed", () => {
const result = getTimeMin();
const expected = new Date();
expected.setUTCDate(1);
expected.setUTCHours(0, 0, 0, 0);
expect(result).toBe(expected.toISOString());
});
it("should return start of month for a given date", () => {
const result = getTimeMin("2025-03-15T10:30:00Z");
expect(result).toMatchInlineSnapshot(`"2025-03-01T00:00:00.000Z"`);
});
it("should handle dates at the start of month", () => {
const result = getTimeMin("2026-03-01T00:00:00Z");
expect(result).toMatchInlineSnapshot(`"2026-03-01T00:00:00.000Z"`);
});
it("should handle DST changes", () => {
const result = getTimeMin("2024-10-27T01:30:00Z");
expect(result).toMatchInlineSnapshot(`"2024-10-01T00:00:00.000Z"`);
});
});
describe("getTimeMax", () => {
it("should return the start of the overnext month when no date is passed", () => {
const result = getTimeMax();
const expected = new Date();
expected.setUTCMonth(expected.getUTCMonth() + 2);
expected.setUTCDate(1);
expected.setUTCHours(0, 0, 0, 0);
expect(result).toBe(expected.toISOString());
});
it("should return the start of overnext month for dates between start of current month and end of next month", () => {
const testDate = "2024-03-15T10:30:00Z";
const result = getTimeMax(testDate);
expect(result).toMatchInlineSnapshot(`"2024-05-01T00:00:00.000Z"`);
});
it("should return end of month for dates beyond overnext month", () => {
const testDate = "2024-05-15T10:30:00Z";
const result = getTimeMax(testDate);
expect(result).toMatchInlineSnapshot(`"2024-07-01T00:00:00.000Z"`);
});
it("should handle dates at the end of month", () => {
const testDate = "2024-03-31T23:59:59Z";
const result = getTimeMax(testDate);
expect(result).toMatchInlineSnapshot(`"2024-05-01T00:00:00.000Z"`);
});
it("should handle October correctly (31 days)", () => {
const testDate = "2024-10-15T10:30:00Z";
const result = getTimeMax(testDate);
expect(result).toMatchInlineSnapshot(`"2024-12-01T00:00:00.000Z"`);
});
it("should handle DST changes", () => {
const testDate = "2024-10-27T01:30:00Z"; // DST change in Europe
const result = getTimeMax(testDate);
expect(result).toMatchInlineSnapshot(`"2024-12-01T00:00:00.000Z"`);
});
it("should handle dates around DST changes in next month", () => {
const testDate = "2024-09-15T10:30:00Z"; // September, next month includes DST change
const result = getTimeMax(testDate);
expect(result).toMatchInlineSnapshot(`"2024-11-01T00:00:00.000Z"`);
});
});
@@ -1,16 +1,66 @@
/** Expand the start date to the beginning of the current month */
export const getTimeMin = (timeMin?: string) => {
const date = timeMin ? new Date(timeMin) : new Date();
date.setDate(1);
date.setHours(0, 0, 0, 0);
return date.toISOString();
// Set to UTC to avoid timezone issues
const result = new Date(Date.UTC(date.getUTCFullYear(), date.getUTCMonth(), 1));
result.setUTCHours(0, 0, 0, 0);
return result.toISOString();
};
/** Expand the end date to the end of the next month */
/**
* Expand the end date to the start of the overnext month if date
* is between start of current month and end of next month,
* otherwise return start of overnext month from the passed date
* @example
* Today: March 15, 2024 ▼-------------▼ getTimeMax returns May 1st 00:00:00
* Mar Apr May Jun
* ├────────┼────────┼────────┼────────┤
* Current Month █████████| | | |
* Next Month | █████████| | |
**/
export function getTimeMax(timeMax?: string) {
const date = timeMax ? new Date(timeMax) : new Date();
date.setMonth(date.getMonth() + 1);
date.setDate(0);
date.setHours(0, 0, 0, 0);
return date.toISOString();
const now = new Date();
const currentMonth = now.getUTCMonth();
const currentYear = now.getUTCFullYear();
if (!timeMax) {
// If no date passed, return start of the overnext month from current date
const result = new Date(Date.UTC(currentYear, currentMonth + 2, 1));
result.setUTCHours(0, 0, 0, 0);
return result.toISOString();
}
const date = new Date(timeMax);
const dateMonth = date.getUTCMonth();
const dateYear = date.getUTCFullYear();
// If the date is within current month or next month, return start of overnext month
// Otherwise, return start of overnext month from the date
let targetYear = dateYear;
let targetMonth = dateMonth;
// Check if date is within current month or next month
const isWithinCurrentOrNextMonth =
(dateYear === currentYear && dateMonth <= currentMonth + 1) ||
(dateYear === currentYear + 1 && currentMonth === 11 && dateMonth === 0);
if (isWithinCurrentOrNextMonth) {
// For dates within current month or next month, return start of overnext month
targetMonth = dateMonth + 2;
if (targetMonth > 11) {
targetMonth = 0;
targetYear++;
}
} else {
// For dates beyond overnext month, return start of overnext month from the date
targetMonth = dateMonth + 2;
if (targetMonth > 11) {
targetMonth = 0;
targetYear++;
}
}
const result = new Date(Date.UTC(targetYear, targetMonth, 1));
result.setUTCHours(0, 0, 0, 0);
return result.toISOString();
}
@@ -1,23 +1,26 @@
"use client";
import type { SortingState, OnChangeFn, VisibilityState } from "@tanstack/react-table";
import type { SortingState, OnChangeFn, VisibilityState, ColumnSizingState } from "@tanstack/react-table";
import { usePathname } from "next/navigation";
import { useQueryState, parseAsArrayOf, parseAsJson, parseAsInteger } from "nuqs";
import { createContext, useCallback, useRef, useEffect } from "react";
import { z } from "zod";
import { createContext, useCallback, useEffect, useRef } from "react";
import { type FilterValue, ZFilterValue, ZSorting, ZColumnVisibility } from "./lib/types";
const ZActiveFilter = z.object({
f: z.string(),
v: ZFilterValue.optional(),
});
type ActiveFilter = z.infer<typeof ZActiveFilter>;
import { useSegments } from "./lib/segments";
import {
type FilterValue,
ZSorting,
ZColumnVisibility,
ZActiveFilter,
ZColumnSizing,
type FilterSegmentOutput,
type ActiveFilters,
} from "./lib/types";
export type DataTableContextType = {
tableIdentifier: string;
ctaContainerRef?: React.RefObject<HTMLDivElement>;
activeFilters: ActiveFilter[];
activeFilters: ActiveFilters;
clearAll: (exclude?: string[]) => void;
addFilter: (columnId: string) => void;
updateFilter: (columnId: string, value: FilterValue) => void;
@@ -29,29 +32,41 @@ export type DataTableContextType = {
columnVisibility: VisibilityState;
setColumnVisibility: OnChangeFn<VisibilityState>;
columnSizing: ColumnSizingState;
setColumnSizing: OnChangeFn<ColumnSizingState>;
pageIndex: number;
pageSize: number;
setPageIndex: (pageIndex: number) => void;
setPageSize: (pageSize: number) => void;
setPageIndex: (pageIndex: number | null) => void;
setPageSize: (pageSize: number | null) => void;
offset: number;
limit: number;
segments: FilterSegmentOutput[];
selectedSegment: FilterSegmentOutput | undefined;
segmentId: number | undefined;
setSegmentId: (id: number | null) => void;
canSaveSegment: boolean;
};
export const DataTableContext = createContext<DataTableContextType | null>(null);
const DEFAULT_ACTIVE_FILTERS: ActiveFilter[] = [];
const DEFAULT_ACTIVE_FILTERS: ActiveFilters = [];
const DEFAULT_SORTING: SortingState = [];
const DEFAULT_COLUMN_VISIBILITY: VisibilityState = {};
const DEFAULT_COLUMN_SIZING: ColumnSizingState = {};
const DEFAULT_PAGE_SIZE = 10;
interface DataTableProviderProps {
tableIdentifier?: string;
children: React.ReactNode;
ctaContainerClassName?: string;
defaultPageSize?: number;
}
export function DataTableProvider({
tableIdentifier: _tableIdentifier,
children,
defaultPageSize = DEFAULT_PAGE_SIZE,
ctaContainerClassName,
@@ -68,13 +83,25 @@ export function DataTableProvider({
"cols",
parseAsJson(ZColumnVisibility.parse).withDefault(DEFAULT_COLUMN_VISIBILITY)
);
const [columnSizing, setColumnSizing] = useQueryState<ColumnSizingState>(
"widths",
parseAsJson(ZColumnSizing.parse).withDefault(DEFAULT_COLUMN_SIZING)
);
const [segmentId, setSegmentId] = useQueryState("segment", parseAsInteger.withDefault(-1));
const [pageIndex, setPageIndex] = useQueryState("page", parseAsInteger.withDefault(0));
const [pageSize, setPageSize] = useQueryState("size", parseAsInteger.withDefault(defaultPageSize));
const pathname = usePathname() as string | null;
const tableIdentifier = _tableIdentifier ?? pathname ?? undefined;
if (!tableIdentifier) {
throw new Error("tableIdentifier is required");
}
const addFilter = useCallback(
(columnId: string) => {
if (!activeFilters?.some((filter) => filter.f === columnId)) {
// do not reset the page to 0 here,
// because we don't have the filter value yet (`v: undefined`)
setActiveFilters([...activeFilters, { f: columnId, v: undefined }]);
}
},
@@ -83,15 +110,23 @@ export function DataTableProvider({
const clearAll = useCallback(
(exclude?: string[]) => {
setPageIndex(0);
setActiveFilters((prev) => prev.filter((filter) => exclude?.includes(filter.f)));
setPageIndex(null);
setActiveFilters((prev) => {
const remainingFilters = prev.filter((filter) => exclude?.includes(filter.f));
return remainingFilters.length === 0 ? null : remainingFilters;
});
},
[setActiveFilters]
[setActiveFilters, setPageIndex]
);
const setPageIndexWrapper = useCallback(
(newPageIndex: number | null) => setPageIndex(newPageIndex || null),
[setPageIndex]
);
const updateFilter = useCallback(
(columnId: string, value: FilterValue) => {
setPageIndex(0);
setPageIndex(null);
setActiveFilters((prev) => {
let added = false;
const newFilters = prev.map((item) => {
@@ -107,25 +142,46 @@ export function DataTableProvider({
return newFilters;
});
},
[setActiveFilters]
[setActiveFilters, setPageIndex]
);
const removeFilter = useCallback(
(columnId: string) => {
setPageIndex(0);
setActiveFilters((prev) => prev.filter((filter) => filter.f !== columnId));
setPageIndex(null);
setActiveFilters((prev) => {
const remainingFilters = prev.filter((filter) => filter.f !== columnId);
return remainingFilters.length === 0 ? null : remainingFilters;
});
},
[setActiveFilters]
[setActiveFilters, setPageIndex]
);
const setPageSizeAndGoToFirstPage = useCallback(
(newPageSize: number) => {
setPageSize(newPageSize);
setPageIndex(0);
(newPageSize: number | null) => {
setPageSize(newPageSize === DEFAULT_PAGE_SIZE ? null : newPageSize);
setPageIndex(null);
},
[setPageSize, setPageIndex]
);
const { segments, selectedSegment, canSaveSegment, setSegmentIdAndSaveToLocalStorage } = useSegments({
tableIdentifier,
activeFilters,
sorting,
columnVisibility,
columnSizing,
pageSize,
defaultPageSize,
segmentId,
setSegmentId,
setActiveFilters,
setSorting,
setColumnVisibility,
setColumnSizing,
setPageSize,
setPageIndex,
});
const ctaContainerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (ctaContainerClassName) {
@@ -137,6 +193,7 @@ export function DataTableProvider({
return (
<DataTableContext.Provider
value={{
tableIdentifier,
ctaContainerRef,
activeFilters,
addFilter,
@@ -147,12 +204,19 @@ export function DataTableProvider({
setSorting,
columnVisibility,
setColumnVisibility,
columnSizing,
setColumnSizing,
pageIndex,
pageSize,
setPageIndex,
setPageIndex: setPageIndexWrapper,
setPageSize: setPageSizeAndGoToFirstPage,
limit: pageSize,
offset: pageIndex * pageSize,
segments,
selectedSegment,
segmentId: segmentId || undefined,
setSegmentId: setSegmentIdAndSaveToLocalStorage,
canSaveSegment,
}}>
{children}
</DataTableContext.Provider>
@@ -6,7 +6,6 @@ import type { Table as ReactTableType, Header, HeaderGroup } from "@tanstack/rea
import { useVirtualizer, type Virtualizer, type VirtualItem } from "@tanstack/react-virtual";
// eslint-disable-next-line no-restricted-imports
import kebabCase from "lodash/kebabCase";
import { usePathname } from "next/navigation";
import { useEffect, useState, memo, useMemo } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -34,6 +33,7 @@ export type DataTablePropsFromWrapper<TData> = {
testId?: string;
bodyTestId?: string;
children?: React.ReactNode;
enableColumnResizing?: boolean;
className?: string;
containerClassName?: string;
headerClassName?: string;
@@ -45,7 +45,6 @@ export type DataTableProps<TData> = DataTablePropsFromWrapper<TData> & {
onRowMouseclick?: (row: Row<TData>) => void;
onScroll?: (e: Pick<React.UIEvent<HTMLDivElement, UIEvent>, "target">) => void;
tableOverlay?: React.ReactNode;
identifier?: string;
enableColumnResizing?: boolean;
};
@@ -57,7 +56,6 @@ export function DataTable<TData>({
onRowMouseclick,
onScroll,
children,
identifier: _identifier,
enableColumnResizing,
testId,
bodyTestId,
@@ -68,9 +66,6 @@ export function DataTable<TData>({
paginationMode = "infinite",
...rest
}: DataTableProps<TData> & React.ComponentPropsWithoutRef<"div">) {
const pathname = usePathname() as string | null;
const identifier = _identifier ?? pathname ?? undefined;
const { rows } = table.getRowModel();
const rowVirtualizer = useVirtualizer({
@@ -102,10 +97,9 @@ export function DataTable<TData>({
const columnSizingVars = useColumnSizingVars({ table });
usePersistentColumnResizing({
enabled: Boolean(enableColumnResizing && identifier),
enabled: Boolean(enableColumnResizing),
table,
tableContainerRef,
identifier,
});
return (

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