Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42cbd582f2 | ||
|
|
5481248c1a | ||
|
|
bd4a949a7c | ||
|
|
b768cc5b44 | ||
|
|
de80ca2aaa | ||
|
|
4afbe26506 | ||
|
|
f22d585c7d | ||
|
|
1ee4fa0906 | ||
|
|
1fc376c51a | ||
|
|
9382b862e4 | ||
|
|
c2662638a5 | ||
|
|
c2291164fa | ||
|
|
9d01025a1f | ||
|
|
86af6bd626 | ||
|
|
2f4dd328c3 | ||
|
|
6df994389b | ||
|
|
93e93a9c7a | ||
|
|
9087f5307b | ||
|
|
5f6032131b | ||
|
|
4e90b65884 | ||
|
|
b7792d0d0a | ||
|
|
c8cd260112 | ||
|
|
242268d580 | ||
|
|
3999f84eff | ||
|
|
995bf09dde | ||
|
|
59aff41ca3 | ||
|
|
b0e3e4c82f | ||
|
|
3e0d53228e | ||
|
|
df2ef84251 | ||
|
|
dcd07d1cc6 | ||
|
|
5f0cc805a9 | ||
|
|
c5677f1dd9 | ||
|
|
bf9be5915c |
@@ -5,6 +5,7 @@
|
||||
# - DAILY.CO VIDEO
|
||||
# - GOOGLE CALENDAR/MEET/LOGIN
|
||||
# - HUBSPOT
|
||||
# - HUDDLE01
|
||||
# - OFFICE 365
|
||||
# - SLACK
|
||||
# - STRIPE
|
||||
@@ -137,3 +138,7 @@ REVERT_PUBLIC_TOKEN=
|
||||
# NOTE: If you're self hosting Revert, update this URL to point to your own instance.
|
||||
REVERT_API_URL=https://api.revert.dev/
|
||||
# *********************************************************************************************************
|
||||
|
||||
# - Huddle01
|
||||
# Used for the huddle01 integration
|
||||
HUDDLE01_API_TOKEN=
|
||||
|
||||
@@ -66,11 +66,8 @@ describe("GET /api/slots", () => {
|
||||
buildMockData();
|
||||
await handler(req, res);
|
||||
console.log({ statusCode: res._getStatusCode(), data: JSON.parse(res._getData()) });
|
||||
expect(JSON.parse(res._getData())).toMatchInlineSnapshot(`
|
||||
{
|
||||
"slots": {},
|
||||
}
|
||||
`);
|
||||
const response = JSON.parse(res._getData());
|
||||
expect(response.slots).toEqual(expect.objectContaining({}));
|
||||
});
|
||||
test("Returns and event type available slots with passed timeZone", async () => {
|
||||
const { req, res } = createMocks<CustomNextApiRequest, CustomNextApiResponse>({
|
||||
@@ -87,11 +84,8 @@ describe("GET /api/slots", () => {
|
||||
buildMockData();
|
||||
await handler(req, res);
|
||||
console.log({ statusCode: res._getStatusCode(), data: JSON.parse(res._getData()) });
|
||||
expect(JSON.parse(res._getData())).toMatchInlineSnapshot(`
|
||||
{
|
||||
"slots": {},
|
||||
}
|
||||
`);
|
||||
const response = JSON.parse(res._getData());
|
||||
expect(response.slots).toEqual(expect.objectContaining({}));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -28,7 +28,7 @@
|
||||
"dependencies": {
|
||||
"@calcom/platform-constants": "*",
|
||||
"@calcom/platform-enums": "*",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.46",
|
||||
"@calcom/platform-libraries": "npm:@calcom/platform-libraries@0.0.51",
|
||||
"@calcom/platform-libraries-0.0.2": "npm:@calcom/platform-libraries@0.0.2",
|
||||
"@calcom/platform-types": "*",
|
||||
"@calcom/platform-utils": "*",
|
||||
|
||||
@@ -355,13 +355,14 @@ export class BookingsController_2024_04_15 {
|
||||
oAuthClientId?: string,
|
||||
platformBookingLocation?: string
|
||||
): Promise<NextApiRequest & { userId?: number } & OAuthRequestParams> {
|
||||
const clone = { ...req };
|
||||
const userId = (await this.getOwnerId(req)) ?? -1;
|
||||
const oAuthParams = oAuthClientId
|
||||
? await this.getOAuthClientsParams(oAuthClientId)
|
||||
: DEFAULT_PLATFORM_PARAMS;
|
||||
Object.assign(req, { userId, ...oAuthParams, platformBookingLocation });
|
||||
req.body = { ...req.body, noEmail: !oAuthParams.arePlatformEmailsEnabled };
|
||||
return req as unknown as NextApiRequest & { userId?: number } & OAuthRequestParams;
|
||||
Object.assign(clone, { userId, ...oAuthParams, platformBookingLocation });
|
||||
clone.body = { ...clone.body, noEmail: !oAuthParams.arePlatformEmailsEnabled };
|
||||
return clone as unknown as NextApiRequest & { userId?: number } & OAuthRequestParams;
|
||||
}
|
||||
|
||||
private async createNextApiRecurringBookingRequest(
|
||||
@@ -369,17 +370,18 @@ export class BookingsController_2024_04_15 {
|
||||
oAuthClientId?: string,
|
||||
platformBookingLocation?: string
|
||||
): Promise<NextApiRequest & { userId?: number } & OAuthRequestParams> {
|
||||
const clone = { ...req };
|
||||
const userId = (await this.getOwnerId(req)) ?? -1;
|
||||
const oAuthParams = oAuthClientId
|
||||
? await this.getOAuthClientsParams(oAuthClientId)
|
||||
: DEFAULT_PLATFORM_PARAMS;
|
||||
Object.assign(req, {
|
||||
Object.assign(clone, {
|
||||
userId,
|
||||
...oAuthParams,
|
||||
platformBookingLocation,
|
||||
noEmail: !oAuthParams.arePlatformEmailsEnabled,
|
||||
});
|
||||
return req as unknown as NextApiRequest & { userId?: number } & OAuthRequestParams;
|
||||
return clone as unknown as NextApiRequest & { userId?: number } & OAuthRequestParams;
|
||||
}
|
||||
|
||||
private handleBookingErrors(
|
||||
|
||||
@@ -6,6 +6,7 @@ import { OutputBookingsService_2024_08_13 } from "@/ee/bookings/2024-08-13/servi
|
||||
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
|
||||
import { ApiKeyRepository } from "@/modules/api-key/api-key-repository";
|
||||
import { BillingModule } from "@/modules/billing/billing.module";
|
||||
import { BookingSeatModule } from "@/modules/booking-seat/booking-seat.module";
|
||||
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
|
||||
import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
@@ -16,7 +17,7 @@ import { UsersModule } from "@/modules/users/users.module";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule, RedisModule, TokensModule, BillingModule, UsersModule],
|
||||
imports: [PrismaModule, RedisModule, TokensModule, BillingModule, UsersModule, BookingSeatModule],
|
||||
providers: [
|
||||
TokensRepository,
|
||||
OAuthFlowService,
|
||||
|
||||
@@ -29,6 +29,25 @@ export class BookingsRepository_2024_08_13 {
|
||||
});
|
||||
}
|
||||
|
||||
async getByIdsWithAttendeesWithBookingSeatAndUserAndEvent(ids: number[]) {
|
||||
return this.dbRead.prisma.booking.findMany({
|
||||
where: {
|
||||
id: {
|
||||
in: ids,
|
||||
},
|
||||
},
|
||||
include: {
|
||||
attendees: {
|
||||
include: {
|
||||
bookingSeat: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
eventType: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getByUid(bookingUid: string) {
|
||||
return this.dbRead.prisma.booking.findUnique({
|
||||
where: {
|
||||
@@ -50,6 +69,23 @@ export class BookingsRepository_2024_08_13 {
|
||||
});
|
||||
}
|
||||
|
||||
async getByIdWithAttendeesWithBookingSeatAndUserAndEvent(id: number) {
|
||||
return this.dbRead.prisma.booking.findUnique({
|
||||
where: {
|
||||
id,
|
||||
},
|
||||
include: {
|
||||
attendees: {
|
||||
include: {
|
||||
bookingSeat: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
eventType: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getByUidWithAttendeesAndUserAndEvent(uid: string) {
|
||||
return this.dbRead.prisma.booking.findUnique({
|
||||
where: {
|
||||
@@ -63,6 +99,31 @@ export class BookingsRepository_2024_08_13 {
|
||||
});
|
||||
}
|
||||
|
||||
async getByUidWithAttendeesWithBookingSeatAndUserAndEvent(uid: string) {
|
||||
return this.dbRead.prisma.booking.findUnique({
|
||||
where: {
|
||||
uid,
|
||||
},
|
||||
include: {
|
||||
attendees: {
|
||||
include: {
|
||||
bookingSeat: true,
|
||||
},
|
||||
},
|
||||
user: true,
|
||||
eventType: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getRecurringByUid(uid: string) {
|
||||
return this.dbRead.prisma.booking.findMany({
|
||||
where: {
|
||||
recurringEventId: uid,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getRecurringByUidWithAttendeesAndUserAndEvent(uid: string) {
|
||||
return this.dbRead.prisma.booking.findMany({
|
||||
where: {
|
||||
|
||||
@@ -34,7 +34,14 @@ import { User } from "@prisma/client";
|
||||
import { Request } from "express";
|
||||
|
||||
import { BOOKING_READ, BOOKING_WRITE, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { GetBookingOutput_2024_08_13, GetBookingsOutput_2024_08_13 } from "@calcom/platform-types";
|
||||
import {
|
||||
CancelBookingInput,
|
||||
CancelBookingInputPipe,
|
||||
GetBookingOutput_2024_08_13,
|
||||
GetBookingsOutput_2024_08_13,
|
||||
RescheduleBookingInput,
|
||||
RescheduleBookingInputPipe,
|
||||
} from "@calcom/platform-types";
|
||||
import {
|
||||
CreateBookingInputPipe,
|
||||
CreateBookingInput,
|
||||
@@ -166,7 +173,8 @@ export class BookingsController_2024_08_13 {
|
||||
})
|
||||
async rescheduleBooking(
|
||||
@Param("bookingUid") bookingUid: string,
|
||||
@Body() body: RescheduleBookingInput_2024_08_13,
|
||||
@Body(new RescheduleBookingInputPipe())
|
||||
body: RescheduleBookingInput,
|
||||
@Req() request: Request
|
||||
): Promise<RescheduleBookingOutput_2024_08_13> {
|
||||
const newBooking = await this.bookingsService.rescheduleBooking(request, bookingUid, body);
|
||||
@@ -185,7 +193,8 @@ export class BookingsController_2024_08_13 {
|
||||
async cancelBooking(
|
||||
@Req() request: Request,
|
||||
@Param("bookingUid") bookingUid: string,
|
||||
@Body() body: CancelBookingInput_2024_08_13
|
||||
@Body(new CancelBookingInputPipe())
|
||||
body: CancelBookingInput
|
||||
): Promise<CancelBookingOutput_2024_08_13> {
|
||||
const cancelledBooking = await this.bookingsService.cancelBooking(request, bookingUid, body);
|
||||
|
||||
|
||||
@@ -0,0 +1,645 @@
|
||||
import { bootstrap } from "@/app";
|
||||
import { AppModule } from "@/app.module";
|
||||
import { CreateBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/create-booking.output";
|
||||
import { RescheduleBookingOutput_2024_08_13 } from "@/ee/bookings/2024-08-13/outputs/reschedule-booking.output";
|
||||
import { CreateScheduleInput_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/inputs/create-schedule.input";
|
||||
import { SchedulesModule_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/schedules.module";
|
||||
import { SchedulesService_2024_04_15 } from "@/ee/schedules/schedules_2024_04_15/services/schedules.service";
|
||||
import { PermissionsGuard } from "@/modules/auth/guards/permissions/permissions.guard";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { UsersModule } from "@/modules/users/users.module";
|
||||
import { INestApplication } from "@nestjs/common";
|
||||
import { NestExpressApplication } from "@nestjs/platform-express";
|
||||
import { Test } from "@nestjs/testing";
|
||||
import { User } from "@prisma/client";
|
||||
import { DateTime } from "luxon";
|
||||
import * as request from "supertest";
|
||||
import { BookingsRepositoryFixture } from "test/fixtures/repository/bookings.repository.fixture";
|
||||
import { EventTypesRepositoryFixture } from "test/fixtures/repository/event-types.repository.fixture";
|
||||
import { OAuthClientRepositoryFixture } from "test/fixtures/repository/oauth-client.repository.fixture";
|
||||
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
|
||||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
|
||||
import { withApiAuth } from "test/utils/withApiAuth";
|
||||
|
||||
import { CAL_API_VERSION_HEADER, SUCCESS_STATUS, VERSION_2024_08_13 } from "@calcom/platform-constants";
|
||||
import {
|
||||
CancelSeatedBookingInput_2024_08_13,
|
||||
CreateRecurringSeatedBookingOutput_2024_08_13,
|
||||
CreateSeatedBookingOutput_2024_08_13,
|
||||
GetBookingOutput_2024_08_13,
|
||||
GetBookingsOutput_2024_08_13,
|
||||
GetRecurringSeatedBookingOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
RescheduleSeatedBookingInput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
import {
|
||||
CreateBookingInput_2024_08_13,
|
||||
CreateRecurringBookingInput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
import { PlatformOAuthClient, Team } from "@calcom/prisma/client";
|
||||
|
||||
describe("Bookings Endpoints 2024-08-13", () => {
|
||||
describe("Seated bookings", () => {
|
||||
let app: INestApplication;
|
||||
let organization: Team;
|
||||
|
||||
let userRepositoryFixture: UserRepositoryFixture;
|
||||
let bookingsRepositoryFixture: BookingsRepositoryFixture;
|
||||
let schedulesService: SchedulesService_2024_04_15;
|
||||
let eventTypesRepositoryFixture: EventTypesRepositoryFixture;
|
||||
let oauthClientRepositoryFixture: OAuthClientRepositoryFixture;
|
||||
let oAuthClient: PlatformOAuthClient;
|
||||
let teamRepositoryFixture: TeamRepositoryFixture;
|
||||
|
||||
const userEmail = "seated-bookings-controller-e2e@api.com";
|
||||
let user: User;
|
||||
|
||||
const seatedTventTypeSlug = "peer-coding-seated";
|
||||
const recurringSeatedTventTypeSlug = "peer-coding-recurring-seated";
|
||||
let seatedEventTypeId: number;
|
||||
let recurringSeatedEventTypeId: number;
|
||||
const maxRecurrenceCount = 3;
|
||||
|
||||
let createdSeatedBooking: CreateSeatedBookingOutput_2024_08_13;
|
||||
let createdRecurringSeatedBooking: CreateRecurringSeatedBookingOutput_2024_08_13[];
|
||||
|
||||
const emailAttendeeOne = "mr_proper_seated@gmail.com";
|
||||
const nameAttendeeOne = "Mr Proper Seated";
|
||||
const emailAttendeeTwo = "mr_proper_friend_seated@gmail.com";
|
||||
const nameAttendeeTwo = "Mr Proper Friend Seated";
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, SchedulesModule_2024_04_15],
|
||||
})
|
||||
)
|
||||
.overrideGuard(PermissionsGuard)
|
||||
.useValue({
|
||||
canActivate: () => true,
|
||||
})
|
||||
.compile();
|
||||
|
||||
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
||||
bookingsRepositoryFixture = new BookingsRepositoryFixture(moduleRef);
|
||||
eventTypesRepositoryFixture = new EventTypesRepositoryFixture(moduleRef);
|
||||
oauthClientRepositoryFixture = new OAuthClientRepositoryFixture(moduleRef);
|
||||
teamRepositoryFixture = new TeamRepositoryFixture(moduleRef);
|
||||
schedulesService = moduleRef.get<SchedulesService_2024_04_15>(SchedulesService_2024_04_15);
|
||||
|
||||
organization = await teamRepositoryFixture.create({ name: "organization bookings" });
|
||||
oAuthClient = await createOAuthClient(organization.id);
|
||||
|
||||
user = await userRepositoryFixture.create({
|
||||
email: userEmail,
|
||||
});
|
||||
|
||||
const userSchedule: CreateScheduleInput_2024_04_15 = {
|
||||
name: "working time",
|
||||
timeZone: "Europe/Rome",
|
||||
isDefault: true,
|
||||
};
|
||||
await schedulesService.createUserSchedule(user.id, userSchedule);
|
||||
const seatedEvent = await eventTypesRepositoryFixture.create(
|
||||
{
|
||||
title: "peer coding",
|
||||
slug: seatedTventTypeSlug,
|
||||
length: 60,
|
||||
seatsPerTimeSlot: 5,
|
||||
seatsShowAttendees: true,
|
||||
seatsShowAvailabilityCount: true,
|
||||
locations: [{ type: "inPerson", address: "via 10, rome, italy" }],
|
||||
},
|
||||
user.id
|
||||
);
|
||||
seatedEventTypeId = seatedEvent.id;
|
||||
|
||||
const recurringSeatedEvent = await eventTypesRepositoryFixture.create(
|
||||
// note(Lauris): freq 2 means weekly, interval 1 means every week and count 3 means 3 weeks in a row
|
||||
{
|
||||
title: "peer coding recurring",
|
||||
slug: recurringSeatedTventTypeSlug,
|
||||
length: 60,
|
||||
recurringEvent: { freq: 2, count: maxRecurrenceCount, interval: 1 },
|
||||
seatsPerTimeSlot: 5,
|
||||
seatsShowAttendees: true,
|
||||
seatsShowAvailabilityCount: true,
|
||||
locations: [{ type: "inPerson", address: "via 10, rome, italy" }],
|
||||
},
|
||||
user.id
|
||||
);
|
||||
recurringSeatedEventTypeId = recurringSeatedEvent.id;
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
bootstrap(app as NestExpressApplication);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
async function createOAuthClient(organizationId: number) {
|
||||
const data = {
|
||||
logo: "logo-url",
|
||||
name: "name",
|
||||
redirectUris: ["http://localhost:5555"],
|
||||
permissions: 32,
|
||||
};
|
||||
const secret = "secret";
|
||||
|
||||
const client = await oauthClientRepositoryFixture.create(organizationId, data, secret);
|
||||
return client;
|
||||
}
|
||||
|
||||
it("should book an event type with seats for the first time", async () => {
|
||||
const body: CreateBookingInput_2024_08_13 = {
|
||||
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
|
||||
eventTypeId: seatedEventTypeId,
|
||||
attendee: {
|
||||
name: nameAttendeeOne,
|
||||
email: emailAttendeeOne,
|
||||
timeZone: "Europe/Rome",
|
||||
language: "it",
|
||||
},
|
||||
bookingFieldsResponses: {
|
||||
codingLanguage: "TypeScript",
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post("/v2/bookings")
|
||||
.send(body)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(201)
|
||||
.then(async (response) => {
|
||||
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsCreateSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsCreateSeatedBooking(responseBody.data)) {
|
||||
const data: CreateSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||
expect(data.seatUid).toBeDefined();
|
||||
const seatUid = data.seatUid;
|
||||
expect(data.id).toBeDefined();
|
||||
expect(data.uid).toBeDefined();
|
||||
expect(data.hosts[0].id).toEqual(user.id);
|
||||
expect(data.status).toEqual("accepted");
|
||||
expect(data.start).toEqual(body.start);
|
||||
expect(data.end).toEqual(
|
||||
DateTime.fromISO(body.start, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||
);
|
||||
expect(data.duration).toEqual(60);
|
||||
expect(data.eventTypeId).toEqual(seatedEventTypeId);
|
||||
expect(data.eventType).toEqual({
|
||||
id: seatedEventTypeId,
|
||||
slug: seatedTventTypeSlug,
|
||||
});
|
||||
expect(data.attendees.length).toEqual(1);
|
||||
expect(data.attendees[0]).toEqual({
|
||||
name: body.attendee.name,
|
||||
timeZone: body.attendee.timeZone,
|
||||
language: body.attendee.language,
|
||||
absent: false,
|
||||
seatUid,
|
||||
bookingFieldsResponses: {
|
||||
name: body.attendee.name,
|
||||
...body.bookingFieldsResponses,
|
||||
},
|
||||
});
|
||||
expect(data.location).toBeDefined();
|
||||
expect(data.absentHost).toEqual(false);
|
||||
createdSeatedBooking = data;
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid response data - expected recurring booking but received non array response"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should book an event type with seats for the second time", async () => {
|
||||
const body: CreateBookingInput_2024_08_13 = {
|
||||
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
|
||||
eventTypeId: seatedEventTypeId,
|
||||
attendee: {
|
||||
name: nameAttendeeTwo,
|
||||
email: emailAttendeeTwo,
|
||||
timeZone: "Europe/Rome",
|
||||
language: "it",
|
||||
},
|
||||
bookingFieldsResponses: {
|
||||
codingLanguage: "Rust",
|
||||
},
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post("/v2/bookings")
|
||||
.send(body)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(201)
|
||||
.then(async (response) => {
|
||||
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsCreateSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsCreateSeatedBooking(responseBody.data)) {
|
||||
const data: CreateSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||
expect(data.seatUid).toBeDefined();
|
||||
const seatUid = data.seatUid;
|
||||
expect(data.id).toBeDefined();
|
||||
expect(data.uid).toBeDefined();
|
||||
expect(data.hosts[0].id).toEqual(user.id);
|
||||
expect(data.status).toEqual("accepted");
|
||||
expect(data.start).toEqual(body.start);
|
||||
expect(data.end).toEqual(
|
||||
DateTime.fromISO(body.start, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||
);
|
||||
expect(data.duration).toEqual(60);
|
||||
expect(data.eventTypeId).toEqual(seatedEventTypeId);
|
||||
expect(data.eventType).toEqual({
|
||||
id: seatedEventTypeId,
|
||||
slug: seatedTventTypeSlug,
|
||||
});
|
||||
expect(data.attendees.length).toEqual(2);
|
||||
// note(Lauris): first attendee is from previous test request
|
||||
const firstAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeOne);
|
||||
expect(firstAttendee).toEqual({
|
||||
name: createdSeatedBooking.attendees[0].name,
|
||||
timeZone: createdSeatedBooking.attendees[0].timeZone,
|
||||
language: createdSeatedBooking.attendees[0].language,
|
||||
absent: false,
|
||||
seatUid: createdSeatedBooking.seatUid,
|
||||
bookingFieldsResponses: {
|
||||
name: createdSeatedBooking.attendees[0].name,
|
||||
...createdSeatedBooking.attendees[0].bookingFieldsResponses,
|
||||
},
|
||||
});
|
||||
const secondAttendee = data.attendees.find((attendee) => attendee.name === nameAttendeeTwo);
|
||||
expect(secondAttendee).toEqual({
|
||||
name: body.attendee.name,
|
||||
timeZone: body.attendee.timeZone,
|
||||
language: body.attendee.language,
|
||||
absent: false,
|
||||
seatUid,
|
||||
bookingFieldsResponses: {
|
||||
name: body.attendee.name,
|
||||
...body.bookingFieldsResponses,
|
||||
},
|
||||
});
|
||||
expect(data.location).toBeDefined();
|
||||
expect(data.absentHost).toEqual(false);
|
||||
createdSeatedBooking = data;
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid response data - expected recurring booking but received non array response"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should book a recurring event type with seats", async () => {
|
||||
const recurrenceCount = 2;
|
||||
const body: CreateRecurringBookingInput_2024_08_13 = {
|
||||
start: new Date(Date.UTC(2030, 0, 8, 11, 0, 0)).toISOString(),
|
||||
eventTypeId: recurringSeatedEventTypeId,
|
||||
attendee: {
|
||||
name: "Mr Proper",
|
||||
email: "mr_proper@gmail.com",
|
||||
timeZone: "Europe/Rome",
|
||||
language: "it",
|
||||
},
|
||||
location: "https://meet.google.com/abc-def-ghi",
|
||||
bookingFieldsResponses: {
|
||||
codingLanguage: "TypeScript",
|
||||
},
|
||||
recurrenceCount,
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post("/v2/bookings")
|
||||
.send(body)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(201)
|
||||
.then(async (response) => {
|
||||
const responseBody: CreateBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsCreateRecurringSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsCreateRecurringSeatedBooking(responseBody.data)) {
|
||||
const data: CreateRecurringSeatedBookingOutput_2024_08_13[] = responseBody.data;
|
||||
expect(data.length).toEqual(recurrenceCount);
|
||||
|
||||
const firstBooking = data[0];
|
||||
expect(firstBooking.seatUid).toBeDefined();
|
||||
const seatUid = firstBooking.seatUid;
|
||||
expect(firstBooking.id).toBeDefined();
|
||||
expect(firstBooking.uid).toBeDefined();
|
||||
expect(firstBooking.hosts[0].id).toEqual(user.id);
|
||||
expect(firstBooking.status).toEqual("accepted");
|
||||
expect(firstBooking.start).toEqual(body.start);
|
||||
expect(firstBooking.end).toEqual(
|
||||
DateTime.fromISO(body.start, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||
);
|
||||
expect(firstBooking.duration).toEqual(60);
|
||||
expect(firstBooking.eventTypeId).toEqual(recurringSeatedEventTypeId);
|
||||
expect(firstBooking.eventType).toEqual({
|
||||
id: recurringSeatedEventTypeId,
|
||||
slug: recurringSeatedTventTypeSlug,
|
||||
});
|
||||
expect(firstBooking.attendees.length).toEqual(1);
|
||||
expect(firstBooking.attendees[0]).toEqual({
|
||||
name: body.attendee.name,
|
||||
timeZone: body.attendee.timeZone,
|
||||
language: body.attendee.language,
|
||||
absent: false,
|
||||
seatUid,
|
||||
bookingFieldsResponses: {
|
||||
name: body.attendee.name,
|
||||
...body.bookingFieldsResponses,
|
||||
},
|
||||
});
|
||||
expect(firstBooking.location).toEqual(body.location);
|
||||
expect(firstBooking.absentHost).toEqual(false);
|
||||
|
||||
const secondBooking = data[1];
|
||||
expect(secondBooking.seatUid).toBeDefined();
|
||||
const secondSeatUid = secondBooking.seatUid;
|
||||
expect(secondBooking.id).toBeDefined();
|
||||
expect(secondBooking.uid).toBeDefined();
|
||||
expect(secondBooking.hosts[0].id).toEqual(user.id);
|
||||
expect(secondBooking.status).toEqual("accepted");
|
||||
const expectedStart = DateTime.fromISO(body.start, { zone: "utc" }).plus({ weeks: 1 }).toISO();
|
||||
expect(secondBooking.start).toEqual(expectedStart);
|
||||
expect(secondBooking.end).toEqual(
|
||||
DateTime.fromISO(expectedStart!, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||
);
|
||||
expect(secondBooking.duration).toEqual(60);
|
||||
expect(secondBooking.eventTypeId).toEqual(recurringSeatedEventTypeId);
|
||||
expect(secondBooking.eventType).toEqual({
|
||||
id: recurringSeatedEventTypeId,
|
||||
slug: recurringSeatedTventTypeSlug,
|
||||
});
|
||||
expect(secondBooking.attendees.length).toEqual(1);
|
||||
expect(secondBooking.attendees[0]).toEqual({
|
||||
name: body.attendee.name,
|
||||
timeZone: body.attendee.timeZone,
|
||||
language: body.attendee.language,
|
||||
absent: false,
|
||||
seatUid: secondSeatUid,
|
||||
bookingFieldsResponses: {
|
||||
name: body.attendee.name,
|
||||
...body.bookingFieldsResponses,
|
||||
},
|
||||
});
|
||||
expect(secondBooking.location).toEqual(body.location);
|
||||
expect(secondBooking.absentHost).toEqual(false);
|
||||
createdRecurringSeatedBooking = data;
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid response data - expected recurring booking but received non array response"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should get a booking for an event type with seats", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/bookings/${createdSeatedBooking.uid}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsGetSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsGetSeatedBooking(responseBody.data)) {
|
||||
const data: GetSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||
const expected = structuredClone(createdSeatedBooking);
|
||||
// note(Lauris): seatUid in get response resides only in each attendee object
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
delete expected.seatUid;
|
||||
expect(data).toEqual(expected);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid response data - expected recurring booking but received non array response"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should get a booking for a recurring event type with seats", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/bookings/${createdRecurringSeatedBooking[1].recurringBookingUid}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsGetRecurringSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsGetRecurringSeatedBooking(responseBody.data)) {
|
||||
const data: GetRecurringSeatedBookingOutput_2024_08_13[] = responseBody.data;
|
||||
const expected = structuredClone(createdRecurringSeatedBooking);
|
||||
for (const booking of expected) {
|
||||
// note(Lauris): seatUid in get response resides only in each attendee object
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
delete booking.seatUid;
|
||||
}
|
||||
expect(data).toEqual(expected);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid response data - expected recurring booking but received non array response"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should get a specific recurrence of a booking for a recurring event type with seats", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/bookings/${createdRecurringSeatedBooking[0].uid}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsGetSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsGetSeatedBooking(responseBody.data)) {
|
||||
const data: GetSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||
const expected = structuredClone(createdRecurringSeatedBooking[0]);
|
||||
// note(Lauris): seatUid in get response resides only in each attendee object
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
delete expected.seatUid;
|
||||
expect(data).toEqual(expected);
|
||||
} else {
|
||||
throw new Error(
|
||||
"Invalid response data - expected recurring booking but received non array response"
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should get all seated bookings", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get("/v2/bookings?sortCreated=asc")
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseBody.data.length).toEqual(3);
|
||||
|
||||
const seatedBooking = responseBody.data[0];
|
||||
const seatedBookingExpected = structuredClone(createdSeatedBooking);
|
||||
// note(Lauris): seatUid in get response resides only in each attendee object
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
delete seatedBookingExpected.seatUid;
|
||||
expect(seatedBooking).toEqual(seatedBookingExpected);
|
||||
|
||||
const recurringSeatedBookings = [responseBody.data[1], responseBody.data[2]];
|
||||
const recurringSeatedBookingsExpected = structuredClone(createdRecurringSeatedBooking);
|
||||
for (const booking of recurringSeatedBookingsExpected) {
|
||||
// note(Lauris): seatUid in get response resides only in each attendee object
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
delete booking.seatUid;
|
||||
}
|
||||
expect(recurringSeatedBookings).toEqual(recurringSeatedBookingsExpected);
|
||||
});
|
||||
});
|
||||
|
||||
it("should reschedule seated booking", async () => {
|
||||
const body: RescheduleSeatedBookingInput_2024_08_13 = {
|
||||
start: new Date(Date.UTC(2030, 0, 8, 15, 0, 0)).toISOString(),
|
||||
seatUid: createdSeatedBooking.seatUid,
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/bookings/${createdSeatedBooking.uid}/reschedule`)
|
||||
.send(body)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(201)
|
||||
.then(async (response) => {
|
||||
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsGetSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsGetSeatedBooking(responseBody.data)) {
|
||||
const data: CreateSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||
expect(data.seatUid).toBeDefined();
|
||||
const seatUid = data.seatUid;
|
||||
expect(data.id).toBeDefined();
|
||||
expect(data.uid).toBeDefined();
|
||||
expect(data.hosts[0].id).toEqual(user.id);
|
||||
expect(data.status).toEqual("accepted");
|
||||
expect(data.start).toEqual(body.start);
|
||||
expect(data.end).toEqual(
|
||||
DateTime.fromISO(body.start, { zone: "utc" }).plus({ hours: 1 }).toISO()
|
||||
);
|
||||
expect(data.duration).toEqual(60);
|
||||
expect(data.eventTypeId).toEqual(seatedEventTypeId);
|
||||
expect(data.eventType).toEqual({
|
||||
id: seatedEventTypeId,
|
||||
slug: seatedTventTypeSlug,
|
||||
});
|
||||
expect(data.attendees.length).toEqual(1);
|
||||
const attendee = createdSeatedBooking.attendees.find((a) => a.seatUid === body.seatUid);
|
||||
expect(data.attendees[0]).toEqual({
|
||||
name: attendee?.name,
|
||||
timeZone: attendee?.timeZone,
|
||||
language: attendee?.language,
|
||||
absent: false,
|
||||
seatUid,
|
||||
bookingFieldsResponses: {
|
||||
name: attendee?.name,
|
||||
...attendee?.bookingFieldsResponses,
|
||||
},
|
||||
});
|
||||
expect(data.location).toBeDefined();
|
||||
expect(data.absentHost).toEqual(false);
|
||||
createdSeatedBooking = data;
|
||||
} else {
|
||||
throw new Error("Invalid response data - expected booking but received array response");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("should cancel seated booking", async () => {
|
||||
const body: CancelSeatedBookingInput_2024_08_13 = {
|
||||
seatUid: createdSeatedBooking.seatUid,
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.post(`/v2/bookings/${createdSeatedBooking.uid}/cancel`)
|
||||
.send(body)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13)
|
||||
.expect(200)
|
||||
.then(async (response) => {
|
||||
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
expect(responseDataIsGetSeatedBooking(responseBody.data)).toBe(true);
|
||||
|
||||
if (responseDataIsGetSeatedBooking(responseBody.data)) {
|
||||
const data: GetSeatedBookingOutput_2024_08_13 = responseBody.data;
|
||||
expect(data.id).toBeDefined();
|
||||
expect(data.uid).toBeDefined();
|
||||
expect(data.hosts[0].id).toEqual(user.id);
|
||||
expect(data.status).toEqual("cancelled");
|
||||
expect(data.start).toEqual(createdSeatedBooking.start);
|
||||
expect(data.end).toEqual(createdSeatedBooking.end);
|
||||
expect(data.duration).toEqual(60);
|
||||
expect(data.eventTypeId).toEqual(seatedEventTypeId);
|
||||
expect(data.eventType).toEqual({
|
||||
id: seatedEventTypeId,
|
||||
slug: seatedTventTypeSlug,
|
||||
});
|
||||
expect(data.attendees.length).toEqual(0);
|
||||
expect(data.location).toBeDefined();
|
||||
expect(data.absentHost).toEqual(false);
|
||||
} else {
|
||||
throw new Error("Invalid response data - expected booking but received array response");
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function responseDataIsCreateSeatedBooking(data: any): data is CreateSeatedBookingOutput_2024_08_13 {
|
||||
return data.hasOwnProperty("seatUid");
|
||||
}
|
||||
|
||||
function responseDataIsCreateRecurringSeatedBooking(
|
||||
data: any
|
||||
): data is CreateRecurringSeatedBookingOutput_2024_08_13[] {
|
||||
return Array.isArray(data);
|
||||
}
|
||||
|
||||
function responseDataIsGetSeatedBooking(data: any): data is GetSeatedBookingOutput_2024_08_13 {
|
||||
return data?.attendees?.every((attendee: any) => attendee?.hasOwnProperty("seatUid"));
|
||||
}
|
||||
|
||||
function responseDataIsGetRecurringSeatedBooking(
|
||||
data: any
|
||||
): data is GetRecurringSeatedBookingOutput_2024_08_13[] {
|
||||
return Array.isArray(data);
|
||||
}
|
||||
|
||||
afterAll(async () => {
|
||||
await oauthClientRepositoryFixture.delete(oAuthClient.id);
|
||||
await teamRepositoryFixture.delete(organization.id);
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await bookingsRepositoryFixture.deleteAllBookings(user.id, user.email);
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
+17
-3
@@ -29,6 +29,7 @@ import {
|
||||
BookingOutput_2024_08_13,
|
||||
RecurringBookingOutput_2024_08_13,
|
||||
GetBookingsOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
import { PlatformOAuthClient, Team } from "@calcom/prisma/client";
|
||||
|
||||
@@ -369,7 +370,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(1);
|
||||
expect(data[0].eventTypeId).toEqual(team1EventTypeId);
|
||||
});
|
||||
@@ -384,7 +389,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(1);
|
||||
expect(data[0].eventTypeId).toEqual(team2EventTypeId);
|
||||
});
|
||||
@@ -399,7 +408,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data.find((booking) => booking.eventTypeId === team1EventTypeId)).toBeDefined();
|
||||
expect(data.find((booking) => booking.eventTypeId === team2EventTypeId)).toBeDefined();
|
||||
@@ -428,6 +441,7 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
await oauthClientRepositoryFixture.delete(oAuthClient.id);
|
||||
await teamRepositoryFixture.delete(organization.id);
|
||||
await userRepositoryFixture.deleteByEmail(teamUser.email);
|
||||
await userRepositoryFixture.deleteByEmail(teamUserEmail2);
|
||||
await bookingsRepositoryFixture.deleteAllBookings(teamUser.id, teamUser.email);
|
||||
await app.close();
|
||||
});
|
||||
|
||||
+104
-21
@@ -28,7 +28,11 @@ import {
|
||||
VERSION_2024_08_13,
|
||||
X_CAL_CLIENT_ID,
|
||||
} from "@calcom/platform-constants";
|
||||
import { GetBookingOutput_2024_08_13, GetBookingsOutput_2024_08_13 } from "@calcom/platform-types";
|
||||
import {
|
||||
GetBookingOutput_2024_08_13,
|
||||
GetBookingsOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
import {
|
||||
CreateBookingInput_2024_08_13,
|
||||
BookingOutput_2024_08_13,
|
||||
@@ -478,7 +482,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(5);
|
||||
});
|
||||
});
|
||||
@@ -492,7 +500,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -506,7 +518,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -520,7 +536,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(4);
|
||||
});
|
||||
});
|
||||
@@ -534,7 +554,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
@@ -548,7 +572,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(5);
|
||||
});
|
||||
});
|
||||
@@ -562,7 +590,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -576,7 +608,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(1);
|
||||
});
|
||||
});
|
||||
@@ -590,7 +626,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -604,7 +644,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
@@ -618,7 +662,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(5);
|
||||
});
|
||||
});
|
||||
@@ -632,7 +680,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
});
|
||||
});
|
||||
@@ -646,7 +698,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(3);
|
||||
});
|
||||
});
|
||||
@@ -660,9 +716,12 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
console.log("asap data", JSON.stringify(data, null, 2));
|
||||
expect(data[0].start).toEqual(createdBooking.start);
|
||||
expect(data[1].start).toEqual(bookingInThePast.startTime.toISOString());
|
||||
});
|
||||
@@ -677,7 +736,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data[0].start).toEqual(bookingInThePast.startTime.toISOString());
|
||||
expect(data[1].start).toEqual(createdBooking.start);
|
||||
@@ -693,7 +756,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data[0].start).toEqual(createdBooking.start);
|
||||
expect(data[1].start).toEqual(bookingInThePast.startTime.toISOString());
|
||||
@@ -709,7 +776,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data[0].start).toEqual(bookingInThePast.startTime.toISOString());
|
||||
expect(data[1].start).toEqual(createdBooking.start);
|
||||
@@ -725,7 +796,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data[0].start).toEqual(createdBooking.start);
|
||||
expect(data[1].start).toEqual(bookingInThePast.startTime.toISOString());
|
||||
@@ -741,7 +816,11 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: GetBookingsOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
const data: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = responseBody.data;
|
||||
const data: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
)[] = responseBody.data;
|
||||
expect(data.length).toEqual(2);
|
||||
expect(data[0].start).toEqual(bookingInThePast.startTime.toISOString());
|
||||
expect(data[1].start).toEqual(createdBooking.start);
|
||||
@@ -765,6 +844,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const data: BookingOutput_2024_08_13 = responseBody.data;
|
||||
expect(data.reschedulingReason).toEqual(body.reschedulingReason);
|
||||
expect(data.start).toEqual(body.start);
|
||||
@@ -793,6 +874,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
|
||||
const responseBody: RescheduleBookingOutput_2024_08_13 = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data).toBeDefined();
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const data: BookingOutput_2024_08_13 = responseBody.data;
|
||||
expect(data.status).toEqual("cancelled");
|
||||
|
||||
|
||||
@@ -3,7 +3,12 @@ import { Type } from "class-transformer";
|
||||
import { IsEnum, ValidateNested } from "class-validator";
|
||||
|
||||
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
||||
import { BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13 } from "@calcom/platform-types";
|
||||
import {
|
||||
BookingOutput_2024_08_13,
|
||||
GetRecurringSeatedBookingOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
RecurringBookingOutput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
|
||||
@ApiExtraModels(BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13)
|
||||
export class CancelBookingOutput_2024_08_13 {
|
||||
@@ -17,10 +22,19 @@ export class CancelBookingOutput_2024_08_13 {
|
||||
{ $ref: getSchemaPath(BookingOutput_2024_08_13) },
|
||||
{ $ref: getSchemaPath(RecurringBookingOutput_2024_08_13) },
|
||||
{ type: "array", items: { $ref: getSchemaPath(RecurringBookingOutput_2024_08_13) } },
|
||||
{ $ref: getSchemaPath(GetSeatedBookingOutput_2024_08_13) },
|
||||
{ $ref: getSchemaPath(GetRecurringSeatedBookingOutput_2024_08_13) },
|
||||
{ type: "array", items: { $ref: getSchemaPath(GetRecurringSeatedBookingOutput_2024_08_13) } },
|
||||
],
|
||||
description:
|
||||
"Booking data, which can be either a BookingOutput object, a RecurringBookingOutput object, or an array of RecurringBookingOutput objects",
|
||||
})
|
||||
@Type(() => Object)
|
||||
data!: BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13[];
|
||||
data!:
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13[]
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
| GetRecurringSeatedBookingOutput_2024_08_13
|
||||
| GetRecurringSeatedBookingOutput_2024_08_13[];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,12 @@ import { Type } from "class-transformer";
|
||||
import { IsEnum, ValidateNested } from "class-validator";
|
||||
|
||||
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
||||
import { BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13 } from "@calcom/platform-types";
|
||||
import {
|
||||
BookingOutput_2024_08_13,
|
||||
RecurringBookingOutput_2024_08_13,
|
||||
CreateSeatedBookingOutput_2024_08_13,
|
||||
CreateRecurringSeatedBookingOutput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
|
||||
@ApiExtraModels(BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13)
|
||||
export class CreateBookingOutput_2024_08_13 {
|
||||
@@ -21,5 +26,9 @@ export class CreateBookingOutput_2024_08_13 {
|
||||
"Booking data, which can be either a BookingOutput object or an array of RecurringBookingOutput objects",
|
||||
})
|
||||
@Type(() => Object)
|
||||
data!: BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13[];
|
||||
data!:
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13[]
|
||||
| CreateSeatedBookingOutput_2024_08_13
|
||||
| CreateRecurringSeatedBookingOutput_2024_08_13[];
|
||||
}
|
||||
|
||||
@@ -3,7 +3,14 @@ import { Type } from "class-transformer";
|
||||
import { IsEnum, ValidateNested } from "class-validator";
|
||||
|
||||
import { SUCCESS_STATUS, ERROR_STATUS } from "@calcom/platform-constants";
|
||||
import { BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13 } from "@calcom/platform-types";
|
||||
import {
|
||||
BookingOutput_2024_08_13,
|
||||
CreateRecurringSeatedBookingOutput_2024_08_13,
|
||||
CreateSeatedBookingOutput_2024_08_13,
|
||||
GetRecurringSeatedBookingOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
RecurringBookingOutput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
|
||||
@ApiExtraModels(BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13)
|
||||
export class RescheduleBookingOutput_2024_08_13 {
|
||||
@@ -15,11 +22,17 @@ export class RescheduleBookingOutput_2024_08_13 {
|
||||
oneOf: [
|
||||
{ $ref: getSchemaPath(BookingOutput_2024_08_13) },
|
||||
{ $ref: getSchemaPath(RecurringBookingOutput_2024_08_13) },
|
||||
{ $ref: getSchemaPath(CreateSeatedBookingOutput_2024_08_13) },
|
||||
{ $ref: getSchemaPath(CreateRecurringSeatedBookingOutput_2024_08_13) },
|
||||
],
|
||||
description:
|
||||
"Booking data, which can be either a BookingOutput object or a RecurringBookingOutput object",
|
||||
})
|
||||
@ValidateNested()
|
||||
@Type(() => Object)
|
||||
data!: BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13;
|
||||
data!:
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| CreateSeatedBookingOutput_2024_08_13
|
||||
| CreateRecurringSeatedBookingOutput_2024_08_13;
|
||||
}
|
||||
|
||||
@@ -18,7 +18,6 @@ import {
|
||||
} from "@calcom/platform-libraries";
|
||||
import {
|
||||
CreateBookingInput_2024_08_13,
|
||||
RescheduleBookingInput_2024_08_13,
|
||||
CreateBookingInput,
|
||||
CreateRecurringBookingInput_2024_08_13,
|
||||
GetBookingsInput_2024_08_13,
|
||||
@@ -27,6 +26,10 @@ import {
|
||||
MarkAbsentBookingInput_2024_08_13,
|
||||
BookingOutput_2024_08_13,
|
||||
RecurringBookingOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
GetRecurringSeatedBookingOutput_2024_08_13,
|
||||
RescheduleBookingInput,
|
||||
CancelBookingInput,
|
||||
} from "@calcom/platform-types";
|
||||
import { PrismaClient } from "@calcom/prisma";
|
||||
|
||||
@@ -54,9 +57,19 @@ export class BookingsService_2024_08_13 {
|
||||
return await this.createInstantBooking(request, body);
|
||||
}
|
||||
|
||||
if (await this.isRecurring(body)) {
|
||||
const eventType = await this.eventTypesRepository.getEventTypeById(body.eventTypeId);
|
||||
const isRecurring = !!eventType?.recurringEvent;
|
||||
const isSeated = !!eventType?.seatsPerTimeSlot;
|
||||
|
||||
if (isRecurring && isSeated) {
|
||||
return await this.createRecurringSeatedBooking(request, body);
|
||||
}
|
||||
if (isRecurring && !isSeated) {
|
||||
return await this.createRecurringBooking(request, body);
|
||||
}
|
||||
if (isSeated) {
|
||||
return await this.createSeatedBooking(request, body);
|
||||
}
|
||||
|
||||
return await this.createRegularBooking(request, body);
|
||||
} catch (error) {
|
||||
@@ -83,47 +96,71 @@ export class BookingsService_2024_08_13 {
|
||||
return this.outputService.getOutputBooking(databaseBooking);
|
||||
}
|
||||
|
||||
async isRecurring(body: CreateBookingInput) {
|
||||
const eventType = await this.eventTypesRepository.getEventTypeById(body.eventTypeId);
|
||||
return !!eventType?.recurringEvent;
|
||||
}
|
||||
|
||||
async createRecurringBooking(request: Request, body: CreateRecurringBookingInput_2024_08_13) {
|
||||
const bookingRequest = await this.inputService.createRecurringBookingRequest(request, body);
|
||||
const bookings = await handleNewRecurringBooking(bookingRequest);
|
||||
const uid = bookings[0].recurringEventId;
|
||||
if (!uid) {
|
||||
throw new Error("Recurring booking was not created");
|
||||
}
|
||||
const ids = bookings.map((booking) => booking.id || 0);
|
||||
return this.outputService.getOutputRecurringBookings(ids);
|
||||
}
|
||||
|
||||
const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUserAndEvent(uid);
|
||||
return this.outputService.getOutputRecurringBookings(recurringBooking);
|
||||
async createRecurringSeatedBooking(request: Request, body: CreateRecurringBookingInput_2024_08_13) {
|
||||
const bookingRequest = await this.inputService.createRecurringBookingRequest(request, body);
|
||||
const bookings = await handleNewRecurringBooking(bookingRequest);
|
||||
return this.outputService.getOutputCreateRecurringSeatedBookings(
|
||||
bookings.map((booking) => ({ id: booking.id || 0, seatUid: booking.seatReferenceUid || "" }))
|
||||
);
|
||||
}
|
||||
|
||||
async createRegularBooking(request: Request, body: CreateBookingInput_2024_08_13) {
|
||||
const bookingRequest = await this.inputService.createBookingRequest(request, body);
|
||||
const booking = await handleNewBooking(bookingRequest);
|
||||
|
||||
if (!booking.id) {
|
||||
throw new Error("Booking was not created");
|
||||
if (!booking.uid) {
|
||||
throw new Error("Booking missing uid");
|
||||
}
|
||||
|
||||
const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(booking.id);
|
||||
const databaseBooking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(booking.uid);
|
||||
if (!databaseBooking) {
|
||||
throw new Error(`Booking with id=${booking.id} was not found in the database`);
|
||||
throw new Error(`Booking with uid=${booking.uid} was not found in the database`);
|
||||
}
|
||||
|
||||
return this.outputService.getOutputBooking(databaseBooking);
|
||||
}
|
||||
|
||||
async createSeatedBooking(request: Request, body: CreateBookingInput_2024_08_13) {
|
||||
const bookingRequest = await this.inputService.createBookingRequest(request, body);
|
||||
const booking = await handleNewBooking(bookingRequest);
|
||||
|
||||
if (!booking.uid) {
|
||||
throw new Error("Booking missing uid");
|
||||
}
|
||||
|
||||
const databaseBooking = await this.bookingsRepository.getByUidWithAttendeesWithBookingSeatAndUserAndEvent(
|
||||
booking.uid
|
||||
);
|
||||
if (!databaseBooking) {
|
||||
throw new Error(`Booking with uid=${booking.uid} was not found in the database`);
|
||||
}
|
||||
|
||||
return this.outputService.getOutputCreateSeatedBooking(databaseBooking, booking.seatReferenceUid || "");
|
||||
}
|
||||
|
||||
async getBooking(uid: string) {
|
||||
const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(uid);
|
||||
const booking = await this.bookingsRepository.getByUidWithAttendeesWithBookingSeatAndUserAndEvent(uid);
|
||||
|
||||
if (booking) {
|
||||
const isRecurring = !!booking.recurringEventId;
|
||||
if (isRecurring) {
|
||||
const isSeated = !!booking.eventType?.seatsPerTimeSlot;
|
||||
|
||||
if (isRecurring && !isSeated) {
|
||||
return this.outputService.getOutputRecurringBooking(booking);
|
||||
}
|
||||
if (isRecurring && isSeated) {
|
||||
return this.outputService.getOutputRecurringSeatedBooking(booking);
|
||||
}
|
||||
if (isSeated) {
|
||||
return this.outputService.getOutputSeatedBooking(booking);
|
||||
}
|
||||
return this.outputService.getOutputBooking(booking);
|
||||
}
|
||||
|
||||
@@ -131,8 +168,13 @@ export class BookingsService_2024_08_13 {
|
||||
if (!recurringBooking.length) {
|
||||
throw new NotFoundException(`Booking with uid=${uid} was not found in the database`);
|
||||
}
|
||||
const ids = recurringBooking.map((booking) => booking.id);
|
||||
const isRecurringSeated = !!recurringBooking[0].eventType?.seatsPerTimeSlot;
|
||||
if (isRecurringSeated) {
|
||||
return this.outputService.getOutputRecurringSeatedBookings(ids);
|
||||
}
|
||||
|
||||
return this.outputService.getOutputRecurringBookings(recurringBooking);
|
||||
return this.outputService.getOutputRecurringBookings(ids);
|
||||
}
|
||||
|
||||
async getBookings(queryParams: GetBookingsInput_2024_08_13, user: { email: string; id: number }) {
|
||||
@@ -151,12 +193,17 @@ export class BookingsService_2024_08_13 {
|
||||
// note(Lauris): fetchedBookings don't have attendees information and responses and i don't want to add them to the handler query,
|
||||
// because its used elsewhere in code that does not need that information, so i get ids, fetch bookings and then return them formatted in same order as ids.
|
||||
const ids = fetchedBookings.bookings.map((booking) => booking.id);
|
||||
const bookings = await this.bookingsRepository.getByIdsWithAttendeesAndUserAndEvent(ids);
|
||||
const bookings = await this.bookingsRepository.getByIdsWithAttendeesWithBookingSeatAndUserAndEvent(ids);
|
||||
|
||||
const bookingMap = new Map(bookings.map((booking) => [booking.id, booking]));
|
||||
const orderedBookings = ids.map((id) => bookingMap.get(id));
|
||||
|
||||
const formattedBookings: (BookingOutput_2024_08_13 | RecurringBookingOutput_2024_08_13)[] = [];
|
||||
const formattedBookings: (
|
||||
| BookingOutput_2024_08_13
|
||||
| RecurringBookingOutput_2024_08_13
|
||||
| GetSeatedBookingOutput_2024_08_13
|
||||
| GetRecurringSeatedBookingOutput_2024_08_13
|
||||
)[] = [];
|
||||
for (const booking of orderedBookings) {
|
||||
if (!booking) {
|
||||
continue;
|
||||
@@ -172,8 +219,13 @@ export class BookingsService_2024_08_13 {
|
||||
};
|
||||
|
||||
const isRecurring = !!formatted.recurringEventId;
|
||||
if (isRecurring) {
|
||||
const isSeated = !!formatted.eventType?.seatsPerTimeSlot;
|
||||
if (isRecurring && !isSeated) {
|
||||
formattedBookings.push(this.outputService.getOutputRecurringBooking(formatted));
|
||||
} else if (isRecurring && isSeated) {
|
||||
formattedBookings.push(this.outputService.getOutputRecurringSeatedBooking(formatted));
|
||||
} else if (isSeated) {
|
||||
formattedBookings.push(this.outputService.getOutputSeatedBooking(formatted));
|
||||
} else {
|
||||
formattedBookings.push(this.outputService.getOutputBooking(formatted));
|
||||
}
|
||||
@@ -182,7 +234,7 @@ export class BookingsService_2024_08_13 {
|
||||
return formattedBookings;
|
||||
}
|
||||
|
||||
async rescheduleBooking(request: Request, bookingUid: string, body: RescheduleBookingInput_2024_08_13) {
|
||||
async rescheduleBooking(request: Request, bookingUid: string, body: RescheduleBookingInput) {
|
||||
try {
|
||||
const bookingRequest = await this.inputService.createRescheduleBookingRequest(
|
||||
request,
|
||||
@@ -190,18 +242,34 @@ export class BookingsService_2024_08_13 {
|
||||
body
|
||||
);
|
||||
const booking = await handleNewBooking(bookingRequest);
|
||||
if (!booking.id) {
|
||||
throw new Error("Booking was not created");
|
||||
if (!booking.uid) {
|
||||
throw new Error("Booking missing uid");
|
||||
}
|
||||
|
||||
const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(booking.id);
|
||||
const databaseBooking =
|
||||
await this.bookingsRepository.getByUidWithAttendeesWithBookingSeatAndUserAndEvent(booking.uid);
|
||||
if (!databaseBooking) {
|
||||
throw new Error(`Booking with id=${booking.id} was not found in the database`);
|
||||
throw new Error(`Booking with uid=${booking.uid} was not found in the database`);
|
||||
}
|
||||
|
||||
if (databaseBooking.recurringEventId) {
|
||||
const isRecurring = !!databaseBooking.recurringEventId;
|
||||
const isSeated = !!databaseBooking.eventType?.seatsPerTimeSlot;
|
||||
|
||||
if (isRecurring && !isSeated) {
|
||||
return this.outputService.getOutputRecurringBooking(databaseBooking);
|
||||
}
|
||||
if (isRecurring && isSeated) {
|
||||
return this.outputService.getOutputCreateRecurringSeatedBooking(
|
||||
databaseBooking,
|
||||
booking?.seatReferenceUid || ""
|
||||
);
|
||||
}
|
||||
if (isSeated) {
|
||||
return this.outputService.getOutputCreateSeatedBooking(
|
||||
databaseBooking,
|
||||
booking.seatReferenceUid || ""
|
||||
);
|
||||
}
|
||||
return this.outputService.getOutputBooking(databaseBooking);
|
||||
} catch (error) {
|
||||
if (error instanceof Error) {
|
||||
@@ -213,7 +281,7 @@ export class BookingsService_2024_08_13 {
|
||||
}
|
||||
}
|
||||
|
||||
async cancelBooking(request: Request, bookingUid: string, body: CancelBookingInput_2024_08_13) {
|
||||
async cancelBooking(request: Request, bookingUid: string, body: CancelBookingInput) {
|
||||
const bookingRequest = await this.inputService.createCancelBookingRequest(request, bookingUid, body);
|
||||
await handleCancelBooking(bookingRequest);
|
||||
return this.getBooking(bookingUid);
|
||||
@@ -249,7 +317,7 @@ export class BookingsService_2024_08_13 {
|
||||
}
|
||||
|
||||
async billBooking(booking: CreatedBooking) {
|
||||
const hostId = booking.hosts[0].id;
|
||||
const hostId = booking.hosts?.[0]?.id;
|
||||
if (!hostId) {
|
||||
this.logger.error(`Booking with uid=${booking.uid} has no host`);
|
||||
return;
|
||||
|
||||
@@ -1,8 +1,12 @@
|
||||
import { BookingsRepository_2024_08_13 } from "@/ee/bookings/2024-08-13/bookings.repository";
|
||||
import { bookingResponsesSchema } from "@/ee/bookings/2024-08-13/services/output.service";
|
||||
import {
|
||||
bookingResponsesSchema,
|
||||
seatedBookingResponsesSchema,
|
||||
} from "@/ee/bookings/2024-08-13/services/output.service";
|
||||
import { EventTypesRepository_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.repository";
|
||||
import { hashAPIKey, isApiKey, stripApiKey } from "@/lib/api-key";
|
||||
import { ApiKeyRepository } from "@/modules/api-key/api-key-repository";
|
||||
import { BookingSeatRepository } from "@/modules/booking-seat/booking-seat.repository";
|
||||
import { OAuthClientRepository } from "@/modules/oauth-clients/oauth-client.repository";
|
||||
import { OAuthFlowService } from "@/modules/oauth-clients/services/oauth-flow.service";
|
||||
import { BadRequestException, Injectable, NotFoundException } from "@nestjs/common";
|
||||
@@ -16,13 +20,17 @@ import { z } from "zod";
|
||||
|
||||
import { X_CAL_CLIENT_ID } from "@calcom/platform-constants";
|
||||
import {
|
||||
CancelBookingInput,
|
||||
CancelBookingInput_2024_08_13,
|
||||
CancelSeatedBookingInput_2024_08_13,
|
||||
CreateBookingInput_2024_08_13,
|
||||
CreateInstantBookingInput_2024_08_13,
|
||||
CreateRecurringBookingInput_2024_08_13,
|
||||
GetBookingsInput_2024_08_13,
|
||||
MarkAbsentBookingInput_2024_08_13,
|
||||
RescheduleBookingInput,
|
||||
RescheduleBookingInput_2024_08_13,
|
||||
RescheduleSeatedBookingInput_2024_08_13,
|
||||
} from "@calcom/platform-types";
|
||||
|
||||
type BookingRequest = NextApiRequest & { userId: number | undefined } & OAuthRequestParams;
|
||||
@@ -73,7 +81,8 @@ export class InputBookingsService_2024_08_13 {
|
||||
private readonly eventTypesRepository: EventTypesRepository_2024_06_14,
|
||||
private readonly bookingsRepository: BookingsRepository_2024_08_13,
|
||||
private readonly config: ConfigService,
|
||||
private readonly apiKeyRepository: ApiKeyRepository
|
||||
private readonly apiKeyRepository: ApiKeyRepository,
|
||||
private readonly bookingSeatRepository: BookingSeatRepository
|
||||
) {}
|
||||
|
||||
async createBookingRequest(
|
||||
@@ -239,9 +248,11 @@ export class InputBookingsService_2024_08_13 {
|
||||
async createRescheduleBookingRequest(
|
||||
request: Request,
|
||||
bookingUid: string,
|
||||
body: RescheduleBookingInput_2024_08_13
|
||||
body: RescheduleBookingInput
|
||||
): Promise<BookingRequest> {
|
||||
const bodyTransformed = await this.transformInputRescheduleBooking(bookingUid, body);
|
||||
const bodyTransformed = this.isRescheduleSeatedBody(body)
|
||||
? await this.transformInputRescheduleSeatedBooking(bookingUid, body)
|
||||
: await this.transformInputRescheduleBooking(bookingUid, body);
|
||||
const oAuthClientId = request.get(X_CAL_CLIENT_ID);
|
||||
|
||||
const newRequest = { ...request };
|
||||
@@ -258,6 +269,60 @@ export class InputBookingsService_2024_08_13 {
|
||||
return newRequest as unknown as BookingRequest;
|
||||
}
|
||||
|
||||
isRescheduleSeatedBody(body: RescheduleBookingInput): body is RescheduleSeatedBookingInput_2024_08_13 {
|
||||
return body.hasOwnProperty("seatUid");
|
||||
}
|
||||
|
||||
async transformInputRescheduleSeatedBooking(
|
||||
bookingUid: string,
|
||||
inputBooking: RescheduleSeatedBookingInput_2024_08_13
|
||||
) {
|
||||
const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid);
|
||||
// todo create booking seat module, repository and fetch the seat to get info
|
||||
if (!booking) {
|
||||
throw new NotFoundException(`Booking with uid=${bookingUid} not found`);
|
||||
}
|
||||
if (!booking.eventTypeId) {
|
||||
throw new NotFoundException(`Booking with uid=${bookingUid} is missing event type`);
|
||||
}
|
||||
const eventType = await this.eventTypesRepository.getEventTypeByIdWithOwnerAndTeam(booking.eventTypeId);
|
||||
if (!eventType) {
|
||||
throw new NotFoundException(`Event type with id=${booking.eventTypeId} not found`);
|
||||
}
|
||||
|
||||
const seat = await this.bookingSeatRepository.getByReferenceUid(inputBooking.seatUid);
|
||||
if (!seat) {
|
||||
throw new NotFoundException(`Seat with uid=${inputBooking.seatUid} does not exist.`);
|
||||
}
|
||||
|
||||
const { responses: bookingResponses } = seatedBookingResponsesSchema.parse(seat.data);
|
||||
const attendee = booking.attendees.find((attendee) => attendee.email === bookingResponses.email);
|
||||
|
||||
if (!attendee) {
|
||||
throw new NotFoundException(
|
||||
`Attendee with e-mail ${bookingResponses.email} for booking with uid=${bookingUid} and seatUid=${inputBooking.seatUid} not found`
|
||||
);
|
||||
}
|
||||
|
||||
const startTime = DateTime.fromISO(inputBooking.start, { zone: "utc" }).setZone(attendee.timeZone);
|
||||
const endTime = startTime.plus({ minutes: eventType.length });
|
||||
|
||||
return {
|
||||
start: startTime.toISO(),
|
||||
end: endTime.toISO(),
|
||||
eventTypeId: eventType.id,
|
||||
timeZone: attendee.timeZone,
|
||||
language: attendee.locale,
|
||||
// todo(Lauris): expose after refactoring metadata https://app.campsite.co/cal/posts/zysq8w9rwm9c
|
||||
// metadata: booking.metadata || {},
|
||||
metadata: {},
|
||||
hasHashedBookingLink: false,
|
||||
guests: [],
|
||||
responses: { ...bookingResponses },
|
||||
rescheduleUid: inputBooking.seatUid,
|
||||
};
|
||||
}
|
||||
|
||||
async transformInputRescheduleBooking(bookingUid: string, inputBooking: RescheduleBookingInput_2024_08_13) {
|
||||
const booking = await this.bookingsRepository.getByUidWithAttendeesAndUserAndEvent(bookingUid);
|
||||
if (!booking) {
|
||||
@@ -371,9 +436,11 @@ export class InputBookingsService_2024_08_13 {
|
||||
async createCancelBookingRequest(
|
||||
request: Request,
|
||||
bookingUid: string,
|
||||
body: CancelBookingInput_2024_08_13
|
||||
body: CancelBookingInput
|
||||
): Promise<BookingRequest> {
|
||||
const bodyTransformed = await this.transformInputCancelBooking(bookingUid, body);
|
||||
const bodyTransformed = this.isCancelSeatedBody(body)
|
||||
? await this.transformInputCancelSeatedBooking(bookingUid, body)
|
||||
: await this.transformInputCancelBooking(bookingUid, body);
|
||||
const oAuthClientId = request.get(X_CAL_CLIENT_ID);
|
||||
|
||||
const newRequest = { ...request };
|
||||
@@ -389,6 +456,10 @@ export class InputBookingsService_2024_08_13 {
|
||||
return newRequest as unknown as BookingRequest;
|
||||
}
|
||||
|
||||
isCancelSeatedBody(body: CancelBookingInput): body is CancelSeatedBookingInput_2024_08_13 {
|
||||
return body.hasOwnProperty("seatUid");
|
||||
}
|
||||
|
||||
async transformInputCancelBooking(bookingUid: string, inputBooking: CancelBookingInput_2024_08_13) {
|
||||
let allRemainingBookings = false;
|
||||
let uid = bookingUid;
|
||||
@@ -410,6 +481,31 @@ export class InputBookingsService_2024_08_13 {
|
||||
};
|
||||
}
|
||||
|
||||
async transformInputCancelSeatedBooking(
|
||||
bookingUid: string,
|
||||
inputBooking: CancelSeatedBookingInput_2024_08_13
|
||||
) {
|
||||
let allRemainingBookings = false;
|
||||
let uid = bookingUid;
|
||||
const recurringBooking = await this.bookingsRepository.getRecurringByUidWithAttendeesAndUserAndEvent(
|
||||
bookingUid
|
||||
);
|
||||
|
||||
if (recurringBooking.length) {
|
||||
// note(Lauirs): this means that bookingUid is equal to recurringEventId on individual bookings of recurring one aka main recurring event
|
||||
allRemainingBookings = true;
|
||||
// note(Lauirs): we need to set uid as one of the individual recurring ids, not the main recurring event id
|
||||
uid = recurringBooking[0].uid;
|
||||
}
|
||||
|
||||
return {
|
||||
uid,
|
||||
cancellationReason: "",
|
||||
allRemainingBookings,
|
||||
seatReferenceUid: inputBooking.seatUid,
|
||||
};
|
||||
}
|
||||
|
||||
transformInputMarkAbsentBooking(inputBooking: MarkAbsentBookingInput_2024_08_13) {
|
||||
return {
|
||||
noShowHost: inputBooking.host,
|
||||
|
||||
@@ -4,15 +4,34 @@ import { plainToClass } from "class-transformer";
|
||||
import { DateTime } from "luxon";
|
||||
import { z } from "zod";
|
||||
|
||||
import { BookingOutput_2024_08_13, RecurringBookingOutput_2024_08_13 } from "@calcom/platform-types";
|
||||
import { Booking } from "@calcom/prisma/client";
|
||||
import {
|
||||
BookingOutput_2024_08_13,
|
||||
CreateRecurringSeatedBookingOutput_2024_08_13,
|
||||
CreateSeatedBookingOutput_2024_08_13,
|
||||
GetRecurringSeatedBookingOutput_2024_08_13,
|
||||
GetSeatedBookingOutput_2024_08_13,
|
||||
RecurringBookingOutput_2024_08_13,
|
||||
SeatedAttendee,
|
||||
} from "@calcom/platform-types";
|
||||
import { Booking, BookingSeat } from "@calcom/prisma/client";
|
||||
|
||||
export const bookingResponsesSchema = z
|
||||
.object({
|
||||
email: z.string(),
|
||||
name: z.string(),
|
||||
guests: z.array(z.string()).optional(),
|
||||
rescheduledReason: z.string().optional(),
|
||||
rescheduleReason: z.string().optional(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
export const seatedBookingResponsesSchema = z
|
||||
.object({
|
||||
responses: z
|
||||
.object({
|
||||
email: z.string(),
|
||||
name: z.string(),
|
||||
})
|
||||
.passthrough(),
|
||||
})
|
||||
.passthrough();
|
||||
|
||||
@@ -27,6 +46,7 @@ type DatabaseBooking = Booking & {
|
||||
timeZone: string;
|
||||
locale: string | null;
|
||||
noShow: boolean | null;
|
||||
bookingSeat?: BookingSeat | null;
|
||||
}[];
|
||||
} & { user: { id: number; name: string | null; email: string } | null };
|
||||
|
||||
@@ -72,17 +92,18 @@ export class OutputBookingsService_2024_08_13 {
|
||||
};
|
||||
|
||||
const bookingTransformed = plainToClass(BookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
||||
// note(Lauris): I don't know why plainToClass erases bookings responses so attaching manually
|
||||
bookingTransformed.bookingFieldsResponses = bookingResponses;
|
||||
return bookingTransformed;
|
||||
}
|
||||
|
||||
async getOutputRecurringBookings(databaseBookings: DatabaseBooking[]) {
|
||||
async getOutputRecurringBookings(bookingsIds: number[]) {
|
||||
const transformed = [];
|
||||
|
||||
for (const booking of databaseBookings) {
|
||||
const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(booking.id);
|
||||
for (const bookingId of bookingsIds) {
|
||||
const databaseBooking = await this.bookingsRepository.getByIdWithAttendeesAndUserAndEvent(bookingId);
|
||||
if (!databaseBooking) {
|
||||
throw new Error(`Booking with id=${booking.id} was not found in the database`);
|
||||
throw new Error(`Booking with id=${bookingId} was not found in the database`);
|
||||
}
|
||||
|
||||
transformed.push(this.getOutputRecurringBooking(databaseBooking));
|
||||
@@ -132,4 +153,159 @@ export class OutputBookingsService_2024_08_13 {
|
||||
|
||||
return plainToClass(RecurringBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
||||
}
|
||||
|
||||
getOutputCreateSeatedBooking(
|
||||
databaseBooking: DatabaseBooking,
|
||||
seatUid: string
|
||||
): CreateSeatedBookingOutput_2024_08_13 {
|
||||
const getSeatedBookingOutput = this.getOutputSeatedBooking(databaseBooking);
|
||||
return { ...getSeatedBookingOutput, seatUid };
|
||||
}
|
||||
|
||||
getOutputSeatedBooking(databaseBooking: DatabaseBooking) {
|
||||
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
||||
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
||||
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
||||
|
||||
const booking = {
|
||||
id: databaseBooking.id,
|
||||
uid: databaseBooking.uid,
|
||||
title: databaseBooking.title,
|
||||
description: databaseBooking.description,
|
||||
hosts: [databaseBooking.user],
|
||||
status: databaseBooking.status.toLowerCase(),
|
||||
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
|
||||
start: databaseBooking.startTime,
|
||||
end: databaseBooking.endTime,
|
||||
duration,
|
||||
eventType: databaseBooking.eventType,
|
||||
// note(Lauris): eventTypeId is deprecated
|
||||
eventTypeId: databaseBooking.eventTypeId,
|
||||
attendees: [],
|
||||
location: databaseBooking.location,
|
||||
// note(Lauris): meetingUrl is deprecated
|
||||
meetingUrl: databaseBooking.location,
|
||||
absentHost: !!databaseBooking.noShowHost,
|
||||
};
|
||||
|
||||
const parsed = plainToClass(GetSeatedBookingOutput_2024_08_13, booking, { strategy: "excludeAll" });
|
||||
|
||||
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
||||
parsed.attendees = databaseBooking.attendees.map((attendee) => {
|
||||
const { responses } = seatedBookingResponsesSchema.parse(attendee.bookingSeat?.data);
|
||||
|
||||
const attendeeData = {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
timeZone: attendee.timeZone,
|
||||
language: attendee.locale,
|
||||
absent: !!attendee.noShow,
|
||||
seatUid: attendee.bookingSeat?.referenceUid,
|
||||
bookingFieldsResponses: {},
|
||||
};
|
||||
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
||||
attendeeParsed.bookingFieldsResponses = responses || {};
|
||||
// note(Lauris): as of now email is not returned for privacy
|
||||
delete attendeeParsed.bookingFieldsResponses.email;
|
||||
|
||||
return attendeeParsed;
|
||||
});
|
||||
|
||||
return parsed;
|
||||
}
|
||||
|
||||
async getOutputRecurringSeatedBookings(bookingsIds: number[]) {
|
||||
const transformed = [];
|
||||
|
||||
for (const bookingId of bookingsIds) {
|
||||
const databaseBooking =
|
||||
await this.bookingsRepository.getByIdWithAttendeesWithBookingSeatAndUserAndEvent(bookingId);
|
||||
if (!databaseBooking) {
|
||||
throw new Error(`Booking with id=${bookingId} was not found in the database`);
|
||||
}
|
||||
|
||||
transformed.push(this.getOutputRecurringSeatedBooking(databaseBooking));
|
||||
}
|
||||
|
||||
return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
||||
}
|
||||
|
||||
async getOutputCreateRecurringSeatedBookings(bookings: { id: number; seatUid: string }[]) {
|
||||
const transformed = [];
|
||||
|
||||
for (const booking of bookings) {
|
||||
const databaseBooking =
|
||||
await this.bookingsRepository.getByIdWithAttendeesWithBookingSeatAndUserAndEvent(booking.id);
|
||||
if (!databaseBooking) {
|
||||
throw new Error(`Booking with id=${booking.id} was not found in the database`);
|
||||
}
|
||||
transformed.push(this.getOutputCreateRecurringSeatedBooking(databaseBooking, booking.seatUid));
|
||||
}
|
||||
|
||||
return transformed.sort((a, b) => new Date(a.start).getTime() - new Date(b.start).getTime());
|
||||
}
|
||||
|
||||
getOutputCreateRecurringSeatedBooking(
|
||||
databaseBooking: DatabaseBooking,
|
||||
seatUid: string
|
||||
): CreateRecurringSeatedBookingOutput_2024_08_13 {
|
||||
const getRecurringSeatedBookingOutput = this.getOutputRecurringSeatedBooking(databaseBooking);
|
||||
return { ...getRecurringSeatedBookingOutput, seatUid };
|
||||
}
|
||||
|
||||
getOutputRecurringSeatedBooking(databaseBooking: DatabaseBooking) {
|
||||
const dateStart = DateTime.fromISO(databaseBooking.startTime.toISOString());
|
||||
const dateEnd = DateTime.fromISO(databaseBooking.endTime.toISOString());
|
||||
const duration = dateEnd.diff(dateStart, "minutes").minutes;
|
||||
|
||||
const booking = {
|
||||
id: databaseBooking.id,
|
||||
uid: databaseBooking.uid,
|
||||
title: databaseBooking.title,
|
||||
description: databaseBooking.description,
|
||||
hosts: [databaseBooking.user],
|
||||
status: databaseBooking.status.toLowerCase(),
|
||||
cancellationReason: databaseBooking.cancellationReason || undefined,
|
||||
rescheduledFromUid: databaseBooking.fromReschedule || undefined,
|
||||
start: databaseBooking.startTime,
|
||||
end: databaseBooking.endTime,
|
||||
duration,
|
||||
eventType: databaseBooking.eventType,
|
||||
// note(Lauris): eventTypeId is deprecated
|
||||
eventTypeId: databaseBooking.eventTypeId,
|
||||
attendees: [],
|
||||
location: databaseBooking.location,
|
||||
// note(Lauris): meetingUrl is deprecated
|
||||
meetingUrl: databaseBooking.location,
|
||||
recurringBookingUid: databaseBooking.recurringEventId,
|
||||
absentHost: !!databaseBooking.noShowHost,
|
||||
};
|
||||
|
||||
const parsed = plainToClass(GetRecurringSeatedBookingOutput_2024_08_13, booking, {
|
||||
strategy: "excludeAll",
|
||||
});
|
||||
|
||||
// note(Lauris): I don't know why plainToClass erases booking.attendees[n].responses so attaching manually
|
||||
parsed.attendees = databaseBooking.attendees.map((attendee) => {
|
||||
const { responses } = seatedBookingResponsesSchema.parse(attendee.bookingSeat?.data);
|
||||
|
||||
const attendeeData = {
|
||||
name: attendee.name,
|
||||
email: attendee.email,
|
||||
timeZone: attendee.timeZone,
|
||||
language: attendee.locale,
|
||||
absent: !!attendee.noShow,
|
||||
seatUid: attendee.bookingSeat?.referenceUid,
|
||||
bookingFieldsResponses: {},
|
||||
};
|
||||
const attendeeParsed = plainToClass(SeatedAttendee, attendeeData, { strategy: "excludeAll" });
|
||||
attendeeParsed.bookingFieldsResponses = responses || {};
|
||||
// note(Lauris): as of now email is not returned for privacy
|
||||
delete attendeeParsed.bookingFieldsResponses.email;
|
||||
|
||||
return attendeeParsed;
|
||||
});
|
||||
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IntegrationLocation } from "@calcom/lib";
|
||||
import { OrganizerIntegrationLocation } from "@calcom/lib";
|
||||
|
||||
type BaseEventType = {
|
||||
length: number;
|
||||
@@ -7,7 +7,7 @@ type BaseEventType = {
|
||||
};
|
||||
|
||||
type EventTypeWithLocation = BaseEventType & {
|
||||
locations: IntegrationLocation[];
|
||||
locations: OrganizerIntegrationLocation[];
|
||||
};
|
||||
|
||||
const thirtyMinutes: BaseEventType = {
|
||||
|
||||
+59
-4
@@ -31,6 +31,7 @@ import {
|
||||
ApiSuccessResponse,
|
||||
CreateEventTypeInput_2024_06_14,
|
||||
EventTypeOutput_2024_06_14,
|
||||
NameFieldInput_2024_06_14,
|
||||
UpdateEventTypeInput_2024_06_14,
|
||||
} from "@calcom/platform-types";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
@@ -199,6 +200,13 @@ describe("Event types Endpoints", () => {
|
||||
});
|
||||
|
||||
it("should create an event type", async () => {
|
||||
const nameBookingField: NameFieldInput_2024_06_14 = {
|
||||
type: "name",
|
||||
label: "Your name sir / madam",
|
||||
placeholder: "john doe",
|
||||
disableOnPrefill: false,
|
||||
};
|
||||
|
||||
const body: CreateEventTypeInput_2024_06_14 = {
|
||||
title: "Coding class",
|
||||
slug: "coding-class",
|
||||
@@ -209,8 +217,18 @@ describe("Event types Endpoints", () => {
|
||||
type: "integration",
|
||||
integration: "cal-video",
|
||||
},
|
||||
{
|
||||
type: "attendeePhone",
|
||||
},
|
||||
{
|
||||
type: "attendeeAddress",
|
||||
},
|
||||
{
|
||||
type: "attendeeDefined",
|
||||
},
|
||||
],
|
||||
bookingFields: [
|
||||
nameBookingField,
|
||||
{
|
||||
type: "select",
|
||||
label: "select which language you want to learn",
|
||||
@@ -218,6 +236,7 @@ describe("Event types Endpoints", () => {
|
||||
required: true,
|
||||
placeholder: "select language",
|
||||
options: ["javascript", "python", "cobol"],
|
||||
disableOnPrefill: true,
|
||||
},
|
||||
],
|
||||
scheduleId: firstSchedule.id,
|
||||
@@ -304,12 +323,16 @@ describe("Event types Endpoints", () => {
|
||||
);
|
||||
expect(createdEventType.color).toEqual(body.color);
|
||||
|
||||
const responseBookingFields = body.bookingFields || [];
|
||||
const requestBookingFields = body.bookingFields || [];
|
||||
const expectedBookingFields = [
|
||||
{ isDefault: true, required: true, slug: "name", type: "name" },
|
||||
{ isDefault: true, required: true, slug: "name", ...nameBookingField },
|
||||
{ isDefault: true, required: true, slug: "email", type: "email" },
|
||||
// note(Lauris): location booking field is added if multiple locations are passed
|
||||
{ isDefault: true, required: false, slug: "location", type: "radioInput" },
|
||||
{ isDefault: true, required: false, slug: "rescheduleReason", type: "textarea" },
|
||||
...responseBookingFields.map((field) => ({ isDefault: false, ...field })),
|
||||
...requestBookingFields
|
||||
.filter((field) => field.type !== "name" && field.type !== "email")
|
||||
.map((field) => ({ isDefault: false, ...field })),
|
||||
];
|
||||
|
||||
expect(createdEventType.bookingFields).toEqual(expectedBookingFields);
|
||||
@@ -630,11 +653,30 @@ describe("Event types Endpoints", () => {
|
||||
});
|
||||
|
||||
it("should update event type", async () => {
|
||||
const nameBookingField: NameFieldInput_2024_06_14 = {
|
||||
type: "name",
|
||||
label: "Your name sir / madam",
|
||||
placeholder: "john doe",
|
||||
disableOnPrefill: true,
|
||||
};
|
||||
|
||||
const newTitle = "Coding class in Italian!";
|
||||
|
||||
const body: UpdateEventTypeInput_2024_06_14 = {
|
||||
title: newTitle,
|
||||
scheduleId: secondSchedule.id,
|
||||
bookingFields: [
|
||||
nameBookingField,
|
||||
{
|
||||
type: "select",
|
||||
label: "select which language you want to learn",
|
||||
slug: "select-language",
|
||||
required: true,
|
||||
placeholder: "select language",
|
||||
options: ["javascript", "python", "cobol"],
|
||||
disableOnPrefill: false,
|
||||
},
|
||||
],
|
||||
bookingLimitsCount: {
|
||||
day: 4,
|
||||
week: 10,
|
||||
@@ -694,7 +736,19 @@ describe("Event types Endpoints", () => {
|
||||
expect(updatedEventType.description).toEqual(eventType.description);
|
||||
expect(updatedEventType.lengthInMinutes).toEqual(eventType.lengthInMinutes);
|
||||
expect(updatedEventType.locations).toEqual(eventType.locations);
|
||||
expect(updatedEventType.bookingFields).toEqual(eventType.bookingFields);
|
||||
|
||||
const requestBookingFields = body.bookingFields || [];
|
||||
const expectedBookingFields = [
|
||||
{ isDefault: true, required: true, slug: "name", ...nameBookingField },
|
||||
{ isDefault: true, required: true, slug: "email", type: "email" },
|
||||
{ isDefault: true, required: false, slug: "rescheduleReason", type: "textarea" },
|
||||
...requestBookingFields
|
||||
.filter((field) => field.type !== "name" && field.type !== "email")
|
||||
.map((field) => ({ isDefault: false, ...field })),
|
||||
];
|
||||
|
||||
expect(updatedEventType.bookingFields).toEqual(expectedBookingFields);
|
||||
|
||||
expect(updatedEventType.ownerId).toEqual(user.id);
|
||||
expect(updatedEventType.scheduleId).toEqual(secondSchedule.id);
|
||||
expect(updatedEventType.bookingLimitsCount).toEqual(body.bookingLimitsCount);
|
||||
@@ -732,6 +786,7 @@ describe("Event types Endpoints", () => {
|
||||
eventType.hideCalendarEventDetails = updatedEventType.hideCalendarEventDetails;
|
||||
eventType.lockTimeZoneToggleOnBookingPage = updatedEventType.lockTimeZoneToggleOnBookingPage;
|
||||
eventType.color = updatedEventType.color;
|
||||
eventType.bookingFields = updatedEventType.bookingFields;
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
+16
-3
@@ -20,6 +20,8 @@ import {
|
||||
transformEventColorsApiToInternal,
|
||||
validateCustomEventName,
|
||||
transformSeatsApiToInternal,
|
||||
SystemField,
|
||||
CustomField,
|
||||
} from "@calcom/platform-libraries";
|
||||
import {
|
||||
CreateEventTypeInput_2024_06_14,
|
||||
@@ -211,16 +213,27 @@ export class InputEventTypesService_2024_06_14 {
|
||||
inputBookingFields: CreateEventTypeInput_2024_06_14["bookingFields"],
|
||||
hasMultipleLocations: boolean
|
||||
) {
|
||||
const defaultFieldsBefore = [systemBeforeFieldName, systemBeforeFieldEmail];
|
||||
const customFields: (SystemField | CustomField)[] = inputBookingFields
|
||||
? transformBookingFieldsApiToInternal(inputBookingFields)
|
||||
: [];
|
||||
const customFieldsWithoutNameEmail = customFields.filter(
|
||||
(field) => field.type !== "name" && field.type !== "email"
|
||||
);
|
||||
const customNameField = customFields?.find((field) => field.type === "name");
|
||||
const customEmailField = customFields?.find((field) => field.type === "email");
|
||||
|
||||
const defaultFieldsBefore: (SystemField | CustomField)[] = [
|
||||
customNameField || systemBeforeFieldName,
|
||||
customEmailField || systemBeforeFieldEmail,
|
||||
];
|
||||
// note(Lauris): if event type has multiple locations then a radio button booking field has to be displayed to allow booker to pick location
|
||||
if (hasMultipleLocations) {
|
||||
defaultFieldsBefore.push(systemBeforeFieldLocation);
|
||||
}
|
||||
|
||||
const customFields = transformBookingFieldsApiToInternal(inputBookingFields);
|
||||
const defaultFieldsAfter = [systemAfterFieldRescheduleReason];
|
||||
|
||||
return [...defaultFieldsBefore, ...customFields, ...defaultFieldsAfter];
|
||||
return [...defaultFieldsBefore, ...customFieldsWithoutNameEmail, ...defaultFieldsAfter];
|
||||
}
|
||||
|
||||
transformInputIntervalLimits(inputBookingFields: CreateEventTypeInput_2024_06_14["bookingLimitsCount"]) {
|
||||
|
||||
+34
-5
@@ -121,11 +121,13 @@ describe("Schedules Endpoints", () => {
|
||||
expect(outputSchedule?.isDefault).toEqual(expected.isDefault);
|
||||
expect(outputSchedule?.availability.length).toEqual(expectedAvailabilityLength);
|
||||
|
||||
const outputScheduleAvailability = outputSchedule?.availability[0];
|
||||
expect(outputScheduleAvailability).toBeDefined();
|
||||
expect(outputScheduleAvailability?.days).toEqual(expected.availability?.[0].days);
|
||||
expect(outputScheduleAvailability?.startTime).toEqual(expected.availability?.[0].startTime);
|
||||
expect(outputScheduleAvailability?.endTime).toEqual(expected.availability?.[0].endTime);
|
||||
if (expectedAvailabilityLength) {
|
||||
const outputScheduleAvailability = outputSchedule?.availability[0];
|
||||
expect(outputScheduleAvailability).toBeDefined();
|
||||
expect(outputScheduleAvailability?.days).toEqual(expected.availability?.[0].days);
|
||||
expect(outputScheduleAvailability?.startTime).toEqual(expected.availability?.[0].startTime);
|
||||
expect(outputScheduleAvailability?.endTime).toEqual(expected.availability?.[0].endTime);
|
||||
}
|
||||
|
||||
expect(JSON.stringify(outputSchedule?.overrides)).toEqual(JSON.stringify(expected.overrides));
|
||||
}
|
||||
@@ -222,6 +224,33 @@ describe("Schedules Endpoints", () => {
|
||||
});
|
||||
});
|
||||
|
||||
it("should empty availabilities and overrides", async () => {
|
||||
const body: UpdateScheduleInput_2024_06_11 = {
|
||||
availability: [],
|
||||
overrides: [],
|
||||
};
|
||||
|
||||
return request(app.getHttpServer())
|
||||
.patch(`/api/v2/schedules/${createdSchedule.id}`)
|
||||
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_11)
|
||||
.send(body)
|
||||
.expect(200)
|
||||
.then((response: any) => {
|
||||
const responseData: UpdateScheduleOutput_2024_06_11 = response.body;
|
||||
expect(responseData.status).toEqual(SUCCESS_STATUS);
|
||||
const responseSchedule = responseData.data;
|
||||
|
||||
const expectedSchedule = {
|
||||
...createdSchedule,
|
||||
overrides: body.overrides,
|
||||
availability: body.availability,
|
||||
};
|
||||
outputScheduleMatchesExpected(responseSchedule, expectedSchedule, 0);
|
||||
|
||||
createdSchedule = responseSchedule;
|
||||
});
|
||||
});
|
||||
|
||||
it("should delete schedule", async () => {
|
||||
return request(app.getHttpServer()).delete(`/api/v2/schedules/${createdSchedule.id}`).expect(200);
|
||||
});
|
||||
|
||||
@@ -119,14 +119,17 @@ export class SchedulesRepository_2024_06_11 {
|
||||
timeZone: schedule.timeZone,
|
||||
};
|
||||
|
||||
const availabilitiesAndOverrides: Prisma.AvailabilityCreateManyInput[] = [];
|
||||
const createAvailabilityStatements: Prisma.AvailabilityCreateManyInput[] = [];
|
||||
const createOverridesStatements: Prisma.AvailabilityCreateManyInput[] = [];
|
||||
|
||||
const deleteAvailabilityStatements: Prisma.AvailabilityWhereInput[] = [];
|
||||
const deleteOverridesStatements: Prisma.AvailabilityWhereInput[] = [];
|
||||
|
||||
const deleteConditions = [];
|
||||
if (availability) {
|
||||
// note(Lauris): availabilities and overrides are stored in the same "Availability" table,
|
||||
// but availabilities have "date" field as null, while overrides have it as not null, so delete
|
||||
// condition below results in deleting only rows from Availability table that are availabilities.
|
||||
deleteConditions.push({
|
||||
deleteAvailabilityStatements.push({
|
||||
scheduleId: { equals: scheduleId },
|
||||
date: null,
|
||||
});
|
||||
@@ -136,7 +139,7 @@ export class SchedulesRepository_2024_06_11 {
|
||||
// note(Lauris): availabilities and overrides are stored in the same "Availability" table,
|
||||
// but overrides have "date" field as not-null, while availabilities have it as null, so delete
|
||||
// condition below results in deleting only rows from Availability table that are overrides.
|
||||
deleteConditions.push({
|
||||
deleteOverridesStatements.push({
|
||||
scheduleId: { equals: scheduleId },
|
||||
NOT: { date: null },
|
||||
});
|
||||
@@ -144,7 +147,7 @@ export class SchedulesRepository_2024_06_11 {
|
||||
|
||||
if (availability && availability.length > 0) {
|
||||
availability.forEach((availability) => {
|
||||
availabilitiesAndOverrides.push({
|
||||
createAvailabilityStatements.push({
|
||||
days: availability.days,
|
||||
startTime: availability.startTime,
|
||||
endTime: availability.endTime,
|
||||
@@ -155,7 +158,7 @@ export class SchedulesRepository_2024_06_11 {
|
||||
|
||||
if (overrides && overrides.length > 0) {
|
||||
overrides.forEach((override) => {
|
||||
availabilitiesAndOverrides.push({
|
||||
createOverridesStatements.push({
|
||||
date: override.date,
|
||||
startTime: override.startTime,
|
||||
endTime: override.endTime,
|
||||
@@ -164,11 +167,21 @@ export class SchedulesRepository_2024_06_11 {
|
||||
});
|
||||
}
|
||||
|
||||
if (availabilitiesAndOverrides.length > 0) {
|
||||
const deleteStatements = [...deleteAvailabilityStatements, ...deleteOverridesStatements];
|
||||
const createStatements = [...createAvailabilityStatements, ...createOverridesStatements];
|
||||
|
||||
if (deleteStatements.length > 0) {
|
||||
updateScheduleData.availability = {
|
||||
deleteMany: deleteConditions,
|
||||
deleteMany: deleteStatements,
|
||||
};
|
||||
}
|
||||
|
||||
if (createStatements.length > 0) {
|
||||
updateScheduleData.availability = {
|
||||
// note(Lauris): keep deleteMany statements
|
||||
...(updateScheduleData.availability || {}),
|
||||
createMany: {
|
||||
data: availabilitiesAndOverrides,
|
||||
data: createStatements,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,8 +1,11 @@
|
||||
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
|
||||
import { CallHandler, ExecutionContext, Injectable, NestInterceptor, Logger } from "@nestjs/common";
|
||||
import { Request, Response } from "express";
|
||||
import { tap } from "rxjs/operators";
|
||||
|
||||
@Injectable()
|
||||
export class ResponseInterceptor implements NestInterceptor {
|
||||
private readonly logger = new Logger("ResponseInterceptor - NestInterceptor");
|
||||
|
||||
intercept(context: ExecutionContext, next: CallHandler) {
|
||||
const ctx = context.switchToHttp();
|
||||
const request = ctx.getRequest<Request>();
|
||||
@@ -10,7 +13,28 @@ export class ResponseInterceptor implements NestInterceptor {
|
||||
|
||||
const requestId = request.headers["X-Request-Id"] ?? "unknown-request-id";
|
||||
response.setHeader("X-Request-Id", requestId.toString());
|
||||
const { method, url } = request;
|
||||
const startTime = Date.now();
|
||||
|
||||
return next.handle();
|
||||
return next.handle().pipe(
|
||||
tap((data) => {
|
||||
const { statusCode } = response;
|
||||
const responseTime = Date.now() - startTime;
|
||||
|
||||
// Log response in JSON format
|
||||
this.logger.log(
|
||||
JSON.stringify({
|
||||
requestId,
|
||||
method,
|
||||
url,
|
||||
statusCode,
|
||||
responseTime,
|
||||
responseBody: data,
|
||||
timestamp: new Date().toISOString(),
|
||||
message: "Outgoing Response",
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,11 +1,28 @@
|
||||
import { Injectable, NestMiddleware } from "@nestjs/common";
|
||||
import { Injectable, NestMiddleware, Logger } from "@nestjs/common";
|
||||
import { Request, Response, NextFunction } from "express";
|
||||
import { v4 as uuid } from "uuid";
|
||||
|
||||
@Injectable()
|
||||
export class RequestIdMiddleware implements NestMiddleware {
|
||||
private readonly logger = new Logger("RequestIdMiddleware - NestMiddleware");
|
||||
|
||||
use(req: Request, res: Response, next: NextFunction) {
|
||||
req.headers["X-Request-Id"] = uuid();
|
||||
const requestId = uuid();
|
||||
req.headers["X-Request-Id"] = requestId;
|
||||
const { method, headers, body: requestBody, baseUrl } = req;
|
||||
|
||||
this.logger.log(
|
||||
JSON.stringify({
|
||||
requestId,
|
||||
method,
|
||||
url: baseUrl,
|
||||
headers,
|
||||
requestBody,
|
||||
timestamp: new Date().toISOString(),
|
||||
message: "Incoming Request",
|
||||
})
|
||||
);
|
||||
|
||||
next();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_0
|
||||
import { SchedulesRepository_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/schedules.repository";
|
||||
import { AtomsController } from "@/modules/atoms/controllers/atoms.controller";
|
||||
import { EventTypesAtomService } from "@/modules/atoms/services/event-types-atom.service";
|
||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||
import { OrganizationsModule } from "@/modules/organizations/organizations.module";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
@@ -14,6 +15,7 @@ import { Module } from "@nestjs/common";
|
||||
providers: [
|
||||
EventTypesAtomService,
|
||||
MembershipsRepository,
|
||||
CredentialsRepository,
|
||||
UsersRepository,
|
||||
UsersService,
|
||||
SchedulesRepository_2024_06_11,
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||
import { EventTypesAppInput } from "@/modules/atoms/inputs/event-types-app.input";
|
||||
import { EventTypesAtomService } from "@/modules/atoms/services/event-types-atom.service";
|
||||
import { GetUser } from "@/modules/auth/decorators/get-user/get-user.decorator";
|
||||
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
|
||||
@@ -13,6 +14,7 @@ import {
|
||||
VERSION_NEUTRAL,
|
||||
Patch,
|
||||
Body,
|
||||
Query,
|
||||
} from "@nestjs/common";
|
||||
import { ApiTags as DocsTags, ApiExcludeController as DocsExcludeController } from "@nestjs/swagger";
|
||||
|
||||
@@ -50,6 +52,26 @@ export class AtomsController {
|
||||
};
|
||||
}
|
||||
|
||||
@Get("event-types-app/:appSlug")
|
||||
@Version(VERSION_NEUTRAL)
|
||||
@UseGuards(ApiAuthGuard)
|
||||
async getAtomEventTypeApp(
|
||||
@GetUser() user: UserWithProfile,
|
||||
@Param("appSlug") appSlug: string,
|
||||
@Query() queryParams: EventTypesAppInput
|
||||
): Promise<ApiResponse<unknown>> {
|
||||
const { teamId } = queryParams;
|
||||
|
||||
const app = await this.eventTypesService.getEventTypesAppIntegration(appSlug, user.id, user.name, teamId);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: {
|
||||
app,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@Patch("event-types/:eventTypeId")
|
||||
@Version(VERSION_NEUTRAL)
|
||||
@UseGuards(ApiAuthGuard)
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import { Transform } from "class-transformer";
|
||||
import { IsNumber, IsOptional } from "class-validator";
|
||||
|
||||
export class EventTypesAppInput {
|
||||
@Transform(({ value }: { value: string }) => value && parseInt(value))
|
||||
@IsNumber()
|
||||
@IsOptional()
|
||||
teamId?: number;
|
||||
}
|
||||
@@ -1,5 +1,7 @@
|
||||
import { EventTypesService_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/services/event-types.service";
|
||||
import { CredentialsRepository } from "@/modules/credentials/credentials.repository";
|
||||
import { MembershipsRepository } from "@/modules/memberships/memberships.repository";
|
||||
import { OrganizationsTeamsRepository } from "@/modules/organizations/repositories/organizations-teams.repository";
|
||||
import { OrganizationsEventTypesService } from "@/modules/organizations/services/event-types/organizations-event-types.service";
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
@@ -12,13 +14,33 @@ import {
|
||||
TUpdateEventTypeInputSchema,
|
||||
systemBeforeFieldEmail,
|
||||
getEventTypeById,
|
||||
getEnabledAppsFromCredentials,
|
||||
getAppFromSlug,
|
||||
MembershipRole,
|
||||
} from "@calcom/platform-libraries";
|
||||
import type {
|
||||
App,
|
||||
CredentialDataWithTeamName,
|
||||
LocationOption,
|
||||
TeamQuery,
|
||||
CredentialOwner,
|
||||
TDependencyData,
|
||||
CredentialPayload,
|
||||
} from "@calcom/platform-libraries";
|
||||
import { PrismaClient } from "@calcom/prisma/client";
|
||||
|
||||
type EnabledAppType = App & {
|
||||
credential: CredentialDataWithTeamName;
|
||||
credentials: CredentialDataWithTeamName[];
|
||||
locationOption: LocationOption | null;
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
export class EventTypesAtomService {
|
||||
constructor(
|
||||
private readonly membershipsRepository: MembershipsRepository,
|
||||
private readonly credentialsRepository: CredentialsRepository,
|
||||
private readonly organizationsTeamsRepository: OrganizationsTeamsRepository,
|
||||
private readonly usersService: UsersService,
|
||||
private readonly dbWrite: PrismaWriteService,
|
||||
private readonly dbRead: PrismaReadService,
|
||||
@@ -146,4 +168,110 @@ export class EventTypesAtomService {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
async getEventTypesAppIntegration(slug: string, userId: number, userName: string | null, teamId?: number) {
|
||||
let credentials = await this.credentialsRepository.getAllUserCredentialsById(userId);
|
||||
let userTeams: TeamQuery[] = [];
|
||||
if (teamId) {
|
||||
const teamsQuery = await this.organizationsTeamsRepository.getUserTeamsById(userId);
|
||||
// If a team is a part of an org then include those apps
|
||||
// Don't want to iterate over these parent teams
|
||||
const filteredTeams: TeamQuery[] = [];
|
||||
const parentTeams: TeamQuery[] = [];
|
||||
// Only loop and grab parent teams if a teamId was given. If not then all teams will be queried
|
||||
if (teamId) {
|
||||
teamsQuery.forEach((team) => {
|
||||
if (team?.parent) {
|
||||
const { parent, ...filteredTeam } = team;
|
||||
filteredTeams.push(filteredTeam);
|
||||
// Only add parent team if it's not already in teamsQuery
|
||||
if (!teamsQuery.some((t) => t.id === parent.id)) {
|
||||
parentTeams.push(parent);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
userTeams = [...teamsQuery, ...parentTeams];
|
||||
const teamAppCredentials: CredentialPayload[] = userTeams.flatMap((teamApp) => {
|
||||
return teamApp.credentials ? teamApp.credentials.flat() : [];
|
||||
});
|
||||
if (teamId) {
|
||||
credentials = teamAppCredentials;
|
||||
} else {
|
||||
credentials = credentials.concat(teamAppCredentials);
|
||||
}
|
||||
}
|
||||
const enabledApps = await getEnabledAppsFromCredentials(credentials, {
|
||||
where: { slug },
|
||||
});
|
||||
const apps = await Promise.all(
|
||||
enabledApps
|
||||
.filter(({ ...app }) => app.slug === slug)
|
||||
.map(
|
||||
async ({
|
||||
credentials: _,
|
||||
credential,
|
||||
key: _2 /* don't leak to frontend */,
|
||||
...app
|
||||
}: EnabledAppType) => {
|
||||
const userCredentialIds = credentials
|
||||
.filter((c) => c.appId === app.slug && !c.teamId)
|
||||
.map((c) => c.id);
|
||||
const invalidCredentialIds = credentials
|
||||
.filter((c) => c.appId === app.slug && c.invalid)
|
||||
.map((c) => c.id);
|
||||
const teams = await Promise.all(
|
||||
credentials
|
||||
.filter((c) => c.appId === app.slug && c.teamId)
|
||||
.map(async (c) => {
|
||||
const team = userTeams.find((team) => team.id === c.teamId);
|
||||
if (!team) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
teamId: team.id,
|
||||
name: team.name,
|
||||
logoUrl: team.logoUrl,
|
||||
credentialId: c.id,
|
||||
isAdmin:
|
||||
team.members[0].role === MembershipRole.ADMIN ||
|
||||
team.members[0].role === MembershipRole.OWNER,
|
||||
};
|
||||
})
|
||||
);
|
||||
const isSetupAlready = credential && app.categories.includes("payment") ? true : undefined;
|
||||
let dependencyData: TDependencyData = [];
|
||||
if (app.dependencies?.length) {
|
||||
dependencyData = app.dependencies.map((dependency) => {
|
||||
const dependencyInstalled = enabledApps.some(
|
||||
(dbAppIterator: EnabledAppType) =>
|
||||
dbAppIterator.credentials.length && dbAppIterator.slug === dependency
|
||||
);
|
||||
// If the app marked as dependency is simply deleted from the codebase,
|
||||
// we can have the situation where App is marked installed in DB but we couldn't get the app.
|
||||
const dependencyName = getAppFromSlug(dependency)?.name;
|
||||
return { name: dependencyName, installed: dependencyInstalled };
|
||||
});
|
||||
}
|
||||
const credentialOwner: CredentialOwner = {
|
||||
name: userName,
|
||||
teamId,
|
||||
};
|
||||
return {
|
||||
...app,
|
||||
...(teams.length && {
|
||||
credentialOwner,
|
||||
}),
|
||||
userCredentialIds,
|
||||
invalidCredentialIds,
|
||||
teams,
|
||||
isInstalled: !!userCredentialIds.length || !!teams.length || app.isGlobal,
|
||||
isSetupAlready,
|
||||
...(app.dependencies && { dependencyData }),
|
||||
};
|
||||
}
|
||||
)
|
||||
);
|
||||
return apps[0];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
import { Injectable, CanActivate, ExecutionContext, ForbiddenException } from "@nestjs/common";
|
||||
import { Request } from "express";
|
||||
|
||||
import { Team } from "@calcom/prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class IsUserInOrgTeam implements CanActivate {
|
||||
constructor(private organizationsRepository: OrganizationsRepository) {}
|
||||
|
||||
async canActivate(context: ExecutionContext): Promise<boolean> {
|
||||
const request = context.switchToHttp().getRequest<Request & { team: Team }>();
|
||||
const teamId: string = request.params.teamId;
|
||||
const orgId: string = request.params.orgId;
|
||||
const userId: string = request.params.userId;
|
||||
|
||||
if (!userId) {
|
||||
throw new ForbiddenException("No user id found in request params.");
|
||||
}
|
||||
|
||||
if (!orgId) {
|
||||
throw new ForbiddenException("No org id found in request params.");
|
||||
}
|
||||
|
||||
if (!teamId) {
|
||||
throw new ForbiddenException("No team id found in request params.");
|
||||
}
|
||||
|
||||
const user = await this.organizationsRepository.findOrgTeamUser(
|
||||
Number(orgId),
|
||||
Number(teamId),
|
||||
Number(userId)
|
||||
);
|
||||
|
||||
if (user) {
|
||||
request.user = user;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
import { BookingSeatRepository } from "@/modules/booking-seat/booking-seat.repository";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { Module } from "@nestjs/common";
|
||||
|
||||
@Module({
|
||||
imports: [PrismaModule],
|
||||
providers: [BookingSeatRepository],
|
||||
exports: [BookingSeatRepository],
|
||||
})
|
||||
export class BookingSeatModule {}
|
||||
@@ -0,0 +1,17 @@
|
||||
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
@Injectable()
|
||||
export class BookingSeatRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
|
||||
|
||||
async getByReferenceUid(referenceUid: string) {
|
||||
return this.dbRead.prisma.bookingSeat.findUnique({
|
||||
where: {
|
||||
referenceUid,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@ import { Injectable } from "@nestjs/common";
|
||||
import { Prisma } from "@prisma/client";
|
||||
|
||||
import { APPS_TYPE_ID_MAPPING } from "@calcom/platform-constants";
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/platform-libraries";
|
||||
|
||||
@Injectable()
|
||||
export class CredentialsRepository {
|
||||
@@ -65,6 +66,18 @@ export class CredentialsRepository {
|
||||
});
|
||||
}
|
||||
|
||||
async getAllUserCredentialsById(userId: number) {
|
||||
return await this.dbRead.prisma.credential.findMany({
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: credentialForCalendarServiceSelect,
|
||||
orderBy: {
|
||||
id: "asc",
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async getUserCredentialById(userId: number, credentialId: number, type: string) {
|
||||
return await this.dbRead.prisma.credential.findUnique({
|
||||
where: {
|
||||
|
||||
+24
@@ -248,6 +248,30 @@ describe("OAuth Client Users Endpoints", () => {
|
||||
expect(schedule?.userId).toEqual(userId);
|
||||
}
|
||||
|
||||
it(`should fail /POST using already used managed user email`, async () => {
|
||||
const requestBody: CreateManagedUserInput = {
|
||||
email: userEmail,
|
||||
timeZone: userTimeZone,
|
||||
weekStart: "Monday",
|
||||
timeFormat: 24,
|
||||
locale: Locales.FR,
|
||||
name: "Alice Smith",
|
||||
};
|
||||
|
||||
const response = await request(app.getHttpServer())
|
||||
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
|
||||
.set("x-cal-secret-key", oAuthClient.secret)
|
||||
.send(requestBody)
|
||||
.expect(409);
|
||||
|
||||
const responseBody: CreateManagedUserOutput = response.body;
|
||||
const error = responseBody.error;
|
||||
expect(error).toBeDefined();
|
||||
expect(error?.message).toEqual(
|
||||
`User with the provided e-mail already exists. Existing user ID=${postResponseData.user.id}`
|
||||
);
|
||||
});
|
||||
|
||||
it(`/GET: return list of managed users`, async () => {
|
||||
const response = await request(app.getHttpServer())
|
||||
.get(`/api/v2/oauth-clients/${oAuthClient.id}/users?limit=10&offset=0`)
|
||||
|
||||
+2
@@ -34,4 +34,6 @@ export class CreateManagedUserOutput {
|
||||
@ValidateNested()
|
||||
@Type(() => CreateManagedUserData)
|
||||
data!: CreateManagedUserData;
|
||||
|
||||
error?: Error;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { TokensRepository } from "@/modules/tokens/tokens.repository";
|
||||
import { CreateManagedUserInput } from "@/modules/users/inputs/create-managed-user.input";
|
||||
import { UpdateManagedUserInput } from "@/modules/users/inputs/update-managed-user.input";
|
||||
import { UsersRepository } from "@/modules/users/users.repository";
|
||||
import { BadRequestException, Injectable } from "@nestjs/common";
|
||||
import { BadRequestException, ConflictException, Injectable } from "@nestjs/common";
|
||||
import { User } from "@prisma/client";
|
||||
|
||||
import { createNewUsersConnectToOrgIfExists, slugify } from "@calcom/platform-libraries";
|
||||
@@ -26,9 +26,11 @@ export class OAuthClientUsersService {
|
||||
isPlatformManaged: boolean,
|
||||
organizationId?: number
|
||||
) {
|
||||
const existsWithEmail = await this.managedUserExistsWithEmail(oAuthClientId, body.email);
|
||||
if (existsWithEmail) {
|
||||
throw new BadRequestException("User with the provided e-mail already exists.");
|
||||
const existingUser = await this.getExistingUserByEmail(oAuthClientId, body.email);
|
||||
if (existingUser) {
|
||||
throw new ConflictException(
|
||||
`User with the provided e-mail already exists. Existing user ID=${existingUser.id}`
|
||||
);
|
||||
}
|
||||
|
||||
let user: User;
|
||||
@@ -93,10 +95,9 @@ export class OAuthClientUsersService {
|
||||
};
|
||||
}
|
||||
|
||||
async managedUserExistsWithEmail(oAuthClientId: string, email: string) {
|
||||
async getExistingUserByEmail(oAuthClientId: string, email: string) {
|
||||
const oAuthEmail = this.getOAuthUserEmail(oAuthClientId, email);
|
||||
const user = await this.userRepository.findByEmail(oAuthEmail);
|
||||
return !!user;
|
||||
return await this.userRepository.findByEmail(oAuthEmail);
|
||||
}
|
||||
|
||||
async updateOAuthClientUser(oAuthClientId: string, userId: number, body: UpdateManagedUserInput) {
|
||||
|
||||
+9
-6
@@ -387,15 +387,18 @@ describe("Organizations Event Types Endpoints", () => {
|
||||
1
|
||||
);
|
||||
|
||||
const responseTeamEvent = responseBody.data[0];
|
||||
expect(responseTeamEvent?.teamId).toEqual(team.id);
|
||||
const responseTeamEvent = responseBody.data.find((event) => event.teamId === team.id);
|
||||
expect(responseTeamEvent).toBeDefined();
|
||||
if (!responseTeamEvent) {
|
||||
throw new Error("Team event not found");
|
||||
}
|
||||
|
||||
const responseTeammate1Event = responseBody.data[1];
|
||||
expect(responseTeammate1Event?.ownerId).toEqual(teammate1.id);
|
||||
const responseTeammate1Event = responseBody.data.find((event) => event.ownerId === teammate1.id);
|
||||
expect(responseTeammate1Event).toBeDefined();
|
||||
expect(responseTeammate1Event?.parentEventTypeId).toEqual(responseTeamEvent?.id);
|
||||
|
||||
const responseTeammate2Event = responseBody.data[2];
|
||||
expect(responseTeammate2Event?.ownerId).toEqual(teammate2.id);
|
||||
const responseTeammate2Event = responseBody.data.find((event) => event.ownerId === teammate2.id);
|
||||
expect(responseTeammate2Event).toBeDefined();
|
||||
expect(responseTeammate2Event?.parentEventTypeId).toEqual(responseTeamEvent?.id);
|
||||
|
||||
managedEventType = responseTeamEvent;
|
||||
|
||||
+10
-7
@@ -2,6 +2,7 @@ import { bootstrap } from "@/app";
|
||||
import { AppModule } from "@/app.module";
|
||||
import { CreateOrgTeamMembershipDto } from "@/modules/organizations/inputs/create-organization-team-membership.input";
|
||||
import { UpdateOrgTeamMembershipDto } from "@/modules/organizations/inputs/update-organization-team-membership.input";
|
||||
import { OrgTeamMembershipOutputDto } from "@/modules/organizations/outputs/organization-teams-memberships.output";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||
import { UsersModule } from "@/modules/users/users.module";
|
||||
@@ -37,7 +38,7 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
let nonOrgTeam: Team;
|
||||
let membership: Membership;
|
||||
let membership2: Membership;
|
||||
let membershipCreatedViaApi: Membership;
|
||||
let membershipCreatedViaApi: OrgTeamMembershipOutputDto;
|
||||
|
||||
const userEmail = "org-admin-membership-teams-controller-e2e@api.com";
|
||||
const userEmail2 = "org-member-membership-teams-controller-e2e@api.com";
|
||||
@@ -200,7 +201,7 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
.get(`/v2/organizations/${org.id}/teams/${orgTeam.id}/memberships`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<Membership[]> = response.body;
|
||||
const responseBody: ApiSuccessResponse<OrgTeamMembershipOutputDto[]> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data[0].id).toEqual(membership.id);
|
||||
expect(responseBody.data[1].id).toEqual(membership2.id);
|
||||
@@ -219,7 +220,7 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
.get(`/v2/organizations/${org.id}/teams/${orgTeam.id}/memberships?skip=1&take=1`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<Membership[]> = response.body;
|
||||
const responseBody: ApiSuccessResponse<OrgTeamMembershipOutputDto[]> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data[0].id).toEqual(membership2.id);
|
||||
expect(responseBody.data[0].userId).toEqual(user2.id);
|
||||
@@ -238,10 +239,11 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
.get(`/v2/organizations/${org.id}/teams/${orgTeam.id}/memberships/${membership.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<Membership> = response.body;
|
||||
const responseBody: ApiSuccessResponse<OrgTeamMembershipOutputDto> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data.id).toEqual(membership.id);
|
||||
expect(responseBody.data.userId).toEqual(user.id);
|
||||
expect(responseBody.data.user.email).toEqual(user.email);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -272,12 +274,13 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
} satisfies CreateOrgTeamMembershipDto)
|
||||
.expect(201)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<Membership> = response.body;
|
||||
const responseBody: ApiSuccessResponse<OrgTeamMembershipOutputDto> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
membershipCreatedViaApi = responseBody.data;
|
||||
expect(membershipCreatedViaApi.teamId).toEqual(orgTeam.id);
|
||||
expect(membershipCreatedViaApi.role).toEqual("MEMBER");
|
||||
expect(membershipCreatedViaApi.userId).toEqual(userToInviteViaApi.id);
|
||||
expect(membershipCreatedViaApi.user.email).toEqual(userToInviteViaApi.email);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -300,7 +303,7 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
} satisfies UpdateOrgTeamMembershipDto)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<Membership> = response.body;
|
||||
const responseBody: ApiSuccessResponse<OrgTeamMembershipOutputDto> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
membershipCreatedViaApi = responseBody.data;
|
||||
expect(membershipCreatedViaApi.role).toEqual("OWNER");
|
||||
@@ -312,7 +315,7 @@ describe("Organizations Teams Memberships Endpoints", () => {
|
||||
.delete(`/v2/organizations/${org.id}/teams/${orgTeam.id}/memberships/${membershipCreatedViaApi.id}`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<Membership> = response.body;
|
||||
const responseBody: ApiSuccessResponse<OrgTeamMembershipOutputDto> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data.id).toEqual(membershipCreatedViaApi.id);
|
||||
});
|
||||
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { SchedulesService_2024_06_11 } from "@/ee/schedules/schedules_2024_06_11/services/schedules.service";
|
||||
import { API_VERSIONS_VALUES } from "@/lib/api-versions";
|
||||
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";
|
||||
import { PlatformPlanGuard } from "@/modules/auth/guards/billing/platform-plan.guard";
|
||||
import { IsAdminAPIEnabledGuard } from "@/modules/auth/guards/organizations/is-admin-api-enabled.guard";
|
||||
import { IsOrgGuard } from "@/modules/auth/guards/organizations/is-org.guard";
|
||||
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 { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { GetSchedulesOutput_2024_06_11 } from "@calcom/platform-types";
|
||||
|
||||
@Controller({
|
||||
path: "/v2/organizations/:orgId/teams/:teamId",
|
||||
version: API_VERSIONS_VALUES,
|
||||
})
|
||||
@UseGuards(ApiAuthGuard, IsOrgGuard, IsTeamInOrg, RolesGuard, PlatformPlanGuard, IsAdminAPIEnabledGuard)
|
||||
@DocsTags("Orgs / Teams / Schedules")
|
||||
export class OrganizationsTeamsSchedulesController {
|
||||
constructor(private schedulesService: SchedulesService_2024_06_11) {}
|
||||
|
||||
@Roles("TEAM_ADMIN")
|
||||
@PlatformPlan("ESSENTIALS")
|
||||
@UseGuards(IsUserInOrgTeam)
|
||||
@Get("/users/:userId/schedules")
|
||||
@DocsTags("Orgs / Teams / Users / Schedules")
|
||||
@ApiOperation({ summary: "Get schedules of a team member" })
|
||||
async getUserSchedules(
|
||||
@Param("userId", ParseIntPipe) userId: number
|
||||
): Promise<GetSchedulesOutput_2024_06_11> {
|
||||
const schedules = await this.schedulesService.getUserSchedules(userId);
|
||||
|
||||
return {
|
||||
status: SUCCESS_STATUS,
|
||||
data: schedules,
|
||||
};
|
||||
}
|
||||
}
|
||||
+227
@@ -0,0 +1,227 @@
|
||||
import { bootstrap } from "@/app";
|
||||
import { AppModule } from "@/app.module";
|
||||
import { PrismaModule } from "@/modules/prisma/prisma.module";
|
||||
import { TokensModule } from "@/modules/tokens/tokens.module";
|
||||
import { UsersModule } from "@/modules/users/users.module";
|
||||
import { INestApplication } from "@nestjs/common";
|
||||
import { NestExpressApplication } from "@nestjs/platform-express";
|
||||
import { Test } from "@nestjs/testing";
|
||||
import { User } from "@prisma/client";
|
||||
import * as request from "supertest";
|
||||
import { MembershipRepositoryFixture } from "test/fixtures/repository/membership.repository.fixture";
|
||||
import { OrganizationRepositoryFixture } from "test/fixtures/repository/organization.repository.fixture";
|
||||
import { ProfileRepositoryFixture } from "test/fixtures/repository/profiles.repository.fixture";
|
||||
import { SchedulesRepositoryFixture } from "test/fixtures/repository/schedules.repository.fixture";
|
||||
import { TeamRepositoryFixture } from "test/fixtures/repository/team.repository.fixture";
|
||||
import { UserRepositoryFixture } from "test/fixtures/repository/users.repository.fixture";
|
||||
import { withApiAuth } from "test/utils/withApiAuth";
|
||||
|
||||
import { SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
import { ApiSuccessResponse, ScheduleOutput_2024_06_11 } from "@calcom/platform-types";
|
||||
import { Team, Schedule } from "@calcom/prisma/client";
|
||||
|
||||
describe("Organizations Teams Schedules Endpoints", () => {
|
||||
describe("User Authentication - User is Org Admin", () => {
|
||||
let app: INestApplication;
|
||||
|
||||
let userRepositoryFixture: UserRepositoryFixture;
|
||||
let organizationsRepositoryFixture: OrganizationRepositoryFixture;
|
||||
let teamsRepositoryFixture: TeamRepositoryFixture;
|
||||
let profileRepositoryFixture: ProfileRepositoryFixture;
|
||||
let scheduleRepositoryFixture: SchedulesRepositoryFixture;
|
||||
|
||||
let membershipsRepositoryFixture: MembershipRepositoryFixture;
|
||||
|
||||
let org: Team;
|
||||
let orgTeam: Team;
|
||||
let nonOrgTeam: Team;
|
||||
let user2Schedule: Schedule;
|
||||
|
||||
const userEmail = "org-admin-schedules-teams-controller-e2e@api.com";
|
||||
const userEmail2 = "org-member-schedules-teams-controller-e2e@api.com";
|
||||
const nonOrgUserEmail = "non-org-member-schedules-teams-controller-e2e@api.com";
|
||||
|
||||
const invitedUserEmail = "org-member-invited-schedules-teams-controller-e2e@api.com";
|
||||
|
||||
let user: User;
|
||||
let user2: User;
|
||||
let nonOrgUser: User;
|
||||
|
||||
let userToInviteViaApi: User;
|
||||
|
||||
beforeAll(async () => {
|
||||
const moduleRef = await withApiAuth(
|
||||
userEmail,
|
||||
Test.createTestingModule({
|
||||
imports: [AppModule, PrismaModule, UsersModule, TokensModule],
|
||||
})
|
||||
).compile();
|
||||
|
||||
userRepositoryFixture = new UserRepositoryFixture(moduleRef);
|
||||
organizationsRepositoryFixture = new OrganizationRepositoryFixture(moduleRef);
|
||||
teamsRepositoryFixture = new TeamRepositoryFixture(moduleRef);
|
||||
profileRepositoryFixture = new ProfileRepositoryFixture(moduleRef);
|
||||
scheduleRepositoryFixture = new SchedulesRepositoryFixture(moduleRef);
|
||||
membershipsRepositoryFixture = new MembershipRepositoryFixture(moduleRef);
|
||||
|
||||
user = await userRepositoryFixture.create({
|
||||
email: userEmail,
|
||||
username: userEmail,
|
||||
});
|
||||
user2 = await userRepositoryFixture.create({
|
||||
email: userEmail2,
|
||||
username: userEmail2,
|
||||
});
|
||||
|
||||
user2Schedule = await scheduleRepositoryFixture.create({
|
||||
user: {
|
||||
connect: {
|
||||
id: user2.id,
|
||||
},
|
||||
},
|
||||
name: "User 2 schedule",
|
||||
timeZone: "America/New_York",
|
||||
});
|
||||
|
||||
nonOrgUser = await userRepositoryFixture.create({
|
||||
email: nonOrgUserEmail,
|
||||
username: nonOrgUserEmail,
|
||||
});
|
||||
|
||||
userToInviteViaApi = await userRepositoryFixture.create({
|
||||
email: invitedUserEmail,
|
||||
username: invitedUserEmail,
|
||||
});
|
||||
|
||||
org = await organizationsRepositoryFixture.create({
|
||||
name: "Test Organization",
|
||||
isOrganization: true,
|
||||
});
|
||||
|
||||
orgTeam = await teamsRepositoryFixture.create({
|
||||
name: "Org Team",
|
||||
isOrganization: false,
|
||||
parent: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
nonOrgTeam = await teamsRepositoryFixture.create({
|
||||
name: "Non Org Team",
|
||||
isOrganization: false,
|
||||
});
|
||||
|
||||
await membershipsRepositoryFixture.create({
|
||||
role: "ADMIN",
|
||||
user: { connect: { id: user.id } },
|
||||
team: { connect: { id: orgTeam.id } },
|
||||
});
|
||||
|
||||
await membershipsRepositoryFixture.create({
|
||||
role: "MEMBER",
|
||||
user: { connect: { id: user2.id } },
|
||||
team: { connect: { id: orgTeam.id } },
|
||||
});
|
||||
|
||||
await membershipsRepositoryFixture.create({
|
||||
role: "ADMIN",
|
||||
user: { connect: { id: user.id } },
|
||||
team: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
await membershipsRepositoryFixture.create({
|
||||
role: "MEMBER",
|
||||
user: { connect: { id: user2.id } },
|
||||
team: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
await membershipsRepositoryFixture.create({
|
||||
role: "MEMBER",
|
||||
user: { connect: { id: userToInviteViaApi.id } },
|
||||
team: { connect: { id: org.id } },
|
||||
});
|
||||
|
||||
await membershipsRepositoryFixture.create({
|
||||
role: "MEMBER",
|
||||
user: { connect: { id: nonOrgUser.id } },
|
||||
team: { connect: { id: nonOrgTeam.id } },
|
||||
});
|
||||
|
||||
await profileRepositoryFixture.create({
|
||||
uid: `usr-${user.id}`,
|
||||
username: userEmail,
|
||||
organization: {
|
||||
connect: {
|
||||
id: org.id,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: user.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await profileRepositoryFixture.create({
|
||||
uid: `usr-${user2.id}`,
|
||||
username: userEmail2,
|
||||
organization: {
|
||||
connect: {
|
||||
id: org.id,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: user2.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await profileRepositoryFixture.create({
|
||||
uid: `usr-${userToInviteViaApi.id}`,
|
||||
username: invitedUserEmail,
|
||||
organization: {
|
||||
connect: {
|
||||
id: org.id,
|
||||
},
|
||||
},
|
||||
user: {
|
||||
connect: {
|
||||
id: userToInviteViaApi.id,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
app = moduleRef.createNestApplication();
|
||||
bootstrap(app as NestExpressApplication);
|
||||
|
||||
await app.init();
|
||||
});
|
||||
|
||||
it("should be defined", () => {
|
||||
expect(userRepositoryFixture).toBeDefined();
|
||||
expect(organizationsRepositoryFixture).toBeDefined();
|
||||
expect(user).toBeDefined();
|
||||
expect(org).toBeDefined();
|
||||
});
|
||||
|
||||
it("should get all the schedule of the org's team's member", async () => {
|
||||
return request(app.getHttpServer())
|
||||
.get(`/v2/organizations/${org.id}/teams/${orgTeam.id}/users/${user2.id}/schedules`)
|
||||
.expect(200)
|
||||
.then((response) => {
|
||||
const responseBody: ApiSuccessResponse<ScheduleOutput_2024_06_11[]> = response.body;
|
||||
expect(responseBody.status).toEqual(SUCCESS_STATUS);
|
||||
expect(responseBody.data.find((d) => d.id === user2Schedule.id)?.name).toEqual(user2Schedule.name);
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await userRepositoryFixture.deleteByEmail(user.email);
|
||||
await userRepositoryFixture.deleteByEmail(userToInviteViaApi.email);
|
||||
await userRepositoryFixture.deleteByEmail(nonOrgUser.email);
|
||||
await userRepositoryFixture.deleteByEmail(user2.email);
|
||||
await organizationsRepositoryFixture.delete(org.id);
|
||||
await teamsRepositoryFixture.delete(nonOrgTeam.id);
|
||||
await app.close();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -11,6 +11,7 @@ import { OutputTeamEventTypesResponsePipe } from "@/modules/organizations/contro
|
||||
import { OrganizationsSchedulesController } from "@/modules/organizations/controllers/schedules/organizations-schedules.controller";
|
||||
import { OrganizationsTeamsMembershipsController } from "@/modules/organizations/controllers/teams/memberships/organizations-teams-memberships.controller";
|
||||
import { OrganizationsTeamsController } from "@/modules/organizations/controllers/teams/organizations-teams.controller";
|
||||
import { OrganizationsTeamsSchedulesController } from "@/modules/organizations/controllers/teams/schedules/organizations-teams-schedules.controller";
|
||||
import { OrganizationsUsersController } from "@/modules/organizations/controllers/users/organizations-users.controller";
|
||||
import { OrganizationsWebhooksController } from "@/modules/organizations/controllers/webhooks/organizations-webhooks.controller";
|
||||
import { OrganizationsRepository } from "@/modules/organizations/organizations.repository";
|
||||
@@ -112,6 +113,7 @@ import { Module } from "@nestjs/common";
|
||||
OrganizationsAttributesController,
|
||||
OrganizationsOptionsAttributesController,
|
||||
OrganizationsWebhooksController,
|
||||
OrganizationsTeamsSchedulesController,
|
||||
],
|
||||
})
|
||||
export class OrganizationsModule {}
|
||||
|
||||
@@ -103,6 +103,24 @@ export class OrganizationsRepository {
|
||||
});
|
||||
}
|
||||
|
||||
async findOrgTeamUser(organizationId: number, teamId: number, userId: number) {
|
||||
return this.dbRead.prisma.user.findUnique({
|
||||
where: {
|
||||
id: userId,
|
||||
profiles: {
|
||||
some: {
|
||||
organizationId,
|
||||
},
|
||||
},
|
||||
teams: {
|
||||
some: {
|
||||
teamId: teamId,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
async fetchOrgAdminApiStatus(organizationId: number) {
|
||||
return this.dbRead.prisma.organizationSettings.findUnique({
|
||||
where: {
|
||||
|
||||
+26
@@ -5,6 +5,27 @@ import { IsArray, IsBoolean, IsEnum, IsInt, IsOptional, IsString, ValidateNested
|
||||
|
||||
import { ERROR_STATUS, SUCCESS_STATUS } from "@calcom/platform-constants";
|
||||
|
||||
export class MembershipUserOutputDto {
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Expose()
|
||||
readonly avatarUrl?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Expose()
|
||||
readonly username?: string;
|
||||
|
||||
@IsOptional()
|
||||
@IsString()
|
||||
@Expose()
|
||||
readonly name?: string;
|
||||
|
||||
@IsBoolean()
|
||||
@Expose()
|
||||
readonly email!: string;
|
||||
}
|
||||
|
||||
export class OrgTeamMembershipOutputDto {
|
||||
@IsInt()
|
||||
@Expose()
|
||||
@@ -31,6 +52,11 @@ export class OrgTeamMembershipOutputDto {
|
||||
@IsBoolean()
|
||||
@Expose()
|
||||
readonly disableImpersonation?: boolean;
|
||||
|
||||
@ValidateNested()
|
||||
@Type(() => MembershipUserOutputDto)
|
||||
@Expose()
|
||||
user!: MembershipUserOutputDto;
|
||||
}
|
||||
|
||||
export class OrgTeamMembershipsOutputResponseDto {
|
||||
|
||||
+5
-1
@@ -9,13 +9,14 @@ export class OrganizationsTeamsMembershipsRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
|
||||
|
||||
async findOrgTeamMembershipsPaginated(organizationId: number, teamId: number, skip: number, take: number) {
|
||||
return this.dbRead.prisma.membership.findMany({
|
||||
return await this.dbRead.prisma.membership.findMany({
|
||||
where: {
|
||||
teamId: teamId,
|
||||
team: {
|
||||
parentId: organizationId,
|
||||
},
|
||||
},
|
||||
include: { user: { select: { username: true, email: true, avatarUrl: true, name: true } } },
|
||||
skip,
|
||||
take,
|
||||
});
|
||||
@@ -30,6 +31,7 @@ export class OrganizationsTeamsMembershipsRepository {
|
||||
parentId: organizationId,
|
||||
},
|
||||
},
|
||||
include: { user: { select: { username: true, email: true, avatarUrl: true, name: true } } },
|
||||
});
|
||||
}
|
||||
async deleteOrgTeamMembershipById(organizationId: number, teamId: number, membershipId: number) {
|
||||
@@ -59,12 +61,14 @@ export class OrganizationsTeamsMembershipsRepository {
|
||||
parentId: organizationId,
|
||||
},
|
||||
},
|
||||
include: { user: { select: { username: true, email: true, avatarUrl: true, name: true } } },
|
||||
});
|
||||
}
|
||||
|
||||
async createOrgTeamMembership(teamId: number, data: CreateOrgTeamMembershipDto) {
|
||||
return this.dbWrite.prisma.membership.create({
|
||||
data: { ...data, teamId: teamId },
|
||||
include: { user: { select: { username: true, email: true, avatarUrl: true, name: true } } },
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
|
||||
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
|
||||
import { Injectable } from "@nestjs/common";
|
||||
|
||||
import { credentialForCalendarServiceSelect } from "@calcom/platform-libraries";
|
||||
|
||||
@Injectable()
|
||||
export class OrganizationsTeamsRepository {
|
||||
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
|
||||
@@ -119,4 +121,53 @@ export class OrganizationsTeamsRepository {
|
||||
|
||||
return team.members.map((member) => member.userId);
|
||||
}
|
||||
|
||||
async getUserTeamsById(userId: number) {
|
||||
const userTeams = await this.dbRead.prisma.team.findMany({
|
||||
where: {
|
||||
members: {
|
||||
some: {
|
||||
userId,
|
||||
accepted: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
credentials: {
|
||||
select: credentialForCalendarServiceSelect,
|
||||
},
|
||||
name: true,
|
||||
logoUrl: true,
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
parent: {
|
||||
select: {
|
||||
id: true,
|
||||
credentials: {
|
||||
select: credentialForCalendarServiceSelect,
|
||||
},
|
||||
name: true,
|
||||
logoUrl: true,
|
||||
members: {
|
||||
where: {
|
||||
userId,
|
||||
},
|
||||
select: {
|
||||
role: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
return userTeams;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,8 @@ export class CreateManagedUserInput {
|
||||
@ApiProperty({
|
||||
example: "America/New_York",
|
||||
description: `Timezone is used to create user's default schedule from Monday to Friday from 9AM to 5PM. If it is not passed then user does not have
|
||||
a default schedule and it must be created manually via the /schedules endpoint. Until the schedule is created, the user can't access availability atom to set his / her availability nor booked.`,
|
||||
a default schedule and it must be created manually via the /schedules endpoint. Until the schedule is created, the user can't access availability atom to set his / her availability nor booked.
|
||||
It will default to Europe/London if not passed.`,
|
||||
})
|
||||
timeZone?: string;
|
||||
|
||||
|
||||
@@ -2412,6 +2412,38 @@
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/teams/{teamId}/users/{userId}/schedules": {
|
||||
"get": {
|
||||
"operationId": "OrganizationsTeamsSchedulesController_getUserSchedules",
|
||||
"summary": "Get schedules of a team member",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "userId",
|
||||
"required": true,
|
||||
"in": "path",
|
||||
"schema": {
|
||||
"type": "number"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/GetSchedulesOutput_2024_06_11"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"tags": [
|
||||
"Orgs / Teams / Schedules",
|
||||
"Orgs / Teams / Users / Schedules"
|
||||
]
|
||||
}
|
||||
},
|
||||
"/v2/organizations/{orgId}/users": {
|
||||
"get": {
|
||||
"operationId": "OrganizationsUsersController_getOrganizationsUsers",
|
||||
@@ -2988,7 +3020,7 @@
|
||||
"required": false,
|
||||
"in": "query",
|
||||
"description": "Sort results by their creation time (when booking was made) in ascending or descending order.",
|
||||
"example": "?sortEnd=asc OR ?sortEnd=desc",
|
||||
"example": "?sortCreated=asc OR ?sortCreated=desc",
|
||||
"schema": {
|
||||
"enum": [
|
||||
"asc",
|
||||
@@ -3109,16 +3141,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/RescheduleBookingInput_2024_08_13"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"201": {
|
||||
"description": "",
|
||||
@@ -3159,16 +3181,6 @@
|
||||
}
|
||||
}
|
||||
],
|
||||
"requestBody": {
|
||||
"required": true,
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/CancelBookingInput_2024_08_13"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "",
|
||||
@@ -5381,7 +5393,7 @@
|
||||
"timeZone": {
|
||||
"type": "string",
|
||||
"example": "America/New_York",
|
||||
"description": "Timezone is used to create user's default schedule from Monday to Friday from 9AM to 5PM. If it is not passed then user does not have\n a default schedule and it must be created manually via the /schedules endpoint. Until the schedule is created, the user can't access availability atom to set his / her availability nor booked."
|
||||
"description": "Timezone is used to create user's default schedule from Monday to Friday from 9AM to 5PM. If it is not passed then user does not have\n a default schedule and it must be created manually via the /schedules endpoint. Until the schedule is created, the user can't access availability atom to set his / her availability nor booked.\n It will default to Europe/London if not passed."
|
||||
},
|
||||
"locale": {
|
||||
"enum": [
|
||||
@@ -5945,6 +5957,10 @@
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `phone` and the URL contains query parameter `&phone=1234567890`, the phone field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -5978,6 +5994,10 @@
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"example": "e.g., 1234 Main St"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `address` and the URL contains query parameter `&address=1234 Main St, London`, the address field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6011,6 +6031,10 @@
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"example": "e.g., Enter text here"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `who-referred-you` and the URL contains query parameter `&who-referred-you=bob`, the text field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6044,6 +6068,10 @@
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"example": "e.g., 100"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `calories-per-day` and the URL contains query parameter `&calories-per-day=3000`, the number field will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6077,6 +6105,10 @@
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"example": "e.g., Detailed description here..."
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `dear-diary` and the URL contains query parameter `&dear-diary=Today I shipped a feature`, the text area will be prefilled with this value and disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6120,6 +6152,10 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `language` and options of this select field are ['english', 'italian'] and the URL contains query parameter `&language=italian`, the 'italian' will be selected and the select field will be disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6160,6 +6196,10 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `consultants` and the URL contains query parameter `&consultants=en&language=it`, the 'en' and 'it' will be selected and the select field will be disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6193,6 +6233,10 @@
|
||||
"placeholder": {
|
||||
"type": "string",
|
||||
"example": "e.g., example@example.com"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `consultants` and the URL contains query parameter `&consultants=alice@gmail.com&consultants=bob@gmail.com`, the these emails will be added and none more can be added."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6232,6 +6276,10 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `notify-me` and the URL contains query parameter `¬ify-me=true`, the checkbox will be selected and the checkbox field will be disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6271,6 +6319,10 @@
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `language` and options of this select field are ['english', 'italian'] and the URL contains query parameter `&language=italian`, the 'italian' radio buttom will be selected and the select field will be disabled."
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6300,6 +6352,9 @@
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -6551,6 +6606,45 @@
|
||||
"showAvailabilityCount"
|
||||
]
|
||||
},
|
||||
"AttendeeAddressLocation_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "attendeeAddress",
|
||||
"description": "only allowed value for type is `attendeeAddress`"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"AttendeePhoneLocation_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "attendeePhone",
|
||||
"description": "only allowed value for type is `attendeePhone`"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"AttendeeDefinedLocation_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"example": "attendeeDefined",
|
||||
"description": "only allowed value for type is `attendeeDefined`"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type"
|
||||
]
|
||||
},
|
||||
"BookerLayouts_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -6650,6 +6744,15 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeAddressLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeePhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeDefinedLocation_2024_06_14"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -6843,6 +6946,37 @@
|
||||
"EmailDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
"number",
|
||||
"textarea",
|
||||
"select",
|
||||
"multiselect",
|
||||
"multiemail",
|
||||
"checkbox",
|
||||
"radio",
|
||||
"boolean"
|
||||
],
|
||||
"example": "email",
|
||||
"description": "only allowed value for type is `email`",
|
||||
"default": "email"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&email=bob@gmail.com`, the email field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
@@ -6853,24 +6987,51 @@
|
||||
"type": "string",
|
||||
"default": "email"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "email"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"isDefault",
|
||||
"slug",
|
||||
"type",
|
||||
"required"
|
||||
]
|
||||
},
|
||||
"NameDefaultFieldOutput_2024_06_14": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
"number",
|
||||
"textarea",
|
||||
"select",
|
||||
"multiselect",
|
||||
"multiemail",
|
||||
"checkbox",
|
||||
"radio",
|
||||
"boolean"
|
||||
],
|
||||
"example": "name",
|
||||
"description": "only allowed value for type is `name`",
|
||||
"default": "name"
|
||||
},
|
||||
"label": {
|
||||
"type": "string"
|
||||
},
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if URL contains query parameter `&name=bob`, the name field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": true,
|
||||
@@ -6881,18 +7042,14 @@
|
||||
"type": "string",
|
||||
"default": "name"
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
"default": "name"
|
||||
},
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"type",
|
||||
"isDefault",
|
||||
"slug",
|
||||
"type",
|
||||
"required"
|
||||
]
|
||||
},
|
||||
@@ -6907,7 +7064,8 @@
|
||||
},
|
||||
"slug": {
|
||||
"type": "string",
|
||||
"default": "location"
|
||||
"default": "location",
|
||||
"description": "This booking field is returned only if the event type has more than one location. The purpose of this field is to allow the user to select the location where the event will take place."
|
||||
},
|
||||
"type": {
|
||||
"type": "string",
|
||||
@@ -7042,6 +7200,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7073,6 +7233,10 @@
|
||||
"type": "string",
|
||||
"example": "e.g., 1234 Main St"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `address` and the URL contains query parameter `&address=1234 Main St, London`, the address field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7094,6 +7258,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7121,6 +7287,9 @@
|
||||
"required": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7142,6 +7311,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7179,6 +7350,10 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `notify-me` and the URL contains query parameter `¬ify-me=true`, the checkbox will be selected and the checkbox field will be disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7201,6 +7376,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7232,6 +7409,10 @@
|
||||
"type": "string",
|
||||
"example": "e.g., example@example.com"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `consultants` and the URL contains query parameter `&consultants=alice@gmail.com&consultants=bob@gmail.com`, the these emails will be added and none more can be added."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7253,6 +7434,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7290,6 +7473,10 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `consultants` and the URL contains query parameter `&consultants=en&language=it`, the 'en' and 'it' will be selected and the select field will be disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7312,6 +7499,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7343,6 +7532,10 @@
|
||||
"type": "string",
|
||||
"example": "e.g., 100"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `calories-per-day` and the URL contains query parameter `&calories-per-day=3000`, the number field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7364,6 +7557,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7393,6 +7588,10 @@
|
||||
"placeholder": {
|
||||
"type": "string"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `phone` and the URL contains query parameter `&phone=1234567890`, the phone field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7414,6 +7613,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7451,6 +7652,10 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `language` and options of this select field are ['english', 'italian'] and the URL contains query parameter `&language=italian`, the 'italian' radio buttom will be selected and the select field will be disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7473,6 +7678,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7514,6 +7721,10 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `language` and options of this select field are ['english', 'italian'] and the URL contains query parameter `&language=italian`, the 'italian' will be selected and the select field will be disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7536,6 +7747,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7567,6 +7780,10 @@
|
||||
"type": "string",
|
||||
"example": "e.g., Detailed description here..."
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `dear-diary` and the URL contains query parameter `&dear-diary=Today I shipped a feature`, the text area will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -7588,6 +7805,8 @@
|
||||
"type": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"name",
|
||||
"email",
|
||||
"phone",
|
||||
"address",
|
||||
"text",
|
||||
@@ -7619,6 +7838,10 @@
|
||||
"type": "string",
|
||||
"example": "e.g., Enter text here"
|
||||
},
|
||||
"disableOnPrefill": {
|
||||
"type": "boolean",
|
||||
"description": "Disable this booking field if the URL contains query parameter with key equal to the slug and prefill it with the provided value. For example, if the slug is `who-referred-you` and the URL contains query parameter `&who-referred-you=bob`, the text field will be prefilled with this value and disabled."
|
||||
},
|
||||
"isDefault": {
|
||||
"type": "object",
|
||||
"default": false,
|
||||
@@ -8005,6 +8228,15 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeAddressLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeePhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeDefinedLocation_2024_06_14"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -9475,6 +9707,15 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeAddressLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeePhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeDefinedLocation_2024_06_14"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10202,6 +10443,15 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/PhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeAddressLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeePhoneLocation_2024_06_14"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/AttendeeDefinedLocation_2024_06_14"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -10447,6 +10697,26 @@
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"MembershipUserOutputDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"avatarUrl": {
|
||||
"type": "string"
|
||||
},
|
||||
"username": {
|
||||
"type": "string"
|
||||
},
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"email": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"email"
|
||||
]
|
||||
},
|
||||
"OrgTeamMembershipOutputDto": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -10472,6 +10742,9 @@
|
||||
},
|
||||
"disableImpersonation": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"user": {
|
||||
"$ref": "#/components/schemas/MembershipUserOutputDto"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -10479,7 +10752,8 @@
|
||||
"id",
|
||||
"userId",
|
||||
"teamId",
|
||||
"accepted"
|
||||
"accepted",
|
||||
"user"
|
||||
]
|
||||
},
|
||||
"OrgTeamMembershipsOutputResponseDto": {
|
||||
@@ -12301,8 +12575,7 @@
|
||||
"cancelled",
|
||||
"accepted",
|
||||
"rejected",
|
||||
"pending",
|
||||
"rescheduled"
|
||||
"pending"
|
||||
],
|
||||
"example": "accepted"
|
||||
},
|
||||
@@ -12339,6 +12612,20 @@
|
||||
"eventType": {
|
||||
"$ref": "#/components/schemas/EventType"
|
||||
},
|
||||
"meetingUrl": {
|
||||
"type": "string",
|
||||
"description": "Deprecated - rely on 'location' field instead.",
|
||||
"example": "https://example.com/recurring-meeting",
|
||||
"deprecated": true
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/meeting"
|
||||
},
|
||||
"absentHost": {
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"attendees": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
@@ -12355,20 +12642,6 @@
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"meetingUrl": {
|
||||
"type": "string",
|
||||
"description": "Deprecated - rely on 'location' field instead.",
|
||||
"example": "https://example.com/recurring-meeting",
|
||||
"deprecated": true
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/meeting"
|
||||
},
|
||||
"absentHost": {
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"bookingFieldsResponses": {
|
||||
"type": "object",
|
||||
"description": "Booking field responses consisting of an object with booking field slug as keys and user response as values.",
|
||||
@@ -12389,8 +12662,8 @@
|
||||
"duration",
|
||||
"eventTypeId",
|
||||
"eventType",
|
||||
"attendees",
|
||||
"absentHost"
|
||||
"absentHost",
|
||||
"attendees"
|
||||
]
|
||||
},
|
||||
"RecurringBookingOutput_2024_08_13": {
|
||||
@@ -12398,15 +12671,15 @@
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "number",
|
||||
"example": 456
|
||||
"example": 123
|
||||
},
|
||||
"uid": {
|
||||
"type": "string",
|
||||
"example": "recurring_uid_123"
|
||||
"example": "booking_uid_123"
|
||||
},
|
||||
"title": {
|
||||
"type": "string",
|
||||
"example": "Recurring meeting"
|
||||
"example": "Consultation"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
@@ -12426,19 +12699,19 @@
|
||||
"rejected",
|
||||
"pending"
|
||||
],
|
||||
"example": "pending"
|
||||
"example": "accepted"
|
||||
},
|
||||
"cancellationReason": {
|
||||
"type": "string",
|
||||
"example": "Event was cancelled"
|
||||
"example": "User requested cancellation"
|
||||
},
|
||||
"reschedulingReason": {
|
||||
"type": "string",
|
||||
"example": "Event was rescheduled"
|
||||
"example": "User rescheduled the event"
|
||||
},
|
||||
"rescheduledFromUid": {
|
||||
"type": "string",
|
||||
"example": "previous_recurring_uid_123"
|
||||
"example": "previous_uid_123"
|
||||
},
|
||||
"start": {
|
||||
"type": "string",
|
||||
@@ -12450,7 +12723,7 @@
|
||||
},
|
||||
"duration": {
|
||||
"type": "number",
|
||||
"example": 30
|
||||
"example": 60
|
||||
},
|
||||
"eventTypeId": {
|
||||
"type": "number",
|
||||
@@ -12461,9 +12734,19 @@
|
||||
"eventType": {
|
||||
"$ref": "#/components/schemas/EventType"
|
||||
},
|
||||
"recurringBookingUid": {
|
||||
"meetingUrl": {
|
||||
"type": "string",
|
||||
"example": "recurring_uid_987"
|
||||
"description": "Deprecated - rely on 'location' field instead.",
|
||||
"example": "https://example.com/recurring-meeting",
|
||||
"deprecated": true
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/meeting"
|
||||
},
|
||||
"absentHost": {
|
||||
"type": "boolean",
|
||||
"example": true
|
||||
},
|
||||
"attendees": {
|
||||
"type": "array",
|
||||
@@ -12473,34 +12756,24 @@
|
||||
},
|
||||
"guests": {
|
||||
"example": [
|
||||
"guest3@example.com",
|
||||
"guest4@example.com"
|
||||
"guest1@example.com",
|
||||
"guest2@example.com"
|
||||
],
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"meetingUrl": {
|
||||
"type": "string",
|
||||
"description": "Deprecated - rely on 'location' field instead.",
|
||||
"example": "https://example.com/recurring-meeting",
|
||||
"deprecated": true
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"example": "https://example.com/recurring-meeting"
|
||||
},
|
||||
"absentHost": {
|
||||
"type": "boolean",
|
||||
"example": false
|
||||
},
|
||||
"bookingFieldsResponses": {
|
||||
"type": "object",
|
||||
"description": "Booking field responses consisting of an object with booking field slug as keys and user response as values.",
|
||||
"example": {
|
||||
"customField": "customValue"
|
||||
}
|
||||
},
|
||||
"recurringBookingUid": {
|
||||
"type": "string",
|
||||
"example": "recurring_uid_987"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
@@ -12515,9 +12788,9 @@
|
||||
"duration",
|
||||
"eventTypeId",
|
||||
"eventType",
|
||||
"recurringBookingUid",
|
||||
"absentHost",
|
||||
"attendees",
|
||||
"absentHost"
|
||||
"recurringBookingUid"
|
||||
]
|
||||
},
|
||||
"CreateBookingOutput_2024_08_13": {
|
||||
@@ -12575,6 +12848,18 @@
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/RecurringBookingOutput_2024_08_13"
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GetSeatedBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Booking data, which can be either a BookingOutput object, a RecurringBookingOutput object, or an array of RecurringBookingOutput objects"
|
||||
@@ -12608,6 +12893,12 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RecurringBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GetSeatedBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -12622,24 +12913,6 @@
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"RescheduleBookingInput_2024_08_13": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"start": {
|
||||
"type": "string",
|
||||
"description": "Start time in ISO 8601 format for the new booking",
|
||||
"example": "2024-08-13T10:00:00Z"
|
||||
},
|
||||
"reschedulingReason": {
|
||||
"type": "string",
|
||||
"example": "User requested reschedule",
|
||||
"description": "Reason for rescheduling the booking"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"start"
|
||||
]
|
||||
},
|
||||
"RescheduleBookingOutput_2024_08_13": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -12658,6 +12931,12 @@
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/RecurringBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/CreateSeatedBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/CreateRecurringSeatedBookingOutput_2024_08_13"
|
||||
}
|
||||
],
|
||||
"description": "Booking data, which can be either a BookingOutput object or a RecurringBookingOutput object"
|
||||
@@ -12668,18 +12947,6 @@
|
||||
"data"
|
||||
]
|
||||
},
|
||||
"CancelBookingInput_2024_08_13": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"cancellationReason": {
|
||||
"type": "string",
|
||||
"example": "User requested cancellation"
|
||||
}
|
||||
},
|
||||
"required": [
|
||||
"cancellationReason"
|
||||
]
|
||||
},
|
||||
"CancelBookingOutput_2024_08_13": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@@ -12704,6 +12971,18 @@
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/RecurringBookingOutput_2024_08_13"
|
||||
}
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GetSeatedBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
|
||||
},
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"$ref": "#/components/schemas/GetRecurringSeatedBookingOutput_2024_08_13"
|
||||
}
|
||||
}
|
||||
],
|
||||
"description": "Booking data, which can be either a BookingOutput object, a RecurringBookingOutput object, or an array of RecurringBookingOutput objects"
|
||||
|
||||
@@ -126,7 +126,7 @@ const EventTypeGroup = ({
|
||||
}: ConfigureStepCardProps & {
|
||||
groupIndex: number;
|
||||
setUpdatedEventTypesStatus: Dispatch<SetStateAction<TUpdatedEventTypesStatus>>;
|
||||
submitRefs: Array<React.RefObject<HTMLButtonElement>>;
|
||||
submitRefs: React.MutableRefObject<(HTMLButtonElement | null)[]>;
|
||||
}) => {
|
||||
const { control } = useFormContext<TEventTypesForm>();
|
||||
const { fields, update } = useFieldArray({
|
||||
@@ -174,7 +174,9 @@ const EventTypeGroup = ({
|
||||
return res;
|
||||
});
|
||||
}}
|
||||
ref={submitRefs[index]}
|
||||
ref={(el) => {
|
||||
submitRefs.current[index] = el;
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
@@ -193,17 +195,8 @@ export const ConfigureStepCard: FC<ConfigureStepCardProps> = (props) => {
|
||||
keyName: "fieldId",
|
||||
});
|
||||
const eventTypeGroups = watch("eventTypeGroups");
|
||||
const submitRefs = useRef<Array<Array<React.RefObject<HTMLButtonElement>>>>([]);
|
||||
const submitRefs = useRef<(HTMLButtonElement | null)[]>([]);
|
||||
|
||||
submitRefs.current = eventTypeGroups.reduce(
|
||||
(arr: Array<Array<React.RefObject<HTMLButtonElement>>>, field) => {
|
||||
const res = field.eventTypes
|
||||
.filter((eventType) => eventType.selected)
|
||||
.map((_ref) => React.createRef<HTMLButtonElement>());
|
||||
return [...arr, res];
|
||||
},
|
||||
[]
|
||||
);
|
||||
const mainForSubmitRef = useRef<HTMLButtonElement>(null);
|
||||
const [updatedEventTypesStatus, setUpdatedEventTypesStatus] = useState<TUpdatedEventTypesStatus>(
|
||||
eventTypeGroups.reduce((arr: Array<{ id: number; updated: boolean }[]>, field) => {
|
||||
@@ -245,7 +238,7 @@ export const ConfigureStepCard: FC<ConfigureStepCardProps> = (props) => {
|
||||
<EventTypeGroup
|
||||
groupIndex={groupIndex}
|
||||
setUpdatedEventTypesStatus={setUpdatedEventTypesStatus}
|
||||
submitRefs={submitRefs.current[groupIndex]}
|
||||
submitRefs={submitRefs}
|
||||
{...props}
|
||||
/>
|
||||
</div>
|
||||
@@ -258,7 +251,7 @@ export const ConfigureStepCard: FC<ConfigureStepCardProps> = (props) => {
|
||||
type="button"
|
||||
data-testid="configure-step-save"
|
||||
onClick={() => {
|
||||
submitRefs.current.map((group) => group?.map((ref) => ref.current?.click()));
|
||||
submitRefs.current.forEach((ref) => ref?.click());
|
||||
setSubmit(true);
|
||||
}}
|
||||
loading={loading}>
|
||||
|
||||
@@ -1,9 +1,24 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useMemo } from "react";
|
||||
import { useForm } from "react-hook-form";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { ErrorCode } from "@calcom/lib/errorCodes";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, Dialog, DialogClose, DialogContent, DialogFooter, showToast } from "@calcom/ui";
|
||||
import {
|
||||
Button,
|
||||
Form,
|
||||
Label,
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
showToast,
|
||||
Select,
|
||||
RadioGroup as RadioArea,
|
||||
} from "@calcom/ui";
|
||||
|
||||
type ReassignDialog = {
|
||||
isOpenDialog: boolean;
|
||||
@@ -12,9 +27,44 @@ type ReassignDialog = {
|
||||
bookingId: number;
|
||||
};
|
||||
|
||||
type FormValues = {
|
||||
reassignType: "round_robin" | "team_member";
|
||||
teamMemberId?: number;
|
||||
};
|
||||
|
||||
export const ReassignDialog = ({ isOpenDialog, setIsOpenDialog, teamId, bookingId }: ReassignDialog) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useUtils();
|
||||
const [animationParentRef] = useAutoAnimate<HTMLFormElement>({
|
||||
duration: 150,
|
||||
easing: "ease-in-out",
|
||||
});
|
||||
// Were using legacy list members here because we don't currently have an easy way to paginate a select via infinite scroll
|
||||
const teamMembers = trpc.viewer.teams.getRoundRobinHostsToReassign.useQuery({
|
||||
bookingId,
|
||||
exclude: "fixedHosts",
|
||||
});
|
||||
|
||||
const teamMemberOptions = useMemo(() => {
|
||||
if (teamMembers.isLoading)
|
||||
return [
|
||||
{
|
||||
label: "Loading...",
|
||||
value: 0,
|
||||
},
|
||||
];
|
||||
|
||||
return teamMembers.data?.map((member) => ({
|
||||
label: member.name,
|
||||
value: member.id,
|
||||
}));
|
||||
}, [teamMembers]);
|
||||
|
||||
const form = useForm<FormValues>({
|
||||
defaultValues: {
|
||||
reassignType: "round_robin",
|
||||
},
|
||||
});
|
||||
|
||||
const roundRobinReassignMutation = trpc.viewer.teams.roundRobinReassign.useMutation({
|
||||
onSuccess: async () => {
|
||||
@@ -31,6 +81,33 @@ export const ReassignDialog = ({ isOpenDialog, setIsOpenDialog, teamId, bookingI
|
||||
},
|
||||
});
|
||||
|
||||
const roundRobinManualReassignMutation = trpc.viewer.teams.roundRobinManualReassign.useMutation({
|
||||
onSuccess: async () => {
|
||||
await utils.viewer.bookings.get.invalidate();
|
||||
setIsOpenDialog(false);
|
||||
showToast(t("booking_reassigned"), "success");
|
||||
},
|
||||
onError: async (error) => {
|
||||
if (error.message.includes(ErrorCode.NoAvailableUsersFound)) {
|
||||
showToast(t("no_available_hosts"), "error");
|
||||
} else {
|
||||
showToast(t(error.message), "error");
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
const handleSubmit = (values: FormValues) => {
|
||||
if (values.reassignType === "round_robin") {
|
||||
roundRobinReassignMutation.mutate({ teamId, bookingId });
|
||||
} else {
|
||||
if (values.teamMemberId) {
|
||||
roundRobinManualReassignMutation.mutate({ bookingId, teamMemberId: values.teamMemberId });
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const watchedReassignType = form.watch("reassignType");
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
open={isOpenDialog}
|
||||
@@ -39,17 +116,50 @@ export const ReassignDialog = ({ isOpenDialog, setIsOpenDialog, teamId, bookingI
|
||||
}}>
|
||||
<DialogContent title={t("reassign_round_robin_host")} description={t("reassign_to_another_rr_host")}>
|
||||
{/* TODO add team member reassignment*/}
|
||||
<DialogFooter>
|
||||
<DialogClose />
|
||||
<Button
|
||||
data-testid="rejection-confirm"
|
||||
loading={roundRobinReassignMutation.isPending}
|
||||
onClick={() => {
|
||||
roundRobinReassignMutation.mutate({ teamId, bookingId });
|
||||
}}>
|
||||
{t("reassign")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
<Form form={form} handleSubmit={handleSubmit} ref={animationParentRef}>
|
||||
<RadioArea.Group
|
||||
onValueChange={(val) => {
|
||||
form.setValue("reassignType", val as "team_member" | "round_robin");
|
||||
}}
|
||||
className={classNames("mt-1 flex flex-col gap-4")}>
|
||||
<RadioArea.Item
|
||||
value="round_robin"
|
||||
className={classNames("w-full text-sm")}
|
||||
classNames={{ container: classNames("w-full") }}>
|
||||
<strong className="mb-1 block">{t("round_robin")}</strong>
|
||||
<p>{t("round_robin_reassign_description")}</p>
|
||||
</RadioArea.Item>
|
||||
<RadioArea.Item
|
||||
value="team_member"
|
||||
className={classNames("text-sm")}
|
||||
classNames={{ container: classNames("w-full") }}>
|
||||
<strong className="mb-1 block">{t("team_member_round_robin_reassign")}</strong>
|
||||
<p>{t("team_member_round_robin_reassign_description")}</p>
|
||||
</RadioArea.Item>
|
||||
</RadioArea.Group>
|
||||
|
||||
{watchedReassignType === "team_member" && (
|
||||
<div className="mb-2">
|
||||
<Label className="text-emphasis mt-6">{t("select_team_member")}</Label>
|
||||
<Select
|
||||
value={teamMemberOptions?.find((option) => option.value === form.getValues("teamMemberId"))}
|
||||
options={teamMemberOptions}
|
||||
onChange={(event) => {
|
||||
form.setValue("teamMemberId", event?.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<DialogFooter>
|
||||
<DialogClose />
|
||||
<Button
|
||||
type="submit"
|
||||
data-testid="rejection-confirm"
|
||||
loading={roundRobinReassignMutation.isPending || roundRobinManualReassignMutation.isPending}>
|
||||
{t("reassign")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
|
||||
@@ -696,13 +696,13 @@ const RerouteDialogContentAndFooter = ({
|
||||
}: Pick<RerouteDialogProps, "isOpenDialog" | "setIsOpenDialog"> & {
|
||||
booking: TeamEventTypeBookingToReroute;
|
||||
}) => {
|
||||
const { data: responseWithForm, isPending: isRoutingFormLoading } =
|
||||
const { data: responseWithForm, isPending: isRoutingFormLoading, error: formResponseFetchError } =
|
||||
trpc.viewer.appRoutingForms.getResponseWithFormFields.useQuery({
|
||||
formResponseId: booking.routedFromRoutingFormReponse.id,
|
||||
});
|
||||
|
||||
const { t } = useLocale();
|
||||
// useUpdateIsReroutingQueryParam({ isOpenDialog });
|
||||
|
||||
if (isRoutingFormLoading)
|
||||
return (
|
||||
<>
|
||||
@@ -718,7 +718,11 @@ const RerouteDialogContentAndFooter = ({
|
||||
</>
|
||||
);
|
||||
|
||||
if (!responseWithForm) return <div>{t("something_went_wrong")}</div>;
|
||||
if (formResponseFetchError) {
|
||||
return <div className="mb-8">{formResponseFetchError.message}</div>;
|
||||
}
|
||||
|
||||
if (!responseWithForm) return <div className="mb-8">{t("something_went_wrong")}</div>;
|
||||
|
||||
return (
|
||||
<RerouteDialogContentAndFooterWithFormResponse
|
||||
|
||||
@@ -83,6 +83,7 @@ vi.mock("@calcom/web/lib/hooks/useRouterQuery", () => ({
|
||||
}));
|
||||
|
||||
vi.mock("@calcom/ui", async (importOriginal) => {
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
const actual = await importOriginal<any>("@calcom/ui");
|
||||
return {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import type { GetServerSidePropsContext } from "next";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -10,7 +11,7 @@ import slugify from "@calcom/lib/slugify";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { RedirectType } from "@calcom/prisma/client";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import type { RouterOutputs } from "@calcom/trpc";
|
||||
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
import { getTemporaryOrgRedirect } from "@lib/getTemporaryOrgRedirect";
|
||||
|
||||
@@ -29,7 +30,6 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
const session = await getServerSession({ req });
|
||||
const { slug: teamSlug, type: meetingSlug } = paramsSchema.parse(params);
|
||||
const { rescheduleUid, isInstantMeeting: queryIsInstantMeeting, email } = query;
|
||||
const ssr = await ssrInit(context);
|
||||
const { currentOrgDomain, isValidOrgDomain } = orgDomainConfig(req, params?.orgSlug);
|
||||
const isOrgContext = currentOrgDomain && isValidOrgDomain;
|
||||
|
||||
@@ -54,44 +54,54 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
select: {
|
||||
id: true,
|
||||
hideBranding: true,
|
||||
parent: true,
|
||||
name: true,
|
||||
slug: true,
|
||||
eventTypes: {
|
||||
where: {
|
||||
slug: meetingSlug,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
isInstantEvent: true,
|
||||
schedulingType: true,
|
||||
metadata: true,
|
||||
length: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!team) {
|
||||
if (!team || !team.eventTypes?.[0]) {
|
||||
return {
|
||||
notFound: true,
|
||||
} as const;
|
||||
}
|
||||
|
||||
const eventData = team.eventTypes[0];
|
||||
|
||||
let booking: GetBookingType | null = null;
|
||||
if (rescheduleUid) {
|
||||
booking = await getBookingForReschedule(`${rescheduleUid}`, session?.user?.id);
|
||||
}
|
||||
|
||||
const org = isValidOrgDomain ? currentOrgDomain : null;
|
||||
// We use this to both prefetch the query on the server,
|
||||
// as well as to check if the event exist, so we c an show a 404 otherwise.
|
||||
const eventData = await ssr.viewer.public.event.fetch({
|
||||
username: teamSlug,
|
||||
eventSlug: meetingSlug,
|
||||
isTeamEvent: true,
|
||||
org,
|
||||
fromRedirectOfNonOrgLink: context.query.orgRedirection === "true",
|
||||
});
|
||||
|
||||
if (!eventData) {
|
||||
return {
|
||||
notFound: true,
|
||||
} as const;
|
||||
}
|
||||
const ssr = await ssrInit(context);
|
||||
const fromRedirectOfNonOrgLink = context.query.orgRedirection === "true";
|
||||
const isUnpublished = team.parent ? !team.parent.slug : !team.slug;
|
||||
|
||||
return {
|
||||
props: {
|
||||
eventData: {
|
||||
eventTypeId: eventData.id,
|
||||
entity: eventData.entity,
|
||||
entity: {
|
||||
fromRedirectOfNonOrgLink,
|
||||
considerUnpublished: isUnpublished && !fromRedirectOfNonOrgLink,
|
||||
orgSlug: isValidOrgDomain ? currentOrgDomain : null,
|
||||
teamSlug: team.slug ?? null,
|
||||
name: team.parent?.name ?? team.name ?? null,
|
||||
},
|
||||
length: eventData.length,
|
||||
metadata: eventData.metadata,
|
||||
metadata: EventTypeMetaDataSchema.parse(eventData.metadata),
|
||||
},
|
||||
booking,
|
||||
user: teamSlug,
|
||||
@@ -99,20 +109,27 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
slug: meetingSlug,
|
||||
trpcState: ssr.dehydrate(),
|
||||
isBrandingHidden: team?.hideBranding,
|
||||
isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false,
|
||||
isInstantMeeting: eventData && queryIsInstantMeeting ? true : false,
|
||||
themeBasis: null,
|
||||
orgBannerUrl: eventData?.team?.parent?.bannerUrl ?? "",
|
||||
orgBannerUrl: team.parent?.bannerUrl ?? "",
|
||||
teamMemberEmail: await getTeamMemberEmail(eventData, email as string),
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
type EventData = RouterOutputs["viewer"]["public"]["event"];
|
||||
|
||||
async function getTeamMemberEmail(eventData: EventData, email?: string): Promise<string | null> {
|
||||
async function getTeamMemberEmail(
|
||||
eventData: {
|
||||
id: number;
|
||||
isInstantEvent: boolean;
|
||||
schedulingType: SchedulingType | null;
|
||||
metadata: Prisma.JsonValue | null;
|
||||
length: number;
|
||||
},
|
||||
email?: string
|
||||
): Promise<string | null> {
|
||||
// Pre-requisites
|
||||
if (!eventData || !email || eventData.schedulingType !== SchedulingType.ROUND_ROBIN) return null;
|
||||
const crmContactOwnerEmail = await getCRMContactOwnerForRRLeadSkip(email, eventData.id);
|
||||
const crmContactOwnerEmail = await getCRMContactOwnerForRRLeadSkip(email, eventData.metadata);
|
||||
if (!crmContactOwnerEmail) return null;
|
||||
// Determine if the contactOwner is a part of the event type
|
||||
const contactOwnerQuery = await prisma.user.findFirst({
|
||||
|
||||
@@ -201,7 +201,7 @@ export default function AvailabilityPage({ currentOrg }: PageProps) {
|
||||
(data && (data.user.role === MembershipRole.OWNER || data.user.role === MembershipRole.ADMIN)) ?? false;
|
||||
const isOrgAndPrivate = data?.isOrganization && data.isPrivate;
|
||||
|
||||
const canViewTeamAvailability = isOrg && (isOrgAdminOrOwner || !isOrgAndPrivate);
|
||||
const canViewTeamAvailability = isOrgAdminOrOwner || !isOrgAndPrivate;
|
||||
|
||||
const toggleGroupOptions = [{ value: "mine", label: t("my_availability") }];
|
||||
|
||||
@@ -233,7 +233,7 @@ export default function AvailabilityPage({ currentOrg }: PageProps) {
|
||||
</div>
|
||||
}>
|
||||
{searchParams?.get("type") === "team" && canViewTeamAvailability ? (
|
||||
<AvailabilitySliderTable userTimeFormat={me?.data?.timeFormat ?? null} />
|
||||
<AvailabilitySliderTable userTimeFormat={me?.data?.timeFormat ?? null} isOrg={isOrg} />
|
||||
) : (
|
||||
<AvailabilityListWithQuery />
|
||||
)}
|
||||
|
||||
@@ -116,6 +116,7 @@ const GeneralView = ({ localeProp, user, travelSchedules, revalidatePage }: Gene
|
||||
|
||||
if (res.locale) {
|
||||
window.calNewLocale = res.locale;
|
||||
document.cookie = `calNewLocale=${res.locale}; path=/`;
|
||||
}
|
||||
await revalidatePage();
|
||||
},
|
||||
|
||||
@@ -182,6 +182,8 @@ const nextConfig = {
|
||||
},
|
||||
i18n: {
|
||||
...i18n,
|
||||
defaultLocale: "en",
|
||||
locales: ["en"],
|
||||
localeDetection: false,
|
||||
},
|
||||
productionBrowserSourceMaps: false,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "4.6.6",
|
||||
"version": "4.6.8",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "السماح للحضور بالحجز لك عن طريق حجز المجموعات الديناميكي",
|
||||
"dynamic_booking": "روابط المجموعة الديناميكية",
|
||||
"allow_seo_indexing": "السماح لمحركات البحث بالوصول إلى المحتوى العام لديك",
|
||||
"seo_indexing": "السماح بالفهرسة وفق SEO",
|
||||
"seo_indexing": "السماح بفهرسة محركات البحث",
|
||||
"allow_browser_notifications": "السماح بإشعارات المتصفح",
|
||||
"you_have_denied_notifications": "لقد رفضت الإشعارات. أعد ضبط الإذن في إعدادات المتصفح لتمكينها.",
|
||||
"disable_browser_notifications": "تعطيل إشعارات المتصفح",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "إخفاء الزر \"حجز عضو فريق\"",
|
||||
"hide_book_a_team_member_description": "إخفاء الزر \"حجز عضو فريق\" من صفحاتك العامة.",
|
||||
"danger_zone": "منطقة خطر",
|
||||
"account_deletion_cannot_be_undone": "احترس. لا يمكن التراجع عن حذف الحساب.",
|
||||
"team_deletion_cannot_be_undone": "كن حذرا. لا يمكن التراجع عن حذف الفريق.",
|
||||
"account_deletion_cannot_be_undone": "كن حذرًا. لا يمكن التراجع عن حذف الحساب.",
|
||||
"team_deletion_cannot_be_undone": "كن حذرًا. لا يمكن التراجع عن حذف الفريق.",
|
||||
"back": "عودة",
|
||||
"cancel": "إلغاء",
|
||||
"cancel_all_remaining": "إلغاء كل ما تبقى",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "ما تواتر مرات الحجز",
|
||||
"event_advanced_tab_description": "إعدادات الرزنامة والمزيد...",
|
||||
"event_advanced_tab_title": "متقدم",
|
||||
"event_payments_tab_title": "المدفوعات",
|
||||
"event_payments_tab_description": "إعداد المدفوعات للفعالية",
|
||||
"event_setup_multiple_duration_error": "إعداد الحدث: تستلزم فترات متعددة خيارًا واحدًا على الأقل.",
|
||||
"event_setup_multiple_duration_default_error": "إعداد الحدث: الرجاء تحديد مدة افتراضية صالحة.",
|
||||
"event_setup_booking_limits_error": "يجب أن تكون حدود الحجز بترتيب تصاعدي. [يوم، أسبوع، شهر، سنة]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "التوجيه إلى الشخص المناسب بناءً على إجابات النموذج الخاص بك",
|
||||
"form_description": "قم بإنشاء النموذج الخاص بك لتوجيه صاحب حجز",
|
||||
"copy_link_to_form": "نسخ الرابط إلى النموذج",
|
||||
"theme": "السمة",
|
||||
"theme": "سمة صفحة الحجز",
|
||||
"theme_applies_note": "ينطبق هذا فقط على صفحات الحجز العامة",
|
||||
"app_theme": "مظهر لوحة التحكم",
|
||||
"app_theme": "سمة لوحة التحكم",
|
||||
"app_theme_applies_note": "هذا ينطبق فقط على لوحة التحكم المسجلة دخولك",
|
||||
"theme_system": "الافتراضي للنظام",
|
||||
"add_a_team": "إضافة فريق",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "لا يوجد تقويم مثبت",
|
||||
"no_calendar_installed_description": "لم تقم بربط أي من التقويمات الخاصة بك",
|
||||
"add_a_calendar": "إضافة تقويم",
|
||||
"change_email_hint": "قد تحتاج إلى تسجيل الخروج والعودة لرؤية التغييرات التي تم تنفيذها",
|
||||
"change_email_hint": "قد تحتاج إلى تسجيل الخروج وإعادة تسجيل الدخول لرؤية أي تغيير يتم تطبيقه.",
|
||||
"confirm_password_change_email": "يرجى تأكيد كلمة المرور قبل تغيير عنوان بريدك الإلكتروني",
|
||||
"seats": "مقاعد",
|
||||
"every_app_published": "كل تطبيق منشور على App Store {{appName}} مفتوح المصدر ويتم اختباره بدقة من خلال مراجعات الأقران. ومع ذلك، فإن شركة {{companyName}} لا تدعم هذه التطبيقات أو تصرح بها ما لم يتم نشرها بواسطة {{appName}}. إذا واجهت محتوى أو سلوكًا غير لائق، فيرجى الإبلاغ عنه.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "رمز التحقق غير صحيح",
|
||||
"not_verified": "لم يتم التحقق منه بعد",
|
||||
"no_availability_in_month": "غير متاح في شهر {{month}}",
|
||||
"view_previous_month": "عرض الشهر السابق",
|
||||
"view_next_month": "عرض الشهر التالي",
|
||||
"send_code": "إرسال الرمز",
|
||||
"number_verified": "تم التحقق من الرقم",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "الإعداد",
|
||||
"setup_description": "إعداد مثال Cal.com",
|
||||
"configure": "تهيئة",
|
||||
"sso_configuration": "تسجيل الدخول لمرة واحدة",
|
||||
"sso_configuration": "تسجيل الدخول الموحد",
|
||||
"sso_configuration_description": "قم بتهيئة SAML/OIDC SSO والسماح لأعضاء الفريق بتسجيل الدخول باستخدام موفر الهوية",
|
||||
"sso_configuration_description_orgs": "قم بتكوين SAML/OIDC SSO واسمح لأعضاء المؤسسة بتسجيل الدخول باستخدام موفر الهوية",
|
||||
"sso_oidc_heading": "SSO مع OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "الأحداث الأكثر شعبية",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "إليك ملخصك للأحداث الشعبية لفريقك {{teamName}} خلال آخر 30 يومًا",
|
||||
"me": "أنا",
|
||||
"monthly_digest_email": "الرسالة الإلكترونية للموجز الشهري",
|
||||
"monthly_digest_email": "البريد الإلكتروني الشهري الموجز",
|
||||
"monthly_digest_email_for_teams": "رسالة إلكترونية بالموجز الشهري للفرق",
|
||||
"verify_team_tooltip": "تأكيد فريقك لتمكين إرسال الرسائل إلى الحاضرين",
|
||||
"member_removed": "تمت إزالة العضو",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "إذا لم تكن جهة الاتصال موجودة تحت حساب، قم بإنشاء عميل محتمل جديد من المشارك",
|
||||
"salesforce_create_new_contact_under_account": "إنشاء جهة اتصال جديدة تحت حساب بناءً على نطاق البريد الإلكتروني للمشارك وجهات الاتصال الموجودة",
|
||||
"mass_assign_attributes": "تعيين السمات بشكل جماعي",
|
||||
"reroute_preview_custom_message": "ينتج عنه عرض رسالة مخصصة. حاول تغيير الاستجابة لتوجيه إلى حدث",
|
||||
"reroute_preview_external_redirect": "ينتج عنه إعادة التوجيه إلى {{externalUrl}}. حاول تغيير الاستجابة لتوجيه إلى حدث",
|
||||
"reroute_preview_possible_host": "المضيف المحتمل",
|
||||
"current_routing_status": "حالة التوجيه الحالية",
|
||||
"reroute_booking": "إعادة توجيه الحجز",
|
||||
"reroute_booking_description": "إعادة توجيه الحجز إلى أعضاء فريق مختلفين",
|
||||
"verify_new_route": "التحقق من المسار الجديد",
|
||||
"reroute": "إعادة التوجيه",
|
||||
"new_routing_status": "حالة التوجيه الجديدة",
|
||||
"salesforce_book_directly_with_attendee_owner": "إذا كان المشارك موجودًا في Salesforce، احجز مباشرة مع المالك",
|
||||
"salesforce_check_owner_of": "الكيان الذي يجب التحقق من مالكه للحجز مباشرة",
|
||||
"account": "حساب",
|
||||
"choose_hosts_schedule": "اختر جداول المضيفين",
|
||||
"hosts_schedule_description": "يمكنك اختيار جدول مختلف لكل مضيف. يتم اختيار جدول المضيف الافتراضي لكل مضيف بشكل افتراضي.",
|
||||
"no_hosts_description": "لا يوجد مضيفون معينون، أضف مضيفين في علامة التبويب التعيين.",
|
||||
"reschedule_with_same_timeslot": "إعادة الجدولة بنفس الفترة الزمنية",
|
||||
"reschedule_with_same_timeslot_of_new_event": "إعادة الجدولة بنفس الفترة الزمنية للحدث الجديد",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "إعادة الجدولة للحدث الجديد بفترة زمنية مختلفة",
|
||||
"reschedule_with_different_timeslot": "إعادة الجدولة بفترة زمنية مختلفة",
|
||||
"we_dont_have_id_for_the_new_event": "ليس لدينا معرف للحدث الجديد. يرجى الانتقال إلى نموذج التوجيه وحفظه مرة أخرى.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "تمت إعادة توجيه الحجز بنجاح. جاري إعادة التوجيه إلى صفحة الحجز",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "İştirakçılara dinamik qrup rezervasiyaları vasitəsilə sizi rezervasiya etməyə icazə verin",
|
||||
"dynamic_booking": "Dinamik qrup bağlantıları",
|
||||
"allow_seo_indexing": "Axtarış motorlarının ictimai məzmununuza daxil olmasına icazə verin",
|
||||
"seo_indexing": "SEO İndeksləməsinə İcazə Verin",
|
||||
"seo_indexing": "Axtarış sisteminin indeksləməsinə icazə verin",
|
||||
"allow_browser_notifications": "Brauzer bildirişlərinə icazə ver",
|
||||
"you_have_denied_notifications": "Bildirişlərə icazə verməmisiniz. Aktivləşdirmək üçün brauzer parametrlərində icazəni yenidən qurun.",
|
||||
"disable_browser_notifications": "Brauzer bildirişlərini söndür",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Nə qədər tez-tez rezervasiya edilə bilərsiniz",
|
||||
"event_advanced_tab_description": "Təqvim parametrləri və daha çox...",
|
||||
"event_advanced_tab_title": "İrəli",
|
||||
"event_payments_tab_title": "Ödənişlər",
|
||||
"event_payments_tab_description": "Tədbir üçün ödənişləri təyin edin",
|
||||
"event_setup_multiple_duration_error": "Tədbir Quraşdırması: Bir neçə müddət üçün ən azı 1 seçim tələb olunur.",
|
||||
"event_setup_multiple_duration_default_error": "Tədbir Quraşdırması: Zəhmət olmasa, etibarlı bir standart müddət seçin.",
|
||||
"event_setup_booking_limits_error": "Rezervasiya limitləri artan qaydada olmalıdır. [gün, həftə, ay, il]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Formunuza verdiyiniz cavablara əsasən düzgün şəxsə yönləndirin",
|
||||
"form_description": "Rezervasiya edən şəxsi yönləndirmək üçün formanızı yaradın",
|
||||
"copy_link_to_form": "Formanın linkini kopyalayın",
|
||||
"theme": "Rezervasiya Səhifəsinin Mövzusu",
|
||||
"theme": "Rezervasiya səhifəsinin teması",
|
||||
"theme_applies_note": "Bu yalnız sizin ictimai rezervasiya səhifələrinizə aiddir",
|
||||
"app_theme": "İdarə Panelinin Mövzusu",
|
||||
"app_theme": "İdarəetmə panelinin teması",
|
||||
"app_theme_applies_note": "Bu yalnız sizin daxil olduğunuz idarə panelinə aiddir",
|
||||
"theme_system": "Sistem defoltu",
|
||||
"add_a_team": "Komanda əlavə edin",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Quraşdırılmış təqvim yoxdur",
|
||||
"no_calendar_installed_description": "Hələ heç bir təqviminizi qoşmamısınız",
|
||||
"add_a_calendar": "Təqvim əlavə edin",
|
||||
"change_email_hint": "Dəyişikliklərin qüvvəyə minməsi üçün çıxış edib yenidən daxil olmanız lazım ola bilər",
|
||||
"change_email_hint": "Dəyişikliyin qüvvəyə minməsi üçün sistemdən çıxıb yenidən daxil olmağınız lazım ola bilər.",
|
||||
"confirm_password_change_email": "E-poçt ünvanınızı dəyişdirmədən əvvəl parolunuzu təsdiqləyin",
|
||||
"seats": "oturacaqlar",
|
||||
"every_app_published": "{{appName}} App Store-da yayımlanan hər bir tətbiq açıq mənbəlidir və həmkarların rəyləri vasitəsilə hərtərəfli sınaqdan keçirilir. Buna baxmayaraq, {{companyName}} bu tətbiqləri təsdiqləmir və ya sertifikatlaşdırmır, əgər onlar {{appName}} tərəfindən yayımlanmayıbsa. Uyğunsuz məzmun və ya davranışla qarşılaşsanız, xahiş edirik bildirin.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Yanlış təsdiq kodu",
|
||||
"not_verified": "Hələ təsdiqlənməyib",
|
||||
"no_availability_in_month": "{{month}} ayında əlçatanlıq yoxdur",
|
||||
"view_previous_month": "Əvvəlki aya baxın",
|
||||
"view_next_month": "Növbəti aya bax",
|
||||
"send_code": "Kodu göndər",
|
||||
"number_verified": "Nömrə təsdiqləndi",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Quraşdırma",
|
||||
"setup_description": "Cal.com instansiyasını quraşdırın",
|
||||
"configure": "Konfiqurasiya",
|
||||
"sso_configuration": "Tək Giriş (SSO) Konfiqurasiyası",
|
||||
"sso_configuration": "Vahid giriş",
|
||||
"sso_configuration_description": "SAML/OIDC SSO-nu konfiqurasiya edin və komanda üzvlərinin Kimlik Provayderi istifadə edərək daxil olmasına icazə verin",
|
||||
"sso_configuration_description_orgs": "SAML/OIDC SSO-nu konfiqurasiya edin və təşkilat üzvlərinin Kimlik Provayderi istifadə edərək daxil olmasına icazə verin",
|
||||
"sso_oidc_heading": "OIDC ilə SSO",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Ən Populyar Tədbirlər",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Son 30 gün ərzində komandanız üçün populyar tədbirlərin xülasəsi {{teamName}}",
|
||||
"me": "Mən",
|
||||
"monthly_digest_email": "Aylıq Xülasə E-poçtu",
|
||||
"monthly_digest_email": "Aylıq icmal e-poçtu",
|
||||
"monthly_digest_email_for_teams": "Komandalar üçün aylıq xülasə e-poçtu",
|
||||
"verify_team_tooltip": "İştirakçılara mesaj göndərməyi aktivləşdirmək üçün komandanızı təsdiqləyin",
|
||||
"member_removed": "Üzv silindi",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Əgər əlaqə hesab altında mövcud deyilsə, iştirakçıdan yeni potensial müştəri yaradın",
|
||||
"salesforce_create_new_contact_under_account": "İştirakçının email domeni və mövcud əlaqələr əsasında hesab altında yeni əlaqə yaradın",
|
||||
"mass_assign_attributes": "Atributları kütləvi təyin et",
|
||||
"reroute_preview_custom_message": "Bu, xüsusi mesajın göstərilməsi ilə nəticələnir. Cavabı bir tədbir üçün marşruta dəyişdirməyə çalışın",
|
||||
"reroute_preview_external_redirect": "Bu, {{externalUrl}} ünvanına yönləndirmə ilə nəticələnir. Cavabı bir tədbir üçün marşruta dəyişdirməyə çalışın",
|
||||
"reroute_preview_possible_host": "Mümkün ev sahibi",
|
||||
"current_routing_status": "Cari marşrutlaşdırma statusu",
|
||||
"reroute_booking": "Rezervasiyanı yenidən yönləndirin",
|
||||
"reroute_booking_description": "Rezervasiyanı fərqli komanda üzvlərinə yönləndirin",
|
||||
"verify_new_route": "Yeni marşrutu təsdiqləyin",
|
||||
"reroute": "Yenidən yönləndirmə",
|
||||
"new_routing_status": "Yeni marşrutlaşdırma statusu",
|
||||
"salesforce_book_directly_with_attendee_owner": "Əgər iştirakçı Salesforce-da mövcuddursa, birbaşa sahib ilə rezervasiya edin",
|
||||
"salesforce_check_owner_of": "Birbaşa rezervasiya etmək üçün sahibini yoxlayacaq obyekt",
|
||||
"account": "Hesab",
|
||||
"choose_hosts_schedule": "Hostların cədvəlini seçin",
|
||||
"hosts_schedule_description": "Hər bir host üçün fərqli cədvəl seçə bilərsiniz. Hər hostun standart cədvəli avtomatik olaraq seçilir.",
|
||||
"no_hosts_description": "Host təyin edilməyib, təyinat sekmesinde host əlavə edin.",
|
||||
"reschedule_with_same_timeslot": "Eyni vaxt aralığı ilə yenidən planlaşdırın",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Yeni tədbirin eyni vaxt aralığı ilə yenidən planlaşdırın",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Yeni tədbiri fərqli vaxt aralığı ilə yenidən planlaşdırın",
|
||||
"reschedule_with_different_timeslot": "Fərqli vaxt aralığı ilə yenidən planlaşdırın",
|
||||
"we_dont_have_id_for_the_new_event": "Yeni tədbir üçün ID-miz yoxdur. Zəhmət olmasa, Marşrutlaşdırma Formasına keçin və yenidən saxlayın.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Rezervasiya uğurla yenidən yönləndirildi. Rezervasiya səhifəsinə yönləndirilir",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Povolit účastníkům rezervovat vás prostřednictvím dynamických skupinových rezervací",
|
||||
"dynamic_booking": "Dynamické odkazy na skupiny",
|
||||
"allow_seo_indexing": "Povolte vyhledávačům přístup k vašemu veřejnému obsahu",
|
||||
"seo_indexing": "Zapnout indexování SEO",
|
||||
"seo_indexing": "Povolit indexování vyhledávači",
|
||||
"allow_browser_notifications": "Povolit oznámení v prohlížeči",
|
||||
"you_have_denied_notifications": "Odmítli jste oznámení. Obnovte oprávnění v nastavení prohlížeče, abyste je mohli povolit.",
|
||||
"disable_browser_notifications": "Zakázat oznámení v prohlížeči",
|
||||
@@ -673,7 +673,7 @@
|
||||
"hide_book_a_team_member": "Skrýt tlačítko Rezervovat člena týmu",
|
||||
"hide_book_a_team_member_description": "Skrýt tlačítko Rezervovat člena týmu na veřejných stránkách.",
|
||||
"danger_zone": "Nebezpečná zóna",
|
||||
"account_deletion_cannot_be_undone": "Pozor. Odstranění účtu nelze vrátit.",
|
||||
"account_deletion_cannot_be_undone": "Buďte opatrní. Smazání účtu nelze vrátit zpět.",
|
||||
"team_deletion_cannot_be_undone": "Buďte opatrní. Smazání týmu nelze vrátit zpět.",
|
||||
"back": "Zpět",
|
||||
"cancel": "Zrušit",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Jak často lze u vás provést rezervaci",
|
||||
"event_advanced_tab_description": "Nastavení kalendáře a další...",
|
||||
"event_advanced_tab_title": "Upřesnit",
|
||||
"event_payments_tab_title": "Platby",
|
||||
"event_payments_tab_description": "Nastavit platby pro událost",
|
||||
"event_setup_multiple_duration_error": "Nastavení události: více dob trvání vyžaduje alespoň 1 možnost.",
|
||||
"event_setup_multiple_duration_default_error": "Nastavení události: vyberte prosím platnou výchozí dobu trvání.",
|
||||
"event_setup_booking_limits_error": "Limity rezervací musí být seřazeny vzestupně. [den,týden,měsíc,rok]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Nasměrovat té správné osobě na základě odpovědí ve vašem formuláři",
|
||||
"form_description": "Vytvořte vlastní formulář, který adresujete rezervující osobě",
|
||||
"copy_link_to_form": "Kopírovat odkaz na formulář",
|
||||
"theme": "Motiv",
|
||||
"theme": "Motiv stránky pro rezervace",
|
||||
"theme_applies_note": "Toto se týká pouze vašich veřejných stránek s rezervacemi",
|
||||
"app_theme": "Téma dashboardu",
|
||||
"app_theme": "Motiv dashboardu",
|
||||
"app_theme_applies_note": "Tento se týká pouze vaše přihlášené dashboardu",
|
||||
"theme_system": "Výchozí nastavení systému",
|
||||
"add_a_team": "Přidat tým",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Není nainstalován kalendář",
|
||||
"no_calendar_installed_description": "Ještě jste nepropojili žádný ze svých kalendářů",
|
||||
"add_a_calendar": "Přidat kalendář",
|
||||
"change_email_hint": "Možná se budete muset odhlásit a znovu přihlásit, aby se změny projevily",
|
||||
"change_email_hint": "Možná se budete muset odhlásit a znovu přihlásit, aby se změna projevila.",
|
||||
"confirm_password_change_email": "Před změnou e-mailové adresy potvrďte své heslo",
|
||||
"seats": "míst",
|
||||
"every_app_published": "Každá aplikace zveřejněná v obchodě s aplikacemi na {{appName}} má otevřený zdrojový kód a je důkladně testována prostřednictvím vzájemného hodnocení. Společnost {{companyName}} nicméně tyto aplikace neschvaluje ani necertifikuje, pokud nejsou zveřejněny přímo společností {{appName}}. Pokud se setkáte s nevhodným obsahem nebo chováním, nahlaste to.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Nesprávný ověřovací kód",
|
||||
"not_verified": "Zatím neověřeno",
|
||||
"no_availability_in_month": "Žádná dostupnost pro {{month}}",
|
||||
"view_previous_month": "Zobrazit předchozí měsíc",
|
||||
"view_next_month": "Zobrazit další měsíc",
|
||||
"send_code": "Odeslat kód",
|
||||
"number_verified": "Číslo ověřeno",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Nastavení",
|
||||
"setup_description": "Nastavte instanci Cal.com",
|
||||
"configure": "Konfigurovat",
|
||||
"sso_configuration": "Single Sign-On",
|
||||
"sso_configuration": "Jednotné přihlášení",
|
||||
"sso_configuration_description": "Konfigurace SAML/OIDC SSO a umožnění členům týmu přihlašování prostřednictvím poskytovatele identity",
|
||||
"sso_configuration_description_orgs": "Nastavte SAML/OIDC SSO a umožněte členům organizace přihlásit se pomocí poskytovatele identity",
|
||||
"sso_oidc_heading": "SSO s OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Nejoblíbenější události",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Zde je přehled oblíbených událostí vašeho týmu {{teamName}} za posledních 30 dní",
|
||||
"me": "Já",
|
||||
"monthly_digest_email": "E-mail s měsíčním přehledem",
|
||||
"monthly_digest_email": "Měsíční souhrnný e-mail",
|
||||
"monthly_digest_email_for_teams": "Měsíční e-mail s přehledem pro týmy",
|
||||
"verify_team_tooltip": "Proveďte ověření svého týmu a zapněte odesílání zpráv účastníkům",
|
||||
"member_removed": "Člen odstraněn",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Pokud kontakt neexistuje pod účtem, vytvořte nový lead z účastníka",
|
||||
"salesforce_create_new_contact_under_account": "Vytvořte nový kontakt pod účtem na základě e-mailové domény účastníka a existujících kontaktů",
|
||||
"mass_assign_attributes": "Hromadné přiřazení atributů",
|
||||
"reroute_preview_custom_message": "Výsledkem je zobrazení vlastní zprávy. Zkuste změnit odpověď na přesměrování na událost",
|
||||
"reroute_preview_external_redirect": "Výsledkem je přesměrování na {{externalUrl}}. Zkuste změnit odpověď na přesměrování na událost",
|
||||
"reroute_preview_possible_host": "Možný hostitel",
|
||||
"current_routing_status": "Aktuální stav směrování",
|
||||
"reroute_booking": "Přesměrovat rezervaci",
|
||||
"reroute_booking_description": "Přesměrovat rezervaci na jiné členy týmu",
|
||||
"verify_new_route": "Ověřit novou trasu",
|
||||
"reroute": "Přesměrovat",
|
||||
"new_routing_status": "Nový stav směrování",
|
||||
"salesforce_book_directly_with_attendee_owner": "Pokud účastník existuje v Salesforce, rezervujte přímo s vlastníkem",
|
||||
"salesforce_check_owner_of": "Entita ke kontrole vlastníka pro přímou rezervaci",
|
||||
"account": "Účet",
|
||||
"choose_hosts_schedule": "Vyberte rozvrhy hostitelů",
|
||||
"hosts_schedule_description": "Můžete vybrat jiný rozvrh pro každého hostitele. Výchozí rozvrh každého hostitele je vybrán automaticky.",
|
||||
"no_hosts_description": "Žádní hostitelé nejsou přiřazeni, přidejte hostitele na kartě přiřazení.",
|
||||
"reschedule_with_same_timeslot": "Přeplánovat se stejným časovým úsekem",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Přeplánovat se stejným časovým úsekem nové události",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Přeplánovat na novou událost s jiným časovým úsekem",
|
||||
"reschedule_with_different_timeslot": "Přeplánovat s jiným časovým úsekem",
|
||||
"we_dont_have_id_for_the_new_event": "Nemáme ID pro novou událost. Prosím, přejděte do formuláře směrování a uložte jej znovu.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Rezervace byla úspěšně přesměrována. Přesměrování na stránku rezervace",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Tillad deltagere at booke dig gennem dynamiske gruppebookinger",
|
||||
"dynamic_booking": "Dynamiske gruppelinks",
|
||||
"allow_seo_indexing": "Tillad søgemaskiner at få adgang til dit offentlige indhold",
|
||||
"seo_indexing": "Tillad SEO-indeksering",
|
||||
"seo_indexing": "Tillad indeksering af søgemaskiner",
|
||||
"allow_browser_notifications": "Tillad browsermeddelelser",
|
||||
"you_have_denied_notifications": "Du har nægtet notifikationer. Nulstil tilladelsen i browserindstillingerne for at aktivere.",
|
||||
"disable_browser_notifications": "Deaktiver browsernotifikationer",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Hvor ofte du kan bookes",
|
||||
"event_advanced_tab_description": "Kalenderindstillinger & mere...",
|
||||
"event_advanced_tab_title": "Avanceret",
|
||||
"event_payments_tab_title": "Betalinger",
|
||||
"event_payments_tab_description": "Opsæt betalinger for arrangement",
|
||||
"event_setup_multiple_duration_error": "Opsætning af begivenheder: Flere varigheder kræver mindst 1 mulighed.",
|
||||
"event_setup_multiple_duration_default_error": "Opsætning af begivenheder: Vælg venligst en gyldig standardvarighed.",
|
||||
"event_setup_booking_limits_error": "Bookinggrænser skal være i stigende rækkefølge. [dag,uge,måned,år]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "Rute til den rette person baseret på svarene på din formular",
|
||||
"form_description": "Opret din formular for at rute en booker",
|
||||
"copy_link_to_form": "Kopiér link til formular",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema for bookingside",
|
||||
"theme_applies_note": "Dette gælder kun for dine offentlige bookingsider",
|
||||
"app_theme": "Dashboard-tema",
|
||||
"app_theme_applies_note": "Dette gælder kun for din loggede dashboard",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Ingen kalender installeret",
|
||||
"no_calendar_installed_description": "Du har endnu ikke tilsluttet nogen af dine kalendere",
|
||||
"add_a_calendar": "Tilføj en kalender",
|
||||
"change_email_hint": "Det kan være nødvendigt at logge ud og ind igen for at se ændringerne træde i kraft",
|
||||
"change_email_hint": "Du skal muligvis logge ud og ind igen for at se ændringen træde i kraft.",
|
||||
"confirm_password_change_email": "Bekræft venligst din adgangskode, før du ændrer din e-mailadresse",
|
||||
"seats": "pladser",
|
||||
"every_app_published": "Hver app, der offentliggøres i {{appName}} App Store, er open source og grundigt testet via peer reviews. Ikke desto mindre godkender eller bekræfter {{companyName}} ikke disse apps, medmindre de offentliggøres af {{appName}}. Hvis du støder på upassende indhold eller adfærd, bedes du rapportere det.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Forkert bekræftelseskode",
|
||||
"not_verified": "Endnu ikke bekræftet",
|
||||
"no_availability_in_month": "Ingen tilgængelighed i {{month}}",
|
||||
"view_previous_month": "Se forrige måned",
|
||||
"view_next_month": "Vis næste måned",
|
||||
"send_code": "Send kode",
|
||||
"number_verified": "Nummer Bekræftet",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Opsætning",
|
||||
"setup_description": "Opsæt Cal.com instans",
|
||||
"configure": "Indstil",
|
||||
"sso_configuration": "Enkelt Log På",
|
||||
"sso_configuration": "Single sign-on",
|
||||
"sso_configuration_description": "Konfigurer SAML/OIDC SSO og tillad teammedlemmer at logge ind ved hjælp af en Identitetsudbyder",
|
||||
"sso_configuration_description_orgs": "Konfigurer SAML/OIDC SSO og tillad organisationsmedlemmer at logge ind ved hjælp af en identitetsudbyder",
|
||||
"sso_oidc_heading": "SSO med OIDC",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Hvis kontakten ikke findes under en konto, opret nyt lead fra deltager",
|
||||
"salesforce_create_new_contact_under_account": "Opret en ny kontakt under en konto baseret på deltagerens e-maildomæne og eksisterende kontakter",
|
||||
"mass_assign_attributes": "Masse tildel attributter",
|
||||
"reroute_preview_custom_message": "Det resulterer i at vise en tilpasset besked. Prøv at ændre svaret til at omdirigere til en begivenhed",
|
||||
"reroute_preview_external_redirect": "Det resulterer i en omdirigering til {{externalUrl}}. Prøv at ændre svaret til at omdirigere til en begivenhed",
|
||||
"reroute_preview_possible_host": "Mulig vært",
|
||||
"current_routing_status": "Nuværende omdirigeringsstatus",
|
||||
"reroute_booking": "Omdiriger booking",
|
||||
"reroute_booking_description": "Omdiriger bookingen til forskellige teammedlemmer",
|
||||
"verify_new_route": "Bekræft ny rute",
|
||||
"reroute": "Omdiriger",
|
||||
"new_routing_status": "Ny omdirigeringsstatus",
|
||||
"salesforce_book_directly_with_attendee_owner": "Hvis deltageren findes i Salesforce, book direkte med ejeren",
|
||||
"salesforce_check_owner_of": "Enhed til at tjekke ejeren af for at booke direkte",
|
||||
"account": "Konto",
|
||||
"choose_hosts_schedule": "Vælg værternes tidsplaner",
|
||||
"hosts_schedule_description": "Du kan vælge en anden tidsplan for hver vært. Hver værts standardtidsplan er valgt som standard.",
|
||||
"no_hosts_description": "Ingen værter tildelt, tilføj værter i tildelingsfanen.",
|
||||
"reschedule_with_same_timeslot": "Ombook med samme tidspunkt",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Ombook med samme tidspunkt for den nye begivenhed",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Ombook til den nye begivenhed med et andet tidspunkt",
|
||||
"reschedule_with_different_timeslot": "Ombook med et andet tidspunkt",
|
||||
"we_dont_have_id_for_the_new_event": "Vi har ikke et ID for den nye begivenhed. Gå venligst til Ruteskemaet og gem det igen.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Booking omdirigeret med succes. Omdirigerer til bookingsiden",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Tilføj dine nye strenge ovenfor her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Ermöglichen Sie es den Teilnehmenden, Sie über dynamische Gruppenbuchungen zu buchen",
|
||||
"dynamic_booking": "Dynamische Gruppenlinks",
|
||||
"allow_seo_indexing": "Suchmaschinen erlauben, auf Ihre öffentlichen Inhalte zuzugreifen",
|
||||
"seo_indexing": "Suchmaschinen-Indexierung erlauben",
|
||||
"seo_indexing": "Suchmaschinenindexierung erlauben",
|
||||
"allow_browser_notifications": "Browser-Benachrichtigungen zulassen",
|
||||
"you_have_denied_notifications": "Sie haben Benachrichtigungen abgelehnt. Setzen Sie die Berechtigung in den Browser-Einstellungen zurück, um sie zu aktivieren.",
|
||||
"disable_browser_notifications": "Browser-Benachrichtigungen deaktivieren",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "„Ein Teammitglied buchen“-Schaltfläche ausblenden",
|
||||
"hide_book_a_team_member_description": "Blendet die „Ein Teammitglied buchen“-Schaltflächen auf Ihren öffentlichen Seiten aus.",
|
||||
"danger_zone": "Achtung",
|
||||
"account_deletion_cannot_be_undone": "Vorsicht. Löschen eines Kontos kann nicht rückgängig gemacht werden.",
|
||||
"team_deletion_cannot_be_undone": "Seien Sie vorsichtig. Das Löschen eines Teams kann nicht rückgängig gemacht werden",
|
||||
"account_deletion_cannot_be_undone": "Seien Sie vorsichtig. Die Kontolöschung kann nicht rückgängig gemacht werden.",
|
||||
"team_deletion_cannot_be_undone": "Seien Sie vorsichtig. Die Teamlöschung kann nicht rückgängig gemacht werden.",
|
||||
"back": "Zurück",
|
||||
"cancel": "Abbrechen",
|
||||
"cancel_all_remaining": "Alle verbleibenden absagen",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Wie oft können Sie gebucht werden",
|
||||
"event_advanced_tab_description": "Kalendereinstellungen & mehr...",
|
||||
"event_advanced_tab_title": "Erweitert",
|
||||
"event_payments_tab_title": "Zahlungen",
|
||||
"event_payments_tab_description": "Zahlungen für Veranstaltung einrichten",
|
||||
"event_setup_multiple_duration_error": "Terminkonfiguration: Mehrere Laufzeiten erfordern mindestens eine Option.",
|
||||
"event_setup_multiple_duration_default_error": "Termineinrichtung: Bitte wählen Sie eine gültige Standarddauer.",
|
||||
"event_setup_booking_limits_error": "Die Buchungslimits müssen in aufsteigender Reihenfolge sein. [Tag, Woche, Monat, Jahr]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Anhand der Formularantworten an die richtige Person weiterleiten",
|
||||
"form_description": "Erstellen Sie Ihr Formular, um einen Bucher zu anzuleiten",
|
||||
"copy_link_to_form": "Link in Formular kopieren",
|
||||
"theme": "Theme",
|
||||
"theme": "Design der Buchungsseite",
|
||||
"theme_applies_note": "Dies gilt nur für Ihre öffentlichen Buchungsseiten",
|
||||
"app_theme": "Dashboard-Thema",
|
||||
"app_theme": "Dashboard-Design",
|
||||
"app_theme_applies_note": "Dies gilt nur für Ihr eingeloggtes Dashboard",
|
||||
"theme_system": "Systemstandard",
|
||||
"add_a_team": "Team hinzufügen",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Kein Kalender installiert",
|
||||
"no_calendar_installed_description": "Sie haben noch keinen Ihrer Kalender verbunden",
|
||||
"add_a_calendar": "Kalender hinzufügen",
|
||||
"change_email_hint": "Möglicherweise müssen Sie sich aus- und einloggen, um jegliche Änderung wirksam zu machen",
|
||||
"change_email_hint": "Möglicherweise müssen Sie sich ab- und wieder anmelden, um Änderungen wirksam zu sehen.",
|
||||
"confirm_password_change_email": "Bitte bestätigen Sie Ihr Passwort, bevor Sie Ihre E-Mail-Adresse ändern",
|
||||
"seats": "Plätze",
|
||||
"every_app_published": "Jede App, die im {{appName}} App Store veröffentlicht wird, ist Open Source und gründlich durch Peer Reviews getestet. {{companyName}} unterstützt oder zertifiziert diese Apps jedoch nicht, es sei denn, sie werden von {{appName}} selbst veröffentlicht. Wenn Sie auf unangemessene Inhalte oder Verhaltensweisen stoßen, melden Sie diese bitte.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Bestätigungscode falsch",
|
||||
"not_verified": "Noch nicht bestätigt",
|
||||
"no_availability_in_month": "Keine Verfügbarkeit im {{month}}",
|
||||
"view_previous_month": "Vormonat anzeigen",
|
||||
"view_next_month": "Nächsten Monat anzeigen",
|
||||
"send_code": "Code senden",
|
||||
"number_verified": "Nummer bestätigt",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Einrichtung",
|
||||
"setup_description": "Cal.com Instanz einrichten",
|
||||
"configure": "Konfigurieren",
|
||||
"sso_configuration": "SAML-Konfiguration",
|
||||
"sso_configuration": "Single Sign-On",
|
||||
"sso_configuration_description": "SAML/OIDC SSO konfigurieren und Teammitgliedern erlauben, sich mit einem Identitätsanbieter anzumelden",
|
||||
"sso_configuration_description_orgs": "SAML/OIDC SSO konfigurieren und Organisationsmitgliedern erlauben, sich mit einem Identitätsanbieter anzumelden",
|
||||
"sso_oidc_heading": "SSO mit OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Beliebteste Termine",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Hier ist Ihre Zusammenfassung der beliebten Termine für Ihr Team {{teamName}} der letzten 30 Tage",
|
||||
"me": "Ich",
|
||||
"monthly_digest_email": "Monatliche Statistik E-Mail",
|
||||
"monthly_digest_email": "Monatliche Zusammenfassungs-E-Mail",
|
||||
"monthly_digest_email_for_teams": "Monatliche Statistik E-Mail für Teams",
|
||||
"verify_team_tooltip": "Verifizieren Sie Ihr Team, um das Senden von Nachrichten an Teilnehmer zu aktivieren",
|
||||
"member_removed": "Mitglied entfernt",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Wenn der Kontakt nicht unter einem Account existiert, erstellen Sie einen neuen Lead aus dem Teilnehmer",
|
||||
"salesforce_create_new_contact_under_account": "Erstellen Sie einen neuen Kontakt unter einem Account basierend auf der E-Mail-Domain des Teilnehmers und bestehenden Kontakten",
|
||||
"mass_assign_attributes": "Attribute massenweise zuweisen",
|
||||
"reroute_preview_custom_message": "Es führt zur Anzeige einer benutzerdefinierten Nachricht. Versuchen Sie, die Antwort zu ändern, um zu einem Ereignis weiterzuleiten",
|
||||
"reroute_preview_external_redirect": "Es führt zur Weiterleitung auf {{externalUrl}}. Versuchen Sie, die Antwort zu ändern, um zu einem Ereignis weiterzuleiten",
|
||||
"reroute_preview_possible_host": "Möglicher Gastgeber",
|
||||
"current_routing_status": "Aktueller Weiterleitungsstatus",
|
||||
"reroute_booking": "Buchung umleiten",
|
||||
"reroute_booking_description": "Leiten Sie die Buchung an andere Teammitglieder um",
|
||||
"verify_new_route": "Neue Route überprüfen",
|
||||
"reroute": "Umleiten",
|
||||
"new_routing_status": "Neuer Weiterleitungsstatus",
|
||||
"salesforce_book_directly_with_attendee_owner": "Wenn der Teilnehmer in Salesforce existiert, buchen Sie direkt beim Besitzer",
|
||||
"salesforce_check_owner_of": "Entität, deren Besitzer für die direkte Buchung überprüft werden soll",
|
||||
"account": "Account",
|
||||
"choose_hosts_schedule": "Zeitpläne der Gastgeber auswählen",
|
||||
"hosts_schedule_description": "Sie können für jeden Gastgeber einen anderen Zeitplan auswählen. Standardmäßig wird der Standardzeitplan jedes Gastgebers ausgewählt.",
|
||||
"no_hosts_description": "Keine Gastgeber zugewiesen. Fügen Sie Gastgeber im Zuweisungs-Tab hinzu.",
|
||||
"reschedule_with_same_timeslot": "Mit gleichem Zeitfenster neu planen",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Mit gleichem Zeitfenster des neuen Ereignisses neu planen",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Zum neuen Ereignis mit anderem Zeitfenster neu planen",
|
||||
"reschedule_with_different_timeslot": "Mit anderem Zeitfenster neu planen",
|
||||
"we_dont_have_id_for_the_new_event": "Wir haben keine ID für das neue Ereignis. Bitte gehen Sie zum Routing-Formular und speichern Sie es erneut.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Buchung erfolgreich umgeleitet. Weiterleitung zur Buchungsseite",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Fügen Sie Ihre neuen Code-Zeilen über dieser hinzu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -488,6 +488,7 @@
|
||||
"booking_payment_initiated": "Booking Payment Initiated",
|
||||
"meeting_ended": "Meeting Ended",
|
||||
"form_submitted": "Form Submitted",
|
||||
"form_submitted_no_event": "Form submitted, no event booked",
|
||||
"booking_paid": "Booking Paid",
|
||||
"ooo_created": "Out of Office Created",
|
||||
"booking_no_show_updated": "Booking No-Show Updated",
|
||||
@@ -1387,6 +1388,8 @@
|
||||
"event_limit_tab_description": "How often you can be booked",
|
||||
"event_advanced_tab_description": "Calendar settings & more...",
|
||||
"event_advanced_tab_title": "Advanced",
|
||||
"event_payments_tab_title": "Payments",
|
||||
"event_payments_tab_description": "Set up payments for event",
|
||||
"event_setup_multiple_duration_error": "Event Setup: Multiple durations requires at least 1 option.",
|
||||
"event_setup_multiple_duration_default_error": "Event Setup: Please select a valid default duration.",
|
||||
"event_setup_booking_limits_error": "Booking limits must be in ascending order. [day, week, month, year]",
|
||||
@@ -1797,6 +1800,7 @@
|
||||
"wrong_code": "Wrong verification code",
|
||||
"not_verified": "Not yet verified",
|
||||
"no_availability_in_month": "No availability in {{month}}",
|
||||
"view_previous_month": "View previous month",
|
||||
"view_next_month": "View next month",
|
||||
"send_code": "Send code",
|
||||
"number_verified": "Number Verified",
|
||||
@@ -2670,11 +2674,18 @@
|
||||
"choose_hosts_schedule": "Choose host schedules",
|
||||
"hosts_schedule_description": "You can select a different schedule for each host. Each host's default schedule is chosen by default.",
|
||||
"no_hosts_description": "No hosts assigned, add hosts in assignment tab.",
|
||||
"team_member_round_robin_reassign": "Manually reassign round robin host",
|
||||
"team_member_round_robin_reassign_description": "Manually reassign round robin host to another available host",
|
||||
"round_robin_reassign_description": "Reassign round robin host to another available host",
|
||||
"invalid_round_robin_host": "New user is not a valid round-robin host for this event type",
|
||||
"user_is_round_robin_fixed": "New user is a fixed host for this event type",
|
||||
"reschedule_with_same_timeslot": "Reschedule with same timeslot",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Reschedule with same timeslot of the new event",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Reschedule to the new event with different timeslot",
|
||||
"reschedule_with_different_timeslot": "Reschedule with different timeslot",
|
||||
"we_dont_have_id_for_the_new_event": "We don't have ID for the new event. Please go to the Routing Form and save it again.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Rerouted booking successfully. Redirecting to booking page",
|
||||
"you_dont_have_access_to_reroute_this_booking": "You don't have access to reroute this booking",
|
||||
"form_response_not_found": "Form response not found",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Permitir a los asistentes reservar a través de reservas dinámicas de grupo",
|
||||
"dynamic_booking": "Enlaces de grupo dinámicos",
|
||||
"allow_seo_indexing": "Permitir que los motores de búsqueda accedan a tu contenido público",
|
||||
"seo_indexing": "Permitir Indexación SEO",
|
||||
"seo_indexing": "Permitir indexación en motores de búsqueda",
|
||||
"allow_browser_notifications": "Permitir notificaciones del navegador",
|
||||
"you_have_denied_notifications": "Has denegado las notificaciones. Restablece el permiso en la configuración del navegador para habilitarlas.",
|
||||
"disable_browser_notifications": "Desactivar notificaciones del navegador",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Con qué frecuencia puedes ser reservado",
|
||||
"event_advanced_tab_description": "Configuración del calendario y más...",
|
||||
"event_advanced_tab_title": "Avanzado",
|
||||
"event_payments_tab_title": "Pagos",
|
||||
"event_payments_tab_description": "Configurar pagos para el evento",
|
||||
"event_setup_multiple_duration_error": "Configuración del evento: Duraciones múltiples requieren al menos 1 opción.",
|
||||
"event_setup_multiple_duration_default_error": "Configuración del evento: Por favor selecciona una duración predeterminada válida.",
|
||||
"event_setup_booking_limits_error": "Los límites de reserva deben estar en orden ascendente. [día, semana, mes, año]",
|
||||
@@ -1568,7 +1570,7 @@
|
||||
"copy_link_to_form": "Copiar enlace al formulario",
|
||||
"theme": "Tema de la página de reservas",
|
||||
"theme_applies_note": "Esto solo se aplica a tus páginas públicas de reservas",
|
||||
"app_theme": "Tema del panel",
|
||||
"app_theme": "Tema del panel de control",
|
||||
"app_theme_applies_note": "Esto solo se aplica a tu panel de control cuando has iniciado sesión",
|
||||
"theme_system": "Predeterminado del sistema",
|
||||
"add_a_team": "Agregar un equipo",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "No hay calendario instalado",
|
||||
"no_calendar_installed_description": "Aún no has conectado ninguno de tus calendarios",
|
||||
"add_a_calendar": "Agregar un calendario",
|
||||
"change_email_hint": "Es posible que necesites cerrar sesión y volver a iniciarla para que los cambios surtan efecto",
|
||||
"change_email_hint": "Es posible que necesites cerrar sesión y volver a iniciarla para ver los cambios aplicados.",
|
||||
"confirm_password_change_email": "Por favor confirma tu contraseña antes de cambiar tu dirección de correo electrónico",
|
||||
"seats": "asientos",
|
||||
"every_app_published": "Cada aplicación publicada en la {{appName}} App Store es de código abierto y ha sido rigurosamente probada mediante revisiones por pares. Sin embargo, {{companyName}} no respalda ni certifica estas aplicaciones a menos que sean publicadas por {{appName}}. Si encuentras contenido o comportamiento inapropiado, por favor repórtalo.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Código de verificación incorrecto",
|
||||
"not_verified": "Aún no verificado",
|
||||
"no_availability_in_month": "No hay disponibilidad en {{month}}",
|
||||
"view_previous_month": "Ver mes anterior",
|
||||
"view_next_month": "Ver el próximo mes",
|
||||
"send_code": "Enviar código",
|
||||
"number_verified": "Número verificado",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Configuración",
|
||||
"setup_description": "Configurar instancia de Cal.com",
|
||||
"configure": "Configurar",
|
||||
"sso_configuration": "Inicio de Sesión Único (SSO)",
|
||||
"sso_configuration": "Inicio de sesión único",
|
||||
"sso_configuration_description": "Configurar SSO SAML/OIDC y permitir que los miembros del equipo inicien sesión usando un Proveedor de Identidad",
|
||||
"sso_configuration_description_orgs": "Configurar SSO SAML/OIDC y permitir que los miembros de la organización inicien sesión usando un Proveedor de Identidad",
|
||||
"sso_oidc_heading": "SSO con OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Eventos Más Populares",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Aquí tienes un resumen de los eventos populares para tu equipo {{teamName}} en los últimos 30 días",
|
||||
"me": "Yo",
|
||||
"monthly_digest_email": "Correo Electrónico del Resumen Mensual",
|
||||
"monthly_digest_email": "Correo electrónico de resumen mensual",
|
||||
"monthly_digest_email_for_teams": "Correo electrónico del resumen mensual para equipos",
|
||||
"verify_team_tooltip": "Verifica tu equipo para habilitar el envío de mensajes a los asistentes",
|
||||
"member_removed": "Miembro eliminado",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Si el contacto no existe bajo una cuenta, crear un nuevo cliente potencial a partir del asistente",
|
||||
"salesforce_create_new_contact_under_account": "Crear un nuevo contacto bajo una cuenta basado en el dominio de correo electrónico del asistente y los contactos existentes",
|
||||
"mass_assign_attributes": "Asignar atributos en masa",
|
||||
"reroute_preview_custom_message": "Resulta en mostrar un mensaje personalizado. Intenta cambiar la respuesta para redirigir a un evento",
|
||||
"reroute_preview_external_redirect": "Resulta en redirigir a {{externalUrl}}. Intenta cambiar la respuesta para redirigir a un evento",
|
||||
"reroute_preview_possible_host": "Posible anfitrión",
|
||||
"current_routing_status": "Estado actual de enrutamiento",
|
||||
"reroute_booking": "Redirigir reserva",
|
||||
"reroute_booking_description": "Redirige la reserva a diferentes miembros del equipo",
|
||||
"verify_new_route": "Verificar nueva ruta",
|
||||
"reroute": "Redirigir",
|
||||
"new_routing_status": "Nuevo estado de enrutamiento",
|
||||
"salesforce_book_directly_with_attendee_owner": "Si el asistente existe en Salesforce, reservar directamente con el propietario",
|
||||
"salesforce_check_owner_of": "Entidad de la cual verificar el propietario para reservar directamente",
|
||||
"account": "Cuenta",
|
||||
"choose_hosts_schedule": "Elegir horarios de los anfitriones",
|
||||
"hosts_schedule_description": "Puedes seleccionar un horario diferente para cada anfitrión. Por defecto, se elige el horario predeterminado de cada anfitrión.",
|
||||
"no_hosts_description": "No hay anfitriones asignados, agrega anfitriones en la pestaña de asignación.",
|
||||
"reschedule_with_same_timeslot": "Reprogramar con el mismo horario",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Reprogramar con el mismo horario del nuevo evento",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Reprogramar al nuevo evento con un horario diferente",
|
||||
"reschedule_with_different_timeslot": "Reprogramar con un horario diferente",
|
||||
"we_dont_have_id_for_the_new_event": "No tenemos el ID para el nuevo evento. Por favor, ve al Formulario de Enrutamiento y guárdalo nuevamente.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Reserva redirigida exitosamente. Redirigiendo a la página de reserva",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agrega tus nuevas cadenas arriba de esta línea ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Permitir que los asistentes lo reserven a través de reservas dinámicas de grupo",
|
||||
"dynamic_booking": "Enlaces dinámicos de grupo",
|
||||
"allow_seo_indexing": "Permita que los motores de búsqueda accedan a su contenido público",
|
||||
"seo_indexing": "Permitir indexación SEO",
|
||||
"seo_indexing": "Permitir indexación en motores de búsqueda",
|
||||
"allow_browser_notifications": "Permitir notificaciones del navegador",
|
||||
"you_have_denied_notifications": "Has denegado las notificaciones. Restablece el permiso en la configuración del navegador para habilitarlas.",
|
||||
"disable_browser_notifications": "Desactivar notificaciones del navegador",
|
||||
@@ -673,7 +673,7 @@
|
||||
"hide_book_a_team_member": "Ocultar el botón Reservar un miembro del equipo",
|
||||
"hide_book_a_team_member_description": "Ocultar el botón Reservar un miembro del equipo de sus páginas públicas.",
|
||||
"danger_zone": "Paso definitivo",
|
||||
"account_deletion_cannot_be_undone": "Tenga cuidado. La eliminación de la cuenta no se puede deshacer.",
|
||||
"account_deletion_cannot_be_undone": "Ten cuidado. La eliminación de la cuenta no se puede deshacer.",
|
||||
"team_deletion_cannot_be_undone": "Ten cuidado. La eliminación del equipo no se puede deshacer.",
|
||||
"back": "Atrás",
|
||||
"cancel": "Cancelar",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Con qué frecuencia se puede reservar",
|
||||
"event_advanced_tab_description": "Ajustes del calendario y más...",
|
||||
"event_advanced_tab_title": "Avanzado",
|
||||
"event_payments_tab_title": "Pagos",
|
||||
"event_payments_tab_description": "Configurar pagos para el evento",
|
||||
"event_setup_multiple_duration_error": "Configuración de eventos: varias duraciones requieren al menos una opción.",
|
||||
"event_setup_multiple_duration_default_error": "Configuración de eventos: seleccione una duración predeterminada válida.",
|
||||
"event_setup_booking_limits_error": "Los límites de las reservas deben estar en orden ascendente. [día, semana, mes, año]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Ruta a la persona adecuada en función de las respuestas a su formulario",
|
||||
"form_description": "Crea tu formulario para dirigirlo a un agente de reservas",
|
||||
"copy_link_to_form": "Copiar enlace al formulario",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema de la página de reserva",
|
||||
"theme_applies_note": "Esto sólo se aplica a tus páginas públicas de reservas",
|
||||
"app_theme": "Tema del tablero",
|
||||
"app_theme": "Tema del panel de control",
|
||||
"app_theme_applies_note": "Esto solo se aplica a su tabla de control conectada",
|
||||
"theme_system": "Predeterminado del sistema",
|
||||
"add_a_team": "Añadir un equipo",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "No hay ningún calendario instalado",
|
||||
"no_calendar_installed_description": "Aún no has conectado ninguno de tus calendarios",
|
||||
"add_a_calendar": "Añadir un calendario",
|
||||
"change_email_hint": "Es posible que tengas que cerrar la sesión y volver a entrar para que los cambios surtan efecto",
|
||||
"change_email_hint": "Es posible que necesites cerrar sesión y volver a iniciarla para ver los cambios aplicados.",
|
||||
"confirm_password_change_email": "Confirma tu contraseña antes de cambiar la dirección de correo electrónico",
|
||||
"seats": "plazas",
|
||||
"every_app_published": "Todas las aplicaciones publicadas en la tienda de aplicaciones de {{appName}} son de código abierto y han sido probadas exhaustivamente mediante revisiones por parte de los usuarios. Sin embargo, {{companyName}} no respalda ni certifica estas aplicaciones a menos que sean publicadas por {{appName}}. Si encuentras contenidos o comportamientos inapropiados, notifícalos.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Código de verificación incorrecto",
|
||||
"not_verified": "Aún no se ha verificado",
|
||||
"no_availability_in_month": "Sin disponibilidad en {{month}}",
|
||||
"view_previous_month": "Ver mes anterior",
|
||||
"view_next_month": "Ver el próximo mes",
|
||||
"send_code": "Enviar código",
|
||||
"number_verified": "Número verificado",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Configuración",
|
||||
"setup_description": "Configurar la instancia de Cal.com",
|
||||
"configure": "Configurar",
|
||||
"sso_configuration": "Configuración de SAML",
|
||||
"sso_configuration": "Inicio de sesión único",
|
||||
"sso_configuration_description": "Configure SAML/OIDC SSO y permita que los miembros del equipo inicien sesión con un proveedor de identidad",
|
||||
"sso_configuration_description_orgs": "Configure SSO SAML/OIDC y permita que los miembros de la organización inicien sesión utilizando un proveedor de identidad",
|
||||
"sso_oidc_heading": "SSO con OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Eventos más populares",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Este es el resumen de los eventos populares de su equipo {{teamName}} durante los últimos 30 días",
|
||||
"me": "Yo",
|
||||
"monthly_digest_email": "Correo electrónico del resumen mensual",
|
||||
"monthly_digest_email": "Correo electrónico de resumen mensual",
|
||||
"monthly_digest_email_for_teams": "Correo electrónico de resumen mensual para equipos",
|
||||
"verify_team_tooltip": "Verifique su equipo para activar el envío de mensajes a los asistentes",
|
||||
"member_removed": "Miembro eliminado",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Si el contacto no existe bajo una cuenta, crear un nuevo cliente potencial a partir del asistente",
|
||||
"salesforce_create_new_contact_under_account": "Crear un nuevo contacto bajo una cuenta basado en el dominio de correo electrónico del asistente y los contactos existentes",
|
||||
"mass_assign_attributes": "Asignar atributos en masa",
|
||||
"reroute_preview_custom_message": "Resulta en mostrar un mensaje personalizado. Intenta cambiar la respuesta para redirigir a un evento",
|
||||
"reroute_preview_external_redirect": "Resulta en redirigir a {{externalUrl}}. Intenta cambiar la respuesta para redirigir a un evento",
|
||||
"reroute_preview_possible_host": "Posible anfitrión",
|
||||
"current_routing_status": "Estado actual de enrutamiento",
|
||||
"reroute_booking": "Redirigir reserva",
|
||||
"reroute_booking_description": "Redirige la reserva a diferentes miembros del equipo",
|
||||
"verify_new_route": "Verificar nueva ruta",
|
||||
"reroute": "Redirigir",
|
||||
"new_routing_status": "Nuevo estado de enrutamiento",
|
||||
"salesforce_book_directly_with_attendee_owner": "Si el asistente existe en Salesforce, reservar directamente con el propietario",
|
||||
"salesforce_check_owner_of": "Entidad de la que verificar el propietario para reservar directamente",
|
||||
"account": "Cuenta",
|
||||
"choose_hosts_schedule": "Elegir horarios de los anfitriones",
|
||||
"hosts_schedule_description": "Puedes seleccionar un horario diferente para cada anfitrión. Por defecto, se elige el horario predeterminado de cada anfitrión.",
|
||||
"no_hosts_description": "No hay anfitriones asignados, añade anfitriones en la pestaña de asignación.",
|
||||
"reschedule_with_same_timeslot": "Reprogramar con el mismo horario",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Reprogramar con el mismo horario del nuevo evento",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Reprogramar al nuevo evento con un horario diferente",
|
||||
"reschedule_with_different_timeslot": "Reprogramar con un horario diferente",
|
||||
"we_dont_have_id_for_the_new_event": "No tenemos ID para el nuevo evento. Por favor, ve al Formulario de Enrutamiento y guárdalo de nuevo.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Reserva redirigida con éxito. Redirigiendo a la página de reserva",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agregue sus nuevas cadenas arriba ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Luba osalejatel teid dünaamiliste grupibroneeringute kaudu broneerida",
|
||||
"dynamic_booking": "Dünaamilised grupilingid",
|
||||
"allow_seo_indexing": "Luba otsingumootoritel juurdepääs teie avalikule sisule",
|
||||
"seo_indexing": "Luba SEO indekseerimine",
|
||||
"seo_indexing": "Luba otsingumootorite indekseerimine",
|
||||
"allow_browser_notifications": "Luba brauseri teavitused",
|
||||
"you_have_denied_notifications": "Oled teavitused keelanud. Luba need brauseri seadetes uuesti.",
|
||||
"disable_browser_notifications": "Keela brauseri teavitused",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Kui sageli saate broneerida",
|
||||
"event_advanced_tab_description": "Kalendri seaded ja muu...",
|
||||
"event_advanced_tab_title": "Täpsem",
|
||||
"event_payments_tab_title": "Maksed",
|
||||
"event_payments_tab_description": "Seadista sündmuse maksed",
|
||||
"event_setup_multiple_duration_error": "Sündmuse seadistamine: mitu kestust nõuab vähemalt ühte valikut.",
|
||||
"event_setup_multiple_duration_default_error": "Sündmuse seadistamine: valige kehtiv vaikekestus.",
|
||||
"event_setup_booking_limits_error": "Broneerimislimiidid peavad olema kasvavas järjekorras. [päev, nädal, kuu, aasta]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Tee vormi vastuste põhjal marsruut õigele inimesele",
|
||||
"form_description": "Loo oma vorm broneerija suunamiseks",
|
||||
"copy_link_to_form": "Kopeeri link vormile",
|
||||
"theme": "Broneerimislehe teema",
|
||||
"theme": "Broneerimislehe kujundus",
|
||||
"theme_applies_note": "See kehtib ainult teie avalike broneerimislehtede kohta",
|
||||
"app_theme": "Armatuurlaua teema",
|
||||
"app_theme": "Juhtpaneeli kujundus",
|
||||
"app_theme_applies_note": "See kehtib ainult teie sisselogitud juhtpaneeli kohta",
|
||||
"theme_system": "Süsteemi vaikeseade",
|
||||
"add_a_team": "Lisa meeskond",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Kalendrit pole installitud",
|
||||
"no_calendar_installed_description": "Te pole veel ühtegi oma kalendrit ühendanud",
|
||||
"add_a_calendar": "Lisa kalender",
|
||||
"change_email_hint": "Muudatuste jõustumiseks peate võib-olla välja logima ja uuesti sisse logima",
|
||||
"change_email_hint": "Muudatuse jõustumiseks võib olla vaja välja logida ja uuesti sisse logida.",
|
||||
"confirm_password_change_email": "Palun kinnitage oma parool enne e-posti aadressi muutmist",
|
||||
"seats": "istmed",
|
||||
"every_app_published": "Kõik rakenduses {{appName}} App Store avaldatud rakendused on avatud lähtekoodiga ja neid testitakse põhjalikult kolleegide arvustuste kaudu. Sellegipoolest ei toeta ega sertifitseeri {{companyName}} neid rakendusi, välja arvatud juhul, kui {{appName}} on need avaldanud. Kui te Kui märkate sobimatut sisu või käitumist, teavitage sellest.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Vale kinnituskood",
|
||||
"not_verified": "Pole veel kinnitatud",
|
||||
"no_availability_in_month": "Pole saadaval {{kuus}}",
|
||||
"view_previous_month": "Vaata eelmist kuud",
|
||||
"view_next_month": "Vaata järgmist kuud",
|
||||
"send_code": "Saada kood",
|
||||
"number_verified": "Number kinnitatud",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Kõige populaarsemad sündmused",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Siin on teie meeskonna {{teamName}} populaarsete sündmuste kokkuvõte viimase 30 päeva jooksul",
|
||||
"me": "Mina",
|
||||
"monthly_digest_email": "Monthly Digest Email",
|
||||
"monthly_digest_email": "Igakuine kokkuvõttev e-kiri",
|
||||
"monthly_digest_email_for_teams": "Igakuine kokkuvõtte meilimeeskondadele",
|
||||
"verify_team_tooltip": "Kinnitage oma meeskond, et võimaldada osalejatele sõnumite saatmine",
|
||||
"member_removed": "Liige eemaldatud",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Kui kontakti ei eksisteeri konto all, loo osaleja andmetest uus müügivihje",
|
||||
"salesforce_create_new_contact_under_account": "Loo uus kontakt konto alla, põhinedes osaleja e-posti domeenile ja olemasolevatele kontaktidele",
|
||||
"mass_assign_attributes": "Massiline atribuutide määramine",
|
||||
"reroute_preview_custom_message": "See toob kaasa kohandatud sõnumi kuvamise. Proovi muuta vastust, et suunata sündmusele",
|
||||
"reroute_preview_external_redirect": "See toob kaasa ümbersuunamise aadressile {{externalUrl}}. Proovi muuta vastust, et suunata sündmusele",
|
||||
"reroute_preview_possible_host": "Võimalik võõrustaja",
|
||||
"current_routing_status": "Praegune marsruutimise olek",
|
||||
"reroute_booking": "Suuna broneering ümber",
|
||||
"reroute_booking_description": "Suuna broneering erinevatele meeskonnaliikmetele",
|
||||
"verify_new_route": "Kontrolli uut marsruuti",
|
||||
"reroute": "Suuna ümber",
|
||||
"new_routing_status": "Uus marsruutimise olek",
|
||||
"salesforce_book_directly_with_attendee_owner": "Kui osaleja eksisteerib Salesforce'is, broneeri otse omanikuga",
|
||||
"salesforce_check_owner_of": "Üksus, mille omanikku kontrollida otsebroneerimiseks",
|
||||
"account": "Konto",
|
||||
"choose_hosts_schedule": "Vali võõrustajate ajakavad",
|
||||
"hosts_schedule_description": "Saad valida igale võõrustajale erineva ajakava. Vaikimisi on valitud iga võõrustaja tavaline ajakava.",
|
||||
"no_hosts_description": "Võõrustajaid pole määratud, lisa need määramise vahekaardil.",
|
||||
"reschedule_with_same_timeslot": "Ajasta ümber sama ajavahemikuga",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Ajasta ümber uue sündmuse sama ajavahemikuga",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Ajasta ümber uuele sündmusele erineva ajavahemikuga",
|
||||
"reschedule_with_different_timeslot": "Ajasta ümber erineva ajavahemikuga",
|
||||
"we_dont_have_id_for_the_new_event": "Meil pole uue sündmuse ID-d. Palun mine marsruutimise vormile ja salvesta see uuesti.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Broneering edukalt ümber suunatud. Suunatakse broneerimislehele",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Autorisez les participants à prendre rendez-vous avec vous via des réservations de groupe dynamiques.",
|
||||
"dynamic_booking": "Liens de groupe dynamiques",
|
||||
"allow_seo_indexing": "Autorisez les moteurs de recherche à accéder à votre contenu public.",
|
||||
"seo_indexing": "Autoriser l'indexation SEO",
|
||||
"seo_indexing": "Autoriser l'indexation par les moteurs de recherche",
|
||||
"allow_browser_notifications": "Autoriser les notifications du navigateur",
|
||||
"you_have_denied_notifications": "Vous avez refusé les notifications. Réinitialisez l'autorisation dans les paramètres du navigateur pour les activer.",
|
||||
"disable_browser_notifications": "Désactiver les notifications du navigateur",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Masquer le bouton Réserver un membre d'équipe",
|
||||
"hide_book_a_team_member_description": "Masquez le bouton Réserver un membre d'équipe de vos pages publiques.",
|
||||
"danger_zone": "Zone de danger",
|
||||
"account_deletion_cannot_be_undone": "Attention, la suppression de compte est irréversible.",
|
||||
"team_deletion_cannot_be_undone": "Attention. La suppression de l'équipe est irréversible",
|
||||
"account_deletion_cannot_be_undone": "Attention. La suppression du compte ne peut pas être annulée.",
|
||||
"team_deletion_cannot_be_undone": "Attention. La suppression de l'équipe ne peut pas être annulée.",
|
||||
"back": "Retour",
|
||||
"cancel": "Annuler",
|
||||
"cancel_all_remaining": "Annuler tous les événements restants",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Fréquence de réservation",
|
||||
"event_advanced_tab_description": "Paramètres du calendrier, etc.",
|
||||
"event_advanced_tab_title": "Avancé",
|
||||
"event_payments_tab_title": "Paiements",
|
||||
"event_payments_tab_description": "Configurer les paiements pour l'événement",
|
||||
"event_setup_multiple_duration_error": "Configuration de l'événement : plusieurs durées requièrent au moins une option.",
|
||||
"event_setup_multiple_duration_default_error": "Configuration de l'événement : veuillez sélectionner une durée par défaut valide.",
|
||||
"event_setup_booking_limits_error": "Les limites de réservation doivent être dans l'ordre croissant. [jour, semaine, mois, année]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "Acheminez vers la bonne personne en fonction des réponses à votre formulaire.",
|
||||
"form_description": "Créez votre formulaire pour rediriger un utilisateur.",
|
||||
"copy_link_to_form": "Copier le lien vers le formulaire",
|
||||
"theme": "Apparence",
|
||||
"theme": "Thème de la page de réservation",
|
||||
"theme_applies_note": "Ceci s'applique uniquement à vos pages publiques de réservation.",
|
||||
"app_theme": "Thème du tableau de bord",
|
||||
"app_theme_applies_note": "Cela s'applique uniquement à votre tableau de bord connecté",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Aucun calendrier installé",
|
||||
"no_calendar_installed_description": "Vous n'avez pas encore connecté vos calendriers.",
|
||||
"add_a_calendar": "Ajouter un calendrier",
|
||||
"change_email_hint": "Vous devrez peut-être vous déconnecter et vous reconnecter pour que le changement prenne effet.",
|
||||
"change_email_hint": "Vous devrez peut-être vous déconnecter et vous reconnecter pour voir les changements prendre effet.",
|
||||
"confirm_password_change_email": "Veuillez confirmer votre mot de passe avant de changer votre adresse e-mail",
|
||||
"seats": "places",
|
||||
"every_app_published": "Chaque application publiée sur l'App Store de {{appName}} est open source et soigneusement testée via des évaluations par les pairs. Néanmoins, {{companyName}} n'approuve ni ne certifie ces applications à moins qu'elles ne soient publiées par {{appName}}. Si vous rencontrez un contenu ou comportement inapproprié, veuillez le signaler.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Code de vérification incorrect",
|
||||
"not_verified": "Pas encore vérifié",
|
||||
"no_availability_in_month": "Aucune disponibilité en {{month}}",
|
||||
"view_previous_month": "Voir le mois précédent",
|
||||
"view_next_month": "Voir le mois suivant",
|
||||
"send_code": "Envoyer le code",
|
||||
"number_verified": "Numéro vérifié",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Événements les plus populaires",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Voici votre résumé des événements populaires pour votre équipe {{teamName}} au cours des 30 derniers jours",
|
||||
"me": "Moi",
|
||||
"monthly_digest_email": "E-mail de résumé mensuel",
|
||||
"monthly_digest_email": "E-mail de synthèse mensuelle",
|
||||
"monthly_digest_email_for_teams": "E-mail de résumé mensuel pour les équipes.",
|
||||
"verify_team_tooltip": "Vérifiez votre équipe pour activer l'envoi de messages aux participants",
|
||||
"member_removed": "Membre supprimé",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Si le contact n'existe pas sous un compte, créer un nouveau prospect à partir du participant",
|
||||
"salesforce_create_new_contact_under_account": "Créer un nouveau contact sous un compte basé sur le domaine e-mail du participant et les contacts existants",
|
||||
"mass_assign_attributes": "Attribuer des attributs en masse",
|
||||
"reroute_preview_custom_message": "Cela affiche un message personnalisé. Essayez de modifier la réponse pour rediriger vers un événement",
|
||||
"reroute_preview_external_redirect": "Cela entraîne une redirection vers {{externalUrl}}. Essayez de modifier la réponse pour rediriger vers un événement",
|
||||
"reroute_preview_possible_host": "Hôte possible",
|
||||
"current_routing_status": "Statut d'acheminement actuel",
|
||||
"reroute_booking": "Réacheminer la réservation",
|
||||
"reroute_booking_description": "Réacheminer la réservation vers différents membres de l'équipe",
|
||||
"verify_new_route": "Vérifier le nouvel itinéraire",
|
||||
"reroute": "Réacheminer",
|
||||
"new_routing_status": "Nouveau statut d'acheminement",
|
||||
"salesforce_book_directly_with_attendee_owner": "Si le participant existe dans Salesforce, réserver directement avec le propriétaire",
|
||||
"salesforce_check_owner_of": "Entité dont il faut vérifier le propriétaire pour réserver directement",
|
||||
"account": "Compte",
|
||||
"choose_hosts_schedule": "Choisir les horaires des hôtes",
|
||||
"hosts_schedule_description": "Vous pouvez sélectionner un horaire différent pour chaque hôte. L'horaire par défaut de chaque hôte est choisi par défaut.",
|
||||
"no_hosts_description": "Aucun hôte assigné, ajoutez des hôtes dans l'onglet d'affectation.",
|
||||
"reschedule_with_same_timeslot": "Replanifier avec le même créneau horaire",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Replanifier avec le même créneau horaire du nouvel événement",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Replanifier vers le nouvel événement avec un créneau horaire différent",
|
||||
"reschedule_with_different_timeslot": "Replanifier avec un créneau horaire différent",
|
||||
"we_dont_have_id_for_the_new_event": "Nous n'avons pas d'identifiant pour le nouvel événement. Veuillez retourner au formulaire de routage et l'enregistrer à nouveau.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Réservation réacheminée avec succès. Redirection vers la page de réservation",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Ajoutez vos nouvelles chaînes ci-dessus ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "לאפשר למשתתפים לשריין איתך פגישה באמצעות הזמנות קבוצתיות דינמיות",
|
||||
"dynamic_booking": "קישורים לקבוצות דינמיות",
|
||||
"allow_seo_indexing": "מתן אפשרות למנועי חיפוש לגשת לתוכן הציבורי שלך",
|
||||
"seo_indexing": "מתן אפשרות להוספה לאינדקסים של SEO",
|
||||
"seo_indexing": "אפשר אינדוקס במנועי חיפוש",
|
||||
"allow_browser_notifications": "אפשר התראות דפדפן",
|
||||
"you_have_denied_notifications": "סירבת לקבל התראות. אפס את ההרשאה בהגדרות הדפדפן כדי לאפשר.",
|
||||
"disable_browser_notifications": "השבת התראות דפדפן",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "הסתרת הלחצן לשריון זמן של חבר/ת צוות",
|
||||
"hide_book_a_team_member_description": "הסתר/י את הלחצן לשריון זמן של חבר/ת צוות מהדפים הציבוריים שלך.",
|
||||
"danger_zone": "אזור מסוכן",
|
||||
"account_deletion_cannot_be_undone": "יש לנקוט זהירות. מחיקת חשבון היא פעולה בלתי הפיכה.",
|
||||
"team_deletion_cannot_be_undone": "יש לנקוט במשנה זהירות. מחיקת צוות היא פעולה בלתי הפיכה",
|
||||
"account_deletion_cannot_be_undone": "היזהר. לא ניתן לבטל מחיקת חשבון.",
|
||||
"team_deletion_cannot_be_undone": "היזהר. לא ניתן לבטל מחיקת צוות.",
|
||||
"back": "הקודם",
|
||||
"cancel": "ביטול",
|
||||
"cancel_all_remaining": "לבטל את כל הנותרים",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "באיזו תדירות ניתן להזמין אותך",
|
||||
"event_advanced_tab_description": "הגדרות לוח שנה ועוד...",
|
||||
"event_advanced_tab_title": "מתקדם",
|
||||
"event_payments_tab_title": "תשלומים",
|
||||
"event_payments_tab_description": "הגדרת תשלומים עבור האירוע",
|
||||
"event_setup_multiple_duration_error": "הגדרת אירוע: משכי זמן מרובים מחייבים הגדרה של אפשרות אחת לפחות.",
|
||||
"event_setup_multiple_duration_default_error": "הגדרת אירוע: יש לבחור משך זמן חוקי המוגדר כברירת מחדל.",
|
||||
"event_setup_booking_limits_error": "מגבלות על תזמונים חייבים להיות מוגדרים בסדר עולה. [יום, שבוע, חודש, שנה]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "מסלל לאדם הנכון בהתאם לתשובות בטופס שלך",
|
||||
"form_description": "צור/י טופס משלך כדי לנתב אל מזמין/ה",
|
||||
"copy_link_to_form": "להעתיק את הקישור לטופס",
|
||||
"theme": "ערכת נושא",
|
||||
"theme": "ערכת נושא לדף ההזמנות",
|
||||
"theme_applies_note": "חלה רק על דפים של הזמנות ציבוריות",
|
||||
"app_theme": "ערכת עיצוב ללוח בקרה",
|
||||
"app_theme": "ערכת נושא ללוח הבקרה",
|
||||
"app_theme_applies_note": "חל רק על לוח הבקרה שנכנסת אליו",
|
||||
"theme_system": "ברירת המחדל של המערכת",
|
||||
"add_a_team": "הוספת צוות",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "לא מותקן לוח שנה",
|
||||
"no_calendar_installed_description": "לא קישרת אף אחד מלוחות השנה שלך",
|
||||
"add_a_calendar": "הוספת לוח שנה",
|
||||
"change_email_hint": "ייתכן שיהיה צורך לצאת ולהיכנס שוב כדי לראות שהשינויים נכנסו לתוקף",
|
||||
"change_email_hint": "ייתכן שתצטרך להתנתק ולהתחבר מחדש כדי לראות את השינוי.",
|
||||
"confirm_password_change_email": "יש לאשר את הסיסמה לפני שינוי כתובת הדוא\"ל",
|
||||
"seats": "מקומות",
|
||||
"every_app_published": "כל אפליקציה שמתפרסמת ב-App Store של {{appName}} היא בעלת קוד מקור פתוח. כל אפליקציה כזו נבדקת בקפידה על ידי עמיתים. עם זאת, {{companyName}} אינה מעודדת את השימוש באף אפליקציה כזו ואינה מעניקה אישור לאפליקציות כאלה, אלא אם מדובר באפליקציות שפורסמו על ידי {{appName}}. אם נתקלת בהתנהגות או בתכנים בלתי הולמים, עליך לדווח על כך.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "קוד האימות שגוי",
|
||||
"not_verified": "האימות עדיין לא בוצע",
|
||||
"no_availability_in_month": "אין זמינות ב{{month}}",
|
||||
"view_previous_month": "צפה בחודש הקודם",
|
||||
"view_next_month": "צפייה בחודש הבא",
|
||||
"send_code": "שליחת קוד",
|
||||
"number_verified": "המספר אומת",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "התקנה",
|
||||
"setup_description": "התקנת מופע Cal.com",
|
||||
"configure": "הגדרה",
|
||||
"sso_configuration": "כניסת הזדהות אחידה SSO",
|
||||
"sso_configuration": "כניסה חד פעמית",
|
||||
"sso_configuration_description": "הגדר SSO מסוג SAML/OIDC ואפשר לחברי הצוות לבצע כניסה עם ספק הזדהות",
|
||||
"sso_configuration_description_orgs": "הגדרת SSO מסוג SAML/OIDC ולאפשר לחברי הארגון להיכנס עם ספק הזדהות",
|
||||
"sso_oidc_heading": "SSO עם OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "האירועים הפופולריים ביותר",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "הנה הסיכום של האירועים הפופולריים של הצוות שלך, {{teamName}}, ל-30 הימים האחרונים",
|
||||
"me": "אני",
|
||||
"monthly_digest_email": "אימייל עם סיכום חודשי",
|
||||
"monthly_digest_email": "דוא\"ל סיכום חודשי",
|
||||
"monthly_digest_email_for_teams": "אימייל עם סיכום חודשי עבור צוותים",
|
||||
"verify_team_tooltip": "אמת/י את הצוות שלך כדי לאפשר שליחת הודעות למשתתפים",
|
||||
"member_removed": "החבר הוסר",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "אם איש הקשר לא קיים תחת חשבון, צור ליד חדש מהמשתתף",
|
||||
"salesforce_create_new_contact_under_account": "צור איש קשר חדש תחת חשבון בהתבסס על דומיין האימייל של המשתתף ואנשי קשר קיימים",
|
||||
"mass_assign_attributes": "הקצאת מאפיינים באופן גורף",
|
||||
"reroute_preview_custom_message": "התוצאה היא הצגת הודעה מותאמת אישית. נסה לשנות את התגובה כדי לנתב לאירוע",
|
||||
"reroute_preview_external_redirect": "התוצאה היא הפניה ל-{{externalUrl}}. נסה לשנות את התגובה כדי לנתב לאירוע",
|
||||
"reroute_preview_possible_host": "מארח אפשרי",
|
||||
"current_routing_status": "מצב ניתוב נוכחי",
|
||||
"reroute_booking": "ניתוב מחדש של ההזמנה",
|
||||
"reroute_booking_description": "נתב מחדש את ההזמנה לחברי צוות שונים",
|
||||
"verify_new_route": "אמת נתיב חדש",
|
||||
"reroute": "נתב מחדש",
|
||||
"new_routing_status": "מצב ניתוב חדש",
|
||||
"salesforce_book_directly_with_attendee_owner": "אם המשתתף קיים ב-Salesforce, הזמן ישירות עם הבעלים",
|
||||
"salesforce_check_owner_of": "ישות לבדוק את הבעלים שלה כדי להזמין ישירות",
|
||||
"account": "חשבון",
|
||||
"choose_hosts_schedule": "בחר לוחות זמנים למארחים",
|
||||
"hosts_schedule_description": "ניתן לבחור לוח זמנים שונה לכל מארח. לוח הזמנים המוגדר כברירת מחדל נבחר עבור כל מארח.",
|
||||
"no_hosts_description": "לא הוקצו מארחים, הוסף מארחים בלשונית ההקצאה.",
|
||||
"reschedule_with_same_timeslot": "קבע מועד חדש באותו משבצת זמן",
|
||||
"reschedule_with_same_timeslot_of_new_event": "קבע מועד חדש באותה משבצת זמן של האירוע החדש",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "קבע מועד חדש לאירוע החדש במשבצת זמן שונה",
|
||||
"reschedule_with_different_timeslot": "קבע מועד חדש במשבצת זמן שונה",
|
||||
"we_dont_have_id_for_the_new_event": "אין לנו מזהה לאירוע החדש. אנא עבור לטופס הניתוב ושמור אותו שוב.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "ההזמנה נותבה מחדש בהצלחה. מפנה לדף ההזמנות",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Lehetővé teszi a résztvevők számára, hogy dinamikus csoportos foglalásokon keresztül foglalhassanak",
|
||||
"dynamic_booking": "Dinamikus csoporthivatkozások",
|
||||
"allow_seo_indexing": "Engedélyezze a keresőmotorok számára, hogy hozzáférjenek nyilvános tartalmaihoz",
|
||||
"seo_indexing": "SEO indexelés engedélyezése",
|
||||
"seo_indexing": "Keresőmotorok indexelésének engedélyezése",
|
||||
"allow_browser_notifications": "Engedélyezze a böngésző értesítéseit",
|
||||
"you_have_denied_notifications": "Elutasította az értesítéseket. Engedélyezze újra a böngésző beállításaiban.",
|
||||
"disable_browser_notifications": "Tiltsa le a böngésző értesítéseit",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Csapattag foglalása gomb elrejtése",
|
||||
"hide_book_a_team_member_description": "A Csoporttag foglalása gomb elrejtése nyilvános oldalairól.",
|
||||
"danger_zone": "Veszélyzóna",
|
||||
"account_deletion_cannot_be_undone": "Légy óvatos. A fiók törlése nem vonható vissza.",
|
||||
"team_deletion_cannot_be_undone": "Légy óvatos. A csapat törlése nem vonható vissza.",
|
||||
"account_deletion_cannot_be_undone": "Legyen óvatos. A fiók törlése nem vonható vissza.",
|
||||
"team_deletion_cannot_be_undone": "Legyen óvatos. A csapat törlése nem vonható vissza.",
|
||||
"back": "Vissza",
|
||||
"cancel": "Mégse",
|
||||
"cancel_all_remaining": "Összes lemondása",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Milyen gyakran vagy foglalható",
|
||||
"event_advanced_tab_description": "A naptárbeállítások és egyebek...",
|
||||
"event_advanced_tab_title": "Haladó",
|
||||
"event_payments_tab_title": "Fizetések",
|
||||
"event_payments_tab_description": "Fizetések beállítása az eseményhez",
|
||||
"event_setup_multiple_duration_error": "Esemény beállítása: Több időtartamhoz legalább 1 opció szükséges.",
|
||||
"event_setup_multiple_duration_default_error": "Esemény beállítása: Válasszon érvényes alapértelmezett időtartamot.",
|
||||
"event_setup_booking_limits_error": "A foglalási limiteknek növekvő sorrendben kell lenniük. [nap, hét, hónap, év]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Az űrlapra adott válaszok alapján irányítson a megfelelő személyhez",
|
||||
"form_description": "Hozzon létre űrlapot a foglaló átirányításához",
|
||||
"copy_link_to_form": "Link másolása az űrlapra",
|
||||
"theme": "Foglalási oldal téma",
|
||||
"theme": "Foglalási oldal témája",
|
||||
"theme_applies_note": "Ez csak a nyilvános foglalási oldalaira vonatkozik",
|
||||
"app_theme": "Irányítópult téma",
|
||||
"app_theme": "Irányítópult témája",
|
||||
"app_theme_applies_note": "Ez csak a bejelentkezett irányítópultra vonatkozik",
|
||||
"theme_system": "Rendszer alapértelmezett",
|
||||
"add_a_team": "Csapat hozzáadása",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Nincs telepítve naptár",
|
||||
"no_calendar_installed_description": "Még nem kapcsolta be egyik naptárát sem",
|
||||
"add_a_calendar": "Naptár hozzáadása",
|
||||
"change_email_hint": "Előfordulhat, hogy ki kell jelentkeznie, majd újra be kell jelentkeznie ahhoz, hogy a változtatások életbe lépjenek",
|
||||
"change_email_hint": "Előfordulhat, hogy ki kell jelentkeznie, majd újra be kell jelentkeznie, hogy lássa a változásokat.",
|
||||
"confirm_password_change_email": "Kérjük, erősítse meg jelszavát, mielőtt megváltoztatná e-mail címét",
|
||||
"seats": "ülések",
|
||||
"every_app_published": "Minden, az {{appName}} Alkalmazás Áruházban közzétett alkalmazás nyílt forráskódú, és szakértői értékeléseken keresztül alaposan tesztelték. Ennek ellenére a {{companyName}} nem támogatja és nem tanúsítja ezeket az alkalmazásokat, kivéve, ha azokat a {{appName}} tette közzé. Ha nem megfelelő tartalommal vagy viselkedéssel találkozik, kérjük, jelentse.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Rossz ellenőrző kód",
|
||||
"not_verified": "Még nincs ellenőrizve",
|
||||
"no_availability_in_month": "Nem elérhető: {{month}}",
|
||||
"view_previous_month": "Előző hónap megtekintése",
|
||||
"view_next_month": "Következő hónap megtekintése",
|
||||
"send_code": "Kód küldése",
|
||||
"number_verified": "Szám ellenőrizve",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Legnépszerűbb események",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Íme az Ön csapata ({{teamName}}) népszerű eseményeinek összefoglalója az elmúlt 30 napban",
|
||||
"me": "Én",
|
||||
"monthly_digest_email": "Havi kivonat e-mail",
|
||||
"monthly_digest_email": "Havi összefoglaló e-mail",
|
||||
"monthly_digest_email_for_teams": "Havi kivonat e-mailek csapatoknak",
|
||||
"verify_team_tooltip": "Erősítse meg csapatát, hogy engedélyezze az üzenetek küldését a résztvevőknek",
|
||||
"member_removed": "Tag eltávolítva",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Ha a kapcsolat nem létezik egy fiók alatt, hozzon létre új érdeklődőt a résztvevőből",
|
||||
"salesforce_create_new_contact_under_account": "Hozzon létre új kapcsolatot egy fiók alatt a résztvevő e-mail domainje és meglévő kapcsolatok alapján",
|
||||
"mass_assign_attributes": "Attribútumok tömeges hozzárendelése",
|
||||
"reroute_preview_custom_message": "Egyedi üzenet megjelenítését eredményezi. Próbálja meg megváltoztatni a választ egy eseményhez való átirányításra",
|
||||
"reroute_preview_external_redirect": "A {{externalUrl}} címre történő átirányítást eredményezi. Próbálja meg megváltoztatni a választ egy eseményhez való átirányításra",
|
||||
"reroute_preview_possible_host": "Lehetséges házigazda",
|
||||
"current_routing_status": "Jelenlegi útválasztási állapot",
|
||||
"reroute_booking": "Foglalás átirányítása",
|
||||
"reroute_booking_description": "A foglalás átirányítása más csapattagokhoz",
|
||||
"verify_new_route": "Új útvonal ellenőrzése",
|
||||
"reroute": "Átirányítás",
|
||||
"new_routing_status": "Új útválasztási állapot",
|
||||
"salesforce_book_directly_with_attendee_owner": "Ha a résztvevő létezik a Salesforce-ban, foglaljon közvetlenül a tulajdonossal",
|
||||
"salesforce_check_owner_of": "Entitás, amelynek tulajdonosát ellenőrizni kell a közvetlen foglaláshoz",
|
||||
"account": "Fiók",
|
||||
"choose_hosts_schedule": "Válassza ki a házigazdák ütemtervét",
|
||||
"hosts_schedule_description": "Minden házigazdához különböző ütemtervet választhat. Alapértelmezés szerint minden házigazda alapértelmezett ütemterve van kiválasztva.",
|
||||
"no_hosts_description": "Nincsenek hozzárendelt házigazdák, adjon hozzá házigazdákat a hozzárendelési fülön.",
|
||||
"reschedule_with_same_timeslot": "Átütemezés ugyanabban az időpontban",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Átütemezés az új esemény ugyanazon időpontjában",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Átütemezés az új eseményre más időpontban",
|
||||
"reschedule_with_different_timeslot": "Átütemezés más időpontban",
|
||||
"we_dont_have_id_for_the_new_event": "Nincs azonosítónk az új eseményhez. Kérjük, menjen az Útválasztási űrlaphoz és mentse el újra.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "A foglalás átirányítása sikeres. Átirányítás a foglalási oldalra",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adja hozzá az új karakterláncokat fent ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Consenti ai partecipanti di prenotare per te utilizzando le prenotazioni di gruppo dinamiche",
|
||||
"dynamic_booking": "Collegamenti dinamici di gruppo",
|
||||
"allow_seo_indexing": "Consenti ai motori di ricerca di accedere ai tuoi contenuti pubblici",
|
||||
"seo_indexing": "Consenti l'indicizzazione SEO",
|
||||
"seo_indexing": "Consenti l'indicizzazione dei motori di ricerca",
|
||||
"allow_browser_notifications": "Consenti notifiche del browser",
|
||||
"you_have_denied_notifications": "Hai negato le notifiche. Reimposta i permessi nelle impostazioni del browser per abilitarle.",
|
||||
"disable_browser_notifications": "Disabilita notifiche del browser",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Nascondi pulsante Prenota un membro del team",
|
||||
"hide_book_a_team_member_description": "Nasconde il pulsante Prenota un membro del team nelle pagine pubbliche.",
|
||||
"danger_zone": "Zona pericolosa",
|
||||
"account_deletion_cannot_be_undone": "Attenzione. Non è possibile annullare l'eliminazione dell'account.",
|
||||
"team_deletion_cannot_be_undone": "Attenzione. Non è possibile annullare l'eliminazione del team",
|
||||
"account_deletion_cannot_be_undone": "Attenzione. L'eliminazione dell'account non può essere annullata.",
|
||||
"team_deletion_cannot_be_undone": "Attenzione. L'eliminazione del team non può essere annullata.",
|
||||
"back": "Indietro",
|
||||
"cancel": "Annulla",
|
||||
"cancel_all_remaining": "Annulla tutti i rimanenti",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Con quale frequenza è possibile fare una prenotazione",
|
||||
"event_advanced_tab_description": "Impostazioni calendario e altro...",
|
||||
"event_advanced_tab_title": "Avanzate",
|
||||
"event_payments_tab_title": "Pagamenti",
|
||||
"event_payments_tab_description": "Configura i pagamenti per l'evento",
|
||||
"event_setup_multiple_duration_error": "Impostazione evento: In caso di più durate è richiesta almeno 1 opzione.",
|
||||
"event_setup_multiple_duration_default_error": "Impostazione evento: Selezionare una durata predefinita valida.",
|
||||
"event_setup_booking_limits_error": "I limiti per le prenotazioni devono essere specificati in ordine crescente [giorno,settimana,mese,anno]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Instrada gli utenti verso la persona giusta in base alle risposte al modulo",
|
||||
"form_description": "Crea un modulo per instradare le persone che prenotano",
|
||||
"copy_link_to_form": "Copia il link del modulo",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema della pagina di prenotazione",
|
||||
"theme_applies_note": "Si applica solo alle pagine di prenotazione pubbliche",
|
||||
"app_theme": "Tema Pannello di controllo",
|
||||
"app_theme": "Tema della dashboard",
|
||||
"app_theme_applies_note": "Questo riguarda solo il pannello di controllo per cui hai effettuato l'accesso",
|
||||
"theme_system": "Predefinito di sistema",
|
||||
"add_a_team": "Aggiungi un team",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Nessun calendario installato",
|
||||
"no_calendar_installed_description": "Non hai ancora collegato alcun calendario",
|
||||
"add_a_calendar": "Aggiungi un calendario",
|
||||
"change_email_hint": "Per rendere effettive le modifiche, potrebbe essere necessario disconnettersi e accedere nuovamente",
|
||||
"change_email_hint": "Potrebbe essere necessario disconnettersi e riconnettersi per vedere le modifiche applicate.",
|
||||
"confirm_password_change_email": "Conferma la password prima di cambiare l'indirizzo e-mail",
|
||||
"seats": "posti",
|
||||
"every_app_published": "Tutte le applicazioni pubblicate nell'App store di {{appName}} sono open source e sono state testate in modo approfondito attraverso una valutazione tra pari. Tuttavia, {{companyName}} non promuove né certifica tali applicazioni, a meno che siano pubblicate da {{appName}}. Se noti contenuti o comportamenti inappropriati, ti invitiamo a segnalarceli.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Codice di verifica errato",
|
||||
"not_verified": "Non ancora verificato",
|
||||
"no_availability_in_month": "Nessuna disponibilità in {{month}}",
|
||||
"view_previous_month": "Visualizza mese precedente",
|
||||
"view_next_month": "Visualizza il mese successivo",
|
||||
"send_code": "Invia codice",
|
||||
"number_verified": "Numero verificato",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Impostazione",
|
||||
"setup_description": "Imposta istanza Cal.com",
|
||||
"configure": "Configura",
|
||||
"sso_configuration": "Configurazione SAML",
|
||||
"sso_configuration": "Configurazione Single Sign-On",
|
||||
"sso_configuration_description": "Configura accesso SSO tramite SAML/OIDC e consenti ai membri del team di accedere utilizzando un provider di identità",
|
||||
"sso_configuration_description_orgs": "Configura l'accesso SSO con SAML/OIDC per consentire ai membri dell'organizzazione di accedere utilizzando un provider di identità",
|
||||
"sso_oidc_heading": "SSO con OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Eventi più popolari",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Ecco il riepilogo degli eventi popolari per il tuo team {{teamName}} negli ultimi 30 giorni",
|
||||
"me": "Io",
|
||||
"monthly_digest_email": "E-mail riepilogo mensile",
|
||||
"monthly_digest_email": "Email di riepilogo mensile",
|
||||
"monthly_digest_email_for_teams": "E-mail di riepilogo mensili per i team",
|
||||
"verify_team_tooltip": "Effettua la verifica del tuo team per abilitare l'invio di messaggi ai partecipanti",
|
||||
"member_removed": "Membro rimosso",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Se il contatto non esiste sotto un account, crea un nuovo lead dal partecipante",
|
||||
"salesforce_create_new_contact_under_account": "Crea un nuovo contatto sotto un account basato sul dominio email del partecipante e contatti esistenti",
|
||||
"mass_assign_attributes": "Assegna attributi in massa",
|
||||
"reroute_preview_custom_message": "Risulta nella visualizzazione di un messaggio personalizzato. Prova a modificare la risposta per indirizzare a un evento",
|
||||
"reroute_preview_external_redirect": "Risulta nel reindirizzamento a {{externalUrl}}. Prova a modificare la risposta per indirizzare a un evento",
|
||||
"reroute_preview_possible_host": "Possibile host",
|
||||
"current_routing_status": "Stato di instradamento attuale",
|
||||
"reroute_booking": "Reindirizza prenotazione",
|
||||
"reroute_booking_description": "Reindirizza la prenotazione a diversi membri del team",
|
||||
"verify_new_route": "Verifica nuovo percorso",
|
||||
"reroute": "Reindirizza",
|
||||
"new_routing_status": "Nuovo stato di instradamento",
|
||||
"salesforce_book_directly_with_attendee_owner": "Se il partecipante esiste in Salesforce, prenota direttamente con il proprietario",
|
||||
"salesforce_check_owner_of": "Entità per verificare il proprietario per prenotare direttamente",
|
||||
"account": "Account",
|
||||
"choose_hosts_schedule": "Scegli gli orari degli host",
|
||||
"hosts_schedule_description": "Puoi selezionare un orario diverso per ogni host. L'orario predefinito di ciascun host è selezionato di default.",
|
||||
"no_hosts_description": "Nessun host assegnato, aggiungi host nella scheda di assegnazione.",
|
||||
"reschedule_with_same_timeslot": "Riprogramma con lo stesso slot orario",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Riprogramma con lo stesso slot orario del nuovo evento",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Riprogramma al nuovo evento con slot orario diverso",
|
||||
"reschedule_with_different_timeslot": "Riprogramma con slot orario diverso",
|
||||
"we_dont_have_id_for_the_new_event": "Non abbiamo l'ID per il nuovo evento. Per favore, vai al Modulo di Instradamento e salvalo di nuovo.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Prenotazione reindirizzata con successo. Reindirizzamento alla pagina di prenotazione",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Aggiungi le tue nuove stringhe qui sopra ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "出席者が動的なグループ予約を通じてあなたを予約できるようにする",
|
||||
"dynamic_booking": "ダイナミックグループリンク",
|
||||
"allow_seo_indexing": "検索エンジンが公開コンテンツにアクセスできるようにする",
|
||||
"seo_indexing": "SEO インデックス作成を許可",
|
||||
"seo_indexing": "検索エンジンのインデックス作成を許可",
|
||||
"allow_browser_notifications": "ブラウザ通知を許可",
|
||||
"you_have_denied_notifications": "通知が拒否されました。ブラウザの設定で権限をリセットして有効にしてください。",
|
||||
"disable_browser_notifications": "ブラウザ通知を無効にする",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "「チームメンバーを予約する」ボタンを非表示にする",
|
||||
"hide_book_a_team_member_description": "公開ページから「チームメンバーを予約する」ボタンを非表示にします。",
|
||||
"danger_zone": "危険ゾーン",
|
||||
"account_deletion_cannot_be_undone": "ご注意ください。アカウントの削除は元に戻せません。",
|
||||
"team_deletion_cannot_be_undone": "注意: チームの削除は元に戻せません。",
|
||||
"account_deletion_cannot_be_undone": "注意してください。アカウントの削除は元に戻せません。",
|
||||
"team_deletion_cannot_be_undone": "注意してください。チームの削除は元に戻せません。",
|
||||
"back": "戻る",
|
||||
"cancel": "キャンセル",
|
||||
"cancel_all_remaining": "残りをすべてキャンセル",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "予約できる頻度",
|
||||
"event_advanced_tab_description": "カレンダー設定など...",
|
||||
"event_advanced_tab_title": "詳細設定",
|
||||
"event_payments_tab_title": "支払い",
|
||||
"event_payments_tab_description": "イベントの支払いを設定する",
|
||||
"event_setup_multiple_duration_error": "イベント設定:複数の期間には少なくとも1つのオプションが必要です。",
|
||||
"event_setup_multiple_duration_default_error": "イベント設定:有効なデフォルト期間を選択してください。",
|
||||
"event_setup_booking_limits_error": "予約制限の日時表記は、昇順にする必要があります。[日、週、月、年]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "フォームへの回答に基づいて適切なユーザーにルーティングする",
|
||||
"form_description": "予約者をルーティングするフォームを作成します",
|
||||
"copy_link_to_form": "フォームへのリンクをコピー",
|
||||
"theme": "テーマ",
|
||||
"theme": "予約ページのテーマ",
|
||||
"theme_applies_note": "公開予約ページにのみ適用されます",
|
||||
"app_theme": "ダッシュボードのテーマ",
|
||||
"app_theme_applies_note": "これは、ログインしているダッシュボードにのみ適用されます",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "カレンダーがインストールされていません",
|
||||
"no_calendar_installed_description": "まだどのカレンダーも接続していません",
|
||||
"add_a_calendar": "カレンダーを追加",
|
||||
"change_email_hint": "変更内容を反映させるためには、一度ログアウトしてからもう一度ログインする必要があります。",
|
||||
"change_email_hint": "変更を反映させるには、一度ログアウトして再度ログインする必要があるかもしれません。",
|
||||
"confirm_password_change_email": "メールアドレスを変更する前に、パスワードをご確認ください",
|
||||
"seats": "座席数",
|
||||
"every_app_published": "{{appName}} の App Store で公開されているすべてのアプリはオープンソースであり、ピアレビューを介して入念なテストが実施されています。しかしながら、{{companyName}} はこれらのアプリが {{appName}} で公開されていない限りは推奨または認証を行っていません。不適切なコンテンツまたは行為を発見した場合には、ぜひご一報ください。",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "確認コードが間違っています",
|
||||
"not_verified": "まだ確認されていません",
|
||||
"no_availability_in_month": "{{month}} は参加できません",
|
||||
"view_previous_month": "前月を表示",
|
||||
"view_next_month": "来月を表示",
|
||||
"send_code": "コードを送信",
|
||||
"number_verified": "番号が確認されました",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "最も人気のイベント",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "こちらはこの 30 日間、チーム {{teamName}} で最も人気があったイベントのサマリーです",
|
||||
"me": "私",
|
||||
"monthly_digest_email": "月 1 回のダイジェストのメール",
|
||||
"monthly_digest_email": "月次ダイジェストメール",
|
||||
"monthly_digest_email_for_teams": "チームのための月 1 回のダイジェストのメール",
|
||||
"verify_team_tooltip": "出席者へのメッセージ送信ができるようにするには、チームを確認してください",
|
||||
"member_removed": "メンバーが削除されました",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "連絡先がアカウントに存在しない場合、参加者から新しいリードを作成する",
|
||||
"salesforce_create_new_contact_under_account": "参加者のメールドメインと既存の連絡先に基づいて、新しい連絡先をアカウント下に作成する",
|
||||
"mass_assign_attributes": "属性の一括割り当て",
|
||||
"reroute_preview_custom_message": "カスタムメッセージが表示されます。応答を変更してイベントにルーティングしてみてください",
|
||||
"reroute_preview_external_redirect": "{{externalUrl}}にリダイレクトされます。応答を変更してイベントにルーティングしてみてください",
|
||||
"reroute_preview_possible_host": "可能なホスト",
|
||||
"current_routing_status": "現在のルーティング状況",
|
||||
"reroute_booking": "予約の再ルーティング",
|
||||
"reroute_booking_description": "予約を別のチームメンバーに再ルーティングする",
|
||||
"verify_new_route": "新しいルートを確認",
|
||||
"reroute": "再ルーティング",
|
||||
"new_routing_status": "新しいルーティング状況",
|
||||
"salesforce_book_directly_with_attendee_owner": "参加者がSalesforceに存在する場合、所有者と直接予約する",
|
||||
"salesforce_check_owner_of": "直接予約するために所有者を確認するエンティティ",
|
||||
"account": "アカウント",
|
||||
"choose_hosts_schedule": "ホストのスケジュールを選択",
|
||||
"hosts_schedule_description": "各ホストに異なるスケジュールを選択できます。各ホストのデフォルトスケジュールが初期設定として選択されています。",
|
||||
"no_hosts_description": "ホストが割り当てられていません。割り当てタブでホストを追加してください。",
|
||||
"reschedule_with_same_timeslot": "同じタイムスロットで再スケジュール",
|
||||
"reschedule_with_same_timeslot_of_new_event": "新しいイベントの同じタイムスロットで再スケジュール",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "異なるタイムスロットで新しいイベントに再スケジュール",
|
||||
"reschedule_with_different_timeslot": "異なるタイムスロットで再スケジュール",
|
||||
"we_dont_have_id_for_the_new_event": "新しいイベントのIDがありません。ルーティングフォームに戻って再度保存してください。",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "予約の再ルーティングに成功しました。予約ページにリダイレクトしています",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ この上に新しい文字列を追加してください ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "អនុញ្ញាតឱ្យអ្នកចូលរួមកក់អ្នកតាមរយៈការកក់ក្រុមដោយស្វ័យប្រវត្តិ",
|
||||
"dynamic_booking": "តំណភ្ជាប់ក្រុមដោយស្វ័យប្រវត្តិ",
|
||||
"allow_seo_indexing": "អនុញ្ញាតឱ្យម៉ាស៊ីនស្វែងរកចូលដំណើរការការគ្រប់គ្រងសាធារណៈរបស់អ្នក",
|
||||
"seo_indexing": "អនុញ្ញាត SEO Indexing",
|
||||
"seo_indexing": "អនុញ្ញាតឱ្យម៉ាស៊ីនស្វែងរកធ្វើការដាក់លិបិក្រម",
|
||||
"allow_browser_notifications": "អនុញ្ញាតការជូនដំណឹងតាមរយៈកម្មវិធីរុករក",
|
||||
"you_have_denied_notifications": "អ្នកបានបដិសេធការជូនដំណឹង។ កំណត់សិទ្ធិឡើងវិញក្នុងការកំណត់កម្មវិធីរុករកដើម្បីអនុញ្ញាត។",
|
||||
"disable_browser_notifications": "បិទការជូនដំណឹងតាមរយៈកម្មវិធីរុករក",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "លាក់ប៊ូតុងកក់សមាជិកក្រុម",
|
||||
"hide_book_a_team_member_description": "លាក់ប៊ូតុងកក់សមាជិកក្រុមពីទំព័រសាធារណៈរបស់អ្នក។",
|
||||
"danger_zone": "តំបន់គ្រោះថ្នាក់",
|
||||
"account_deletion_cannot_be_undone": "ប្រយ័ត្ន។ ការលុបគណនីមិនអាចត្រឡប់វិញបានទេ។",
|
||||
"team_deletion_cannot_be_undone": "ប្រយ័ត្ន។ ការលុបក្រុមមិនអាចត្រឡប់វិញបានទេ។",
|
||||
"account_deletion_cannot_be_undone": "សូមប្រុងប្រយ័ត្ន។ ការលុបគណនីមិនអាចត្រឡប់ក្រោយវិញបានទេ។",
|
||||
"team_deletion_cannot_be_undone": "សូមប្រុងប្រយ័ត្ន។ ការលុបក្រុមមិនអាចត្រឡប់ក្រោយវិញបានទេ។",
|
||||
"back": "ត្រឡប់",
|
||||
"cancel": "បោះបង់",
|
||||
"cancel_all_remaining": "បោះបង់ទាំងអស់ដែលនៅសល់",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "ភាពញឹកញាប់ដែលអ្នកអាចត្រូវបានកក់",
|
||||
"event_advanced_tab_description": "ការកំណត់កាលវិភាគ និងច្រើនទៀត...",
|
||||
"event_advanced_tab_title": "កម្រិតខ្ពស់",
|
||||
"event_payments_tab_title": "ការទូទាត់",
|
||||
"event_payments_tab_description": "រៀបចំការទូទាត់សម្រាប់ព្រឹត្តិការណ៍",
|
||||
"event_setup_multiple_duration_error": "ការកំណត់ព្រឹត្តិការណ៍: រយៈពេលច្រើនត្រូវការយ៉ាងហោចណាស់ 1 ជម្រើស។",
|
||||
"event_setup_multiple_duration_default_error": "ការកំណត់ព្រឹត្តិការណ៍: សូមជ្រើសរយៈពេលលំនាំដើមដែលត្រឹមត្រូវ។",
|
||||
"event_setup_booking_limits_error": "ដែនកំណត់ការកក់ត្រូវតែមានលំដាប់ឡើងលើ។ [ថ្ងៃ, សប្តាហ៍, ខែ, ឆ្នាំ]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "ដឹកនាំទៅកាន់មនុស្សត្រឹមត្រូវដោយផ្អែកលើចម្លើយនៃទម្រង់របស់អ្នក",
|
||||
"form_description": "បង្កើតទម្រង់របស់អ្នកដើម្បីដឹកនាំអ្នកកក់",
|
||||
"copy_link_to_form": "ចម្លងតំណទៅទម្រង់",
|
||||
"theme": "ប្រធានបទទំព័រការកក់",
|
||||
"theme": "រូបរាងទំព័រកក់",
|
||||
"theme_applies_note": "នេះអនុវត្តចំពោះទំព័រការកក់សាធារណៈរបស់អ្នកប៉ុណ្ណោះ",
|
||||
"app_theme": "ប្រធានបទផ្ទាំងគ្រប់គ្រង",
|
||||
"app_theme": "រូបរាងផ្ទាំងគ្រប់គ្រង",
|
||||
"app_theme_applies_note": "នេះអនុវត្តចំពោះផ្ទាំងគ្រប់គ្រងដែលបានចូលរបស់អ្នកប៉ុណ្ណោះ",
|
||||
"theme_system": "លំនាំប្រព័ន្ធ",
|
||||
"add_a_team": "បន្ថែមក្រុម",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "មិនមានប្រតិទិនបានដំឡើង",
|
||||
"no_calendar_installed_description": "អ្នកមិនទាន់បានភ្ជាប់ប្រតិទិនណាមួយទេ",
|
||||
"add_a_calendar": "បន្ថែមប្រតិទិន",
|
||||
"change_email_hint": "អ្នកប្រហែលជាត្រូវចាកចេញហើយចូលវិញដើម្បីឃើញការផ្លាស់ប្តូរបានប្រសិទ្ធិភាព",
|
||||
"change_email_hint": "អ្នកអាចត្រូវចេញពីគណនី និងចូលម្តងទៀតដើម្បីមើលឃើញការផ្លាស់ប្តូរណាមួយមានប្រសិទ្ធភាព។",
|
||||
"confirm_password_change_email": "សូមបញ្ជាក់ពាក្យសម្ងាត់របស់អ្នកមុនពេលផ្លាស់ប្តូរអាសយដ្ឋានអ៊ីមែល",
|
||||
"seats": "កៅអី",
|
||||
"every_app_published": "កម្មវិធីគ្រប់យ៉ាងដែលបានបោះពុម្ពផ្សាយនៅលើ {{appName}} App Store គឺជាប្រភពបើកចំហ និងត្រូវបានធ្វើតេស្តយ៉ាងម៉ត់ចត់តាមរយៈការពិនិត្យរបស់មិត្តរួមការងារ។ ទោះយ៉ាងណា {{companyName}} មិនទទួលស្គាល់ឬបញ្ជាក់កម្មវិធីទាំងនេះទេ លុះត្រាតែវាត្រូវបានបោះពុម្ពផ្សាយដោយ {{appName}}។ ប្រសិនបើអ្នកជួបប្រទះមាតិកាឬអាកប្បកិរិយាដែលមិនសមរម្យ សូមរាយការណ៍។",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "កូដផ្ទៀងផ្ទាត់មិនត្រឹមត្រូវ",
|
||||
"not_verified": "មិនទាន់បានផ្ទៀងផ្ទាត់",
|
||||
"no_availability_in_month": "គ្មានភាពអាចប្រើបានក្នុងខែ {{month}}",
|
||||
"view_previous_month": "មើលខែមុន",
|
||||
"view_next_month": "មើលខែក្រោយ",
|
||||
"send_code": "ផ្ញើកូដ",
|
||||
"number_verified": "លេខបានផ្ទៀងផ្ទាត់",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "ការដំឡើង",
|
||||
"setup_description": "ដំឡើងឧបករណ៍ Cal.com",
|
||||
"configure": "កំណត់រចនា",
|
||||
"sso_configuration": "Single Sign-On",
|
||||
"sso_configuration": "ការកំណត់រចនាសម្ព័ន្ធចូលប្រើប្រាស់តែមួយ",
|
||||
"sso_configuration_description": "កំណត់ SAML/OIDC SSO និងអនុញ្ញាតឱ្យសមាជិកក្រុមចូលប្រើដោយប្រើអ្នកផ្តល់អត្តសញ្ញាណ",
|
||||
"sso_configuration_description_orgs": "កំណត់ SAML/OIDC SSO និងអនុញ្ញាតឱ្យសមាជិកអង្គការចូលប្រើដោយប្រើអ្នកផ្តល់អត្តសញ្ញាណ",
|
||||
"sso_oidc_heading": "SSO ជាមួយ OIDC",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "ប្រសិនបើទំនាក់ទំនងមិនមានក្រោមគណនី សូមបង្កើតអ្នកដឹកនាំថ្មីពីអ្នកចូលរួម",
|
||||
"salesforce_create_new_contact_under_account": "បង្កើតទំនាក់ទំនងថ្មីក្រោមគណនីដោយផ្អែកលើដែនអ៊ីមែលរបស់អ្នកចូលរួម និងទំនាក់ទំនងដែលមានស្រាប់",
|
||||
"mass_assign_attributes": "ផ្ដល់គុណលក្ខណៈជាសាធារណៈ",
|
||||
"reroute_preview_custom_message": "វានាំឱ្យបង្ហាញសារផ្ទាល់ខ្លួន។ សាកល្បងផ្លាស់ប្តូរការឆ្លើយតបដើម្បីបញ្ជូនទៅកាន់ព្រឹត្តិការណ៍",
|
||||
"reroute_preview_external_redirect": "វានាំឱ្យបញ្ជូនបន្តទៅ {{externalUrl}}។ សាកល្បងផ្លាស់ប្តូរការឆ្លើយតបដើម្បីបញ្ជូនទៅកាន់ព្រឹត្តិការណ៍",
|
||||
"reroute_preview_possible_host": "ម្ចាស់ផ្ទះដែលអាចមាន",
|
||||
"current_routing_status": "ស្ថានភាពនៃការបញ្ជូនបច្ចុប្បន្ន",
|
||||
"reroute_booking": "បញ្ជូនការកក់ឡើងវិញ",
|
||||
"reroute_booking_description": "បញ្ជូនការកក់ទៅសមាជិកក្រុមផ្សេងទៀត",
|
||||
"verify_new_route": "ផ្ទៀងផ្ទាត់ផ្លូវថ្មី",
|
||||
"reroute": "បញ្ជូនឡើងវិញ",
|
||||
"new_routing_status": "ស្ថានភាពនៃការបញ្ជូនថ្មី",
|
||||
"salesforce_book_directly_with_attendee_owner": "ប្រសិនបើអ្នកចូលរួមមាននៅក្នុង Salesforce សូមកក់ដោយផ្ទាល់ជាមួយម្ចាស់",
|
||||
"salesforce_check_owner_of": "អង្គភាពត្រូវពិនិត្យម្ចាស់ដើម្បីកក់ដោយផ្ទាល់",
|
||||
"account": "គណនី",
|
||||
"choose_hosts_schedule": "ជ្រើសរើសកាលវិភាគរបស់ម្ចាស់ផ្ទះ",
|
||||
"hosts_schedule_description": "អ្នកអាចជ្រើសរើសកាលវិភាគផ្សេងៗសម្រាប់ម្ចាស់ផ្ទះនីមួយៗ។ កាលវិភាគលំនាំដើមរបស់ម្ចាស់ផ្ទះនីមួយៗត្រូវបានជ្រើសរើសជាលំនាំដើម។",
|
||||
"no_hosts_description": "មិនមានម្ចាស់ផ្ទះបានចាត់តាំងទេ សូមបន្ថែមម្ចាស់ផ្ទះនៅក្នុងផ្ទាំងចាត់តាំង។",
|
||||
"reschedule_with_same_timeslot": "កំណត់ពេលវេលាឡើងវិញជាមួយនឹងពេលវេលាដដែល",
|
||||
"reschedule_with_same_timeslot_of_new_event": "កំណត់ពេលវេលាឡើងវិញជាមួយនឹងពេលវេលាដដែលនៃព្រឹត្តិការណ៍ថ្មី",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "កំណត់ពេលវេលាឡើងវិញទៅព្រឹត្តិការណ៍ថ្មីជាមួយនឹងពេលវេលាខុសគ្នា",
|
||||
"reschedule_with_different_timeslot": "កំណត់ពេលវេលាឡើងវិញជាមួយនឹងពេលវេលាខុសគ្នា",
|
||||
"we_dont_have_id_for_the_new_event": "យើងមិនមានលេខសម្គាល់សម្រាប់ព្រឹត្តិការណ៍ថ្មីទេ។ សូមទៅកាន់ទម្រង់បញ្ជូន និងរក្សាទុកម្តងទៀត។",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "បានបញ្ជូនការកក់ដោយជោគជ័យ។ កំពុងបញ្ជូនបន្តទៅទំព័រកក់",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ បន្ថែមខ្សែអក្សរថ្មីរបស់អ្នកនៅខាងលើនេះ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "참석자가 동적 그룹 예약을 통해 귀하를 예약하도록 허용",
|
||||
"dynamic_booking": "동적 그룹 링크",
|
||||
"allow_seo_indexing": "검색 엔진이 공개 콘텐츠에 액세스하도록 허용",
|
||||
"seo_indexing": "SEO 인덱싱 허용",
|
||||
"seo_indexing": "검색 엔진 색인 허용",
|
||||
"allow_browser_notifications": "브라우저 알림 허용",
|
||||
"you_have_denied_notifications": "알림이 거부되었습니다. 브라우저 설정에서 권한을 재설정하여 활성화하세요.",
|
||||
"disable_browser_notifications": "브라우저 알림 비활성화",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "팀원 예약 버튼 숨기기",
|
||||
"hide_book_a_team_member_description": "공개 페이지에서 팀원 예약 버튼을 숨깁니다.",
|
||||
"danger_zone": "위험 구역",
|
||||
"account_deletion_cannot_be_undone": "주의하세요. 계정 삭제 후에는 되돌릴 수 없습니다.",
|
||||
"team_deletion_cannot_be_undone": "주의하세요. 팀 삭제는 되돌릴 수 없습니다.",
|
||||
"account_deletion_cannot_be_undone": "주의하세요. 계정 삭제는 취소할 수 없습니다.",
|
||||
"team_deletion_cannot_be_undone": "주의하세요. 팀 삭제는 취소할 수 없습니다.",
|
||||
"back": "뒤로 가기",
|
||||
"cancel": "취소",
|
||||
"cancel_all_remaining": "나머지 모두 취소",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "예약 가능 횟수",
|
||||
"event_advanced_tab_description": "캘린더 설정 및 기타...",
|
||||
"event_advanced_tab_title": "고급",
|
||||
"event_payments_tab_title": "결제",
|
||||
"event_payments_tab_description": "이벤트 결제 설정하기",
|
||||
"event_setup_multiple_duration_error": "이벤트 설정: 복수 기간에는 최소 1개의 옵션이 필요합니다.",
|
||||
"event_setup_multiple_duration_default_error": "이벤트 설정: 유효한 기본 기간을 선택하십시오.",
|
||||
"event_setup_booking_limits_error": "예약 한도는 오름차순이어야 합니다. [일, 주, 월, 년]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "양식에 대한 답변을 바탕으로 올바른 당사자에게 전달",
|
||||
"form_description": "예약자를 라우팅할 양식 생성",
|
||||
"copy_link_to_form": "양식에 링크 복사",
|
||||
"theme": "테마",
|
||||
"theme": "예약 페이지 테마",
|
||||
"theme_applies_note": "공개 예약 페이지에만 적용됩니다",
|
||||
"app_theme": "대시보드 테마",
|
||||
"app_theme_applies_note": "이것은 로그인한 대시보드에만 적용됩니다",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "설치된 캘린더 없음",
|
||||
"no_calendar_installed_description": "아직 연결된 캘린더가 없습니다",
|
||||
"add_a_calendar": "캘린더 추가",
|
||||
"change_email_hint": "변경 사항을 적용하려면 로그아웃했다가 다시 로그인해야 할 수 있습니다",
|
||||
"change_email_hint": "변경 사항을 적용하려면 로그아웃 후 다시 로그인해야 할 수 있습니다.",
|
||||
"confirm_password_change_email": "이메일 주소 변경 전 비밀번호를 확인하세요",
|
||||
"seats": "시트",
|
||||
"every_app_published": "{{appName}} App Store에 게시된 모든 앱은 오픈 소스이며 피어 리뷰를 통해 철저한 테스트를 거쳤습니다. 그럼에도 불구하고 {{companyName}}는 {{appName}}에서 게시하지 않는 한 이 앱을 보증 또는 인증하지 않습니다. 부적절한 콘텐츠나 활동을 발견하면 신고해 주시기 바랍니다.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "잘못된 인증 코드",
|
||||
"not_verified": "아직 인증되지 않음",
|
||||
"no_availability_in_month": "{{month}}에 이용 불가",
|
||||
"view_previous_month": "이전 달 보기",
|
||||
"view_next_month": "다음 달 보기",
|
||||
"send_code": "코드 전송",
|
||||
"number_verified": "번호 인증됨",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "연락처가 계정에 존재하지 않으면, 참석자로부터 새로운 리드를 생성합니다",
|
||||
"salesforce_create_new_contact_under_account": "참석자의 이메일 도메인과 기존 연락처를 기반으로 계정 내에 새로운 연락처를 생성합니다",
|
||||
"mass_assign_attributes": "속성 일괄 할당",
|
||||
"reroute_preview_custom_message": "사용자 지정 메시지를 표시하는 결과가 나타납니다. 응답을 변경하여 이벤트로 라우팅해 보세요",
|
||||
"reroute_preview_external_redirect": "{{externalUrl}}로 리디렉션되는 결과가 나타납니다. 응답을 변경하여 이벤트로 라우팅해 보세요",
|
||||
"reroute_preview_possible_host": "가능한 호스트",
|
||||
"current_routing_status": "현재 라우팅 상태",
|
||||
"reroute_booking": "예약 재라우팅",
|
||||
"reroute_booking_description": "예약을 다른 팀원에게 재라우팅합니다",
|
||||
"verify_new_route": "새 경로 확인",
|
||||
"reroute": "재라우팅",
|
||||
"new_routing_status": "새 라우팅 상태",
|
||||
"salesforce_book_directly_with_attendee_owner": "참석자가 Salesforce에 존재하면, 소유자와 직접 예약합니다",
|
||||
"salesforce_check_owner_of": "직접 예약하기 위해 소유자를 확인할 엔터티",
|
||||
"account": "계정",
|
||||
"choose_hosts_schedule": "호스트 일정 선택",
|
||||
"hosts_schedule_description": "각 호스트에 대해 다른 일정을 선택할 수 있습니다. 각 호스트의 기본 일정이 기본적으로 선택됩니다.",
|
||||
"no_hosts_description": "할당 탭에서 호스트를 추가하십시오. 현재 할당된 호스트가 없습니다.",
|
||||
"reschedule_with_same_timeslot": "동일한 시간대로 일정 변경",
|
||||
"reschedule_with_same_timeslot_of_new_event": "새 이벤트의 동일한 시간대로 일정 변경",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "다른 시간대의 새 이벤트로 일정 변경",
|
||||
"reschedule_with_different_timeslot": "다른 시간대로 일정 변경",
|
||||
"we_dont_have_id_for_the_new_event": "새 이벤트의 ID가 없습니다. 라우팅 양식으로 이동하여 다시 저장해 주세요.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "예약이 성공적으로 재라우팅되었습니다. 예약 페이지로 리디렉션 중입니다",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 여기에 새 문자열을 추가하세요 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Sta deelnemers toe om u te boeken via dynamische groepsboekingen",
|
||||
"dynamic_booking": "Dynamische groepslinks",
|
||||
"allow_seo_indexing": "Geef zoekmachines toegang tot uw openbare content",
|
||||
"seo_indexing": "Sta SEO-indexering toe",
|
||||
"seo_indexing": "Indexering door zoekmachines toestaan",
|
||||
"allow_browser_notifications": "Browsermeldingen toestaan",
|
||||
"you_have_denied_notifications": "Je hebt meldingen geweigerd. Stel de toestemming opnieuw in via de browserinstellingen om deze in te schakelen.",
|
||||
"disable_browser_notifications": "Browsermeldingen uitschakelen",
|
||||
@@ -674,7 +674,7 @@
|
||||
"hide_book_a_team_member_description": "Verberg de knop 'Teamlid boeken' op uw openbare pagina's.",
|
||||
"danger_zone": "Gevarenzone",
|
||||
"account_deletion_cannot_be_undone": "Wees voorzichtig. Het verwijderen van een account kan niet ongedaan worden gemaakt.",
|
||||
"team_deletion_cannot_be_undone": "Wees voorzichtig. Het verwijderen van het team kan niet ongedaan worden gemaakt.",
|
||||
"team_deletion_cannot_be_undone": "Wees voorzichtig. Het verwijderen van een team kan niet ongedaan worden gemaakt.",
|
||||
"back": "Terug",
|
||||
"cancel": "Annuleren",
|
||||
"cancel_all_remaining": "Alle resterende annuleren",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Hoe vaak u geboekt kunt worden",
|
||||
"event_advanced_tab_description": "Agenda-instellingen en meer...",
|
||||
"event_advanced_tab_title": "Geavanceerd",
|
||||
"event_payments_tab_title": "Betalingen",
|
||||
"event_payments_tab_description": "Betalingen instellen voor evenement",
|
||||
"event_setup_multiple_duration_error": "Gebeurtenisconfiguratie: voor meerdere duren is minimaal 1 optie vereist.",
|
||||
"event_setup_multiple_duration_default_error": "Gebeurtenisconfiguratie: selecteer een geldige standaardduur.",
|
||||
"event_setup_booking_limits_error": "Reserveringslimieten moeten in oplopende volgorde zijn. [dag, week, maand, jaar]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "Route naar de juiste persoon op basis van de antwoorden op uw formulier",
|
||||
"form_description": "Maak uw formulier om naar een boeker door te verwijzen",
|
||||
"copy_link_to_form": "Link kopiëren naar formulier",
|
||||
"theme": "Thema",
|
||||
"theme": "Thema van de boekingspagina",
|
||||
"theme_applies_note": "Dit is alleen van toepassing op uw openbare boekingspagina's",
|
||||
"app_theme": "Dashboard-thema",
|
||||
"app_theme_applies_note": "Dit is alleen van toepassing op uw aangemelde dashboard",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Geen agenda geïnstalleerd",
|
||||
"no_calendar_installed_description": "U heeft nog geen agenda's gekoppeld",
|
||||
"add_a_calendar": "Voeg een agenda toe",
|
||||
"change_email_hint": "U moet zich mogelijk afmelden en weer aanmelden voordat de wijzigingen van kracht worden",
|
||||
"change_email_hint": "Mogelijk moet u uit- en weer inloggen om de wijziging te zien.",
|
||||
"confirm_password_change_email": "Bevestig uw wachtwoord voordat u uw e-mailadres wijzigt",
|
||||
"seats": "plaatsen",
|
||||
"every_app_published": "Elke app die in de App Store van {{appName}} wordt gepubliceerd is open source en grondig getest via collegiale beoordelingen. Desondanks onderschrijft of certificeert {{companyName}} deze apps niet, tenzij ze zijn gepubliceerd door {{appName}}. Meld het als u ongepaste inhoud of gedrag tegenkomt.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Onjuiste verificatiecode",
|
||||
"not_verified": "Nog niet geverifieerd",
|
||||
"no_availability_in_month": "Geen beschikbaarheid in {{month}}",
|
||||
"view_previous_month": "Vorige maand bekijken",
|
||||
"view_next_month": "Volgende maand weergeven",
|
||||
"send_code": "Code verzenden",
|
||||
"number_verified": "Nummer geverifieerd",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Configuratie",
|
||||
"setup_description": "Cal.com-exemplaar configureren",
|
||||
"configure": "Configureren",
|
||||
"sso_configuration": "Eenmalige aanmelding",
|
||||
"sso_configuration": "Single sign-on",
|
||||
"sso_configuration_description": "SAML/OIDC SSO configureren en teamleden laten inloggen met behulp van een identiteitsprovider",
|
||||
"sso_configuration_description_orgs": "Configureer SAML/OIDC SSO en laat organisatieleden inloggen met een Identity Provider",
|
||||
"sso_oidc_heading": "SSO met OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Populairste gebeurtenissen",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Dit is uw overzicht van populaire evenementen voor uw team {{teamName}} van de afgelopen 30 dagen",
|
||||
"me": "Ik",
|
||||
"monthly_digest_email": "Maandelijks overzichts-e-mail",
|
||||
"monthly_digest_email": "Maandelijkse overzichtsmail",
|
||||
"monthly_digest_email_for_teams": "Maandelijks overzichts-e-mail voor teams",
|
||||
"verify_team_tooltip": "Verifieer uw team om het versturen van berichten naar deelnemers in te schakelen",
|
||||
"member_removed": "Lid verwijderd",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Als de contactpersoon niet onder een account bestaat, maak een nieuwe lead van de deelnemer",
|
||||
"salesforce_create_new_contact_under_account": "Maak een nieuw contact onder een account op basis van het e-maildomein van de deelnemer en bestaande contacten",
|
||||
"mass_assign_attributes": "Attribuuttoewijzing in bulk",
|
||||
"reroute_preview_custom_message": "Dit resulteert in het tonen van een aangepast bericht. Probeer het antwoord te wijzigen om naar een evenement te routeren",
|
||||
"reroute_preview_external_redirect": "Dit resulteert in een doorverwijzing naar {{externalUrl}}. Probeer het antwoord te wijzigen om naar een evenement te routeren",
|
||||
"reroute_preview_possible_host": "Mogelijke host",
|
||||
"current_routing_status": "Huidige routeringsstatus",
|
||||
"reroute_booking": "Boeking omleiden",
|
||||
"reroute_booking_description": "Leid de boeking om naar verschillende teamleden",
|
||||
"verify_new_route": "Nieuwe route verifiëren",
|
||||
"reroute": "Omleiden",
|
||||
"new_routing_status": "Nieuwe routeringsstatus",
|
||||
"salesforce_book_directly_with_attendee_owner": "Als de deelnemer in Salesforce bestaat, boek direct met de eigenaar",
|
||||
"salesforce_check_owner_of": "Entiteit om eigenaar van te controleren voor directe boeking",
|
||||
"account": "Account",
|
||||
"choose_hosts_schedule": "Kies hostschema's",
|
||||
"hosts_schedule_description": "U kunt een ander schema selecteren voor elke host. Standaard is het standaardschema van elke host geselecteerd.",
|
||||
"no_hosts_description": "Geen hosts toegewezen, voeg hosts toe in het toewijzingstabblad.",
|
||||
"reschedule_with_same_timeslot": "Opnieuw plannen met hetzelfde tijdslot",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Opnieuw plannen met hetzelfde tijdslot van het nieuwe evenement",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Opnieuw plannen naar het nieuwe evenement met een ander tijdslot",
|
||||
"reschedule_with_different_timeslot": "Opnieuw plannen met een ander tijdslot",
|
||||
"we_dont_have_id_for_the_new_event": "We hebben geen ID voor het nieuwe evenement. Ga naar het Routeringsformulier en sla het opnieuw op.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Boeking succesvol omgeleid. U wordt doorgestuurd naar de boekingspagina",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Voeg uw nieuwe strings hierboven toe ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Tillat deltakere å booke deg gjennom dynamiske gruppebookinger",
|
||||
"dynamic_booking": "Dynamiske gruppelenker",
|
||||
"allow_seo_indexing": "Tillat søkemotorer å få tilgang til ditt offentlige innhold",
|
||||
"seo_indexing": "Tillat SEO-indeksering",
|
||||
"seo_indexing": "Tillat indeksering av søkemotorer",
|
||||
"allow_browser_notifications": "Tillat nettleservarsler",
|
||||
"you_have_denied_notifications": "Du har avslått varsler. Tilbakestill tillatelse i nettleserinnstillingene for å aktivere.",
|
||||
"disable_browser_notifications": "Deaktiver nettleservarsler",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Hvor ofte du kan bookes",
|
||||
"event_advanced_tab_description": "Kalender-innstillinger og mer...",
|
||||
"event_advanced_tab_title": "Avansert",
|
||||
"event_payments_tab_title": "Betalinger",
|
||||
"event_payments_tab_description": "Sett opp betalinger for arrangement",
|
||||
"event_setup_multiple_duration_error": "Hendelsesoppsett: Flere varigheter krever minst 1 alternativ.",
|
||||
"event_setup_multiple_duration_default_error": "Hendelsesoppsett: Velg en gyldig standard-varighet.",
|
||||
"event_setup_booking_limits_error": "Booking-grensene må være i stigende rekkefølge. [dag,uke,måned,år]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Rute til riktig person basert på svarene i skjemaet ditt",
|
||||
"form_description": "Lag skjemaet ditt for å videresende noen som booker",
|
||||
"copy_link_to_form": "Kopier lenken til skjemaet",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema for bookingside",
|
||||
"theme_applies_note": "Dette gjelder kun for dine offentlige bookingsider",
|
||||
"app_theme": "Dashboard-tema",
|
||||
"app_theme": "Dashbord-tema",
|
||||
"app_theme_applies_note": "Dette gjelder kun ditt påloggede dashbord",
|
||||
"theme_system": "System standard",
|
||||
"add_a_team": "Legg til et team",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Ingen kalender installert",
|
||||
"no_calendar_installed_description": "Du har ikke koblet til noen av kalenderne dine ennå",
|
||||
"add_a_calendar": "Legg til en kalender",
|
||||
"change_email_hint": "Det kan hende du må logge ut og inn igjen for å se at endringer trer i kraft",
|
||||
"change_email_hint": "Du må kanskje logge ut og inn igjen for å se endringene tre i kraft.",
|
||||
"confirm_password_change_email": "Vennligst bekreft passordet ditt før du endrer e-postadressen din",
|
||||
"seats": "seter",
|
||||
"every_app_published": "Hver app publisert på {{appName}} App Butikken er åpen kildekode og grundig testet via fagfellevurderinger. Uansett, {{companyName}} godkjenner eller sertifiserer likevel ikke disse appene med mindre de er publisert av {{appName}}. Hvis du møter upassende innhold eller oppførsel, vennligst rapporter det.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Feil verifiseringskode",
|
||||
"not_verified": "Ikke verifisert ennå",
|
||||
"no_availability_in_month": "Ingen tilgjengelighet i {{month}}",
|
||||
"view_previous_month": "Vis forrige måned",
|
||||
"view_next_month": "Vis neste måned",
|
||||
"send_code": "Send kode",
|
||||
"number_verified": "Nummer Bekreftet",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Oppsett",
|
||||
"setup_description": "Sett opp Cal.com-instans",
|
||||
"configure": "Konfigurer",
|
||||
"sso_configuration": "Enkel Pålogging",
|
||||
"sso_configuration": "Single sign-on",
|
||||
"sso_configuration_description": "Konfigurer SAML/OIDC SSO og la teammedlemmer logge inn ved hjelp av en identitetsleverandør",
|
||||
"sso_configuration_description_orgs": "Konfigurer SAML/OIDC SSO og la organisasjonsmedlemmer logge inn ved hjelp av en identitetsleverandør",
|
||||
"sso_oidc_heading": "SSO med OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Mest populære arrangementer",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Her er din oppsummering av populære arrangementer for teamet ditt {{teamName}} de siste 30 dagene",
|
||||
"me": "Meg",
|
||||
"monthly_digest_email": "Månedlig oppsummerings-e-post",
|
||||
"monthly_digest_email": "Månedlig sammendrag på e-post",
|
||||
"monthly_digest_email_for_teams": "Månedlig oppsummerings-e-post for team",
|
||||
"verify_team_tooltip": "Verifiser teamet ditt for å aktivere sending av meldinger til deltakere",
|
||||
"member_removed": "Medlem fjernet",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Hvis kontakten ikke finnes under en konto, opprett ny lead fra deltaker",
|
||||
"salesforce_create_new_contact_under_account": "Opprett en ny kontakt under en konto basert på e-postdomenet til deltaker og eksisterende kontakter",
|
||||
"mass_assign_attributes": "Masseopptildel attributter",
|
||||
"reroute_preview_custom_message": "Det resulterer i å vise en tilpasset melding. Prøv å endre svaret for å rute til en hendelse",
|
||||
"reroute_preview_external_redirect": "Det resulterer i omdirigering til {{externalUrl}}. Prøv å endre svaret for å rute til en hendelse",
|
||||
"reroute_preview_possible_host": "Mulig vert",
|
||||
"current_routing_status": "Nåværende rutingsstatus",
|
||||
"reroute_booking": "Omdirigér booking",
|
||||
"reroute_booking_description": "Omdirigér bookingen til forskjellige teammedlemmer",
|
||||
"verify_new_route": "Bekreft ny rute",
|
||||
"reroute": "Omdirigér",
|
||||
"new_routing_status": "Ny rutingsstatus",
|
||||
"salesforce_book_directly_with_attendee_owner": "Hvis deltaker finnes i Salesforce, book direkte med eieren",
|
||||
"salesforce_check_owner_of": "Enhet for å sjekke eier av for å booke direkte",
|
||||
"account": "Konto",
|
||||
"choose_hosts_schedule": "Velg vertsplaner",
|
||||
"hosts_schedule_description": "Du kan velge en annen plan for hver vert. Hver verts standardplan er valgt som standard.",
|
||||
"no_hosts_description": "Ingen verter tildelt, legg til verter i tildelingsfanen.",
|
||||
"reschedule_with_same_timeslot": "Endre tidspunkt med samme tidsrom",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Endre tidspunkt med samme tidsrom for den nye hendelsen",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Endre tidspunkt til den nye hendelsen med et annet tidsrom",
|
||||
"reschedule_with_different_timeslot": "Endre tidspunkt med et annet tidsrom",
|
||||
"we_dont_have_id_for_the_new_event": "Vi har ikke ID for den nye hendelsen. Gå til rutingsskjemaet og lagre det på nytt.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Bookingen ble omdirigert. Omdirigerer til bookingsiden",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Legg til dine nye strenger over her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Pozwól uczestnikom rezerwować cię przez dynamiczne rezerwacje grupowe",
|
||||
"dynamic_booking": "Dynamiczne linki grup",
|
||||
"allow_seo_indexing": "Zezwól wyszukiwarkom na dostęp do Twoich publicznych treści",
|
||||
"seo_indexing": "Zezwalaj na indeksowanie SEO",
|
||||
"seo_indexing": "Zezwól na indeksowanie przez wyszukiwarki",
|
||||
"allow_browser_notifications": "Zezwól na powiadomienia przeglądarki",
|
||||
"you_have_denied_notifications": "Odrzuciłeś powiadomienia. Zresetuj uprawnienia w ustawieniach przeglądarki, aby je włączyć.",
|
||||
"disable_browser_notifications": "Wyłącz powiadomienia przeglądarki",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Ukryj przycisk Zarezerwuj członka zespołu",
|
||||
"hide_book_a_team_member_description": "Ukryj przycisk Zarezerwuj członka zespołu na swoich stronach publicznych.",
|
||||
"danger_zone": "Strefa zagrożenia",
|
||||
"account_deletion_cannot_be_undone": "Uwaga. Usunięcie konta jest nieodwracalne.",
|
||||
"team_deletion_cannot_be_undone": "Uwaga. Usunięcie zespołu jest nieodwracalne.",
|
||||
"account_deletion_cannot_be_undone": "Uwaga. Usunięcia konta nie można cofnąć.",
|
||||
"team_deletion_cannot_be_undone": "Uwaga. Usunięcia zespołu nie można cofnąć.",
|
||||
"back": "Wróć",
|
||||
"cancel": "Anuluj",
|
||||
"cancel_all_remaining": "Anuluj wszystkie pozostałe",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Jak często można rezerwować Twoje usługi",
|
||||
"event_advanced_tab_description": "Ustawienia kalendarza i więcej...",
|
||||
"event_advanced_tab_title": "Zaawansowane",
|
||||
"event_payments_tab_title": "Płatności",
|
||||
"event_payments_tab_description": "Skonfiguruj płatności za wydarzenie",
|
||||
"event_setup_multiple_duration_error": "Konfiguracja wydarzeń: wiele czasów trwania wymaga co najmniej 1 opcji.",
|
||||
"event_setup_multiple_duration_default_error": "Konfiguracja wydarzenia: wybierz prawidłowy domyślny czas trwania.",
|
||||
"event_setup_booking_limits_error": "Limity rezerwacji muszą być podane w kolejności rosnącej. [dzień, tydzień, miesiąc, rok]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Przekierowuj do właściwej osoby na podstawie odpowiedzi na Twój formularz",
|
||||
"form_description": "Utwórz formularz, aby przekierowywać osoby rezerwujące",
|
||||
"copy_link_to_form": "Skopiuj link do formularza",
|
||||
"theme": "Motyw",
|
||||
"theme": "Motyw strony rezerwacji",
|
||||
"theme_applies_note": "Dotyczy tylko publicznych stron rezerwacji",
|
||||
"app_theme": "Motyw pulpitu nawigacyjnego",
|
||||
"app_theme": "Motyw panelu kontrolnego",
|
||||
"app_theme_applies_note": "To dotyczy tylko pulpitu nawigacyjnego, do którego się zalogowałeś",
|
||||
"theme_system": "Ustawienia domyślne systemu",
|
||||
"add_a_team": "Dodaj zespół",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Brak zainstalowanego kalendarza",
|
||||
"no_calendar_installed_description": "Nie połączono jeszcze żadnego kalendarza.",
|
||||
"add_a_calendar": "Dodaj kalendarz",
|
||||
"change_email_hint": "Aby zmiany weszły w życie, konieczne może być wylogowanie się i ponowne zalogowanie",
|
||||
"change_email_hint": "Może być konieczne wylogowanie się i ponowne zalogowanie, aby zobaczyć efekt zmiany.",
|
||||
"confirm_password_change_email": "Potwierdź hasło przed zmianą adresu e-mail",
|
||||
"seats": "miejsca",
|
||||
"every_app_published": "Każda aplikacja opublikowana w sklepie App Store serwisu {{appName}} jest otwartym oprogramowaniem i jest dokładnie testowana przez innych użytkowników. Niemniej jednak, {{companyName}} nie odpowiada za aplikacje ani nie certyfikuje ich, chyba że są one publikowane przez {{appName}}. Jeśli natrafisz na nieodpowiednie treści lub zachowanie, zgłoś je.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Nieprawidłowy kod weryfikacyjny",
|
||||
"not_verified": "Jeszcze nie zweryfikowano",
|
||||
"no_availability_in_month": "Brak dostępności w danym miesiącu ({{month}})",
|
||||
"view_previous_month": "Zobacz poprzedni miesiąc",
|
||||
"view_next_month": "Wyświetl następny miesiąc",
|
||||
"send_code": "Wyślij kod",
|
||||
"number_verified": "Numer zweryfikowany",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Konfiguracja",
|
||||
"setup_description": "Skonfiguruj wystąpienie Cal.com",
|
||||
"configure": "Konfiguruj",
|
||||
"sso_configuration": "Pojedyncze logowanie",
|
||||
"sso_configuration": "Konfiguracja pojedynczego logowania",
|
||||
"sso_configuration_description": "Skonfiguruj logowanie jednokrotne SAML/OIDC i zezwól członkom zespołu na logowanie się przy użyciu dostawcy tożsamości",
|
||||
"sso_configuration_description_orgs": "Skonfiguruj SAML/OIDC SSO i pozwól członkom organizacji logować się za pomocą dostawcy tożsamości",
|
||||
"sso_oidc_heading": "Logowanie jednokrotne za pomocą OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Najpopularniejsze wydarzenia",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Oto podsumowanie popularnych wydarzeń dla Twojego zespołu {{teamName}} z ostatnich 30 dni",
|
||||
"me": "Ja",
|
||||
"monthly_digest_email": "E-mail z miesięcznym podsumowaniem",
|
||||
"monthly_digest_email": "Miesięczny e-mail z podsumowaniem",
|
||||
"monthly_digest_email_for_teams": "E-mail dla zespołów z miesięcznym podsumowaniem",
|
||||
"verify_team_tooltip": "Zweryfikuj zespół, aby włączyć wysyłanie wiadomości do uczestników",
|
||||
"member_removed": "Usunięto członka",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Jeśli kontakt nie istnieje w ramach konta, utwórz nowy lead z uczestnika",
|
||||
"salesforce_create_new_contact_under_account": "Utwórz nowy kontakt w ramach konta na podstawie domeny e-mail uczestnika i istniejących kontaktów",
|
||||
"mass_assign_attributes": "Masowe przypisywanie atrybutów",
|
||||
"reroute_preview_custom_message": "Skutkuje to wyświetleniem niestandardowej wiadomości. Spróbuj zmienić odpowiedź, aby przekierować do wydarzenia",
|
||||
"reroute_preview_external_redirect": "Skutkuje to przekierowaniem do {{externalUrl}}. Spróbuj zmienić odpowiedź, aby przekierować do wydarzenia",
|
||||
"reroute_preview_possible_host": "Możliwy gospodarz",
|
||||
"current_routing_status": "Aktualny status przekierowania",
|
||||
"reroute_booking": "Zmień trasę rezerwacji",
|
||||
"reroute_booking_description": "Przekieruj rezerwację do innych członków zespołu",
|
||||
"verify_new_route": "Zweryfikuj nową trasę",
|
||||
"reroute": "Zmień trasę",
|
||||
"new_routing_status": "Nowy status przekierowania",
|
||||
"salesforce_book_directly_with_attendee_owner": "Jeśli uczestnik istnieje w Salesforce, zarezerwuj bezpośrednio z właścicielem",
|
||||
"salesforce_check_owner_of": "Podmiot do sprawdzenia właściciela w celu bezpośredniej rezerwacji",
|
||||
"account": "Konto",
|
||||
"choose_hosts_schedule": "Wybierz harmonogramy gospodarzy",
|
||||
"hosts_schedule_description": "Możesz wybrać inny harmonogram dla każdego gospodarza. Domyślny harmonogram każdego gospodarza jest wybierany automatycznie.",
|
||||
"no_hosts_description": "Brak przypisanych gospodarzy, dodaj gospodarzy w zakładce przypisania.",
|
||||
"reschedule_with_same_timeslot": "Zmień termin na ten sam przedział czasowy",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Zmień termin na ten sam przedział czasowy nowego wydarzenia",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Zmień termin na nowe wydarzenie z innym przedziałem czasowym",
|
||||
"reschedule_with_different_timeslot": "Zmień termin na inny przedział czasowy",
|
||||
"we_dont_have_id_for_the_new_event": "Nie mamy ID dla nowego wydarzenia. Przejdź do Formularza Przekierowań i zapisz go ponownie.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Pomyślnie zmieniono trasę rezerwacji. Przekierowywanie do strony rezerwacji",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodaj nowe ciągi powyżej ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Permita que os participantes façam reservas com as reservas de grupo dinâmicas",
|
||||
"dynamic_booking": "Links de grupo dinâmicos",
|
||||
"allow_seo_indexing": "Permitir que mecanismos de busca acessem seu conteúdo público",
|
||||
"seo_indexing": "Pemitir indexação em SEO",
|
||||
"seo_indexing": "Permitir indexação por motores de busca",
|
||||
"allow_browser_notifications": "Permitir notificações do navegador",
|
||||
"you_have_denied_notifications": "Você negou as notificações. Redefina a permissão nas configurações do navegador para ativar.",
|
||||
"disable_browser_notifications": "Desativar notificações do navegador",
|
||||
@@ -674,7 +674,7 @@
|
||||
"hide_book_a_team_member_description": "Oculte o botão de agendar um membro da equipe em suas páginas públicas.",
|
||||
"danger_zone": "Zona de perigo",
|
||||
"account_deletion_cannot_be_undone": "Cuidado. A exclusão da conta não pode ser desfeita.",
|
||||
"team_deletion_cannot_be_undone": "Cuidado. A exclusão de uma equipe não pode ser desfeita",
|
||||
"team_deletion_cannot_be_undone": "Cuidado. A exclusão da equipe não pode ser desfeita.",
|
||||
"back": "Voltar",
|
||||
"cancel": "Cancelar",
|
||||
"cancel_all_remaining": "Cancelar todos os restantes",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Com que frequência você pode receber reserva",
|
||||
"event_advanced_tab_description": "Configurações de calendário e muito mais...",
|
||||
"event_advanced_tab_title": "Avançado",
|
||||
"event_payments_tab_title": "Pagamentos",
|
||||
"event_payments_tab_description": "Configurar pagamentos para o evento",
|
||||
"event_setup_multiple_duration_error": "Configuração do evento: múltiplas durações requerem pelo menos uma opção.",
|
||||
"event_setup_multiple_duration_default_error": "Configuração de evento: selecione uma duração padrão válida.",
|
||||
"event_setup_booking_limits_error": "Limites de reserva devem estar em ordem crescente. [dia, semana, mês, ano,]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "Roteie para a pessoa certa com base nas respostas do seu formulário",
|
||||
"form_description": "Crie o seu formulário para encaminhar um reservante",
|
||||
"copy_link_to_form": "Copiar link para formulário",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema da página de agendamento",
|
||||
"theme_applies_note": "Isto só se aplica às suas páginas de reservas públicas",
|
||||
"app_theme": "Tema do painel",
|
||||
"app_theme_applies_note": "Isso se aplica apenas ao seu painel conectado",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Nenhum calendário instalado",
|
||||
"no_calendar_installed_description": "Você ainda não conectou nenhum de seus calendários",
|
||||
"add_a_calendar": "Adicionar um calendário",
|
||||
"change_email_hint": "Você pode precisar sair e entrar novamente para as alterações serem aplicadas",
|
||||
"change_email_hint": "Pode ser necessário sair e entrar novamente para ver as alterações aplicadas.",
|
||||
"confirm_password_change_email": "Confirme sua senha antes de alterar o endereço de e-mail",
|
||||
"seats": "assentos",
|
||||
"every_app_published": "Todos os aplicativos publicados na App Store da {{appName}} são de código aberto e testados minuciosamente por meio de avaliações por pares. No entanto, a {{companyName}} não endossa ou certifica esses aplicativos a não ser que sejam publicados pela {{appName}}. Se você encontrar conteúdo ou comportamento inadequado, denuncie.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Código de verificação incorreto",
|
||||
"not_verified": "Verificação ainda não realizada",
|
||||
"no_availability_in_month": "Sem disponibilidade em {{month}}",
|
||||
"view_previous_month": "Ver mês anterior",
|
||||
"view_next_month": "Ver próximo mês",
|
||||
"send_code": "Enviar código",
|
||||
"number_verified": "Número verificado",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Configuração",
|
||||
"setup_description": "Instância de configuração do Cal.com",
|
||||
"configure": "Configurar",
|
||||
"sso_configuration": "Logon único",
|
||||
"sso_configuration": "Configuração de login único",
|
||||
"sso_configuration_description": "Configure o SAML/OIDC SSO e permita que membros da equipe façam login com um provedor de identidade",
|
||||
"sso_configuration_description_orgs": "Configure o SAML/OIDC SSO e permita que membros da organização façam login usando um provedor de identidade",
|
||||
"sso_oidc_heading": "SSO com OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Eventos Mais Populares",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Aqui está seu resumo dos eventos populares da sua equipe {{teamName}} nos últimos 30 dias",
|
||||
"me": "Eu",
|
||||
"monthly_digest_email": "E-mail de Resumo Mensal",
|
||||
"monthly_digest_email": "E-mail de resumo mensal",
|
||||
"monthly_digest_email_for_teams": "E-mail de resumo mensal para as equipes",
|
||||
"verify_team_tooltip": "Verifique seu time para habilitar o envio de mensagens aos participantes",
|
||||
"member_removed": "Membro removido",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Se o contato não existir em uma conta, criar novo lead a partir do participante",
|
||||
"salesforce_create_new_contact_under_account": "Criar um novo contato em uma conta com base no domínio de e-mail do participante e contatos existentes",
|
||||
"mass_assign_attributes": "Atribuir atributos em massa",
|
||||
"reroute_preview_custom_message": "Isso resulta na exibição de uma mensagem personalizada. Tente alterar a resposta para redirecionar a um evento",
|
||||
"reroute_preview_external_redirect": "Isso resulta no redirecionamento para {{externalUrl}}. Tente alterar a resposta para redirecionar a um evento",
|
||||
"reroute_preview_possible_host": "Possível anfitrião",
|
||||
"current_routing_status": "Status atual de roteamento",
|
||||
"reroute_booking": "Redirecionar agendamento",
|
||||
"reroute_booking_description": "Redirecionar o agendamento para diferentes membros da equipe",
|
||||
"verify_new_route": "Verificar nova rota",
|
||||
"reroute": "Redirecionar",
|
||||
"new_routing_status": "Novo status de roteamento",
|
||||
"salesforce_book_directly_with_attendee_owner": "Se o participante existir no Salesforce, agendar diretamente com o proprietário",
|
||||
"salesforce_check_owner_of": "Entidade para verificar o proprietário para agendar diretamente",
|
||||
"account": "Conta",
|
||||
"choose_hosts_schedule": "Escolher horários dos anfitriões",
|
||||
"hosts_schedule_description": "Você pode selecionar um horário diferente para cada anfitrião. O horário padrão de cada anfitrião é escolhido por padrão.",
|
||||
"no_hosts_description": "Nenhum anfitrião atribuído, adicione anfitriões na aba de atribuição.",
|
||||
"reschedule_with_same_timeslot": "Reagendar com o mesmo horário",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Reagendar com o mesmo horário do novo evento",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Reagendar para o novo evento com horário diferente",
|
||||
"reschedule_with_different_timeslot": "Reagendar com horário diferente",
|
||||
"we_dont_have_id_for_the_new_event": "Não temos o ID para o novo evento. Por favor, vá para o Formulário de Roteamento e salve-o novamente.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Agendamento redirecionado com sucesso. Redirecionando para a página de agendamento",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adicione suas novas strings aqui em cima ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Permitir que os participantes façam reservas consigo através de reservas dinâmicas de grupos",
|
||||
"dynamic_booking": "Ligações dinâmicas de grupos",
|
||||
"allow_seo_indexing": "Permitir que os motores de pesquisa acedam ao seu conteúdo público",
|
||||
"seo_indexing": "Permitir indexação para SEO",
|
||||
"seo_indexing": "Permitir indexação por motores de busca",
|
||||
"allow_browser_notifications": "Permitir notificações do navegador",
|
||||
"you_have_denied_notifications": "Negou as notificações. Redefina a permissão nas configurações do navegador para ativar.",
|
||||
"disable_browser_notifications": "Desativar notificações do navegador",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Ocultar o botão Reservar um Membro da equipa",
|
||||
"hide_book_a_team_member_description": "Ocultar o botão Reservar um Membro da equipa das suas páginas públicas.",
|
||||
"danger_zone": "Zona de perigo",
|
||||
"account_deletion_cannot_be_undone": "Cuidado. A eliminação da conta não pode ser anulada.",
|
||||
"team_deletion_cannot_be_undone": "Cuidado. A eliminação de uma equipa não pode ser anulada",
|
||||
"account_deletion_cannot_be_undone": "Tenha cuidado. A eliminação da conta não pode ser desfeita.",
|
||||
"team_deletion_cannot_be_undone": "Tenha cuidado. A eliminação da equipa não pode ser desfeita.",
|
||||
"back": "Anterior",
|
||||
"cancel": "Cancelar",
|
||||
"cancel_all_remaining": "Cancelar todos os restantes",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Com que frequência pode ser reservado",
|
||||
"event_advanced_tab_description": "Definições do calendário e muito mais...",
|
||||
"event_advanced_tab_title": "Avançado",
|
||||
"event_payments_tab_title": "Pagamentos",
|
||||
"event_payments_tab_description": "Configurar pagamentos para o evento",
|
||||
"event_setup_multiple_duration_error": "Configuração do evento: durações múltiplas requerem pelo menos 1 opção.",
|
||||
"event_setup_multiple_duration_default_error": "Configuração do evento: por favor, selecione uma duração predefinida válida.",
|
||||
"event_setup_booking_limits_error": "Os limites de reserva devem estar ordenados de forma ascendente. [dia,semana,mês,ano]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Encaminhe para a pessoa certa com base nas respostas ao seu formulário",
|
||||
"form_description": "Crie o seu formulário para encaminhar um reservante",
|
||||
"copy_link_to_form": "Copiar ligação para o formulário",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema da página de marcação",
|
||||
"theme_applies_note": "Isto só se aplica às suas páginas de reservas públicas",
|
||||
"app_theme": "Tema do Painel",
|
||||
"app_theme": "Tema do painel de controlo",
|
||||
"app_theme_applies_note": "Isto aplica-se apenas ao seu painel de administração quando tem a sessão iniciada",
|
||||
"theme_system": "Predefinição do sistema",
|
||||
"add_a_team": "Adicionar uma equipa",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Nenhum calendário instalado",
|
||||
"no_calendar_installed_description": "Ainda não associou nenhum dos seus calendários",
|
||||
"add_a_calendar": "Adicionar um calendário",
|
||||
"change_email_hint": "Pode ter de terminar sessão e voltar a iniciar para que a alteração produza efeitos",
|
||||
"change_email_hint": "Poderá ser necessário terminar sessão e voltar a iniciar para ver as alterações aplicadas.",
|
||||
"confirm_password_change_email": "Confirme a sua palavra-passe antes de alterar o endereço de e-mail",
|
||||
"seats": "lugares",
|
||||
"every_app_published": "Todas as aplicações publicadas na loja de aplicações {{appName}} são de código aberto e minuciosamente testadas através da avaliação por pares. No entanto, {{companyName}} não apoia ou certifica estas aplicações a não ser que sejam publicadas pela {{appName}}. Se encontrar conteúdos ou comportamentos inadequados, por favor, reporte a situação.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Código de verificação errado",
|
||||
"not_verified": "Ainda não foi verificado",
|
||||
"no_availability_in_month": "Sem disponibilidade em {{month}}",
|
||||
"view_previous_month": "Ver mês anterior",
|
||||
"view_next_month": "Ver o próximo mês",
|
||||
"send_code": "Enviar código",
|
||||
"number_verified": "Número verificado",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Configuração",
|
||||
"setup_description": "Configuração da instância Cal.com",
|
||||
"configure": "Configurar",
|
||||
"sso_configuration": "Configuração SAML",
|
||||
"sso_configuration": "Autenticação única",
|
||||
"sso_configuration_description": "Configurar o SSO SAML/OIDC e permitir que os membros da equipa se autentiquem utilizando um fornecedor de identidades",
|
||||
"sso_configuration_description_orgs": "Configurar o SSO SAML/OIDC e permitir que os membros da organização se autentiquem utilizando um fornecedor de identidades",
|
||||
"sso_oidc_heading": "SSO com OIDC",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Se o contacto não existir sob uma conta, criar um novo lead a partir do participante",
|
||||
"salesforce_create_new_contact_under_account": "Criar um novo contacto sob uma conta com base no domínio de email do participante e contactos existentes",
|
||||
"mass_assign_attributes": "Atribuir atributos em massa",
|
||||
"reroute_preview_custom_message": "Resulta na exibição de uma mensagem personalizada. Tente alterar a resposta para encaminhar para um evento",
|
||||
"reroute_preview_external_redirect": "Resulta no redirecionamento para {{externalUrl}}. Tente alterar a resposta para encaminhar para um evento",
|
||||
"reroute_preview_possible_host": "Possível anfitrião",
|
||||
"current_routing_status": "Estado atual do encaminhamento",
|
||||
"reroute_booking": "Reencaminhar marcação",
|
||||
"reroute_booking_description": "Reencaminhar a marcação para diferentes membros da equipa",
|
||||
"verify_new_route": "Verificar nova rota",
|
||||
"reroute": "Reencaminhar",
|
||||
"new_routing_status": "Novo estado de encaminhamento",
|
||||
"salesforce_book_directly_with_attendee_owner": "Se o participante existir no Salesforce, agendar diretamente com o proprietário",
|
||||
"salesforce_check_owner_of": "Entidade para verificar o proprietário para agendar diretamente",
|
||||
"account": "Conta",
|
||||
"choose_hosts_schedule": "Escolher horários dos anfitriões",
|
||||
"hosts_schedule_description": "Pode selecionar um horário diferente para cada anfitrião. O horário padrão de cada anfitrião é escolhido por defeito.",
|
||||
"no_hosts_description": "Nenhum anfitrião atribuído, adicione anfitriões na aba de atribuição.",
|
||||
"reschedule_with_same_timeslot": "Reagendar com o mesmo horário",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Reagendar com o mesmo horário do novo evento",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Reagendar para o novo evento com horário diferente",
|
||||
"reschedule_with_different_timeslot": "Reagendar com horário diferente",
|
||||
"we_dont_have_id_for_the_new_event": "Não temos ID para o novo evento. Por favor, vá ao Formulário de Encaminhamento e guarde-o novamente.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Marcação reencaminhada com sucesso. A redirecionar para a página de marcação",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Permiteți participanților să vă efectueze rezervări dinamice în grup",
|
||||
"dynamic_booking": "Linkuri de grup dinamice",
|
||||
"allow_seo_indexing": "Permiteți motoarelor de căutare să acceseze conținutul dvs. public",
|
||||
"seo_indexing": "Permiteți indexarea SEO",
|
||||
"seo_indexing": "Permite indexarea de către motoarele de căutare",
|
||||
"allow_browser_notifications": "Permite notificările browserului",
|
||||
"you_have_denied_notifications": "Ai refuzat notificările. Resetează permisiunea în setările browserului pentru a le activa.",
|
||||
"disable_browser_notifications": "Dezactivează notificările browserului",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Ascunde butonul Rezervare membru echipă",
|
||||
"hide_book_a_team_member_description": "Ascunde butonul de rezervare pentru un membru al echipei din paginile tale publice.",
|
||||
"danger_zone": "Zonă periculoasă",
|
||||
"account_deletion_cannot_be_undone": "Aveți grijă. Ștergerea contului nu poate fi anulată.",
|
||||
"team_deletion_cannot_be_undone": "Fii atent. Ștergerea echipei nu poate fi anulată.",
|
||||
"account_deletion_cannot_be_undone": "Atenție. Ștergerea contului nu poate fi anulată.",
|
||||
"team_deletion_cannot_be_undone": "Atenție. Ștergerea echipei nu poate fi anulată.",
|
||||
"back": "Înapoi",
|
||||
"cancel": "Anulează",
|
||||
"cancel_all_remaining": "Anulați toate pozițiile rămase",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Cât de des puteți fi rezervat",
|
||||
"event_advanced_tab_description": "Setări calendar și mai multe...",
|
||||
"event_advanced_tab_title": "Avansat",
|
||||
"event_payments_tab_title": "Plăți",
|
||||
"event_payments_tab_description": "Configurați plățile pentru eveniment",
|
||||
"event_setup_multiple_duration_error": "Configurare eveniment: duratele multiple necesită cel puțin o opțiune.",
|
||||
"event_setup_multiple_duration_default_error": "Configurare eveniment: selectează o durată implicită validă.",
|
||||
"event_setup_booking_limits_error": "Limitele de rezervare trebuie să fie în ordine crescătoare. [zi,săptămână,lună,an]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "Direcționați către persoana potrivită, pe baza răspunsurilor la formularul dvs.",
|
||||
"form_description": "Creați formularul pentru direcționarea unei persoane care face rezervare",
|
||||
"copy_link_to_form": "Copiați linkul în formular",
|
||||
"theme": "Temă",
|
||||
"theme": "Tema paginii de rezervare",
|
||||
"theme_applies_note": "Acest aspect se aplică numai paginilor dvs. de rezervare publice",
|
||||
"app_theme": "Tema tabloului de bord",
|
||||
"app_theme_applies_note": "Acest lucru se aplică numai tabloului de bord conectat",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Niciun calendar instalat",
|
||||
"no_calendar_installed_description": "Încă nu v-ați conectat niciunul dintre calendare",
|
||||
"add_a_calendar": "Adăugați un calendar",
|
||||
"change_email_hint": "Pentru implementarea modificărilor, ar putea fi necesar să vă deconectați și să vă reconectați",
|
||||
"change_email_hint": "Este posibil să fie nevoie să vă deconectați și să vă reconectați pentru a vedea efectul modificării.",
|
||||
"confirm_password_change_email": "Confirmați-vă parola înainte de a vă schimba adresa de e-mail",
|
||||
"seats": "locuri",
|
||||
"every_app_published": "Fiecare aplicație publicată în magazinul de aplicații {{appName}} este open-source și testată temeinic prin evaluări inter pares. Cu toate acestea, {{companyName}} nu susține sau certifică aceste aplicații decât dacă sunt publicate de {{appName}}. Dacă descoperiți conținut sau comportament necorespunzător, vă încurajăm să îl raportați.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Cod de verificare incorect",
|
||||
"not_verified": "Încă nu a fost verificat",
|
||||
"no_availability_in_month": "Nicio disponibilitate în {{month}}",
|
||||
"view_previous_month": "Vizualizează luna anterioară",
|
||||
"view_next_month": "Vezi luna următoare",
|
||||
"send_code": "Trimite codul",
|
||||
"number_verified": "Număr verificat",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Configurare",
|
||||
"setup_description": "Configurare eveniment Cal.com",
|
||||
"configure": "Configurare",
|
||||
"sso_configuration": "Single Sign-On",
|
||||
"sso_configuration": "Autentificare unică",
|
||||
"sso_configuration_description": "Configurați SSO cu SAML/OIDC și permiteți membrilor echipei să se conecteze folosind un furnizor de identitate",
|
||||
"sso_configuration_description_orgs": "Configurează SAML/OIDC SSO și permite membrilor organizației să se autentifice folosind un furnizor de identitate",
|
||||
"sso_oidc_heading": "SSO cu OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Cele mai populare evenimente",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Iată rezumatul evenimentelor populare pentru echipa dvs. {{teamName}} din ultimele 30 de zile",
|
||||
"me": "Eu",
|
||||
"monthly_digest_email": "E-mail cu rezumatul lunar",
|
||||
"monthly_digest_email": "Email cu rezumat lunar",
|
||||
"monthly_digest_email_for_teams": "E-mail cu rezumatul lunar pentru echipe",
|
||||
"verify_team_tooltip": "Verificați-vă echipa pentru a permite trimiterea de mesaje către participanți",
|
||||
"member_removed": "Membru eliminat",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Dacă contactul nu există sub un cont, creează un lead nou din participant",
|
||||
"salesforce_create_new_contact_under_account": "Creează un contact nou sub un cont pe baza domeniului de email al participantului și a contactelor existente",
|
||||
"mass_assign_attributes": "Atribuire în masă a atributelor",
|
||||
"reroute_preview_custom_message": "Rezultă în afișarea unui mesaj personalizat. Încercați să modificați răspunsul pentru a redirecționa către un eveniment",
|
||||
"reroute_preview_external_redirect": "Rezultă în redirecționarea către {{externalUrl}}. Încercați să modificați răspunsul pentru a redirecționa către un eveniment",
|
||||
"reroute_preview_possible_host": "Gazdă posibilă",
|
||||
"current_routing_status": "Starea actuală de direcționare",
|
||||
"reroute_booking": "Redirecționează rezervarea",
|
||||
"reroute_booking_description": "Redirecționează rezervarea către alți membri ai echipei",
|
||||
"verify_new_route": "Verifică noua rută",
|
||||
"reroute": "Redirecționează",
|
||||
"new_routing_status": "Noua stare de direcționare",
|
||||
"salesforce_book_directly_with_attendee_owner": "Dacă participantul există în Salesforce, rezervă direct cu proprietarul",
|
||||
"salesforce_check_owner_of": "Entitatea pentru a verifica proprietarul pentru a rezerva direct",
|
||||
"account": "Cont",
|
||||
"choose_hosts_schedule": "Alege programele gazdelor",
|
||||
"hosts_schedule_description": "Poți selecta un program diferit pentru fiecare gazdă. Programul implicit al fiecărei gazde este selectat în mod prestabilit.",
|
||||
"no_hosts_description": "Nicio gazdă nu este atribuită, adaugă gazde în fila de atribuire.",
|
||||
"reschedule_with_same_timeslot": "Reprogramează cu același interval orar",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Reprogramează cu același interval orar al noului eveniment",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Reprogramează la noul eveniment cu un interval orar diferit",
|
||||
"reschedule_with_different_timeslot": "Reprogramează cu un interval orar diferit",
|
||||
"we_dont_have_id_for_the_new_event": "Nu avem ID pentru noul eveniment. Vă rugăm să accesați Formularul de Direcționare și să îl salvați din nou.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Rezervarea a fost redirecționată cu succes. Se redirecționează către pagina de rezervare",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adăugați stringurile noi deasupra acestui rând ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Dozvolite učesnicima da rezervišu termin sa vama putem dinamičkih grupnih rezervacija",
|
||||
"dynamic_booking": "Dinamični linkovi grupa",
|
||||
"allow_seo_indexing": "Dozvolite pretraživačima da pristupe vašem javnom sadržaju",
|
||||
"seo_indexing": "Dozvolite SEO indeksiranje",
|
||||
"seo_indexing": "Dozvoli indeksiranje pretraživača",
|
||||
"allow_browser_notifications": "Dozvolite obaveštenja u pregledaču",
|
||||
"you_have_denied_notifications": "Odbili ste obaveštenja. Resetujte dozvolu u podešavanjima pregledača da biste omogućili.",
|
||||
"disable_browser_notifications": "Onemogućite obaveštenja u pregledaču",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Sakrij dugme „Rezerviši člana tima”",
|
||||
"hide_book_a_team_member_description": "Sakrij sa javnih strana dugme „Rezerviši člana tima”.",
|
||||
"danger_zone": "Opasna Zona",
|
||||
"account_deletion_cannot_be_undone": "Budite pažljivi. Brisanje naloga ne može da se opozove.",
|
||||
"team_deletion_cannot_be_undone": "Budite pažljivi. Brisanje tima ne može biti poništeno.",
|
||||
"account_deletion_cannot_be_undone": "Budite oprezni. Brisanje naloga se ne može poništiti.",
|
||||
"team_deletion_cannot_be_undone": "Budite oprezni. Brisanje tima se ne može poništiti.",
|
||||
"back": "Nazad",
|
||||
"cancel": "Otkaži",
|
||||
"cancel_all_remaining": "Otkaži sve preostalo",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Koliko često možete da budete rezervisani",
|
||||
"event_advanced_tab_description": "Podešavanja kalendara i više...",
|
||||
"event_advanced_tab_title": "Napredno",
|
||||
"event_payments_tab_title": "Плаћања",
|
||||
"event_payments_tab_description": "Подесите плаћања за догађај",
|
||||
"event_setup_multiple_duration_error": "Podešavanje događaja: Višestruka trajanja zahteva najmanje 1 opciju.",
|
||||
"event_setup_multiple_duration_default_error": "Podešavanje događaja: Izaberite važeće podrazumevano trajanje.",
|
||||
"event_setup_booking_limits_error": "Ograničenja zakazivanja moraju da budu u rastućem nizu. [dan,nedelja,mesec,godina]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Usmeravanje na pravu osobu na osnovu odgovora iz obrasca",
|
||||
"form_description": "Kreirajte formular da biste rutirali osobu koja vrši rezervaciju",
|
||||
"copy_link_to_form": "Kopiraj vezu u formular",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema stranice za rezervacije",
|
||||
"theme_applies_note": "Ovo se odnosi samo na vaše javne stranice za zakazivanje",
|
||||
"app_theme": "Тема контролне табле",
|
||||
"app_theme": "Tema kontrolne table",
|
||||
"app_theme_applies_note": "TОво се примењује само на вашу пријављену контролну таблу",
|
||||
"theme_system": "Sistemski podrazumevano",
|
||||
"add_a_team": "Dodaj tim",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Nije instaliran kalendar",
|
||||
"no_calendar_installed_description": "Još niste povezali nijedan od svojih kalendara",
|
||||
"add_a_calendar": "Dodajte kalendar",
|
||||
"change_email_hint": "Možda ćete morati da se odjavite i ponovo prijavite da biste videli bilo kakve izvršene promene",
|
||||
"change_email_hint": "Možda ćete morati da se odjavite i ponovo prijavite da biste videli promene.",
|
||||
"confirm_password_change_email": "Potvrdite svoju lozinku pre promene adrese e-pošte",
|
||||
"seats": "mesta",
|
||||
"every_app_published": "Svaka aplikacija objavljena na {{appName}} prodavnici je otvorenog tipa i detaljno je testirana kroz recenzije saradnika. Bez obzira na to, {{companyName}} ne podržava niti sertifikuje ove aplikacije osim ako ih nije objavio {{appName}}. Ako naiđete na neprikladan sardžaj ili ponašanje, molimo vas da to prijavite.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Pogrešan kôd za verifikaciju",
|
||||
"not_verified": "Nije još verifikovano",
|
||||
"no_availability_in_month": "Nije raspoloživo za mesec {{month}}",
|
||||
"view_previous_month": "Прикажи претходни месец",
|
||||
"view_next_month": "Prikaži naredni mesec",
|
||||
"send_code": "Pošalji kod",
|
||||
"number_verified": "Broj je verifikovan",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Podešavanje",
|
||||
"setup_description": "Podešavanje Cal.com instance",
|
||||
"configure": "Konfiguriši",
|
||||
"sso_configuration": "Pojedinačno prijavljivanje",
|
||||
"sso_configuration": "Jedinstvena prijava",
|
||||
"sso_configuration_description": "Konfigurišite SAML/OIDC SSO i omogućite članovima tima da se prijave korišćenjem Identity Provider-a",
|
||||
"sso_configuration_description_orgs": "Konfigurišite SAML/OIDC SSO i omogućite članovima organizacije da se prijave koristeći provajdera identiteta",
|
||||
"sso_oidc_heading": "SSO sa OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Najpopularniji događaji",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Evo preseka popularnih događaja za vaš tim {{teamName}} u poslednjih 30 dana",
|
||||
"me": "Ja",
|
||||
"monthly_digest_email": "Imejl sa mesečnih presekom",
|
||||
"monthly_digest_email": "Mesečni e-mail sa pregledom",
|
||||
"monthly_digest_email_for_teams": "Imejl sa mesečnim presekom za timove",
|
||||
"verify_team_tooltip": "Verifikujte svoj tim da biste omogućili slanje poruka učesnicima",
|
||||
"member_removed": "Član je uklonjen",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Ako kontakt ne postoji pod nalogom, kreiraj novog potencijalnog klijenta od učesnika",
|
||||
"salesforce_create_new_contact_under_account": "Kreiraj novi kontakt pod nalogom na osnovu email domena učesnika i postojećih kontakata",
|
||||
"mass_assign_attributes": "Masovno dodeljivanje atributa",
|
||||
"reroute_preview_custom_message": "Rezultat je prikazivanje prilagođene poruke. Pokušajte da promenite odgovor da biste preusmerili na događaj",
|
||||
"reroute_preview_external_redirect": "Rezultat je preusmeravanje na {{externalUrl}}. Pokušajte da promenite odgovor da biste preusmerili na događaj",
|
||||
"reroute_preview_possible_host": "Mogući domaćin",
|
||||
"current_routing_status": "Trenutni status rutiranja",
|
||||
"reroute_booking": "Preusmeravanje rezervacije",
|
||||
"reroute_booking_description": "Preusmerite rezervaciju na različite članove tima",
|
||||
"verify_new_route": "Proverite novu rutu",
|
||||
"reroute": "Preusmeriti",
|
||||
"new_routing_status": "Novi status rutiranja",
|
||||
"salesforce_book_directly_with_attendee_owner": "Ako učesnik postoji u Salesforce-u, zakaži direktno sa vlasnikom",
|
||||
"salesforce_check_owner_of": "Entitet čijeg vlasnika treba proveriti za direktno zakazivanje",
|
||||
"account": "Nalog",
|
||||
"choose_hosts_schedule": "Изаберите распореде домаћина",
|
||||
"hosts_schedule_description": "Можете изабрати различит распоред за сваког домаћина. Подразумевано је изабран подразумевани распоред сваког домаћина.",
|
||||
"no_hosts_description": "Нема додељених домаћина, додајте домаћине у картици за доделу.",
|
||||
"reschedule_with_same_timeslot": "Promenite termin sa istim vremenskim slotom",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Promenite termin sa istim vremenskim slotom novog događaja",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Promenite termin na novi događaj sa različitim vremenskim slotom",
|
||||
"reschedule_with_different_timeslot": "Promenite termin sa različitim vremenskim slotom",
|
||||
"we_dont_have_id_for_the_new_event": "Nemamo ID za novi događaj. Molimo vas da odete na Obrazac za rutiranje i ponovo ga sačuvate.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Rezervacija je uspešno preusmerena. Preusmeravanje na stranicu za rezervaciju",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodajte svoje nove stringove iznad ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Tillåt deltagare att boka dig via dynamiska gruppbokningar",
|
||||
"dynamic_booking": "Dynamiska grupplänkar",
|
||||
"allow_seo_indexing": "Tillåt sökmotorer att komma åt ditt publika innehåll",
|
||||
"seo_indexing": "Tillåt SEO-indexering",
|
||||
"seo_indexing": "Tillåt indexering av sökmotorer",
|
||||
"allow_browser_notifications": "Tillåt webbläsaraviseringar",
|
||||
"you_have_denied_notifications": "Du har nekat aviseringar. Återställ behörigheten i webbläsarinställningarna för att aktivera.",
|
||||
"disable_browser_notifications": "Inaktivera webbläsaraviseringar",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Dölj knappen Boka en teammedlem",
|
||||
"hide_book_a_team_member_description": "Dölj knappen Boka en teammedlem från dina offentliga sidor.",
|
||||
"danger_zone": "Farozon",
|
||||
"account_deletion_cannot_be_undone": "Var försiktig. Borttagning av konto kan inte ångras.",
|
||||
"team_deletion_cannot_be_undone": "Var försiktig. Teamradering kan inte ångras.",
|
||||
"account_deletion_cannot_be_undone": "Var försiktig. Kontoborttagning kan inte ångras.",
|
||||
"team_deletion_cannot_be_undone": "Var försiktig. Teamborttagning kan inte ångras.",
|
||||
"back": "Tillbaka",
|
||||
"cancel": "Avbryt",
|
||||
"cancel_all_remaining": "Avbryt alla återstående",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Hur ofta du kan bokas",
|
||||
"event_advanced_tab_description": "Kalenderinställningar och mer...",
|
||||
"event_advanced_tab_title": "Avancerat",
|
||||
"event_payments_tab_title": "Betalningar",
|
||||
"event_payments_tab_description": "Ställ in betalningar för evenemang",
|
||||
"event_setup_multiple_duration_error": "Konfiguration av händelse: Flera varaktigheter kräver minst 1 alternativ.",
|
||||
"event_setup_multiple_duration_default_error": "Konfiguration av händelse: Välj en giltig standardvaraktighet.",
|
||||
"event_setup_booking_limits_error": "Bokningsgränserna ska vara i stigande ordning. [dag,vecka,månad,år]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Skicka vidare till rätt person baserat på svaren i formuläret",
|
||||
"form_description": "Skapa ditt formulär för att dirigera en bokare",
|
||||
"copy_link_to_form": "Kopiera länk till formulär",
|
||||
"theme": "Tema",
|
||||
"theme": "Tema för bokningssida",
|
||||
"theme_applies_note": "Detta gäller endast för dina offentliga bokningssidor",
|
||||
"app_theme": "Dashboard-tema",
|
||||
"app_theme": "Tema för instrumentpanel",
|
||||
"app_theme_applies_note": "Detta gäller endast din inloggade instrumentpanel",
|
||||
"theme_system": "Systemstandard",
|
||||
"add_a_team": "Lägg till ett team",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Ingen kalender installerad",
|
||||
"no_calendar_installed_description": "Du har ännu inte anslutit någon av dina kalendrar",
|
||||
"add_a_calendar": "Lägg till en kalender",
|
||||
"change_email_hint": "Du kanske måste logga ut och in igen för att se förändringen träda i kraft",
|
||||
"change_email_hint": "Du kan behöva logga ut och in igen för att se ändringen träda i kraft.",
|
||||
"confirm_password_change_email": "Bekräfta ditt lösenord innan du ändrar din e-postadress",
|
||||
"seats": "platser",
|
||||
"every_app_published": "Alla appar som publiceras i {{appName}} App Store har öppen källkod och är noggrant testade genom referentgranskningar. {{companyName}} stöder eller certifierar dock inte dessa appar om de inte publiceras av {{appName}}. Om du stöter på olämpligt innehåll eller beteende ber vi dig att rapportera det.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Fel verifieringskod",
|
||||
"not_verified": "Har inte verifierats än",
|
||||
"no_availability_in_month": "Ingen tillgänglighet i {{month}}",
|
||||
"view_previous_month": "Visa föregående månad",
|
||||
"view_next_month": "Visa nästa månad",
|
||||
"send_code": "Skicka kod",
|
||||
"number_verified": "Numret har verifierats",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Konfiguration",
|
||||
"setup_description": "Installera Cal.com-instans",
|
||||
"configure": "Konfigurera",
|
||||
"sso_configuration": "Enkel inloggning",
|
||||
"sso_configuration": "Single sign-on",
|
||||
"sso_configuration_description": "Konfigurera SAML/OIDC SSO och tillåt teammedlemmar att logga in med en identitetsprovider",
|
||||
"sso_configuration_description_orgs": "Konfigurera SAML/OIDC SSO och tillåt organisationsmedlemmar att logga in med en identitetsleverantör",
|
||||
"sso_oidc_heading": "SSO med OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Mest populära händelser",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Här är din sammanfattning av populära händelser för ditt team {{teamName}} de senaste 30 dagarna",
|
||||
"me": "Jag",
|
||||
"monthly_digest_email": "E-post med månadssammandrag",
|
||||
"monthly_digest_email": "Månatligt sammanfattningsmail",
|
||||
"monthly_digest_email_for_teams": "E-post med månadssammandrag för teams",
|
||||
"verify_team_tooltip": "Verifiera ditt team för att aktivera att skicka meddelanden till deltagare",
|
||||
"member_removed": "Medlem borttagen",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Om kontakten inte finns under ett konto, skapa en ny lead från deltagaren",
|
||||
"salesforce_create_new_contact_under_account": "Skapa en ny kontakt under ett konto baserat på deltagarens e-postdomän och befintliga kontakter",
|
||||
"mass_assign_attributes": "Massuppdatera attribut",
|
||||
"reroute_preview_custom_message": "Det resulterar i att visa ett anpassat meddelande. Försök ändra svaret för att dirigera till en händelse",
|
||||
"reroute_preview_external_redirect": "Det resulterar i omdirigering till {{externalUrl}}. Försök ändra svaret för att dirigera till en händelse",
|
||||
"reroute_preview_possible_host": "Möjlig värd",
|
||||
"current_routing_status": "Nuvarande dirigeringsstatus",
|
||||
"reroute_booking": "Omdirigera bokning",
|
||||
"reroute_booking_description": "Omdirigera bokningen till olika teammedlemmar",
|
||||
"verify_new_route": "Verifiera ny rutt",
|
||||
"reroute": "Omdirigera",
|
||||
"new_routing_status": "Ny dirigeringsstatus",
|
||||
"salesforce_book_directly_with_attendee_owner": "Om deltagaren finns i Salesforce, boka direkt med ägaren",
|
||||
"salesforce_check_owner_of": "Entitet att kontrollera ägare av för att boka direkt",
|
||||
"account": "Konto",
|
||||
"choose_hosts_schedule": "Välj värdarnas scheman",
|
||||
"hosts_schedule_description": "Du kan välja ett annat schema för varje värd. Varje värds standardschema är valt som standard.",
|
||||
"no_hosts_description": "Inga värdar tilldelade, lägg till värdar i tilldelningsfliken.",
|
||||
"reschedule_with_same_timeslot": "Omboka med samma tidslucka",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Omboka med samma tidslucka för den nya händelsen",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Omboka till den nya händelsen med annan tidslucka",
|
||||
"reschedule_with_different_timeslot": "Omboka med annan tidslucka",
|
||||
"we_dont_have_id_for_the_new_event": "Vi har inget ID för den nya händelsen. Gå till Dirigeringsformuläret och spara det igen.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Omdirigering av bokning lyckades. Omdirigerar till bokningssidan",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Katılımcıların dinamik grup rezervasyonları aracılığıyla sizin adınıza rezervasyon yapmalarına izin verin",
|
||||
"dynamic_booking": "Dinamik grup bağlantıları",
|
||||
"allow_seo_indexing": "Arama motorlarının genel içeriğinize erişmesine izin verin",
|
||||
"seo_indexing": "SEO Dizine Eklemeye İzin Verin",
|
||||
"seo_indexing": "Arama motoru indekslemesine izin ver",
|
||||
"allow_browser_notifications": "Tarayıcı Bildirimlerine İzin Ver",
|
||||
"you_have_denied_notifications": "Bildirimlere izin vermediniz. Etkinleştirmek için tarayıcı ayarlarından izinleri sıfırlayın.",
|
||||
"disable_browser_notifications": "Tarayıcı Bildirimlerini Devre Dışı Bırak",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "Ekip Üyesi İçin Rezervasyon Yap Düğmesini Gizle",
|
||||
"hide_book_a_team_member_description": "Ekip Üyesi İçin Rezervasyon Yap Düğmesini herkese açık sayfalarınızda gizleyin.",
|
||||
"danger_zone": "Tehlikeli bölge",
|
||||
"account_deletion_cannot_be_undone": "Dikkat. Hesap silme işlemi geri alınamaz.",
|
||||
"team_deletion_cannot_be_undone": "Dikkatli olun. Takım silme işlemi geri alınamaz.",
|
||||
"account_deletion_cannot_be_undone": "Dikkatli olun. Hesap silme işlemi geri alınamaz.",
|
||||
"team_deletion_cannot_be_undone": "Dikkatli olun. Ekip silme işlemi geri alınamaz.",
|
||||
"back": "Geri",
|
||||
"cancel": "İptal et",
|
||||
"cancel_all_remaining": "Kalanların tümünü iptal et",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Ne sıklıkla rezervasyon yapabilirsiniz",
|
||||
"event_advanced_tab_description": "Takvim ayarları ve daha fazlası...",
|
||||
"event_advanced_tab_title": "Gelişmiş",
|
||||
"event_payments_tab_title": "Ödemeler",
|
||||
"event_payments_tab_description": "Etkinlik için ödemeleri ayarla",
|
||||
"event_setup_multiple_duration_error": "Etkinlik Kurulumu: Birden çok süre için en az 1 seçenek gereklidir.",
|
||||
"event_setup_multiple_duration_default_error": "Etkinlik Kurulumu: Lütfen geçerli bir varsayılan süre seçin.",
|
||||
"event_setup_booking_limits_error": "Rezervasyon limitleri artan sırada olmalıdır. [gün, hafta, ay, yıl]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Formunuza verilen yanıtlara göre doğru kişiye yönlendirin",
|
||||
"form_description": "Rezervasyon yapan kişiyi yönlendirmek için formunuzu oluşturun",
|
||||
"copy_link_to_form": "Bağlantıyı forma kopyala",
|
||||
"theme": "Tema",
|
||||
"theme": "Rezervasyon sayfası teması",
|
||||
"theme_applies_note": "Bu, sadece genel rezervasyon sayfalarınız için geçerlidir",
|
||||
"app_theme": "Gösterge Paneli tema",
|
||||
"app_theme": "Kontrol paneli teması",
|
||||
"app_theme_applies_note": "Bu yalnızca oturum açmış kontrol paneliniz için geçerlidir",
|
||||
"theme_system": "Sistem varsayılanı",
|
||||
"add_a_team": "Ekip ekle",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Yüklü takvim yok",
|
||||
"no_calendar_installed_description": "Henüz hiçbir takviminizi bağlamadınız",
|
||||
"add_a_calendar": "Takvim ekle",
|
||||
"change_email_hint": "Değişikliklerin geçerli olduğunu görmek için çıkış yapıp tekrar giriş yapmanız gerekebilir",
|
||||
"change_email_hint": "Değişikliğin etkisini görmek için oturumu kapatıp yeniden açmanız gerekebilir.",
|
||||
"confirm_password_change_email": "Lütfen e-posta adresinizi değiştirmeden önce şifrenizi onaylayın",
|
||||
"seats": "yer",
|
||||
"every_app_published": "{{appName}} Uygulama Mağazası'nda yayınlanan her uygulama açık kaynaklıdır ve uzman incelemeleriyle kapsamlı bir şekilde test edilmiştir. {{companyName}}, {{appName}} tarafından yayınlanmadıkça bu uygulamaları desteklemez veya onaylamaz. Uygunsuz içerik veya davranışla karşılaşırsanız lütfen bildirin.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Yanlış doğrulama kodu",
|
||||
"not_verified": "Henüz doğrulanmadı",
|
||||
"no_availability_in_month": "{{month}} ayında müsaitlik yok",
|
||||
"view_previous_month": "Önceki ayı görüntüle",
|
||||
"view_next_month": "Sonraki ayı görüntüle",
|
||||
"send_code": "Kod gönder",
|
||||
"number_verified": "Numara Doğrulandı",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Kurulum",
|
||||
"setup_description": "Cal.com örneğini kurun",
|
||||
"configure": "Yapılandır",
|
||||
"sso_configuration": "Çoklu Oturum Açma",
|
||||
"sso_configuration": "Tek oturum açma",
|
||||
"sso_configuration_description": "SAML/OIDC SSO'yu yapılandırın ve ekip üyelerinin bir Kimlik Sağlayıcı kullanarak oturum açmasına izin verin",
|
||||
"sso_configuration_description_orgs": "SAML/OIDC SSO'yu yapılandırın ve organizasyon üyelerinin bir Kimlik Sağlayıcı kullanarak giriş yapmalarına izin verin",
|
||||
"sso_oidc_heading": "OIDC ile SSO",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "En Popüler Etkinlikler",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "{{teamName}} ekibinize ait son 30 gündeki popüler etkinliklerin özetini burada bulabilirsiniz",
|
||||
"me": "Ben",
|
||||
"monthly_digest_email": "Aylık Özet E-postası",
|
||||
"monthly_digest_email": "Aylık özet e-postası",
|
||||
"monthly_digest_email_for_teams": "Ekipler için aylık özet e-postası",
|
||||
"verify_team_tooltip": "Katılımcılara mesaj göndermeyi etkinleştirmek için ekibinizi doğrulayın",
|
||||
"member_removed": "Üye kaldırıldı",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "İletişim bir hesap altında yoksa, katılımcıdan yeni potansiyel müşteri oluştur",
|
||||
"salesforce_create_new_contact_under_account": "Katılımcının e-posta alanı ve mevcut iletişimler temelinde bir hesap altında yeni bir iletişim oluştur",
|
||||
"mass_assign_attributes": "Özellikleri toplu olarak atama",
|
||||
"reroute_preview_custom_message": "Özel mesaj göstermeyle sonuçlanır. Yanıtı bir etkinliğe yönlendirmek için değiştirmeyi deneyin",
|
||||
"reroute_preview_external_redirect": "{{externalUrl}} adresine yönlendirmeyle sonuçlanır. Yanıtı bir etkinliğe yönlendirmek için değiştirmeyi deneyin",
|
||||
"reroute_preview_possible_host": "Olası ev sahibi",
|
||||
"current_routing_status": "Mevcut yönlendirme durumu",
|
||||
"reroute_booking": "Rezervasyonu yeniden yönlendir",
|
||||
"reroute_booking_description": "Rezervasyonu farklı ekip üyelerine yeniden yönlendir",
|
||||
"verify_new_route": "Yeni rotayı doğrula",
|
||||
"reroute": "Yeniden yönlendir",
|
||||
"new_routing_status": "Yeni yönlendirme durumu",
|
||||
"salesforce_book_directly_with_attendee_owner": "Katılımcı Salesforce'ta mevcutsa, doğrudan sahibiyle rezervasyon yap",
|
||||
"salesforce_check_owner_of": "Doğrudan rezervasyon yapmak için sahibini kontrol edilecek varlık",
|
||||
"account": "Hesap",
|
||||
"choose_hosts_schedule": "Ev sahibi programlarını seçin",
|
||||
"hosts_schedule_description": "Her ev sahibi için farklı bir program seçebilirsiniz. Her ev sahibinin varsayılan programı varsayılan olarak seçilir.",
|
||||
"no_hosts_description": "Atanmış ev sahibi yok, atama sekmesinde ev sahipleri ekleyin.",
|
||||
"reschedule_with_same_timeslot": "Aynı zaman dilimiyle yeniden planla",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Yeni etkinliğin aynı zaman dilimiyle yeniden planla",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Yeni etkinliği farklı zaman dilimiyle yeniden planla",
|
||||
"reschedule_with_different_timeslot": "Farklı zaman dilimiyle yeniden planla",
|
||||
"we_dont_have_id_for_the_new_event": "Yeni etkinlik için bir kimliğimiz yok. Lütfen Yönlendirme Formuna gidin ve tekrar kaydedin.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Rezervasyon başarıyla yeniden yönlendirildi. Rezervasyon sayfasına yönlendiriliyor",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni dizelerinizi yukarıya ekleyin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Дозволити учасникам бронювати ваш час за допомогою динамічних групових бронювань",
|
||||
"dynamic_booking": "Динамічні групові посилання",
|
||||
"allow_seo_indexing": "Надати пошуковим системам доступ до вашого загальнодоступного вмісту",
|
||||
"seo_indexing": "Дозволити SEO індексування",
|
||||
"seo_indexing": "Дозволити індексацію пошуковими системами",
|
||||
"allow_browser_notifications": "Дозволити сповіщення браузера",
|
||||
"you_have_denied_notifications": "Ви відмовилися від сповіщень. Скиньте дозвіл у налаштуваннях браузера, щоб увімкнути.",
|
||||
"disable_browser_notifications": "Вимкнути сповіщення браузера",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Як часто ваш час можуть бронювати",
|
||||
"event_advanced_tab_description": "Налаштування календаря та інше…",
|
||||
"event_advanced_tab_title": "Додатково",
|
||||
"event_payments_tab_title": "Платежі",
|
||||
"event_payments_tab_description": "Налаштувати платежі для події",
|
||||
"event_setup_multiple_duration_error": "Налаштування заходу: якщо мають бути доступні різні варіанти, потрібно вказати принаймні один.",
|
||||
"event_setup_multiple_duration_default_error": "Налаштування заходу: виберіть припустиму тривалість за замовчуванням.",
|
||||
"event_setup_booking_limits_error": "Ліміти бронювань має бути впорядковано за зростанням. [день, тиждень, місяць, рік]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Переспрямовуйте клієнтів відповідній особі з урахуванням відповідей на запитання форми",
|
||||
"form_description": "Створіть власну форму для переспрямування автора бронювання",
|
||||
"copy_link_to_form": "Копіювати посилання на форму",
|
||||
"theme": "Тема",
|
||||
"theme": "Тема сторінки бронювання",
|
||||
"theme_applies_note": "Застосовується тільки до ваших загальнодоступних сторінок бронювання",
|
||||
"app_theme": "Тема інформаційної панелі",
|
||||
"app_theme": "Тема панелі керування",
|
||||
"app_theme_applies_note": "Це стосується лише вашої інформаційної панелі, увійшовши в систему",
|
||||
"theme_system": "Стандартне системне значення",
|
||||
"add_a_team": "Додати команду",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Немає встановлених календарів",
|
||||
"no_calendar_installed_description": "Ви ще не підключили свої календарі",
|
||||
"add_a_calendar": "Додати календар",
|
||||
"change_email_hint": "Щоб зміни почали діяти, можливо, знадобиться вийти із системи та знову ввійти",
|
||||
"change_email_hint": "Можливо, вам доведеться вийти з системи та увійти знову, щоб побачити зміни.",
|
||||
"confirm_password_change_email": "Перш ніж змінити адресу електронної пошти, підтвердьте свій пароль",
|
||||
"seats": "місця",
|
||||
"every_app_published": "Усі додатки, опубліковані в магазині {{appName}}, мають відкритий код і ретельно перевіряються учасниками спільноти. Утім {{companyName}} рекомендує та сертифікує лише додатки, опубліковані компанією {{appName}}. Якщо ви натрапили на неприйнятний вміст або поведінку, надішліть нам скаргу.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Неправильний код перевірки",
|
||||
"not_verified": "Ще не перевірено",
|
||||
"no_availability_in_month": "Недоступний(-а) у такому місяці: {{month}}",
|
||||
"view_previous_month": "Переглянути попередній місяць",
|
||||
"view_next_month": "Показати наступний місяць",
|
||||
"send_code": "Надіслати код",
|
||||
"number_verified": "Номер перевірено",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Налаштування",
|
||||
"setup_description": "Налаштуйте екземпляр Cal.com",
|
||||
"configure": "Налаштувати",
|
||||
"sso_configuration": "Єдиний вхід",
|
||||
"sso_configuration": "Єдиний вхід (SSO)",
|
||||
"sso_configuration_description": "Налаштуйте єдиний вхід через SAML/OIDC і дайте змогу учасникам команди входити за допомогою постачальника посвідчень",
|
||||
"sso_configuration_description_orgs": "Налаштуйте SAML/OIDC SSO і дозвольте членам організації входити за допомогою постачальника ідентифікації",
|
||||
"sso_oidc_heading": "Єдиний вхід через OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Найпопулярніші заходи",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Ось зведена інформація про популярні заходи для вашої команди «{{teamName}}» за останні 30 днів",
|
||||
"me": "Я",
|
||||
"monthly_digest_email": "Ел. лист із місячним дайджестом",
|
||||
"monthly_digest_email": "Щомісячний дайджест електронною поштою",
|
||||
"monthly_digest_email_for_teams": "Ел. лист із місячним дайджестом для команд",
|
||||
"verify_team_tooltip": "Верифікуйте свою команду, щоб увімкнути надсилання повідомлень відвідувачам",
|
||||
"member_removed": "Учасника вилучено",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Якщо контакт не існує під обліковим записом, створити новий лід з учасника",
|
||||
"salesforce_create_new_contact_under_account": "Створити новий контакт під обліковим записом на основі домену електронної пошти учасника та існуючих контактів",
|
||||
"mass_assign_attributes": "Масове призначення атрибутів",
|
||||
"reroute_preview_custom_message": "Це призводить до показу користувацького повідомлення. Спробуйте змінити відповідь, щоб перенаправити на подію",
|
||||
"reroute_preview_external_redirect": "Це призводить до перенаправлення на {{externalUrl}}. Спробуйте змінити відповідь, щоб перенаправити на подію",
|
||||
"reroute_preview_possible_host": "Можливий організатор",
|
||||
"current_routing_status": "Поточний статус маршрутизації",
|
||||
"reroute_booking": "Перенаправити бронювання",
|
||||
"reroute_booking_description": "Перенаправити бронювання іншим членам команди",
|
||||
"verify_new_route": "Перевірити новий маршрут",
|
||||
"reroute": "Перенаправити",
|
||||
"new_routing_status": "Новий статус маршрутизації",
|
||||
"salesforce_book_directly_with_attendee_owner": "Якщо учасник існує в Salesforce, бронювати безпосередньо з власником",
|
||||
"salesforce_check_owner_of": "Сутність для перевірки власника для прямого бронювання",
|
||||
"account": "Обліковий запис",
|
||||
"choose_hosts_schedule": "Виберіть розклад для ведучих",
|
||||
"hosts_schedule_description": "Ви можете вибрати інший розклад для кожного ведучого. За замовчуванням обрано стандартний розклад кожного ведучого.",
|
||||
"no_hosts_description": "Ведучих не призначено, додайте ведучих на вкладці призначення.",
|
||||
"reschedule_with_same_timeslot": "Перепланувати на той самий часовий проміжок",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Перепланувати на той самий часовий проміжок нової події",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Перепланувати на нову подію з іншим часовим проміжком",
|
||||
"reschedule_with_different_timeslot": "Перепланувати на інший часовий проміжок",
|
||||
"we_dont_have_id_for_the_new_event": "У нас немає ID для нової події. Будь ласка, перейдіть до форми маршрутизації та збережіть її знову.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Бронювання успішно перенаправлено. Перенаправлення на сторінку бронювання",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "Cho phép người tham gia đặt chỗ cho bạn thông qua chức năng đặt chỗ động cho nhóm",
|
||||
"dynamic_booking": "Liên kết động cho nhóm",
|
||||
"allow_seo_indexing": "Cho phép các công cụ tìm kiếm truy cập nội dung công cộng của bạn",
|
||||
"seo_indexing": "Cho phép lập chỉ mục SEO",
|
||||
"seo_indexing": "Cho phép công cụ tìm kiếm lập chỉ mục",
|
||||
"allow_browser_notifications": "Cho phép thông báo trình duyệt",
|
||||
"you_have_denied_notifications": "Bạn đã từ chối thông báo. Đặt lại quyền trong cài đặt trình duyệt để bật.",
|
||||
"disable_browser_notifications": "Tắt thông báo trình duyệt",
|
||||
@@ -673,7 +673,7 @@
|
||||
"hide_book_a_team_member": "Giấu nút Đặt lịch hẹn thành viên nhóm",
|
||||
"hide_book_a_team_member_description": "Giấu nút Đặt lịch hẹn thành viên nhóm trên trang công khai của bạn.",
|
||||
"danger_zone": "Khu vực nguy hiểm",
|
||||
"account_deletion_cannot_be_undone": "Cẩn thận. Tài khoản đã xóa không thể phục hồi.",
|
||||
"account_deletion_cannot_be_undone": "Hãy cẩn thận. Việc xóa tài khoản không thể hoàn tác.",
|
||||
"team_deletion_cannot_be_undone": "Hãy cẩn thận. Việc xóa nhóm không thể hoàn tác.",
|
||||
"back": "Trở lại",
|
||||
"cancel": "Hủy bỏ",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "Bạn có thể được đặt lịch bao lần",
|
||||
"event_advanced_tab_description": "Cài đặt cho lịch & nhiều tính năng khác...",
|
||||
"event_advanced_tab_title": "Nâng cao",
|
||||
"event_payments_tab_title": "Thanh toán",
|
||||
"event_payments_tab_description": "Thiết lập thanh toán cho sự kiện",
|
||||
"event_setup_multiple_duration_error": "Thiết lập sự kiện: Nhiều khoảng thời gian cần ít nhất 1 tuỳ chọn.",
|
||||
"event_setup_multiple_duration_default_error": "Thiết lập sự kiện: Vui lòng chọn một khoảng thời gian mặc định hợp lệ.",
|
||||
"event_setup_booking_limits_error": "Giới hạn lịch hẹn phải theo thứ tự tăng dần. [ngày, tuần, tháng, năm]",
|
||||
@@ -1566,9 +1568,9 @@
|
||||
"route_to_the_right_person": "Định hướng đến người phù hợp dựa trên những câu trả lời cho biểu mẫu của bạn",
|
||||
"form_description": "Tạo biểu mẫu của bạn để định hướng người đặt lịch hẹn",
|
||||
"copy_link_to_form": "Sao chép liên kết vào biểu mẫu",
|
||||
"theme": "Chủ đề",
|
||||
"theme": "Giao diện trang đặt lịch",
|
||||
"theme_applies_note": "Cái này chỉ áp dụng cho trang lịch hẹn công khai của bạn",
|
||||
"app_theme": "Chủ đề trang tổng quan",
|
||||
"app_theme": "Giao diện bảng điều khiển",
|
||||
"app_theme_applies_note": "Điều này chỉ áp dụng cho bảng điều khiển đã đăng nhập của bạn",
|
||||
"theme_system": "Mặc định của hệ thống",
|
||||
"add_a_team": "Thêm một nhóm",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "Chưa cài đặt lịch nào",
|
||||
"no_calendar_installed_description": "Bạn chưa kết nối lịch nào của bạn",
|
||||
"add_a_calendar": "Thêm vào một lịch",
|
||||
"change_email_hint": "Bạn có thể cần đăng xuất và đăng nhập lại để thấy được sự thay đổi có hiệu lực",
|
||||
"change_email_hint": "Bạn có thể cần đăng xuất và đăng nhập lại để thấy thay đổi có hiệu lực.",
|
||||
"confirm_password_change_email": "Vui lòng xác nhận mật khẩu của bạn trước khi đổi địa chỉ email",
|
||||
"seats": "ghế",
|
||||
"every_app_published": "Mọi ứng dụng được công bố trên {{appName}} App Store là dạng mã nguồn mở và được kiểm nghiệm kĩ càng thông qua khâu bình duyệt ngang hàng. Dẫu vậy, {{companyName}} không công nhận hay chứng nhận những ứng dụng nếu chúng không do {{appName}} công bố. Nếu bạn gặp nội dụng hay hành vi không đúng mực nào, vui lòng báo cáo vụ việc.",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "Mã xác minh sai",
|
||||
"not_verified": "Chưa xác minh",
|
||||
"no_availability_in_month": "Không còn giờ sẵn sàng làm việc trong {{month}}",
|
||||
"view_previous_month": "Xem tháng trước",
|
||||
"view_next_month": "Xem tháng kế tiếp",
|
||||
"send_code": "Gửi mã",
|
||||
"number_verified": "Số đã được xác minh",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "Thiết lập",
|
||||
"setup_description": "Thiết lập thực thể Cal.com",
|
||||
"configure": "Cấu hình",
|
||||
"sso_configuration": "Cấu hình Đăng nhập một lần",
|
||||
"sso_configuration": "Đăng nhập một lần",
|
||||
"sso_configuration_description": "Cấu hình SAML/OIDC SSO và cho phép thành viên nhóm đăng nhập bằng cách dùng một Nhà cung cấp danh tính",
|
||||
"sso_configuration_description_orgs": "Cấu hình SAML/OIDC SSO và cho phép các thành viên tổ chức đăng nhập bằng Nhà cung cấp Danh tính",
|
||||
"sso_oidc_heading": "SSO với OIDC",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "Những sự kiện phổ biến nhất",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Đây là tóm tắt những sự kiện phổ biến cho nhóm {{teamName}} của bạn trong 30 ngày qua",
|
||||
"me": "Tôi",
|
||||
"monthly_digest_email": "Email tin nhanh hằng tháng",
|
||||
"monthly_digest_email": "Email tổng hợp hàng tháng",
|
||||
"monthly_digest_email_for_teams": "Email tin nhanh hằng tháng cho các nhóm",
|
||||
"verify_team_tooltip": "Xác minh nhóm của bạn để kích hoạt chức năng gửi tin nhắn cho người tham dự",
|
||||
"member_removed": "Thành xiên đã được xoá bỏ",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "Nếu liên hệ không tồn tại dưới một tài khoản, tạo khách hàng tiềm năng mới từ người tham dự",
|
||||
"salesforce_create_new_contact_under_account": "Tạo liên hệ mới dưới một tài khoản dựa trên tên miền email của người tham dự và các liên hệ hiện có",
|
||||
"mass_assign_attributes": "Gán thuộc tính hàng loạt",
|
||||
"reroute_preview_custom_message": "Kết quả là hiển thị thông điệp tùy chỉnh. Hãy thử thay đổi phản hồi để chuyển hướng đến một sự kiện",
|
||||
"reroute_preview_external_redirect": "Kết quả là chuyển hướng đến {{externalUrl}}. Hãy thử thay đổi phản hồi để chuyển hướng đến một sự kiện",
|
||||
"reroute_preview_possible_host": "Người chủ trì có thể",
|
||||
"current_routing_status": "Trạng thái định tuyến hiện tại",
|
||||
"reroute_booking": "Định tuyến lại cuộc hẹn",
|
||||
"reroute_booking_description": "Định tuyến lại cuộc hẹn cho các thành viên khác trong nhóm",
|
||||
"verify_new_route": "Xác minh tuyến đường mới",
|
||||
"reroute": "Định tuyến lại",
|
||||
"new_routing_status": "Trạng thái định tuyến mới",
|
||||
"salesforce_book_directly_with_attendee_owner": "Nếu người tham dự tồn tại trong Salesforce, đặt lịch trực tiếp với chủ sở hữu",
|
||||
"salesforce_check_owner_of": "Thực thể để kiểm tra chủ sở hữu để đặt lịch trực tiếp",
|
||||
"account": "Tài khoản",
|
||||
"choose_hosts_schedule": "Chọn lịch trình của người tổ chức",
|
||||
"hosts_schedule_description": "Bạn có thể chọn lịch trình khác nhau cho từng người tổ chức. Lịch trình mặc định của mỗi người tổ chức sẽ được chọn theo mặc định.",
|
||||
"no_hosts_description": "Không có người tổ chức nào được chỉ định, hãy thêm người tổ chức trong tab phân công.",
|
||||
"reschedule_with_same_timeslot": "Đặt lại lịch với cùng khung giờ",
|
||||
"reschedule_with_same_timeslot_of_new_event": "Đặt lại lịch với cùng khung giờ của sự kiện mới",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "Đặt lại lịch cho sự kiện mới với khung giờ khác",
|
||||
"reschedule_with_different_timeslot": "Đặt lại lịch với khung giờ khác",
|
||||
"we_dont_have_id_for_the_new_event": "Chúng tôi không có ID cho sự kiện mới. Vui lòng truy cập Biểu mẫu Định tuyến và lưu lại.",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "Đã định tuyến lại cuộc hẹn thành công. Đang chuyển hướng đến trang đặt lịch",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "允许参与者通过动态组预约来向您预约",
|
||||
"dynamic_booking": "动态组链接",
|
||||
"allow_seo_indexing": "允许搜索引擎访问您的公开内容",
|
||||
"seo_indexing": "允许 SEO 索引",
|
||||
"seo_indexing": "允许搜索引擎索引",
|
||||
"allow_browser_notifications": "允许浏览器通知",
|
||||
"you_have_denied_notifications": "您已拒绝通知。请在浏览器设置中重置权限以启用。",
|
||||
"disable_browser_notifications": "禁用浏览器通知",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "隐藏“预约团队成员”按钮",
|
||||
"hide_book_a_team_member_description": "隐藏公共页面中的“预约团队成员”按钮。",
|
||||
"danger_zone": "危险区域",
|
||||
"account_deletion_cannot_be_undone": "请小心。账户删除无法撤消。",
|
||||
"team_deletion_cannot_be_undone": "请小心,团队账户删除无法撤消!",
|
||||
"account_deletion_cannot_be_undone": "请谨慎操作。账户删除无法撤销。",
|
||||
"team_deletion_cannot_be_undone": "请谨慎操作。团队删除无法撤销。",
|
||||
"back": "后退",
|
||||
"cancel": "取消",
|
||||
"cancel_all_remaining": "取消所有剩余活动",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "您多久可被预约一次",
|
||||
"event_advanced_tab_description": "日历设置及更多...",
|
||||
"event_advanced_tab_title": "高级",
|
||||
"event_payments_tab_title": "支付",
|
||||
"event_payments_tab_description": "设置活动的支付方式",
|
||||
"event_setup_multiple_duration_error": "活动设置: 多个时长需要至少 1 个选项。",
|
||||
"event_setup_multiple_duration_default_error": "活动设置: 请选择有效的默认时长。",
|
||||
"event_setup_booking_limits_error": "预约限制必须按升序排列:[日、周、月、年]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "根据对您表格的回答转给合适的人",
|
||||
"form_description": "创建表格来引导预约者",
|
||||
"copy_link_to_form": "将链接复制到表格",
|
||||
"theme": "主题",
|
||||
"theme": "预约页面主题",
|
||||
"theme_applies_note": "这仅适用于您的公开预约页面",
|
||||
"app_theme": "仪表板主题",
|
||||
"app_theme_applies_note": "这仅在您登录仪表板时适用",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "未安装任何日历",
|
||||
"no_calendar_installed_description": "您尚未连接任何日历",
|
||||
"add_a_calendar": "添加日历",
|
||||
"change_email_hint": "您可能需要登出并重新登录才能看到更改生效",
|
||||
"change_email_hint": "您可能需要登出并重新登录才能看到更改生效。",
|
||||
"confirm_password_change_email": "请在更改电子邮件地址之前确认密码",
|
||||
"seats": "位置",
|
||||
"every_app_published": "{{appName}} 应用商店上发布的每个应用都是开源的,并通过同行评审进行了全面测试。尽管如此,除非应用是由 {{appName}} 发布,否则 {{companyName}} 不认可或认证这些应用。如果您遇到不当内容或行为,请举报。",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "验证码错误",
|
||||
"not_verified": "尚未验证",
|
||||
"no_availability_in_month": "在 {{month}}不可预约",
|
||||
"view_previous_month": "查看上个月",
|
||||
"view_next_month": "查看下个月",
|
||||
"send_code": "发送代码",
|
||||
"number_verified": "号码已验证",
|
||||
@@ -2222,7 +2225,7 @@
|
||||
"most_popular_events": "最受欢迎的活动",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "以下是您的团队 {{teamName}} 在过去 30 天的热门活动摘要",
|
||||
"me": "我",
|
||||
"monthly_digest_email": "每月摘要电子邮件",
|
||||
"monthly_digest_email": "月度摘要邮件",
|
||||
"monthly_digest_email_for_teams": "团队的每月摘要电子邮件",
|
||||
"verify_team_tooltip": "验证您的团队以允许向参与者发送消息",
|
||||
"member_removed": "成员已移除",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "如果联系人不在某个账户下,则从参与者创建新的潜在客户",
|
||||
"salesforce_create_new_contact_under_account": "根据参与者的电子邮件域和现有联系人,在账户下创建新联系人",
|
||||
"mass_assign_attributes": "批量分配属性",
|
||||
"reroute_preview_custom_message": "这将显示自定义消息。尝试更改响应以路由到一个事件",
|
||||
"reroute_preview_external_redirect": "这将重定向到 {{externalUrl}}。尝试更改响应以路由到一个事件",
|
||||
"reroute_preview_possible_host": "可能的主持人",
|
||||
"current_routing_status": "当前路由状态",
|
||||
"reroute_booking": "重新路由预约",
|
||||
"reroute_booking_description": "将预约重新路由到不同的团队成员",
|
||||
"verify_new_route": "验证新路由",
|
||||
"reroute": "重新路由",
|
||||
"new_routing_status": "新路由状态",
|
||||
"salesforce_book_directly_with_attendee_owner": "如果参与者在Salesforce中存在,则直接与所有者预订",
|
||||
"salesforce_check_owner_of": "检查实体的所有者以直接预订",
|
||||
"account": "账户",
|
||||
"choose_hosts_schedule": "选择主持人日程",
|
||||
"hosts_schedule_description": "您可以为每位主持人选择不同的日程。默认情况下,每位主持人的默认日程被选中。",
|
||||
"no_hosts_description": "未分配主持人,请在分配选项卡中添加主持人。",
|
||||
"reschedule_with_same_timeslot": "使用相同时间段重新安排",
|
||||
"reschedule_with_same_timeslot_of_new_event": "使用新事件的相同时间段重新安排",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "重新安排到具有不同时间段的新事件",
|
||||
"reschedule_with_different_timeslot": "使用不同时间段重新安排",
|
||||
"we_dont_have_id_for_the_new_event": "我们没有新事件的ID。请转到路由表单并再次保存。",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "成功重新路由预约。正在重定向到预约页面",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 在此上方添加您的新字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -462,7 +462,7 @@
|
||||
"allow_dynamic_booking": "允許與會者透過動態群組向您預約",
|
||||
"dynamic_booking": "動態群組連結",
|
||||
"allow_seo_indexing": "允許搜尋引擎存取您的公開內容",
|
||||
"seo_indexing": "允許 SEO 編纂索引",
|
||||
"seo_indexing": "允許搜尋引擎索引",
|
||||
"allow_browser_notifications": "允許瀏覽器通知",
|
||||
"you_have_denied_notifications": "您已拒絕通知。請在瀏覽器設置中重置權限以啟用。",
|
||||
"disable_browser_notifications": "禁用瀏覽器通知",
|
||||
@@ -673,8 +673,8 @@
|
||||
"hide_book_a_team_member": "隱藏「預約團隊成員」按鈕",
|
||||
"hide_book_a_team_member_description": "隱藏您公開頁面中的「預約團隊成員」按鈕。",
|
||||
"danger_zone": "危險區域",
|
||||
"account_deletion_cannot_be_undone": "請注意,帳號一經刪除即無法還原。",
|
||||
"team_deletion_cannot_be_undone": "請小心。團隊刪除後無法恢復。",
|
||||
"account_deletion_cannot_be_undone": "請小心。帳戶刪除後無法復原。",
|
||||
"team_deletion_cannot_be_undone": "請小心。團隊刪除後無法復原。",
|
||||
"back": "上一步",
|
||||
"cancel": "取消",
|
||||
"cancel_all_remaining": "取消所有剩餘活動",
|
||||
@@ -1387,6 +1387,8 @@
|
||||
"event_limit_tab_description": "您的可預約頻率",
|
||||
"event_advanced_tab_description": "行事曆設定等...",
|
||||
"event_advanced_tab_title": "進階",
|
||||
"event_payments_tab_title": "付款",
|
||||
"event_payments_tab_description": "設定活動的付款方式",
|
||||
"event_setup_multiple_duration_error": "活動設定:多個時間長度需要至少 1 個選項。",
|
||||
"event_setup_multiple_duration_default_error": "活動設定:請選擇有效的預設時間長度。",
|
||||
"event_setup_booking_limits_error": "預約限制必須按遞增順序排列。[日、週、月、年]",
|
||||
@@ -1566,7 +1568,7 @@
|
||||
"route_to_the_right_person": "根據表單回覆引導至合適的人員",
|
||||
"form_description": "建立您的表單來引導預約者",
|
||||
"copy_link_to_form": "複製連結至表單",
|
||||
"theme": "主題",
|
||||
"theme": "預約頁面主題",
|
||||
"theme_applies_note": "這只適用於您的公開預約頁面",
|
||||
"app_theme": "儀表板主題",
|
||||
"app_theme_applies_note": "這僅適用於您登入的儀表板",
|
||||
@@ -1599,7 +1601,7 @@
|
||||
"no_calendar_installed": "未安裝行事曆",
|
||||
"no_calendar_installed_description": "您尚未連接任何行事曆",
|
||||
"add_a_calendar": "新增行事曆",
|
||||
"change_email_hint": "可能得要登出後再登入,才可以看到變更生效",
|
||||
"change_email_hint": "您可能需要登出並重新登入才能看到變更生效。",
|
||||
"confirm_password_change_email": "請先確認密碼,再變更電子郵件地址",
|
||||
"seats": "座位",
|
||||
"every_app_published": "{{appName}} App Store 上發佈的每個應用程式皆為開放原始碼,並經過同業審查仔細測試過。不過除了 {{appName}} 發佈的應用程式外,{{companyName}} 無法為其他應用程式背書或提出擔保。若您發現這些應用程式有不當內容或行為,請立即回報。",
|
||||
@@ -1797,6 +1799,7 @@
|
||||
"wrong_code": "驗證碼錯誤",
|
||||
"not_verified": "尚未驗證",
|
||||
"no_availability_in_month": "{{month}}不開放預約",
|
||||
"view_previous_month": "查看上個月",
|
||||
"view_next_month": "檢視下個月",
|
||||
"send_code": "傳送驗證碼",
|
||||
"number_verified": "號碼已驗證",
|
||||
@@ -1848,7 +1851,7 @@
|
||||
"setup": "設定",
|
||||
"setup_description": "設定 Cal.com 實例",
|
||||
"configure": "設定",
|
||||
"sso_configuration": "SAML 設定",
|
||||
"sso_configuration": "單一登入",
|
||||
"sso_configuration_description": "設定 SAML/OIDC SSO,並允許團隊成員使用 Identity Provider 登入",
|
||||
"sso_configuration_description_orgs": "配置 SAML/OIDC SSO,允許組織成員使用身份提供者登錄",
|
||||
"sso_oidc_heading": "OIDC 型 SSO",
|
||||
@@ -2651,11 +2654,26 @@
|
||||
"salesforce_if_account_does_not_exist": "如果聯絡人不存在於帳戶下,則從參加者創建新的潛在客戶",
|
||||
"salesforce_create_new_contact_under_account": "根據參加者的電子郵件域和現有聯絡人,在帳戶下創建新的聯絡人",
|
||||
"mass_assign_attributes": "批量分配屬性",
|
||||
"reroute_preview_custom_message": "這將顯示自訂訊息。嘗試更改回應以路由到一個事件",
|
||||
"reroute_preview_external_redirect": "這將重新導向到 {{externalUrl}}。嘗試更改回應以路由到一個事件",
|
||||
"reroute_preview_possible_host": "可能的主持人",
|
||||
"current_routing_status": "目前路由狀態",
|
||||
"reroute_booking": "重新路由預約",
|
||||
"reroute_booking_description": "將預約重新路由到不同的團隊成員",
|
||||
"verify_new_route": "驗證新路由",
|
||||
"reroute": "重新路由",
|
||||
"new_routing_status": "新的路由狀態",
|
||||
"salesforce_book_directly_with_attendee_owner": "如果參加者存在於 Salesforce 中,則直接與擁有者預約",
|
||||
"salesforce_check_owner_of": "檢查實體的擁有者以直接預約",
|
||||
"account": "帳戶",
|
||||
"choose_hosts_schedule": "選擇主持人的排程",
|
||||
"hosts_schedule_description": "您可以為每位主持人選擇不同的排程。每位主持人的預設排程會自動選擇。",
|
||||
"no_hosts_description": "未分配主持人,請在分配標籤中添加主持人。",
|
||||
"reschedule_with_same_timeslot": "使用相同時段重新安排",
|
||||
"reschedule_with_same_timeslot_of_new_event": "使用新事件的相同時段重新安排",
|
||||
"reschedule_to_the_new_event_with_different_timeslot": "重新安排到新事件的不同時段",
|
||||
"reschedule_with_different_timeslot": "使用不同時段重新安排",
|
||||
"we_dont_have_id_for_the_new_event": "我們沒有新事件的 ID。請前往路由表單並再次儲存。",
|
||||
"rerouted_booking_successfully_redirecting_to_booking_page": "成功重新路由預約。正在重新導向到預約頁面",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 請在此處新增您的字串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
|
||||
@@ -15,10 +15,10 @@
|
||||
],
|
||||
"functions": {
|
||||
"pages/api/trpc/public/[trpc].ts": {
|
||||
"memory": 2048
|
||||
"memory": 1024
|
||||
},
|
||||
"pages/api/trpc/slots/[trpc].ts": {
|
||||
"memory": 2048
|
||||
"memory": 1024
|
||||
},
|
||||
"pages/api/trpc/appRoutingForms/[trpc].ts": {
|
||||
"memory": 2048
|
||||
|
||||
|
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 9.3 KiB |
+1
-1
@@ -129,7 +129,7 @@
|
||||
"pages": [
|
||||
"self-hosting/deployments/aws",
|
||||
"self-hosting/deployments/azure",
|
||||
"self-hosting/deployments/elastio",
|
||||
"self-hosting/deployments/elestio",
|
||||
"self-hosting/deployments/gcp",
|
||||
"self-hosting/deployments/railway",
|
||||
"self-hosting/deployments/render",
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
---
|
||||
title: "Elastio"
|
||||
icon: "square-e"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
You can deploy Cal.com on Elastio using the button below.
|
||||
|
||||
## One Click Deployment
|
||||
|
||||
<a href="https://elest.io/open-source/cal.com" target="_blank">
|
||||
<img src="/images/deploy-elastio.png" noZoom alt="Deploy on Elastio" />
|
||||
</a>
|
||||
@@ -0,0 +1,13 @@
|
||||
---
|
||||
title: "Elestio"
|
||||
icon: "square-e"
|
||||
iconType: "solid"
|
||||
---
|
||||
|
||||
You can deploy Cal.com on Elestio using the button below.
|
||||
|
||||
## One Click Deployment
|
||||
|
||||
<a href="https://elest.io/open-source/cal.com" target="_blank">
|
||||
<img src="/images/deploy-elestio.png" noZoom alt="Deploy on Elestio" />
|
||||
</a>
|
||||
@@ -464,7 +464,7 @@ checksums:
|
||||
allow_dynamic_booking: 19c086c37cc0aec137e730dd8fc3ae34
|
||||
dynamic_booking: 927c71b4f3ba40240b664d7c9a1c8efd
|
||||
allow_seo_indexing: 421a6a2100fd741cbffa8764f5e0e72d
|
||||
seo_indexing: 8d91183c10c7e4b56e0208acecb79d21
|
||||
seo_indexing: e1a289f4535753aaa3c636125f90ee35
|
||||
allow_browser_notifications: cbe4e1677d717c5330816389fe2e6b28
|
||||
you_have_denied_notifications: 6693a68b32a3baa592bc5b6063ec3c89
|
||||
disable_browser_notifications: 16740fdd3fed13b0344fecd83c2bec4d
|
||||
@@ -675,8 +675,8 @@ checksums:
|
||||
hide_book_a_team_member: bb36ad3c9ee01969ce50ca441ca8078e
|
||||
hide_book_a_team_member_description: 578efa247fb72d5e29d79ccb1a282970
|
||||
danger_zone: 79466492fd0b4e50e28c490441959ea2
|
||||
account_deletion_cannot_be_undone: 7756c7c53553bda5f2c6aaad95bf4db4
|
||||
team_deletion_cannot_be_undone: cac4b6c616025fd0ae733b5a66c37fc5
|
||||
account_deletion_cannot_be_undone: e854cd7b836e45d4ba9529097e6c2de9
|
||||
team_deletion_cannot_be_undone: e634bb3d3129635bb96dfc1a2aec71ca
|
||||
back: f541015a827e37cb3b1234e56bc2aa3c
|
||||
cancel: 2e2a849c2223911717de8caa2c71bade
|
||||
cancel_all_remaining: 7effc00853a384a79645b6685967c45b
|
||||
@@ -1389,6 +1389,8 @@ checksums:
|
||||
event_limit_tab_description: 0a6df44e16911fed68d3757e82486f69
|
||||
event_advanced_tab_description: e742ae193d93e359f3707f1bb4612060
|
||||
event_advanced_tab_title: 16e9b03eda6f6c1324cce54b96bbccd6
|
||||
event_payments_tab_title: 1ce1a8d31ed709fb29fce2057045d104
|
||||
event_payments_tab_description: 69a201094365d68c395879baf96d85ef
|
||||
event_setup_multiple_duration_error: a30305a88bf672e8131cb3b7cae4c1d1
|
||||
event_setup_multiple_duration_default_error: 6767f85bf2a790eb77e2ad4d6a46ae7c
|
||||
event_setup_booking_limits_error: c88422fdc86c4f62d2a6391cf06b918d
|
||||
@@ -1568,9 +1570,9 @@ checksums:
|
||||
route_to_the_right_person: 69407ddfae2119a01251961530738e17
|
||||
form_description: 087ef954207ba2fe11e8806acc4236c7
|
||||
copy_link_to_form: 1c2cc8ec0322107c9542547798045ef3
|
||||
theme: 26a60012895802d36f7cbd11c8493f41
|
||||
theme: c9835133b15db4a30587af5468a91909
|
||||
theme_applies_note: a5481245c5e32c8d6aa4de1a024319f6
|
||||
app_theme: 9cb21b3ee79cdf3a86d1a883aac23078
|
||||
app_theme: 58e1617e61ab0c087a01947079d4733a
|
||||
app_theme_applies_note: cb23a3c7d8cede766963222f3b071267
|
||||
theme_system: b86b75074126596ed417c2a1cd32ed5d
|
||||
add_a_team: 017d560e0236ad8cf750204c6626fb81
|
||||
@@ -1601,7 +1603,7 @@ checksums:
|
||||
no_calendar_installed: 6cf54042f3df4941a2b3b05644b36145
|
||||
no_calendar_installed_description: dd91732b26fa6d7fdcccdb99fad008eb
|
||||
add_a_calendar: ed59e59573b2b4dc35575bb3363415b2
|
||||
change_email_hint: 987ac6f21d1c0950c23a63a12ccf64a9
|
||||
change_email_hint: 34674ecd3f55203058816a7d6709483e
|
||||
confirm_password_change_email: 98d7d01007e06e01606032976839bb8a
|
||||
seats: 6e3f470ef5468c5195ae09f518b4dea4
|
||||
every_app_published: 1edfa8158f66dcc982e9ac2cd139d124
|
||||
@@ -1799,6 +1801,7 @@ checksums:
|
||||
wrong_code: 38edf077b2ff6b64c11d537f0b71e81c
|
||||
not_verified: 702985645b380ed947d9a0e2653ff96d
|
||||
no_availability_in_month: 2550fb427301f1029a7272781cabe8c4
|
||||
view_previous_month: 309181f7f5e17bc1e8da744bc5e7b8bc
|
||||
view_next_month: 8ef4e7245d6863295078abe3657dcd10
|
||||
send_code: 3604a3bd89c33a995b7dc7c695772947
|
||||
number_verified: 8853b69d7f63ea9f5f6e3e38995bd583
|
||||
@@ -1850,7 +1853,7 @@ checksums:
|
||||
setup: f86fa3a6189a3f354a21ba238dd7bd12
|
||||
setup_description: b3d7dcce80c5a19ecd4dcd4d800bd281
|
||||
configure: e3ab18ebb36c218cd4897c620f5809ac
|
||||
sso_configuration: b562d4c80c590e48d428cab93c2e69cd
|
||||
sso_configuration: 58c80e21cf5e88d556ae604726c18e4d
|
||||
sso_configuration_description: b3780a7550be929456b276531a11c56e
|
||||
sso_configuration_description_orgs: 48eac859f9adec7c0ed95d280303ddc6
|
||||
sso_oidc_heading: 9be7a5f0a4b5e0579fcda7f64a930463
|
||||
@@ -2224,7 +2227,7 @@ checksums:
|
||||
most_popular_events: 3e39e95bebef3ca0fdff17e2db89a2a5
|
||||
summary_of_events_for_your_team_for_the_last_30_days: 461d07ec64a94861f5755424a0e510c7
|
||||
me: e0cdc99c6c88fe486dbd78d9d366b788
|
||||
monthly_digest_email: 07ffa7bd5300db6f7de3acf3dd0dbafe
|
||||
monthly_digest_email: f3a57bd7d2d92d334d6049b2f6480f8c
|
||||
monthly_digest_email_for_teams: 6bfade28f5788ca4f5dceb22b58ffd57
|
||||
verify_team_tooltip: 1e2dd94b5f862165e92a43338caecc5c
|
||||
member_removed: c0f44b0f3e20c1bda1bc152a9cc4da84
|
||||
@@ -2653,10 +2656,25 @@ checksums:
|
||||
salesforce_if_account_does_not_exist: ff7ce8c176eaa2ed8a7e6fe09327e830
|
||||
salesforce_create_new_contact_under_account: 929d1effb813cc09b87f79fee1cb699d
|
||||
mass_assign_attributes: 92df3a9cc0a1d55a616c014517c5bee5
|
||||
reroute_preview_custom_message: 4f049f09f79b30cbd9594934ed5d4494
|
||||
reroute_preview_external_redirect: db7340a28a89f755d92fe2f37c67a3ee
|
||||
reroute_preview_possible_host: d9afb2621fb3f8809d2d4df6c012bb19
|
||||
current_routing_status: 3407ebb397e41be73c7db91664f6e885
|
||||
reroute_booking: c369ff6dcdd73c7624625d81f93de63f
|
||||
reroute_booking_description: e3b1dc5a3a2a351562ad06d22a6eb419
|
||||
verify_new_route: b544c001afeb6d48fa0fba26536e88da
|
||||
reroute: d94c2dd8cf9fd4970b110fcb7e6b059f
|
||||
new_routing_status: 16a03500a3a91cc6c0a9d47435ecf45a
|
||||
salesforce_book_directly_with_attendee_owner: 32527f7bc50272de1cb81401e0bf17a9
|
||||
salesforce_check_owner_of: f9e2369739fa3b659b86ed78c1937b11
|
||||
account: 01215c12fb1cdb93bd0c84c1382bef56
|
||||
choose_hosts_schedule: b5405848d78c8bbf891764eb85ad6c78
|
||||
hosts_schedule_description: 9fbda99f7028e2cc126d158c903af652
|
||||
no_hosts_description: e9b5a1e85244a43ee254ded46c81f1ec
|
||||
reschedule_with_same_timeslot: 48b6411da3040a7cb6d1e51922155ae3
|
||||
reschedule_with_same_timeslot_of_new_event: de384b584b07281248c4d736abad0a59
|
||||
reschedule_to_the_new_event_with_different_timeslot: 340767653b1c28da54fbae04f12a823c
|
||||
reschedule_with_different_timeslot: cedbdabd15cfdd1caefaf04992a63b0f
|
||||
we_dont_have_id_for_the_new_event: 55c3fb8891e8b93a22590f723e19e24c
|
||||
rerouted_booking_successfully_redirecting_to_booking_page: 7fef68e0677cd52170230f5567262211
|
||||
ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS: 18323f2f3fabb169a7cb50ff62433850
|
||||
|
||||
@@ -3,6 +3,7 @@ import Link from "next/link";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { useAppContextWithSchema } from "@calcom/app-store/EventTypeAppContext";
|
||||
import { useIsPlatform } from "@calcom/atoms/monorepo";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { Switch, Badge, Avatar, Button, Icon } from "@calcom/ui";
|
||||
@@ -39,6 +40,7 @@ export default function AppCard({
|
||||
const { t } = useTranslation();
|
||||
const [animationRef] = useAutoAnimate<HTMLDivElement>();
|
||||
const { setAppData, LockedIcon, disabled: managedDisabled } = useAppContextWithSchema();
|
||||
const isPlatform = useIsPlatform();
|
||||
|
||||
return (
|
||||
<div
|
||||
@@ -128,7 +130,7 @@ export default function AppCard({
|
||||
{app?.isInstalled && switchChecked ? (
|
||||
app.isSetupAlready === undefined || app.isSetupAlready ? (
|
||||
<div className="relative p-4 pt-5 text-sm [&_input]:mb-0 [&_input]:leading-4">
|
||||
{!hideSettingsIcon && (
|
||||
{!hideSettingsIcon && !isPlatform && (
|
||||
<Link href={`/apps/${app.slug}/setup`} className="absolute right-4 top-4">
|
||||
<Icon name="settings" className="text-default h-4 w-4" aria-hidden="true" />
|
||||
</Link>
|
||||
|
||||
@@ -10,9 +10,15 @@ import { ErrorBoundary } from "@calcom/ui";
|
||||
import type { EventTypeAppCardComponentProps, CredentialOwner } from "../types";
|
||||
import { DynamicComponent } from "./DynamicComponent";
|
||||
|
||||
export type EventTypeApp = RouterOutputs["viewer"]["integrations"]["items"][number] & {
|
||||
credentialOwner?: CredentialOwner;
|
||||
};
|
||||
|
||||
export type EventTypeForAppCard = EventTypeAppCardComponentProps["eventType"];
|
||||
|
||||
export const EventTypeAppCard = (props: {
|
||||
app: RouterOutputs["viewer"]["integrations"]["items"][number] & { credentialOwner?: CredentialOwner };
|
||||
eventType: EventTypeAppCardComponentProps["eventType"];
|
||||
app: EventTypeApp;
|
||||
eventType: EventTypeForAppCard;
|
||||
getAppData: GetAppData;
|
||||
setAppData: SetAppData;
|
||||
// For event type apps, get these props from shouldLockDisableProps
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import type { Prisma } from "@prisma/client";
|
||||
import type { z } from "zod";
|
||||
|
||||
import CrmManager from "@calcom/core/crmManager/crmManager";
|
||||
@@ -7,20 +8,13 @@ import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
|
||||
export async function getCRMContactOwnerForRRLeadSkip(
|
||||
bookerEmail: string,
|
||||
eventTypeId: number
|
||||
eventTypeMetadata: Prisma.JsonValue
|
||||
): Promise<string | undefined> {
|
||||
const eventTypeMetadataQuery = await prisma.eventType.findUnique({
|
||||
where: {
|
||||
id: eventTypeId,
|
||||
},
|
||||
select: { metadata: true },
|
||||
});
|
||||
const parsedEventTypeMetadata = EventTypeMetaDataSchema.safeParse(eventTypeMetadata);
|
||||
|
||||
const eventTypeMetadata = EventTypeMetaDataSchema.safeParse(eventTypeMetadataQuery?.metadata);
|
||||
if (!parsedEventTypeMetadata.success || !parsedEventTypeMetadata.data?.apps) return;
|
||||
|
||||
if (!eventTypeMetadata.success || !eventTypeMetadata.data?.apps) return;
|
||||
|
||||
const crm = await getCRMManagerWithRRLeadSkip(eventTypeMetadata.data.apps);
|
||||
const crm = await getCRMManagerWithRRLeadSkip(parsedEventTypeMetadata.data.apps);
|
||||
|
||||
if (!crm) return;
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
### Obtaining Huddle01 API Token
|
||||
|
||||
- As of now, we haven't made OAuth authentication support public.
|
||||
- Please contact us at support@huddle01.com with the subject line "Require Huddle01 API Token" and mention - details like app name and purpose of token.
|
||||
- We will share token details via email.
|
||||
@@ -1,4 +1,3 @@
|
||||
import { randomString } from "@calcom/lib/random";
|
||||
import type { AppMeta } from "@calcom/types/App";
|
||||
|
||||
import _package from "./package.json";
|
||||
@@ -9,7 +8,7 @@ export const metadata = {
|
||||
installed: true,
|
||||
type: "huddle01_video",
|
||||
variant: "conferencing",
|
||||
categories: ["conferencing"],
|
||||
categories: ["video", "conferencing"],
|
||||
logo: "icon.svg",
|
||||
publisher: "huddle01.com",
|
||||
url: "https://huddle01.com",
|
||||
@@ -21,11 +20,10 @@ export const metadata = {
|
||||
appData: {
|
||||
location: {
|
||||
linkType: "dynamic",
|
||||
type: "integrations:huddle01",
|
||||
type: "integrations:huddle01_video",
|
||||
label: "Huddle01 Video",
|
||||
},
|
||||
},
|
||||
key: { apikey: randomString(12) },
|
||||
dirName: "huddle01video",
|
||||
concurrentMeetings: true,
|
||||
isOAuth: false,
|
||||
|
||||
@@ -1,54 +1,27 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
import type { NextApiRequest } from "next";
|
||||
import { stringify } from "querystring";
|
||||
|
||||
import { throwIfNotHaveAdminAccessToTeam } from "@calcom/app-store/_utils/throwIfNotHaveAdminAccessToTeam";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { defaultHandler, defaultResponder } from "@calcom/lib/server";
|
||||
|
||||
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
|
||||
import { encodeOAuthState } from "../../_utils/oauth/encodeOAuthState";
|
||||
|
||||
/**
|
||||
* This is an example endpoint for an app, these will run under `/api/integrations/[...args]`
|
||||
* @param req
|
||||
* @param res
|
||||
*/
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
if (!req.session?.user?.id) {
|
||||
return res.status(401).json({ message: "You must be logged in to do this" });
|
||||
}
|
||||
const { teamId, returnTo } = req.query;
|
||||
async function handler(req: NextApiRequest) {
|
||||
const state = encodeOAuthState(req);
|
||||
|
||||
await throwIfNotHaveAdminAccessToTeam({ teamId: Number(teamId) ?? null, userId: req.session.user.id });
|
||||
const installForObject = teamId ? { teamId: Number(teamId) } : { userId: req.session.user.id };
|
||||
const params = {
|
||||
response_type: "code",
|
||||
redirect_uri: `${WEBAPP_URL}/api/integrations/huddle01video/callback`,
|
||||
state,
|
||||
appName: "Calcom",
|
||||
};
|
||||
const query = stringify(params);
|
||||
|
||||
const appType = "huddle01_video";
|
||||
try {
|
||||
const alreadyInstalled = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: appType,
|
||||
...installForObject,
|
||||
},
|
||||
});
|
||||
if (alreadyInstalled) {
|
||||
throw new Error("Already installed");
|
||||
}
|
||||
const installation = await prisma.credential.create({
|
||||
data: {
|
||||
type: appType,
|
||||
key: {},
|
||||
...installForObject,
|
||||
appId: "huddle01",
|
||||
},
|
||||
});
|
||||
if (!installation) {
|
||||
throw new Error("Unable to create user credential for huddle01video");
|
||||
}
|
||||
} catch (error: unknown) {
|
||||
if (error instanceof Error) {
|
||||
return res.status(500).json({ message: error.message });
|
||||
}
|
||||
return res.status(500);
|
||||
}
|
||||
// need to return a json object with the response status
|
||||
return res
|
||||
.status(200)
|
||||
.json({ url: returnTo ?? getInstalledAppPath({ variant: "conferencing", slug: "huddle01" }) });
|
||||
const url = `https://huddle01.app/thirdparty_auth?${query}`;
|
||||
|
||||
return { url };
|
||||
}
|
||||
|
||||
export default defaultHandler({
|
||||
GET: Promise.resolve({ default: defaultResponder(handler) }),
|
||||
});
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
|
||||
|
||||
import getInstalledAppPath from "../../_utils/getInstalledAppPath";
|
||||
import { decodeOAuthState } from "../../_utils/oauth/decodeOAuthState";
|
||||
import { storeHuddle01Credential } from "../utils/storage";
|
||||
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const session = await getServerSession({ req, res });
|
||||
|
||||
const state = decodeOAuthState(req);
|
||||
|
||||
if (!session) {
|
||||
return res.status(401).json({ message: "Unauthorized" });
|
||||
}
|
||||
|
||||
const userId = session.user.id;
|
||||
|
||||
const { identityToken } = req.query;
|
||||
|
||||
const token = Array.isArray(identityToken) ? identityToken[0] : identityToken;
|
||||
if (!token) {
|
||||
return res.status(401).json({
|
||||
message: "Something went wrong",
|
||||
});
|
||||
}
|
||||
|
||||
await storeHuddle01Credential(userId, token);
|
||||
|
||||
res.redirect(
|
||||
getSafeRedirectUrl(state?.returnTo) ?? getInstalledAppPath({ variant: "conferencing", slug: "huddle01" })
|
||||
);
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
export { default as add } from "./add";
|
||||
export { default as callback } from "./callback";
|
||||
|
||||
@@ -1,46 +1,143 @@
|
||||
import z from "zod";
|
||||
|
||||
import { handleErrorsJson } from "@calcom/lib/errors";
|
||||
import { randomString } from "@calcom/lib/random";
|
||||
import logger from "@calcom/lib/logger";
|
||||
import type { CalendarEvent } from "@calcom/types/Calendar";
|
||||
import type { CredentialPayload } from "@calcom/types/Credential";
|
||||
import type { PartialReference } from "@calcom/types/EventManager";
|
||||
import type { VideoApiAdapter, VideoCallData } from "@calcom/types/VideoApiAdapter";
|
||||
import type { VideoApiAdapter } from "@calcom/types/VideoApiAdapter";
|
||||
|
||||
const huddle01Schema = z.object({ url: z.string().url(), roomId: z.string() });
|
||||
import { getHuddle01APIKey, getHuddle01Credential } from "../utils/storage";
|
||||
|
||||
const Huddle01VideoApiAdapter = (): VideoApiAdapter => {
|
||||
const API_END_POINT = "https://platform-api.huddle01.workers.dev/api/v2/calendar";
|
||||
|
||||
const fetchHuddleAPI = async (userId: number) => {
|
||||
const { identityToken } = await getHuddle01Credential(userId);
|
||||
const { apiKey } = await getHuddle01APIKey();
|
||||
|
||||
const headers = {
|
||||
"x-api-key": apiKey,
|
||||
"x-identity-token": identityToken,
|
||||
};
|
||||
return (
|
||||
endpoint: "subdomains" | "createMeeting" | "deleteMeeting" | "updateMeeting",
|
||||
option?: RequestInit
|
||||
) => {
|
||||
return fetch(`${API_END_POINT}/${endpoint}`, {
|
||||
...option,
|
||||
headers: {
|
||||
...option?.headers,
|
||||
...headers,
|
||||
},
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
const log = logger.getSubLogger({ prefix: ["app-store/huddle01video/lib/VideoApiAdapter"] });
|
||||
|
||||
const Huddle01ApiAdapter = (credential: CredentialPayload): VideoApiAdapter => {
|
||||
return {
|
||||
getAvailability: () => {
|
||||
return Promise.resolve([]);
|
||||
},
|
||||
createMeeting: async (): Promise<VideoCallData> => {
|
||||
const res = await fetch(
|
||||
"https://wpss2zlpb9.execute-api.us-east-1.amazonaws.com/new-meeting?utmCampaign=cal.com&utmSource=partner&utmMedium=calendar"
|
||||
);
|
||||
|
||||
const json = await handleErrorsJson<{ url: string }>(res);
|
||||
const { url } = huddle01Schema.parse(json);
|
||||
if (url) {
|
||||
return Promise.resolve({
|
||||
type: "huddle01_video",
|
||||
id: randomString(21),
|
||||
password: "",
|
||||
url,
|
||||
});
|
||||
createMeeting: async (e: CalendarEvent) => {
|
||||
if (!credential.userId) {
|
||||
log.error("[Huddle01 Error] -> User is not logged in");
|
||||
throw new Error("User is not logged in");
|
||||
}
|
||||
|
||||
try {
|
||||
const fetch = await fetchHuddleAPI(credential.userId);
|
||||
|
||||
const res = await fetch("createMeeting", {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
title: e.title,
|
||||
startTime: e.startTime,
|
||||
endTime: e.endTime,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const data = (await res.json()) as {
|
||||
roomId: string;
|
||||
meetingLink: string;
|
||||
};
|
||||
|
||||
return {
|
||||
type: "huddle01_video",
|
||||
id: data.roomId,
|
||||
password: "",
|
||||
url: data.meetingLink,
|
||||
};
|
||||
} catch (e) {
|
||||
log.error("[Huddle01 Error] -> Error while creating meeeting", e);
|
||||
throw Error("Error while creating meeting");
|
||||
}
|
||||
return Promise.reject("Url was not received in response body.");
|
||||
},
|
||||
deleteMeeting: async (): Promise<void> => {
|
||||
Promise.resolve();
|
||||
},
|
||||
updateMeeting: (bookingRef: PartialReference): Promise<VideoCallData> => {
|
||||
return Promise.resolve({
|
||||
type: "huddle01_video",
|
||||
id: bookingRef.meetingId as string,
|
||||
password: bookingRef.meetingPassword as string,
|
||||
url: bookingRef.meetingUrl as string,
|
||||
updateMeeting: async (bookingRef: PartialReference, e: CalendarEvent) => {
|
||||
if (!credential.userId) {
|
||||
log.error("[Huddle01 Error] -> User is not logged in");
|
||||
throw new Error("User is not logged in");
|
||||
}
|
||||
|
||||
const fetch = await fetchHuddleAPI(credential.userId);
|
||||
|
||||
const res = await fetch("updateMeeting", {
|
||||
method: "PUT",
|
||||
body: JSON.stringify({
|
||||
title: e.title,
|
||||
startTime: e.startTime,
|
||||
endTime: e.endTime,
|
||||
meetingId: bookingRef.uid,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const data = (await res.json()) as {
|
||||
roomId: string;
|
||||
meetingLink: string;
|
||||
};
|
||||
|
||||
return {
|
||||
type: "huddle01_video",
|
||||
id: data.roomId,
|
||||
password: "",
|
||||
url: data.meetingLink,
|
||||
};
|
||||
},
|
||||
deleteMeeting: async (meetingId: string) => {
|
||||
if (!credential.userId) {
|
||||
log.error("[Huddle01 Error] -> User is not logged in");
|
||||
throw new Error("User is not logged in");
|
||||
}
|
||||
|
||||
const fetch = await fetchHuddleAPI(credential.userId);
|
||||
|
||||
const res = await fetch("deleteMeeting", {
|
||||
method: "DELETE",
|
||||
body: JSON.stringify({
|
||||
meetingId,
|
||||
}),
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
});
|
||||
|
||||
const data = (await res.json()) as {
|
||||
roomId: string;
|
||||
meetingLink: string;
|
||||
};
|
||||
|
||||
return {
|
||||
type: "huddle01_video",
|
||||
id: data.roomId,
|
||||
password: "",
|
||||
url: data.meetingLink,
|
||||
};
|
||||
},
|
||||
getAvailability: async () => {
|
||||
return [];
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
export default Huddle01VideoApiAdapter;
|
||||
export default Huddle01ApiAdapter;
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
import { z } from "zod";
|
||||
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
const huddle01AppKeySchema = z.object({
|
||||
identityToken: z.string(),
|
||||
});
|
||||
|
||||
export async function storeHuddle01Credential(userId: number, identityToken: string) {
|
||||
const existingCredential = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: "huddle01_video",
|
||||
userId: userId,
|
||||
appId: "huddle01",
|
||||
},
|
||||
});
|
||||
|
||||
if (existingCredential) {
|
||||
// Update the existing credential with the new identityToken
|
||||
await prisma.credential.update({
|
||||
where: { id: existingCredential.id },
|
||||
data: { key: { identityToken } },
|
||||
});
|
||||
} else {
|
||||
// Create a new credential if it doesn't exist
|
||||
await prisma.credential.create({
|
||||
data: {
|
||||
type: "huddle01_video",
|
||||
key: { identityToken },
|
||||
userId: userId,
|
||||
appId: "huddle01",
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export async function getHuddle01Credential(userId: number) {
|
||||
const credential = await prisma.credential.findFirst({
|
||||
where: {
|
||||
type: "huddle01_video",
|
||||
userId: userId,
|
||||
appId: "huddle01",
|
||||
},
|
||||
});
|
||||
|
||||
if (!credential) {
|
||||
throw new Error("Huddle01 credential not found");
|
||||
}
|
||||
|
||||
return huddle01AppKeySchema.parse(credential?.key);
|
||||
}
|
||||
|
||||
const huddle01APIKeySchema = z.object({
|
||||
apiKey: z.string(),
|
||||
});
|
||||
|
||||
export async function getHuddle01APIKey() {
|
||||
const appCreds = await prisma.app.findFirst({
|
||||
where: {
|
||||
slug: "huddle01",
|
||||
},
|
||||
});
|
||||
|
||||
if (!appCreds) {
|
||||
throw new Error("Huddle01 app credential not found");
|
||||
}
|
||||
|
||||
return huddle01APIKeySchema.parse(appCreds?.keys);
|
||||
}
|
||||
@@ -87,10 +87,13 @@ describe("Query Builder Config", () => {
|
||||
assertCommonStructure(AttributesBaseConfig);
|
||||
});
|
||||
|
||||
it("should support multiselect_some_in operator for multiselect", () => {
|
||||
it("should support multiselect_some_in and multiselect_not_some_in operators for multiselect", () => {
|
||||
expect(AttributesBaseConfig.types.multiselect.widgets.multiselect.operators).toContain(
|
||||
"multiselect_some_in"
|
||||
);
|
||||
expect(AttributesBaseConfig.types.multiselect.widgets.multiselect.operators).toContain(
|
||||
"multiselect_not_some_in"
|
||||
);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-ignore
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user