Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b2ade568c8 | ||
|
|
27820ce897 | ||
|
|
fea7e66996 | ||
|
|
474d1571ae | ||
|
|
2ef627b87e | ||
|
|
2dfb82b623 | ||
|
|
ccd7fdfd52 | ||
|
|
f92e59ed34 | ||
|
|
d29477349a | ||
|
|
259e480baa | ||
|
|
0055dd1142 | ||
|
|
61c44780f0 | ||
|
|
9710d3b15c | ||
|
|
04cefebd75 | ||
|
|
dbd927c002 | ||
|
|
e6b2116a2b | ||
|
|
855a02091f | ||
|
|
358434429e | ||
|
|
b00061d85e | ||
|
|
022f08c05e | ||
|
|
6ff5c8ce5f | ||
|
|
3be7fb9dd8 | ||
|
|
72dd970b6e | ||
|
|
f4d09ece43 | ||
|
|
239e6085ba | ||
|
|
b17eab3aeb | ||
|
|
752f7fcde0 | ||
|
|
f6b061f787 | ||
|
|
cc8a1290ee | ||
|
|
b7cf893169 | ||
|
|
1458cacdbb |
@@ -144,6 +144,12 @@ GOOGLE_WEBHOOK_TOKEN=
|
|||||||
# Optional URL to override for tunelling webhooks. Defaults to NEXT_PUBLIC_WEBAPP_URL.
|
# Optional URL to override for tunelling webhooks. Defaults to NEXT_PUBLIC_WEBAPP_URL.
|
||||||
GOOGLE_WEBHOOK_URL=
|
GOOGLE_WEBHOOK_URL=
|
||||||
|
|
||||||
|
# Token to verify incoming webhooks from Microsoft Calendar
|
||||||
|
MICROSOFT_WEBHOOK_TOKEN=
|
||||||
|
|
||||||
|
# Optional URL to override for tunelling webhooks. Defaults to NEXT_PUBLIC_WEBAPP_URL.
|
||||||
|
MICROSOFT_WEBHOOK_URL=
|
||||||
|
|
||||||
# Inbox to send user feedback
|
# Inbox to send user feedback
|
||||||
SEND_FEEDBACK_EMAIL=
|
SEND_FEEDBACK_EMAIL=
|
||||||
|
|
||||||
|
|||||||
Vendored
Vendored
@@ -6,6 +6,7 @@ import { createMocks } from "node-mocks-http";
|
|||||||
import { describe, expect, test } from "vitest";
|
import { describe, expect, test } from "vitest";
|
||||||
|
|
||||||
import { HttpError } from "@calcom/lib/http-error";
|
import { HttpError } from "@calcom/lib/http-error";
|
||||||
|
import type { User } from "@calcom/prisma/client";
|
||||||
|
|
||||||
import handler from "../../../pages/api/selected-calendars/_post";
|
import handler from "../../../pages/api/selected-calendars/_post";
|
||||||
|
|
||||||
@@ -72,7 +73,7 @@ describe("POST /api/selected-calendars", () => {
|
|||||||
|
|
||||||
prismaMock.user.findFirstOrThrow.mockResolvedValue({
|
prismaMock.user.findFirstOrThrow.mockResolvedValue({
|
||||||
id: 444444,
|
id: 444444,
|
||||||
} as any);
|
} as User);
|
||||||
|
|
||||||
prismaMock.selectedCalendar.create.mockResolvedValue({
|
prismaMock.selectedCalendar.create.mockResolvedValue({
|
||||||
credentialId: 1,
|
credentialId: 1,
|
||||||
@@ -95,6 +96,16 @@ describe("POST /api/selected-calendars", () => {
|
|||||||
unwatchAttempts: 0,
|
unwatchAttempts: 0,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
channelId: null,
|
||||||
|
channelKind: null,
|
||||||
|
channelResourceId: null,
|
||||||
|
channelResourceUri: null,
|
||||||
|
channelExpiration: null,
|
||||||
|
syncSubscribedAt: null,
|
||||||
|
syncToken: null,
|
||||||
|
syncedAt: null,
|
||||||
|
syncErrorAt: null,
|
||||||
|
syncErrorCount: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
await handler(req, res);
|
await handler(req, res);
|
||||||
@@ -140,6 +151,16 @@ describe("POST /api/selected-calendars", () => {
|
|||||||
unwatchAttempts: 0,
|
unwatchAttempts: 0,
|
||||||
createdAt: new Date(),
|
createdAt: new Date(),
|
||||||
updatedAt: new Date(),
|
updatedAt: new Date(),
|
||||||
|
channelId: null,
|
||||||
|
channelKind: null,
|
||||||
|
channelResourceId: null,
|
||||||
|
channelResourceUri: null,
|
||||||
|
channelExpiration: null,
|
||||||
|
syncSubscribedAt: null,
|
||||||
|
syncToken: null,
|
||||||
|
syncedAt: null,
|
||||||
|
syncErrorAt: null,
|
||||||
|
syncErrorCount: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
await handler(req, res);
|
await handler(req, res);
|
||||||
|
|||||||
+51
-9
@@ -38,6 +38,7 @@ import type {
|
|||||||
GuestsDefaultFieldOutput_2024_06_14,
|
GuestsDefaultFieldOutput_2024_06_14,
|
||||||
NameDefaultFieldInput_2024_06_14,
|
NameDefaultFieldInput_2024_06_14,
|
||||||
NotesDefaultFieldInput_2024_06_14,
|
NotesDefaultFieldInput_2024_06_14,
|
||||||
|
SplitNameDefaultFieldOutput_2024_06_14,
|
||||||
UpdateEventTypeInput_2024_06_14,
|
UpdateEventTypeInput_2024_06_14,
|
||||||
} from "@calcom/platform-types";
|
} from "@calcom/platform-types";
|
||||||
import type { PlatformOAuthClient, Team, User, Schedule, EventType } from "@calcom/prisma/client";
|
import type { PlatformOAuthClient, Team, User, Schedule, EventType } from "@calcom/prisma/client";
|
||||||
@@ -1447,23 +1448,23 @@ describe("Event types Endpoints", () => {
|
|||||||
try {
|
try {
|
||||||
await eventTypesRepositoryFixture.delete(eventType.id);
|
await eventTypesRepositoryFixture.delete(eventType.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Event type might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await userRepositoryFixture.delete(user.id);
|
await userRepositoryFixture.delete(user.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// User might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await userRepositoryFixture.delete(falseTestUser.id);
|
await userRepositoryFixture.delete(falseTestUser.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// User might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await userRepositoryFixture.delete(orgUser.id);
|
await userRepositoryFixture.delete(orgUser.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// User might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
await app.close();
|
await app.close();
|
||||||
});
|
});
|
||||||
@@ -2187,6 +2188,47 @@ describe("Event types Endpoints", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("split name booking field", () => {
|
||||||
|
it("should create an event type with split name booking field", async () => {
|
||||||
|
const splitNameBookingField: SplitNameDefaultFieldOutput_2024_06_14 = {
|
||||||
|
isDefault: true,
|
||||||
|
type: "splitName",
|
||||||
|
slug: "splitName",
|
||||||
|
firstNameLabel: "First name",
|
||||||
|
firstNamePlaceholder: "",
|
||||||
|
lastNameLabel: "last name",
|
||||||
|
lastNamePlaceholder: "",
|
||||||
|
lastNameRequired: false,
|
||||||
|
disableOnPrefill: false,
|
||||||
|
};
|
||||||
|
|
||||||
|
const body: CreateEventTypeInput_2024_06_14 = {
|
||||||
|
title: "Coding class 9",
|
||||||
|
slug: "coding-class-9",
|
||||||
|
description: "Let's learn how to code like a pro.",
|
||||||
|
lengthInMinutes: 60,
|
||||||
|
bookingFields: [splitNameBookingField],
|
||||||
|
};
|
||||||
|
|
||||||
|
return request(app.getHttpServer())
|
||||||
|
.post("/api/v2/event-types")
|
||||||
|
.set(CAL_API_VERSION_HEADER, VERSION_2024_06_14)
|
||||||
|
.send(body)
|
||||||
|
.expect(201)
|
||||||
|
.then(async (response) => {
|
||||||
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
||||||
|
const createdEventType = responseBody.data;
|
||||||
|
|
||||||
|
const splitNameBookingFieldResponse = createdEventType.bookingFields.find(
|
||||||
|
(field) => field.type === "splitName"
|
||||||
|
) as SplitNameDefaultFieldOutput_2024_06_14 | undefined;
|
||||||
|
expect(splitNameBookingFieldResponse).toEqual(splitNameBookingField);
|
||||||
|
|
||||||
|
await eventTypesRepositoryFixture.delete(createdEventType.id);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
afterAll(async () => {
|
afterAll(async () => {
|
||||||
await oauthClientRepositoryFixture.delete(oAuthClient.id);
|
await oauthClientRepositoryFixture.delete(oAuthClient.id);
|
||||||
await teamRepositoryFixture.delete(organization.id);
|
await teamRepositoryFixture.delete(organization.id);
|
||||||
@@ -2194,12 +2236,12 @@ describe("Event types Endpoints", () => {
|
|||||||
await eventTypesRepositoryFixture.delete(legacyEventTypeId1);
|
await eventTypesRepositoryFixture.delete(legacyEventTypeId1);
|
||||||
await eventTypesRepositoryFixture.delete(legacyEventTypeId2);
|
await eventTypesRepositoryFixture.delete(legacyEventTypeId2);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Event type might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await userRepositoryFixture.delete(user.id);
|
await userRepositoryFixture.delete(user.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// User might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
await app.close();
|
await app.close();
|
||||||
});
|
});
|
||||||
@@ -2379,7 +2421,7 @@ describe("Event types Endpoints", () => {
|
|||||||
|
|
||||||
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
const responseBody: ApiSuccessResponse<EventTypeOutput_2024_06_14> = response.body;
|
||||||
const updatedEventType = responseBody.data;
|
const updatedEventType = responseBody.data;
|
||||||
+expect(updatedEventType.hidden).toBe(false);
|
expect(updatedEventType.hidden).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -2389,12 +2431,12 @@ describe("Event types Endpoints", () => {
|
|||||||
try {
|
try {
|
||||||
await eventTypesRepositoryFixture.delete(legacyEventTypeId1);
|
await eventTypesRepositoryFixture.delete(legacyEventTypeId1);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// Event type might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await userRepositoryFixture.delete(user.id);
|
await userRepositoryFixture.delete(user.id);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
// User might have been deleted by the test
|
console.log(e);
|
||||||
}
|
}
|
||||||
await app.close();
|
await app.close();
|
||||||
});
|
});
|
||||||
|
|||||||
+2
@@ -351,6 +351,7 @@ describe("Organizations Users Endpoints", () => {
|
|||||||
email: `organizations-users-new-member-${randomString()}@api.com`,
|
email: `organizations-users-new-member-${randomString()}@api.com`,
|
||||||
bio,
|
bio,
|
||||||
metadata,
|
metadata,
|
||||||
|
timeZone: "Europe/Rome",
|
||||||
};
|
};
|
||||||
|
|
||||||
const emailSpy = jest
|
const emailSpy = jest
|
||||||
@@ -367,6 +368,7 @@ describe("Organizations Users Endpoints", () => {
|
|||||||
expect(userData.email).toBe(newOrgUser.email);
|
expect(userData.email).toBe(newOrgUser.email);
|
||||||
expect(userData.bio).toBe(newOrgUser.bio);
|
expect(userData.bio).toBe(newOrgUser.bio);
|
||||||
expect(userData.metadata).toEqual(newOrgUser.metadata);
|
expect(userData.metadata).toEqual(newOrgUser.metadata);
|
||||||
|
expect(userData.timeZone).toBe(newOrgUser.timeZone);
|
||||||
expect(emailSpy).toHaveBeenCalledWith({
|
expect(emailSpy).toHaveBeenCalledWith({
|
||||||
usernameOrEmail: newOrgUser.email,
|
usernameOrEmail: newOrgUser.email,
|
||||||
orgName: org.name,
|
orgName: org.name,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { CapitalizeTimeZone } from "@/lib/inputs/capitalize-timezone";
|
||||||
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
|
||||||
import { Expose, Transform } from "class-transformer";
|
import { Expose, Transform } from "class-transformer";
|
||||||
import {
|
import {
|
||||||
@@ -112,6 +113,7 @@ export class CreateUserInput {
|
|||||||
@ApiProperty({ type: String, required: false, description: "Time zone", example: "America/New_York" })
|
@ApiProperty({ type: String, required: false, description: "Time zone", example: "America/New_York" })
|
||||||
@IsOptional()
|
@IsOptional()
|
||||||
@IsString()
|
@IsString()
|
||||||
|
@CapitalizeTimeZone()
|
||||||
@Validate(TimeZoneValidator)
|
@Validate(TimeZoneValidator)
|
||||||
@Expose()
|
@Expose()
|
||||||
timeZone?: string;
|
timeZone?: string;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ValidatorConstraintInterface } from "class-validator";
|
import type { ValidatorConstraintInterface } from "class-validator";
|
||||||
import { ValidatorConstraint } from "class-validator";
|
import { ValidatorConstraint } from "class-validator";
|
||||||
import tzdata from "tzdata";
|
import * as tzdata from "tzdata";
|
||||||
|
|
||||||
@ValidatorConstraint({ name: "timezoneValidator", async: false })
|
@ValidatorConstraint({ name: "timezoneValidator", async: false })
|
||||||
export class TimeZoneValidator implements ValidatorConstraintInterface {
|
export class TimeZoneValidator implements ValidatorConstraintInterface {
|
||||||
|
|||||||
@@ -636,10 +636,6 @@ select:focus {
|
|||||||
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intercom-lightweight-app {
|
|
||||||
@apply z-40 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
@import "../../web/styles/tokens.css";
|
@import "../../web/styles/tokens.css";
|
||||||
|
|
||||||
@layer base {
|
@layer base {
|
||||||
@@ -1027,10 +1023,6 @@ select:focus {
|
|||||||
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intercom-lightweight-app {
|
|
||||||
@apply z-40 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prose :where(a:not([data-card])):not(:where([class~="not-prose"] *)) {
|
.prose :where(a:not([data-card])):not(:where([class~="not-prose"] *)) {
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,10 +5,11 @@ import { unstable_cache } from "next/cache";
|
|||||||
import { cookies, headers } from "next/headers";
|
import { cookies, headers } from "next/headers";
|
||||||
import { redirect } from "next/navigation";
|
import { redirect } from "next/navigation";
|
||||||
|
|
||||||
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||||
import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable";
|
import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable";
|
||||||
import { OrganizationRepository } from "@calcom/lib/server/repository/organization";
|
import { OrganizationRepository } from "@calcom/lib/server/repository/organization";
|
||||||
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router";
|
import { availabilityRouter } from "@calcom/trpc/server/routers/viewer/availability/_router";
|
||||||
|
|
||||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||||
@@ -72,8 +73,14 @@ const Page = async ({ searchParams: _searchParams }: PageProps) => {
|
|||||||
orgId: organizationId,
|
orgId: organizationId,
|
||||||
})
|
})
|
||||||
: false;
|
: false;
|
||||||
const isOrgAdminOrOwner = checkAdminOrOwner(session?.user?.org?.role);
|
|
||||||
const canViewTeamAvailability = isOrgAdminOrOwner || !isOrgPrivate;
|
const permissionService = new PermissionCheckService();
|
||||||
|
const teamIdsWithPermission = await permissionService.getTeamIdsWithPermission({
|
||||||
|
userId: session.user.id,
|
||||||
|
permission: "availability.read",
|
||||||
|
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
|
||||||
|
});
|
||||||
|
const canViewTeamAvailability = teamIdsWithPermission.length > 0 || !isOrgPrivate;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ShellMainAppDir
|
<ShellMainAppDir
|
||||||
|
|||||||
@@ -23,7 +23,8 @@ const ServerPage = async ({ searchParams: _searchParams }: ServerPageProps) => {
|
|||||||
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
const session = await getServerSession({ req: buildLegacyRequest(await headers(), await cookies()) });
|
||||||
const searchParams = await _searchParams;
|
const searchParams = await _searchParams;
|
||||||
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
||||||
const callbackUrl = token ? `/teams?token=${encodeURIComponent(token)}` : null;
|
const autoAccept = Array.isArray(searchParams?.autoAccept) ? searchParams.autoAccept[0] : searchParams?.autoAccept;
|
||||||
|
const callbackUrl = token ? `/teams?token=${encodeURIComponent(token)}${autoAccept ? `&autoAccept=${encodeURIComponent(autoAccept)}` : ''}` : null;
|
||||||
|
|
||||||
if (!session) {
|
if (!session) {
|
||||||
redirect(callbackUrl ? `/auth/login?callbackUrl=${callbackUrl}` : "/auth/login");
|
redirect(callbackUrl ? `/auth/login?callbackUrl=${callbackUrl}` : "/auth/login");
|
||||||
|
|||||||
@@ -2,11 +2,12 @@ import type { SearchParams } from "app/_types";
|
|||||||
import type { Session } from "next-auth";
|
import type { Session } from "next-auth";
|
||||||
import { unstable_cache } from "next/cache";
|
import { unstable_cache } from "next/cache";
|
||||||
|
|
||||||
import { checkAdminOrOwner } from "@calcom/features/auth/lib/checkAdminOrOwner";
|
|
||||||
import { TeamsListing } from "@calcom/features/ee/teams/components/TeamsListing";
|
import { TeamsListing } from "@calcom/features/ee/teams/components/TeamsListing";
|
||||||
|
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||||
import { TeamRepository } from "@calcom/lib/server/repository/team";
|
import { TeamRepository } from "@calcom/lib/server/repository/team";
|
||||||
import { TeamService } from "@calcom/lib/server/service/teamService";
|
import { TeamService } from "@calcom/lib/server/service/teamService";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import { TRPCError } from "@trpc/server";
|
import { TRPCError } from "@trpc/server";
|
||||||
|
|
||||||
@@ -32,14 +33,21 @@ export const ServerTeamsListing = async ({
|
|||||||
session: Session;
|
session: Session;
|
||||||
}) => {
|
}) => {
|
||||||
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
const token = Array.isArray(searchParams?.token) ? searchParams.token[0] : searchParams?.token;
|
||||||
|
const autoAccept = Array.isArray(searchParams?.autoAccept) ? searchParams.autoAccept[0] : searchParams?.autoAccept;
|
||||||
const userId = session.user.id;
|
const userId = session.user.id;
|
||||||
|
let invitationAccepted = false;
|
||||||
|
|
||||||
let teamNameFromInvite,
|
let teamNameFromInvite,
|
||||||
errorMsgFromInvite = null;
|
errorMsgFromInvite = null;
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
try {
|
try {
|
||||||
teamNameFromInvite = await TeamService.inviteMemberByToken(token, userId);
|
if (autoAccept === "true") {
|
||||||
|
await TeamService.acceptInvitationByToken(token, userId);
|
||||||
|
invitationAccepted = true;
|
||||||
|
} else {
|
||||||
|
teamNameFromInvite = await TeamService.inviteMemberByToken(token, userId);
|
||||||
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
errorMsgFromInvite = "Error while fetching teams";
|
errorMsgFromInvite = "Error while fetching teams";
|
||||||
if (e instanceof TRPCError) errorMsgFromInvite = e.message;
|
if (e instanceof TRPCError) errorMsgFromInvite = e.message;
|
||||||
@@ -49,21 +57,30 @@ export const ServerTeamsListing = async ({
|
|||||||
const teams = await getCachedTeams(userId);
|
const teams = await getCachedTeams(userId);
|
||||||
const userProfile = session?.user?.profile;
|
const userProfile = session?.user?.profile;
|
||||||
const orgId = userProfile?.organizationId ?? session?.user.org?.id;
|
const orgId = userProfile?.organizationId ?? session?.user.org?.id;
|
||||||
const orgRole =
|
|
||||||
session?.user?.org?.role ??
|
const permissionCheckService = new PermissionCheckService();
|
||||||
userProfile?.organization?.members.find((m: { userId: number }) => m.userId === userId)?.role;
|
const canCreateTeam = orgId
|
||||||
const isOrgAdminOrOwner = checkAdminOrOwner(orgRole);
|
? await permissionCheckService.checkPermission({
|
||||||
|
userId: session.user.id,
|
||||||
|
teamId: orgId,
|
||||||
|
permission: "team.create",
|
||||||
|
fallbackRoles: [MembershipRole.ADMIN, MembershipRole.OWNER],
|
||||||
|
})
|
||||||
|
: false;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
Main: (
|
Main: (
|
||||||
<TeamsListing
|
<TeamsListing
|
||||||
|
invitationAccepted={invitationAccepted}
|
||||||
teams={teams}
|
teams={teams}
|
||||||
orgId={orgId ?? null}
|
orgId={orgId ?? null}
|
||||||
isOrgAdmin={isOrgAdminOrOwner}
|
permissions={{
|
||||||
|
canCreateTeam: canCreateTeam,
|
||||||
|
}}
|
||||||
teamNameFromInvite={teamNameFromInvite ?? null}
|
teamNameFromInvite={teamNameFromInvite ?? null}
|
||||||
errorMsgFromInvite={errorMsgFromInvite}
|
errorMsgFromInvite={errorMsgFromInvite}
|
||||||
/>
|
/>
|
||||||
),
|
),
|
||||||
CTA: !orgId || isOrgAdminOrOwner ? <TeamsCTA /> : null,
|
CTA: !orgId || canCreateTeam ? <TeamsCTA /> : null,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants";
|
|||||||
import { useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan";
|
import { useHasPaidPlan } from "@calcom/lib/hooks/useHasPaidPlan";
|
||||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||||
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
import { useRouterQuery } from "@calcom/lib/hooks/useRouterQuery";
|
||||||
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
import { trpc } from "@calcom/trpc/react";
|
import { trpc } from "@calcom/trpc/react";
|
||||||
import { ArrowButton } from "@calcom/ui/components/arrow-button";
|
import { ArrowButton } from "@calcom/ui/components/arrow-button";
|
||||||
import { Badge } from "@calcom/ui/components/badge";
|
import { Badge } from "@calcom/ui/components/badge";
|
||||||
@@ -46,6 +47,10 @@ function NewFormButton({ setNewFormDialogState }: { setNewFormDialogState: SetNe
|
|||||||
createFunction={(teamId) => {
|
createFunction={(teamId) => {
|
||||||
setNewFormDialogState({ action: "new", target: teamId ? String(teamId) : "" });
|
setNewFormDialogState({ action: "new", target: teamId ? String(teamId) : "" });
|
||||||
}}
|
}}
|
||||||
|
withPermission={{
|
||||||
|
permission: "routingForm.create",
|
||||||
|
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
+9
-11
@@ -175,6 +175,7 @@ const organizationAdminKeys = ["privacy", "OAuth Clients", "SSO", "directory_syn
|
|||||||
export interface SettingsPermissions {
|
export interface SettingsPermissions {
|
||||||
canViewRoles?: boolean;
|
canViewRoles?: boolean;
|
||||||
canViewOrganizationBilling?: boolean;
|
canViewOrganizationBilling?: boolean;
|
||||||
|
canUpdateOrganization?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const useTabs = ({
|
const useTabs = ({
|
||||||
@@ -190,7 +191,6 @@ const useTabs = ({
|
|||||||
const { data: user } = trpc.viewer.me.get.useQuery({ includePasswordAdded: true });
|
const { data: user } = trpc.viewer.me.get.useQuery({ includePasswordAdded: true });
|
||||||
const orgBranding = useOrgBranding();
|
const orgBranding = useOrgBranding();
|
||||||
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
|
const isAdmin = session.data?.user.role === UserPermissionRole.ADMIN;
|
||||||
const isOrgAdminOrOwner = checkAdminOrOwner(orgBranding?.role);
|
|
||||||
|
|
||||||
const processTabsMemod = useMemo(() => {
|
const processTabsMemod = useMemo(() => {
|
||||||
const processedTabs = getTabs(orgBranding).map((tab) => {
|
const processedTabs = getTabs(orgBranding).map((tab) => {
|
||||||
@@ -203,10 +203,10 @@ const useTabs = ({
|
|||||||
};
|
};
|
||||||
} else if (tab.href === "/settings/organizations") {
|
} else if (tab.href === "/settings/organizations") {
|
||||||
const newArray = (tab?.children ?? []).filter(
|
const newArray = (tab?.children ?? []).filter(
|
||||||
(child) => isOrgAdminOrOwner || !organizationAdminKeys.includes(child.name)
|
(child) => permissions?.canUpdateOrganization || !organizationAdminKeys.includes(child.name)
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isOrgAdminOrOwner) {
|
if (permissions?.canUpdateOrganization) {
|
||||||
newArray.splice(4, 0, {
|
newArray.splice(4, 0, {
|
||||||
name: "attributes",
|
name: "attributes",
|
||||||
href: "/settings/organizations/attributes",
|
href: "/settings/organizations/attributes",
|
||||||
@@ -238,7 +238,7 @@ const useTabs = ({
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (isOrgAdminOrOwner) {
|
if (permissions?.canUpdateOrganization) {
|
||||||
newArray.push({
|
newArray.push({
|
||||||
name: "billing",
|
name: "billing",
|
||||||
href: "/settings/organizations/billing",
|
href: "/settings/organizations/billing",
|
||||||
@@ -264,7 +264,7 @@ const useTabs = ({
|
|||||||
return { ...tab, children: filtered };
|
return { ...tab, children: filtered };
|
||||||
} else if (tab.href === "/settings/developer") {
|
} else if (tab.href === "/settings/developer") {
|
||||||
const filtered = tab?.children?.filter(
|
const filtered = tab?.children?.filter(
|
||||||
(childTab) => isOrgAdminOrOwner || childTab.name !== "admin_api"
|
(childTab) => permissions?.canUpdateOrganization || childTab.name !== "admin_api"
|
||||||
);
|
);
|
||||||
return { ...tab, children: filtered };
|
return { ...tab, children: filtered };
|
||||||
}
|
}
|
||||||
@@ -274,12 +274,12 @@ const useTabs = ({
|
|||||||
// check if name is in adminRequiredKeys
|
// check if name is in adminRequiredKeys
|
||||||
return processedTabs.filter((tab) => {
|
return processedTabs.filter((tab) => {
|
||||||
if (organizationRequiredKeys.includes(tab.name)) return !!orgBranding;
|
if (organizationRequiredKeys.includes(tab.name)) return !!orgBranding;
|
||||||
if (tab.name === "other_teams" && !isOrgAdminOrOwner) return false;
|
if (tab.name === "other_teams" && !permissions?.canUpdateOrganization) return false;
|
||||||
|
|
||||||
if (isAdmin) return true;
|
if (isAdmin) return true;
|
||||||
return !adminRequiredKeys.includes(tab.name);
|
return !adminRequiredKeys.includes(tab.name);
|
||||||
});
|
});
|
||||||
}, [isAdmin, orgBranding, isOrgAdminOrOwner, user, isDelegationCredentialEnabled, isPbacEnabled, permissions]);
|
}, [isAdmin, orgBranding, user, isDelegationCredentialEnabled, isPbacEnabled, permissions]);
|
||||||
|
|
||||||
return processTabsMemod;
|
return processTabsMemod;
|
||||||
};
|
};
|
||||||
@@ -560,8 +560,6 @@ const SettingsSidebarContainer = ({
|
|||||||
}
|
}
|
||||||
}, [searchParams?.get("id"), otherTeams]);
|
}, [searchParams?.get("id"), otherTeams]);
|
||||||
|
|
||||||
const isOrgAdminOrOwner = checkAdminOrOwner(orgBranding?.role);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<nav
|
<nav
|
||||||
style={{ maxHeight: `calc(100vh - ${bannersHeight}px)`, top: `${bannersHeight}px` }}
|
style={{ maxHeight: `calc(100vh - ${bannersHeight}px)`, top: `${bannersHeight}px` }}
|
||||||
@@ -638,12 +636,12 @@ const SettingsSidebarContainer = ({
|
|||||||
as="p"
|
as="p"
|
||||||
className="text-subtle truncate text-sm font-medium leading-5"
|
className="text-subtle truncate text-sm font-medium leading-5"
|
||||||
loadingClassName="ms-3">
|
loadingClassName="ms-3">
|
||||||
{t(isOrgAdminOrOwner ? "my_teams" : tab.name)}
|
{t("my_teams")}
|
||||||
</Skeleton>
|
</Skeleton>
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
<TeamListCollapsible teamFeatures={teamFeatures} />
|
<TeamListCollapsible teamFeatures={teamFeatures} />
|
||||||
{(!orgBranding?.id || isOrgAdminOrOwner) && (
|
{(!orgBranding?.id || permissions?.canUpdateOrganization) && (
|
||||||
<VerticalTabItem
|
<VerticalTabItem
|
||||||
name={t("add_a_team")}
|
name={t("add_a_team")}
|
||||||
href={`${WEBAPP_URL}/settings/teams/new`}
|
href={`${WEBAPP_URL}/settings/teams/new`}
|
||||||
|
|||||||
@@ -47,6 +47,7 @@ export default async function SettingsLayoutAppDir(props: SettingsLayoutProps) {
|
|||||||
let teamFeatures: Record<number, TeamFeatures> | null = null;
|
let teamFeatures: Record<number, TeamFeatures> | null = null;
|
||||||
let canViewRoles = false;
|
let canViewRoles = false;
|
||||||
let canViewOrganizationBilling = false;
|
let canViewOrganizationBilling = false;
|
||||||
|
let canUpdateOrganization = false;
|
||||||
const orgId = session?.user?.profile?.organizationId ?? session?.user.org?.id;
|
const orgId = session?.user?.profile?.organizationId ?? session?.user.org?.id;
|
||||||
|
|
||||||
// For now we only grab organization features but it would be nice to fetch these on the server side for specific team feature flags
|
// For now we only grab organization features but it would be nice to fetch these on the server side for specific team feature flags
|
||||||
@@ -68,6 +69,11 @@ export default async function SettingsLayoutAppDir(props: SettingsLayoutProps) {
|
|||||||
canViewRoles = roleActions[CrudAction.Read] ?? false;
|
canViewRoles = roleActions[CrudAction.Read] ?? false;
|
||||||
const orgActions = PermissionMapper.toActionMap(organizationPermissions, Resource.Organization);
|
const orgActions = PermissionMapper.toActionMap(organizationPermissions, Resource.Organization);
|
||||||
canViewOrganizationBilling = orgActions[CustomAction.ManageBilling] ?? isOrgAdminOrOwner;
|
canViewOrganizationBilling = orgActions[CustomAction.ManageBilling] ?? isOrgAdminOrOwner;
|
||||||
|
canUpdateOrganization = orgActions[CrudAction.Update] ?? isOrgAdminOrOwner;
|
||||||
|
} else {
|
||||||
|
canViewRoles = false;
|
||||||
|
canViewOrganizationBilling = isOrgAdminOrOwner;
|
||||||
|
canUpdateOrganization = isOrgAdminOrOwner;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,7 +82,7 @@ export default async function SettingsLayoutAppDir(props: SettingsLayoutProps) {
|
|||||||
<SettingsLayoutAppDirClient
|
<SettingsLayoutAppDirClient
|
||||||
{...props}
|
{...props}
|
||||||
teamFeatures={teamFeatures ?? {}}
|
teamFeatures={teamFeatures ?? {}}
|
||||||
permissions={{ canViewRoles, canViewOrganizationBilling }}
|
permissions={{ canViewRoles, canViewOrganizationBilling, canUpdateOrganization }}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -22,6 +22,7 @@ interface AdvancedPermissionGroupProps {
|
|||||||
onChange: (permissions: string[]) => void;
|
onChange: (permissions: string[]) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
scope?: Scope;
|
scope?: Scope;
|
||||||
|
isPrivate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const INTERNAL_DATAACCESS_KEY = "_resource";
|
const INTERNAL_DATAACCESS_KEY = "_resource";
|
||||||
@@ -32,10 +33,11 @@ export function AdvancedPermissionGroup({
|
|||||||
onChange,
|
onChange,
|
||||||
disabled,
|
disabled,
|
||||||
scope = Scope.Organization,
|
scope = Scope.Organization,
|
||||||
|
isPrivate = false,
|
||||||
}: AdvancedPermissionGroupProps) {
|
}: AdvancedPermissionGroupProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { toggleSinglePermission, toggleResourcePermissionLevel } = usePermissions(scope);
|
const { toggleSinglePermission, toggleResourcePermissionLevel } = usePermissions(scope);
|
||||||
const scopedRegistry = getPermissionsForScope(scope);
|
const scopedRegistry = getPermissionsForScope(scope, isPrivate);
|
||||||
const resourceConfig = scopedRegistry[resource];
|
const resourceConfig = scopedRegistry[resource];
|
||||||
const [isExpanded, setIsExpanded] = useState(false);
|
const [isExpanded, setIsExpanded] = useState(false);
|
||||||
|
|
||||||
|
|||||||
+14
-3
@@ -56,9 +56,17 @@ interface RoleSheetProps {
|
|||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
scope?: Scope;
|
scope?: Scope;
|
||||||
|
isPrivate?: boolean; // Add isPrivate prop to control permission visibility
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Organization }: RoleSheetProps) {
|
export function RoleSheet({
|
||||||
|
role,
|
||||||
|
open,
|
||||||
|
onOpenChange,
|
||||||
|
teamId,
|
||||||
|
scope = Scope.Organization,
|
||||||
|
isPrivate = false
|
||||||
|
}: RoleSheetProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const isEditing = Boolean(role);
|
const isEditing = Boolean(role);
|
||||||
@@ -108,7 +116,8 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
|
|||||||
const { isAdvancedMode, permissions, color } = form.watch();
|
const { isAdvancedMode, permissions, color } = form.watch();
|
||||||
|
|
||||||
const { filteredResources, scopedRegistry } = useMemo(() => {
|
const { filteredResources, scopedRegistry } = useMemo(() => {
|
||||||
const scopedRegistry = getPermissionsForScope(scope);
|
// Use privacy-aware filtering if we have privacy information
|
||||||
|
const scopedRegistry = getPermissionsForScope(scope, isPrivate);
|
||||||
const filteredResources = Object.keys(scopedRegistry).filter((resource) =>
|
const filteredResources = Object.keys(scopedRegistry).filter((resource) =>
|
||||||
t(
|
t(
|
||||||
scopedRegistry[resource as Resource][CrudAction.All as keyof (typeof scopedRegistry)[Resource]]
|
scopedRegistry[resource as Resource][CrudAction.All as keyof (typeof scopedRegistry)[Resource]]
|
||||||
@@ -118,7 +127,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
|
|||||||
.includes(searchQuery.toLowerCase())
|
.includes(searchQuery.toLowerCase())
|
||||||
);
|
);
|
||||||
return { filteredResources, scopedRegistry };
|
return { filteredResources, scopedRegistry };
|
||||||
}, [searchQuery, t, scope]);
|
}, [searchQuery, t, scope, isPrivate]);
|
||||||
|
|
||||||
const createMutation = trpc.viewer.pbac.createRole.useMutation({
|
const createMutation = trpc.viewer.pbac.createRole.useMutation({
|
||||||
onSuccess: async () => {
|
onSuccess: async () => {
|
||||||
@@ -221,6 +230,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
|
|||||||
onChange={(newPermissions) => form.setValue("permissions", newPermissions)}
|
onChange={(newPermissions) => form.setValue("permissions", newPermissions)}
|
||||||
disabled={isSystemRole}
|
disabled={isSystemRole}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
|
isPrivate={isPrivate}
|
||||||
/>
|
/>
|
||||||
))}{" "}
|
))}{" "}
|
||||||
</div>
|
</div>
|
||||||
@@ -245,6 +255,7 @@ export function RoleSheet({ role, open, onOpenChange, teamId, scope = Scope.Orga
|
|||||||
onChange={(newPermissions) => form.setValue("permissions", newPermissions)}
|
onChange={(newPermissions) => form.setValue("permissions", newPermissions)}
|
||||||
disabled={isSystemRole}
|
disabled={isSystemRole}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
|
isPrivate={isPrivate}
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
</div>{" "}
|
</div>{" "}
|
||||||
|
|||||||
+3
@@ -48,6 +48,7 @@ interface RolesListProps {
|
|||||||
initialSheetOpen?: boolean;
|
initialSheetOpen?: boolean;
|
||||||
teamId: number;
|
teamId: number;
|
||||||
scope?: Scope;
|
scope?: Scope;
|
||||||
|
isPrivate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function RolesList({
|
export function RolesList({
|
||||||
@@ -57,6 +58,7 @@ export function RolesList({
|
|||||||
initialSheetOpen,
|
initialSheetOpen,
|
||||||
teamId,
|
teamId,
|
||||||
scope,
|
scope,
|
||||||
|
isPrivate,
|
||||||
}: RolesListProps) {
|
}: RolesListProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { isOpen, setIsOpen, selectedRoleId, setSelectedRoleId, handleSheetOpenChange } = useRoleStates(
|
const { isOpen, setIsOpen, selectedRoleId, setSelectedRoleId, handleSheetOpenChange } = useRoleStates(
|
||||||
@@ -113,6 +115,7 @@ export function RolesList({
|
|||||||
onOpenChange={handleSheetOpenChange}
|
onOpenChange={handleSheetOpenChange}
|
||||||
teamId={teamId}
|
teamId={teamId}
|
||||||
scope={scope}
|
scope={scope}
|
||||||
|
isPrivate={isPrivate}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
+3
-1
@@ -18,6 +18,7 @@ interface SimplePermissionItemProps {
|
|||||||
onChange: (permissions: string[]) => void;
|
onChange: (permissions: string[]) => void;
|
||||||
disabled?: boolean;
|
disabled?: boolean;
|
||||||
scope?: Scope;
|
scope?: Scope;
|
||||||
|
isPrivate?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function SimplePermissionItem({
|
export function SimplePermissionItem({
|
||||||
@@ -26,10 +27,11 @@ export function SimplePermissionItem({
|
|||||||
onChange,
|
onChange,
|
||||||
disabled,
|
disabled,
|
||||||
scope = Scope.Organization,
|
scope = Scope.Organization,
|
||||||
|
isPrivate = false,
|
||||||
}: SimplePermissionItemProps) {
|
}: SimplePermissionItemProps) {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
const { getResourcePermissionLevel, toggleResourcePermissionLevel } = usePermissions(scope);
|
const { getResourcePermissionLevel, toggleResourcePermissionLevel } = usePermissions(scope);
|
||||||
const scopedRegistry = getPermissionsForScope(scope);
|
const scopedRegistry = getPermissionsForScope(scope, isPrivate);
|
||||||
|
|
||||||
const registry = scopedRegistry || PERMISSION_REGISTRY;
|
const registry = scopedRegistry || PERMISSION_REGISTRY;
|
||||||
const isAllResources = resource === "*";
|
const isAllResources = resource === "*";
|
||||||
|
|||||||
+15
-1
@@ -44,6 +44,18 @@ const getCachedResourcePermissions = unstable_cache(
|
|||||||
{ revalidate: 3600 }
|
{ revalidate: 3600 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getCachedTeamPrivacy = unstable_cache(
|
||||||
|
async (teamId: number) => {
|
||||||
|
const team = await prisma.team.findUnique({
|
||||||
|
where: { id: teamId },
|
||||||
|
select: { isPrivate: true },
|
||||||
|
});
|
||||||
|
return team?.isPrivate ?? false;
|
||||||
|
},
|
||||||
|
["team-privacy"],
|
||||||
|
{ revalidate: 3600 }
|
||||||
|
);
|
||||||
|
|
||||||
export const generateMetadata = async () =>
|
export const generateMetadata = async () =>
|
||||||
await _generateMetadata(
|
await _generateMetadata(
|
||||||
(t) => t("roles_and_permissions"),
|
(t) => t("roles_and_permissions"),
|
||||||
@@ -69,9 +81,10 @@ const Page = async ({ searchParams }: { searchParams: Record<string, string | st
|
|||||||
|
|
||||||
roleSearchParamsCache.parse(searchParams);
|
roleSearchParamsCache.parse(searchParams);
|
||||||
|
|
||||||
const [roles, rolePermissions] = await Promise.all([
|
const [roles, rolePermissions, isPrivate] = await Promise.all([
|
||||||
getCachedTeamRoles(session.user.org.id),
|
getCachedTeamRoles(session.user.org.id),
|
||||||
getCachedResourcePermissions(session.user.id, session.user.org.id, Resource.Role),
|
getCachedResourcePermissions(session.user.id, session.user.org.id, Resource.Role),
|
||||||
|
getCachedTeamPrivacy(session.user.org.id),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// NOTE: this approach of fetching permssions per resource does not account for fall back roles.
|
// NOTE: this approach of fetching permssions per resource does not account for fall back roles.
|
||||||
@@ -108,6 +121,7 @@ const Page = async ({ searchParams }: { searchParams: Record<string, string | st
|
|||||||
}}
|
}}
|
||||||
initialSelectedRole={selectedRole}
|
initialSelectedRole={selectedRole}
|
||||||
initialSheetOpen={isSheetOpen}
|
initialSheetOpen={isSheetOpen}
|
||||||
|
isPrivate={isPrivate}
|
||||||
/>
|
/>
|
||||||
</SettingsHeader>
|
</SettingsHeader>
|
||||||
);
|
);
|
||||||
|
|||||||
+7
-1
@@ -102,6 +102,7 @@ const Page = async ({ params }: { params: Promise<{ id: string }> }) => {
|
|||||||
CustomAction.ChangeMemberRole,
|
CustomAction.ChangeMemberRole,
|
||||||
CustomAction.Remove,
|
CustomAction.Remove,
|
||||||
CustomAction.ListMembers,
|
CustomAction.ListMembers,
|
||||||
|
CustomAction.ListMembersPrivate,
|
||||||
CustomAction.Impersonate,
|
CustomAction.Impersonate,
|
||||||
],
|
],
|
||||||
fallbackRoles: {
|
fallbackRoles: {
|
||||||
@@ -120,12 +121,17 @@ const Page = async ({ params }: { params: Promise<{ id: string }> }) => {
|
|||||||
[CustomAction.Impersonate]: {
|
[CustomAction.Impersonate]: {
|
||||||
roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
|
roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
|
||||||
},
|
},
|
||||||
|
[CustomAction.ListMembersPrivate]: {
|
||||||
|
roles: fallbackRolesCanListMembers,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
// Map specific permissions to member actions
|
// Map specific permissions to member actions
|
||||||
const memberPermissions = {
|
const memberPermissions = {
|
||||||
canListMembers: permissions[CustomAction.ListMembers],
|
canListMembers: team.isPrivate
|
||||||
|
? permissions[CustomAction.ListMembersPrivate]
|
||||||
|
: permissions[CustomAction.ListMembers],
|
||||||
canInvite: permissions[CustomAction.Invite],
|
canInvite: permissions[CustomAction.Invite],
|
||||||
canChangeMemberRole: permissions[CustomAction.ChangeMemberRole],
|
canChangeMemberRole: permissions[CustomAction.ChangeMemberRole],
|
||||||
canRemove: permissions[CustomAction.Remove],
|
canRemove: permissions[CustomAction.Remove],
|
||||||
|
|||||||
+17
-2
@@ -4,6 +4,7 @@ import { cookies, headers } from "next/headers";
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
|
|
||||||
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
|
||||||
|
import { getTeamWithMembers } from "@calcom/features/ee/teams/lib/queries";
|
||||||
import type { AppFlags } from "@calcom/features/flags/config";
|
import type { AppFlags } from "@calcom/features/flags/config";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import { PermissionMapper } from "@calcom/features/pbac/domain/mappers/PermissionMapper";
|
import { PermissionMapper } from "@calcom/features/pbac/domain/mappers/PermissionMapper";
|
||||||
@@ -11,7 +12,6 @@ import { Resource, CrudAction, Scope } from "@calcom/features/pbac/domain/types/
|
|||||||
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
import { PermissionCheckService } from "@calcom/features/pbac/services/permission-check.service";
|
||||||
import { RoleService } from "@calcom/features/pbac/services/role.service";
|
import { RoleService } from "@calcom/features/pbac/services/role.service";
|
||||||
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
|
||||||
import { getTeamWithMembers } from "@calcom/features/ee/teams/lib/queries";
|
|
||||||
import { prisma } from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
|
|
||||||
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
import { buildLegacyRequest } from "@lib/buildLegacyCtx";
|
||||||
@@ -51,6 +51,19 @@ const getCachedResourcePermissions = (userId: number, teamId: number, resource:
|
|||||||
{ revalidate: 3600, tags: [`resource-permissions-${teamId}`] }
|
{ revalidate: 3600, tags: [`resource-permissions-${teamId}`] }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const getCachedTeamPrivacy = (teamId: number) =>
|
||||||
|
unstable_cache(
|
||||||
|
async () => {
|
||||||
|
const team = await prisma.team.findUnique({
|
||||||
|
where: { id: teamId },
|
||||||
|
select: { isPrivate: true },
|
||||||
|
});
|
||||||
|
return team?.isPrivate ?? false;
|
||||||
|
},
|
||||||
|
[`team-privacy-${teamId}`],
|
||||||
|
{ revalidate: 3600, tags: [`team-privacy-${teamId}`] }
|
||||||
|
);
|
||||||
|
|
||||||
const getCachedTeam = (teamId: string, userId: number) =>
|
const getCachedTeam = (teamId: string, userId: number) =>
|
||||||
unstable_cache(
|
unstable_cache(
|
||||||
async () => {
|
async () => {
|
||||||
@@ -111,9 +124,10 @@ const Page = async ({
|
|||||||
|
|
||||||
roleSearchParamsCache.parse(searchParams);
|
roleSearchParamsCache.parse(searchParams);
|
||||||
|
|
||||||
const [roles, rolePermissions] = await Promise.all([
|
const [roles, rolePermissions, isPrivate] = await Promise.all([
|
||||||
getCachedTeamRoles(team.id)(),
|
getCachedTeamRoles(team.id)(),
|
||||||
getCachedResourcePermissions(session.user.id, team.id, Resource.Role)(),
|
getCachedResourcePermissions(session.user.id, team.id, Resource.Role)(),
|
||||||
|
getCachedTeamPrivacy(team.id)(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// NOTE: this approach of fetching permssions per resource does not account for fall back roles.
|
// NOTE: this approach of fetching permssions per resource does not account for fall back roles.
|
||||||
@@ -153,6 +167,7 @@ const Page = async ({
|
|||||||
initialSelectedRole={selectedRole}
|
initialSelectedRole={selectedRole}
|
||||||
initialSheetOpen={isSheetOpen}
|
initialSheetOpen={isSheetOpen}
|
||||||
scope={Scope.Team}
|
scope={Scope.Team}
|
||||||
|
isPrivate={isPrivate}
|
||||||
/>
|
/>
|
||||||
</SettingsHeader>
|
</SettingsHeader>
|
||||||
);
|
);
|
||||||
|
|||||||
+7
-1
@@ -70,6 +70,7 @@ const Page = async () => {
|
|||||||
userRole: session.user.org.role,
|
userRole: session.user.org.role,
|
||||||
actions: [
|
actions: [
|
||||||
CustomAction.ListMembers,
|
CustomAction.ListMembers,
|
||||||
|
CustomAction.ListMembersPrivate,
|
||||||
CustomAction.Invite,
|
CustomAction.Invite,
|
||||||
CustomAction.ChangeMemberRole,
|
CustomAction.ChangeMemberRole,
|
||||||
CustomAction.Remove,
|
CustomAction.Remove,
|
||||||
@@ -79,6 +80,9 @@ const Page = async () => {
|
|||||||
[CustomAction.ListMembers]: {
|
[CustomAction.ListMembers]: {
|
||||||
roles: fallbackRolesThatCanSeeMembers,
|
roles: fallbackRolesThatCanSeeMembers,
|
||||||
},
|
},
|
||||||
|
[CustomAction.ListMembersPrivate]: {
|
||||||
|
roles: fallbackRolesThatCanSeeMembers,
|
||||||
|
},
|
||||||
[CustomAction.Invite]: {
|
[CustomAction.Invite]: {
|
||||||
roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
|
roles: [MembershipRole.ADMIN, MembershipRole.OWNER],
|
||||||
},
|
},
|
||||||
@@ -96,7 +100,9 @@ const Page = async () => {
|
|||||||
|
|
||||||
// Map specific permissions to member actions
|
// Map specific permissions to member actions
|
||||||
const memberPermissions = {
|
const memberPermissions = {
|
||||||
canListMembers: permissions[CustomAction.ListMembers],
|
canListMembers: org.isPrivate
|
||||||
|
? permissions[CustomAction.ListMembersPrivate]
|
||||||
|
: permissions[CustomAction.ListMembers],
|
||||||
canInvite: permissions[CustomAction.Invite],
|
canInvite: permissions[CustomAction.Invite],
|
||||||
canChangeMemberRole: permissions[CustomAction.ChangeMemberRole],
|
canChangeMemberRole: permissions[CustomAction.ChangeMemberRole],
|
||||||
canRemove: permissions[CustomAction.Remove],
|
canRemove: permissions[CustomAction.Remove],
|
||||||
|
|||||||
@@ -0,0 +1,213 @@
|
|||||||
|
import { NextRequest } from "next/server";
|
||||||
|
import { describe, test, expect, vi, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
import { CalendarCacheEventService } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService";
|
||||||
|
|
||||||
|
vi.mock("next/server", () => ({
|
||||||
|
NextRequest: class MockNextRequest {
|
||||||
|
url: string;
|
||||||
|
method: string;
|
||||||
|
nextUrl: { searchParams: URLSearchParams };
|
||||||
|
private _headers: Map<string, string>;
|
||||||
|
|
||||||
|
constructor(url: string, options: { method?: string } = {}) {
|
||||||
|
this.url = url;
|
||||||
|
this.method = options.method || "GET";
|
||||||
|
this._headers = new Map();
|
||||||
|
this.nextUrl = { searchParams: new URLSearchParams(url.split("?")[1] || "") };
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
get: (key: string): string | null => this._headers.get(key.toLowerCase()) || null,
|
||||||
|
set: (key: string, value: string): void => {
|
||||||
|
this._headers.set(key.toLowerCase(), value);
|
||||||
|
},
|
||||||
|
has: (key: string): boolean => this._headers.has(key.toLowerCase()),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
NextResponse: {
|
||||||
|
json: vi.fn((body, init) => ({
|
||||||
|
json: vi.fn().mockResolvedValue(body),
|
||||||
|
status: init?.status || 200,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService");
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository");
|
||||||
|
vi.mock("@calcom/lib/logger", () => ({
|
||||||
|
default: {
|
||||||
|
getSubLogger: vi.fn(() => ({
|
||||||
|
info: vi.fn(),
|
||||||
|
error: vi.fn(),
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
vi.mock("@calcom/lib/server/perfObserver", () => ({
|
||||||
|
performance: {
|
||||||
|
mark: vi.fn(),
|
||||||
|
measure: vi.fn(),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
vi.mock("@sentry/nextjs", () => ({
|
||||||
|
wrapApiHandlerWithSentry: vi.fn((handler) => handler),
|
||||||
|
captureException: vi.fn(),
|
||||||
|
}));
|
||||||
|
vi.mock("@calcom/lib/server/getServerErrorFromUnknown", () => ({
|
||||||
|
getServerErrorFromUnknown: vi.fn((error) => ({
|
||||||
|
message: error instanceof Error ? error.message : "Unknown error",
|
||||||
|
statusCode: 500,
|
||||||
|
url: "test-url",
|
||||||
|
method: "GET",
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
vi.mock("../../defaultResponderForAppDir", () => ({
|
||||||
|
defaultResponderForAppDir: vi.fn((handler) => handler),
|
||||||
|
}));
|
||||||
|
vi.mock("@calcom/prisma", () => ({
|
||||||
|
prisma: {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockCalendarCacheEventService = vi.mocked(CalendarCacheEventService);
|
||||||
|
|
||||||
|
describe("/api/cron/calendar-subscriptions-cleanup", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.stubEnv("CRON_API_KEY", "test-cron-key");
|
||||||
|
vi.stubEnv("CRON_SECRET", "test-cron-secret");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Authentication", () => {
|
||||||
|
test("should return 403 when no API key is provided", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(403);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Forbidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return 403 when invalid API key is provided", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "invalid-key");
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(403);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Forbidden");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should accept CRON_API_KEY in authorization header", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockResolvedValue(undefined);
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(mockCleanupStaleCache).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should accept CRON_SECRET as Bearer token", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "Bearer test-cron-secret");
|
||||||
|
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockResolvedValue(undefined);
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(mockCleanupStaleCache).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should accept API key as query parameter", async () => {
|
||||||
|
const request = new NextRequest(
|
||||||
|
"http://localhost/api/cron/calendar-subscriptions-cleanup?apiKey=test-cron-key"
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockResolvedValue(undefined);
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(mockCleanupStaleCache).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Cleanup functionality", () => {
|
||||||
|
test("should successfully cleanup stale cache", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockResolvedValue(undefined);
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.ok).toBe(true);
|
||||||
|
expect(mockCleanupStaleCache).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle cleanup errors gracefully", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockError = new Error("Database connection failed");
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockRejectedValue(mockError);
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Database connection failed");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle non-Error exceptions", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockRejectedValue("String error");
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Unknown error");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Service instantiation", () => {
|
||||||
|
test("should instantiate CalendarCacheEventService with correct dependencies", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions-cleanup");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockCleanupStaleCache = vi.fn().mockResolvedValue(undefined);
|
||||||
|
mockCalendarCacheEventService.prototype.cleanupStaleCache = mockCleanupStaleCache;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(mockCalendarCacheEventService).toHaveBeenCalledWith({
|
||||||
|
calendarCacheEventRepository: expect.any(Object),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
import type { NextRequest } from "next/server";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
||||||
|
import { CalendarCacheEventService } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService";
|
||||||
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cron webhook
|
||||||
|
* Cleanup stale calendar cache
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function getHandler(request: NextRequest) {
|
||||||
|
const apiKey = request.headers.get("authorization") || request.nextUrl.searchParams.get("apiKey");
|
||||||
|
|
||||||
|
if (![process.env.CRON_API_KEY, `Bearer ${process.env.CRON_SECRET}`].includes(`${apiKey}`)) {
|
||||||
|
return NextResponse.json({ message: "Forbidden" }, { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// instantiate dependencies
|
||||||
|
const calendarCacheEventRepository = new CalendarCacheEventRepository(prisma);
|
||||||
|
const calendarCacheEventService = new CalendarCacheEventService({
|
||||||
|
calendarCacheEventRepository,
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await calendarCacheEventService.cleanupStaleCache();
|
||||||
|
return NextResponse.json({ ok: true });
|
||||||
|
} catch (e: unknown) {
|
||||||
|
const message = e instanceof Error ? e.message : "Unknown error";
|
||||||
|
console.error(`[calendar-subscriptions-cleanup] ${message}:`, e);
|
||||||
|
return NextResponse.json({ message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GET = defaultResponderForAppDir(getHandler);
|
||||||
@@ -0,0 +1,229 @@
|
|||||||
|
import { NextRequest } from "next/server";
|
||||||
|
import { describe, test, expect, vi, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||||
|
|
||||||
|
vi.mock("next/server", () => ({
|
||||||
|
NextRequest: class MockNextRequest {
|
||||||
|
url: string;
|
||||||
|
method: string;
|
||||||
|
nextUrl: { searchParams: URLSearchParams };
|
||||||
|
private _headers: Map<string, string>;
|
||||||
|
|
||||||
|
constructor(url: string, options: { method?: string } = {}) {
|
||||||
|
this.url = url;
|
||||||
|
this.method = options.method || "GET";
|
||||||
|
this._headers = new Map();
|
||||||
|
this.nextUrl = { searchParams: new URLSearchParams(url.split("?")[1] || "") };
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
get: (key: string): string | null => this._headers.get(key.toLowerCase()) || null,
|
||||||
|
set: (key: string, value: string): void => {
|
||||||
|
this._headers.set(key.toLowerCase(), value);
|
||||||
|
},
|
||||||
|
has: (key: string): boolean => this._headers.has(key.toLowerCase()),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
NextResponse: {
|
||||||
|
json: vi.fn((body, init) => ({
|
||||||
|
json: vi.fn().mockResolvedValue(body),
|
||||||
|
status: init?.status || 200,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/CalendarSubscriptionService");
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService");
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/sync/CalendarSyncService");
|
||||||
|
vi.mock("@calcom/prisma", () => ({
|
||||||
|
prisma: {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockCalendarSubscriptionService = vi.mocked(CalendarSubscriptionService);
|
||||||
|
|
||||||
|
describe("/api/cron/calendar-subscriptions", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetModules();
|
||||||
|
vi.stubEnv("CRON_API_KEY", "test-cron-key");
|
||||||
|
vi.stubEnv("CRON_SECRET", "test-cron-secret");
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Authentication", () => {
|
||||||
|
test("should return 403 when no API key is provided", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(403);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Forbiden");
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
test("should return 403 when invalid API key is provided", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "invalid-key");
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(403);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Forbiden");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should accept valid API key", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Feature flag checks", () => {
|
||||||
|
test("should return early when cache AND sync are disabled", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn();
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.ok).toBe(true);
|
||||||
|
expect(mockCheckForNewSubscriptions).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should proceed when both cache and sync are enabled", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.ok).toBe(true);
|
||||||
|
expect(mockCheckForNewSubscriptions).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Subscription checking functionality", () => {
|
||||||
|
test("should successfully check for new subscriptions", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.ok).toBe(true);
|
||||||
|
expect(mockCheckForNewSubscriptions).toHaveBeenCalledOnce();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle subscription checking errors gracefully", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockError = new Error("Subscription service unavailable");
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn().mockRejectedValue(mockError);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Subscription service unavailable");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle non-Error exceptions", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn().mockRejectedValue("String error");
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
const response = await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Unknown error");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Service instantiation", () => {
|
||||||
|
test("should instantiate all services with correct dependencies", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/cron/calendar-subscriptions");
|
||||||
|
request.headers.set("authorization", "test-cron-key");
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockCheckForNewSubscriptions = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.checkForNewSubscriptions = mockCheckForNewSubscriptions;
|
||||||
|
|
||||||
|
const { GET } = await import("../route");
|
||||||
|
await GET(request, { params: Promise.resolve({}) });
|
||||||
|
|
||||||
|
expect(mockCalendarSubscriptionService).toHaveBeenCalledWith({
|
||||||
|
adapterFactory: expect.any(Object),
|
||||||
|
selectedCalendarRepository: expect.any(Object),
|
||||||
|
featuresRepository: expect.any(Object),
|
||||||
|
calendarSyncService: expect.any(Object),
|
||||||
|
calendarCacheEventService: expect.any(Object),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
import type { NextRequest } from "next/server";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
import { DefaultAdapterFactory } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||||
|
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||||
|
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
||||||
|
import { CalendarCacheEventService } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService";
|
||||||
|
import { CalendarSyncService } from "@calcom/features/calendar-subscription/lib/sync/CalendarSyncService";
|
||||||
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
|
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
||||||
|
import { BookingRepository } from "@calcom/lib/server/repository/booking";
|
||||||
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cron webhook
|
||||||
|
* Checks for new calendar subscriptions (rollouts)
|
||||||
|
*
|
||||||
|
* @param request
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
async function getHandler(request: NextRequest) {
|
||||||
|
const apiKey = request.headers.get("authorization") || request.nextUrl.searchParams.get("apiKey");
|
||||||
|
|
||||||
|
if (![process.env.CRON_API_KEY, `Bearer ${process.env.CRON_SECRET}`].includes(`${apiKey}`)) {
|
||||||
|
return NextResponse.json({ message: "Forbiden" }, { status: 403 });
|
||||||
|
}
|
||||||
|
|
||||||
|
// instantiate dependencies
|
||||||
|
const bookingRepository = new BookingRepository(prisma);
|
||||||
|
const calendarSyncService = new CalendarSyncService({
|
||||||
|
bookingRepository,
|
||||||
|
});
|
||||||
|
const calendarCacheEventRepository = new CalendarCacheEventRepository(prisma);
|
||||||
|
const calendarCacheEventService = new CalendarCacheEventService({
|
||||||
|
calendarCacheEventRepository,
|
||||||
|
});
|
||||||
|
|
||||||
|
const calendarSubscriptionService = new CalendarSubscriptionService({
|
||||||
|
adapterFactory: new DefaultAdapterFactory(),
|
||||||
|
selectedCalendarRepository: new SelectedCalendarRepository(prisma),
|
||||||
|
featuresRepository: new FeaturesRepository(prisma),
|
||||||
|
calendarSyncService,
|
||||||
|
calendarCacheEventService,
|
||||||
|
});
|
||||||
|
|
||||||
|
// are features globally enabled
|
||||||
|
const [isCacheEnabled, isSyncEnabled] = await Promise.all([
|
||||||
|
calendarSubscriptionService.isCacheEnabled(),
|
||||||
|
calendarSubscriptionService.isSyncEnabled(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isCacheEnabled && !isSyncEnabled) {
|
||||||
|
return NextResponse.json({ ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await calendarSubscriptionService.checkForNewSubscriptions();
|
||||||
|
return NextResponse.json({ ok: true });
|
||||||
|
} catch (e) {
|
||||||
|
const message = e instanceof Error ? e.message : "Unknown error";
|
||||||
|
return NextResponse.json({ message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GET = defaultResponderForAppDir(getHandler);
|
||||||
@@ -1,5 +1,3 @@
|
|||||||
import { wrapApiHandlerWithSentry } from "@sentry/nextjs";
|
|
||||||
import { captureException } from "@sentry/nextjs";
|
|
||||||
import type { Params } from "app/_types";
|
import type { Params } from "app/_types";
|
||||||
import { ApiError } from "next/dist/server/api-utils";
|
import { ApiError } from "next/dist/server/api-utils";
|
||||||
import type { NextRequest } from "next/server";
|
import type { NextRequest } from "next/server";
|
||||||
@@ -21,10 +19,13 @@ export const defaultResponderForAppDir = <T extends NextResponse | Response = Ne
|
|||||||
let ok = false;
|
let ok = false;
|
||||||
try {
|
try {
|
||||||
performance.mark("Start");
|
performance.mark("Start");
|
||||||
|
let result: T | undefined;
|
||||||
const result = endpointRoute
|
if (process.env.NODE_ENV === "development" || !endpointRoute) {
|
||||||
? await wrapApiHandlerWithSentry(async () => await handler(req, { params }), endpointRoute)()
|
result = await handler(req, { params });
|
||||||
: await handler(req, { params });
|
} else {
|
||||||
|
const { wrapApiHandlerWithSentry } = await import("@sentry/nextjs");
|
||||||
|
result = await wrapApiHandlerWithSentry(async () => await handler(req, { params }), endpointRoute)();
|
||||||
|
}
|
||||||
|
|
||||||
ok = true;
|
ok = true;
|
||||||
if (result) {
|
if (result) {
|
||||||
@@ -49,6 +50,7 @@ export const defaultResponderForAppDir = <T extends NextResponse | Response = Ne
|
|||||||
// Don't report 400-499 errors to Sentry/console
|
// Don't report 400-499 errors to Sentry/console
|
||||||
if (!(serverError.statusCode >= 400 && serverError.statusCode < 500)) {
|
if (!(serverError.statusCode >= 400 && serverError.statusCode < 500)) {
|
||||||
console.error(serverError);
|
console.error(serverError);
|
||||||
|
const { captureException } = await import("@sentry/nextjs");
|
||||||
captureException(error);
|
captureException(error);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,315 @@
|
|||||||
|
import { NextRequest } from "next/server";
|
||||||
|
import { describe, test, expect, vi, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||||
|
|
||||||
|
vi.mock("next/server", () => ({
|
||||||
|
NextRequest: class MockNextRequest {
|
||||||
|
url: string;
|
||||||
|
method: string;
|
||||||
|
nextUrl: { searchParams: URLSearchParams };
|
||||||
|
private _headers: Map<string, string>;
|
||||||
|
|
||||||
|
constructor(url: string, options: { method?: string } = {}) {
|
||||||
|
this.url = url;
|
||||||
|
this.method = options.method || "POST";
|
||||||
|
this._headers = new Map();
|
||||||
|
this.nextUrl = { searchParams: new URLSearchParams(url.split("?")[1] || "") };
|
||||||
|
}
|
||||||
|
|
||||||
|
headers = {
|
||||||
|
get: (key: string): string | null => this._headers.get(key.toLowerCase()) || null,
|
||||||
|
set: (key: string, value: string): void => {
|
||||||
|
this._headers.set(key.toLowerCase(), value);
|
||||||
|
},
|
||||||
|
has: (key: string): boolean => this._headers.has(key.toLowerCase()),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
NextResponse: {
|
||||||
|
json: vi.fn((body, init) => ({
|
||||||
|
json: vi.fn().mockResolvedValue(body),
|
||||||
|
status: init?.status || 200,
|
||||||
|
})),
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/CalendarSubscriptionService");
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService");
|
||||||
|
vi.mock("@calcom/features/calendar-subscription/lib/sync/CalendarSyncService");
|
||||||
|
vi.mock("@calcom/prisma", () => ({
|
||||||
|
prisma: {},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockCalendarSubscriptionService = vi.mocked(CalendarSubscriptionService);
|
||||||
|
|
||||||
|
describe("/api/webhooks/calendar-subscription/[provider]", () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
vi.clearAllMocks();
|
||||||
|
vi.resetModules();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Provider validation", () => {
|
||||||
|
test("should accept google_calendar provider", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(mockProcessWebhook).toHaveBeenCalledWith("google_calendar", request);
|
||||||
|
}, 10000);
|
||||||
|
|
||||||
|
test("should accept office365_calendar provider", async () => {
|
||||||
|
const request = new NextRequest(
|
||||||
|
"http://localhost/api/webhooks/calendar-subscription/office365_calendar",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "office365_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
expect(mockProcessWebhook).toHaveBeenCalledWith("office365_calendar", request);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should reject unsupported provider", async () => {
|
||||||
|
const request = new NextRequest(
|
||||||
|
"http://localhost/api/webhooks/calendar-subscription/unsupported_calendar",
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "unsupported_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(400);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Unsupported provider");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Feature flag handling", () => {
|
||||||
|
test("should return 200 when neither cache nor sync is enabled", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn();
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("No cache or sync enabled");
|
||||||
|
expect(mockProcessWebhook).not.toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should process webhook when cache is enabled", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Webhook processed");
|
||||||
|
expect(mockProcessWebhook).toHaveBeenCalledWith("google_calendar", request);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should process webhook when sync is enabled", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockProcessWebhook = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Webhook processed");
|
||||||
|
expect(mockProcessWebhook).toHaveBeenCalledWith("google_calendar", request);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should process webhook when both cache and sync are enabled", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockProcessWebhook = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(200);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Webhook processed");
|
||||||
|
expect(mockProcessWebhook).toHaveBeenCalledWith("google_calendar", request);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Error handling", () => {
|
||||||
|
test("should handle webhook processing errors gracefully", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockError = new Error("Webhook validation failed");
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn().mockRejectedValue(mockError);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Webhook validation failed");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle non-Error exceptions", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn().mockRejectedValue("String error");
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Unknown error");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle feature flag check errors", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockError = new Error("Feature flag service unavailable");
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockRejectedValue(mockError);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
const response = await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(response.status).toBe(500);
|
||||||
|
const body = await response.json();
|
||||||
|
expect(body.message).toBe("Feature flag service unavailable");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Service instantiation", () => {
|
||||||
|
test("should instantiate all services with correct dependencies", async () => {
|
||||||
|
const request = new NextRequest("http://localhost/api/webhooks/calendar-subscription/google_calendar", {
|
||||||
|
method: "POST",
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockIsCacheEnabled = vi.fn().mockResolvedValue(true);
|
||||||
|
const mockIsSyncEnabled = vi.fn().mockResolvedValue(false);
|
||||||
|
const mockProcessWebhook = vi.fn().mockResolvedValue(undefined);
|
||||||
|
|
||||||
|
mockCalendarSubscriptionService.prototype.isCacheEnabled = mockIsCacheEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.isSyncEnabled = mockIsSyncEnabled;
|
||||||
|
mockCalendarSubscriptionService.prototype.processWebhook = mockProcessWebhook;
|
||||||
|
|
||||||
|
const { POST } = await import("../route");
|
||||||
|
await POST(request, {
|
||||||
|
params: Promise.resolve({ provider: "google_calendar" }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(mockCalendarSubscriptionService).toHaveBeenCalledWith({
|
||||||
|
adapterFactory: expect.any(Object),
|
||||||
|
selectedCalendarRepository: expect.any(Object),
|
||||||
|
featuresRepository: expect.any(Object),
|
||||||
|
calendarSyncService: expect.any(Object),
|
||||||
|
calendarCacheEventService: expect.any(Object),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -0,0 +1,87 @@
|
|||||||
|
import type { Params } from "app/_types";
|
||||||
|
import type { NextRequest } from "next/server";
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
|
||||||
|
import type { CalendarSubscriptionProvider } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||||
|
import { DefaultAdapterFactory } from "@calcom/features/calendar-subscription/adapters/AdaptersFactory";
|
||||||
|
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||||
|
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
||||||
|
import { CalendarCacheEventService } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService";
|
||||||
|
import { CalendarSyncService } from "@calcom/features/calendar-subscription/lib/sync/CalendarSyncService";
|
||||||
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import { SelectedCalendarRepository } from "@calcom/lib/server/repository/SelectedCalendarRepository";
|
||||||
|
import { BookingRepository } from "@calcom/lib/server/repository/booking";
|
||||||
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import { defaultResponderForAppDir } from "@calcom/web/app/api/defaultResponderForAppDir";
|
||||||
|
|
||||||
|
const log = logger.getSubLogger({ prefix: ["calendar-webhook"] });
|
||||||
|
|
||||||
|
function extractAndValidateProviderFromParams(params: Params): CalendarSubscriptionProvider | null {
|
||||||
|
if (!("provider" in params)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const { provider } = params;
|
||||||
|
if (provider === "google_calendar" || provider === "office365_calendar") {
|
||||||
|
return provider;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles incoming POST requests for calendar webhooks.
|
||||||
|
* It processes the webhook based on the calendar provider specified in the URL.
|
||||||
|
* If the provider is unsupported, it returns a 400 response.
|
||||||
|
* If the webhook is processed successfully, it returns a 200 response.
|
||||||
|
* In case of errors during processing, it returns a 500 response with the error message.
|
||||||
|
* @param {NextRequest} request - The incoming request object.
|
||||||
|
* @param {Object} context - The context object containing route parameters.
|
||||||
|
* @param {Promise<Params>} context.params - A promise that resolves to the route parameters.
|
||||||
|
* @returns {Promise<NextResponse>} - A promise that resolves to the response object.
|
||||||
|
*/
|
||||||
|
async function postHandler(request: NextRequest, ctx: { params: Promise<Params> }) {
|
||||||
|
const providerFromParams = extractAndValidateProviderFromParams(await ctx.params);
|
||||||
|
if (!providerFromParams) {
|
||||||
|
return NextResponse.json({ message: "Unsupported provider" }, { status: 400 });
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// instantiate dependencies
|
||||||
|
const bookingRepository = new BookingRepository(prisma);
|
||||||
|
const calendarSyncService = new CalendarSyncService({
|
||||||
|
bookingRepository,
|
||||||
|
});
|
||||||
|
const calendarCacheEventRepository = new CalendarCacheEventRepository(prisma);
|
||||||
|
const calendarCacheEventService = new CalendarCacheEventService({
|
||||||
|
calendarCacheEventRepository,
|
||||||
|
});
|
||||||
|
|
||||||
|
const calendarSubscriptionService = new CalendarSubscriptionService({
|
||||||
|
adapterFactory: new DefaultAdapterFactory(),
|
||||||
|
selectedCalendarRepository: new SelectedCalendarRepository(prisma),
|
||||||
|
featuresRepository: new FeaturesRepository(prisma),
|
||||||
|
calendarSyncService,
|
||||||
|
calendarCacheEventService,
|
||||||
|
});
|
||||||
|
|
||||||
|
// are features globally enabled
|
||||||
|
const [isCacheEnabled, isSyncEnabled] = await Promise.all([
|
||||||
|
calendarSubscriptionService.isCacheEnabled(),
|
||||||
|
calendarSubscriptionService.isSyncEnabled(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
if (!isCacheEnabled && !isSyncEnabled) {
|
||||||
|
log.debug("No cache or sync enabled");
|
||||||
|
return NextResponse.json({ message: "No cache or sync enabled" }, { status: 200 });
|
||||||
|
}
|
||||||
|
|
||||||
|
await calendarSubscriptionService.processWebhook(providerFromParams, request);
|
||||||
|
return NextResponse.json({ message: "Webhook processed" }, { status: 200 });
|
||||||
|
} catch (error) {
|
||||||
|
log.error("Error processing webhook", { error });
|
||||||
|
const message = error instanceof Error ? error.message : "Unknown error";
|
||||||
|
return NextResponse.json({ message }, { status: 500 });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const POST = defaultResponderForAppDir(postHandler);
|
||||||
Regular → Executable
+2
-2
@@ -23,9 +23,9 @@ try {
|
|||||||
"*/5 * * * * *",
|
"*/5 * * * * *",
|
||||||
async function () {
|
async function () {
|
||||||
await Promise.allSettled([
|
await Promise.allSettled([
|
||||||
fetchCron("/calendar-cache/cron"),
|
fetchCron("/cron/calendar-subscriptions"),
|
||||||
|
// fetchCron("/calendar-cache/cron"),
|
||||||
// fetchCron("/cron/calVideoNoShowWebhookTriggers"),
|
// fetchCron("/cron/calVideoNoShowWebhookTriggers"),
|
||||||
//
|
|
||||||
fetchCron("/tasks/cron"),
|
fetchCron("/tasks/cron"),
|
||||||
]);
|
]);
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -5,13 +5,13 @@ import {
|
|||||||
getOrganizationSettings,
|
getOrganizationSettings,
|
||||||
getVerifiedDomain,
|
getVerifiedDomain,
|
||||||
} from "@calcom/features/ee/organizations/lib/orgSettings";
|
} from "@calcom/features/ee/organizations/lib/orgSettings";
|
||||||
|
import { getTeamWithMembers } from "@calcom/features/ee/teams/lib/queries";
|
||||||
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import { IS_CALCOM } from "@calcom/lib/constants";
|
import { IS_CALCOM } from "@calcom/lib/constants";
|
||||||
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
import { getUserAvatarUrl } from "@calcom/lib/getAvatarUrl";
|
||||||
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
|
import { getBookerBaseUrlSync } from "@calcom/lib/getBookerUrl/client";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
import { markdownToSafeHTML } from "@calcom/lib/markdownToSafeHTML";
|
||||||
import { getTeamWithMembers } from "@calcom/features/ee/teams/lib/queries";
|
|
||||||
import slugify from "@calcom/lib/slugify";
|
import slugify from "@calcom/lib/slugify";
|
||||||
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
|
import { stripMarkdown } from "@calcom/lib/stripMarkdown";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@calcom/web",
|
"name": "@calcom/web",
|
||||||
"version": "5.7.5",
|
"version": "5.7.7",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"analyze": "ANALYZE=true next build",
|
"analyze": "ANALYZE=true next build",
|
||||||
@@ -138,7 +138,7 @@
|
|||||||
"react-select": "^5.7.0",
|
"react-select": "^5.7.0",
|
||||||
"react-timezone-select": "^1.4.0",
|
"react-timezone-select": "^1.4.0",
|
||||||
"react-turnstile": "^1.1.3",
|
"react-turnstile": "^1.1.3",
|
||||||
"react-use-intercom": "1.5.1",
|
"react-use-intercom": "^5.5.0",
|
||||||
"recoil": "^0.7.7",
|
"recoil": "^0.7.7",
|
||||||
"remove-markdown": "^0.5.0",
|
"remove-markdown": "^0.5.0",
|
||||||
"retell-client-js-sdk": "^2.0.0",
|
"retell-client-js-sdk": "^2.0.0",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ async function handler(req: NextApiRequest & { userId?: number }) {
|
|||||||
const userIp = getIP(req);
|
const userIp = getIP(req);
|
||||||
|
|
||||||
await checkRateLimitAndThrowError({
|
await checkRateLimitAndThrowError({
|
||||||
rateLimitingType: "core",
|
rateLimitingType: "instantMeeting",
|
||||||
identifier: `instant.event-${piiHasher.hash(userIp)}`,
|
identifier: `instant.event-${piiHasher.hash(userIp)}`,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import stripe from "@calcom/features/ee/payments/server/stripe";
|
|||||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||||
import { HttpError as HttpCode } from "@calcom/lib/http-error";
|
import { HttpError as HttpCode } from "@calcom/lib/http-error";
|
||||||
import prisma from "@calcom/prisma";
|
import { prisma } from "@calcom/prisma";
|
||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
api: {
|
api: {
|
||||||
@@ -27,7 +27,10 @@ const handleSubscriptionUpdate = async (event: Stripe.Event) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!app) {
|
if (!app) {
|
||||||
throw new HttpCode({ statusCode: 202, message: "Received and discarded" });
|
throw new HttpCode({
|
||||||
|
statusCode: 202,
|
||||||
|
message: `No credential found with subscription ID ${subscription.id}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
await prisma.credential.update({
|
await prisma.credential.update({
|
||||||
@@ -51,7 +54,10 @@ const handleSubscriptionDeleted = async (event: Stripe.Event) => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
if (!app) {
|
if (!app) {
|
||||||
throw new HttpCode({ statusCode: 202, message: "Received and discarded" });
|
throw new HttpCode({
|
||||||
|
statusCode: 202,
|
||||||
|
message: `No credential found with subscription ID ${subscription.id}`,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// should we delete the credential here rather than marking as inactive?
|
// should we delete the credential here rather than marking as inactive?
|
||||||
@@ -103,7 +109,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||||||
}
|
}
|
||||||
} catch (_err) {
|
} catch (_err) {
|
||||||
const err = getErrorFromUnknown(_err);
|
const err = getErrorFromUnknown(_err);
|
||||||
console.error(`Webhook Error: ${err.message}`);
|
if (!err.message.includes("No credential found with subscription ID")) {
|
||||||
|
console.error(`Webhook Error: ${err.message}`);
|
||||||
|
}
|
||||||
res.status(err.statusCode ?? 500).send({
|
res.status(err.statusCode ?? 500).send({
|
||||||
message: err.message,
|
message: err.message,
|
||||||
stack: IS_PRODUCTION ? undefined : err.stack,
|
stack: IS_PRODUCTION ? undefined : err.stack,
|
||||||
|
|||||||
@@ -15,7 +15,6 @@ export async function expectInvitationEmailToBeReceived(
|
|||||||
) {
|
) {
|
||||||
if (!emails) return null;
|
if (!emails) return null;
|
||||||
|
|
||||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
|
||||||
await page.waitForTimeout(2000);
|
await page.waitForTimeout(2000);
|
||||||
const receivedEmails = await getEmailsReceivedByUser({ emails, userEmail });
|
const receivedEmails = await getEmailsReceivedByUser({ emails, userEmail });
|
||||||
expect(receivedEmails?.total).toBe(1);
|
expect(receivedEmails?.total).toBe(1);
|
||||||
@@ -36,5 +35,5 @@ export async function expectExistingUserToBeInvitedToOrganization(
|
|||||||
userEmail: string,
|
userEmail: string,
|
||||||
subject?: string | null
|
subject?: string | null
|
||||||
) {
|
) {
|
||||||
return expectInvitationEmailToBeReceived(page, emails, userEmail, subject, "settings/team");
|
return expectInvitationEmailToBeReceived(page, emails, userEmail, subject, "teams?token");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,7 @@ test.describe("Team", () => {
|
|||||||
page.locator(`[data-testid="email-${invitedUserEmail.replace("@", "")}-pending"]`)
|
page.locator(`[data-testid="email-${invitedUserEmail.replace("@", "")}-pending"]`)
|
||||||
).toHaveCount(1);
|
).toHaveCount(1);
|
||||||
|
|
||||||
// eslint-disable-next-line playwright/no-conditional-in-test
|
expect(inviteLink).toBeTruthy();
|
||||||
if (!inviteLink) return null;
|
|
||||||
|
|
||||||
// Follow invite link to new window
|
// Follow invite link to new window
|
||||||
const context = await browser.newContext();
|
const context = await browser.newContext();
|
||||||
@@ -103,7 +102,7 @@ test.describe("Team", () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test("Invitation (verified)", async ({ browser, page, users, emails }) => {
|
test("Invitation (verified)", async ({ page, users, emails }) => {
|
||||||
const t = await localize("en");
|
const t = await localize("en");
|
||||||
const teamOwner = await users.create({ name: `team-owner-${Date.now()}` }, { hasTeam: true });
|
const teamOwner = await users.create({ name: `team-owner-${Date.now()}` }, { hasTeam: true });
|
||||||
const { team } = await teamOwner.getFirstTeamMembership();
|
const { team } = await teamOwner.getFirstTeamMembership();
|
||||||
@@ -192,4 +191,128 @@ test.describe("Team", () => {
|
|||||||
});
|
});
|
||||||
expect(parentEventType?.children.find((et) => et.userId === invitedMember.id)).toBeTruthy();
|
expect(parentEventType?.children.find((et) => et.userId === invitedMember.id)).toBeTruthy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("Auto-accept invitation for existing user", async ({ browser, page, users, emails }) => {
|
||||||
|
const t = await localize("en");
|
||||||
|
const teamOwner = await users.create({ name: "Invited User" }, { hasTeam: true });
|
||||||
|
const { team } = await teamOwner.getFirstTeamMembership();
|
||||||
|
const invitedUser = await users.create({
|
||||||
|
email: `invited-user-${Date.now()}@example.com`,
|
||||||
|
name: "Invited User",
|
||||||
|
});
|
||||||
|
|
||||||
|
await teamOwner.apiLogin();
|
||||||
|
await page.goto(`/settings/teams/${team.id}/members`);
|
||||||
|
|
||||||
|
let inviteLink: string;
|
||||||
|
|
||||||
|
await test.step("Send invitation to existing user", async () => {
|
||||||
|
await page.waitForLoadState("domcontentloaded");
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.getByTestId("new-member-button").click();
|
||||||
|
await page.locator('input[name="inviteUser"]').fill(invitedUser.email);
|
||||||
|
await page.getByText(t("send_invite")).click();
|
||||||
|
|
||||||
|
inviteLink = await expectInvitationEmailToBeReceived(
|
||||||
|
page,
|
||||||
|
emails,
|
||||||
|
invitedUser.email,
|
||||||
|
`${teamOwner.name} invited you to join the team ${team.name} on Cal.com`,
|
||||||
|
"teams?token"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(inviteLink).toContain("autoAccept=true");
|
||||||
|
|
||||||
|
const membership = await prisma.membership.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: invitedUser.id,
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(membership?.accepted).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step("Auto-accept invitation by clicking link", async () => {
|
||||||
|
const [secondContext, secondPage] = await invitedUser.apiLoginOnNewBrowser(browser);
|
||||||
|
|
||||||
|
await secondPage.goto(inviteLink);
|
||||||
|
|
||||||
|
await expect(secondPage.getByText("Successfully joined")).toBeVisible();
|
||||||
|
|
||||||
|
const membership = await prisma.membership.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: invitedUser.id,
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(membership?.accepted).toBe(true);
|
||||||
|
|
||||||
|
await secondPage.close();
|
||||||
|
await secondContext.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("Error when wrong user tries to use invitation link", async ({ browser, page, users, emails }) => {
|
||||||
|
const t = await localize("en");
|
||||||
|
const teamOwner = await users.create({ name: "Wrong User" }, { hasTeam: true });
|
||||||
|
const { team } = await teamOwner.getFirstTeamMembership();
|
||||||
|
const invitedUser = await users.create({
|
||||||
|
email: `invited-user-${Date.now()}@example.com`,
|
||||||
|
name: "Invited User",
|
||||||
|
});
|
||||||
|
const wrongUser = await users.create({
|
||||||
|
email: `wrong-user-${Date.now()}@example.com`,
|
||||||
|
name: "Wrong User",
|
||||||
|
});
|
||||||
|
|
||||||
|
await teamOwner.apiLogin();
|
||||||
|
await page.goto(`/settings/teams/${team.id}/members`);
|
||||||
|
|
||||||
|
let inviteLink: string;
|
||||||
|
|
||||||
|
await test.step("Send invitation to specific user", async () => {
|
||||||
|
await page.waitForLoadState("domcontentloaded");
|
||||||
|
await page.waitForTimeout(500);
|
||||||
|
await page.getByTestId("new-member-button").click();
|
||||||
|
await page.locator('input[name="inviteUser"]').fill(invitedUser.email);
|
||||||
|
await page.getByText(t("send_invite")).click();
|
||||||
|
|
||||||
|
inviteLink = await expectInvitationEmailToBeReceived(
|
||||||
|
page,
|
||||||
|
emails,
|
||||||
|
invitedUser.email,
|
||||||
|
`${teamOwner.name} invited you to join the team ${team.name} on Cal.com`,
|
||||||
|
"teams?token"
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(inviteLink).toContain("autoAccept=true");
|
||||||
|
});
|
||||||
|
|
||||||
|
await test.step("Wrong user tries to use invitation link", async () => {
|
||||||
|
const [secondContext, secondPage] = await wrongUser.apiLoginOnNewBrowser(browser);
|
||||||
|
|
||||||
|
await secondPage.goto(inviteLink);
|
||||||
|
|
||||||
|
await expect(secondPage.getByText("This invitation is not for your account")).toBeVisible();
|
||||||
|
|
||||||
|
const membership = await prisma.membership.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: wrongUser.id,
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(membership).toBeNull();
|
||||||
|
|
||||||
|
const invitedMembership = await prisma.membership.findFirst({
|
||||||
|
where: {
|
||||||
|
userId: invitedUser.id,
|
||||||
|
teamId: team.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
expect(invitedMembership?.accepted).toBe(false);
|
||||||
|
|
||||||
|
await secondPage.close();
|
||||||
|
await secondContext.close();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "اجتماع سري",
|
"secret_meeting": "اجتماع سري",
|
||||||
"login_instead": "تسجيل الدخول بدلًا من ذلك",
|
"login_instead": "تسجيل الدخول بدلًا من ذلك",
|
||||||
"create_account": "إنشاء حساب",
|
"create_account": "إنشاء حساب",
|
||||||
|
"get_started": "ابدأ الآن",
|
||||||
"confirm_password": "تأكيد كلمة المرور",
|
"confirm_password": "تأكيد كلمة المرور",
|
||||||
"reset_your_password": "عيّن كلمة المرور الجديدة متبعاً الإرشادات المرسلة إلى عنوان بريدك الإلكتروني.",
|
"reset_your_password": "عيّن كلمة المرور الجديدة متبعاً الإرشادات المرسلة إلى عنوان بريدك الإلكتروني.",
|
||||||
"org_banner_instructions": "الرجاء رفع صورة بعرض {{width}} وطول {{height}}.",
|
"org_banner_instructions": "الرجاء رفع صورة بعرض {{width}} وطول {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "الحضور",
|
"attendees": "الحضور",
|
||||||
"add_attendees": "إضافة الحضور",
|
"add_attendees": "إضافة الحضور",
|
||||||
"show_advanced_settings": "عرض الإعدادات المتقدمة",
|
"show_advanced_settings": "عرض الإعدادات المتقدمة",
|
||||||
|
"hide_advanced_settings": "إخفاء الإعدادات المتقدمة",
|
||||||
"event_name": "اسم الحدث",
|
"event_name": "اسم الحدث",
|
||||||
"event_name_in_calendar": "اسم الحدث في التقويم",
|
"event_name_in_calendar": "اسم الحدث في التقويم",
|
||||||
"event_name_tooltip": "الاسم الذي سيظهر في التقاويم",
|
"event_name_tooltip": "الاسم الذي سيظهر في التقاويم",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "تواصل مع قسم المبيعات",
|
"contact_sales": "تواصل مع قسم المبيعات",
|
||||||
"error_404": "خطأ 404",
|
"error_404": "خطأ 404",
|
||||||
"default": "الافتراضي",
|
"default": "الافتراضي",
|
||||||
"set_to_default": "تعيين إلى الافتراضي",
|
"set_to_default": "تعيين كافتراضي",
|
||||||
"new_schedule_btn": "جدول جديد",
|
"new_schedule_btn": "جدول جديد",
|
||||||
"add_new_schedule": "إضافة جدول جديد",
|
"add_new_schedule": "إضافة جدول جديد",
|
||||||
"add_new_calendar": "إضافة تقويم جديد",
|
"add_new_calendar": "إضافة تقويم جديد",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "لن يتمكن أعضاء فريقك من رؤية أعضاء الفريق الآخرين عند تشغيل هذا الإعداد.",
|
"make_team_private_description": "لن يتمكن أعضاء فريقك من رؤية أعضاء الفريق الآخرين عند تشغيل هذا الإعداد.",
|
||||||
"you_cannot_see_team_members": "لا يمكنك رؤية جميع أعضاء الفريق السري.",
|
"you_cannot_see_team_members": "لا يمكنك رؤية جميع أعضاء الفريق السري.",
|
||||||
"you_cannot_see_teams_of_org": "لا يمكنك رؤية فرق المؤسسة الخاصة.",
|
"you_cannot_see_teams_of_org": "لا يمكنك رؤية فرق المؤسسة الخاصة.",
|
||||||
"allow_booker_to_select_duration": "السماح لمن يقوم بالحجز بتحديد المدة",
|
"allow_multiple_durations": "السماح بمدد متعددة",
|
||||||
"impersonate_user_tip": "يتم مراجعة جميع استخدامات هذه الميزة.",
|
"impersonate_user_tip": "يتم مراجعة جميع استخدامات هذه الميزة.",
|
||||||
"impersonating_user_warning": "انتحال اسم مستخدم \"{{user}}\".",
|
"impersonating_user_warning": "انتحال اسم مستخدم \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "انقر هنا للإيقاف",
|
"impersonating_stop_instructions": "انقر هنا للإيقاف",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "فشل تأكيد الحجز",
|
"booking_confirmation_failed": "فشل تأكيد الحجز",
|
||||||
"not_enough_seats": "لا توجد مقاعد كافية",
|
"not_enough_seats": "لا توجد مقاعد كافية",
|
||||||
"form_builder_field_already_exists": "يوجد حقل بهذا الاسم بالفعل",
|
"form_builder_field_already_exists": "يوجد حقل بهذا الاسم بالفعل",
|
||||||
|
"guests_field_must_be_multiemail": "يجب أن يكون حقل الضيوف من نوع 'بريد إلكتروني متعدد'",
|
||||||
"show_on_booking_page": "إظهار في صفحة الحجز",
|
"show_on_booking_page": "إظهار في صفحة الحجز",
|
||||||
"visit_cancelled_booking": "يمكنك زيارة صفحة الحجز الملغى",
|
"visit_cancelled_booking": "يمكنك زيارة صفحة الحجز الملغى",
|
||||||
"get_started_zapier_templates": "البدء في استخدام قوالب Zapier",
|
"get_started_zapier_templates": "البدء في استخدام قوالب Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "مقفل من قبل المشرف",
|
"locked_by_team_admin": "مقفل من قبل المشرف",
|
||||||
"app_not_connected": "أنت غير متصل بحساب {{appName}}.",
|
"app_not_connected": "أنت غير متصل بحساب {{appName}}.",
|
||||||
"connect_now": "اتصل الآن",
|
"connect_now": "اتصل الآن",
|
||||||
|
"connect_now_unavailable_tooltip": "الاتصال الآن غير متاح حاليًا. يرجى الحجز من الفترات المتاحة أعلاه أو المحاولة مرة أخرى لاحقًا.",
|
||||||
"managed_event_dialog_confirm_button_one": "الاستبدال وإخطار {{count}} عضو",
|
"managed_event_dialog_confirm_button_one": "الاستبدال وإخطار {{count}} عضو",
|
||||||
"managed_event_dialog_confirm_button_other": "الاستبدال وإخطار {{count}} من الأعضاء",
|
"managed_event_dialog_confirm_button_other": "الاستبدال وإخطار {{count}} من الأعضاء",
|
||||||
"managed_event_dialog_title_one": "رابط /{{slug}} موجود بالفعل لـ {{count}} عضو. هل تريد استبداله؟",
|
"managed_event_dialog_title_one": "رابط /{{slug}} موجود بالفعل لـ {{count}} عضو. هل تريد استبداله؟",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "بالمتابعة، فإنك توافق على",
|
"proceeding_agreement": "بالمتابعة، فإنك توافق على",
|
||||||
"terms": "الشروط",
|
"terms": "الشروط",
|
||||||
"always_show_x_days": "دائمًا {{x}} أيام متاحة",
|
"always_show_x_days": "دائمًا {{x}} أيام متاحة",
|
||||||
|
"always_show_x_days_description": "ينقل تلقائيًا نافذة الحجز لمدة {{x}} يوم إلى الأمام كل يوم، مما يضمن أن تقويمك يعرض دائمًا الفترات المتاحة للـ {{x}} يومًا القادمة.",
|
||||||
"team_subscription_cancelled_successfully": "تم إلغاء اشتراك الفريق بنجاح",
|
"team_subscription_cancelled_successfully": "تم إلغاء اشتراك الفريق بنجاح",
|
||||||
"team_subscription_cancellation_error": "حدث خطأ أثناء إلغاء اشتراك الفريق",
|
"team_subscription_cancellation_error": "حدث خطأ أثناء إلغاء اشتراك الفريق",
|
||||||
"unable_to_subscribe_to_the_platform": "حدث خطأ أثناء محاولة الاشتراك في خطة المنصة، يرجى المحاولة مرة أخرى لاحقًا",
|
"unable_to_subscribe_to_the_platform": "حدث خطأ أثناء محاولة الاشتراك في خطة المنصة، يرجى المحاولة مرة أخرى لاحقًا",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "ينتهي بـ",
|
"filter_operator_ends_with": "ينتهي بـ",
|
||||||
"filter_operator_is_empty": "فارغ",
|
"filter_operator_is_empty": "فارغ",
|
||||||
"filter_operator_not_empty": "غير فارغ",
|
"filter_operator_not_empty": "غير فارغ",
|
||||||
"translate_description_button": "ترجمة العنوان/الوصف إلى لغة متصفح الزائر باستخدام الذكاء الاصطناعي",
|
"translate_description_button": "ترجمة العنوان والوصف تلقائيًا",
|
||||||
|
"translate_description_button_description": "ترجمة العناوين والأوصاف تلقائيًا إلى لغة متصفح الزائر باستخدام الذكاء الاصطناعي.",
|
||||||
"rr_distribution_method": "التوزيع",
|
"rr_distribution_method": "التوزيع",
|
||||||
"rr_distribution_method_description": "يتيح تحسين التوزيع لتحقيق أقصى قدر من التوفر أو لتحقيق توزيع أكثر توازناً.",
|
"rr_distribution_method_description": "يتيح تحسين التوزيع لتحقيق أقصى قدر من التوفر أو لتحقيق توزيع أكثر توازناً.",
|
||||||
"rr_distribution_method_availability_title": "تعظيم التوفر",
|
"rr_distribution_method_availability_title": "تعظيم التوفر",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "ميزة المقاعد لا تدعم الفعاليات المتكررة. قم بتعطيل ميزة المقاعد لجعلها متكررة.",
|
"seats_doesnt_support_recurring": "ميزة المقاعد لا تدعم الفعاليات المتكررة. قم بتعطيل ميزة المقاعد لجعلها متكررة.",
|
||||||
"recurring_event_seats_error": "الفعالية المتكررة لا تدعم ميزة المقاعد. قم بتعطيل ميزة المقاعد أو اجعل الفعالية غير متكررة.",
|
"recurring_event_seats_error": "الفعالية المتكررة لا تدعم ميزة المقاعد. قم بتعطيل ميزة المقاعد أو اجعل الفعالية غير متكررة.",
|
||||||
"segment": "شريحة",
|
"segment": "شريحة",
|
||||||
|
"saved_filters": "الفلاتر المحفوظة",
|
||||||
"rename": "إعادة تسمية",
|
"rename": "إعادة تسمية",
|
||||||
"rename_segment": "إعادة تسمية الشريحة",
|
"rename_segment": "إعادة تسمية الشريحة",
|
||||||
"delete_segment": "حذف الشريحة",
|
"delete_segment": "حذف الشريحة",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "قائمة الاختيار",
|
"picklist": "قائمة الاختيار",
|
||||||
"most_cancelled_bookings": "الأكثر إلغاءً للحجوزات",
|
"most_cancelled_bookings": "الأكثر إلغاءً للحجوزات",
|
||||||
"interface_language": "لغة الواجهة",
|
"interface_language": "لغة الواجهة",
|
||||||
|
"interface_language_description": "اختر لغتك المفضلة لواجهة الحجز",
|
||||||
"visitors_browser_language": "لغة متصفح الزائر",
|
"visitors_browser_language": "لغة متصفح الزائر",
|
||||||
"name_or_email": "الاسم أو البريد الإلكتروني",
|
"name_or_email": "الاسم أو البريد الإلكتروني",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "إذا لم يتم العثور على جهة اتصال، يتم الرجوع إلى مالك العميل المحتمل إذا كان موجودًا",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "إذا لم يتم العثور على جهة اتصال، يتم الرجوع إلى مالك العميل المحتمل إذا كان موجودًا",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "تعديل وتغيير إعدادات سير العمل",
|
"pbac_desc_update_workflows": "تعديل وتغيير إعدادات سير العمل",
|
||||||
"pbac_desc_delete_workflows": "إزالة سير العمل من النظام",
|
"pbac_desc_delete_workflows": "إزالة سير العمل من النظام",
|
||||||
"pbac_resource_webhook": "ويب هوك",
|
"pbac_resource_webhook": "ويب هوك",
|
||||||
|
"pbac_resource_availability": "الأوقات المتاحة",
|
||||||
|
"pbac_resource_out_of_office": "خارج المكتب",
|
||||||
"pbac_desc_create_webhooks": "إنشاء ويب هوكس",
|
"pbac_desc_create_webhooks": "إنشاء ويب هوكس",
|
||||||
"pbac_desc_view_webhooks": "عرض ويب هوكس",
|
"pbac_desc_view_webhooks": "عرض ويب هوكس",
|
||||||
"pbac_desc_update_webhooks": "تحديث الويب هوك",
|
"pbac_desc_update_webhooks": "تحديث الويب هوك",
|
||||||
"pbac_desc_delete_webhooks": "حذف الويب هوك",
|
"pbac_desc_delete_webhooks": "حذف الويب هوك",
|
||||||
|
"pbac_desc_create_availability": "إنشاء وقت متاح",
|
||||||
|
"pbac_desc_view_availability": "عرض الأوقات المتاحة",
|
||||||
|
"pbac_desc_update_availability": "تحديث الأوقات المتاحة",
|
||||||
|
"pbac_desc_delete_availability": "حذف الأوقات المتاحة",
|
||||||
|
"pbac_desc_create_out_of_office": "إنشاء حالة خارج المكتب",
|
||||||
|
"pbac_desc_view_out_of_office": "عرض حالة خارج المكتب",
|
||||||
|
"pbac_desc_update_out_of_office": "تحديث حالة خارج المكتب",
|
||||||
|
"pbac_desc_delete_out_of_office": "حذف حالة خارج المكتب",
|
||||||
"pbac_desc_manage_workflows": "وصول كامل لإدارة جميع سير العمل",
|
"pbac_desc_manage_workflows": "وصول كامل لإدارة جميع سير العمل",
|
||||||
"pbac_desc_create_event_types": "إنشاء أنواع الأحداث",
|
"pbac_desc_create_event_types": "إنشاء أنواع الأحداث",
|
||||||
"pbac_desc_view_event_types": "عرض أنواع الأحداث",
|
"pbac_desc_view_event_types": "عرض أنواع الأحداث",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Gizli Görüş",
|
"secret_meeting": "Gizli Görüş",
|
||||||
"login_instead": "Əvəzinə daxil olun",
|
"login_instead": "Əvəzinə daxil olun",
|
||||||
"create_account": "Hesab yaradın",
|
"create_account": "Hesab yaradın",
|
||||||
|
"get_started": "Başlayın",
|
||||||
"confirm_password": "Şifrəni təsdiqləyin",
|
"confirm_password": "Şifrəni təsdiqləyin",
|
||||||
"reset_your_password": "E-poçt ünvanınıza göndərilən təlimatlarla yeni şifrənizi təyin edin.",
|
"reset_your_password": "E-poçt ünvanınıza göndərilən təlimatlarla yeni şifrənizi təyin edin.",
|
||||||
"org_banner_instructions": "Zəhmət olmasa {{width}} enində və {{height}} hündürlüyündə şəkil yükləyin.",
|
"org_banner_instructions": "Zəhmət olmasa {{width}} enində və {{height}} hündürlüyündə şəkil yükləyin.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Tədbir Tipi Rəngi (İşıq Mövzusu)",
|
"light_event_type_color": "Tədbir Tipi Rəngi (İşıq Mövzusu)",
|
||||||
"dark_event_type_color": "Tədbir Tipi Rəngi (Qaranlıq Mövzu)",
|
"dark_event_type_color": "Tədbir Tipi Rəngi (Qaranlıq Mövzu)",
|
||||||
"file_not_named": "Fayl adlandırılmayıb [idOrSlug]/[user]",
|
"file_not_named": "Fayl adlandırılmayıb [idOrSlug]/[user]",
|
||||||
"create_team": "Komanda Yarat",
|
"create_team": "Komanda yaradın",
|
||||||
"name": "Ad",
|
"name": "Ad",
|
||||||
"nameless_team": "Adsız Komanda",
|
"nameless_team": "Adsız Komanda",
|
||||||
"oauth_clients": "OAuth Müştəriləri",
|
"oauth_clients": "OAuth Müştəriləri",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "İştirakçılar",
|
"attendees": "İştirakçılar",
|
||||||
"add_attendees": "İştirakçı əlavə et",
|
"add_attendees": "İştirakçı əlavə et",
|
||||||
"show_advanced_settings": "Ətraflı parametrləri göstər",
|
"show_advanced_settings": "Ətraflı parametrləri göstər",
|
||||||
|
"hide_advanced_settings": "Ətraflı parametrləri gizlədin",
|
||||||
"event_name": "Tədbirin Adı",
|
"event_name": "Tədbirin Adı",
|
||||||
"event_name_in_calendar": "Tədbirin adı ilə",
|
"event_name_in_calendar": "Tədbirin adı ilə",
|
||||||
"event_name_tooltip": "Təqvimlərdə görünəcək ad",
|
"event_name_tooltip": "Təqvimlərdə görünəcək ad",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Bu aktiv olduqda komanda üzvləri digər komanda üzvlərini görə bilməyəcək.",
|
"make_team_private_description": "Bu aktiv olduqda komanda üzvləri digər komanda üzvlərini görə bilməyəcək.",
|
||||||
"you_cannot_see_team_members": "Gizli komandanın bütün üzvlərini görə bilməzsiniz.",
|
"you_cannot_see_team_members": "Gizli komandanın bütün üzvlərini görə bilməzsiniz.",
|
||||||
"you_cannot_see_teams_of_org": "Gizli təşkilatın komandalarını görə bilməzsiniz.",
|
"you_cannot_see_teams_of_org": "Gizli təşkilatın komandalarını görə bilməzsiniz.",
|
||||||
"allow_booker_to_select_duration": "Rezervasiya edənə müddəti seçməyə icazə ver",
|
"allow_multiple_durations": "Çoxsaylı müddətlərə icazə verin",
|
||||||
"impersonate_user_tip": "Bu xüsusiyyətin bütün istifadələri auditi olunur.",
|
"impersonate_user_tip": "Bu xüsusiyyətin bütün istifadələri auditi olunur.",
|
||||||
"impersonating_user_warning": "\"{{user}}\" istifadəçi adını təqlid edirsiniz.",
|
"impersonating_user_warning": "\"{{user}}\" istifadəçi adını təqlid edirsiniz.",
|
||||||
"impersonating_stop_instructions": "Dayandırmaq üçün bura klikləyin",
|
"impersonating_stop_instructions": "Dayandırmaq üçün bura klikləyin",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Rezervasiya təsdiqi uğursuz oldu",
|
"booking_confirmation_failed": "Rezervasiya təsdiqi uğursuz oldu",
|
||||||
"not_enough_seats": "Kifayət qədər oturacaq yoxdur",
|
"not_enough_seats": "Kifayət qədər oturacaq yoxdur",
|
||||||
"form_builder_field_already_exists": "Bu adla bir sahə artıq mövcuddur",
|
"form_builder_field_already_exists": "Bu adla bir sahə artıq mövcuddur",
|
||||||
|
"guests_field_must_be_multiemail": "Qonaqlar sahəsi 'Çoxsaylı e-poçtlar' tipində olmalıdır",
|
||||||
"show_on_booking_page": "Rezervasiya səhifəsində göstər",
|
"show_on_booking_page": "Rezervasiya səhifəsində göstər",
|
||||||
"visit_cancelled_booking": "Ləğv edilmiş rezervasiya səhifəsinə baxa bilərsiniz",
|
"visit_cancelled_booking": "Ləğv edilmiş rezervasiya səhifəsinə baxa bilərsiniz",
|
||||||
"get_started_zapier_templates": "Zapier şablonları ilə başlayın",
|
"get_started_zapier_templates": "Zapier şablonları ilə başlayın",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Komanda admini tərəfindən kilidlənib",
|
"locked_by_team_admin": "Komanda admini tərəfindən kilidlənib",
|
||||||
"app_not_connected": "Siz {{appName}} hesabını bağlamamısınız.",
|
"app_not_connected": "Siz {{appName}} hesabını bağlamamısınız.",
|
||||||
"connect_now": "İndi bağlayın",
|
"connect_now": "İndi bağlayın",
|
||||||
|
"connect_now_unavailable_tooltip": "İndi qoşulmaq hazırda əlçatan deyil. Zəhmət olmasa yuxarıdakı slotlardan rezervasiya edin və ya sonra yenidən cəhd edin.",
|
||||||
"managed_event_dialog_confirm_button_one": "Əvəz et və {{count}} üzvü xəbərdar et",
|
"managed_event_dialog_confirm_button_one": "Əvəz et və {{count}} üzvü xəbərdar et",
|
||||||
"managed_event_dialog_confirm_button_other": "Əvəz et və {{count}} üzvləri xəbərdar et",
|
"managed_event_dialog_confirm_button_other": "Əvəz et və {{count}} üzvləri xəbərdar et",
|
||||||
"managed_event_dialog_title_one": "/{{slug}} url-si artıq {{count}} üzv üçün mövcuddur. Onu əvəz etmək istəyirsiniz?",
|
"managed_event_dialog_title_one": "/{{slug}} url-si artıq {{count}} üzv üçün mövcuddur. Onu əvəz etmək istəyirsiniz?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Müəssisə",
|
"enterprise": "Müəssisə",
|
||||||
"enterprise_description": "Təşkilatınızı yaratmaq üçün Enterprise-ə yüksəldin",
|
"enterprise_description": "Təşkilatınızı yaratmaq üçün Enterprise-ə yüksəldin",
|
||||||
"create_your_org": "Təşkilatınızı Yaradın",
|
"create_your_org": "Təşkilatınızı Yaradın",
|
||||||
"create_org": "Təşkilat Yarat",
|
"create_org": "Təşkilat yaradın",
|
||||||
"create_your_org_description": "Təşkilatlara yüksəlin və subdomen, birləşdirilmiş ödəniş, Göstəricilər, geniş ağ etiketləmə və daha çoxunu əldə edin",
|
"create_your_org_description": "Təşkilatlara yüksəlin və subdomen, birləşdirilmiş ödəniş, Göstəricilər, geniş ağ etiketləmə və daha çoxunu əldə edin",
|
||||||
"create_your_enterprise_description": "Enterprise-ə yüksəlin və Active Directory Sync, SCIM Avtomatik İstifadəçi təminatı, Cal.ai Səs Agentləri, Admin API-ləri və daha çoxuna giriş əldə edin!",
|
"create_your_enterprise_description": "Enterprise-ə yüksəlin və Active Directory Sync, SCIM Avtomatik İstifadəçi təminatı, Cal.ai Səs Agentləri, Admin API-ləri və daha çoxuna giriş əldə edin!",
|
||||||
"other_payment_app_enabled": "Hər tədbir növü üçün yalnız bir ödəniş tətbiqini aktivləşdirə bilərsiniz",
|
"other_payment_app_enabled": "Hər tədbir növü üçün yalnız bir ödəniş tətbiqini aktivləşdirə bilərsiniz",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Davam etməklə, bizim",
|
"proceeding_agreement": "Davam etməklə, bizim",
|
||||||
"terms": "Şərtlər",
|
"terms": "Şərtlər",
|
||||||
"always_show_x_days": "Həmişə {{x}} gün mövcuddur",
|
"always_show_x_days": "Həmişə {{x}} gün mövcuddur",
|
||||||
|
"always_show_x_days_description": "Avtomatik olaraq {{x}}-günlük rezervasiya pəncərəsini hər gün irəli aparır, təqviminizin həmişə növbəti {{x}} günün əlçatan slotlarını göstərməsini təmin edir.",
|
||||||
"team_subscription_cancelled_successfully": "Komanda abunəliyi uğurla ləğv edildi",
|
"team_subscription_cancelled_successfully": "Komanda abunəliyi uğurla ləğv edildi",
|
||||||
"team_subscription_cancellation_error": "Komanda abunəliyini ləğv edərkən xəta baş verdi",
|
"team_subscription_cancellation_error": "Komanda abunəliyini ləğv edərkən xəta baş verdi",
|
||||||
"unable_to_subscribe_to_the_platform": "Platforma planına abunə olmağa çalışarkən bir səhv baş verdi, zəhmət olmasa bir az sonra yenidən cəhd edin",
|
"unable_to_subscribe_to_the_platform": "Platforma planına abunə olmağa çalışarkən bir səhv baş verdi, zəhmət olmasa bir az sonra yenidən cəhd edin",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "İlə bitir",
|
"filter_operator_ends_with": "İlə bitir",
|
||||||
"filter_operator_is_empty": "Boşdur",
|
"filter_operator_is_empty": "Boşdur",
|
||||||
"filter_operator_not_empty": "Boş deyil",
|
"filter_operator_not_empty": "Boş deyil",
|
||||||
"translate_description_button": "Süni intellekt vasitəsilə başlıq/təsviri ziyarətçinin brauzer dilinə tərcümə et",
|
"translate_description_button": "Başlıq və təsviri auto tərcümə et",
|
||||||
|
"translate_description_button_description": "Başlıqları və təsvirləri ziyarətçinin brauzer dilinə AI vasitəsilə avtomatik tərcümə edin.",
|
||||||
"rr_distribution_method": "Paylama",
|
"rr_distribution_method": "Paylama",
|
||||||
"rr_distribution_method_description": "Maksimum əlçatanlıq üçün paylamanı optimallaşdırmağa və ya daha balanslı bir təyinat üçün çalışmağa imkan verir.",
|
"rr_distribution_method_description": "Maksimum əlçatanlıq üçün paylamanı optimallaşdırmağa və ya daha balanslı bir təyinat üçün çalışmağa imkan verir.",
|
||||||
"rr_distribution_method_availability_title": "Əlçatanlığı maksimuma çatdırmaq",
|
"rr_distribution_method_availability_title": "Əlçatanlığı maksimuma çatdırmaq",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Yerlər funksiyası təkrarlanan tədbiri dəstəkləmir. Təkrarlanma funksiyasını aktivləşdirmək üçün yerlər funksiyasını deaktiv edin.",
|
"seats_doesnt_support_recurring": "Yerlər funksiyası təkrarlanan tədbiri dəstəkləmir. Təkrarlanma funksiyasını aktivləşdirmək üçün yerlər funksiyasını deaktiv edin.",
|
||||||
"recurring_event_seats_error": "Təkrarlanan tədbir yerlər funksiyasını dəstəkləmir. Yerlər funksiyasını deaktiv edin və ya tədbiri təkrarlanmayan edin.",
|
"recurring_event_seats_error": "Təkrarlanan tədbir yerlər funksiyasını dəstəkləmir. Yerlər funksiyasını deaktiv edin və ya tədbiri təkrarlanmayan edin.",
|
||||||
"segment": "Seqment",
|
"segment": "Seqment",
|
||||||
|
"saved_filters": "Saxlanılmış filtrlər",
|
||||||
"rename": "Adını dəyişdir",
|
"rename": "Adını dəyişdir",
|
||||||
"rename_segment": "Seqmentin adını dəyişdir",
|
"rename_segment": "Seqmentin adını dəyişdir",
|
||||||
"delete_segment": "Seqmenti sil",
|
"delete_segment": "Seqmenti sil",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Seçim siyahısı",
|
"picklist": "Seçim siyahısı",
|
||||||
"most_cancelled_bookings": "Ən Çox Ləğv Edilən",
|
"most_cancelled_bookings": "Ən Çox Ləğv Edilən",
|
||||||
"interface_language": "İnterfeys dili",
|
"interface_language": "İnterfeys dili",
|
||||||
|
"interface_language_description": "Rezervasiya interfeysi üçün üstünlük verdiyiniz dili təyin edin",
|
||||||
"visitors_browser_language": "Ziyarətçinin brauzer dili",
|
"visitors_browser_language": "Ziyarətçinin brauzer dili",
|
||||||
"name_or_email": "Ad və ya E-poçt",
|
"name_or_email": "Ad və ya E-poçt",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Əgər əlaqə tapılmazsa, mövcud olduğu təqdirdə potensial müştəri sahibinə qayıt",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Əgər əlaqə tapılmazsa, mövcud olduğu təqdirdə potensial müştəri sahibinə qayıt",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "İş axını parametrlərini redaktə etmək və dəyişdirmək",
|
"pbac_desc_update_workflows": "İş axını parametrlərini redaktə etmək və dəyişdirmək",
|
||||||
"pbac_desc_delete_workflows": "İş axınlarını sistemdən silmək",
|
"pbac_desc_delete_workflows": "İş axınlarını sistemdən silmək",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Mövcudluq",
|
||||||
|
"pbac_resource_out_of_office": "Ofisdən kənarda",
|
||||||
"pbac_desc_create_webhooks": "Webhook yaradın",
|
"pbac_desc_create_webhooks": "Webhook yaradın",
|
||||||
"pbac_desc_view_webhooks": "Webhook-lara baxın",
|
"pbac_desc_view_webhooks": "Webhook-lara baxın",
|
||||||
"pbac_desc_update_webhooks": "Webhook-ləri yeniləyin",
|
"pbac_desc_update_webhooks": "Webhook-ləri yeniləyin",
|
||||||
"pbac_desc_delete_webhooks": "Webhook-ləri silin",
|
"pbac_desc_delete_webhooks": "Webhook-ləri silin",
|
||||||
|
"pbac_desc_create_availability": "Mövcudluq yaratmaq",
|
||||||
|
"pbac_desc_view_availability": "Mövcudluğa baxmaq",
|
||||||
|
"pbac_desc_update_availability": "Mövcudluğu yeniləmək",
|
||||||
|
"pbac_desc_delete_availability": "Mövcudluğu silmək",
|
||||||
|
"pbac_desc_create_out_of_office": "Ofisdən kənar yaratmaq",
|
||||||
|
"pbac_desc_view_out_of_office": "Ofisdən kənara baxmaq",
|
||||||
|
"pbac_desc_update_out_of_office": "Ofisdən kənarı yeniləmək",
|
||||||
|
"pbac_desc_delete_out_of_office": "Ofisdən kənarı silmək",
|
||||||
"pbac_desc_manage_workflows": "Bütün iş axınlarına tam idarəetmə girişi",
|
"pbac_desc_manage_workflows": "Bütün iş axınlarına tam idarəetmə girişi",
|
||||||
"pbac_desc_create_event_types": "Tədbir növləri yaratmaq",
|
"pbac_desc_create_event_types": "Tədbir növləri yaratmaq",
|
||||||
"pbac_desc_view_event_types": "Tədbir növlərinə baxmaq",
|
"pbac_desc_view_event_types": "Tədbir növlərinə baxmaq",
|
||||||
@@ -3561,7 +3579,7 @@
|
|||||||
"round_robin_settings_description": "Bu komanda üçün standart növbəli təqvim parametrlərini fərdiləşdirin",
|
"round_robin_settings_description": "Bu komanda üçün standart növbəli təqvim parametrlərini fərdiləşdirin",
|
||||||
"round_robin_settings_updated_successfully": "Növbəli təqvim parametrləri uğurla yeniləndi",
|
"round_robin_settings_updated_successfully": "Növbəli təqvim parametrləri uğurla yeniləndi",
|
||||||
"booking_creation_time": "Rezervasiya yaradılma vaxtı",
|
"booking_creation_time": "Rezervasiya yaradılma vaxtı",
|
||||||
"meeting_start_time": "Görüş başlama vaxtı",
|
"meeting_start_time": "Görüşün başlama vaxtı",
|
||||||
"members_affected_by_disabling_delegation_credential": "Təsir edilən üzvlər",
|
"members_affected_by_disabling_delegation_credential": "Təsir edilən üzvlər",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Nümayəndəlik etimadnaməsinin deaktiv edilməsindən təsirlənən üzv yoxdur",
|
"no_members_affected_by_disabling_delegation_credential": "Nümayəndəlik etimadnaməsinin deaktiv edilməsindən təsirlənən üzv yoxdur",
|
||||||
"download_expense_log": "Xərc jurnalını yüklə",
|
"download_expense_log": "Xərc jurnalını yüklə",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Тайна среща",
|
"secret_meeting": "Тайна среща",
|
||||||
"login_instead": "Вместо това влезте",
|
"login_instead": "Вместо това влезте",
|
||||||
"create_account": "Създаване на акаунт",
|
"create_account": "Създаване на акаунт",
|
||||||
|
"get_started": "Започнете",
|
||||||
"confirm_password": "Потвърдете паролата",
|
"confirm_password": "Потвърдете паролата",
|
||||||
"reset_your_password": "Задайте новата си парола с инструкциите, изпратени на вашия имейл адрес.",
|
"reset_your_password": "Задайте новата си парола с инструкциите, изпратени на вашия имейл адрес.",
|
||||||
"org_banner_instructions": "Моля, качете изображение с ширина {{width}} и височина {{height}}.",
|
"org_banner_instructions": "Моля, качете изображение с ширина {{width}} и височина {{height}}.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Цвят на типа събитие (светла тема)",
|
"light_event_type_color": "Цвят на типа събитие (светла тема)",
|
||||||
"dark_event_type_color": "Цвят на типа събитие (тъмна тема)",
|
"dark_event_type_color": "Цвят на типа събитие (тъмна тема)",
|
||||||
"file_not_named": "Файлът не е именуван [idOrSlug]/[user]",
|
"file_not_named": "Файлът не е именуван [idOrSlug]/[user]",
|
||||||
"create_team": "Създай екип",
|
"create_team": "Създаване на екип",
|
||||||
"name": "Име",
|
"name": "Име",
|
||||||
"nameless_team": "Безименен екип",
|
"nameless_team": "Безименен екип",
|
||||||
"oauth_clients": "OAuth клиенти",
|
"oauth_clients": "OAuth клиенти",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Участници",
|
"attendees": "Участници",
|
||||||
"add_attendees": "Добавяне на участници",
|
"add_attendees": "Добавяне на участници",
|
||||||
"show_advanced_settings": "Показване на разширени настройки",
|
"show_advanced_settings": "Показване на разширени настройки",
|
||||||
|
"hide_advanced_settings": "Скриване на разширените настройки",
|
||||||
"event_name": "Име на събитието",
|
"event_name": "Име на събитието",
|
||||||
"event_name_in_calendar": "С име на събитието",
|
"event_name_in_calendar": "С име на събитието",
|
||||||
"event_name_tooltip": "Името, което ще се показва в календарите",
|
"event_name_tooltip": "Името, което ще се показва в календарите",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Свържи се с отдел продажби",
|
"contact_sales": "Свържи се с отдел продажби",
|
||||||
"error_404": "Грешка 404",
|
"error_404": "Грешка 404",
|
||||||
"default": "По подразбиране",
|
"default": "По подразбиране",
|
||||||
"set_to_default": "Задай по подразбиране",
|
"set_to_default": "Задаване по подразбиране",
|
||||||
"new_schedule_btn": "Нов график",
|
"new_schedule_btn": "Нов график",
|
||||||
"add_new_schedule": "Добави нов график",
|
"add_new_schedule": "Добави нов график",
|
||||||
"add_new_calendar": "Добави нов календар",
|
"add_new_calendar": "Добави нов календар",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Когато това е включено, членовете на екипа няма да могат да виждат другите членове.",
|
"make_team_private_description": "Когато това е включено, членовете на екипа няма да могат да виждат другите членове.",
|
||||||
"you_cannot_see_team_members": "Не можеш да видиш всички членове на частния екип.",
|
"you_cannot_see_team_members": "Не можеш да видиш всички членове на частния екип.",
|
||||||
"you_cannot_see_teams_of_org": "Не можеш да видиш екипите на частна организация.",
|
"you_cannot_see_teams_of_org": "Не можеш да видиш екипите на частна организация.",
|
||||||
"allow_booker_to_select_duration": "Позволи на резервиращия да избере продължителност",
|
"allow_multiple_durations": "Разрешаване на множество продължителности",
|
||||||
"impersonate_user_tip": "Всяко използване на тази функция се одитира.",
|
"impersonate_user_tip": "Всяко използване на тази функция се одитира.",
|
||||||
"impersonating_user_warning": "Представяш се като потребител \"{{user}}\".",
|
"impersonating_user_warning": "Представяш се като потребител \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Кликни тук, за да спреш",
|
"impersonating_stop_instructions": "Кликни тук, за да спреш",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Потвърждението на резервацията е неуспешно",
|
"booking_confirmation_failed": "Потвърждението на резервацията е неуспешно",
|
||||||
"not_enough_seats": "Няма достатъчно места",
|
"not_enough_seats": "Няма достатъчно места",
|
||||||
"form_builder_field_already_exists": "Поле с това име вече съществува",
|
"form_builder_field_already_exists": "Поле с това име вече съществува",
|
||||||
|
"guests_field_must_be_multiemail": "Полето за гости трябва да бъде от тип 'Множество имейли'",
|
||||||
"show_on_booking_page": "Покажи на страницата за резервации",
|
"show_on_booking_page": "Покажи на страницата за резервации",
|
||||||
"visit_cancelled_booking": "Можете да посетите страницата с отменената резервация",
|
"visit_cancelled_booking": "Можете да посетите страницата с отменената резервация",
|
||||||
"get_started_zapier_templates": "Започнете със Zapier шаблони",
|
"get_started_zapier_templates": "Започнете със Zapier шаблони",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Заключено от администратора на екипа",
|
"locked_by_team_admin": "Заключено от администратора на екипа",
|
||||||
"app_not_connected": "Не сте свързали {{appName}} акаунт.",
|
"app_not_connected": "Не сте свързали {{appName}} акаунт.",
|
||||||
"connect_now": "Свържете сега",
|
"connect_now": "Свържете сега",
|
||||||
|
"connect_now_unavailable_tooltip": "Свързването сега не е налично. Моля, резервирайте от горните слотове или опитайте отново по-късно.",
|
||||||
"managed_event_dialog_confirm_button_one": "Замени и уведоми {{count}} член",
|
"managed_event_dialog_confirm_button_one": "Замени и уведоми {{count}} член",
|
||||||
"managed_event_dialog_confirm_button_other": "Замени и уведоми {{count}} члена",
|
"managed_event_dialog_confirm_button_other": "Замени и уведоми {{count}} члена",
|
||||||
"managed_event_dialog_title_one": "URL адресът /{{slug}} вече съществува за {{count}} член. Искате ли да го замените?",
|
"managed_event_dialog_title_one": "URL адресът /{{slug}} вече съществува за {{count}} член. Искате ли да го замените?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "С продължаването си, вие се съгласявате с нашите",
|
"proceeding_agreement": "С продължаването си, вие се съгласявате с нашите",
|
||||||
"terms": "Условия",
|
"terms": "Условия",
|
||||||
"always_show_x_days": "Винаги показвай {{x}} свободни дни",
|
"always_show_x_days": "Винаги показвай {{x}} свободни дни",
|
||||||
|
"always_show_x_days_description": "Автоматично премества {{x}}-дневния прозорец за резервации напред всеки ден, гарантирайки, че вашият календар винаги показва следващите {{x}} дни с налични слотове.",
|
||||||
"team_subscription_cancelled_successfully": "Абонаментът на екипа е отменен успешно",
|
"team_subscription_cancelled_successfully": "Абонаментът на екипа е отменен успешно",
|
||||||
"team_subscription_cancellation_error": "Грешка при отмяна на абонамента на екипа",
|
"team_subscription_cancellation_error": "Грешка при отмяна на абонамента на екипа",
|
||||||
"unable_to_subscribe_to_the_platform": "Възникна грешка при опит за абониране за плана на платформата, моля опитайте отново по-късно",
|
"unable_to_subscribe_to_the_platform": "Възникна грешка при опит за абониране за плана на платформата, моля опитайте отново по-късно",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Завършва с",
|
"filter_operator_ends_with": "Завършва с",
|
||||||
"filter_operator_is_empty": "Е празно",
|
"filter_operator_is_empty": "Е празно",
|
||||||
"filter_operator_not_empty": "Не е празно",
|
"filter_operator_not_empty": "Не е празно",
|
||||||
"translate_description_button": "Преведи заглавието/описанието на езика на браузъра на посетителя чрез AI",
|
"translate_description_button": "Автоматичен превод на заглавие и описание",
|
||||||
|
"translate_description_button_description": "Автоматично превеждане на заглавия и описания на езика на браузъра на посетителя с помощта на изкуствен интелект.",
|
||||||
"rr_distribution_method": "Разпределение",
|
"rr_distribution_method": "Разпределение",
|
||||||
"rr_distribution_method_description": "Позволява оптимизиране на разпределението за максимална наличност или за по-балансирано разпределение.",
|
"rr_distribution_method_description": "Позволява оптимизиране на разпределението за максимална наличност или за по-балансирано разпределение.",
|
||||||
"rr_distribution_method_availability_title": "Максимизиране на наличността",
|
"rr_distribution_method_availability_title": "Максимизиране на наличността",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Функцията за места не поддържа повтарящи се събития. Деактивирайте функцията за места, за да го направите повтарящо се.",
|
"seats_doesnt_support_recurring": "Функцията за места не поддържа повтарящи се събития. Деактивирайте функцията за места, за да го направите повтарящо се.",
|
||||||
"recurring_event_seats_error": "Повтарящото се събитие не поддържа функцията за места. Деактивирайте функцията за места или направете събитието неповтарящо се.",
|
"recurring_event_seats_error": "Повтарящото се събитие не поддържа функцията за места. Деактивирайте функцията за места или направете събитието неповтарящо се.",
|
||||||
"segment": "Сегмент",
|
"segment": "Сегмент",
|
||||||
|
"saved_filters": "Запазени филтри",
|
||||||
"rename": "Преименуване",
|
"rename": "Преименуване",
|
||||||
"rename_segment": "Преименуване на сегмент",
|
"rename_segment": "Преименуване на сегмент",
|
||||||
"delete_segment": "Изтриване на сегмент",
|
"delete_segment": "Изтриване на сегмент",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Списък за избор",
|
"picklist": "Списък за избор",
|
||||||
"most_cancelled_bookings": "Най-много отменени",
|
"most_cancelled_bookings": "Най-много отменени",
|
||||||
"interface_language": "Език на интерфейса",
|
"interface_language": "Език на интерфейса",
|
||||||
|
"interface_language_description": "Задайте предпочитания от вас език за интерфейса за резервации",
|
||||||
"visitors_browser_language": "Език на браузъра на посетителя",
|
"visitors_browser_language": "Език на браузъра на посетителя",
|
||||||
"name_or_email": "Име или имейл",
|
"name_or_email": "Име или имейл",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ако не е намерен контакт, връщане към собственика на лийда, ако съществува",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ако не е намерен контакт, връщане към собственика на лийда, ако съществува",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Редактиране и модифициране на настройките на работните процеси",
|
"pbac_desc_update_workflows": "Редактиране и модифициране на настройките на работните процеси",
|
||||||
"pbac_desc_delete_workflows": "Премахване на работни процеси от системата",
|
"pbac_desc_delete_workflows": "Премахване на работни процеси от системата",
|
||||||
"pbac_resource_webhook": "Уебхук",
|
"pbac_resource_webhook": "Уебхук",
|
||||||
|
"pbac_resource_availability": "Наличност",
|
||||||
|
"pbac_resource_out_of_office": "Извън офиса",
|
||||||
"pbac_desc_create_webhooks": "Създаване на уебхуци",
|
"pbac_desc_create_webhooks": "Създаване на уебхуци",
|
||||||
"pbac_desc_view_webhooks": "Преглед на уебхуци",
|
"pbac_desc_view_webhooks": "Преглед на уебхуци",
|
||||||
"pbac_desc_update_webhooks": "Актуализиране на уебхукове",
|
"pbac_desc_update_webhooks": "Актуализиране на уебхукове",
|
||||||
"pbac_desc_delete_webhooks": "Изтриване на уебхукове",
|
"pbac_desc_delete_webhooks": "Изтриване на уебхукове",
|
||||||
|
"pbac_desc_create_availability": "Създаване на наличност",
|
||||||
|
"pbac_desc_view_availability": "Преглед на наличност",
|
||||||
|
"pbac_desc_update_availability": "Актуализиране на наличност",
|
||||||
|
"pbac_desc_delete_availability": "Изтриване на наличност",
|
||||||
|
"pbac_desc_create_out_of_office": "Създаване на статус \"Извън офиса\"",
|
||||||
|
"pbac_desc_view_out_of_office": "Преглед на статус \"Извън офиса\"",
|
||||||
|
"pbac_desc_update_out_of_office": "Актуализиране на статус \"Извън офиса\"",
|
||||||
|
"pbac_desc_delete_out_of_office": "Изтриване на статус \"Извън офиса\"",
|
||||||
"pbac_desc_manage_workflows": "Пълен достъп за управление на всички работни процеси",
|
"pbac_desc_manage_workflows": "Пълен достъп за управление на всички работни процеси",
|
||||||
"pbac_desc_create_event_types": "Създаване на типове събития",
|
"pbac_desc_create_event_types": "Създаване на типове събития",
|
||||||
"pbac_desc_view_event_types": "Преглед на типове събития",
|
"pbac_desc_view_event_types": "Преглед на типове събития",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "গোপন সভা",
|
"secret_meeting": "গোপন সভা",
|
||||||
"login_instead": "পরিবর্তে লগইন",
|
"login_instead": "পরিবর্তে লগইন",
|
||||||
"create_account": "অ্যাকাউন্ট তৈরি করুন",
|
"create_account": "অ্যাকাউন্ট তৈরি করুন",
|
||||||
|
"get_started": "শুরু করুন",
|
||||||
"confirm_password": "পাসওয়ার্ড নিশ্চিত করুন",
|
"confirm_password": "পাসওয়ার্ড নিশ্চিত করুন",
|
||||||
"reset_your_password": "আপনার ইমেল ঠিকানায় প্রেরিত নির্দেশাবলী সহ আপনার নতুন পাসওয়ার্ড সেট করুন।",
|
"reset_your_password": "আপনার ইমেল ঠিকানায় প্রেরিত নির্দেশাবলী সহ আপনার নতুন পাসওয়ার্ড সেট করুন।",
|
||||||
"org_banner_instructions": "এর চিত্র আপলোড করুন {{width}} প্রস্থ এবং {{height}} উচ্চতা।",
|
"org_banner_instructions": "এর চিত্র আপলোড করুন {{width}} প্রস্থ এবং {{height}} উচ্চতা।",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "অংশগ্রহণকারীরা",
|
"attendees": "অংশগ্রহণকারীরা",
|
||||||
"add_attendees": "অংশগ্রহণকারীদের যোগ করুন",
|
"add_attendees": "অংশগ্রহণকারীদের যোগ করুন",
|
||||||
"show_advanced_settings": "উন্নত সেটিংস দেখান",
|
"show_advanced_settings": "উন্নত সেটিংস দেখান",
|
||||||
|
"hide_advanced_settings": "উন্নত সেটিংস লুকান",
|
||||||
"event_name": "ইভেন্টের নাম",
|
"event_name": "ইভেন্টের নাম",
|
||||||
"event_name_in_calendar": "ইভেন্টের নাম সহ",
|
"event_name_in_calendar": "ইভেন্টের নাম সহ",
|
||||||
"event_name_tooltip": "নাম যে ক্যালেন্ডারে প্রদর্শিত হবে",
|
"event_name_tooltip": "নাম যে ক্যালেন্ডারে প্রদর্শিত হবে",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "যোগাযোগ বিক্রয়",
|
"contact_sales": "যোগাযোগ বিক্রয়",
|
||||||
"error_404": "ত্রুটি 404",
|
"error_404": "ত্রুটি 404",
|
||||||
"default": "ডিফল্ট",
|
"default": "ডিফল্ট",
|
||||||
"set_to_default": "ডিফল্ট সেট",
|
"set_to_default": "ডিফল্ট হিসাবে সেট করুন",
|
||||||
"new_schedule_btn": "নতুন সময়সূচী",
|
"new_schedule_btn": "নতুন সময়সূচী",
|
||||||
"add_new_schedule": "একটি নতুন সময়সূচি যুক্ত করুন",
|
"add_new_schedule": "একটি নতুন সময়সূচি যুক্ত করুন",
|
||||||
"add_new_calendar": "একটি নতুন ক্যালেন্ডার যুক্ত করুন",
|
"add_new_calendar": "একটি নতুন ক্যালেন্ডার যুক্ত করুন",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "আপনার দলের সদস্যরা এটি চালু থাকলে অন্য দলের সদস্যদের দেখতে সক্ষম হবেন না।",
|
"make_team_private_description": "আপনার দলের সদস্যরা এটি চালু থাকলে অন্য দলের সদস্যদের দেখতে সক্ষম হবেন না।",
|
||||||
"you_cannot_see_team_members": "আপনি কোনও বেসরকারী দলের সমস্ত দলের সদস্য দেখতে পারবেন না।",
|
"you_cannot_see_team_members": "আপনি কোনও বেসরকারী দলের সমস্ত দলের সদস্য দেখতে পারবেন না।",
|
||||||
"you_cannot_see_teams_of_org": "আপনি কোনও বেসরকারী সংস্থার দল দেখতে পাচ্ছেন না।",
|
"you_cannot_see_teams_of_org": "আপনি কোনও বেসরকারী সংস্থার দল দেখতে পাচ্ছেন না।",
|
||||||
"allow_booker_to_select_duration": "বুকারকে সময়কাল নির্বাচন করার অনুমতি দিন",
|
"allow_multiple_durations": "একাধিক সময়কাল অনুমতি দিন",
|
||||||
"impersonate_user_tip": "এই বৈশিষ্ট্যের সমস্ত ব্যবহার নিরীক্ষণ করা হয়।",
|
"impersonate_user_tip": "এই বৈশিষ্ট্যের সমস্ত ব্যবহার নিরীক্ষণ করা হয়।",
|
||||||
"impersonating_user_warning": "ব্যবহারকারীর নাম \"{{user}}\"।",
|
"impersonating_user_warning": "ব্যবহারকারীর নাম \"{{user}}\"।",
|
||||||
"impersonating_stop_instructions": "থামাতে এখানে ক্লিক করুন",
|
"impersonating_stop_instructions": "থামাতে এখানে ক্লিক করুন",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "বুকিং নিশ্চিতকরণ ব্যর্থ হয়েছে",
|
"booking_confirmation_failed": "বুকিং নিশ্চিতকরণ ব্যর্থ হয়েছে",
|
||||||
"not_enough_seats": "পর্যাপ্ত আসন নেই",
|
"not_enough_seats": "পর্যাপ্ত আসন নেই",
|
||||||
"form_builder_field_already_exists": "এই নাম সহ একটি ক্ষেত্র ইতিমধ্যে বিদ্যমান",
|
"form_builder_field_already_exists": "এই নাম সহ একটি ক্ষেত্র ইতিমধ্যে বিদ্যমান",
|
||||||
|
"guests_field_must_be_multiemail": "অতিথি ক্ষেত্রটি অবশ্যই 'একাধিক ইমেইল' ধরনের হতে হবে",
|
||||||
"show_on_booking_page": "বুকিং পৃষ্ঠায় দেখান",
|
"show_on_booking_page": "বুকিং পৃষ্ঠায় দেখান",
|
||||||
"visit_cancelled_booking": "আপনি বাতিল হওয়া বুকিং পৃষ্ঠাটি দেখতে পারেন",
|
"visit_cancelled_booking": "আপনি বাতিল হওয়া বুকিং পৃষ্ঠাটি দেখতে পারেন",
|
||||||
"get_started_zapier_templates": "জ্যাপিয়ার টেম্পলেটগুলি দিয়ে শুরু করুন",
|
"get_started_zapier_templates": "জ্যাপিয়ার টেম্পলেটগুলি দিয়ে শুরু করুন",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "টিম অ্যাডমিন দ্বারা লক",
|
"locked_by_team_admin": "টিম অ্যাডমিন দ্বারা লক",
|
||||||
"app_not_connected": "আপনি সংযুক্ত না একটি {{appName}} অ্যাকাউন্ট।",
|
"app_not_connected": "আপনি সংযুক্ত না একটি {{appName}} অ্যাকাউন্ট।",
|
||||||
"connect_now": "এখনই সংযুক্ত করুন",
|
"connect_now": "এখনই সংযুক্ত করুন",
|
||||||
|
"connect_now_unavailable_tooltip": "এখন সংযোগ করা বর্তমানে অনুপলব্ধ। অনুগ্রহ করে উপরের স্লট থেকে বুক করুন অথবা পরে আবার চেষ্টা করুন।",
|
||||||
"managed_event_dialog_confirm_button_one": "প্রতিস্থাপন এবং অবহিত {{count}} সদস্য",
|
"managed_event_dialog_confirm_button_one": "প্রতিস্থাপন এবং অবহিত {{count}} সদস্য",
|
||||||
"managed_event_dialog_confirm_button_other": "প্রতিস্থাপন এবং অবহিত {{count}} সদস্য",
|
"managed_event_dialog_confirm_button_other": "প্রতিস্থাপন এবং অবহিত {{count}} সদস্য",
|
||||||
"managed_event_dialog_title_one": "ইউআরএল /{{slug}} ইতিমধ্যে বিদ্যমান {{count}} সদস্য ",
|
"managed_event_dialog_title_one": "ইউআরএল /{{slug}} ইতিমধ্যে বিদ্যমান {{count}} সদস্য ",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "এন্টারপ্রাইজ",
|
"enterprise": "এন্টারপ্রাইজ",
|
||||||
"enterprise_description": "আপনার সংস্থা তৈরি করতে এন্টারপ্রাইজে আপগ্রেড করুন",
|
"enterprise_description": "আপনার সংস্থা তৈরি করতে এন্টারপ্রাইজে আপগ্রেড করুন",
|
||||||
"create_your_org": "আপনার সংস্থা তৈরি করুন",
|
"create_your_org": "আপনার সংস্থা তৈরি করুন",
|
||||||
"create_org": "সংস্থা তৈরি করুন",
|
"create_org": "সংগঠন তৈরি করুন",
|
||||||
"create_your_org_description": "সংস্থাগুলিতে আপগ্রেড করুন এবং একটি সাবডোমেন, ইউনিফাইড বিলিং, অন্তর্দৃষ্টি, বিস্তৃত হোয়াইটেলবেলিং এবং আরও অনেক কিছু পান",
|
"create_your_org_description": "সংস্থাগুলিতে আপগ্রেড করুন এবং একটি সাবডোমেন, ইউনিফাইড বিলিং, অন্তর্দৃষ্টি, বিস্তৃত হোয়াইটেলবেলিং এবং আরও অনেক কিছু পান",
|
||||||
"create_your_enterprise_description": "এন্টারপ্রাইজে আপগ্রেড করুন এবং অ্যাক্টিভ ডিরেক্টরি সিঙ্ক, এসসিআইএম স্বয়ংক্রিয় ব্যবহারকারীর প্রভিশনিং, ক্যাল.এই ভয়েস এজেন্টস, অ্যাডমিন এপিআই এবং আরও অনেক কিছুতে অ্যাক্সেস পান!",
|
"create_your_enterprise_description": "এন্টারপ্রাইজে আপগ্রেড করুন এবং অ্যাক্টিভ ডিরেক্টরি সিঙ্ক, এসসিআইএম স্বয়ংক্রিয় ব্যবহারকারীর প্রভিশনিং, ক্যাল.এই ভয়েস এজেন্টস, অ্যাডমিন এপিআই এবং আরও অনেক কিছুতে অ্যাক্সেস পান!",
|
||||||
"other_payment_app_enabled": "আপনি প্রতি ইভেন্টের জন্য কেবল একটি পেমেন্ট অ্যাপ্লিকেশন সক্ষম করতে পারেন",
|
"other_payment_app_enabled": "আপনি প্রতি ইভেন্টের জন্য কেবল একটি পেমেন্ট অ্যাপ্লিকেশন সক্ষম করতে পারেন",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "এগিয়ে যাওয়ার মাধ্যমে, আপনি আমাদের নিম্নলিখিত শর্তাবলীতে সম্মত হচ্ছেন",
|
"proceeding_agreement": "এগিয়ে যাওয়ার মাধ্যমে, আপনি আমাদের নিম্নলিখিত শর্তাবলীতে সম্মত হচ্ছেন",
|
||||||
"terms": "শর্তাবলী",
|
"terms": "শর্তাবলী",
|
||||||
"always_show_x_days": "সর্বদা {{x}} দিন উপলব্ধ",
|
"always_show_x_days": "সর্বদা {{x}} দিন উপলব্ধ",
|
||||||
|
"always_show_x_days_description": "প্রতিদিন স্বয়ংক্রিয়ভাবে {{x}}-দিনের বুকিং উইন্ডো সামনে সরিয়ে দেয়, যা নিশ্চিত করে যে আপনার ক্যালেন্ডার সবসময় পরবর্তী {{x}} দিনের উপলব্ধ স্লট দেখায়।",
|
||||||
"team_subscription_cancelled_successfully": "দলের সাবস্ক্রিপশন সফলভাবে বাতিল করা হয়েছে",
|
"team_subscription_cancelled_successfully": "দলের সাবস্ক্রিপশন সফলভাবে বাতিল করা হয়েছে",
|
||||||
"team_subscription_cancellation_error": "দলের সাবস্ক্রিপশন বাতিল করার সময় ত্রুটি",
|
"team_subscription_cancellation_error": "দলের সাবস্ক্রিপশন বাতিল করার সময় ত্রুটি",
|
||||||
"unable_to_subscribe_to_the_platform": "প্ল্যাটফর্ম পরিকল্পনায় সাবস্ক্রাইব করার চেষ্টা করার সময় একটি ত্রুটি ঘটেছে, দয়া করে পরে আবার চেষ্টা করুন",
|
"unable_to_subscribe_to_the_platform": "প্ল্যাটফর্ম পরিকল্পনায় সাবস্ক্রাইব করার চেষ্টা করার সময় একটি ত্রুটি ঘটেছে, দয়া করে পরে আবার চেষ্টা করুন",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "সাথে শেষ",
|
"filter_operator_ends_with": "সাথে শেষ",
|
||||||
"filter_operator_is_empty": "খালি আছে",
|
"filter_operator_is_empty": "খালি আছে",
|
||||||
"filter_operator_not_empty": "খালি নয়",
|
"filter_operator_not_empty": "খালি নয়",
|
||||||
"translate_description_button": "এআই ব্যবহার করে দর্শনার্থীর ব্রাউজার ভাষায় বর্ণনা অনুবাদ করুন",
|
"translate_description_button": "শিরোনাম এবং বিবরণ অটো অনুবাদ করুন",
|
||||||
|
"translate_description_button_description": "AI ব্যবহার করে দর্শকের ব্রাউজার ভাষায় শিরোনাম এবং বিবরণ স্বয়ংক্রিয়ভাবে অনুবাদ করুন।",
|
||||||
"rr_distribution_method": "বিতরণ",
|
"rr_distribution_method": "বিতরণ",
|
||||||
"rr_distribution_method_description": "সর্বাধিক প্রাপ্যতার জন্য বিতরণকে অনুকূলকরণের জন্য বা আরও সুষম অ্যাসাইনমেন্টের জন্য লক্ষ্য করার অনুমতি দেয়।",
|
"rr_distribution_method_description": "সর্বাধিক প্রাপ্যতার জন্য বিতরণকে অনুকূলকরণের জন্য বা আরও সুষম অ্যাসাইনমেন্টের জন্য লক্ষ্য করার অনুমতি দেয়।",
|
||||||
"rr_distribution_method_availability_title": "প্রাপ্যতা সর্বাধিক করুন",
|
"rr_distribution_method_availability_title": "প্রাপ্যতা সর্বাধিক করুন",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "সিট ফিচার পুনরাবৃত্ত ইভেন্ট সমর্থন করে না। এটিকে পুনরাবৃত্ত করতে সিট ফিচার নিষ্ক্রিয় করুন।",
|
"seats_doesnt_support_recurring": "সিট ফিচার পুনরাবৃত্ত ইভেন্ট সমর্থন করে না। এটিকে পুনরাবৃত্ত করতে সিট ফিচার নিষ্ক্রিয় করুন।",
|
||||||
"recurring_event_seats_error": "পুনরাবৃত্ত ইভেন্ট সিট ফিচার সমর্থন করে না। সিট ফিচার নিষ্ক্রিয় করুন অথবা ইভেন্টটিকে অপুনরাবৃত্ত করুন।",
|
"recurring_event_seats_error": "পুনরাবৃত্ত ইভেন্ট সিট ফিচার সমর্থন করে না। সিট ফিচার নিষ্ক্রিয় করুন অথবা ইভেন্টটিকে অপুনরাবৃত্ত করুন।",
|
||||||
"segment": "সেগমেন্ট",
|
"segment": "সেগমেন্ট",
|
||||||
|
"saved_filters": "সংরক্ষিত ফিল্টার",
|
||||||
"rename": "পুনঃনামকরণ",
|
"rename": "পুনঃনামকরণ",
|
||||||
"rename_segment": "সেগমেন্ট পুনঃনামকরণ",
|
"rename_segment": "সেগমেন্ট পুনঃনামকরণ",
|
||||||
"delete_segment": "সেগমেন্ট মুছুন",
|
"delete_segment": "সেগমেন্ট মুছুন",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "পিকলিস্ট",
|
"picklist": "পিকলিস্ট",
|
||||||
"most_cancelled_bookings": "সবচেয়ে বেশি বাতিল করা বুকিং",
|
"most_cancelled_bookings": "সবচেয়ে বেশি বাতিল করা বুকিং",
|
||||||
"interface_language": "ইন্টারফেস ভাষা",
|
"interface_language": "ইন্টারফেস ভাষা",
|
||||||
|
"interface_language_description": "বুকিং ইন্টারফেসের জন্য আপনার পছন্দের ভাষা সেট করুন",
|
||||||
"visitors_browser_language": "দর্শনার্থীর ব্রাউজার ভাষা",
|
"visitors_browser_language": "দর্শনার্থীর ব্রাউজার ভাষা",
|
||||||
"name_or_email": "নাম বা ইমেইল",
|
"name_or_email": "নাম বা ইমেইল",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "যদি কোন কন্টাক্ট না পাওয়া যায়, লিড মালিকে ফিরে যান যদি তা বিদ্যমান থাকে",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "যদি কোন কন্টাক্ট না পাওয়া যায়, লিড মালিকে ফিরে যান যদি তা বিদ্যমান থাকে",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "কর্মপ্রবাহ সেটিংস সম্পাদনা ও পরিবর্তন করুন",
|
"pbac_desc_update_workflows": "কর্মপ্রবাহ সেটিংস সম্পাদনা ও পরিবর্তন করুন",
|
||||||
"pbac_desc_delete_workflows": "সিস্টেম থেকে কর্মপ্রবাহ সরান",
|
"pbac_desc_delete_workflows": "সিস্টেম থেকে কর্মপ্রবাহ সরান",
|
||||||
"pbac_resource_webhook": "ওয়েবহুক",
|
"pbac_resource_webhook": "ওয়েবহুক",
|
||||||
|
"pbac_resource_availability": "প্রাপ্যতা",
|
||||||
|
"pbac_resource_out_of_office": "অফিসের বাইরে",
|
||||||
"pbac_desc_create_webhooks": "ওয়েবহুক তৈরি করুন",
|
"pbac_desc_create_webhooks": "ওয়েবহুক তৈরি করুন",
|
||||||
"pbac_desc_view_webhooks": "ওয়েবহুক দেখুন",
|
"pbac_desc_view_webhooks": "ওয়েবহুক দেখুন",
|
||||||
"pbac_desc_update_webhooks": "ওয়েবহুক আপডেট করুন",
|
"pbac_desc_update_webhooks": "ওয়েবহুক আপডেট করুন",
|
||||||
"pbac_desc_delete_webhooks": "ওয়েবহুক মুছুন",
|
"pbac_desc_delete_webhooks": "ওয়েবহুক মুছুন",
|
||||||
|
"pbac_desc_create_availability": "প্রাপ্যতা তৈরি করুন",
|
||||||
|
"pbac_desc_view_availability": "প্রাপ্যতা দেখুন",
|
||||||
|
"pbac_desc_update_availability": "প্রাপ্যতা আপডেট করুন",
|
||||||
|
"pbac_desc_delete_availability": "প্রাপ্যতা মুছুন",
|
||||||
|
"pbac_desc_create_out_of_office": "অফিসের বাইরে তৈরি করুন",
|
||||||
|
"pbac_desc_view_out_of_office": "অফিসের বাইরে দেখুন",
|
||||||
|
"pbac_desc_update_out_of_office": "অফিসের বাইরে আপডেট করুন",
|
||||||
|
"pbac_desc_delete_out_of_office": "অফিসের বাইরে মুছুন",
|
||||||
"pbac_desc_manage_workflows": "সমস্ত কর্মপ্রবাহের জন্য সম্পূর্ণ ব্যবস্থাপনা অ্যাক্সেস",
|
"pbac_desc_manage_workflows": "সমস্ত কর্মপ্রবাহের জন্য সম্পূর্ণ ব্যবস্থাপনা অ্যাক্সেস",
|
||||||
"pbac_desc_create_event_types": "ইভেন্টের ধরণ তৈরি করুন",
|
"pbac_desc_create_event_types": "ইভেন্টের ধরণ তৈরি করুন",
|
||||||
"pbac_desc_view_event_types": "ইভেন্টের ধরণ দেখুন",
|
"pbac_desc_view_event_types": "ইভেন্টের ধরণ দেখুন",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Reunió secreta",
|
"secret_meeting": "Reunió secreta",
|
||||||
"login_instead": "Inicia sessió en lloc seu",
|
"login_instead": "Inicia sessió en lloc seu",
|
||||||
"create_account": "Crea un compte",
|
"create_account": "Crea un compte",
|
||||||
|
"get_started": "Comença",
|
||||||
"confirm_password": "Confirma la contrasenya",
|
"confirm_password": "Confirma la contrasenya",
|
||||||
"reset_your_password": "Estableix la teva nova contrasenya amb les instruccions enviades al teu correu electrònic.",
|
"reset_your_password": "Estableix la teva nova contrasenya amb les instruccions enviades al teu correu electrònic.",
|
||||||
"org_banner_instructions": "Si us plau, puja una imatge de {{width}} d'amplada i {{height}} d'alçada.",
|
"org_banner_instructions": "Si us plau, puja una imatge de {{width}} d'amplada i {{height}} d'alçada.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Color del tipus d'esdeveniment (tema clar)",
|
"light_event_type_color": "Color del tipus d'esdeveniment (tema clar)",
|
||||||
"dark_event_type_color": "Color del tipus d'esdeveniment (tema fosc)",
|
"dark_event_type_color": "Color del tipus d'esdeveniment (tema fosc)",
|
||||||
"file_not_named": "L'arxiu no té el nom [idOrSlug]/[user]",
|
"file_not_named": "L'arxiu no té el nom [idOrSlug]/[user]",
|
||||||
"create_team": "Crea un equip",
|
"create_team": "Crea equip",
|
||||||
"name": "Nom",
|
"name": "Nom",
|
||||||
"nameless_team": "Equip sense nom",
|
"nameless_team": "Equip sense nom",
|
||||||
"oauth_clients": "Clients OAuth",
|
"oauth_clients": "Clients OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Assistents",
|
"attendees": "Assistents",
|
||||||
"add_attendees": "Afegir assistents",
|
"add_attendees": "Afegir assistents",
|
||||||
"show_advanced_settings": "Mostrar configuració avançada",
|
"show_advanced_settings": "Mostrar configuració avançada",
|
||||||
|
"hide_advanced_settings": "Amaga la configuració avançada",
|
||||||
"event_name": "Nom de l'esdeveniment",
|
"event_name": "Nom de l'esdeveniment",
|
||||||
"event_name_in_calendar": "Amb nom d'esdeveniment",
|
"event_name_in_calendar": "Amb nom d'esdeveniment",
|
||||||
"event_name_tooltip": "El nom que apareixerà als calendaris",
|
"event_name_tooltip": "El nom que apareixerà als calendaris",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Contacta amb vendes",
|
"contact_sales": "Contacta amb vendes",
|
||||||
"error_404": "Error 404",
|
"error_404": "Error 404",
|
||||||
"default": "Per defecte",
|
"default": "Per defecte",
|
||||||
"set_to_default": "Estableix per defecte",
|
"set_to_default": "Estableix com a predeterminat",
|
||||||
"new_schedule_btn": "Nou horari",
|
"new_schedule_btn": "Nou horari",
|
||||||
"add_new_schedule": "Afegeix un nou horari",
|
"add_new_schedule": "Afegeix un nou horari",
|
||||||
"add_new_calendar": "Afegeix un nou calendari",
|
"add_new_calendar": "Afegeix un nou calendari",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Els membres del teu equip no podran veure altres membres quan això estigui activat.",
|
"make_team_private_description": "Els membres del teu equip no podran veure altres membres quan això estigui activat.",
|
||||||
"you_cannot_see_team_members": "No pots veure tots els membres d'un equip privat.",
|
"you_cannot_see_team_members": "No pots veure tots els membres d'un equip privat.",
|
||||||
"you_cannot_see_teams_of_org": "No pots veure els equips d'una organització privada.",
|
"you_cannot_see_teams_of_org": "No pots veure els equips d'una organització privada.",
|
||||||
"allow_booker_to_select_duration": "Permetre que el reservant seleccioni la durada",
|
"allow_multiple_durations": "Permet múltiples duracions",
|
||||||
"impersonate_user_tip": "Tots els usos d'aquesta funcionalitat són auditats.",
|
"impersonate_user_tip": "Tots els usos d'aquesta funcionalitat són auditats.",
|
||||||
"impersonating_user_warning": "Suplantant l'usuari \"{{user}}\".",
|
"impersonating_user_warning": "Suplantant l'usuari \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Fes clic aquí per aturar",
|
"impersonating_stop_instructions": "Fes clic aquí per aturar",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "La confirmació de la reserva ha fallat",
|
"booking_confirmation_failed": "La confirmació de la reserva ha fallat",
|
||||||
"not_enough_seats": "No hi ha prou places",
|
"not_enough_seats": "No hi ha prou places",
|
||||||
"form_builder_field_already_exists": "Ja existeix un camp amb aquest nom",
|
"form_builder_field_already_exists": "Ja existeix un camp amb aquest nom",
|
||||||
|
"guests_field_must_be_multiemail": "El camp de convidats ha de ser del tipus 'Múltiples correus electrònics'",
|
||||||
"show_on_booking_page": "Mostra a la pàgina de reserva",
|
"show_on_booking_page": "Mostra a la pàgina de reserva",
|
||||||
"visit_cancelled_booking": "Pots visitar la pàgina de reserva cancel·lada",
|
"visit_cancelled_booking": "Pots visitar la pàgina de reserva cancel·lada",
|
||||||
"get_started_zapier_templates": "Comença amb les plantilles de Zapier",
|
"get_started_zapier_templates": "Comença amb les plantilles de Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bloquejat per l'administrador de l'equip",
|
"locked_by_team_admin": "Bloquejat per l'administrador de l'equip",
|
||||||
"app_not_connected": "No has connectat cap compte de {{appName}}.",
|
"app_not_connected": "No has connectat cap compte de {{appName}}.",
|
||||||
"connect_now": "Connecta ara",
|
"connect_now": "Connecta ara",
|
||||||
|
"connect_now_unavailable_tooltip": "Connectar ara no està disponible actualment. Si us plau, reserva des dels espais disponibles anteriors o torna-ho a provar més tard.",
|
||||||
"managed_event_dialog_confirm_button_one": "Reemplaça i notifica {{count}} membre",
|
"managed_event_dialog_confirm_button_one": "Reemplaça i notifica {{count}} membre",
|
||||||
"managed_event_dialog_confirm_button_other": "Reemplaça i notifica {{count}} membres",
|
"managed_event_dialog_confirm_button_other": "Reemplaça i notifica {{count}} membres",
|
||||||
"managed_event_dialog_title_one": "L'URL /{{slug}} ja existeix per a {{count}} membre. Vols reemplaçar-la?",
|
"managed_event_dialog_title_one": "L'URL /{{slug}} ja existeix per a {{count}} membre. Vols reemplaçar-la?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "En continuar, acceptes les nostres",
|
"proceeding_agreement": "En continuar, acceptes les nostres",
|
||||||
"terms": "Condicions",
|
"terms": "Condicions",
|
||||||
"always_show_x_days": "Sempre {{x}} dies disponibles",
|
"always_show_x_days": "Sempre {{x}} dies disponibles",
|
||||||
|
"always_show_x_days_description": "Mou automàticament la finestra de reserva de {{x}} dies cap endavant cada dia, assegurant que el teu calendari sempre mostri els propers {{x}} dies d'espais disponibles.",
|
||||||
"team_subscription_cancelled_successfully": "Subscripció d'equip cancel·lada amb èxit",
|
"team_subscription_cancelled_successfully": "Subscripció d'equip cancel·lada amb èxit",
|
||||||
"team_subscription_cancellation_error": "Error en cancel·lar la subscripció d'equip",
|
"team_subscription_cancellation_error": "Error en cancel·lar la subscripció d'equip",
|
||||||
"unable_to_subscribe_to_the_platform": "S'ha produït un error en intentar subscriure's al pla de la plataforma, torna-ho a provar més tard",
|
"unable_to_subscribe_to_the_platform": "S'ha produït un error en intentar subscriure's al pla de la plataforma, torna-ho a provar més tard",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Acaba amb",
|
"filter_operator_ends_with": "Acaba amb",
|
||||||
"filter_operator_is_empty": "És buit",
|
"filter_operator_is_empty": "És buit",
|
||||||
"filter_operator_not_empty": "No és buit",
|
"filter_operator_not_empty": "No és buit",
|
||||||
"translate_description_button": "Tradueix el títol/descripció a l'idioma del navegador del visitant utilitzant IA",
|
"translate_description_button": "Tradueix automàticament el títol i la descripció",
|
||||||
|
"translate_description_button_description": "Tradueix automàticament els títols i les descripcions a l'idioma del navegador del visitant utilitzant IA.",
|
||||||
"rr_distribution_method": "Distribució",
|
"rr_distribution_method": "Distribució",
|
||||||
"rr_distribution_method_description": "Permet optimitzar la distribució per maximitzar la disponibilitat o aconseguir una assignació més equilibrada",
|
"rr_distribution_method_description": "Permet optimitzar la distribució per maximitzar la disponibilitat o aconseguir una assignació més equilibrada",
|
||||||
"rr_distribution_method_availability_title": "Maximitzar disponibilitat",
|
"rr_distribution_method_availability_title": "Maximitzar disponibilitat",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "La funció de seients no admet esdeveniments recurrents. Desactiveu la funció de seients per fer-lo recurrent.",
|
"seats_doesnt_support_recurring": "La funció de seients no admet esdeveniments recurrents. Desactiveu la funció de seients per fer-lo recurrent.",
|
||||||
"recurring_event_seats_error": "Els esdeveniments recurrents no admeten la funció de seients. Desactiveu la funció de seients o feu que l'esdeveniment no sigui recurrent.",
|
"recurring_event_seats_error": "Els esdeveniments recurrents no admeten la funció de seients. Desactiveu la funció de seients o feu que l'esdeveniment no sigui recurrent.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Filtres desats",
|
||||||
"rename": "Canviar el nom",
|
"rename": "Canviar el nom",
|
||||||
"rename_segment": "Canviar el nom del segment",
|
"rename_segment": "Canviar el nom del segment",
|
||||||
"delete_segment": "Eliminar segment",
|
"delete_segment": "Eliminar segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Llista de selecció",
|
"picklist": "Llista de selecció",
|
||||||
"most_cancelled_bookings": "Més reserves cancel·lades",
|
"most_cancelled_bookings": "Més reserves cancel·lades",
|
||||||
"interface_language": "Idioma de la interfície",
|
"interface_language": "Idioma de la interfície",
|
||||||
|
"interface_language_description": "Estableix el teu idioma preferit per a la interfície de reserva",
|
||||||
"visitors_browser_language": "Idioma del navegador del visitant",
|
"visitors_browser_language": "Idioma del navegador del visitant",
|
||||||
"name_or_email": "Nom o correu electrònic",
|
"name_or_email": "Nom o correu electrònic",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no es troba cap contacte, recorre al propietari del lead si existeix",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no es troba cap contacte, recorre al propietari del lead si existeix",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editar i modificar la configuració dels fluxos de treball",
|
"pbac_desc_update_workflows": "Editar i modificar la configuració dels fluxos de treball",
|
||||||
"pbac_desc_delete_workflows": "Eliminar fluxos de treball del sistema",
|
"pbac_desc_delete_workflows": "Eliminar fluxos de treball del sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilitat",
|
||||||
|
"pbac_resource_out_of_office": "Fora de l'oficina",
|
||||||
"pbac_desc_create_webhooks": "Crea webhooks",
|
"pbac_desc_create_webhooks": "Crea webhooks",
|
||||||
"pbac_desc_view_webhooks": "Visualitza webhooks",
|
"pbac_desc_view_webhooks": "Visualitza webhooks",
|
||||||
"pbac_desc_update_webhooks": "Actualitza els webhooks",
|
"pbac_desc_update_webhooks": "Actualitza els webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Elimina els webhooks",
|
"pbac_desc_delete_webhooks": "Elimina els webhooks",
|
||||||
|
"pbac_desc_create_availability": "Crear disponibilitat",
|
||||||
|
"pbac_desc_view_availability": "Veure disponibilitat",
|
||||||
|
"pbac_desc_update_availability": "Actualitzar disponibilitat",
|
||||||
|
"pbac_desc_delete_availability": "Eliminar disponibilitat",
|
||||||
|
"pbac_desc_create_out_of_office": "Crear fora de l'oficina",
|
||||||
|
"pbac_desc_view_out_of_office": "Veure fora de l'oficina",
|
||||||
|
"pbac_desc_update_out_of_office": "Actualitzar fora de l'oficina",
|
||||||
|
"pbac_desc_delete_out_of_office": "Eliminar fora de l'oficina",
|
||||||
"pbac_desc_manage_workflows": "Accés complet de gestió a tots els fluxos de treball",
|
"pbac_desc_manage_workflows": "Accés complet de gestió a tots els fluxos de treball",
|
||||||
"pbac_desc_create_event_types": "Crear tipus d'esdeveniments",
|
"pbac_desc_create_event_types": "Crear tipus d'esdeveniments",
|
||||||
"pbac_desc_view_event_types": "Veure tipus d'esdeveniments",
|
"pbac_desc_view_event_types": "Veure tipus d'esdeveniments",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Tajná schůzka",
|
"secret_meeting": "Tajná schůzka",
|
||||||
"login_instead": "Namísto toho se přihlásit",
|
"login_instead": "Namísto toho se přihlásit",
|
||||||
"create_account": "Vytvořit účet",
|
"create_account": "Vytvořit účet",
|
||||||
|
"get_started": "Začít",
|
||||||
"confirm_password": "Potvrdit heslo",
|
"confirm_password": "Potvrdit heslo",
|
||||||
"reset_your_password": "Nastavte si nové heslo podle pokynů odeslaných na vaši e-mailovou adresu.",
|
"reset_your_password": "Nastavte si nové heslo podle pokynů odeslaných na vaši e-mailovou adresu.",
|
||||||
"org_banner_instructions": "Nahrajte prosím obrázek o šířce {{width}} a výšce {{height}}.",
|
"org_banner_instructions": "Nahrajte prosím obrázek o šířce {{width}} a výšce {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Účastníci",
|
"attendees": "Účastníci",
|
||||||
"add_attendees": "Přidat účastníky",
|
"add_attendees": "Přidat účastníky",
|
||||||
"show_advanced_settings": "Zobrazit pokročilá nastavení",
|
"show_advanced_settings": "Zobrazit pokročilá nastavení",
|
||||||
|
"hide_advanced_settings": "Skrýt pokročilá nastavení",
|
||||||
"event_name": "Název události",
|
"event_name": "Název události",
|
||||||
"event_name_in_calendar": "Název události v kalendáři",
|
"event_name_in_calendar": "Název události v kalendáři",
|
||||||
"event_name_tooltip": "Název, který se zobrazí v kalendářích",
|
"event_name_tooltip": "Název, který se zobrazí v kalendářích",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Po zapnutí této funkce neuvidí členové vašeho týmu ostatní členy týmu.",
|
"make_team_private_description": "Po zapnutí této funkce neuvidí členové vašeho týmu ostatní členy týmu.",
|
||||||
"you_cannot_see_team_members": "V soukromém týmu nelze vidět všechny členy týmu.",
|
"you_cannot_see_team_members": "V soukromém týmu nelze vidět všechny členy týmu.",
|
||||||
"you_cannot_see_teams_of_org": "Nemůžete vidět týmy soukromé organizace.",
|
"you_cannot_see_teams_of_org": "Nemůžete vidět týmy soukromé organizace.",
|
||||||
"allow_booker_to_select_duration": "Umožnit rezervující osobě vybrat dobu trvání",
|
"allow_multiple_durations": "Povolit více délek trvání",
|
||||||
"impersonate_user_tip": "Všechna použití této funkce jsou kontrolována.",
|
"impersonate_user_tip": "Všechna použití této funkce jsou kontrolována.",
|
||||||
"impersonating_user_warning": "Zosobnění uživatelského jména „{{user}}“.",
|
"impersonating_user_warning": "Zosobnění uživatelského jména „{{user}}“.",
|
||||||
"impersonating_stop_instructions": "Kliknutím sem zastavíte",
|
"impersonating_stop_instructions": "Kliknutím sem zastavíte",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Potvrzení rezervace se nezdařilo",
|
"booking_confirmation_failed": "Potvrzení rezervace se nezdařilo",
|
||||||
"not_enough_seats": "Nedostatek míst",
|
"not_enough_seats": "Nedostatek míst",
|
||||||
"form_builder_field_already_exists": "Pole s tímto názvem již existuje",
|
"form_builder_field_already_exists": "Pole s tímto názvem již existuje",
|
||||||
|
"guests_field_must_be_multiemail": "Pole pro hosty musí být typu 'Více e-mailů'",
|
||||||
"show_on_booking_page": "Zobrazit na stránce rezervace",
|
"show_on_booking_page": "Zobrazit na stránce rezervace",
|
||||||
"visit_cancelled_booking": "Můžete navštívit stránku zrušené rezervace",
|
"visit_cancelled_booking": "Můžete navštívit stránku zrušené rezervace",
|
||||||
"get_started_zapier_templates": "Začněte používat šablony Zapier",
|
"get_started_zapier_templates": "Začněte používat šablony Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Uzamčeno správcem týmu",
|
"locked_by_team_admin": "Uzamčeno správcem týmu",
|
||||||
"app_not_connected": "Nemáte připojený účet {{appName}}.",
|
"app_not_connected": "Nemáte připojený účet {{appName}}.",
|
||||||
"connect_now": "Připojte se",
|
"connect_now": "Připojte se",
|
||||||
|
"connect_now_unavailable_tooltip": "Připojit nyní je momentálně nedostupné. Prosím rezervujte si z výše uvedených termínů nebo to zkuste později.",
|
||||||
"managed_event_dialog_confirm_button_one": "Nahradit a upozornit {{count}} člena",
|
"managed_event_dialog_confirm_button_one": "Nahradit a upozornit {{count}} člena",
|
||||||
"managed_event_dialog_confirm_button_other": "Nahradit a upozornit členy {{count}}",
|
"managed_event_dialog_confirm_button_other": "Nahradit a upozornit členy {{count}}",
|
||||||
"managed_event_dialog_title_one": "Adresa URL /{{slug}} již existuje pro {{count}} člena. Chcete ji nahradit?",
|
"managed_event_dialog_title_one": "Adresa URL /{{slug}} již existuje pro {{count}} člena. Chcete ji nahradit?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Pokračováním souhlasíte s našimi",
|
"proceeding_agreement": "Pokračováním souhlasíte s našimi",
|
||||||
"terms": "Podmínkami",
|
"terms": "Podmínkami",
|
||||||
"always_show_x_days": "Vždy dostupné {{x}} dní",
|
"always_show_x_days": "Vždy dostupné {{x}} dní",
|
||||||
|
"always_show_x_days_description": "Automaticky posouvá {{x}}-denní rezervační okno každý den vpřed, což zajišťuje, že váš kalendář vždy zobrazuje dostupné termíny na příštích {{x}} dní.",
|
||||||
"team_subscription_cancelled_successfully": "Předplatné týmu bylo úspěšně zrušeno",
|
"team_subscription_cancelled_successfully": "Předplatné týmu bylo úspěšně zrušeno",
|
||||||
"team_subscription_cancellation_error": "Chyba při rušení předplatného týmu",
|
"team_subscription_cancellation_error": "Chyba při rušení předplatného týmu",
|
||||||
"unable_to_subscribe_to_the_platform": "Při pokusu o přihlášení k plánu platformy došlo k chybě, zkuste to prosím znovu později",
|
"unable_to_subscribe_to_the_platform": "Při pokusu o přihlášení k plánu platformy došlo k chybě, zkuste to prosím znovu později",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Končí na",
|
"filter_operator_ends_with": "Končí na",
|
||||||
"filter_operator_is_empty": "Je prázdné",
|
"filter_operator_is_empty": "Je prázdné",
|
||||||
"filter_operator_not_empty": "Není prázdné",
|
"filter_operator_not_empty": "Není prázdné",
|
||||||
"translate_description_button": "Přeložit název/popis do jazyka prohlížeče návštěvníka pomocí AI",
|
"translate_description_button": "Automaticky přeložit název a popis",
|
||||||
|
"translate_description_button_description": "Automaticky překládá názvy a popisy do jazyka prohlížeče návštěvníka pomocí umělé inteligence.",
|
||||||
"rr_distribution_method": "Distribuce",
|
"rr_distribution_method": "Distribuce",
|
||||||
"rr_distribution_method_description": "Umožňuje optimalizovat distribuci pro maximální dostupnost nebo pro vyváženější přidělování.",
|
"rr_distribution_method_description": "Umožňuje optimalizovat distribuci pro maximální dostupnost nebo pro vyváženější přidělování.",
|
||||||
"rr_distribution_method_availability_title": "Maximalizovat dostupnost",
|
"rr_distribution_method_availability_title": "Maximalizovat dostupnost",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Funkce míst nepodporuje opakující se události. Zakažte funkci míst pro vytvoření opakující se události.",
|
"seats_doesnt_support_recurring": "Funkce míst nepodporuje opakující se události. Zakažte funkci míst pro vytvoření opakující se události.",
|
||||||
"recurring_event_seats_error": "Opakující se událost nepodporuje funkci míst. Zakažte funkci míst nebo změňte událost na neopakující se.",
|
"recurring_event_seats_error": "Opakující se událost nepodporuje funkci míst. Zakažte funkci míst nebo změňte událost na neopakující se.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Uložené filtry",
|
||||||
"rename": "Přejmenovat",
|
"rename": "Přejmenovat",
|
||||||
"rename_segment": "Přejmenovat segment",
|
"rename_segment": "Přejmenovat segment",
|
||||||
"delete_segment": "Smazat segment",
|
"delete_segment": "Smazat segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Výběrový seznam",
|
"picklist": "Výběrový seznam",
|
||||||
"most_cancelled_bookings": "Nejvíce zrušených rezervací",
|
"most_cancelled_bookings": "Nejvíce zrušených rezervací",
|
||||||
"interface_language": "Jazyk rozhraní",
|
"interface_language": "Jazyk rozhraní",
|
||||||
|
"interface_language_description": "Nastavte preferovaný jazyk pro rezervační rozhraní",
|
||||||
"visitors_browser_language": "Jazyk prohlížeče návštěvníka",
|
"visitors_browser_language": "Jazyk prohlížeče návštěvníka",
|
||||||
"name_or_email": "Jméno nebo e-mail",
|
"name_or_email": "Jméno nebo e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Pokud není nalezen žádný kontakt, použít vlastníka leadu, pokud existuje",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Pokud není nalezen žádný kontakt, použít vlastníka leadu, pokud existuje",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Upravovat a měnit nastavení pracovních postupů",
|
"pbac_desc_update_workflows": "Upravovat a měnit nastavení pracovních postupů",
|
||||||
"pbac_desc_delete_workflows": "Odstraňovat pracovní postupy ze systému",
|
"pbac_desc_delete_workflows": "Odstraňovat pracovní postupy ze systému",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Dostupnost",
|
||||||
|
"pbac_resource_out_of_office": "Mimo kancelář",
|
||||||
"pbac_desc_create_webhooks": "Vytvářet webhooky",
|
"pbac_desc_create_webhooks": "Vytvářet webhooky",
|
||||||
"pbac_desc_view_webhooks": "Zobrazit webhooky",
|
"pbac_desc_view_webhooks": "Zobrazit webhooky",
|
||||||
"pbac_desc_update_webhooks": "Aktualizovat webhooky",
|
"pbac_desc_update_webhooks": "Aktualizovat webhooky",
|
||||||
"pbac_desc_delete_webhooks": "Smazat webhooky",
|
"pbac_desc_delete_webhooks": "Smazat webhooky",
|
||||||
|
"pbac_desc_create_availability": "Vytvořit dostupnost",
|
||||||
|
"pbac_desc_view_availability": "Zobrazit dostupnost",
|
||||||
|
"pbac_desc_update_availability": "Aktualizovat dostupnost",
|
||||||
|
"pbac_desc_delete_availability": "Smazat dostupnost",
|
||||||
|
"pbac_desc_create_out_of_office": "Vytvořit mimo kancelář",
|
||||||
|
"pbac_desc_view_out_of_office": "Zobrazit mimo kancelář",
|
||||||
|
"pbac_desc_update_out_of_office": "Aktualizovat mimo kancelář",
|
||||||
|
"pbac_desc_delete_out_of_office": "Smazat mimo kancelář",
|
||||||
"pbac_desc_manage_workflows": "Plný přístup ke správě všech pracovních postupů",
|
"pbac_desc_manage_workflows": "Plný přístup ke správě všech pracovních postupů",
|
||||||
"pbac_desc_create_event_types": "Vytvářet typy událostí",
|
"pbac_desc_create_event_types": "Vytvářet typy událostí",
|
||||||
"pbac_desc_view_event_types": "Zobrazit typy událostí",
|
"pbac_desc_view_event_types": "Zobrazit typy událostí",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Skjult Møde",
|
"secret_meeting": "Skjult Møde",
|
||||||
"login_instead": "Log ind i stedet",
|
"login_instead": "Log ind i stedet",
|
||||||
"create_account": "Opret Konto",
|
"create_account": "Opret Konto",
|
||||||
|
"get_started": "Kom i gang",
|
||||||
"confirm_password": "Bekræft adgangskode",
|
"confirm_password": "Bekræft adgangskode",
|
||||||
"reset_your_password": "Indstil din nye adgangskode med instruktionerne sendt til din e-mailadresse.",
|
"reset_your_password": "Indstil din nye adgangskode med instruktionerne sendt til din e-mailadresse.",
|
||||||
"org_banner_instructions": "Upload venligst et billede med {{width}} bredde og {{height}} højde.",
|
"org_banner_instructions": "Upload venligst et billede med {{width}} bredde og {{height}} højde.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Begivenhedstype farve (Lyst tema)",
|
"light_event_type_color": "Begivenhedstype farve (Lyst tema)",
|
||||||
"dark_event_type_color": "Begivenhedstype farve (Mørkt tema)",
|
"dark_event_type_color": "Begivenhedstype farve (Mørkt tema)",
|
||||||
"file_not_named": "Filen er ikke navngivet [idOrSlug]/[user]",
|
"file_not_named": "Filen er ikke navngivet [idOrSlug]/[user]",
|
||||||
"create_team": "Opret Team",
|
"create_team": "Opret team",
|
||||||
"name": "Navn",
|
"name": "Navn",
|
||||||
"nameless_team": "Unavngivet team",
|
"nameless_team": "Unavngivet team",
|
||||||
"oauth_clients": "OAuth-klienter",
|
"oauth_clients": "OAuth-klienter",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Deltagere",
|
"attendees": "Deltagere",
|
||||||
"add_attendees": "Tilføj deltagere",
|
"add_attendees": "Tilføj deltagere",
|
||||||
"show_advanced_settings": "Vis avancerede indstillinger",
|
"show_advanced_settings": "Vis avancerede indstillinger",
|
||||||
|
"hide_advanced_settings": "Skjul avancerede indstillinger",
|
||||||
"event_name": "Begivenhedsnavn",
|
"event_name": "Begivenhedsnavn",
|
||||||
"event_name_in_calendar": "Begivenhedsnavn i kalender",
|
"event_name_in_calendar": "Begivenhedsnavn i kalender",
|
||||||
"event_name_tooltip": "Navnet, der vises i kalendere",
|
"event_name_tooltip": "Navnet, der vises i kalendere",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Dine teammedlemmer vil ikke kunne se andre teammedlemmer, når dette er slået til.",
|
"make_team_private_description": "Dine teammedlemmer vil ikke kunne se andre teammedlemmer, når dette er slået til.",
|
||||||
"you_cannot_see_team_members": "Du kan ikke se alle teammedlemmer i et privat team.",
|
"you_cannot_see_team_members": "Du kan ikke se alle teammedlemmer i et privat team.",
|
||||||
"you_cannot_see_teams_of_org": "Du kan ikke se teams i en privat organisation.",
|
"you_cannot_see_teams_of_org": "Du kan ikke se teams i en privat organisation.",
|
||||||
"allow_booker_to_select_duration": "Tillad bookeren at vælge varighed",
|
"allow_multiple_durations": "Tillad flere varigheder",
|
||||||
"impersonate_user_tip": "Al brug af denne funktion er revideret.",
|
"impersonate_user_tip": "Al brug af denne funktion er revideret.",
|
||||||
"impersonating_user_warning": "Efterligner brugernavnet \"{{user}}\".",
|
"impersonating_user_warning": "Efterligner brugernavnet \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Klik her for at stoppe",
|
"impersonating_stop_instructions": "Klik her for at stoppe",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Reservationsbekræftelse mislykkedes",
|
"booking_confirmation_failed": "Reservationsbekræftelse mislykkedes",
|
||||||
"not_enough_seats": "Ikke nok pladser",
|
"not_enough_seats": "Ikke nok pladser",
|
||||||
"form_builder_field_already_exists": "Et felt med dette navn findes allerede",
|
"form_builder_field_already_exists": "Et felt med dette navn findes allerede",
|
||||||
|
"guests_field_must_be_multiemail": "Gæstefeltet skal være af typen 'Flere e-mails'",
|
||||||
"show_on_booking_page": "Vis på bookingsiden",
|
"show_on_booking_page": "Vis på bookingsiden",
|
||||||
"visit_cancelled_booking": "Du kan besøge siden for den aflyste booking",
|
"visit_cancelled_booking": "Du kan besøge siden for den aflyste booking",
|
||||||
"get_started_zapier_templates": "Kom i gang med Zapier skabeloner",
|
"get_started_zapier_templates": "Kom i gang med Zapier skabeloner",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Låst af teamadministrator",
|
"locked_by_team_admin": "Låst af teamadministrator",
|
||||||
"app_not_connected": "Du har ikke forbundet en {{appName}}-konto.",
|
"app_not_connected": "Du har ikke forbundet en {{appName}}-konto.",
|
||||||
"connect_now": "Forbind nu",
|
"connect_now": "Forbind nu",
|
||||||
|
"connect_now_unavailable_tooltip": "Forbind nu er i øjeblikket ikke tilgængelig. Book venligst fra ovenstående tidsrum eller prøv igen senere.",
|
||||||
"managed_event_dialog_confirm_button_one": "Erstat og underret {{count}} medlem",
|
"managed_event_dialog_confirm_button_one": "Erstat og underret {{count}} medlem",
|
||||||
"managed_event_dialog_confirm_button_other": "Erstat og underret {{count}} medlemmer",
|
"managed_event_dialog_confirm_button_other": "Erstat og underret {{count}} medlemmer",
|
||||||
"managed_event_dialog_title_one": "URL'en /{{slug}} findes allerede for {{count}} medlem. Vil du erstatte den?",
|
"managed_event_dialog_title_one": "URL'en /{{slug}} findes allerede for {{count}} medlem. Vil du erstatte den?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Virksomhed",
|
"enterprise": "Virksomhed",
|
||||||
"enterprise_description": "Opgrader til Enterprise for at oprette din organisation",
|
"enterprise_description": "Opgrader til Enterprise for at oprette din organisation",
|
||||||
"create_your_org": "Opret din Organisation",
|
"create_your_org": "Opret din Organisation",
|
||||||
"create_org": "Opret Organisation",
|
"create_org": "Opret organisation",
|
||||||
"create_your_org_description": "Opgrader til Organisationer og få et subdomæne, samlet fakturering, Insights, omfattende whitelabeling og mere",
|
"create_your_org_description": "Opgrader til Organisationer og få et subdomæne, samlet fakturering, Insights, omfattende whitelabeling og mere",
|
||||||
"create_your_enterprise_description": "Opgrader til Enterprise og få adgang til Active Directory Sync, SCIM Automatisk Brugerprovisionering, Cal.ai Voice Agents, Admin API'er og mere!",
|
"create_your_enterprise_description": "Opgrader til Enterprise og få adgang til Active Directory Sync, SCIM Automatisk Brugerprovisionering, Cal.ai Voice Agents, Admin API'er og mere!",
|
||||||
"other_payment_app_enabled": "Du kan kun aktivere én betalingsapp pr. begivenhedstype",
|
"other_payment_app_enabled": "Du kan kun aktivere én betalingsapp pr. begivenhedstype",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Ved at fortsætte accepterer du vores",
|
"proceeding_agreement": "Ved at fortsætte accepterer du vores",
|
||||||
"terms": "Vilkår",
|
"terms": "Vilkår",
|
||||||
"always_show_x_days": "Vis altid {{x}} dage tilgængelige",
|
"always_show_x_days": "Vis altid {{x}} dage tilgængelige",
|
||||||
|
"always_show_x_days_description": "Flytter automatisk {{x}}-dages bookingvinduet frem hver dag, så din kalender altid viser de næste {{x}} dages tilgængelige tidsrum.",
|
||||||
"team_subscription_cancelled_successfully": "Teamabonnement annulleret med succes",
|
"team_subscription_cancelled_successfully": "Teamabonnement annulleret med succes",
|
||||||
"team_subscription_cancellation_error": "Fejl under annullering af teamabonnement",
|
"team_subscription_cancellation_error": "Fejl under annullering af teamabonnement",
|
||||||
"unable_to_subscribe_to_the_platform": "Der opstod en fejl under forsøget på at abonnere på platformplanen, prøv venligst igen senere",
|
"unable_to_subscribe_to_the_platform": "Der opstod en fejl under forsøget på at abonnere på platformplanen, prøv venligst igen senere",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Slutter med",
|
"filter_operator_ends_with": "Slutter med",
|
||||||
"filter_operator_is_empty": "Er tom",
|
"filter_operator_is_empty": "Er tom",
|
||||||
"filter_operator_not_empty": "Er ikke tom",
|
"filter_operator_not_empty": "Er ikke tom",
|
||||||
"translate_description_button": "Oversæt titel/beskrivelse til besøgendes browsersprog ved hjælp af AI",
|
"translate_description_button": "Autooversæt titel og beskrivelse",
|
||||||
|
"translate_description_button_description": "Oversæt automatisk titler og beskrivelser til besøgendes browsersprog ved hjælp af AI.",
|
||||||
"rr_distribution_method": "Distribution",
|
"rr_distribution_method": "Distribution",
|
||||||
"rr_distribution_method_description": "Gør det muligt at optimere distributionen for maksimal tilgængelighed eller for at opnå en mere balanceret tildeling.",
|
"rr_distribution_method_description": "Gør det muligt at optimere distributionen for maksimal tilgængelighed eller for at opnå en mere balanceret tildeling.",
|
||||||
"rr_distribution_method_availability_title": "Maksimer tilgængelighed",
|
"rr_distribution_method_availability_title": "Maksimer tilgængelighed",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Sædefunktionen understøtter ikke tilbagevendende begivenheder. Deaktiver sædefunktionen for at gøre den tilbagevendende.",
|
"seats_doesnt_support_recurring": "Sædefunktionen understøtter ikke tilbagevendende begivenheder. Deaktiver sædefunktionen for at gøre den tilbagevendende.",
|
||||||
"recurring_event_seats_error": "Tilbagevendende begivenheder understøtter ikke sædefunktionen. Deaktiver sædefunktionen eller gør begivenheden ikke-tilbagevendende.",
|
"recurring_event_seats_error": "Tilbagevendende begivenheder understøtter ikke sædefunktionen. Deaktiver sædefunktionen eller gør begivenheden ikke-tilbagevendende.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Gemte filtre",
|
||||||
"rename": "Omdøb",
|
"rename": "Omdøb",
|
||||||
"rename_segment": "Omdøb segment",
|
"rename_segment": "Omdøb segment",
|
||||||
"delete_segment": "Slet segment",
|
"delete_segment": "Slet segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Valgliste",
|
"picklist": "Valgliste",
|
||||||
"most_cancelled_bookings": "Flest annullerede",
|
"most_cancelled_bookings": "Flest annullerede",
|
||||||
"interface_language": "Grænsefladesprog",
|
"interface_language": "Grænsefladesprog",
|
||||||
|
"interface_language_description": "Indstil dit foretrukne sprog for bookinggrænsefladen",
|
||||||
"visitors_browser_language": "Besøgerens browsersprog",
|
"visitors_browser_language": "Besøgerens browsersprog",
|
||||||
"name_or_email": "Navn eller e-mail",
|
"name_or_email": "Navn eller e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hvis ingen kontakt findes, fald tilbage til lead-ejeren, hvis denne eksisterer",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hvis ingen kontakt findes, fald tilbage til lead-ejeren, hvis denne eksisterer",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Rediger og tilpas workflowindstillinger",
|
"pbac_desc_update_workflows": "Rediger og tilpas workflowindstillinger",
|
||||||
"pbac_desc_delete_workflows": "Fjern workflows fra systemet",
|
"pbac_desc_delete_workflows": "Fjern workflows fra systemet",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Tilgængelighed",
|
||||||
|
"pbac_resource_out_of_office": "Ude af kontoret",
|
||||||
"pbac_desc_create_webhooks": "Opret webhooks",
|
"pbac_desc_create_webhooks": "Opret webhooks",
|
||||||
"pbac_desc_view_webhooks": "Se webhooks",
|
"pbac_desc_view_webhooks": "Se webhooks",
|
||||||
"pbac_desc_update_webhooks": "Opdater webhooks",
|
"pbac_desc_update_webhooks": "Opdater webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Slet webhooks",
|
"pbac_desc_delete_webhooks": "Slet webhooks",
|
||||||
|
"pbac_desc_create_availability": "Opret tilgængelighed",
|
||||||
|
"pbac_desc_view_availability": "Se tilgængelighed",
|
||||||
|
"pbac_desc_update_availability": "Opdater tilgængelighed",
|
||||||
|
"pbac_desc_delete_availability": "Slet tilgængelighed",
|
||||||
|
"pbac_desc_create_out_of_office": "Opret fravær",
|
||||||
|
"pbac_desc_view_out_of_office": "Se fravær",
|
||||||
|
"pbac_desc_update_out_of_office": "Opdater fravær",
|
||||||
|
"pbac_desc_delete_out_of_office": "Slet fravær",
|
||||||
"pbac_desc_manage_workflows": "Fuld administrationsadgang til alle workflows",
|
"pbac_desc_manage_workflows": "Fuld administrationsadgang til alle workflows",
|
||||||
"pbac_desc_create_event_types": "Opret begivenhedstyper",
|
"pbac_desc_create_event_types": "Opret begivenhedstyper",
|
||||||
"pbac_desc_view_event_types": "Vis begivenhedstyper",
|
"pbac_desc_view_event_types": "Vis begivenhedstyper",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Geheimer Termin",
|
"secret_meeting": "Geheimer Termin",
|
||||||
"login_instead": "Stattdessen einloggen",
|
"login_instead": "Stattdessen einloggen",
|
||||||
"create_account": "Konto erstellen",
|
"create_account": "Konto erstellen",
|
||||||
|
"get_started": "Loslegen",
|
||||||
"confirm_password": "Passwort bestätigen",
|
"confirm_password": "Passwort bestätigen",
|
||||||
"reset_your_password": "Legen Sie Ihr neues Passwort mit den Anweisungen fest, die an Ihre E-Mail-Adresse gesendet wurden.",
|
"reset_your_password": "Legen Sie Ihr neues Passwort mit den Anweisungen fest, die an Ihre E-Mail-Adresse gesendet wurden.",
|
||||||
"org_banner_instructions": "Bitte laden Sie Bild mit einer Breite von {{width}} und einer Höhe von {{height}} hoch.",
|
"org_banner_instructions": "Bitte laden Sie Bild mit einer Breite von {{width}} und einer Höhe von {{height}} hoch.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Ereignistyp-Farbe (Helles Design)",
|
"light_event_type_color": "Ereignistyp-Farbe (Helles Design)",
|
||||||
"dark_event_type_color": "Ereignistyp-Farbe (Dunkles Design)",
|
"dark_event_type_color": "Ereignistyp-Farbe (Dunkles Design)",
|
||||||
"file_not_named": "Datei heißt nicht [idOrSlug]/[user]",
|
"file_not_named": "Datei heißt nicht [idOrSlug]/[user]",
|
||||||
"create_team": "Team anlegen",
|
"create_team": "Team erstellen",
|
||||||
"name": "Name",
|
"name": "Name",
|
||||||
"nameless_team": "Namenloses Team",
|
"nameless_team": "Namenloses Team",
|
||||||
"oauth_clients": "OAuth-Clients",
|
"oauth_clients": "OAuth-Clients",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Teilnehmende",
|
"attendees": "Teilnehmende",
|
||||||
"add_attendees": "Teilnehmende hinzufügen",
|
"add_attendees": "Teilnehmende hinzufügen",
|
||||||
"show_advanced_settings": "Erweiterte Einstellungen anzeigen",
|
"show_advanced_settings": "Erweiterte Einstellungen anzeigen",
|
||||||
|
"hide_advanced_settings": "Erweiterte Einstellungen ausblenden",
|
||||||
"event_name": "Termin-Name",
|
"event_name": "Termin-Name",
|
||||||
"event_name_in_calendar": "Ereignisname im Kalender",
|
"event_name_in_calendar": "Ereignisname im Kalender",
|
||||||
"event_name_tooltip": "Der Name, der in Kalendern angezeigt wird",
|
"event_name_tooltip": "Der Name, der in Kalendern angezeigt wird",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Vertrieb kontaktieren",
|
"contact_sales": "Vertrieb kontaktieren",
|
||||||
"error_404": "Fehler 404",
|
"error_404": "Fehler 404",
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"set_to_default": "Auf Standard setzen",
|
"set_to_default": "Als Standard festlegen",
|
||||||
"new_schedule_btn": "Neuer Verfügbarkeitsplan",
|
"new_schedule_btn": "Neuer Verfügbarkeitsplan",
|
||||||
"add_new_schedule": "Neuen Verfügbarkeitsplan hinzufügen",
|
"add_new_schedule": "Neuen Verfügbarkeitsplan hinzufügen",
|
||||||
"add_new_calendar": "Neuen Kalender hinzufügen",
|
"add_new_calendar": "Neuen Kalender hinzufügen",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Wenn diese Einstellung aktiv ist, können deine Teammitglieder keine anderen Teammitglieder sehen.",
|
"make_team_private_description": "Wenn diese Einstellung aktiv ist, können deine Teammitglieder keine anderen Teammitglieder sehen.",
|
||||||
"you_cannot_see_team_members": "Sie können nicht alle Teammitglieder eines privaten Teams sehen.",
|
"you_cannot_see_team_members": "Sie können nicht alle Teammitglieder eines privaten Teams sehen.",
|
||||||
"you_cannot_see_teams_of_org": "Du kannst keine Teams einer privaten Organisation sehen.",
|
"you_cannot_see_teams_of_org": "Du kannst keine Teams einer privaten Organisation sehen.",
|
||||||
"allow_booker_to_select_duration": "Bucher erlauben, die Länge zu wählen",
|
"allow_multiple_durations": "Mehrere Dauern erlauben",
|
||||||
"impersonate_user_tip": "Alle Verwendungen dieser Funktion werden geprüft.",
|
"impersonate_user_tip": "Alle Verwendungen dieser Funktion werden geprüft.",
|
||||||
"impersonating_user_warning": "Imitiere den Account {{user}}.",
|
"impersonating_user_warning": "Imitiere den Account {{user}}.",
|
||||||
"impersonating_stop_instructions": "Klicken Sie hier, um zu stoppen",
|
"impersonating_stop_instructions": "Klicken Sie hier, um zu stoppen",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Buchungsbestätigung fehlgeschlagen",
|
"booking_confirmation_failed": "Buchungsbestätigung fehlgeschlagen",
|
||||||
"not_enough_seats": "Nicht genügend Plätze",
|
"not_enough_seats": "Nicht genügend Plätze",
|
||||||
"form_builder_field_already_exists": "Ein Feld mit diesem Namen ist bereits vorhanden",
|
"form_builder_field_already_exists": "Ein Feld mit diesem Namen ist bereits vorhanden",
|
||||||
|
"guests_field_must_be_multiemail": "Das Gästefeld muss vom Typ 'Mehrere E-Mails' sein",
|
||||||
"show_on_booking_page": "Auf der Buchungsseite anzeigen",
|
"show_on_booking_page": "Auf der Buchungsseite anzeigen",
|
||||||
"visit_cancelled_booking": "Sie können die Seite der stornierten Buchung besuchen",
|
"visit_cancelled_booking": "Sie können die Seite der stornierten Buchung besuchen",
|
||||||
"get_started_zapier_templates": "Legen Sie mit Zapier-Vorlagen los",
|
"get_started_zapier_templates": "Legen Sie mit Zapier-Vorlagen los",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Vom Team-Admin gesperrt",
|
"locked_by_team_admin": "Vom Team-Admin gesperrt",
|
||||||
"app_not_connected": "Sie haben kein {{appName}}-Konto verbunden.",
|
"app_not_connected": "Sie haben kein {{appName}}-Konto verbunden.",
|
||||||
"connect_now": "Jetzt verbinden",
|
"connect_now": "Jetzt verbinden",
|
||||||
|
"connect_now_unavailable_tooltip": "Sofortige Verbindung ist derzeit nicht verfügbar. Bitte buchen Sie aus den obigen Zeitfenstern oder versuchen Sie es später erneut.",
|
||||||
"managed_event_dialog_confirm_button_one": "{{count}} Mitglied ersetzen & benachrichtigen",
|
"managed_event_dialog_confirm_button_one": "{{count}} Mitglied ersetzen & benachrichtigen",
|
||||||
"managed_event_dialog_confirm_button_other": "{{count}} Mitglieder ersetzen & benachrichtigen",
|
"managed_event_dialog_confirm_button_other": "{{count}} Mitglieder ersetzen & benachrichtigen",
|
||||||
"managed_event_dialog_title_one": "Die URL /{{slug}} wird bereits von {{count}} Mitglied genutzt. Möchten Sie sie ersetzen?",
|
"managed_event_dialog_title_one": "Die URL /{{slug}} wird bereits von {{count}} Mitglied genutzt. Möchten Sie sie ersetzen?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Indem Sie fortfahren, stimmen Sie unseren",
|
"proceeding_agreement": "Indem Sie fortfahren, stimmen Sie unseren",
|
||||||
"terms": "Nutzungsbedingungen",
|
"terms": "Nutzungsbedingungen",
|
||||||
"always_show_x_days": "Immer für {{x}} Tage verfügbar",
|
"always_show_x_days": "Immer für {{x}} Tage verfügbar",
|
||||||
|
"always_show_x_days_description": "Verschiebt das {{x}}-Tage-Buchungsfenster automatisch jeden Tag nach vorne und stellt sicher, dass Ihr Kalender immer die nächsten {{x}} Tage mit verfügbaren Zeitfenstern anzeigt.",
|
||||||
"team_subscription_cancelled_successfully": "Team-Abonnement erfolgreich gekündigt",
|
"team_subscription_cancelled_successfully": "Team-Abonnement erfolgreich gekündigt",
|
||||||
"team_subscription_cancellation_error": "Fehler bei der Kündigung des Team-Abonnements",
|
"team_subscription_cancellation_error": "Fehler bei der Kündigung des Team-Abonnements",
|
||||||
"unable_to_subscribe_to_the_platform": "Beim Abonnieren des Platform-Plans ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut",
|
"unable_to_subscribe_to_the_platform": "Beim Abonnieren des Platform-Plans ist ein Fehler aufgetreten. Bitte versuchen Sie es später erneut",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Endet mit",
|
"filter_operator_ends_with": "Endet mit",
|
||||||
"filter_operator_is_empty": "Ist leer",
|
"filter_operator_is_empty": "Ist leer",
|
||||||
"filter_operator_not_empty": "Ist nicht leer",
|
"filter_operator_not_empty": "Ist nicht leer",
|
||||||
"translate_description_button": "Titel/Beschreibung mit KI in die Browsersprache des Besuchers übersetzen",
|
"translate_description_button": "Titel und Beschreibung automatisch übersetzen",
|
||||||
|
"translate_description_button_description": "Titel und Beschreibungen automatisch in die Browsersprache des Besuchers mittels KI übersetzen.",
|
||||||
"rr_distribution_method": "Verteilung",
|
"rr_distribution_method": "Verteilung",
|
||||||
"rr_distribution_method_description": "Ermöglicht die Optimierung der Verteilung für maximale Verfügbarkeit oder eine ausgewogenere Zuweisung.",
|
"rr_distribution_method_description": "Ermöglicht die Optimierung der Verteilung für maximale Verfügbarkeit oder eine ausgewogenere Zuweisung.",
|
||||||
"rr_distribution_method_availability_title": "Verfügbarkeit maximieren",
|
"rr_distribution_method_availability_title": "Verfügbarkeit maximieren",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Die Sitzplatzfunktion unterstützt keine wiederkehrenden Events. Deaktivieren Sie die Sitzplatzfunktion, um es wiederkehrend zu machen.",
|
"seats_doesnt_support_recurring": "Die Sitzplatzfunktion unterstützt keine wiederkehrenden Events. Deaktivieren Sie die Sitzplatzfunktion, um es wiederkehrend zu machen.",
|
||||||
"recurring_event_seats_error": "Wiederkehrende Events unterstützen die Sitzplatzfunktion nicht. Deaktivieren Sie die Sitzplatzfunktion oder machen Sie das Event nicht wiederkehrend.",
|
"recurring_event_seats_error": "Wiederkehrende Events unterstützen die Sitzplatzfunktion nicht. Deaktivieren Sie die Sitzplatzfunktion oder machen Sie das Event nicht wiederkehrend.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Gespeicherte Filter",
|
||||||
"rename": "Umbenennen",
|
"rename": "Umbenennen",
|
||||||
"rename_segment": "Segment umbenennen",
|
"rename_segment": "Segment umbenennen",
|
||||||
"delete_segment": "Segment löschen",
|
"delete_segment": "Segment löschen",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Auswahlliste",
|
"picklist": "Auswahlliste",
|
||||||
"most_cancelled_bookings": "Meiste stornierte Buchungen",
|
"most_cancelled_bookings": "Meiste stornierte Buchungen",
|
||||||
"interface_language": "Oberflächensprache",
|
"interface_language": "Oberflächensprache",
|
||||||
|
"interface_language_description": "Legen Sie Ihre bevorzugte Sprache für die Buchungsoberfläche fest",
|
||||||
"visitors_browser_language": "Browsersprache des Besuchers",
|
"visitors_browser_language": "Browsersprache des Besuchers",
|
||||||
"name_or_email": "Name oder E-Mail",
|
"name_or_email": "Name oder E-Mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Wenn kein Kontakt gefunden wird, auf den Lead-Eigentümer zurückgreifen, falls vorhanden",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Wenn kein Kontakt gefunden wird, auf den Lead-Eigentümer zurückgreifen, falls vorhanden",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Workflow-Einstellungen bearbeiten und ändern",
|
"pbac_desc_update_workflows": "Workflow-Einstellungen bearbeiten und ändern",
|
||||||
"pbac_desc_delete_workflows": "Workflows aus dem System entfernen",
|
"pbac_desc_delete_workflows": "Workflows aus dem System entfernen",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Verfügbarkeit",
|
||||||
|
"pbac_resource_out_of_office": "Abwesenheit",
|
||||||
"pbac_desc_create_webhooks": "Webhooks erstellen",
|
"pbac_desc_create_webhooks": "Webhooks erstellen",
|
||||||
"pbac_desc_view_webhooks": "Webhooks anzeigen",
|
"pbac_desc_view_webhooks": "Webhooks anzeigen",
|
||||||
"pbac_desc_update_webhooks": "Webhooks aktualisieren",
|
"pbac_desc_update_webhooks": "Webhooks aktualisieren",
|
||||||
"pbac_desc_delete_webhooks": "Webhooks löschen",
|
"pbac_desc_delete_webhooks": "Webhooks löschen",
|
||||||
|
"pbac_desc_create_availability": "Verfügbarkeit erstellen",
|
||||||
|
"pbac_desc_view_availability": "Verfügbarkeit anzeigen",
|
||||||
|
"pbac_desc_update_availability": "Verfügbarkeit aktualisieren",
|
||||||
|
"pbac_desc_delete_availability": "Verfügbarkeit löschen",
|
||||||
|
"pbac_desc_create_out_of_office": "Abwesenheit erstellen",
|
||||||
|
"pbac_desc_view_out_of_office": "Abwesenheit anzeigen",
|
||||||
|
"pbac_desc_update_out_of_office": "Abwesenheit aktualisieren",
|
||||||
|
"pbac_desc_delete_out_of_office": "Abwesenheit löschen",
|
||||||
"pbac_desc_manage_workflows": "Vollständiger Verwaltungszugriff auf alle Workflows",
|
"pbac_desc_manage_workflows": "Vollständiger Verwaltungszugriff auf alle Workflows",
|
||||||
"pbac_desc_create_event_types": "Ereignistypen erstellen",
|
"pbac_desc_create_event_types": "Ereignistypen erstellen",
|
||||||
"pbac_desc_view_event_types": "Ereignistypen anzeigen",
|
"pbac_desc_view_event_types": "Ereignistypen anzeigen",
|
||||||
@@ -3560,8 +3578,8 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Lastausgleich ist mit Round-Robin-Gruppen nicht verfügbar",
|
"rr_load_balancing_disabled_with_groups": "Lastausgleich ist mit Round-Robin-Gruppen nicht verfügbar",
|
||||||
"round_robin_settings_description": "Passen Sie die Standard-Round-Robin-Einstellungen für dieses Team an",
|
"round_robin_settings_description": "Passen Sie die Standard-Round-Robin-Einstellungen für dieses Team an",
|
||||||
"round_robin_settings_updated_successfully": "Round-Robin-Einstellungen erfolgreich aktualisiert",
|
"round_robin_settings_updated_successfully": "Round-Robin-Einstellungen erfolgreich aktualisiert",
|
||||||
"booking_creation_time": "Zeitpunkt der Buchungserstellung",
|
"booking_creation_time": "Erstellungszeit der Buchung",
|
||||||
"meeting_start_time": "Beginn des Meetings",
|
"meeting_start_time": "Besprechungsstartzeit",
|
||||||
"members_affected_by_disabling_delegation_credential": "Betroffene Mitglieder",
|
"members_affected_by_disabling_delegation_credential": "Betroffene Mitglieder",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Keine Mitglieder von der Deaktivierung der Delegationsanmeldedaten betroffen",
|
"no_members_affected_by_disabling_delegation_credential": "Keine Mitglieder von der Deaktivierung der Delegationsanmeldedaten betroffen",
|
||||||
"download_expense_log": "Ausgabenprotokoll herunterladen",
|
"download_expense_log": "Ausgabenprotokoll herunterladen",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Μυστική συνάντηση",
|
"secret_meeting": "Μυστική συνάντηση",
|
||||||
"login_instead": "Σύνδεση αντ' αυτού",
|
"login_instead": "Σύνδεση αντ' αυτού",
|
||||||
"create_account": "Δημιουργία Λογαριασμού",
|
"create_account": "Δημιουργία Λογαριασμού",
|
||||||
|
"get_started": "Ξεκινήστε",
|
||||||
"confirm_password": "Επιβεβαίωση κωδικού πρόσβασης",
|
"confirm_password": "Επιβεβαίωση κωδικού πρόσβασης",
|
||||||
"reset_your_password": "Ορίστε τον νέο σας κωδικό πρόσβασης με τις οδηγίες που στάλθηκαν στη διεύθυνση email σας.",
|
"reset_your_password": "Ορίστε τον νέο σας κωδικό πρόσβασης με τις οδηγίες που στάλθηκαν στη διεύθυνση email σας.",
|
||||||
"org_banner_instructions": "Παρακαλούμε ανεβάστε εικόνα με πλάτος {{width}} και ύψος {{height}}.",
|
"org_banner_instructions": "Παρακαλούμε ανεβάστε εικόνα με πλάτος {{width}} και ύψος {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Συμμετέχοντες",
|
"attendees": "Συμμετέχοντες",
|
||||||
"add_attendees": "Προσθήκη συμμετεχόντων",
|
"add_attendees": "Προσθήκη συμμετεχόντων",
|
||||||
"show_advanced_settings": "Εμφάνιση προχωρημένων ρυθμίσεων",
|
"show_advanced_settings": "Εμφάνιση προχωρημένων ρυθμίσεων",
|
||||||
|
"hide_advanced_settings": "Απόκρυψη προχωρημένων ρυθμίσεων",
|
||||||
"event_name": "Όνομα εκδήλωσης",
|
"event_name": "Όνομα εκδήλωσης",
|
||||||
"event_name_in_calendar": "Με όνομα εκδήλωσης",
|
"event_name_in_calendar": "Με όνομα εκδήλωσης",
|
||||||
"event_name_tooltip": "Το όνομα που θα εμφανίζεται στα ημερολόγια",
|
"event_name_tooltip": "Το όνομα που θα εμφανίζεται στα ημερολόγια",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Επικοινωνία με Πωλήσεις",
|
"contact_sales": "Επικοινωνία με Πωλήσεις",
|
||||||
"error_404": "Σφάλμα 404",
|
"error_404": "Σφάλμα 404",
|
||||||
"default": "Προεπιλογή",
|
"default": "Προεπιλογή",
|
||||||
"set_to_default": "Ορισμός ως Προεπιλογή",
|
"set_to_default": "Ορισμός ως προεπιλογή",
|
||||||
"new_schedule_btn": "Νέο πρόγραμμα",
|
"new_schedule_btn": "Νέο πρόγραμμα",
|
||||||
"add_new_schedule": "Προσθήκη νέου προγράμματος",
|
"add_new_schedule": "Προσθήκη νέου προγράμματος",
|
||||||
"add_new_calendar": "Προσθήκη νέου ημερολογίου",
|
"add_new_calendar": "Προσθήκη νέου ημερολογίου",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Τα μέλη της ομάδας σας δεν θα μπορούν να βλέπουν άλλα μέλη της ομάδας όταν αυτό είναι ενεργοποιημένο.",
|
"make_team_private_description": "Τα μέλη της ομάδας σας δεν θα μπορούν να βλέπουν άλλα μέλη της ομάδας όταν αυτό είναι ενεργοποιημένο.",
|
||||||
"you_cannot_see_team_members": "Δεν μπορείτε να δείτε όλα τα μέλη μιας ιδιωτικής ομάδας.",
|
"you_cannot_see_team_members": "Δεν μπορείτε να δείτε όλα τα μέλη μιας ιδιωτικής ομάδας.",
|
||||||
"you_cannot_see_teams_of_org": "Δεν μπορείτε να δείτε τις ομάδες ενός ιδιωτικού οργανισμού.",
|
"you_cannot_see_teams_of_org": "Δεν μπορείτε να δείτε τις ομάδες ενός ιδιωτικού οργανισμού.",
|
||||||
"allow_booker_to_select_duration": "Επιτρέψτε στον χρήστη να επιλέξει διάρκεια",
|
"allow_multiple_durations": "Επιτρέψτε πολλαπλές διάρκειες",
|
||||||
"impersonate_user_tip": "Όλες οι χρήσεις αυτής της λειτουργίας καταγράφονται.",
|
"impersonate_user_tip": "Όλες οι χρήσεις αυτής της λειτουργίας καταγράφονται.",
|
||||||
"impersonating_user_warning": "Μιμείστε τον χρήστη \"{{user}}\".",
|
"impersonating_user_warning": "Μιμείστε τον χρήστη \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Κάντε κλικ εδώ για διακοπή",
|
"impersonating_stop_instructions": "Κάντε κλικ εδώ για διακοπή",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Η επιβεβαίωση της κράτησης απέτυχε",
|
"booking_confirmation_failed": "Η επιβεβαίωση της κράτησης απέτυχε",
|
||||||
"not_enough_seats": "Δεν υπάρχουν αρκετές θέσεις",
|
"not_enough_seats": "Δεν υπάρχουν αρκετές θέσεις",
|
||||||
"form_builder_field_already_exists": "Υπάρχει ήδη ένα πεδίο με αυτό το όνομα",
|
"form_builder_field_already_exists": "Υπάρχει ήδη ένα πεδίο με αυτό το όνομα",
|
||||||
|
"guests_field_must_be_multiemail": "Το πεδίο επισκεπτών πρέπει να είναι τύπου 'Πολλαπλά emails'",
|
||||||
"show_on_booking_page": "Εμφάνιση στη σελίδα κράτησης",
|
"show_on_booking_page": "Εμφάνιση στη σελίδα κράτησης",
|
||||||
"visit_cancelled_booking": "Μπορείτε να επισκεφθείτε τη σελίδα ακυρωμένων κρατήσεων",
|
"visit_cancelled_booking": "Μπορείτε να επισκεφθείτε τη σελίδα ακυρωμένων κρατήσεων",
|
||||||
"get_started_zapier_templates": "Ξεκινήστε με τα πρότυπα του Zapier",
|
"get_started_zapier_templates": "Ξεκινήστε με τα πρότυπα του Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Κλειδωμένο από τον διαχειριστή ομάδας",
|
"locked_by_team_admin": "Κλειδωμένο από τον διαχειριστή ομάδας",
|
||||||
"app_not_connected": "Δεν έχετε συνδέσει λογαριασμό {{appName}}.",
|
"app_not_connected": "Δεν έχετε συνδέσει λογαριασμό {{appName}}.",
|
||||||
"connect_now": "Σύνδεση τώρα",
|
"connect_now": "Σύνδεση τώρα",
|
||||||
|
"connect_now_unavailable_tooltip": "Η άμεση σύνδεση δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλώ κάντε κράτηση από τις παραπάνω διαθέσιμες ώρες ή δοκιμάστε αργότερα.",
|
||||||
"managed_event_dialog_confirm_button_one": "Αντικατάσταση & ειδοποίηση {{count}} μέλους",
|
"managed_event_dialog_confirm_button_one": "Αντικατάσταση & ειδοποίηση {{count}} μέλους",
|
||||||
"managed_event_dialog_confirm_button_other": "Αντικατάσταση & ειδοποίηση {{count}} μελών",
|
"managed_event_dialog_confirm_button_other": "Αντικατάσταση & ειδοποίηση {{count}} μελών",
|
||||||
"managed_event_dialog_title_one": "Το url /{{slug}} υπάρχει ήδη για {{count}} μέλος. Θέλετε να το αντικαταστήσετε;",
|
"managed_event_dialog_title_one": "Το url /{{slug}} υπάρχει ήδη για {{count}} μέλος. Θέλετε να το αντικαταστήσετε;",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Enterprise",
|
"enterprise": "Enterprise",
|
||||||
"enterprise_description": "Αναβαθμίστε σε Enterprise για να δημιουργήσετε τον οργανισμό σας",
|
"enterprise_description": "Αναβαθμίστε σε Enterprise για να δημιουργήσετε τον οργανισμό σας",
|
||||||
"create_your_org": "Δημιουργήστε τον Οργανισμό σας",
|
"create_your_org": "Δημιουργήστε τον Οργανισμό σας",
|
||||||
"create_org": "Δημιουργία Οργανισμού",
|
"create_org": "Δημιουργία οργανισμού",
|
||||||
"create_your_org_description": "Αναβαθμίστε σε Οργανισμούς και λάβετε υποτομέα, ενοποιημένη τιμολόγηση, Αναλύσεις, εκτεταμένο white-labeling και άλλα",
|
"create_your_org_description": "Αναβαθμίστε σε Οργανισμούς και λάβετε υποτομέα, ενοποιημένη τιμολόγηση, Αναλύσεις, εκτεταμένο white-labeling και άλλα",
|
||||||
"create_your_enterprise_description": "Αναβαθμίστε σε Enterprise και αποκτήστε πρόσβαση σε Συγχρονισμό Active Directory, Αυτόματη παροχή χρηστών SCIM, Φωνητικούς πράκτορες Cal.ai, APIs διαχειριστή και άλλα!",
|
"create_your_enterprise_description": "Αναβαθμίστε σε Enterprise και αποκτήστε πρόσβαση σε Συγχρονισμό Active Directory, Αυτόματη παροχή χρηστών SCIM, Φωνητικούς πράκτορες Cal.ai, APIs διαχειριστή και άλλα!",
|
||||||
"other_payment_app_enabled": "Μπορείτε να ενεργοποιήσετε μόνο μία εφαρμογή πληρωμών ανά τύπο εκδήλωσης",
|
"other_payment_app_enabled": "Μπορείτε να ενεργοποιήσετε μόνο μία εφαρμογή πληρωμών ανά τύπο εκδήλωσης",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Προχωρώντας, συμφωνείτε με τους",
|
"proceeding_agreement": "Προχωρώντας, συμφωνείτε με τους",
|
||||||
"terms": "Όρους",
|
"terms": "Όρους",
|
||||||
"always_show_x_days": "Πάντα {{x}} διαθέσιμες ημέρες",
|
"always_show_x_days": "Πάντα {{x}} διαθέσιμες ημέρες",
|
||||||
|
"always_show_x_days_description": "Μετακινεί αυτόματα το παράθυρο κρατήσεων {{x}} ημερών κάθε μέρα, διασφαλίζοντας ότι το ημερολόγιό σας δείχνει πάντα τις επόμενες {{x}} ημέρες διαθέσιμων χρονοθυρίδων.",
|
||||||
"team_subscription_cancelled_successfully": "Η συνδρομή ομάδας ακυρώθηκε με επιτυχία",
|
"team_subscription_cancelled_successfully": "Η συνδρομή ομάδας ακυρώθηκε με επιτυχία",
|
||||||
"team_subscription_cancellation_error": "Σφάλμα κατά την ακύρωση της συνδρομής ομάδας",
|
"team_subscription_cancellation_error": "Σφάλμα κατά την ακύρωση της συνδρομής ομάδας",
|
||||||
"unable_to_subscribe_to_the_platform": "Παρουσιάστηκε σφάλμα κατά την εγγραφή στο πρόγραμμα της πλατφόρμας, παρακαλώ δοκιμάστε ξανά αργότερα",
|
"unable_to_subscribe_to_the_platform": "Παρουσιάστηκε σφάλμα κατά την εγγραφή στο πρόγραμμα της πλατφόρμας, παρακαλώ δοκιμάστε ξανά αργότερα",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Τελειώνει με",
|
"filter_operator_ends_with": "Τελειώνει με",
|
||||||
"filter_operator_is_empty": "Είναι κενό",
|
"filter_operator_is_empty": "Είναι κενό",
|
||||||
"filter_operator_not_empty": "Δεν είναι κενό",
|
"filter_operator_not_empty": "Δεν είναι κενό",
|
||||||
"translate_description_button": "Μετάφραση τίτλου/περιγραφής στη γλώσσα του προγράμματος περιήγησης του επισκέπτη με χρήση AI",
|
"translate_description_button": "Αυτόματη μετάφραση τίτλου και περιγραφής",
|
||||||
|
"translate_description_button_description": "Αυτόματη μετάφραση τίτλων και περιγραφών στη γλώσσα του προγράμματος περιήγησης του επισκέπτη χρησιμοποιώντας AI.",
|
||||||
"rr_distribution_method": "Κατανομή",
|
"rr_distribution_method": "Κατανομή",
|
||||||
"rr_distribution_method_description": "Επιτρέπει τη βελτιστοποίηση της κατανομής για μέγιστη διαθεσιμότητα ή για πιο ισορροπημένη ανάθεση.",
|
"rr_distribution_method_description": "Επιτρέπει τη βελτιστοποίηση της κατανομής για μέγιστη διαθεσιμότητα ή για πιο ισορροπημένη ανάθεση.",
|
||||||
"rr_distribution_method_availability_title": "Μεγιστοποίηση διαθεσιμότητας",
|
"rr_distribution_method_availability_title": "Μεγιστοποίηση διαθεσιμότητας",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Η λειτουργία θέσεων δεν υποστηρίζει επαναλαμβανόμενη εκδήλωση. Απενεργοποιήστε τη λειτουργία θέσεων για να την κάνετε επαναλαμβανόμενη.",
|
"seats_doesnt_support_recurring": "Η λειτουργία θέσεων δεν υποστηρίζει επαναλαμβανόμενη εκδήλωση. Απενεργοποιήστε τη λειτουργία θέσεων για να την κάνετε επαναλαμβανόμενη.",
|
||||||
"recurring_event_seats_error": "Η επαναλαμβανόμενη εκδήλωση δεν υποστηρίζει τη λειτουργία θέσεων. Απενεργοποιήστε τη λειτουργία θέσεων ή κάντε την εκδήλωση μη επαναλαμβανόμενη.",
|
"recurring_event_seats_error": "Η επαναλαμβανόμενη εκδήλωση δεν υποστηρίζει τη λειτουργία θέσεων. Απενεργοποιήστε τη λειτουργία θέσεων ή κάντε την εκδήλωση μη επαναλαμβανόμενη.",
|
||||||
"segment": "Τμήμα",
|
"segment": "Τμήμα",
|
||||||
|
"saved_filters": "Αποθηκευμένα φίλτρα",
|
||||||
"rename": "Μετονομασία",
|
"rename": "Μετονομασία",
|
||||||
"rename_segment": "Μετονομασία τμήματος",
|
"rename_segment": "Μετονομασία τμήματος",
|
||||||
"delete_segment": "Διαγραφή τμήματος",
|
"delete_segment": "Διαγραφή τμήματος",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Λίστα επιλογών",
|
"picklist": "Λίστα επιλογών",
|
||||||
"most_cancelled_bookings": "Περισσότερες ακυρωμένες κρατήσεις",
|
"most_cancelled_bookings": "Περισσότερες ακυρωμένες κρατήσεις",
|
||||||
"interface_language": "Γλώσσα διεπαφής",
|
"interface_language": "Γλώσσα διεπαφής",
|
||||||
|
"interface_language_description": "Ορίστε την προτιμώμενη γλώσσα για το περιβάλλον κρατήσεων",
|
||||||
"visitors_browser_language": "Γλώσσα προγράμματος περιήγησης επισκέπτη",
|
"visitors_browser_language": "Γλώσσα προγράμματος περιήγησης επισκέπτη",
|
||||||
"name_or_email": "Όνομα ή Email",
|
"name_or_email": "Όνομα ή Email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Αν δεν βρεθεί επαφή, επιστροφή στον ιδιοκτήτη του lead εάν υπάρχει",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Αν δεν βρεθεί επαφή, επιστροφή στον ιδιοκτήτη του lead εάν υπάρχει",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Επεξεργασία και τροποποίηση ρυθμίσεων ροών εργασίας",
|
"pbac_desc_update_workflows": "Επεξεργασία και τροποποίηση ρυθμίσεων ροών εργασίας",
|
||||||
"pbac_desc_delete_workflows": "Αφαίρεση ροών εργασίας από το σύστημα",
|
"pbac_desc_delete_workflows": "Αφαίρεση ροών εργασίας από το σύστημα",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Διαθεσιμότητα",
|
||||||
|
"pbac_resource_out_of_office": "Εκτός Γραφείου",
|
||||||
"pbac_desc_create_webhooks": "Δημιουργία webhooks",
|
"pbac_desc_create_webhooks": "Δημιουργία webhooks",
|
||||||
"pbac_desc_view_webhooks": "Προβολή webhooks",
|
"pbac_desc_view_webhooks": "Προβολή webhooks",
|
||||||
"pbac_desc_update_webhooks": "Ενημέρωση webhooks",
|
"pbac_desc_update_webhooks": "Ενημέρωση webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Διαγραφή webhooks",
|
"pbac_desc_delete_webhooks": "Διαγραφή webhooks",
|
||||||
|
"pbac_desc_create_availability": "Δημιουργία διαθεσιμότητας",
|
||||||
|
"pbac_desc_view_availability": "Προβολή διαθεσιμότητας",
|
||||||
|
"pbac_desc_update_availability": "Ενημέρωση διαθεσιμότητας",
|
||||||
|
"pbac_desc_delete_availability": "Διαγραφή διαθεσιμότητας",
|
||||||
|
"pbac_desc_create_out_of_office": "Δημιουργία απουσίας",
|
||||||
|
"pbac_desc_view_out_of_office": "Προβολή απουσίας",
|
||||||
|
"pbac_desc_update_out_of_office": "Ενημέρωση απουσίας",
|
||||||
|
"pbac_desc_delete_out_of_office": "Διαγραφή απουσίας",
|
||||||
"pbac_desc_manage_workflows": "Πλήρης πρόσβαση διαχείρισης σε όλες τις ροές εργασίας",
|
"pbac_desc_manage_workflows": "Πλήρης πρόσβαση διαχείρισης σε όλες τις ροές εργασίας",
|
||||||
"pbac_desc_create_event_types": "Δημιουργία τύπων εκδηλώσεων",
|
"pbac_desc_create_event_types": "Δημιουργία τύπων εκδηλώσεων",
|
||||||
"pbac_desc_view_event_types": "Προβολή τύπων εκδηλώσεων",
|
"pbac_desc_view_event_types": "Προβολή τύπων εκδηλώσεων",
|
||||||
|
|||||||
@@ -2152,6 +2152,9 @@
|
|||||||
"delete_sso_configuration_confirmation_description": "Are you sure you want to delete the {{connectionType}} configuration? Your team members who use {{connectionType}} login will no longer be able to access Cal.com.",
|
"delete_sso_configuration_confirmation_description": "Are you sure you want to delete the {{connectionType}} configuration? Your team members who use {{connectionType}} login will no longer be able to access Cal.com.",
|
||||||
"organizer_timezone": "Organizer timezone",
|
"organizer_timezone": "Organizer timezone",
|
||||||
"email_user_cta": "View Invitation",
|
"email_user_cta": "View Invitation",
|
||||||
|
"accept_invite": "Accept Invite",
|
||||||
|
"join_now": "Join Now",
|
||||||
|
"successfully_joined": "Successfully joined",
|
||||||
"email_no_user_invite_heading_subteam": "You've been invited to join a team of {{parentTeamName}} organization",
|
"email_no_user_invite_heading_subteam": "You've been invited to join a team of {{parentTeamName}} organization",
|
||||||
"email_no_user_invite_heading_org": "You've been invited to join a {{appName}} organization",
|
"email_no_user_invite_heading_org": "You've been invited to join a {{appName}} organization",
|
||||||
"email_no_user_invite_subheading": "{{invitedBy}} has invited you to join their team on {{appName}}. {{appName}} is the event-juggling scheduler that enables you and your team to schedule meetings without the email tennis.",
|
"email_no_user_invite_subheading": "{{invitedBy}} has invited you to join their team on {{appName}}. {{appName}} is the event-juggling scheduler that enables you and your team to schedule meetings without the email tennis.",
|
||||||
@@ -2301,6 +2304,7 @@
|
|||||||
"locked_by_team_admin": "Locked by team admin",
|
"locked_by_team_admin": "Locked by team admin",
|
||||||
"app_not_connected": "You have not connected a {{appName}} account.",
|
"app_not_connected": "You have not connected a {{appName}} account.",
|
||||||
"connect_now": "Connect now",
|
"connect_now": "Connect now",
|
||||||
|
"connect_now_unavailable_tooltip": "Connect now is currently unavailable. Please book from above slots or try again later.",
|
||||||
"managed_event_dialog_confirm_button_one": "Replace & notify {{count}} member",
|
"managed_event_dialog_confirm_button_one": "Replace & notify {{count}} member",
|
||||||
"managed_event_dialog_confirm_button_other": "Replace & notify {{count}} members",
|
"managed_event_dialog_confirm_button_other": "Replace & notify {{count}} members",
|
||||||
"managed_event_dialog_title_one": "The url /{{slug}} already exists for {{count}} member. Do you want to replace it?",
|
"managed_event_dialog_title_one": "The url /{{slug}} already exists for {{count}} member. Do you want to replace it?",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Reunión Secreta",
|
"secret_meeting": "Reunión Secreta",
|
||||||
"login_instead": "Inicia sesión en su lugar",
|
"login_instead": "Inicia sesión en su lugar",
|
||||||
"create_account": "Crear cuenta",
|
"create_account": "Crear cuenta",
|
||||||
|
"get_started": "Comenzar",
|
||||||
"confirm_password": "Confirmar contraseña",
|
"confirm_password": "Confirmar contraseña",
|
||||||
"reset_your_password": "Establece tu nueva contraseña con las instrucciones enviadas a tu dirección de correo electrónico.",
|
"reset_your_password": "Establece tu nueva contraseña con las instrucciones enviadas a tu dirección de correo electrónico.",
|
||||||
"org_banner_instructions": "Por favor, sube una imagen de {{width}} de ancho y {{height}} de alto.",
|
"org_banner_instructions": "Por favor, sube una imagen de {{width}} de ancho y {{height}} de alto.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Color del Tipo de Evento (Tema Claro)",
|
"light_event_type_color": "Color del Tipo de Evento (Tema Claro)",
|
||||||
"dark_event_type_color": "Color del Tipo de Evento (Tema Oscuro)",
|
"dark_event_type_color": "Color del Tipo de Evento (Tema Oscuro)",
|
||||||
"file_not_named": "El archivo no está nombrado [idOrSlug]/[user]",
|
"file_not_named": "El archivo no está nombrado [idOrSlug]/[user]",
|
||||||
"create_team": "Crear Equipo",
|
"create_team": "Crear equipo",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
"nameless_team": "Equipo sin nombre",
|
"nameless_team": "Equipo sin nombre",
|
||||||
"oauth_clients": "Clientes OAuth",
|
"oauth_clients": "Clientes OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Asistentes",
|
"attendees": "Asistentes",
|
||||||
"add_attendees": "Agregar asistentes",
|
"add_attendees": "Agregar asistentes",
|
||||||
"show_advanced_settings": "Mostrar configuraciones avanzadas",
|
"show_advanced_settings": "Mostrar configuraciones avanzadas",
|
||||||
|
"hide_advanced_settings": "Ocultar configuración avanzada",
|
||||||
"event_name": "Nombre del evento",
|
"event_name": "Nombre del evento",
|
||||||
"event_name_in_calendar": "Con nombre del evento",
|
"event_name_in_calendar": "Con nombre del evento",
|
||||||
"event_name_tooltip": "El nombre que aparecerá en los calendarios",
|
"event_name_tooltip": "El nombre que aparecerá en los calendarios",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Los miembros de tu equipo no podrán ver a otros miembros del equipo cuando esto esté activado.",
|
"make_team_private_description": "Los miembros de tu equipo no podrán ver a otros miembros del equipo cuando esto esté activado.",
|
||||||
"you_cannot_see_team_members": "No puedes ver a todos los miembros de un equipo privado.",
|
"you_cannot_see_team_members": "No puedes ver a todos los miembros de un equipo privado.",
|
||||||
"you_cannot_see_teams_of_org": "No puedes ver los equipos de una organización privada.",
|
"you_cannot_see_teams_of_org": "No puedes ver los equipos de una organización privada.",
|
||||||
"allow_booker_to_select_duration": "Permitir al reservador seleccionar la duración",
|
"allow_multiple_durations": "Permitir múltiples duraciones",
|
||||||
"impersonate_user_tip": "Todos los usos de esta función son auditados.",
|
"impersonate_user_tip": "Todos los usos de esta función son auditados.",
|
||||||
"impersonating_user_warning": "Suplantando al usuario \"{{user}}\".",
|
"impersonating_user_warning": "Suplantando al usuario \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Haz clic aquí para detener",
|
"impersonating_stop_instructions": "Haz clic aquí para detener",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "La confirmación de la reserva falló",
|
"booking_confirmation_failed": "La confirmación de la reserva falló",
|
||||||
"not_enough_seats": "No hay suficientes asientos",
|
"not_enough_seats": "No hay suficientes asientos",
|
||||||
"form_builder_field_already_exists": "Ya existe un campo con este nombre",
|
"form_builder_field_already_exists": "Ya existe un campo con este nombre",
|
||||||
|
"guests_field_must_be_multiemail": "El campo de invitados debe ser de tipo 'Múltiples correos electrónicos'",
|
||||||
"show_on_booking_page": "Mostrar en la página de reserva",
|
"show_on_booking_page": "Mostrar en la página de reserva",
|
||||||
"visit_cancelled_booking": "Puedes visitar la página de la reserva cancelada",
|
"visit_cancelled_booking": "Puedes visitar la página de la reserva cancelada",
|
||||||
"get_started_zapier_templates": "Comienza con las plantillas de Zapier",
|
"get_started_zapier_templates": "Comienza con las plantillas de Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
|
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
|
||||||
"app_not_connected": "No has conectado una cuenta de {{appName}}.",
|
"app_not_connected": "No has conectado una cuenta de {{appName}}.",
|
||||||
"connect_now": "Conectar ahora",
|
"connect_now": "Conectar ahora",
|
||||||
|
"connect_now_unavailable_tooltip": "Conectar ahora no está disponible actualmente. Por favor, reserva desde los espacios anteriores o intenta más tarde.",
|
||||||
"managed_event_dialog_confirm_button_one": "Reemplazar y notificar a {{count}} miembro",
|
"managed_event_dialog_confirm_button_one": "Reemplazar y notificar a {{count}} miembro",
|
||||||
"managed_event_dialog_confirm_button_other": "Reemplazar y notificar a {{count}} miembros",
|
"managed_event_dialog_confirm_button_other": "Reemplazar y notificar a {{count}} miembros",
|
||||||
"managed_event_dialog_title_one": "La URL /{{slug}} ya existe para {{count}} miembro. ¿Quieres reemplazarla?",
|
"managed_event_dialog_title_one": "La URL /{{slug}} ya existe para {{count}} miembro. ¿Quieres reemplazarla?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Empresa",
|
"enterprise": "Empresa",
|
||||||
"enterprise_description": "Actualiza a Enterprise para crear tu Organización",
|
"enterprise_description": "Actualiza a Enterprise para crear tu Organización",
|
||||||
"create_your_org": "Crea tu Organización",
|
"create_your_org": "Crea tu Organización",
|
||||||
"create_org": "Crear Organización",
|
"create_org": "Crear organización",
|
||||||
"create_your_org_description": "Actualiza a Organizaciones y recibe un subdominio, facturación unificada, Insights, etiquetado blanco extensivo y más",
|
"create_your_org_description": "Actualiza a Organizaciones y recibe un subdominio, facturación unificada, Insights, etiquetado blanco extensivo y más",
|
||||||
"create_your_enterprise_description": "Actualiza a Enterprise y obtén acceso a la sincronización de Active Directory, aprovisionamiento automático de usuarios SCIM, agentes de voz Cal.ai, APIs de administración y más",
|
"create_your_enterprise_description": "Actualiza a Enterprise y obtén acceso a la sincronización de Active Directory, aprovisionamiento automático de usuarios SCIM, agentes de voz Cal.ai, APIs de administración y más",
|
||||||
"other_payment_app_enabled": "Solo puedes habilitar una aplicación de pago por tipo de evento",
|
"other_payment_app_enabled": "Solo puedes habilitar una aplicación de pago por tipo de evento",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Al continuar, aceptas nuestros",
|
"proceeding_agreement": "Al continuar, aceptas nuestros",
|
||||||
"terms": "Términos",
|
"terms": "Términos",
|
||||||
"always_show_x_days": "Siempre {{x}} días disponibles",
|
"always_show_x_days": "Siempre {{x}} días disponibles",
|
||||||
|
"always_show_x_days_description": "Mueve automáticamente la ventana de reservas de {{x}} días hacia adelante cada día, asegurando que tu calendario siempre muestre los próximos {{x}} días de espacios disponibles.",
|
||||||
"team_subscription_cancelled_successfully": "Suscripción del equipo cancelada con éxito",
|
"team_subscription_cancelled_successfully": "Suscripción del equipo cancelada con éxito",
|
||||||
"team_subscription_cancellation_error": "Error al cancelar la suscripción del equipo",
|
"team_subscription_cancellation_error": "Error al cancelar la suscripción del equipo",
|
||||||
"unable_to_subscribe_to_the_platform": "Ocurrió un error al intentar suscribirse al plan de la plataforma, por favor intenta de nuevo más tarde",
|
"unable_to_subscribe_to_the_platform": "Ocurrió un error al intentar suscribirse al plan de la plataforma, por favor intenta de nuevo más tarde",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Termina con",
|
"filter_operator_ends_with": "Termina con",
|
||||||
"filter_operator_is_empty": "Está vacío",
|
"filter_operator_is_empty": "Está vacío",
|
||||||
"filter_operator_not_empty": "No está vacío",
|
"filter_operator_not_empty": "No está vacío",
|
||||||
"translate_description_button": "Traducir título/descripción al idioma del navegador del visitante usando IA",
|
"translate_description_button": "Traducir automáticamente título y descripción",
|
||||||
|
"translate_description_button_description": "Traduce automáticamente títulos y descripciones al idioma del navegador del visitante usando IA.",
|
||||||
"rr_distribution_method": "Distribución",
|
"rr_distribution_method": "Distribución",
|
||||||
"rr_distribution_method_description": "Permite optimizar la distribución para maximizar la disponibilidad o lograr una asignación más equilibrada.",
|
"rr_distribution_method_description": "Permite optimizar la distribución para maximizar la disponibilidad o lograr una asignación más equilibrada.",
|
||||||
"rr_distribution_method_availability_title": "Maximizar disponibilidad",
|
"rr_distribution_method_availability_title": "Maximizar disponibilidad",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "La función de asientos no admite eventos recurrentes. Desactiva la función de asientos para hacerlo recurrente.",
|
"seats_doesnt_support_recurring": "La función de asientos no admite eventos recurrentes. Desactiva la función de asientos para hacerlo recurrente.",
|
||||||
"recurring_event_seats_error": "Los eventos recurrentes no admiten la función de asientos. Desactiva la función de asientos o haz que el evento no sea recurrente.",
|
"recurring_event_seats_error": "Los eventos recurrentes no admiten la función de asientos. Desactiva la función de asientos o haz que el evento no sea recurrente.",
|
||||||
"segment": "Segmento",
|
"segment": "Segmento",
|
||||||
|
"saved_filters": "Filtros guardados",
|
||||||
"rename": "Renombrar",
|
"rename": "Renombrar",
|
||||||
"rename_segment": "Renombrar segmento",
|
"rename_segment": "Renombrar segmento",
|
||||||
"delete_segment": "Eliminar segmento",
|
"delete_segment": "Eliminar segmento",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista de selección",
|
"picklist": "Lista de selección",
|
||||||
"most_cancelled_bookings": "Más cancelaciones",
|
"most_cancelled_bookings": "Más cancelaciones",
|
||||||
"interface_language": "Idioma de la interfaz",
|
"interface_language": "Idioma de la interfaz",
|
||||||
|
"interface_language_description": "Establece tu idioma preferido para la interfaz de reservas",
|
||||||
"visitors_browser_language": "Idioma del navegador del visitante",
|
"visitors_browser_language": "Idioma del navegador del visitante",
|
||||||
"name_or_email": "Nombre o correo electrónico",
|
"name_or_email": "Nombre o correo electrónico",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no se encuentra ningún contacto, recurrir al propietario del lead si existe",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no se encuentra ningún contacto, recurrir al propietario del lead si existe",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editar y modificar configuraciones de flujos de trabajo",
|
"pbac_desc_update_workflows": "Editar y modificar configuraciones de flujos de trabajo",
|
||||||
"pbac_desc_delete_workflows": "Eliminar flujos de trabajo del sistema",
|
"pbac_desc_delete_workflows": "Eliminar flujos de trabajo del sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilidad",
|
||||||
|
"pbac_resource_out_of_office": "Fuera de la oficina",
|
||||||
"pbac_desc_create_webhooks": "Crear webhooks",
|
"pbac_desc_create_webhooks": "Crear webhooks",
|
||||||
"pbac_desc_view_webhooks": "Ver webhooks",
|
"pbac_desc_view_webhooks": "Ver webhooks",
|
||||||
"pbac_desc_update_webhooks": "Actualizar webhooks",
|
"pbac_desc_update_webhooks": "Actualizar webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Eliminar webhooks",
|
"pbac_desc_delete_webhooks": "Eliminar webhooks",
|
||||||
|
"pbac_desc_create_availability": "Crear disponibilidad",
|
||||||
|
"pbac_desc_view_availability": "Ver disponibilidad",
|
||||||
|
"pbac_desc_update_availability": "Actualizar disponibilidad",
|
||||||
|
"pbac_desc_delete_availability": "Eliminar disponibilidad",
|
||||||
|
"pbac_desc_create_out_of_office": "Crear ausencia",
|
||||||
|
"pbac_desc_view_out_of_office": "Ver ausencia",
|
||||||
|
"pbac_desc_update_out_of_office": "Actualizar ausencia",
|
||||||
|
"pbac_desc_delete_out_of_office": "Eliminar ausencia",
|
||||||
"pbac_desc_manage_workflows": "Acceso completo de gestión a todos los flujos de trabajo",
|
"pbac_desc_manage_workflows": "Acceso completo de gestión a todos los flujos de trabajo",
|
||||||
"pbac_desc_create_event_types": "Crear tipos de eventos",
|
"pbac_desc_create_event_types": "Crear tipos de eventos",
|
||||||
"pbac_desc_view_event_types": "Ver tipos de eventos",
|
"pbac_desc_view_event_types": "Ver tipos de eventos",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Reunión Secreta",
|
"secret_meeting": "Reunión Secreta",
|
||||||
"login_instead": "Iniciar Sesión en su lugar",
|
"login_instead": "Iniciar Sesión en su lugar",
|
||||||
"create_account": "Crear Cuenta",
|
"create_account": "Crear Cuenta",
|
||||||
|
"get_started": "Comenzar",
|
||||||
"confirm_password": "Confirmar Contraseña",
|
"confirm_password": "Confirmar Contraseña",
|
||||||
"reset_your_password": "Establezca su nueva contraseña con las instrucciones enviadas a su dirección de correo electrónico.",
|
"reset_your_password": "Establezca su nueva contraseña con las instrucciones enviadas a su dirección de correo electrónico.",
|
||||||
"org_banner_instructions": "Por favor, sube una imagen de {{width}} de ancho y {{height}} de alto.",
|
"org_banner_instructions": "Por favor, sube una imagen de {{width}} de ancho y {{height}} de alto.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Color del tipo de evento (Tema claro)",
|
"light_event_type_color": "Color del tipo de evento (Tema claro)",
|
||||||
"dark_event_type_color": "Color del tipo de evento (Tema oscuro)",
|
"dark_event_type_color": "Color del tipo de evento (Tema oscuro)",
|
||||||
"file_not_named": "El archivo no se llama [idOrSlug] / [user]",
|
"file_not_named": "El archivo no se llama [idOrSlug] / [user]",
|
||||||
"create_team": "Crear Equipo",
|
"create_team": "Crear equipo",
|
||||||
"name": "Nombre",
|
"name": "Nombre",
|
||||||
"nameless_team": "Equipo sin nombre",
|
"nameless_team": "Equipo sin nombre",
|
||||||
"oauth_clients": "Clientes OAuth",
|
"oauth_clients": "Clientes OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Asistentes",
|
"attendees": "Asistentes",
|
||||||
"add_attendees": "Añadir Asistentes",
|
"add_attendees": "Añadir Asistentes",
|
||||||
"show_advanced_settings": "Mostrar Opciones Avanzadas",
|
"show_advanced_settings": "Mostrar Opciones Avanzadas",
|
||||||
|
"hide_advanced_settings": "Ocultar ajustes avanzados",
|
||||||
"event_name": "Nombre del Evento",
|
"event_name": "Nombre del Evento",
|
||||||
"event_name_in_calendar": "Nombre del evento en el calendario",
|
"event_name_in_calendar": "Nombre del evento en el calendario",
|
||||||
"event_name_tooltip": "Nombre que aparecerá en los calendarios",
|
"event_name_tooltip": "Nombre que aparecerá en los calendarios",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Los miembros de su equipo no podrán ver a otros miembros del equipo cuando esto esté activado.",
|
"make_team_private_description": "Los miembros de su equipo no podrán ver a otros miembros del equipo cuando esto esté activado.",
|
||||||
"you_cannot_see_team_members": "No puede ver a todos los miembros de un equipo privado.",
|
"you_cannot_see_team_members": "No puede ver a todos los miembros de un equipo privado.",
|
||||||
"you_cannot_see_teams_of_org": "No puedes ver los equipos de una organización privada.",
|
"you_cannot_see_teams_of_org": "No puedes ver los equipos de una organización privada.",
|
||||||
"allow_booker_to_select_duration": "Permitir que el encargado de las reservas seleccione la duración",
|
"allow_multiple_durations": "Permitir múltiples duraciones",
|
||||||
"impersonate_user_tip": "Todos los usos de esta función son revisados.",
|
"impersonate_user_tip": "Todos los usos de esta función son revisados.",
|
||||||
"impersonating_user_warning": "Suplantando nombre de usuario \"{{user}}\".",
|
"impersonating_user_warning": "Suplantando nombre de usuario \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Haga clic aquí para detener.",
|
"impersonating_stop_instructions": "Haga clic aquí para detener.",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Error al confirmar la reserva",
|
"booking_confirmation_failed": "Error al confirmar la reserva",
|
||||||
"not_enough_seats": "No hay suficientes plazas",
|
"not_enough_seats": "No hay suficientes plazas",
|
||||||
"form_builder_field_already_exists": "Ya existe un campo con este nombre",
|
"form_builder_field_already_exists": "Ya existe un campo con este nombre",
|
||||||
|
"guests_field_must_be_multiemail": "El campo de invitados debe ser de tipo 'Múltiples correos electrónicos'",
|
||||||
"show_on_booking_page": "Mostrar en la página de reserva",
|
"show_on_booking_page": "Mostrar en la página de reserva",
|
||||||
"visit_cancelled_booking": "Puedes visitar la página de la reserva cancelada",
|
"visit_cancelled_booking": "Puedes visitar la página de la reserva cancelada",
|
||||||
"get_started_zapier_templates": "Comience con las plantillas de Zapier",
|
"get_started_zapier_templates": "Comience con las plantillas de Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
|
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
|
||||||
"app_not_connected": "No ha conectado una cuenta de {{appName}}.",
|
"app_not_connected": "No ha conectado una cuenta de {{appName}}.",
|
||||||
"connect_now": "Conectar ahora",
|
"connect_now": "Conectar ahora",
|
||||||
|
"connect_now_unavailable_tooltip": "Conectar ahora no está disponible actualmente. Por favor, reserve desde los espacios anteriores o inténtelo más tarde.",
|
||||||
"managed_event_dialog_confirm_button_one": "Reemplazar y notificar a {{count}} miembro",
|
"managed_event_dialog_confirm_button_one": "Reemplazar y notificar a {{count}} miembro",
|
||||||
"managed_event_dialog_confirm_button_other": "Reemplazar y notificar a {{count}} miembros",
|
"managed_event_dialog_confirm_button_other": "Reemplazar y notificar a {{count}} miembros",
|
||||||
"managed_event_dialog_title_one": "La url /{{slug}} ya existe para {{count}} miembro. ¿Desea reemplazarla?",
|
"managed_event_dialog_title_one": "La url /{{slug}} ya existe para {{count}} miembro. ¿Desea reemplazarla?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Empresa",
|
"enterprise": "Empresa",
|
||||||
"enterprise_description": "Actualiza a Enterprise para crear tu Organización",
|
"enterprise_description": "Actualiza a Enterprise para crear tu Organización",
|
||||||
"create_your_org": "Crea tu Organización",
|
"create_your_org": "Crea tu Organización",
|
||||||
"create_org": "Crear Organización",
|
"create_org": "Crear organización",
|
||||||
"create_your_org_description": "Actualiza a Organizaciones y recibe un subdominio, facturación unificada, Insights, personalización extensa y más",
|
"create_your_org_description": "Actualiza a Organizaciones y recibe un subdominio, facturación unificada, Insights, personalización extensa y más",
|
||||||
"create_your_enterprise_description": "Actualiza a Enterprise y obtén acceso a la sincronización de Active Directory, aprovisionamiento automático de usuarios SCIM, agentes de voz de Cal.ai, APIs de administración y más",
|
"create_your_enterprise_description": "Actualiza a Enterprise y obtén acceso a la sincronización de Active Directory, aprovisionamiento automático de usuarios SCIM, agentes de voz de Cal.ai, APIs de administración y más",
|
||||||
"other_payment_app_enabled": "Solo puedes habilitar una aplicación de pago por tipo de evento",
|
"other_payment_app_enabled": "Solo puedes habilitar una aplicación de pago por tipo de evento",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Al continuar, aceptas nuestros",
|
"proceeding_agreement": "Al continuar, aceptas nuestros",
|
||||||
"terms": "Términos",
|
"terms": "Términos",
|
||||||
"always_show_x_days": "Siempre {{x}} días disponibles",
|
"always_show_x_days": "Siempre {{x}} días disponibles",
|
||||||
|
"always_show_x_days_description": "Mueve automáticamente la ventana de reservas de {{x}} días hacia adelante cada día, asegurando que tu calendario siempre muestre los próximos {{x}} días de espacios disponibles.",
|
||||||
"team_subscription_cancelled_successfully": "Suscripción del equipo cancelada con éxito",
|
"team_subscription_cancelled_successfully": "Suscripción del equipo cancelada con éxito",
|
||||||
"team_subscription_cancellation_error": "Error al cancelar la suscripción del equipo",
|
"team_subscription_cancellation_error": "Error al cancelar la suscripción del equipo",
|
||||||
"unable_to_subscribe_to_the_platform": "Ocurrió un error al intentar suscribirse al plan de la plataforma, por favor intente de nuevo más tarde",
|
"unable_to_subscribe_to_the_platform": "Ocurrió un error al intentar suscribirse al plan de la plataforma, por favor intente de nuevo más tarde",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Termina con",
|
"filter_operator_ends_with": "Termina con",
|
||||||
"filter_operator_is_empty": "Está vacío",
|
"filter_operator_is_empty": "Está vacío",
|
||||||
"filter_operator_not_empty": "No está vacío",
|
"filter_operator_not_empty": "No está vacío",
|
||||||
"translate_description_button": "Traducir título/descripción al idioma del navegador del visitante usando IA",
|
"translate_description_button": "Auto traducir título y descripción",
|
||||||
|
"translate_description_button_description": "Traduce automáticamente títulos y descripciones al idioma del navegador del visitante usando IA.",
|
||||||
"rr_distribution_method": "Distribución",
|
"rr_distribution_method": "Distribución",
|
||||||
"rr_distribution_method_description": "Permite optimizar la distribución para maximizar la disponibilidad o lograr una asignación más equilibrada.",
|
"rr_distribution_method_description": "Permite optimizar la distribución para maximizar la disponibilidad o lograr una asignación más equilibrada.",
|
||||||
"rr_distribution_method_availability_title": "Maximizar disponibilidad",
|
"rr_distribution_method_availability_title": "Maximizar disponibilidad",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "La función de asientos no admite eventos recurrentes. Desactiva la función de asientos para hacerlo recurrente.",
|
"seats_doesnt_support_recurring": "La función de asientos no admite eventos recurrentes. Desactiva la función de asientos para hacerlo recurrente.",
|
||||||
"recurring_event_seats_error": "Los eventos recurrentes no admiten la función de asientos. Desactiva la función de asientos o haz que el evento no sea recurrente.",
|
"recurring_event_seats_error": "Los eventos recurrentes no admiten la función de asientos. Desactiva la función de asientos o haz que el evento no sea recurrente.",
|
||||||
"segment": "Segmento",
|
"segment": "Segmento",
|
||||||
|
"saved_filters": "Filtros guardados",
|
||||||
"rename": "Renombrar",
|
"rename": "Renombrar",
|
||||||
"rename_segment": "Renombrar segmento",
|
"rename_segment": "Renombrar segmento",
|
||||||
"delete_segment": "Eliminar segmento",
|
"delete_segment": "Eliminar segmento",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista de selección",
|
"picklist": "Lista de selección",
|
||||||
"most_cancelled_bookings": "Más cancelaciones",
|
"most_cancelled_bookings": "Más cancelaciones",
|
||||||
"interface_language": "Idioma de la interfaz",
|
"interface_language": "Idioma de la interfaz",
|
||||||
|
"interface_language_description": "Establece tu idioma preferido para la interfaz de reservas",
|
||||||
"visitors_browser_language": "Idioma del navegador del visitante",
|
"visitors_browser_language": "Idioma del navegador del visitante",
|
||||||
"name_or_email": "Nombre o correo electrónico",
|
"name_or_email": "Nombre o correo electrónico",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no se encuentra ningún contacto, recurrir al propietario del lead si existe",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si no se encuentra ningún contacto, recurrir al propietario del lead si existe",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editar y modificar ajustes de flujos de trabajo",
|
"pbac_desc_update_workflows": "Editar y modificar ajustes de flujos de trabajo",
|
||||||
"pbac_desc_delete_workflows": "Eliminar flujos de trabajo del sistema",
|
"pbac_desc_delete_workflows": "Eliminar flujos de trabajo del sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilidad",
|
||||||
|
"pbac_resource_out_of_office": "Fuera de la oficina",
|
||||||
"pbac_desc_create_webhooks": "Crear webhooks",
|
"pbac_desc_create_webhooks": "Crear webhooks",
|
||||||
"pbac_desc_view_webhooks": "Ver webhooks",
|
"pbac_desc_view_webhooks": "Ver webhooks",
|
||||||
"pbac_desc_update_webhooks": "Actualizar webhooks",
|
"pbac_desc_update_webhooks": "Actualizar webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Eliminar webhooks",
|
"pbac_desc_delete_webhooks": "Eliminar webhooks",
|
||||||
|
"pbac_desc_create_availability": "Crear disponibilidad",
|
||||||
|
"pbac_desc_view_availability": "Ver disponibilidad",
|
||||||
|
"pbac_desc_update_availability": "Actualizar disponibilidad",
|
||||||
|
"pbac_desc_delete_availability": "Eliminar disponibilidad",
|
||||||
|
"pbac_desc_create_out_of_office": "Crear fuera de la oficina",
|
||||||
|
"pbac_desc_view_out_of_office": "Ver fuera de la oficina",
|
||||||
|
"pbac_desc_update_out_of_office": "Actualizar fuera de la oficina",
|
||||||
|
"pbac_desc_delete_out_of_office": "Eliminar fuera de la oficina",
|
||||||
"pbac_desc_manage_workflows": "Acceso completo de gestión a todos los flujos de trabajo",
|
"pbac_desc_manage_workflows": "Acceso completo de gestión a todos los flujos de trabajo",
|
||||||
"pbac_desc_create_event_types": "Crear tipos de eventos",
|
"pbac_desc_create_event_types": "Crear tipos de eventos",
|
||||||
"pbac_desc_view_event_types": "Ver tipos de eventos",
|
"pbac_desc_view_event_types": "Ver tipos de eventos",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Salajane koosolek",
|
"secret_meeting": "Salajane koosolek",
|
||||||
"login_instead": "Selle asemel logi sisse",
|
"login_instead": "Selle asemel logi sisse",
|
||||||
"create_account": "Loo konto",
|
"create_account": "Loo konto",
|
||||||
|
"get_started": "Alusta",
|
||||||
"confirm_password": "Kinnita salasõna",
|
"confirm_password": "Kinnita salasõna",
|
||||||
"reset_your_password": "Seadke oma uus parool teie e-posti aadressile saadetud juhiste järgi.",
|
"reset_your_password": "Seadke oma uus parool teie e-posti aadressile saadetud juhiste järgi.",
|
||||||
"org_banner_instructions": "Palun laadige üles pilt laiusega {{width}} ja kõrgusega {{height}}.",
|
"org_banner_instructions": "Palun laadige üles pilt laiusega {{width}} ja kõrgusega {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Osalejad",
|
"attendees": "Osalejad",
|
||||||
"add_attendees": "Lisa osalejaid",
|
"add_attendees": "Lisa osalejaid",
|
||||||
"show_advanced_settings": "Kuva täpsemad seaded",
|
"show_advanced_settings": "Kuva täpsemad seaded",
|
||||||
|
"hide_advanced_settings": "Peida täpsemad seaded",
|
||||||
"event_name": "Sündmuse nimi",
|
"event_name": "Sündmuse nimi",
|
||||||
"event_name_in_calendar": "Sündmuse nimega",
|
"event_name_in_calendar": "Sündmuse nimega",
|
||||||
"event_name_tooltip": "Kalendrites kuvatav nimi",
|
"event_name_tooltip": "Kalendrites kuvatav nimi",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Võtke ühendust müügiga",
|
"contact_sales": "Võtke ühendust müügiga",
|
||||||
"error_404": "Viga 404",
|
"error_404": "Viga 404",
|
||||||
"default": "Vaikimisi",
|
"default": "Vaikimisi",
|
||||||
"set_to_default": "Set to Default",
|
"set_to_default": "Määra vaikimisi",
|
||||||
"new_schedule_btn": "Uus ajakava",
|
"new_schedule_btn": "Uus ajakava",
|
||||||
"add_new_schedule": "Lisa uus ajakava",
|
"add_new_schedule": "Lisa uus ajakava",
|
||||||
"add_new_calendar": "Lisa uus kalender",
|
"add_new_calendar": "Lisa uus kalender",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Teie meeskonnaliikmed ei näe teisi meeskonnaliikmeid, kui see on sisse lülitatud.",
|
"make_team_private_description": "Teie meeskonnaliikmed ei näe teisi meeskonnaliikmeid, kui see on sisse lülitatud.",
|
||||||
"you_cannot_see_team_members": "Te ei saa näha erameeskonna kõiki meeskonnaliikmeid.",
|
"you_cannot_see_team_members": "Te ei saa näha erameeskonna kõiki meeskonnaliikmeid.",
|
||||||
"you_cannot_see_teams_of_org": "Te ei saa näha eraorganisatsiooni meeskondi.",
|
"you_cannot_see_teams_of_org": "Te ei saa näha eraorganisatsiooni meeskondi.",
|
||||||
"allow_booker_to_select_duration": "Luba broneerijal valida kestus",
|
"allow_multiple_durations": "Luba mitu kestust",
|
||||||
"impersonate_user_tip": "Kõik selle funktsiooni kasutused on auditeeritud.",
|
"impersonate_user_tip": "Kõik selle funktsiooni kasutused on auditeeritud.",
|
||||||
"impersonating_user_warning": "Esitan end kasutajanimena \"{{user}}\".",
|
"impersonating_user_warning": "Esitan end kasutajanimena \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Peatamiseks klõpsake siin",
|
"impersonating_stop_instructions": "Peatamiseks klõpsake siin",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Broneeringu kinnitamine ebaõnnestus",
|
"booking_confirmation_failed": "Broneeringu kinnitamine ebaõnnestus",
|
||||||
"not_enough_seats": "Pole piisavalt kohti",
|
"not_enough_seats": "Pole piisavalt kohti",
|
||||||
"form_builder_field_already_exists": "Selle nimega väli on juba olemas",
|
"form_builder_field_already_exists": "Selle nimega väli on juba olemas",
|
||||||
|
"guests_field_must_be_multiemail": "Külaliste väli peab olema tüüpi 'Mitu e-posti'",
|
||||||
"show_on_booking_page": "Näita broneerimislehel",
|
"show_on_booking_page": "Näita broneerimislehel",
|
||||||
"visit_cancelled_booking": "Saate külastada tühistatud broneeringu lehte",
|
"visit_cancelled_booking": "Saate külastada tühistatud broneeringu lehte",
|
||||||
"get_started_zapier_templates": "Alustage Zapieri mallidega",
|
"get_started_zapier_templates": "Alustage Zapieri mallidega",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Meeskonna administraatori poolt lukustatud",
|
"locked_by_team_admin": "Meeskonna administraatori poolt lukustatud",
|
||||||
"app_not_connected": "Te pole rakenduse {{appName}} kontot ühendanud.",
|
"app_not_connected": "Te pole rakenduse {{appName}} kontot ühendanud.",
|
||||||
"connect_now": "Ühenda kohe",
|
"connect_now": "Ühenda kohe",
|
||||||
|
"connect_now_unavailable_tooltip": "Funktsioon 'Ühenda kohe' ei ole praegu saadaval. Palun broneerige ülaltoodud ajavahemikest või proovige hiljem uuesti.",
|
||||||
"managed_event_dialog_confirm_button_one": "Asenda ja teavita {{count}} liiget",
|
"managed_event_dialog_confirm_button_one": "Asenda ja teavita {{count}} liiget",
|
||||||
"managed_event_dialog_confirm_button_other": "Asenda ja teavitage {{count}} liiget",
|
"managed_event_dialog_confirm_button_other": "Asenda ja teavitage {{count}} liiget",
|
||||||
"managed_event_dialog_title_one": "URL /{{slug}} on {{count}} liikme jaoks juba olemas. Kas soovite selle asendada?",
|
"managed_event_dialog_title_one": "URL /{{slug}} on {{count}} liikme jaoks juba olemas. Kas soovite selle asendada?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Jätkates nõustute meie",
|
"proceeding_agreement": "Jätkates nõustute meie",
|
||||||
"terms": "Tingimustega",
|
"terms": "Tingimustega",
|
||||||
"always_show_x_days": "Alati {{x}} päeva saadaval",
|
"always_show_x_days": "Alati {{x}} päeva saadaval",
|
||||||
|
"always_show_x_days_description": "Liigutab automaatselt {{x}}-päevase broneerimisakna iga päev edasi, tagades, et teie kalender näitab alati järgmise {{x}} päeva saadavalolevaid aegu.",
|
||||||
"team_subscription_cancelled_successfully": "Meeskonna tellimus tühistati edukalt",
|
"team_subscription_cancelled_successfully": "Meeskonna tellimus tühistati edukalt",
|
||||||
"team_subscription_cancellation_error": "Viga meeskonna tellimuse tühistamisel",
|
"team_subscription_cancellation_error": "Viga meeskonna tellimuse tühistamisel",
|
||||||
"unable_to_subscribe_to_the_platform": "Platvormipaketi tellimisel ilmnes viga, proovige hiljem uuesti",
|
"unable_to_subscribe_to_the_platform": "Platvormipaketi tellimisel ilmnes viga, proovige hiljem uuesti",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Lõpeb",
|
"filter_operator_ends_with": "Lõpeb",
|
||||||
"filter_operator_is_empty": "On tühi",
|
"filter_operator_is_empty": "On tühi",
|
||||||
"filter_operator_not_empty": "Ei ole tühi",
|
"filter_operator_not_empty": "Ei ole tühi",
|
||||||
"translate_description_button": "Tõlgi pealkiri/kirjeldus tehisintellekti abil külastaja brauseri keelde",
|
"translate_description_button": "Tõlgi pealkiri ja kirjeldus automaatselt",
|
||||||
|
"translate_description_button_description": "Tõlgib pealkirjad ja kirjeldused automaatselt külastaja brauseri keelde, kasutades tehisintellekti.",
|
||||||
"rr_distribution_method": "Jaotamine",
|
"rr_distribution_method": "Jaotamine",
|
||||||
"rr_distribution_method_description": "Võimaldab optimeerida jaotust maksimaalseks kättesaadavuseks või tasakaalustatumaks määramiseks.",
|
"rr_distribution_method_description": "Võimaldab optimeerida jaotust maksimaalseks kättesaadavuseks või tasakaalustatumaks määramiseks.",
|
||||||
"rr_distribution_method_availability_title": "Maksimeeri kättesaadavus",
|
"rr_distribution_method_availability_title": "Maksimeeri kättesaadavus",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Kohtade funktsioon ei toeta korduvat sündmust. Keelake kohtade funktsioon, et muuta see korduvaks.",
|
"seats_doesnt_support_recurring": "Kohtade funktsioon ei toeta korduvat sündmust. Keelake kohtade funktsioon, et muuta see korduvaks.",
|
||||||
"recurring_event_seats_error": "Korduv sündmus ei toeta kohtade funktsiooni. Keelake kohtade funktsioon või muutke sündmus mittekorduvaks.",
|
"recurring_event_seats_error": "Korduv sündmus ei toeta kohtade funktsiooni. Keelake kohtade funktsioon või muutke sündmus mittekorduvaks.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Salvestatud filtrid",
|
||||||
"rename": "Nimeta ümber",
|
"rename": "Nimeta ümber",
|
||||||
"rename_segment": "Nimeta segment ümber",
|
"rename_segment": "Nimeta segment ümber",
|
||||||
"delete_segment": "Kustuta segment",
|
"delete_segment": "Kustuta segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Valikloend",
|
"picklist": "Valikloend",
|
||||||
"most_cancelled_bookings": "Kõige rohkem tühistatud broneeringuid",
|
"most_cancelled_bookings": "Kõige rohkem tühistatud broneeringuid",
|
||||||
"interface_language": "Liidese keel",
|
"interface_language": "Liidese keel",
|
||||||
|
"interface_language_description": "Määra broneerimisliidese eelistatud keel",
|
||||||
"visitors_browser_language": "Külastaja brauseri keel",
|
"visitors_browser_language": "Külastaja brauseri keel",
|
||||||
"name_or_email": "Nimi või e-post",
|
"name_or_email": "Nimi või e-post",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Kui kontakti ei leita, kasuta võimalusel liidi omanikku",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Kui kontakti ei leita, kasuta võimalusel liidi omanikku",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Muuda ja redigeeri töövoo seadeid",
|
"pbac_desc_update_workflows": "Muuda ja redigeeri töövoo seadeid",
|
||||||
"pbac_desc_delete_workflows": "Eemalda töövood süsteemist",
|
"pbac_desc_delete_workflows": "Eemalda töövood süsteemist",
|
||||||
"pbac_resource_webhook": "Veebihaak",
|
"pbac_resource_webhook": "Veebihaak",
|
||||||
|
"pbac_resource_availability": "Saadavus",
|
||||||
|
"pbac_resource_out_of_office": "Kontorist väljas",
|
||||||
"pbac_desc_create_webhooks": "Loo veebihaake",
|
"pbac_desc_create_webhooks": "Loo veebihaake",
|
||||||
"pbac_desc_view_webhooks": "Vaata veebihaake",
|
"pbac_desc_view_webhooks": "Vaata veebihaake",
|
||||||
"pbac_desc_update_webhooks": "Uuenda veebihaake",
|
"pbac_desc_update_webhooks": "Uuenda veebihaake",
|
||||||
"pbac_desc_delete_webhooks": "Kustuta veebihaake",
|
"pbac_desc_delete_webhooks": "Kustuta veebihaake",
|
||||||
|
"pbac_desc_create_availability": "Loo saadavus",
|
||||||
|
"pbac_desc_view_availability": "Vaata saadavust",
|
||||||
|
"pbac_desc_update_availability": "Uuenda saadavust",
|
||||||
|
"pbac_desc_delete_availability": "Kustuta saadavus",
|
||||||
|
"pbac_desc_create_out_of_office": "Loo kontorist väljas olek",
|
||||||
|
"pbac_desc_view_out_of_office": "Vaata kontorist väljas olekut",
|
||||||
|
"pbac_desc_update_out_of_office": "Uuenda kontorist väljas olekut",
|
||||||
|
"pbac_desc_delete_out_of_office": "Kustuta kontorist väljas olek",
|
||||||
"pbac_desc_manage_workflows": "Täielik haldusõigus kõikidele töövoogudele",
|
"pbac_desc_manage_workflows": "Täielik haldusõigus kõikidele töövoogudele",
|
||||||
"pbac_desc_create_event_types": "Loo sündmuse tüüpe",
|
"pbac_desc_create_event_types": "Loo sündmuse tüüpe",
|
||||||
"pbac_desc_view_event_types": "Vaata sündmuse tüüpe",
|
"pbac_desc_view_event_types": "Vaata sündmuse tüüpe",
|
||||||
@@ -3560,7 +3578,7 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Koormuse tasakaalustamine ei ole saadaval Round Robin gruppidega",
|
"rr_load_balancing_disabled_with_groups": "Koormuse tasakaalustamine ei ole saadaval Round Robin gruppidega",
|
||||||
"round_robin_settings_description": "Kohanda selle meeskonna vaikimisi round robin'i seadeid",
|
"round_robin_settings_description": "Kohanda selle meeskonna vaikimisi round robin'i seadeid",
|
||||||
"round_robin_settings_updated_successfully": "Round robin'i seaded on edukalt uuendatud",
|
"round_robin_settings_updated_successfully": "Round robin'i seaded on edukalt uuendatud",
|
||||||
"booking_creation_time": "Broneeringu loomise aeg",
|
"booking_creation_time": "Broneerimise loomise aeg",
|
||||||
"meeting_start_time": "Kohtumise algusaeg",
|
"meeting_start_time": "Kohtumise algusaeg",
|
||||||
"members_affected_by_disabling_delegation_credential": "Mõjutatud liikmed",
|
"members_affected_by_disabling_delegation_credential": "Mõjutatud liikmed",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Delegatsiooni mandaadi keelamine ei mõjuta ühtegi liiget",
|
"no_members_affected_by_disabling_delegation_credential": "Delegatsiooni mandaadi keelamine ei mõjuta ühtegi liiget",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Bilera sekretua",
|
"secret_meeting": "Bilera sekretua",
|
||||||
"login_instead": "Saioa hasi ordez",
|
"login_instead": "Saioa hasi ordez",
|
||||||
"create_account": "Sortu kontua",
|
"create_account": "Sortu kontua",
|
||||||
|
"get_started": "Hasi",
|
||||||
"confirm_password": "Baieztatu pasahitza",
|
"confirm_password": "Baieztatu pasahitza",
|
||||||
"reset_your_password": "Ezarri zure pasahitz berria zure email helbidera bidalitako argibideak jarraituz.",
|
"reset_your_password": "Ezarri zure pasahitz berria zure email helbidera bidalitako argibideak jarraituz.",
|
||||||
"org_banner_instructions": "Mesedez, igo {{width}} zabalera eta {{height}} altuera duen irudia.",
|
"org_banner_instructions": "Mesedez, igo {{width}} zabalera eta {{height}} altuera duen irudia.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Partaideak",
|
"attendees": "Partaideak",
|
||||||
"add_attendees": "Gehitu partaideak",
|
"add_attendees": "Gehitu partaideak",
|
||||||
"show_advanced_settings": "Erakutsi ezarpen aurreratuak",
|
"show_advanced_settings": "Erakutsi ezarpen aurreratuak",
|
||||||
|
"hide_advanced_settings": "Ezkutatu ezarpen aurreratuak",
|
||||||
"event_name": "Gertaeraren izena",
|
"event_name": "Gertaeraren izena",
|
||||||
"event_name_in_calendar": "Gertaeraren izenarekin",
|
"event_name_in_calendar": "Gertaeraren izenarekin",
|
||||||
"event_name_tooltip": "Egutegian agertuko den izena",
|
"event_name_tooltip": "Egutegian agertuko den izena",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Jarri harremanetan salmenta taldearekin",
|
"contact_sales": "Jarri harremanetan salmenta taldearekin",
|
||||||
"error_404": "404 errorea",
|
"error_404": "404 errorea",
|
||||||
"default": "Lehenetsitakoa",
|
"default": "Lehenetsitakoa",
|
||||||
"set_to_default": "Ezarri lehenetsitako gisa",
|
"set_to_default": "Ezarri lehenetsi gisa",
|
||||||
"new_schedule_btn": "Programazio berria",
|
"new_schedule_btn": "Programazio berria",
|
||||||
"add_new_schedule": "Gehitu programazio berria",
|
"add_new_schedule": "Gehitu programazio berria",
|
||||||
"add_new_calendar": "Gehitu egutegi berria",
|
"add_new_calendar": "Gehitu egutegi berria",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Hau aktibatuta dagoenean, zure taldeko kideek ezin izango dituzte beste taldekideak ikusi.",
|
"make_team_private_description": "Hau aktibatuta dagoenean, zure taldeko kideek ezin izango dituzte beste taldekideak ikusi.",
|
||||||
"you_cannot_see_team_members": "Ezin dituzu talde pribatu bateko kide guztiak ikusi.",
|
"you_cannot_see_team_members": "Ezin dituzu talde pribatu bateko kide guztiak ikusi.",
|
||||||
"you_cannot_see_teams_of_org": "Ezin dituzu erakunde pribatu bateko taldeak ikusi.",
|
"you_cannot_see_teams_of_org": "Ezin dituzu erakunde pribatu bateko taldeak ikusi.",
|
||||||
"allow_booker_to_select_duration": "Erreserbatzaileari iraupena aukeratzen utzi",
|
"allow_multiple_durations": "Baimendu iraupen anitzak",
|
||||||
"impersonate_user_tip": "Ezaugarri honen erabilera guztiak auditatzen dira.",
|
"impersonate_user_tip": "Ezaugarri honen erabilera guztiak auditatzen dira.",
|
||||||
"impersonating_user_warning": "\"{{user}}\" erabiltzailea ordezkatzen.",
|
"impersonating_user_warning": "\"{{user}}\" erabiltzailea ordezkatzen.",
|
||||||
"impersonating_stop_instructions": "Klikatu hemen gelditzeko",
|
"impersonating_stop_instructions": "Klikatu hemen gelditzeko",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Erreserba berrespena huts egin du",
|
"booking_confirmation_failed": "Erreserba berrespena huts egin du",
|
||||||
"not_enough_seats": "Ez dago nahikoa eserleku",
|
"not_enough_seats": "Ez dago nahikoa eserleku",
|
||||||
"form_builder_field_already_exists": "Izen hori duen eremu bat existitzen da dagoeneko",
|
"form_builder_field_already_exists": "Izen hori duen eremu bat existitzen da dagoeneko",
|
||||||
|
"guests_field_must_be_multiemail": "Gonbidatuen eremuak 'Helbide elektroniko anitzak' motakoa izan behar du",
|
||||||
"show_on_booking_page": "Erakutsi erreserba orrian",
|
"show_on_booking_page": "Erakutsi erreserba orrian",
|
||||||
"visit_cancelled_booking": "Bertan behera utzitako erreserba orria bisitatu dezakezu",
|
"visit_cancelled_booking": "Bertan behera utzitako erreserba orria bisitatu dezakezu",
|
||||||
"get_started_zapier_templates": "Hasi Zapier txantiloiekin",
|
"get_started_zapier_templates": "Hasi Zapier txantiloiekin",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Taldeko administratzaileak blokeatuta",
|
"locked_by_team_admin": "Taldeko administratzaileak blokeatuta",
|
||||||
"app_not_connected": "Ez duzu {{appName}} konturik konektatu.",
|
"app_not_connected": "Ez duzu {{appName}} konturik konektatu.",
|
||||||
"connect_now": "Konektatu orain",
|
"connect_now": "Konektatu orain",
|
||||||
|
"connect_now_unavailable_tooltip": "Orain konektatzea ez dago eskuragarri. Mesedez, erreserbatu goiko txandetatik edo saiatu berriro geroago.",
|
||||||
"managed_event_dialog_confirm_button_one": "Ordezkatu eta {{count}} kideari jakinarazi",
|
"managed_event_dialog_confirm_button_one": "Ordezkatu eta {{count}} kideari jakinarazi",
|
||||||
"managed_event_dialog_confirm_button_other": "Ordezkatu eta {{count}} kideei jakinarazi",
|
"managed_event_dialog_confirm_button_other": "Ordezkatu eta {{count}} kideei jakinarazi",
|
||||||
"managed_event_dialog_title_one": "/{{slug}} URLa dagoeneko existitzen da {{count}} kiderentzat. Ordezkatu nahi duzu?",
|
"managed_event_dialog_title_one": "/{{slug}} URLa dagoeneko existitzen da {{count}} kiderentzat. Ordezkatu nahi duzu?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Jarraituz gero, gure honekin ados zaudela adierazten duzu",
|
"proceeding_agreement": "Jarraituz gero, gure honekin ados zaudela adierazten duzu",
|
||||||
"terms": "Baldintzak",
|
"terms": "Baldintzak",
|
||||||
"always_show_x_days": "Beti {{x}} egun erabilgarri",
|
"always_show_x_days": "Beti {{x}} egun erabilgarri",
|
||||||
|
"always_show_x_days_description": "Automatikoki mugitzen du {{x}} eguneko erreserba-leihoa egunero, zure egutegiak beti hurrengo {{x}} egunetako txanda erabilgarriak erakusten dituela ziurtatuz.",
|
||||||
"team_subscription_cancelled_successfully": "Taldearen harpidetza behar bezala ezeztatu da",
|
"team_subscription_cancelled_successfully": "Taldearen harpidetza behar bezala ezeztatu da",
|
||||||
"team_subscription_cancellation_error": "Errorea gertatu da taldearen harpidetza ezeztatzean",
|
"team_subscription_cancellation_error": "Errorea gertatu da taldearen harpidetza ezeztatzean",
|
||||||
"unable_to_subscribe_to_the_platform": "Errore bat gertatu da plataformaren planera harpidetzen saiatzean, saiatu berriro geroago",
|
"unable_to_subscribe_to_the_platform": "Errore bat gertatu da plataformaren planera harpidetzen saiatzean, saiatu berriro geroago",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Honekin amaitzen da",
|
"filter_operator_ends_with": "Honekin amaitzen da",
|
||||||
"filter_operator_is_empty": "Hutsik dago",
|
"filter_operator_is_empty": "Hutsik dago",
|
||||||
"filter_operator_not_empty": "Ez dago hutsik",
|
"filter_operator_not_empty": "Ez dago hutsik",
|
||||||
"translate_description_button": "Bisitariaren nabigatzailearen hizkuntzara itzuli titulua/deskribapena AI erabiliz",
|
"translate_description_button": "Auto itzuli izenburua eta deskribapena",
|
||||||
|
"translate_description_button_description": "Automatikoki itzuli izenburuak eta deskribapenak bisitariaren nabigatzailearen hizkuntzara adimen artifizialaren bidez.",
|
||||||
"rr_distribution_method": "Banaketa",
|
"rr_distribution_method": "Banaketa",
|
||||||
"rr_distribution_method_description": "Banaketa optimizatzeko aukera ematen du, erabilgarritasun maximoa edo esleipen orekatuagoa lortzeko",
|
"rr_distribution_method_description": "Banaketa optimizatzeko aukera ematen du, erabilgarritasun maximoa edo esleipen orekatuagoa lortzeko",
|
||||||
"rr_distribution_method_availability_title": "Erabilgarritasuna maximizatu",
|
"rr_distribution_method_availability_title": "Erabilgarritasuna maximizatu",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Eserlekuen ezaugarriak ez du ekitaldi errepikakorrik onartzen. Desgaitu eserlekuen ezaugarria errepikakor egiteko.",
|
"seats_doesnt_support_recurring": "Eserlekuen ezaugarriak ez du ekitaldi errepikakorrik onartzen. Desgaitu eserlekuen ezaugarria errepikakor egiteko.",
|
||||||
"recurring_event_seats_error": "Ekitaldi errepikakorrak ez du eserlekuen ezaugarria onartzen. Desgaitu eserlekuen ezaugarria edo egin ekitaldia ez-errepikakor.",
|
"recurring_event_seats_error": "Ekitaldi errepikakorrak ez du eserlekuen ezaugarria onartzen. Desgaitu eserlekuen ezaugarria edo egin ekitaldia ez-errepikakor.",
|
||||||
"segment": "Segmentua",
|
"segment": "Segmentua",
|
||||||
|
"saved_filters": "Gordetako iragazkiak",
|
||||||
"rename": "Berrizendatu",
|
"rename": "Berrizendatu",
|
||||||
"rename_segment": "Berrizendatu segmentua",
|
"rename_segment": "Berrizendatu segmentua",
|
||||||
"delete_segment": "Ezabatu segmentua",
|
"delete_segment": "Ezabatu segmentua",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Hautaketa-zerrenda",
|
"picklist": "Hautaketa-zerrenda",
|
||||||
"most_cancelled_bookings": "Bertan behera utzitako erreserba gehien",
|
"most_cancelled_bookings": "Bertan behera utzitako erreserba gehien",
|
||||||
"interface_language": "Interfazearen hizkuntza",
|
"interface_language": "Interfazearen hizkuntza",
|
||||||
|
"interface_language_description": "Ezarri zure hizkuntza hobetsia erreserba interfazerako",
|
||||||
"visitors_browser_language": "Bisitariaren nabigatzailearen hizkuntza",
|
"visitors_browser_language": "Bisitariaren nabigatzailearen hizkuntza",
|
||||||
"name_or_email": "Izena edo helbide elektronikoa",
|
"name_or_email": "Izena edo helbide elektronikoa",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Kontakturik aurkitzen ez bada, itzuli bezeroen arduradunera existitzen bada",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Kontakturik aurkitzen ez bada, itzuli bezeroen arduradunera existitzen bada",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editatu eta aldatu lan-fluxuen ezarpenak",
|
"pbac_desc_update_workflows": "Editatu eta aldatu lan-fluxuen ezarpenak",
|
||||||
"pbac_desc_delete_workflows": "Kendu lan-fluxuak sistematik",
|
"pbac_desc_delete_workflows": "Kendu lan-fluxuak sistematik",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Erabilgarritasuna",
|
||||||
|
"pbac_resource_out_of_office": "Bulegotik kanpo",
|
||||||
"pbac_desc_create_webhooks": "Sortu webhook-ak",
|
"pbac_desc_create_webhooks": "Sortu webhook-ak",
|
||||||
"pbac_desc_view_webhooks": "Ikusi webhook-ak",
|
"pbac_desc_view_webhooks": "Ikusi webhook-ak",
|
||||||
"pbac_desc_update_webhooks": "Webhookak eguneratu",
|
"pbac_desc_update_webhooks": "Webhookak eguneratu",
|
||||||
"pbac_desc_delete_webhooks": "Webhookak ezabatu",
|
"pbac_desc_delete_webhooks": "Webhookak ezabatu",
|
||||||
|
"pbac_desc_create_availability": "Sortu erabilgarritasuna",
|
||||||
|
"pbac_desc_view_availability": "Ikusi erabilgarritasuna",
|
||||||
|
"pbac_desc_update_availability": "Eguneratu erabilgarritasuna",
|
||||||
|
"pbac_desc_delete_availability": "Ezabatu erabilgarritasuna",
|
||||||
|
"pbac_desc_create_out_of_office": "Sortu bulegotik kanpo",
|
||||||
|
"pbac_desc_view_out_of_office": "Ikusi bulegotik kanpo",
|
||||||
|
"pbac_desc_update_out_of_office": "Eguneratu bulegotik kanpo",
|
||||||
|
"pbac_desc_delete_out_of_office": "Ezabatu bulegotik kanpo",
|
||||||
"pbac_desc_manage_workflows": "Lan-fluxu guztien kudeaketa-sarbide osoa",
|
"pbac_desc_manage_workflows": "Lan-fluxu guztien kudeaketa-sarbide osoa",
|
||||||
"pbac_desc_create_event_types": "Gertaera motak sortu",
|
"pbac_desc_create_event_types": "Gertaera motak sortu",
|
||||||
"pbac_desc_view_event_types": "Gertaera motak ikusi",
|
"pbac_desc_view_event_types": "Gertaera motak ikusi",
|
||||||
@@ -3560,8 +3578,8 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Karga-orekatzea ez dago eskuragarri Round Robin taldeekin",
|
"rr_load_balancing_disabled_with_groups": "Karga-orekatzea ez dago eskuragarri Round Robin taldeekin",
|
||||||
"round_robin_settings_description": "Pertsonalizatu talde honentzako txandakako ezarpen lehenetsiak",
|
"round_robin_settings_description": "Pertsonalizatu talde honentzako txandakako ezarpen lehenetsiak",
|
||||||
"round_robin_settings_updated_successfully": "Txandakako ezarpenak ondo eguneratu dira",
|
"round_robin_settings_updated_successfully": "Txandakako ezarpenak ondo eguneratu dira",
|
||||||
"booking_creation_time": "Erreserba sortze-ordua",
|
"booking_creation_time": "Erreserba sortzeko ordua",
|
||||||
"meeting_start_time": "Bilera hasiera-ordua",
|
"meeting_start_time": "Bilera hasteko ordua",
|
||||||
"members_affected_by_disabling_delegation_credential": "Eragindako kideak",
|
"members_affected_by_disabling_delegation_credential": "Eragindako kideak",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Ez dago kiderik delegazio kredentzialak desgaitzeak eraginda",
|
"no_members_affected_by_disabling_delegation_credential": "Ez dago kiderik delegazio kredentzialak desgaitzeak eraginda",
|
||||||
"download_expense_log": "Deskargatu gastuen erregistroa",
|
"download_expense_log": "Deskargatu gastuen erregistroa",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Salainen tapaaminen",
|
"secret_meeting": "Salainen tapaaminen",
|
||||||
"login_instead": "Kirjaudu sen sijaan",
|
"login_instead": "Kirjaudu sen sijaan",
|
||||||
"create_account": "Luo tili",
|
"create_account": "Luo tili",
|
||||||
|
"get_started": "Aloita",
|
||||||
"confirm_password": "Vahvista salasana",
|
"confirm_password": "Vahvista salasana",
|
||||||
"reset_your_password": "Aseta uusi salasanasi sähköpostiisi lähetettyjen ohjeiden mukaisesti.",
|
"reset_your_password": "Aseta uusi salasanasi sähköpostiisi lähetettyjen ohjeiden mukaisesti.",
|
||||||
"org_banner_instructions": "Lataa kuva, jonka leveys on {{width}} ja korkeus {{height}}.",
|
"org_banner_instructions": "Lataa kuva, jonka leveys on {{width}} ja korkeus {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Osallistujat",
|
"attendees": "Osallistujat",
|
||||||
"add_attendees": "Lisää osallistujia",
|
"add_attendees": "Lisää osallistujia",
|
||||||
"show_advanced_settings": "Näytä lisäasetukset",
|
"show_advanced_settings": "Näytä lisäasetukset",
|
||||||
|
"hide_advanced_settings": "Piilota lisäasetukset",
|
||||||
"event_name": "Tapahtuman nimi",
|
"event_name": "Tapahtuman nimi",
|
||||||
"event_name_in_calendar": "Tapahtuman nimellä",
|
"event_name_in_calendar": "Tapahtuman nimellä",
|
||||||
"event_name_tooltip": "Nimi, joka näkyy kalentereissa",
|
"event_name_tooltip": "Nimi, joka näkyy kalentereissa",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Tiimisi jäsenet eivät näe muita tiimin jäseniä, kun tämä on käytössä.",
|
"make_team_private_description": "Tiimisi jäsenet eivät näe muita tiimin jäseniä, kun tämä on käytössä.",
|
||||||
"you_cannot_see_team_members": "Et voi nähdä yksityisen tiimin kaikkia jäseniä.",
|
"you_cannot_see_team_members": "Et voi nähdä yksityisen tiimin kaikkia jäseniä.",
|
||||||
"you_cannot_see_teams_of_org": "Et voi nähdä yksityisen organisaation tiimejä.",
|
"you_cannot_see_teams_of_org": "Et voi nähdä yksityisen organisaation tiimejä.",
|
||||||
"allow_booker_to_select_duration": "Salli varaajan valita kesto",
|
"allow_multiple_durations": "Salli useita kestoja",
|
||||||
"impersonate_user_tip": "Kaikki tämän toiminnon käyttö kirjataan.",
|
"impersonate_user_tip": "Kaikki tämän toiminnon käyttö kirjataan.",
|
||||||
"impersonating_user_warning": "Esiinnyt käyttäjänä \"{{user}}\".",
|
"impersonating_user_warning": "Esiinnyt käyttäjänä \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Klikkaa tästä lopettaaksesi",
|
"impersonating_stop_instructions": "Klikkaa tästä lopettaaksesi",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Varauksen vahvistaminen epäonnistui",
|
"booking_confirmation_failed": "Varauksen vahvistaminen epäonnistui",
|
||||||
"not_enough_seats": "Ei tarpeeksi paikkoja",
|
"not_enough_seats": "Ei tarpeeksi paikkoja",
|
||||||
"form_builder_field_already_exists": "Tämänniminen kenttä on jo olemassa",
|
"form_builder_field_already_exists": "Tämänniminen kenttä on jo olemassa",
|
||||||
|
"guests_field_must_be_multiemail": "Vieraiden kentän täytyy olla tyyppiä 'Useita sähköposteja'",
|
||||||
"show_on_booking_page": "Näytä varaussivulla",
|
"show_on_booking_page": "Näytä varaussivulla",
|
||||||
"visit_cancelled_booking": "Voit tarkastella peruutettua varausta",
|
"visit_cancelled_booking": "Voit tarkastella peruutettua varausta",
|
||||||
"get_started_zapier_templates": "Aloita Zapier-malleilla",
|
"get_started_zapier_templates": "Aloita Zapier-malleilla",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Tiimin ylläpitäjän lukitsema",
|
"locked_by_team_admin": "Tiimin ylläpitäjän lukitsema",
|
||||||
"app_not_connected": "Et ole yhdistänyt {{appName}}-tiliä.",
|
"app_not_connected": "Et ole yhdistänyt {{appName}}-tiliä.",
|
||||||
"connect_now": "Yhdistä nyt",
|
"connect_now": "Yhdistä nyt",
|
||||||
|
"connect_now_unavailable_tooltip": "Yhdistä nyt -toiminto ei ole tällä hetkellä käytettävissä. Varaa yllä olevista ajoista tai yritä myöhemmin uudelleen.",
|
||||||
"managed_event_dialog_confirm_button_one": "Korvaa ja ilmoita {{count}} jäsenelle",
|
"managed_event_dialog_confirm_button_one": "Korvaa ja ilmoita {{count}} jäsenelle",
|
||||||
"managed_event_dialog_confirm_button_other": "Korvaa ja ilmoita {{count}} jäsenelle",
|
"managed_event_dialog_confirm_button_other": "Korvaa ja ilmoita {{count}} jäsenelle",
|
||||||
"managed_event_dialog_title_one": "URL-osoite /{{slug}} on jo käytössä {{count}} jäsenellä. Haluatko korvata sen?",
|
"managed_event_dialog_title_one": "URL-osoite /{{slug}} on jo käytössä {{count}} jäsenellä. Haluatko korvata sen?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Jatkamalla hyväksyt meidän",
|
"proceeding_agreement": "Jatkamalla hyväksyt meidän",
|
||||||
"terms": "Käyttöehdot",
|
"terms": "Käyttöehdot",
|
||||||
"always_show_x_days": "Aina {{x}} päivää saatavilla",
|
"always_show_x_days": "Aina {{x}} päivää saatavilla",
|
||||||
|
"always_show_x_days_description": "Siirtää automaattisesti {{x}} päivän varausikkunaa eteenpäin joka päivä, varmistaen että kalenterisi näyttää aina seuraavat {{x}} päivää vapaista ajoista.",
|
||||||
"team_subscription_cancelled_successfully": "Tiimin tilaus peruutettu onnistuneesti",
|
"team_subscription_cancelled_successfully": "Tiimin tilaus peruutettu onnistuneesti",
|
||||||
"team_subscription_cancellation_error": "Virhe tiimin tilauksen peruuttamisessa",
|
"team_subscription_cancellation_error": "Virhe tiimin tilauksen peruuttamisessa",
|
||||||
"unable_to_subscribe_to_the_platform": "Tilausta alustaan ei voitu tehdä, yritä myöhemmin uudelleen",
|
"unable_to_subscribe_to_the_platform": "Tilausta alustaan ei voitu tehdä, yritä myöhemmin uudelleen",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Päättyy",
|
"filter_operator_ends_with": "Päättyy",
|
||||||
"filter_operator_is_empty": "On tyhjä",
|
"filter_operator_is_empty": "On tyhjä",
|
||||||
"filter_operator_not_empty": "Ei tyhjä",
|
"filter_operator_not_empty": "Ei tyhjä",
|
||||||
"translate_description_button": "Käännä otsikko/kuvaus kävijän selaimen kielelle tekoälyn avulla",
|
"translate_description_button": "Käännä otsikko ja kuvaus automaattisesti",
|
||||||
|
"translate_description_button_description": "Käännä otsikot ja kuvaukset automaattisesti vierailijan selaimen kielelle tekoälyn avulla.",
|
||||||
"rr_distribution_method": "Jakotapa",
|
"rr_distribution_method": "Jakotapa",
|
||||||
"rr_distribution_method_description": "Mahdollistaa jakelun optimoinnin joko maksimaalisen saatavuuden tai tasapainoisen jaon saavuttamiseksi.",
|
"rr_distribution_method_description": "Mahdollistaa jakelun optimoinnin joko maksimaalisen saatavuuden tai tasapainoisen jaon saavuttamiseksi.",
|
||||||
"rr_distribution_method_availability_title": "Maksimoi saatavuus",
|
"rr_distribution_method_availability_title": "Maksimoi saatavuus",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Paikkaominaisuus ei tue toistuvaa tapahtumaa. Poista paikkaominaisuus käytöstä tehdäksesi siitä toistuvan.",
|
"seats_doesnt_support_recurring": "Paikkaominaisuus ei tue toistuvaa tapahtumaa. Poista paikkaominaisuus käytöstä tehdäksesi siitä toistuvan.",
|
||||||
"recurring_event_seats_error": "Toistuva tapahtuma ei tue paikkaominaisuutta. Poista paikkaominaisuus käytöstä tai tee tapahtumasta ei-toistuva.",
|
"recurring_event_seats_error": "Toistuva tapahtuma ei tue paikkaominaisuutta. Poista paikkaominaisuus käytöstä tai tee tapahtumasta ei-toistuva.",
|
||||||
"segment": "Segmentti",
|
"segment": "Segmentti",
|
||||||
|
"saved_filters": "Tallennetut suodattimet",
|
||||||
"rename": "Nimeä uudelleen",
|
"rename": "Nimeä uudelleen",
|
||||||
"rename_segment": "Nimeä segmentti uudelleen",
|
"rename_segment": "Nimeä segmentti uudelleen",
|
||||||
"delete_segment": "Poista segmentti",
|
"delete_segment": "Poista segmentti",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Valintalistaus",
|
"picklist": "Valintalistaus",
|
||||||
"most_cancelled_bookings": "Eniten peruutettuja varauksia",
|
"most_cancelled_bookings": "Eniten peruutettuja varauksia",
|
||||||
"interface_language": "Käyttöliittymän kieli",
|
"interface_language": "Käyttöliittymän kieli",
|
||||||
|
"interface_language_description": "Aseta varausliittymän haluamasi kieli",
|
||||||
"visitors_browser_language": "Vierailijan selaimen kieli",
|
"visitors_browser_language": "Vierailijan selaimen kieli",
|
||||||
"name_or_email": "Nimi tai sähköposti",
|
"name_or_email": "Nimi tai sähköposti",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Jos yhteyshenkilöä ei löydy, käytä liidin omistajaa, jos sellainen on olemassa",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Jos yhteyshenkilöä ei löydy, käytä liidin omistajaa, jos sellainen on olemassa",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Muokkaa työnkulkujen asetuksia",
|
"pbac_desc_update_workflows": "Muokkaa työnkulkujen asetuksia",
|
||||||
"pbac_desc_delete_workflows": "Poista työnkulkuja järjestelmästä",
|
"pbac_desc_delete_workflows": "Poista työnkulkuja järjestelmästä",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Saatavuus",
|
||||||
|
"pbac_resource_out_of_office": "Poissa toimistolta",
|
||||||
"pbac_desc_create_webhooks": "Luo webhookeja",
|
"pbac_desc_create_webhooks": "Luo webhookeja",
|
||||||
"pbac_desc_view_webhooks": "Tarkastele webhookeja",
|
"pbac_desc_view_webhooks": "Tarkastele webhookeja",
|
||||||
"pbac_desc_update_webhooks": "Päivitä webhookit",
|
"pbac_desc_update_webhooks": "Päivitä webhookit",
|
||||||
"pbac_desc_delete_webhooks": "Poista webhookit",
|
"pbac_desc_delete_webhooks": "Poista webhookit",
|
||||||
|
"pbac_desc_create_availability": "Luo saatavuus",
|
||||||
|
"pbac_desc_view_availability": "Näytä saatavuus",
|
||||||
|
"pbac_desc_update_availability": "Päivitä saatavuus",
|
||||||
|
"pbac_desc_delete_availability": "Poista saatavuus",
|
||||||
|
"pbac_desc_create_out_of_office": "Luo poissaolo",
|
||||||
|
"pbac_desc_view_out_of_office": "Näytä poissaolo",
|
||||||
|
"pbac_desc_update_out_of_office": "Päivitä poissaolo",
|
||||||
|
"pbac_desc_delete_out_of_office": "Poista poissaolo",
|
||||||
"pbac_desc_manage_workflows": "Täydet hallintaoikeudet kaikkiin työnkulkuihin",
|
"pbac_desc_manage_workflows": "Täydet hallintaoikeudet kaikkiin työnkulkuihin",
|
||||||
"pbac_desc_create_event_types": "Luo tapahtumatyyppejä",
|
"pbac_desc_create_event_types": "Luo tapahtumatyyppejä",
|
||||||
"pbac_desc_view_event_types": "Näytä tapahtumatyypit",
|
"pbac_desc_view_event_types": "Näytä tapahtumatyypit",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Rendez-vous secret",
|
"secret_meeting": "Rendez-vous secret",
|
||||||
"login_instead": "Se connecter à la place",
|
"login_instead": "Se connecter à la place",
|
||||||
"create_account": "Créer un compte",
|
"create_account": "Créer un compte",
|
||||||
|
"get_started": "Commencer",
|
||||||
"confirm_password": "Confirmer le mot de passe",
|
"confirm_password": "Confirmer le mot de passe",
|
||||||
"reset_your_password": "Définissez votre nouveau mot de passe avec les instructions envoyées à votre adresse e-mail.",
|
"reset_your_password": "Définissez votre nouveau mot de passe avec les instructions envoyées à votre adresse e-mail.",
|
||||||
"org_banner_instructions": "Veuillez télécharger une image d'une largeur de {{width}} et d'une hauteur de {{height}}.",
|
"org_banner_instructions": "Veuillez télécharger une image d'une largeur de {{width}} et d'une hauteur de {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Participants",
|
"attendees": "Participants",
|
||||||
"add_attendees": "Ajouter des participants",
|
"add_attendees": "Ajouter des participants",
|
||||||
"show_advanced_settings": "Afficher les paramètres avancés",
|
"show_advanced_settings": "Afficher les paramètres avancés",
|
||||||
|
"hide_advanced_settings": "Masquer les paramètres avancés",
|
||||||
"event_name": "Nom de l'événement",
|
"event_name": "Nom de l'événement",
|
||||||
"event_name_in_calendar": "Nom de l'événement dans le calendrier",
|
"event_name_in_calendar": "Nom de l'événement dans le calendrier",
|
||||||
"event_name_tooltip": "Le nom qui apparaîtra dans les calendriers",
|
"event_name_tooltip": "Le nom qui apparaîtra dans les calendriers",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Les membres de votre équipe ne pourront pas voir les autres membres de l'équipe si cette option est activée.",
|
"make_team_private_description": "Les membres de votre équipe ne pourront pas voir les autres membres de l'équipe si cette option est activée.",
|
||||||
"you_cannot_see_team_members": "Vous ne pouvez pas voir tous les membres d'une équipe privée.",
|
"you_cannot_see_team_members": "Vous ne pouvez pas voir tous les membres d'une équipe privée.",
|
||||||
"you_cannot_see_teams_of_org": "Vous ne pouvez pas voir les équipes d'une organisation privée.",
|
"you_cannot_see_teams_of_org": "Vous ne pouvez pas voir les équipes d'une organisation privée.",
|
||||||
"allow_booker_to_select_duration": "Autoriser l'utilisateur à sélectionner la durée",
|
"allow_multiple_durations": "Autoriser plusieurs durées",
|
||||||
"impersonate_user_tip": "Toutes les utilisations de cette fonctionnalité sont vérifiées.",
|
"impersonate_user_tip": "Toutes les utilisations de cette fonctionnalité sont vérifiées.",
|
||||||
"impersonating_user_warning": "Emprunt du nom d'utilisateur « {{user}} ».",
|
"impersonating_user_warning": "Emprunt du nom d'utilisateur « {{user}} ».",
|
||||||
"impersonating_stop_instructions": "Cliquez ici pour arrêter",
|
"impersonating_stop_instructions": "Cliquez ici pour arrêter",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Échec de la confirmation de la réservation",
|
"booking_confirmation_failed": "Échec de la confirmation de la réservation",
|
||||||
"not_enough_seats": "Pas assez de places",
|
"not_enough_seats": "Pas assez de places",
|
||||||
"form_builder_field_already_exists": "Un champ avec ce nom existe déjà",
|
"form_builder_field_already_exists": "Un champ avec ce nom existe déjà",
|
||||||
|
"guests_field_must_be_multiemail": "Le champ invités doit être de type 'Emails multiples'",
|
||||||
"show_on_booking_page": "Afficher sur la page de réservation",
|
"show_on_booking_page": "Afficher sur la page de réservation",
|
||||||
"visit_cancelled_booking": "Vous pouvez consulter la page de réservation annulée",
|
"visit_cancelled_booking": "Vous pouvez consulter la page de réservation annulée",
|
||||||
"get_started_zapier_templates": "Démarrer avec les modèles Zapier",
|
"get_started_zapier_templates": "Démarrer avec les modèles Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Verrouillé par l'administrateur de l'équipe",
|
"locked_by_team_admin": "Verrouillé par l'administrateur de l'équipe",
|
||||||
"app_not_connected": "Vous n'avez pas connecté de compte {{appName}}.",
|
"app_not_connected": "Vous n'avez pas connecté de compte {{appName}}.",
|
||||||
"connect_now": "Connecter maintenant",
|
"connect_now": "Connecter maintenant",
|
||||||
|
"connect_now_unavailable_tooltip": "La connexion immédiate n'est pas disponible actuellement. Veuillez réserver parmi les créneaux ci-dessus ou réessayer plus tard.",
|
||||||
"managed_event_dialog_confirm_button_one": "Remplacer et notifier {{count}} membre",
|
"managed_event_dialog_confirm_button_one": "Remplacer et notifier {{count}} membre",
|
||||||
"managed_event_dialog_confirm_button_other": "Remplacer et notifier {{count}} membres",
|
"managed_event_dialog_confirm_button_other": "Remplacer et notifier {{count}} membres",
|
||||||
"managed_event_dialog_title_one": "Le lien /{{slug}} existe déjà pour {{count}} membre. Voulez-vous le remplacer ?",
|
"managed_event_dialog_title_one": "Le lien /{{slug}} existe déjà pour {{count}} membre. Voulez-vous le remplacer ?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "En continuant, vous acceptez nos",
|
"proceeding_agreement": "En continuant, vous acceptez nos",
|
||||||
"terms": "Conditions",
|
"terms": "Conditions",
|
||||||
"always_show_x_days": "Tout disponible pendant {{x}} jours",
|
"always_show_x_days": "Tout disponible pendant {{x}} jours",
|
||||||
|
"always_show_x_days_description": "Déplace automatiquement la fenêtre de réservation de {{x}} jours chaque jour, garantissant que votre calendrier affiche toujours les {{x}} prochains jours de créneaux disponibles.",
|
||||||
"team_subscription_cancelled_successfully": "Abonnement d'équipe annulé avec succès",
|
"team_subscription_cancelled_successfully": "Abonnement d'équipe annulé avec succès",
|
||||||
"team_subscription_cancellation_error": "Erreur lors de l'annulation de l'abonnement d'équipe",
|
"team_subscription_cancellation_error": "Erreur lors de l'annulation de l'abonnement d'équipe",
|
||||||
"unable_to_subscribe_to_the_platform": "Une erreur est survenue lors de la souscription à l'offre de plateforme. Veuillez réessayer plus tard",
|
"unable_to_subscribe_to_the_platform": "Une erreur est survenue lors de la souscription à l'offre de plateforme. Veuillez réessayer plus tard",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Se termine par",
|
"filter_operator_ends_with": "Se termine par",
|
||||||
"filter_operator_is_empty": "Est vide",
|
"filter_operator_is_empty": "Est vide",
|
||||||
"filter_operator_not_empty": "N'est pas vide",
|
"filter_operator_not_empty": "N'est pas vide",
|
||||||
"translate_description_button": "Traduire le titre/la description dans la langue du navigateur du visiteur grâce à l'IA",
|
"translate_description_button": "Traduction automatique du titre et de la description",
|
||||||
|
"translate_description_button_description": "Traduit automatiquement les titres et descriptions dans la langue du navigateur du visiteur en utilisant l'IA.",
|
||||||
"rr_distribution_method": "Distribution",
|
"rr_distribution_method": "Distribution",
|
||||||
"rr_distribution_method_description": "Permet d'optimiser la distribution pour une disponibilité maximale ou pour viser une répartition plus équilibrée.",
|
"rr_distribution_method_description": "Permet d'optimiser la distribution pour une disponibilité maximale ou pour viser une répartition plus équilibrée.",
|
||||||
"rr_distribution_method_availability_title": "Maximiser la disponibilité",
|
"rr_distribution_method_availability_title": "Maximiser la disponibilité",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "La fonctionnalité de places ne prend pas en charge les événements récurrents. Désactivez la fonctionnalité de places pour le rendre récurrent.",
|
"seats_doesnt_support_recurring": "La fonctionnalité de places ne prend pas en charge les événements récurrents. Désactivez la fonctionnalité de places pour le rendre récurrent.",
|
||||||
"recurring_event_seats_error": "Les événements récurrents ne prennent pas en charge la fonctionnalité de places. Désactivez la fonctionnalité de places ou rendez l'événement non récurrent.",
|
"recurring_event_seats_error": "Les événements récurrents ne prennent pas en charge la fonctionnalité de places. Désactivez la fonctionnalité de places ou rendez l'événement non récurrent.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Filtres enregistrés",
|
||||||
"rename": "Renommer",
|
"rename": "Renommer",
|
||||||
"rename_segment": "Renommer le segment",
|
"rename_segment": "Renommer le segment",
|
||||||
"delete_segment": "Supprimer le segment",
|
"delete_segment": "Supprimer le segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Liste de sélection",
|
"picklist": "Liste de sélection",
|
||||||
"most_cancelled_bookings": "Plus d'annulations",
|
"most_cancelled_bookings": "Plus d'annulations",
|
||||||
"interface_language": "Langue de l'interface",
|
"interface_language": "Langue de l'interface",
|
||||||
|
"interface_language_description": "Définissez votre langue préférée pour l'interface de réservation",
|
||||||
"visitors_browser_language": "Langue du navigateur du visiteur",
|
"visitors_browser_language": "Langue du navigateur du visiteur",
|
||||||
"name_or_email": "Nom ou e-mail",
|
"name_or_email": "Nom ou e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si aucun contact n'est trouvé, se rabattre sur le propriétaire du lead s'il existe",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Si aucun contact n'est trouvé, se rabattre sur le propriétaire du lead s'il existe",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Éditer et modifier les paramètres des workflows",
|
"pbac_desc_update_workflows": "Éditer et modifier les paramètres des workflows",
|
||||||
"pbac_desc_delete_workflows": "Supprimer des workflows du système",
|
"pbac_desc_delete_workflows": "Supprimer des workflows du système",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilités",
|
||||||
|
"pbac_resource_out_of_office": "Absence du bureau",
|
||||||
"pbac_desc_create_webhooks": "Créer des webhooks",
|
"pbac_desc_create_webhooks": "Créer des webhooks",
|
||||||
"pbac_desc_view_webhooks": "Consulter les webhooks",
|
"pbac_desc_view_webhooks": "Consulter les webhooks",
|
||||||
"pbac_desc_update_webhooks": "Mettre à jour les webhooks",
|
"pbac_desc_update_webhooks": "Mettre à jour les webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Supprimer les webhooks",
|
"pbac_desc_delete_webhooks": "Supprimer les webhooks",
|
||||||
|
"pbac_desc_create_availability": "Créer des disponibilités",
|
||||||
|
"pbac_desc_view_availability": "Voir les disponibilités",
|
||||||
|
"pbac_desc_update_availability": "Mettre à jour les disponibilités",
|
||||||
|
"pbac_desc_delete_availability": "Supprimer des disponibilités",
|
||||||
|
"pbac_desc_create_out_of_office": "Créer une absence",
|
||||||
|
"pbac_desc_view_out_of_office": "Voir les absences",
|
||||||
|
"pbac_desc_update_out_of_office": "Mettre à jour une absence",
|
||||||
|
"pbac_desc_delete_out_of_office": "Supprimer une absence",
|
||||||
"pbac_desc_manage_workflows": "Accès complet à la gestion de tous les workflows",
|
"pbac_desc_manage_workflows": "Accès complet à la gestion de tous les workflows",
|
||||||
"pbac_desc_create_event_types": "Créer des types d'événements",
|
"pbac_desc_create_event_types": "Créer des types d'événements",
|
||||||
"pbac_desc_view_event_types": "Voir les types d'événements",
|
"pbac_desc_view_event_types": "Voir les types d'événements",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "פגישה חשאית",
|
"secret_meeting": "פגישה חשאית",
|
||||||
"login_instead": "להיכנס במקום זאת",
|
"login_instead": "להיכנס במקום זאת",
|
||||||
"create_account": "צור חשבון",
|
"create_account": "צור חשבון",
|
||||||
|
"get_started": "להתחיל",
|
||||||
"confirm_password": "אישור סיסמה",
|
"confirm_password": "אישור סיסמה",
|
||||||
"reset_your_password": "הגדר/י את הסיסמה החדשה שלך לפי ההוראות שנשלחו אל כתובת הדוא\"ל שלך.",
|
"reset_your_password": "הגדר/י את הסיסמה החדשה שלך לפי ההוראות שנשלחו אל כתובת הדוא\"ל שלך.",
|
||||||
"org_banner_instructions": "נא להעלות תמונה ברוחב {{width}} וגובה {{height}}.",
|
"org_banner_instructions": "נא להעלות תמונה ברוחב {{width}} וגובה {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "משתתפים",
|
"attendees": "משתתפים",
|
||||||
"add_attendees": "הוסף משתתפים",
|
"add_attendees": "הוסף משתתפים",
|
||||||
"show_advanced_settings": "הצגת הגדרות מתקדמות",
|
"show_advanced_settings": "הצגת הגדרות מתקדמות",
|
||||||
|
"hide_advanced_settings": "הסתרת הגדרות מתקדמות",
|
||||||
"event_name": "שם האירוע",
|
"event_name": "שם האירוע",
|
||||||
"event_name_in_calendar": "שם הארוע בלוח השנה",
|
"event_name_in_calendar": "שם הארוע בלוח השנה",
|
||||||
"event_name_tooltip": "השם שיופיע בלוחות השנה",
|
"event_name_tooltip": "השם שיופיע בלוחות השנה",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "פנה למחלקת המכירות",
|
"contact_sales": "פנה למחלקת המכירות",
|
||||||
"error_404": "שגיאת 404",
|
"error_404": "שגיאת 404",
|
||||||
"default": "ברירת מחדל",
|
"default": "ברירת מחדל",
|
||||||
"set_to_default": "הגדר לברירת המחדל",
|
"set_to_default": "הגדרה כברירת מחדל",
|
||||||
"new_schedule_btn": "תזמון חדש",
|
"new_schedule_btn": "תזמון חדש",
|
||||||
"add_new_schedule": "הוסף מועד חדש",
|
"add_new_schedule": "הוסף מועד חדש",
|
||||||
"add_new_calendar": "הוספת לוח שנה חדש",
|
"add_new_calendar": "הוספת לוח שנה חדש",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "כשההגדרה הזו מופעלת, חברי הצוות שלך לא יוכלו לראות חברי צוות אחרים.",
|
"make_team_private_description": "כשההגדרה הזו מופעלת, חברי הצוות שלך לא יוכלו לראות חברי צוות אחרים.",
|
||||||
"you_cannot_see_team_members": "אין לך אפשרות לראות את כל חברי הצוות של צוות פרטי.",
|
"you_cannot_see_team_members": "אין לך אפשרות לראות את כל חברי הצוות של צוות פרטי.",
|
||||||
"you_cannot_see_teams_of_org": "אי אפשר לצפות בצוותים של ארגון פרטי.",
|
"you_cannot_see_teams_of_org": "אי אפשר לצפות בצוותים של ארגון פרטי.",
|
||||||
"allow_booker_to_select_duration": "אפשר למזמין לבחור את משך הזמן",
|
"allow_multiple_durations": "אפשר משכי זמן מרובים",
|
||||||
"impersonate_user_tip": "כל השימושים בתכונה זו נבדקים.",
|
"impersonate_user_tip": "כל השימושים בתכונה זו נבדקים.",
|
||||||
"impersonating_user_warning": "בהתחזות לשם המשתמש \"{{user}}\".",
|
"impersonating_user_warning": "בהתחזות לשם המשתמש \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "לחץ/י כאן כדי להפסיק",
|
"impersonating_stop_instructions": "לחץ/י כאן כדי להפסיק",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "אישור ההזמנה נכשל",
|
"booking_confirmation_failed": "אישור ההזמנה נכשל",
|
||||||
"not_enough_seats": "אין מספיק מושבים",
|
"not_enough_seats": "אין מספיק מושבים",
|
||||||
"form_builder_field_already_exists": "שדה עם שם זה כבר קיים",
|
"form_builder_field_already_exists": "שדה עם שם זה כבר קיים",
|
||||||
|
"guests_field_must_be_multiemail": "שדה האורחים חייב להיות מסוג 'מספר אימיילים'",
|
||||||
"show_on_booking_page": "להציג בדף ההזמנות",
|
"show_on_booking_page": "להציג בדף ההזמנות",
|
||||||
"visit_cancelled_booking": "ניתן לבקר בדף ההזמנה שבוטלה",
|
"visit_cancelled_booking": "ניתן לבקר בדף ההזמנה שבוטלה",
|
||||||
"get_started_zapier_templates": "התחל עם תבניות Zapier",
|
"get_started_zapier_templates": "התחל עם תבניות Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "ננעל על ידי מנהל/ת הצוות",
|
"locked_by_team_admin": "ננעל על ידי מנהל/ת הצוות",
|
||||||
"app_not_connected": "לא קישרת חשבון {{appName}}.",
|
"app_not_connected": "לא קישרת חשבון {{appName}}.",
|
||||||
"connect_now": "להתחבר עכשיו",
|
"connect_now": "להתחבר עכשיו",
|
||||||
|
"connect_now_unavailable_tooltip": "התחברות מיידית אינה זמינה כרגע. אנא הזמן מהמשבצות למעלה או נסה שוב מאוחר יותר.",
|
||||||
"managed_event_dialog_confirm_button_one": "החלפה ועדכון של חבר/ה {{count}}",
|
"managed_event_dialog_confirm_button_one": "החלפה ועדכון של חבר/ה {{count}}",
|
||||||
"managed_event_dialog_confirm_button_other": "החלפה ועדכון של {{count}} חברים",
|
"managed_event_dialog_confirm_button_other": "החלפה ועדכון של {{count}} חברים",
|
||||||
"managed_event_dialog_title_one": "כתובת ה-URL /{{slug}} כבר קיימת עבור חבר/ה {{count}}. האם ברצונך להחליף אותה?",
|
"managed_event_dialog_title_one": "כתובת ה-URL /{{slug}} כבר קיימת עבור חבר/ה {{count}}. האם ברצונך להחליף אותה?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "בהמשך התהליך, הנך מסכים/ה ל",
|
"proceeding_agreement": "בהמשך התהליך, הנך מסכים/ה ל",
|
||||||
"terms": "תנאי השימוש",
|
"terms": "תנאי השימוש",
|
||||||
"always_show_x_days": "תמיד {{x}} ימים זמינים",
|
"always_show_x_days": "תמיד {{x}} ימים זמינים",
|
||||||
|
"always_show_x_days_description": "מזיז אוטומטית את חלון ההזמנות של {{x}} ימים קדימה בכל יום, ומבטיח שהלוח שלך תמיד מציג את {{x}} הימים הבאים של משבצות זמינות.",
|
||||||
"team_subscription_cancelled_successfully": "מנוי הצוות בוטל בהצלחה",
|
"team_subscription_cancelled_successfully": "מנוי הצוות בוטל בהצלחה",
|
||||||
"team_subscription_cancellation_error": "שגיאה בעת ביטול מנוי הצוות",
|
"team_subscription_cancellation_error": "שגיאה בעת ביטול מנוי הצוות",
|
||||||
"unable_to_subscribe_to_the_platform": "אירעה שגיאה בעת ניסיון להירשם לתוכנית הפלטפורמה, אנא נסה שוב מאוחר יותר",
|
"unable_to_subscribe_to_the_platform": "אירעה שגיאה בעת ניסיון להירשם לתוכנית הפלטפורמה, אנא נסה שוב מאוחר יותר",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "מסתיים ב",
|
"filter_operator_ends_with": "מסתיים ב",
|
||||||
"filter_operator_is_empty": "ריק",
|
"filter_operator_is_empty": "ריק",
|
||||||
"filter_operator_not_empty": "לא ריק",
|
"filter_operator_not_empty": "לא ריק",
|
||||||
"translate_description_button": "תרגם כותרת/תיאור לשפת הדפדפן של המבקר באמצעות בינה מלאכותית",
|
"translate_description_button": "תרגום אוטומטי של כותרת ותיאור",
|
||||||
|
"translate_description_button_description": "תרגם אוטומטית כותרות ותיאורים לשפת הדפדפן של המבקר באמצעות בינה מלאכותית.",
|
||||||
"rr_distribution_method": "שיטת חלוקה",
|
"rr_distribution_method": "שיטת חלוקה",
|
||||||
"rr_distribution_method_description": "מאפשר אופטימיזציה של החלוקה למקסימום זמינות או לחלוקה מאוזנת יותר",
|
"rr_distribution_method_description": "מאפשר אופטימיזציה של החלוקה למקסימום זמינות או לחלוקה מאוזנת יותר",
|
||||||
"rr_distribution_method_availability_title": "מקסום זמינות",
|
"rr_distribution_method_availability_title": "מקסום זמינות",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "תכונת המושבים אינה תומכת באירוע חוזר. השבת את תכונת המושבים כדי להפוך אותו לחוזר.",
|
"seats_doesnt_support_recurring": "תכונת המושבים אינה תומכת באירוע חוזר. השבת את תכונת המושבים כדי להפוך אותו לחוזר.",
|
||||||
"recurring_event_seats_error": "אירוע חוזר אינו תומך בתכונת המושבים. השבת את תכונת המושבים או הפוך את האירוע ללא חוזר.",
|
"recurring_event_seats_error": "אירוע חוזר אינו תומך בתכונת המושבים. השבת את תכונת המושבים או הפוך את האירוע ללא חוזר.",
|
||||||
"segment": "מקטע",
|
"segment": "מקטע",
|
||||||
|
"saved_filters": "מסננים שמורים",
|
||||||
"rename": "שנה שם",
|
"rename": "שנה שם",
|
||||||
"rename_segment": "שנה שם מקטע",
|
"rename_segment": "שנה שם מקטע",
|
||||||
"delete_segment": "מחק מקטע",
|
"delete_segment": "מחק מקטע",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "רשימת בחירה",
|
"picklist": "רשימת בחירה",
|
||||||
"most_cancelled_bookings": "הכי הרבה ביטולים",
|
"most_cancelled_bookings": "הכי הרבה ביטולים",
|
||||||
"interface_language": "שפת ממשק",
|
"interface_language": "שפת ממשק",
|
||||||
|
"interface_language_description": "הגדר את השפה המועדפת עליך לממשק ההזמנות",
|
||||||
"visitors_browser_language": "שפת הדפדפן של המבקר",
|
"visitors_browser_language": "שפת הדפדפן של המבקר",
|
||||||
"name_or_email": "שם או אימייל",
|
"name_or_email": "שם או אימייל",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "אם לא נמצא איש קשר, חזור לבעל הליד אם הוא קיים",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "אם לא נמצא איש קשר, חזור לבעל הליד אם הוא קיים",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "עריכה ושינוי הגדרות תהליכים",
|
"pbac_desc_update_workflows": "עריכה ושינוי הגדרות תהליכים",
|
||||||
"pbac_desc_delete_workflows": "הסרת תהליכים מהמערכת",
|
"pbac_desc_delete_workflows": "הסרת תהליכים מהמערכת",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "זמינות",
|
||||||
|
"pbac_resource_out_of_office": "מחוץ למשרד",
|
||||||
"pbac_desc_create_webhooks": "יצירת רכיבי webhook",
|
"pbac_desc_create_webhooks": "יצירת רכיבי webhook",
|
||||||
"pbac_desc_view_webhooks": "צפייה ברכיבי webhook",
|
"pbac_desc_view_webhooks": "צפייה ברכיבי webhook",
|
||||||
"pbac_desc_update_webhooks": "עדכון webhooks",
|
"pbac_desc_update_webhooks": "עדכון webhooks",
|
||||||
"pbac_desc_delete_webhooks": "מחיקת webhooks",
|
"pbac_desc_delete_webhooks": "מחיקת webhooks",
|
||||||
|
"pbac_desc_create_availability": "יצירת זמינות",
|
||||||
|
"pbac_desc_view_availability": "צפייה בזמינות",
|
||||||
|
"pbac_desc_update_availability": "עדכון זמינות",
|
||||||
|
"pbac_desc_delete_availability": "מחיקת זמינות",
|
||||||
|
"pbac_desc_create_out_of_office": "יצירת מצב מחוץ למשרד",
|
||||||
|
"pbac_desc_view_out_of_office": "צפייה במצב מחוץ למשרד",
|
||||||
|
"pbac_desc_update_out_of_office": "עדכון מצב מחוץ למשרד",
|
||||||
|
"pbac_desc_delete_out_of_office": "מחיקת מצב מחוץ למשרד",
|
||||||
"pbac_desc_manage_workflows": "גישת ניהול מלאה לכל התהליכים",
|
"pbac_desc_manage_workflows": "גישת ניהול מלאה לכל התהליכים",
|
||||||
"pbac_desc_create_event_types": "יצירת סוגי אירועים",
|
"pbac_desc_create_event_types": "יצירת סוגי אירועים",
|
||||||
"pbac_desc_view_event_types": "צפייה בסוגי אירועים",
|
"pbac_desc_view_event_types": "צפייה בסוגי אירועים",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Titkos megbeszélés",
|
"secret_meeting": "Titkos megbeszélés",
|
||||||
"login_instead": "Jelentkezzen be helyette",
|
"login_instead": "Jelentkezzen be helyette",
|
||||||
"create_account": "Fiók létrehozása",
|
"create_account": "Fiók létrehozása",
|
||||||
|
"get_started": "Kezdjük el",
|
||||||
"confirm_password": "Jelszó megerősítése",
|
"confirm_password": "Jelszó megerősítése",
|
||||||
"reset_your_password": "Állítsa be új jelszavát az e-mail címére küldött utasítások alapján.",
|
"reset_your_password": "Állítsa be új jelszavát az e-mail címére küldött utasítások alapján.",
|
||||||
"org_banner_instructions": "Kérjük, töltsön fel {{width}} szélességű és {{height}} magasságú képet.",
|
"org_banner_instructions": "Kérjük, töltsön fel {{width}} szélességű és {{height}} magasságú képet.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Résztvevők",
|
"attendees": "Résztvevők",
|
||||||
"add_attendees": "Résztvevők hozzáadása",
|
"add_attendees": "Résztvevők hozzáadása",
|
||||||
"show_advanced_settings": "Speciális beállítások megjelenítése",
|
"show_advanced_settings": "Speciális beállítások megjelenítése",
|
||||||
|
"hide_advanced_settings": "Speciális beállítások elrejtése",
|
||||||
"event_name": "Esemény neve",
|
"event_name": "Esemény neve",
|
||||||
"event_name_in_calendar": "Eseménynévvel",
|
"event_name_in_calendar": "Eseménynévvel",
|
||||||
"event_name_tooltip": "A naptárban megjelenő név",
|
"event_name_tooltip": "A naptárban megjelenő név",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Lépjen kapcsolatba az értékesítéssel",
|
"contact_sales": "Lépjen kapcsolatba az értékesítéssel",
|
||||||
"error_404": "404-es hiba",
|
"error_404": "404-es hiba",
|
||||||
"default": "Alapértelmezett",
|
"default": "Alapértelmezett",
|
||||||
"set_to_default": "Állítsa alapértelmezettre",
|
"set_to_default": "Beállítás alapértelmezettként",
|
||||||
"new_schedule_btn": "Új ütemezés",
|
"new_schedule_btn": "Új ütemezés",
|
||||||
"add_new_schedule": "Új ütemezés hozzáadása",
|
"add_new_schedule": "Új ütemezés hozzáadása",
|
||||||
"add_new_calendar": "Új naptár hozzáadása",
|
"add_new_calendar": "Új naptár hozzáadása",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Ha ez be van kapcsolva, a csapattagok nem láthatják a többi csapattagot.",
|
"make_team_private_description": "Ha ez be van kapcsolva, a csapattagok nem láthatják a többi csapattagot.",
|
||||||
"you_cannot_see_team_members": "Nem láthatja a privát csapat összes csapattagját.",
|
"you_cannot_see_team_members": "Nem láthatja a privát csapat összes csapattagját.",
|
||||||
"you_cannot_see_teams_of_org": "Nem láthatja a magánszervezetek csapatait.",
|
"you_cannot_see_teams_of_org": "Nem láthatja a magánszervezetek csapatait.",
|
||||||
"allow_booker_to_select_duration": "Engedélyezze a foglalónak az időtartam kiválasztását",
|
"allow_multiple_durations": "Több időtartam engedélyezése",
|
||||||
"impersonate_user_tip": "Ennek a funkciónak minden használata auditált.",
|
"impersonate_user_tip": "Ennek a funkciónak minden használata auditált.",
|
||||||
"impersonating_user_warning": "\"{{user}}\" felhasználónév megszemélyesítése.",
|
"impersonating_user_warning": "\"{{user}}\" felhasználónév megszemélyesítése.",
|
||||||
"impersonating_stop_instructions": "Kattintson ide a leállításhoz",
|
"impersonating_stop_instructions": "Kattintson ide a leállításhoz",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "A foglalás megerősítése nem sikerült",
|
"booking_confirmation_failed": "A foglalás megerősítése nem sikerült",
|
||||||
"not_enough_seats": "Nincs elég hely",
|
"not_enough_seats": "Nincs elég hely",
|
||||||
"form_builder_field_already_exists": "Már létezik ilyen nevű mező",
|
"form_builder_field_already_exists": "Már létezik ilyen nevű mező",
|
||||||
|
"guests_field_must_be_multiemail": "A vendégek mezőnek 'Több e-mail cím' típusúnak kell lennie",
|
||||||
"show_on_booking_page": "Megjelenítés a foglalási oldalon",
|
"show_on_booking_page": "Megjelenítés a foglalási oldalon",
|
||||||
"visit_cancelled_booking": "Megtekintheti a lemondott foglalás oldalát",
|
"visit_cancelled_booking": "Megtekintheti a lemondott foglalás oldalát",
|
||||||
"get_started_zapier_templates": "Kezdje el a Zapier-sablonokat",
|
"get_started_zapier_templates": "Kezdje el a Zapier-sablonokat",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Csapatadminisztrátor zárolta",
|
"locked_by_team_admin": "Csapatadminisztrátor zárolta",
|
||||||
"app_not_connected": "Nem csatlakoztatott {{appName}}-fiókot.",
|
"app_not_connected": "Nem csatlakoztatott {{appName}}-fiókot.",
|
||||||
"connect_now": "Csatlakozzon most",
|
"connect_now": "Csatlakozzon most",
|
||||||
|
"connect_now_unavailable_tooltip": "A Csatlakozás most funkció jelenleg nem elérhető. Kérjük, foglaljon időpontot a fenti időpontokból, vagy próbálja újra később.",
|
||||||
"managed_event_dialog_confirm_button_one": "{{count}} tag cseréje és értesítése",
|
"managed_event_dialog_confirm_button_one": "{{count}} tag cseréje és értesítése",
|
||||||
"managed_event_dialog_confirm_button_other": "{{count}} tagok cseréje és értesítése",
|
"managed_event_dialog_confirm_button_other": "{{count}} tagok cseréje és értesítése",
|
||||||
"managed_event_dialog_title_one": "A /{{slug}} URL már létezik {{count}} tagok számára. Ki akarod cserélni?",
|
"managed_event_dialog_title_one": "A /{{slug}} URL már létezik {{count}} tagok számára. Ki akarod cserélni?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "A folytatással elfogadod a",
|
"proceeding_agreement": "A folytatással elfogadod a",
|
||||||
"terms": "Feltételeinket",
|
"terms": "Feltételeinket",
|
||||||
"always_show_x_days": "Mindig {{x}} nap áll rendelkezésre",
|
"always_show_x_days": "Mindig {{x}} nap áll rendelkezésre",
|
||||||
|
"always_show_x_days_description": "Automatikusan előrelépteti a {{x}} napos foglalási ablakot minden nap, így a naptárad mindig a következő {{x}} nap elérhető időpontjait mutatja.",
|
||||||
"team_subscription_cancelled_successfully": "A csapat előfizetése sikeresen lemondva",
|
"team_subscription_cancelled_successfully": "A csapat előfizetése sikeresen lemondva",
|
||||||
"team_subscription_cancellation_error": "Hiba történt a csapat előfizetésének lemondása közben",
|
"team_subscription_cancellation_error": "Hiba történt a csapat előfizetésének lemondása közben",
|
||||||
"unable_to_subscribe_to_the_platform": "Hiba történt a platformcsomagra való előfizetés során. Kérjük, próbálja újra később",
|
"unable_to_subscribe_to_the_platform": "Hiba történt a platformcsomagra való előfizetés során. Kérjük, próbálja újra később",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Ezzel végződik",
|
"filter_operator_ends_with": "Ezzel végződik",
|
||||||
"filter_operator_is_empty": "Üres",
|
"filter_operator_is_empty": "Üres",
|
||||||
"filter_operator_not_empty": "Nem üres",
|
"filter_operator_not_empty": "Nem üres",
|
||||||
"translate_description_button": "A cím/leírás automatikus fordítása a látogató böngészőjének nyelvére MI segítségével",
|
"translate_description_button": "Cím és leírás automatikus fordítása",
|
||||||
|
"translate_description_button_description": "Automatikusan lefordítja a címeket és leírásokat a látogató böngészőjének nyelvére mesterséges intelligencia segítségével.",
|
||||||
"rr_distribution_method": "Elosztás",
|
"rr_distribution_method": "Elosztás",
|
||||||
"rr_distribution_method_description": "Lehetővé teszi az elosztás optimalizálását a maximális rendelkezésre állás érdekében, vagy a kiegyensúlyozottabb feladatkiosztás eléréséhez.",
|
"rr_distribution_method_description": "Lehetővé teszi az elosztás optimalizálását a maximális rendelkezésre állás érdekében, vagy a kiegyensúlyozottabb feladatkiosztás eléréséhez.",
|
||||||
"rr_distribution_method_availability_title": "Rendelkezésre állás maximalizálása",
|
"rr_distribution_method_availability_title": "Rendelkezésre állás maximalizálása",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "A helyek funkció nem támogatja az ismétlődő eseményeket. Kapcsolja ki a helyek funkciót az ismétlődés engedélyezéséhez.",
|
"seats_doesnt_support_recurring": "A helyek funkció nem támogatja az ismétlődő eseményeket. Kapcsolja ki a helyek funkciót az ismétlődés engedélyezéséhez.",
|
||||||
"recurring_event_seats_error": "Az ismétlődő esemény nem támogatja a helyek funkciót. Kapcsolja ki a helyek funkciót vagy állítsa be nem ismétlődőre az eseményt.",
|
"recurring_event_seats_error": "Az ismétlődő esemény nem támogatja a helyek funkciót. Kapcsolja ki a helyek funkciót vagy állítsa be nem ismétlődőre az eseményt.",
|
||||||
"segment": "Szegmens",
|
"segment": "Szegmens",
|
||||||
|
"saved_filters": "Mentett szűrők",
|
||||||
"rename": "Átnevezés",
|
"rename": "Átnevezés",
|
||||||
"rename_segment": "Szegmens átnevezése",
|
"rename_segment": "Szegmens átnevezése",
|
||||||
"delete_segment": "Szegmens törlése",
|
"delete_segment": "Szegmens törlése",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Választólista",
|
"picklist": "Választólista",
|
||||||
"most_cancelled_bookings": "Legtöbb lemondott foglalás",
|
"most_cancelled_bookings": "Legtöbb lemondott foglalás",
|
||||||
"interface_language": "Felhasználói felület nyelve",
|
"interface_language": "Felhasználói felület nyelve",
|
||||||
|
"interface_language_description": "Állítsd be a foglalási felület preferált nyelvét",
|
||||||
"visitors_browser_language": "Látogató böngészőjének nyelve",
|
"visitors_browser_language": "Látogató böngészőjének nyelve",
|
||||||
"name_or_email": "Név vagy e-mail",
|
"name_or_email": "Név vagy e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ha nem található kapcsolat, visszalépés a lead tulajdonoshoz, ha létezik",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ha nem található kapcsolat, visszalépés a lead tulajdonoshoz, ha létezik",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Munkafolyamat-beállítások szerkesztése és módosítása",
|
"pbac_desc_update_workflows": "Munkafolyamat-beállítások szerkesztése és módosítása",
|
||||||
"pbac_desc_delete_workflows": "Munkafolyamatok eltávolítása a rendszerből",
|
"pbac_desc_delete_workflows": "Munkafolyamatok eltávolítása a rendszerből",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Elérhetőség",
|
||||||
|
"pbac_resource_out_of_office": "Irodán kívül",
|
||||||
"pbac_desc_create_webhooks": "Webhookok létrehozása",
|
"pbac_desc_create_webhooks": "Webhookok létrehozása",
|
||||||
"pbac_desc_view_webhooks": "Webhookok megtekintése",
|
"pbac_desc_view_webhooks": "Webhookok megtekintése",
|
||||||
"pbac_desc_update_webhooks": "Webhookok frissítése",
|
"pbac_desc_update_webhooks": "Webhookok frissítése",
|
||||||
"pbac_desc_delete_webhooks": "Webhookok törlése",
|
"pbac_desc_delete_webhooks": "Webhookok törlése",
|
||||||
|
"pbac_desc_create_availability": "Elérhetőség létrehozása",
|
||||||
|
"pbac_desc_view_availability": "Elérhetőség megtekintése",
|
||||||
|
"pbac_desc_update_availability": "Elérhetőség frissítése",
|
||||||
|
"pbac_desc_delete_availability": "Elérhetőség törlése",
|
||||||
|
"pbac_desc_create_out_of_office": "Irodán kívüli állapot létrehozása",
|
||||||
|
"pbac_desc_view_out_of_office": "Irodán kívüli állapot megtekintése",
|
||||||
|
"pbac_desc_update_out_of_office": "Irodán kívüli állapot frissítése",
|
||||||
|
"pbac_desc_delete_out_of_office": "Irodán kívüli állapot törlése",
|
||||||
"pbac_desc_manage_workflows": "Teljes kezelési hozzáférés minden munkafolyamathoz",
|
"pbac_desc_manage_workflows": "Teljes kezelési hozzáférés minden munkafolyamathoz",
|
||||||
"pbac_desc_create_event_types": "Eseménytípusok létrehozása",
|
"pbac_desc_create_event_types": "Eseménytípusok létrehozása",
|
||||||
"pbac_desc_view_event_types": "Eseménytípusok megtekintése",
|
"pbac_desc_view_event_types": "Eseménytípusok megtekintése",
|
||||||
@@ -3561,7 +3579,7 @@
|
|||||||
"round_robin_settings_description": "Testreszabhatja az alapértelmezett körforgó beállításokat ehhez a csapathoz",
|
"round_robin_settings_description": "Testreszabhatja az alapértelmezett körforgó beállításokat ehhez a csapathoz",
|
||||||
"round_robin_settings_updated_successfully": "A körforgó beállítások sikeresen frissítve",
|
"round_robin_settings_updated_successfully": "A körforgó beállítások sikeresen frissítve",
|
||||||
"booking_creation_time": "Foglalás létrehozásának ideje",
|
"booking_creation_time": "Foglalás létrehozásának ideje",
|
||||||
"meeting_start_time": "Találkozó kezdési ideje",
|
"meeting_start_time": "Megbeszélés kezdési ideje",
|
||||||
"members_affected_by_disabling_delegation_credential": "Érintett tagok",
|
"members_affected_by_disabling_delegation_credential": "Érintett tagok",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Nincsenek tagok, akiket érintene a delegálási hitelesítő adatok letiltása",
|
"no_members_affected_by_disabling_delegation_credential": "Nincsenek tagok, akiket érintene a delegálási hitelesítő adatok letiltása",
|
||||||
"download_expense_log": "Költségnapló letöltése",
|
"download_expense_log": "Költségnapló letöltése",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Riunione Segreta",
|
"secret_meeting": "Riunione Segreta",
|
||||||
"login_instead": "Effettua il login invece",
|
"login_instead": "Effettua il login invece",
|
||||||
"create_account": "Crea Account",
|
"create_account": "Crea Account",
|
||||||
|
"get_started": "Inizia",
|
||||||
"confirm_password": "Conferma password",
|
"confirm_password": "Conferma password",
|
||||||
"reset_your_password": "Imposta la nuova password seguendo le istruzioni che ti sono state inviate via e-mail.",
|
"reset_your_password": "Imposta la nuova password seguendo le istruzioni che ti sono state inviate via e-mail.",
|
||||||
"org_banner_instructions": "Carica un'immagine larga {{width}} e alta {{height}}.",
|
"org_banner_instructions": "Carica un'immagine larga {{width}} e alta {{height}}.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Colore del tipo di evento (Tema chiaro)",
|
"light_event_type_color": "Colore del tipo di evento (Tema chiaro)",
|
||||||
"dark_event_type_color": "Colore del tipo di evento (Tema scuro)",
|
"dark_event_type_color": "Colore del tipo di evento (Tema scuro)",
|
||||||
"file_not_named": "Il file non è chiamato [idOrSlug]/[user]",
|
"file_not_named": "Il file non è chiamato [idOrSlug]/[user]",
|
||||||
"create_team": "Crea Team",
|
"create_team": "Crea team",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"nameless_team": "Team senza nome",
|
"nameless_team": "Team senza nome",
|
||||||
"oauth_clients": "Client OAuth",
|
"oauth_clients": "Client OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Partecipanti",
|
"attendees": "Partecipanti",
|
||||||
"add_attendees": "Aggiungi partecipanti",
|
"add_attendees": "Aggiungi partecipanti",
|
||||||
"show_advanced_settings": "Mostra impostazioni avanzate",
|
"show_advanced_settings": "Mostra impostazioni avanzate",
|
||||||
|
"hide_advanced_settings": "Nascondi impostazioni avanzate",
|
||||||
"event_name": "Nome Dell'Evento",
|
"event_name": "Nome Dell'Evento",
|
||||||
"event_name_in_calendar": "Nome dell'evento nel calendario",
|
"event_name_in_calendar": "Nome dell'evento nel calendario",
|
||||||
"event_name_tooltip": "Il nome che apparirà nei calendari",
|
"event_name_tooltip": "Il nome che apparirà nei calendari",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Contatta reparto commerciale",
|
"contact_sales": "Contatta reparto commerciale",
|
||||||
"error_404": "Errore 404",
|
"error_404": "Errore 404",
|
||||||
"default": "Predefinito",
|
"default": "Predefinito",
|
||||||
"set_to_default": "Impostazione predefinita",
|
"set_to_default": "Imposta come predefinito",
|
||||||
"new_schedule_btn": "Nuovo programma",
|
"new_schedule_btn": "Nuovo programma",
|
||||||
"add_new_schedule": "Aggiungi un nuovo programma",
|
"add_new_schedule": "Aggiungi un nuovo programma",
|
||||||
"add_new_calendar": "Aggiungi un nuovo calendario",
|
"add_new_calendar": "Aggiungi un nuovo calendario",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Quando questa opzione è attivata, i membri del tuo team non potranno vedere gli altri membri del team.",
|
"make_team_private_description": "Quando questa opzione è attivata, i membri del tuo team non potranno vedere gli altri membri del team.",
|
||||||
"you_cannot_see_team_members": "Non è possibile vedere tutti i membri di un team privato.",
|
"you_cannot_see_team_members": "Non è possibile vedere tutti i membri di un team privato.",
|
||||||
"you_cannot_see_teams_of_org": "Non puoi vedere i team di un'organizzazione privata.",
|
"you_cannot_see_teams_of_org": "Non puoi vedere i team di un'organizzazione privata.",
|
||||||
"allow_booker_to_select_duration": "Consenti alla persona che prenota di selezionare la durata",
|
"allow_multiple_durations": "Consenti durate multiple",
|
||||||
"impersonate_user_tip": "Ogni utilizzo di questa funzionalità viene controllato.",
|
"impersonate_user_tip": "Ogni utilizzo di questa funzionalità viene controllato.",
|
||||||
"impersonating_user_warning": "Accesso con l'identità dell'utente \"{{user}}\".",
|
"impersonating_user_warning": "Accesso con l'identità dell'utente \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Fai clic qui per interrompere",
|
"impersonating_stop_instructions": "Fai clic qui per interrompere",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Conferma prenotazione non riuscita",
|
"booking_confirmation_failed": "Conferma prenotazione non riuscita",
|
||||||
"not_enough_seats": "Posti insufficienti",
|
"not_enough_seats": "Posti insufficienti",
|
||||||
"form_builder_field_already_exists": "Esiste già un campo con questo nome",
|
"form_builder_field_already_exists": "Esiste già un campo con questo nome",
|
||||||
|
"guests_field_must_be_multiemail": "Il campo ospiti deve essere di tipo 'Email multiple'",
|
||||||
"show_on_booking_page": "Mostra nella pagina di prenotazione",
|
"show_on_booking_page": "Mostra nella pagina di prenotazione",
|
||||||
"visit_cancelled_booking": "Puoi visitare la pagina della prenotazione annullata",
|
"visit_cancelled_booking": "Puoi visitare la pagina della prenotazione annullata",
|
||||||
"get_started_zapier_templates": "Inizia con i modelli Zapier",
|
"get_started_zapier_templates": "Inizia con i modelli Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bloccato dall'amministratore del team",
|
"locked_by_team_admin": "Bloccato dall'amministratore del team",
|
||||||
"app_not_connected": "Non è stato connesso un account di {{appName}}.",
|
"app_not_connected": "Non è stato connesso un account di {{appName}}.",
|
||||||
"connect_now": "Connetti ora",
|
"connect_now": "Connetti ora",
|
||||||
|
"connect_now_unavailable_tooltip": "Connetti ora è attualmente non disponibile. Prenota dagli slot sopra o riprova più tardi.",
|
||||||
"managed_event_dialog_confirm_button_one": "Sostituisci e invia notifica a {{count}} membro",
|
"managed_event_dialog_confirm_button_one": "Sostituisci e invia notifica a {{count}} membro",
|
||||||
"managed_event_dialog_confirm_button_other": "Sostituisci e invia notifica a {{count}} membri",
|
"managed_event_dialog_confirm_button_other": "Sostituisci e invia notifica a {{count}} membri",
|
||||||
"managed_event_dialog_title_one": "L'URL /{{slug}} esiste già per {{count}} membro. Vuoi sostituirlo?",
|
"managed_event_dialog_title_one": "L'URL /{{slug}} esiste già per {{count}} membro. Vuoi sostituirlo?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Procedendo, accetti i nostri",
|
"proceeding_agreement": "Procedendo, accetti i nostri",
|
||||||
"terms": "Termini",
|
"terms": "Termini",
|
||||||
"always_show_x_days": "Sempre {{x}} giorni disponibili",
|
"always_show_x_days": "Sempre {{x}} giorni disponibili",
|
||||||
|
"always_show_x_days_description": "Sposta automaticamente avanti ogni giorno la finestra di prenotazione di {{x}} giorni, assicurando che il tuo calendario mostri sempre i prossimi {{x}} giorni di slot disponibili.",
|
||||||
"team_subscription_cancelled_successfully": "Abbonamento del team annullato con successo",
|
"team_subscription_cancelled_successfully": "Abbonamento del team annullato con successo",
|
||||||
"team_subscription_cancellation_error": "Errore durante l'annullamento dell'abbonamento del team",
|
"team_subscription_cancellation_error": "Errore durante l'annullamento dell'abbonamento del team",
|
||||||
"unable_to_subscribe_to_the_platform": "Si è verificato un errore durante il tentativo di abbonamento al piano Platform, riprova più tardi",
|
"unable_to_subscribe_to_the_platform": "Si è verificato un errore durante il tentativo di abbonamento al piano Platform, riprova più tardi",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Finisce con",
|
"filter_operator_ends_with": "Finisce con",
|
||||||
"filter_operator_is_empty": "È vuoto",
|
"filter_operator_is_empty": "È vuoto",
|
||||||
"filter_operator_not_empty": "Non è vuoto",
|
"filter_operator_not_empty": "Non è vuoto",
|
||||||
"translate_description_button": "Traduci titolo/descrizione nella lingua del browser del visitatore usando l'IA",
|
"translate_description_button": "Auto traduci titolo e descrizione",
|
||||||
|
"translate_description_button_description": "Traduci automaticamente titoli e descrizioni nella lingua del browser del visitatore utilizzando l'IA.",
|
||||||
"rr_distribution_method": "Distribuzione",
|
"rr_distribution_method": "Distribuzione",
|
||||||
"rr_distribution_method_description": "Permette di ottimizzare la distribuzione per massimizzare la disponibilità o per ottenere un'assegnazione più bilanciata.",
|
"rr_distribution_method_description": "Permette di ottimizzare la distribuzione per massimizzare la disponibilità o per ottenere un'assegnazione più bilanciata.",
|
||||||
"rr_distribution_method_availability_title": "Massimizza disponibilità",
|
"rr_distribution_method_availability_title": "Massimizza disponibilità",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "La funzionalità dei posti non supporta eventi ricorrenti. Disabilita la funzionalità dei posti per renderlo ricorrente.",
|
"seats_doesnt_support_recurring": "La funzionalità dei posti non supporta eventi ricorrenti. Disabilita la funzionalità dei posti per renderlo ricorrente.",
|
||||||
"recurring_event_seats_error": "L'evento ricorrente non supporta la funzionalità dei posti. Disabilita la funzionalità dei posti o rendi l'evento non ricorrente.",
|
"recurring_event_seats_error": "L'evento ricorrente non supporta la funzionalità dei posti. Disabilita la funzionalità dei posti o rendi l'evento non ricorrente.",
|
||||||
"segment": "Segmento",
|
"segment": "Segmento",
|
||||||
|
"saved_filters": "Filtri salvati",
|
||||||
"rename": "Rinomina",
|
"rename": "Rinomina",
|
||||||
"rename_segment": "Rinomina segmento",
|
"rename_segment": "Rinomina segmento",
|
||||||
"delete_segment": "Elimina segmento",
|
"delete_segment": "Elimina segmento",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista di selezione",
|
"picklist": "Lista di selezione",
|
||||||
"most_cancelled_bookings": "Prenotazioni più cancellate",
|
"most_cancelled_bookings": "Prenotazioni più cancellate",
|
||||||
"interface_language": "Lingua dell'interfaccia",
|
"interface_language": "Lingua dell'interfaccia",
|
||||||
|
"interface_language_description": "Imposta la tua lingua preferita per l'interfaccia di prenotazione",
|
||||||
"visitors_browser_language": "Lingua del browser del visitatore",
|
"visitors_browser_language": "Lingua del browser del visitatore",
|
||||||
"name_or_email": "Nome o email",
|
"name_or_email": "Nome o email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se non viene trovato alcun contatto, passa al proprietario del lead se esiste",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se non viene trovato alcun contatto, passa al proprietario del lead se esiste",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Modifica e cambia le impostazioni dei flussi di lavoro",
|
"pbac_desc_update_workflows": "Modifica e cambia le impostazioni dei flussi di lavoro",
|
||||||
"pbac_desc_delete_workflows": "Rimuovi flussi di lavoro dal sistema",
|
"pbac_desc_delete_workflows": "Rimuovi flussi di lavoro dal sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilità",
|
||||||
|
"pbac_resource_out_of_office": "Fuori sede",
|
||||||
"pbac_desc_create_webhooks": "Crea webhook",
|
"pbac_desc_create_webhooks": "Crea webhook",
|
||||||
"pbac_desc_view_webhooks": "Visualizza webhook",
|
"pbac_desc_view_webhooks": "Visualizza webhook",
|
||||||
"pbac_desc_update_webhooks": "Aggiorna webhook",
|
"pbac_desc_update_webhooks": "Aggiorna webhook",
|
||||||
"pbac_desc_delete_webhooks": "Elimina webhook",
|
"pbac_desc_delete_webhooks": "Elimina webhook",
|
||||||
|
"pbac_desc_create_availability": "Crea disponibilità",
|
||||||
|
"pbac_desc_view_availability": "Visualizza disponibilità",
|
||||||
|
"pbac_desc_update_availability": "Aggiorna disponibilità",
|
||||||
|
"pbac_desc_delete_availability": "Elimina disponibilità",
|
||||||
|
"pbac_desc_create_out_of_office": "Crea fuori sede",
|
||||||
|
"pbac_desc_view_out_of_office": "Visualizza fuori sede",
|
||||||
|
"pbac_desc_update_out_of_office": "Aggiorna fuori sede",
|
||||||
|
"pbac_desc_delete_out_of_office": "Elimina fuori sede",
|
||||||
"pbac_desc_manage_workflows": "Accesso completo alla gestione di tutti i flussi di lavoro",
|
"pbac_desc_manage_workflows": "Accesso completo alla gestione di tutti i flussi di lavoro",
|
||||||
"pbac_desc_create_event_types": "Crea tipi di evento",
|
"pbac_desc_create_event_types": "Crea tipi di evento",
|
||||||
"pbac_desc_view_event_types": "Visualizza tipi di evento",
|
"pbac_desc_view_event_types": "Visualizza tipi di evento",
|
||||||
@@ -3560,8 +3578,8 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Il bilanciamento del carico non è disponibile con i gruppi Round Robin",
|
"rr_load_balancing_disabled_with_groups": "Il bilanciamento del carico non è disponibile con i gruppi Round Robin",
|
||||||
"round_robin_settings_description": "Personalizza le impostazioni predefinite del round robin per questo team",
|
"round_robin_settings_description": "Personalizza le impostazioni predefinite del round robin per questo team",
|
||||||
"round_robin_settings_updated_successfully": "Impostazioni Round Robin aggiornate con successo",
|
"round_robin_settings_updated_successfully": "Impostazioni Round Robin aggiornate con successo",
|
||||||
"booking_creation_time": "Orario di creazione della prenotazione",
|
"booking_creation_time": "Orario di creazione prenotazione",
|
||||||
"meeting_start_time": "Orario di inizio della riunione",
|
"meeting_start_time": "Orario di inizio meeting",
|
||||||
"members_affected_by_disabling_delegation_credential": "Membri interessati",
|
"members_affected_by_disabling_delegation_credential": "Membri interessati",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Nessun membro interessato dalla disabilitazione delle credenziali di delega",
|
"no_members_affected_by_disabling_delegation_credential": "Nessun membro interessato dalla disabilitazione delle credenziali di delega",
|
||||||
"download_expense_log": "Scarica il registro delle spese",
|
"download_expense_log": "Scarica il registro delle spese",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "秘密のミーティング",
|
"secret_meeting": "秘密のミーティング",
|
||||||
"login_instead": "代わりにログインする",
|
"login_instead": "代わりにログインする",
|
||||||
"create_account": "アカウントを作成",
|
"create_account": "アカウントを作成",
|
||||||
|
"get_started": "始める",
|
||||||
"confirm_password": "パスワードを確認",
|
"confirm_password": "パスワードを確認",
|
||||||
"reset_your_password": "メールアドレスに送信された手順に従って新しいパスワードを設定します。",
|
"reset_your_password": "メールアドレスに送信された手順に従って新しいパスワードを設定します。",
|
||||||
"org_banner_instructions": "{{width}}幅と{{height}}高さの画像をアップロードしてください。",
|
"org_banner_instructions": "{{width}}幅と{{height}}高さの画像をアップロードしてください。",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "出席者",
|
"attendees": "出席者",
|
||||||
"add_attendees": "出席者を追加",
|
"add_attendees": "出席者を追加",
|
||||||
"show_advanced_settings": "詳細設定を表示",
|
"show_advanced_settings": "詳細設定を表示",
|
||||||
|
"hide_advanced_settings": "詳細設定を隠す",
|
||||||
"event_name": "イベント名",
|
"event_name": "イベント名",
|
||||||
"event_name_in_calendar": "カレンダー内のイベント名",
|
"event_name_in_calendar": "カレンダー内のイベント名",
|
||||||
"event_name_tooltip": "カレンダーに表示される名前",
|
"event_name_tooltip": "カレンダーに表示される名前",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "これをオンにすると、チームメンバーは他のチームメンバーを確認することができなくなります。",
|
"make_team_private_description": "これをオンにすると、チームメンバーは他のチームメンバーを確認することができなくなります。",
|
||||||
"you_cannot_see_team_members": "非公開チームでは、チームメンバーを確認することができません。",
|
"you_cannot_see_team_members": "非公開チームでは、チームメンバーを確認することができません。",
|
||||||
"you_cannot_see_teams_of_org": "非公開の組織のチームを見ることはできません。",
|
"you_cannot_see_teams_of_org": "非公開の組織のチームを見ることはできません。",
|
||||||
"allow_booker_to_select_duration": "予約者が時間を選択できるようにする",
|
"allow_multiple_durations": "複数の時間枠を許可",
|
||||||
"impersonate_user_tip": "この機能の使用はすべて監査されています。",
|
"impersonate_user_tip": "この機能の使用はすべて監査されています。",
|
||||||
"impersonating_user_warning": "ユーザー名 \"{{user}}\" になりすましています。",
|
"impersonating_user_warning": "ユーザー名 \"{{user}}\" になりすましています。",
|
||||||
"impersonating_stop_instructions": "こちらをクリックして停止",
|
"impersonating_stop_instructions": "こちらをクリックして停止",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "予約の確認に失敗しました",
|
"booking_confirmation_failed": "予約の確認に失敗しました",
|
||||||
"not_enough_seats": "座席が不足しています",
|
"not_enough_seats": "座席が不足しています",
|
||||||
"form_builder_field_already_exists": "この名前のフィールドは既に存在します",
|
"form_builder_field_already_exists": "この名前のフィールドは既に存在します",
|
||||||
|
"guests_field_must_be_multiemail": "ゲストフィールドは「複数のメール」タイプである必要があります",
|
||||||
"show_on_booking_page": "予約ページに表示",
|
"show_on_booking_page": "予約ページに表示",
|
||||||
"visit_cancelled_booking": "キャンセルされた予約ページを訪問できます",
|
"visit_cancelled_booking": "キャンセルされた予約ページを訪問できます",
|
||||||
"get_started_zapier_templates": "Zapier テンプレートの使用を開始する",
|
"get_started_zapier_templates": "Zapier テンプレートの使用を開始する",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "チームの管理者によりロックされています",
|
"locked_by_team_admin": "チームの管理者によりロックされています",
|
||||||
"app_not_connected": "{{appName}} のアカウントに接続していません。",
|
"app_not_connected": "{{appName}} のアカウントに接続していません。",
|
||||||
"connect_now": "今すぐ接続",
|
"connect_now": "今すぐ接続",
|
||||||
|
"connect_now_unavailable_tooltip": "今すぐ接続は現在利用できません。上記の空き枠から予約するか、後でもう一度お試しください。",
|
||||||
"managed_event_dialog_confirm_button_one": "{{count}} 人のメンバーを置き換えて通知する",
|
"managed_event_dialog_confirm_button_one": "{{count}} 人のメンバーを置き換えて通知する",
|
||||||
"managed_event_dialog_confirm_button_other": "{{count}} 人のメンバーを置き換えて通知する",
|
"managed_event_dialog_confirm_button_other": "{{count}} 人のメンバーを置き換えて通知する",
|
||||||
"managed_event_dialog_title_one": "{{count}} 人のメンバーがすでに URL /{{slug}} を使用しています。置き換えますか?",
|
"managed_event_dialog_title_one": "{{count}} 人のメンバーがすでに URL /{{slug}} を使用しています。置き換えますか?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "続行することで、当社の",
|
"proceeding_agreement": "続行することで、当社の",
|
||||||
"terms": "利用規約",
|
"terms": "利用規約",
|
||||||
"always_show_x_days": "常に{{x}}日間利用可能",
|
"always_show_x_days": "常に{{x}}日間利用可能",
|
||||||
|
"always_show_x_days_description": "予約ウィンドウを毎日自動的に{{x}}日分前進させ、カレンダーに常に今後{{x}}日間の空き枠を表示します。",
|
||||||
"team_subscription_cancelled_successfully": "チームサブスクリプションが正常にキャンセルされました",
|
"team_subscription_cancelled_successfully": "チームサブスクリプションが正常にキャンセルされました",
|
||||||
"team_subscription_cancellation_error": "チームサブスクリプションのキャンセル中にエラーが発生しました",
|
"team_subscription_cancellation_error": "チームサブスクリプションのキャンセル中にエラーが発生しました",
|
||||||
"unable_to_subscribe_to_the_platform": "プラットフォームプランの登録中にエラーが発生しました。後でもう一度お試しください。",
|
"unable_to_subscribe_to_the_platform": "プラットフォームプランの登録中にエラーが発生しました。後でもう一度お試しください。",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "で終わる",
|
"filter_operator_ends_with": "で終わる",
|
||||||
"filter_operator_is_empty": "が空である",
|
"filter_operator_is_empty": "が空である",
|
||||||
"filter_operator_not_empty": "が空ではない",
|
"filter_operator_not_empty": "が空ではない",
|
||||||
"translate_description_button": "AIを使用して訪問者のブラウザ言語にタイトル/説明を翻訳",
|
"translate_description_button": "タイトルと説明を自動翻訳",
|
||||||
|
"translate_description_button_description": "AIを使用して、訪問者のブラウザ言語にタイトルと説明を自動的に翻訳します。",
|
||||||
"rr_distribution_method": "配分方式",
|
"rr_distribution_method": "配分方式",
|
||||||
"rr_distribution_method_description": "最大限の予約可能性を確保するため、または均等な配分を目指すための最適化が可能です。",
|
"rr_distribution_method_description": "最大限の予約可能性を確保するため、または均等な配分を目指すための最適化が可能です。",
|
||||||
"rr_distribution_method_availability_title": "予約可能性を最大化",
|
"rr_distribution_method_availability_title": "予約可能性を最大化",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "座席機能は定期的なイベントをサポートしていません。定期的にするには座席機能を無効にしてください。",
|
"seats_doesnt_support_recurring": "座席機能は定期的なイベントをサポートしていません。定期的にするには座席機能を無効にしてください。",
|
||||||
"recurring_event_seats_error": "定期的なイベントは座席機能をサポートしていません。座席機能を無効にするか、イベントを非定期的にしてください。",
|
"recurring_event_seats_error": "定期的なイベントは座席機能をサポートしていません。座席機能を無効にするか、イベントを非定期的にしてください。",
|
||||||
"segment": "セグメント",
|
"segment": "セグメント",
|
||||||
|
"saved_filters": "保存済みフィルター",
|
||||||
"rename": "名前を変更",
|
"rename": "名前を変更",
|
||||||
"rename_segment": "セグメント名を変更",
|
"rename_segment": "セグメント名を変更",
|
||||||
"delete_segment": "セグメントを削除",
|
"delete_segment": "セグメントを削除",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "選択リスト",
|
"picklist": "選択リスト",
|
||||||
"most_cancelled_bookings": "最もキャンセルが多い",
|
"most_cancelled_bookings": "最もキャンセルが多い",
|
||||||
"interface_language": "インターフェース言語",
|
"interface_language": "インターフェース言語",
|
||||||
|
"interface_language_description": "予約インターフェースの希望言語を設定します",
|
||||||
"visitors_browser_language": "訪問者のブラウザ言語",
|
"visitors_browser_language": "訪問者のブラウザ言語",
|
||||||
"name_or_email": "名前またはメールアドレス",
|
"name_or_email": "名前またはメールアドレス",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "連絡先が見つからない場合、リード所有者が存在すればそちらにフォールバックする",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "連絡先が見つからない場合、リード所有者が存在すればそちらにフォールバックする",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "ワークフロー設定を編集および変更する",
|
"pbac_desc_update_workflows": "ワークフロー設定を編集および変更する",
|
||||||
"pbac_desc_delete_workflows": "システムからワークフローを削除する",
|
"pbac_desc_delete_workflows": "システムからワークフローを削除する",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "空き状況",
|
||||||
|
"pbac_resource_out_of_office": "不在",
|
||||||
"pbac_desc_create_webhooks": "Webhookを作成",
|
"pbac_desc_create_webhooks": "Webhookを作成",
|
||||||
"pbac_desc_view_webhooks": "Webhookを表示",
|
"pbac_desc_view_webhooks": "Webhookを表示",
|
||||||
"pbac_desc_update_webhooks": "ウェブフックを更新する",
|
"pbac_desc_update_webhooks": "ウェブフックを更新する",
|
||||||
"pbac_desc_delete_webhooks": "ウェブフックを削除する",
|
"pbac_desc_delete_webhooks": "ウェブフックを削除する",
|
||||||
|
"pbac_desc_create_availability": "空き状況を作成",
|
||||||
|
"pbac_desc_view_availability": "空き状況を表示",
|
||||||
|
"pbac_desc_update_availability": "空き状況を更新",
|
||||||
|
"pbac_desc_delete_availability": "空き状況を削除",
|
||||||
|
"pbac_desc_create_out_of_office": "不在を作成",
|
||||||
|
"pbac_desc_view_out_of_office": "不在を表示",
|
||||||
|
"pbac_desc_update_out_of_office": "不在を更新",
|
||||||
|
"pbac_desc_delete_out_of_office": "不在を削除",
|
||||||
"pbac_desc_manage_workflows": "すべてのワークフローへの完全な管理アクセス",
|
"pbac_desc_manage_workflows": "すべてのワークフローへの完全な管理アクセス",
|
||||||
"pbac_desc_create_event_types": "イベントタイプを作成する",
|
"pbac_desc_create_event_types": "イベントタイプを作成する",
|
||||||
"pbac_desc_view_event_types": "イベントタイプを表示する",
|
"pbac_desc_view_event_types": "イベントタイプを表示する",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "ការជួបជុំសម្ងាត់",
|
"secret_meeting": "ការជួបជុំសម្ងាត់",
|
||||||
"login_instead": "ចូលប្រើប្រាស់វិញ",
|
"login_instead": "ចូលប្រើប្រាស់វិញ",
|
||||||
"create_account": "បង្កើតគណនី",
|
"create_account": "បង្កើតគណនី",
|
||||||
|
"get_started": "ចាប់ផ្តើម",
|
||||||
"confirm_password": "បញ្ជាក់ពាក្យសម្ងាត់",
|
"confirm_password": "បញ្ជាក់ពាក្យសម្ងាត់",
|
||||||
"reset_your_password": "កំណត់ពាក្យសម្ងាត់ថ្មីរបស់អ្នកដោយអនុវត្តតាមសេចក្តីណែនាំដែលបានផ្ញើទៅអាសយដ្ឋានអ៊ីមែលរបស់អ្នក។",
|
"reset_your_password": "កំណត់ពាក្យសម្ងាត់ថ្មីរបស់អ្នកដោយអនុវត្តតាមសេចក្តីណែនាំដែលបានផ្ញើទៅអាសយដ្ឋានអ៊ីមែលរបស់អ្នក។",
|
||||||
"org_banner_instructions": "សូមផ្ទុករូបភាពដែលមានទទឹង {{width}} និងកម្ពស់ {{height}}។",
|
"org_banner_instructions": "សូមផ្ទុករូបភាពដែលមានទទឹង {{width}} និងកម្ពស់ {{height}}។",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "អ្នកចូលរួម",
|
"attendees": "អ្នកចូលរួម",
|
||||||
"add_attendees": "បន្ថែមអ្នកចូលរួម",
|
"add_attendees": "បន្ថែមអ្នកចូលរួម",
|
||||||
"show_advanced_settings": "បង្ហាញការកំណត់កម្រិតខ្ពស់",
|
"show_advanced_settings": "បង្ហាញការកំណត់កម្រិតខ្ពស់",
|
||||||
|
"hide_advanced_settings": "លាក់ការកំណត់កម្រិតខ្ពស់",
|
||||||
"event_name": "ឈ្មោះព្រឹត្តិការណ៍",
|
"event_name": "ឈ្មោះព្រឹត្តិការណ៍",
|
||||||
"event_name_in_calendar": "ជាមួយឈ្មោះព្រឹត្តិការណ៍",
|
"event_name_in_calendar": "ជាមួយឈ្មោះព្រឹត្តិការណ៍",
|
||||||
"event_name_tooltip": "ឈ្មោះដែលនឹងបង្ហាញនៅក្នុងប្រតិទិន",
|
"event_name_tooltip": "ឈ្មោះដែលនឹងបង្ហាញនៅក្នុងប្រតិទិន",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "ទាក់ទងផ្នែកលក់",
|
"contact_sales": "ទាក់ទងផ្នែកលក់",
|
||||||
"error_404": "កំហុស 404",
|
"error_404": "កំហុស 404",
|
||||||
"default": "លំនាំដើម",
|
"default": "លំនាំដើម",
|
||||||
"set_to_default": "កំណត់ទៅលំនាំដើម",
|
"set_to_default": "កំណត់ជាលំនាំដើម",
|
||||||
"new_schedule_btn": "កាលវិភាគថ្មី",
|
"new_schedule_btn": "កាលវិភាគថ្មី",
|
||||||
"add_new_schedule": "បន្ថែមកាលវិភាគថ្មី",
|
"add_new_schedule": "បន្ថែមកាលវិភាគថ្មី",
|
||||||
"add_new_calendar": "បន្ថែមប្រតិទិនថ្មី",
|
"add_new_calendar": "បន្ថែមប្រតិទិនថ្មី",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "សមាជិកក្រុមរបស់អ្នកនឹងមិនអាចមើលឃើញសមាជិកក្រុមផ្សេងទៀតនៅពេលដែលបើកមុខងារនេះ។",
|
"make_team_private_description": "សមាជិកក្រុមរបស់អ្នកនឹងមិនអាចមើលឃើញសមាជិកក្រុមផ្សេងទៀតនៅពេលដែលបើកមុខងារនេះ។",
|
||||||
"you_cannot_see_team_members": "អ្នកមិនអាចមើលឃើញសមាជិកទាំងអស់នៃក្រុមសម្ងាត់បានទេ។",
|
"you_cannot_see_team_members": "អ្នកមិនអាចមើលឃើញសមាជិកទាំងអស់នៃក្រុមសម្ងាត់បានទេ។",
|
||||||
"you_cannot_see_teams_of_org": "អ្នកមិនអាចមើលឃើញក្រុមនៃអង្គការសម្ងាត់បានទេ។",
|
"you_cannot_see_teams_of_org": "អ្នកមិនអាចមើលឃើញក្រុមនៃអង្គការសម្ងាត់បានទេ។",
|
||||||
"allow_booker_to_select_duration": "អនុញ្ញាតឱ្យអ្នកកក់ជ្រើសរើសរយៈពេល",
|
"allow_multiple_durations": "អនុញ្ញាតឱ្យមានរយៈពេលច្រើន",
|
||||||
"impersonate_user_tip": "ការប្រើប្រាស់មុខងារនេះទាំងអស់ត្រូវបានកត់ត្រា។",
|
"impersonate_user_tip": "ការប្រើប្រាស់មុខងារនេះទាំងអស់ត្រូវបានកត់ត្រា។",
|
||||||
"impersonating_user_warning": "កំពុងសម្តែងជាអ្នកប្រើប្រាស់ \"{{user}}\"។",
|
"impersonating_user_warning": "កំពុងសម្តែងជាអ្នកប្រើប្រាស់ \"{{user}}\"។",
|
||||||
"impersonating_stop_instructions": "ចុចទីនេះដើម្បីបញ្ឈប់",
|
"impersonating_stop_instructions": "ចុចទីនេះដើម្បីបញ្ឈប់",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "ការបញ្ជាក់ការកក់បរាជ័យ",
|
"booking_confirmation_failed": "ការបញ្ជាក់ការកក់បរាជ័យ",
|
||||||
"not_enough_seats": "កៅអីមិនគ្រប់គ្រាន់",
|
"not_enough_seats": "កៅអីមិនគ្រប់គ្រាន់",
|
||||||
"form_builder_field_already_exists": "វាលដែលមានឈ្មោះនេះមានរួចហើយ",
|
"form_builder_field_already_exists": "វាលដែលមានឈ្មោះនេះមានរួចហើយ",
|
||||||
|
"guests_field_must_be_multiemail": "វាលភ្ញៀវត្រូវតែជាប្រភេទ 'អ៊ីមែលច្រើន'",
|
||||||
"show_on_booking_page": "បង្ហាញនៅលើទំព័រការកក់",
|
"show_on_booking_page": "បង្ហាញនៅលើទំព័រការកក់",
|
||||||
"visit_cancelled_booking": "អ្នកអាចចូលទៅកាន់ទំព័រការកក់ដែលបានលុបចោល",
|
"visit_cancelled_booking": "អ្នកអាចចូលទៅកាន់ទំព័រការកក់ដែលបានលុបចោល",
|
||||||
"get_started_zapier_templates": "ចាប់ផ្តើមជាមួយពុម្ព Zapier",
|
"get_started_zapier_templates": "ចាប់ផ្តើមជាមួយពុម្ព Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "បានចាក់សោដោយអ្នកគ្រប់គ្រងក្រុម",
|
"locked_by_team_admin": "បានចាក់សោដោយអ្នកគ្រប់គ្រងក្រុម",
|
||||||
"app_not_connected": "អ្នកមិនបានភ្ជាប់គណនី {{appName}} ទេ។",
|
"app_not_connected": "អ្នកមិនបានភ្ជាប់គណនី {{appName}} ទេ។",
|
||||||
"connect_now": "ភ្ជាប់ឥឡូវនេះ",
|
"connect_now": "ភ្ជាប់ឥឡូវនេះ",
|
||||||
|
"connect_now_unavailable_tooltip": "ការភ្ជាប់ឥឡូវនេះមិនអាចប្រើបានទេ។ សូមកក់ពីពេលវេលាខាងលើ ឬព្យាយាមម្តងទៀតនៅពេលក្រោយ។",
|
||||||
"managed_event_dialog_confirm_button_one": "ជំនួស និងជូនដំណឹងសមាជិក {{count}}",
|
"managed_event_dialog_confirm_button_one": "ជំនួស និងជូនដំណឹងសមាជិក {{count}}",
|
||||||
"managed_event_dialog_confirm_button_other": "ជំនួស និងជូនដំណឹងសមាជិក {{count}} នាក់",
|
"managed_event_dialog_confirm_button_other": "ជំនួស និងជូនដំណឹងសមាជិក {{count}} នាក់",
|
||||||
"managed_event_dialog_title_one": "URL /{{slug}} មានស្រាប់សម្រាប់សមាជិក {{count}}។ តើអ្នកចង់ជំនួសវាឬ?",
|
"managed_event_dialog_title_one": "URL /{{slug}} មានស្រាប់សម្រាប់សមាជិក {{count}}។ តើអ្នកចង់ជំនួសវាឬ?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "ដោយការបន្ត អ្នកយល់ព្រមនឹង",
|
"proceeding_agreement": "ដោយការបន្ត អ្នកយល់ព្រមនឹង",
|
||||||
"terms": "លក្ខខណ្ឌ",
|
"terms": "លក្ខខណ្ឌ",
|
||||||
"always_show_x_days": "តែងតែបង្ហាញ {{x}} ថ្ងៃដែលអាចប្រើបាន",
|
"always_show_x_days": "តែងតែបង្ហាញ {{x}} ថ្ងៃដែលអាចប្រើបាន",
|
||||||
|
"always_show_x_days_description": "ផ្លាស់ទីបង្អួចកក់រយៈពេល {{x}} ថ្ងៃដោយស្វ័យប្រវត្តិរៀងរាល់ថ្ងៃ ដើម្បីធានាថាប្រតិទិនរបស់អ្នកតែងតែបង្ហាញពេលវេលាដែលអាចប្រើបានសម្រាប់ {{x}} ថ្ងៃបន្ទាប់។",
|
||||||
"team_subscription_cancelled_successfully": "បានលុបចោលការជាវរបស់ក្រុមដោយជោគជ័យ",
|
"team_subscription_cancelled_successfully": "បានលុបចោលការជាវរបស់ក្រុមដោយជោគជ័យ",
|
||||||
"team_subscription_cancellation_error": "មានបញ្ហាពេលលុបចោលការជាវរបស់ក្រុម",
|
"team_subscription_cancellation_error": "មានបញ្ហាពេលលុបចោលការជាវរបស់ក្រុម",
|
||||||
"unable_to_subscribe_to_the_platform": "មានកំហុសមួយបានកើតឡើងខណៈពេលព្យាយាមជាវផែនការក្នុងវេទិកា សូមព្យាយាមម្តងទៀតនៅពេលក្រោយ",
|
"unable_to_subscribe_to_the_platform": "មានកំហុសមួយបានកើតឡើងខណៈពេលព្យាយាមជាវផែនការក្នុងវេទិកា សូមព្យាយាមម្តងទៀតនៅពេលក្រោយ",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "បញ្ចប់ដោយ",
|
"filter_operator_ends_with": "បញ្ចប់ដោយ",
|
||||||
"filter_operator_is_empty": "ទទេ",
|
"filter_operator_is_empty": "ទទេ",
|
||||||
"filter_operator_not_empty": "មិនទទេ",
|
"filter_operator_not_empty": "មិនទទេ",
|
||||||
"translate_description_button": "បកប្រែចំណងជើង/ការពិពណ៌នាទៅជាភាសាកម្មវិធីរុករករបស់ភ្ញៀវដោយប្រើបញ្ញាសិប្បនិម្មិត",
|
"translate_description_button": "បកប្រែចំណងជើងនិងការពិពណ៌នាដោយស្វ័យប្រវត្តិ",
|
||||||
|
"translate_description_button_description": "បកប្រែចំណងជើងនិងការពិពណ៌នាដោយស្វ័យប្រវត្តិទៅជាភាសាកម្មវិធីរុករករបស់អ្នកទស្សនាដោយប្រើ AI។",
|
||||||
"rr_distribution_method": "ការចែកចាយ",
|
"rr_distribution_method": "ការចែកចាយ",
|
||||||
"rr_distribution_method_description": "អនុញ្ញាតឱ្យធ្វើការបែងចែកប្រសិទ្ធភាពដើម្បីបង្កើនភាពអាចរកបានអតិបរមា ឬដើម្បីសម្រេចបាននូវការចាត់តាំងដែលមានតុល្យភាពជាងមុន។",
|
"rr_distribution_method_description": "អនុញ្ញាតឱ្យធ្វើការបែងចែកប្រសិទ្ធភាពដើម្បីបង្កើនភាពអាចរកបានអតិបរមា ឬដើម្បីសម្រេចបាននូវការចាត់តាំងដែលមានតុល្យភាពជាងមុន។",
|
||||||
"rr_distribution_method_availability_title": "បង្កើនភាពអាចរកបាន",
|
"rr_distribution_method_availability_title": "បង្កើនភាពអាចរកបាន",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "មុខងារកន្លែងអង្គុយមិនគាំទ្រព្រឹត្តិការណ៍ដែលកើតឡើងជាប្រចាំទេ។ បិទមុខងារកន្លែងអង្គុយដើម្បីធ្វើឱ្យវាកើតឡើងជាប្រចាំ។",
|
"seats_doesnt_support_recurring": "មុខងារកន្លែងអង្គុយមិនគាំទ្រព្រឹត្តិការណ៍ដែលកើតឡើងជាប្រចាំទេ។ បិទមុខងារកន្លែងអង្គុយដើម្បីធ្វើឱ្យវាកើតឡើងជាប្រចាំ។",
|
||||||
"recurring_event_seats_error": "ព្រឹត្តិការណ៍ដែលកើតឡើងជាប្រចាំមិនគាំទ្រមុខងារកន្លែងអង្គុយទេ។ បិទមុខងារកន្លែងអង្គុយឬធ្វើឱ្យព្រឹត្តិការណ៍មិនកើតឡើងជាប្រចាំ។",
|
"recurring_event_seats_error": "ព្រឹត្តិការណ៍ដែលកើតឡើងជាប្រចាំមិនគាំទ្រមុខងារកន្លែងអង្គុយទេ។ បិទមុខងារកន្លែងអង្គុយឬធ្វើឱ្យព្រឹត្តិការណ៍មិនកើតឡើងជាប្រចាំ។",
|
||||||
"segment": "ផ្នែក",
|
"segment": "ផ្នែក",
|
||||||
|
"saved_filters": "តម្រងដែលបានរក្សាទុក",
|
||||||
"rename": "ប្តូរឈ្មោះ",
|
"rename": "ប្តូរឈ្មោះ",
|
||||||
"rename_segment": "ប្តូរឈ្មោះផ្នែក",
|
"rename_segment": "ប្តូរឈ្មោះផ្នែក",
|
||||||
"delete_segment": "លុបផ្នែក",
|
"delete_segment": "លុបផ្នែក",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "បញ្ជីជម្រើស",
|
"picklist": "បញ្ជីជម្រើស",
|
||||||
"most_cancelled_bookings": "ការកក់ដែលត្រូវបានលុបចោលច្រើនបំផុត",
|
"most_cancelled_bookings": "ការកក់ដែលត្រូវបានលុបចោលច្រើនបំផុត",
|
||||||
"interface_language": "ភាសាចំណុចប្រទាក់",
|
"interface_language": "ភាសាចំណុចប្រទាក់",
|
||||||
|
"interface_language_description": "កំណត់ភាសាដែលអ្នកចូលចិត្តសម្រាប់ចំណុចប្រទាក់នៃការកក់",
|
||||||
"visitors_browser_language": "ភាសាកម្មវិធីរុករករបស់អ្នកទស្សនា",
|
"visitors_browser_language": "ភាសាកម្មវិធីរុករករបស់អ្នកទស្សនា",
|
||||||
"name_or_email": "ឈ្មោះ ឬអ៊ីមែល",
|
"name_or_email": "ឈ្មោះ ឬអ៊ីមែល",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "ប្រសិនបើមិនមានទំនាក់ទំនងត្រូវបានរកឃើញទេ ត្រឡប់ទៅម្ចាស់ Lead ប្រសិនបើមាន",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "ប្រសិនបើមិនមានទំនាក់ទំនងត្រូវបានរកឃើញទេ ត្រឡប់ទៅម្ចាស់ Lead ប្រសិនបើមាន",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "កែសម្រួលនិងកែប្រែការកំណត់លំហូរការងារ",
|
"pbac_desc_update_workflows": "កែសម្រួលនិងកែប្រែការកំណត់លំហូរការងារ",
|
||||||
"pbac_desc_delete_workflows": "លុបលំហូរការងារចេញពីប្រព័ន្ធ",
|
"pbac_desc_delete_workflows": "លុបលំហូរការងារចេញពីប្រព័ន្ធ",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "ភាពអាចប្រើបាន",
|
||||||
|
"pbac_resource_out_of_office": "ក្រៅការិយាល័យ",
|
||||||
"pbac_desc_create_webhooks": "បង្កើត webhooks",
|
"pbac_desc_create_webhooks": "បង្កើត webhooks",
|
||||||
"pbac_desc_view_webhooks": "មើល webhooks",
|
"pbac_desc_view_webhooks": "មើល webhooks",
|
||||||
"pbac_desc_update_webhooks": "ធ្វើបច្ចុប្បន្នភាព webhooks",
|
"pbac_desc_update_webhooks": "ធ្វើបច្ចុប្បន្នភាព webhooks",
|
||||||
"pbac_desc_delete_webhooks": "លុប webhooks",
|
"pbac_desc_delete_webhooks": "លុប webhooks",
|
||||||
|
"pbac_desc_create_availability": "បង្កើតភាពអាចប្រើបាន",
|
||||||
|
"pbac_desc_view_availability": "មើលភាពអាចប្រើបាន",
|
||||||
|
"pbac_desc_update_availability": "ធ្វើបច្ចុប្បន្នភាពភាពអាចប្រើបាន",
|
||||||
|
"pbac_desc_delete_availability": "លុបភាពអាចប្រើបាន",
|
||||||
|
"pbac_desc_create_out_of_office": "បង្កើតស្ថានភាពក្រៅការិយាល័យ",
|
||||||
|
"pbac_desc_view_out_of_office": "មើលស្ថានភាពក្រៅការិយាល័យ",
|
||||||
|
"pbac_desc_update_out_of_office": "ធ្វើបច្ចុប្បន្នភាពស្ថានភាពក្រៅការិយាល័យ",
|
||||||
|
"pbac_desc_delete_out_of_office": "លុបស្ថានភាពក្រៅការិយាល័យ",
|
||||||
"pbac_desc_manage_workflows": "ការចូលប្រើប្រាស់គ្រប់គ្រងពេញលេញទៅលើលំហូរការងារទាំងអស់",
|
"pbac_desc_manage_workflows": "ការចូលប្រើប្រាស់គ្រប់គ្រងពេញលេញទៅលើលំហូរការងារទាំងអស់",
|
||||||
"pbac_desc_create_event_types": "បង្កើតប្រភេទព្រឹត្តិការណ៍",
|
"pbac_desc_create_event_types": "បង្កើតប្រភេទព្រឹត្តិការណ៍",
|
||||||
"pbac_desc_view_event_types": "មើលប្រភេទព្រឹត្តិការណ៍",
|
"pbac_desc_view_event_types": "មើលប្រភេទព្រឹត្តិការណ៍",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "비밀 회의",
|
"secret_meeting": "비밀 회의",
|
||||||
"login_instead": "로그인하십시오",
|
"login_instead": "로그인하십시오",
|
||||||
"create_account": "계정 만들기",
|
"create_account": "계정 만들기",
|
||||||
|
"get_started": "시작하기",
|
||||||
"confirm_password": "비밀번호 확인",
|
"confirm_password": "비밀번호 확인",
|
||||||
"reset_your_password": "이메일 주소로 전송된 지침에 따라 새 비밀번호를 설정하십시오.",
|
"reset_your_password": "이메일 주소로 전송된 지침에 따라 새 비밀번호를 설정하십시오.",
|
||||||
"org_banner_instructions": "{{width}} 너비와 {{height}} 높이의 이미지를 업로드해 주세요.",
|
"org_banner_instructions": "{{width}} 너비와 {{height}} 높이의 이미지를 업로드해 주세요.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "참석자",
|
"attendees": "참석자",
|
||||||
"add_attendees": "참석자 추가하기",
|
"add_attendees": "참석자 추가하기",
|
||||||
"show_advanced_settings": "고급 설정 표시",
|
"show_advanced_settings": "고급 설정 표시",
|
||||||
|
"hide_advanced_settings": "고급 설정 숨기기",
|
||||||
"event_name": "이벤트 이름",
|
"event_name": "이벤트 이름",
|
||||||
"event_name_in_calendar": "캘린더의 이벤트 이름",
|
"event_name_in_calendar": "캘린더의 이벤트 이름",
|
||||||
"event_name_tooltip": "캘린더에 표시될 이름",
|
"event_name_tooltip": "캘린더에 표시될 이름",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "이 기능을 켜면 팀원이 다른 팀원을 볼 수 없게 됩니다.",
|
"make_team_private_description": "이 기능을 켜면 팀원이 다른 팀원을 볼 수 없게 됩니다.",
|
||||||
"you_cannot_see_team_members": "비공개 팀의 모든 팀원을 볼 수 없습니다.",
|
"you_cannot_see_team_members": "비공개 팀의 모든 팀원을 볼 수 없습니다.",
|
||||||
"you_cannot_see_teams_of_org": "비공개 조직의 팀을 볼 수 없습니다.",
|
"you_cannot_see_teams_of_org": "비공개 조직의 팀을 볼 수 없습니다.",
|
||||||
"allow_booker_to_select_duration": "예약자가 기간을 선택하도록 허용",
|
"allow_multiple_durations": "여러 시간 옵션 허용",
|
||||||
"impersonate_user_tip": "이 기능의 사용은 모두 감사됩니다.",
|
"impersonate_user_tip": "이 기능의 사용은 모두 감사됩니다.",
|
||||||
"impersonating_user_warning": "\"{{user}}\"의 사용자 이름을 가장하는 중입니다.",
|
"impersonating_user_warning": "\"{{user}}\"의 사용자 이름을 가장하는 중입니다.",
|
||||||
"impersonating_stop_instructions": "중지하려면 여기를 클릭",
|
"impersonating_stop_instructions": "중지하려면 여기를 클릭",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "예약 확인 실패",
|
"booking_confirmation_failed": "예약 확인 실패",
|
||||||
"not_enough_seats": "좌석이 부족합니다",
|
"not_enough_seats": "좌석이 부족합니다",
|
||||||
"form_builder_field_already_exists": "이 이름을 가진 필드가 이미 존재합니다",
|
"form_builder_field_already_exists": "이 이름을 가진 필드가 이미 존재합니다",
|
||||||
|
"guests_field_must_be_multiemail": "게스트 필드는 '여러 이메일' 유형이어야 합니다",
|
||||||
"show_on_booking_page": "예약 페이지에 표시",
|
"show_on_booking_page": "예약 페이지에 표시",
|
||||||
"visit_cancelled_booking": "취소된 예약 페이지를 방문할 수 있습니다",
|
"visit_cancelled_booking": "취소된 예약 페이지를 방문할 수 있습니다",
|
||||||
"get_started_zapier_templates": "Zapier 템플릿 시작하기",
|
"get_started_zapier_templates": "Zapier 템플릿 시작하기",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "팀 관리자에 의해 잠김",
|
"locked_by_team_admin": "팀 관리자에 의해 잠김",
|
||||||
"app_not_connected": "{{appName}} 계정을 연결하지 않았습니다.",
|
"app_not_connected": "{{appName}} 계정을 연결하지 않았습니다.",
|
||||||
"connect_now": "지금 연결",
|
"connect_now": "지금 연결",
|
||||||
|
"connect_now_unavailable_tooltip": "지금 연결하기는 현재 사용할 수 없습니다. 위의 시간대에서 예약하거나 나중에 다시 시도해 주세요.",
|
||||||
"managed_event_dialog_confirm_button_one": "회원 {{count}}명 바꾸기 및 알림",
|
"managed_event_dialog_confirm_button_one": "회원 {{count}}명 바꾸기 및 알림",
|
||||||
"managed_event_dialog_confirm_button_other": "회원 {{count}}명 바꾸기 및 알림",
|
"managed_event_dialog_confirm_button_other": "회원 {{count}}명 바꾸기 및 알림",
|
||||||
"managed_event_dialog_title_one": "URL /{{slug}}은(는) {{count}}명의 회원에 대해 이미 존재합니다. 바꾸시겠습니까?",
|
"managed_event_dialog_title_one": "URL /{{slug}}은(는) {{count}}명의 회원에 대해 이미 존재합니다. 바꾸시겠습니까?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "계속 진행하시면 당사의",
|
"proceeding_agreement": "계속 진행하시면 당사의",
|
||||||
"terms": "이용약관",
|
"terms": "이용약관",
|
||||||
"always_show_x_days": "항상 {{x}}일 사용 가능",
|
"always_show_x_days": "항상 {{x}}일 사용 가능",
|
||||||
|
"always_show_x_days_description": "매일 {{x}}일 예약 창을 자동으로 앞당겨 캘린더에 항상 다음 {{x}}일의 가용 시간대를 표시합니다.",
|
||||||
"team_subscription_cancelled_successfully": "팀 구독이 성공적으로 취소되었습니다",
|
"team_subscription_cancelled_successfully": "팀 구독이 성공적으로 취소되었습니다",
|
||||||
"team_subscription_cancellation_error": "팀 구독 취소 중 오류가 발생했습니다",
|
"team_subscription_cancellation_error": "팀 구독 취소 중 오류가 발생했습니다",
|
||||||
"unable_to_subscribe_to_the_platform": "플랫폼 플랜 구독 중 오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
"unable_to_subscribe_to_the_platform": "플랫폼 플랜 구독 중 오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "다음으로 끝남",
|
"filter_operator_ends_with": "다음으로 끝남",
|
||||||
"filter_operator_is_empty": "비어 있음",
|
"filter_operator_is_empty": "비어 있음",
|
||||||
"filter_operator_not_empty": "비어 있지 않음",
|
"filter_operator_not_empty": "비어 있지 않음",
|
||||||
"translate_description_button": "AI를 사용하여 방문자의 브라우저 언어로 제목/설명 번역하기",
|
"translate_description_button": "제목 및 설명 자동 번역",
|
||||||
|
"translate_description_button_description": "AI를 사용하여 방문자의 브라우저 언어로 제목과 설명을 자동 번역합니다.",
|
||||||
"rr_distribution_method": "분배 방식",
|
"rr_distribution_method": "분배 방식",
|
||||||
"rr_distribution_method_description": "최대 가용성을 위한 분배 최적화 또는 균형 잡힌 할당을 위한 설정이 가능합니다.",
|
"rr_distribution_method_description": "최대 가용성을 위한 분배 최적화 또는 균형 잡힌 할당을 위한 설정이 가능합니다.",
|
||||||
"rr_distribution_method_availability_title": "가용성 최대화",
|
"rr_distribution_method_availability_title": "가용성 최대화",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "좌석 기능은 반복 이벤트를 지원하지 않습니다. 반복으로 설정하려면 좌석 기능을 비활성화하세요.",
|
"seats_doesnt_support_recurring": "좌석 기능은 반복 이벤트를 지원하지 않습니다. 반복으로 설정하려면 좌석 기능을 비활성화하세요.",
|
||||||
"recurring_event_seats_error": "반복 이벤트는 좌석 기능을 지원하지 않습니다. 좌석 기능을 비활성화하거나 이벤트를 비반복으로 설정하세요.",
|
"recurring_event_seats_error": "반복 이벤트는 좌석 기능을 지원하지 않습니다. 좌석 기능을 비활성화하거나 이벤트를 비반복으로 설정하세요.",
|
||||||
"segment": "세그먼트",
|
"segment": "세그먼트",
|
||||||
|
"saved_filters": "저장된 필터",
|
||||||
"rename": "이름 변경",
|
"rename": "이름 변경",
|
||||||
"rename_segment": "세그먼트 이름 변경",
|
"rename_segment": "세그먼트 이름 변경",
|
||||||
"delete_segment": "세그먼트 삭제",
|
"delete_segment": "세그먼트 삭제",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "선택 목록",
|
"picklist": "선택 목록",
|
||||||
"most_cancelled_bookings": "가장 많이 취소된 예약",
|
"most_cancelled_bookings": "가장 많이 취소된 예약",
|
||||||
"interface_language": "인터페이스 언어",
|
"interface_language": "인터페이스 언어",
|
||||||
|
"interface_language_description": "예약 인터페이스의 선호 언어 설정",
|
||||||
"visitors_browser_language": "방문자의 브라우저 언어",
|
"visitors_browser_language": "방문자의 브라우저 언어",
|
||||||
"name_or_email": "이름 또는 이메일",
|
"name_or_email": "이름 또는 이메일",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "연락처를 찾을 수 없는 경우, 리드 소유자가 있으면 대체",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "연락처를 찾을 수 없는 경우, 리드 소유자가 있으면 대체",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "워크플로 설정 편집 및 수정",
|
"pbac_desc_update_workflows": "워크플로 설정 편집 및 수정",
|
||||||
"pbac_desc_delete_workflows": "시스템에서 워크플로 제거",
|
"pbac_desc_delete_workflows": "시스템에서 워크플로 제거",
|
||||||
"pbac_resource_webhook": "웹훅",
|
"pbac_resource_webhook": "웹훅",
|
||||||
|
"pbac_resource_availability": "가용성",
|
||||||
|
"pbac_resource_out_of_office": "부재중",
|
||||||
"pbac_desc_create_webhooks": "웹훅 생성",
|
"pbac_desc_create_webhooks": "웹훅 생성",
|
||||||
"pbac_desc_view_webhooks": "웹훅 보기",
|
"pbac_desc_view_webhooks": "웹훅 보기",
|
||||||
"pbac_desc_update_webhooks": "웹훅 업데이트",
|
"pbac_desc_update_webhooks": "웹훅 업데이트",
|
||||||
"pbac_desc_delete_webhooks": "웹훅 삭제",
|
"pbac_desc_delete_webhooks": "웹훅 삭제",
|
||||||
|
"pbac_desc_create_availability": "가용성 생성",
|
||||||
|
"pbac_desc_view_availability": "가용성 보기",
|
||||||
|
"pbac_desc_update_availability": "가용성 업데이트",
|
||||||
|
"pbac_desc_delete_availability": "가용성 삭제",
|
||||||
|
"pbac_desc_create_out_of_office": "부재중 생성",
|
||||||
|
"pbac_desc_view_out_of_office": "부재중 보기",
|
||||||
|
"pbac_desc_update_out_of_office": "부재중 업데이트",
|
||||||
|
"pbac_desc_delete_out_of_office": "부재중 삭제",
|
||||||
"pbac_desc_manage_workflows": "모든 워크플로에 대한 전체 관리 액세스",
|
"pbac_desc_manage_workflows": "모든 워크플로에 대한 전체 관리 액세스",
|
||||||
"pbac_desc_create_event_types": "이벤트 타입 생성",
|
"pbac_desc_create_event_types": "이벤트 타입 생성",
|
||||||
"pbac_desc_view_event_types": "이벤트 타입 보기",
|
"pbac_desc_view_event_types": "이벤트 타입 보기",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Geheime Afspraak",
|
"secret_meeting": "Geheime Afspraak",
|
||||||
"login_instead": "In plaats daarvan inloggen",
|
"login_instead": "In plaats daarvan inloggen",
|
||||||
"create_account": "Account Aanmaken",
|
"create_account": "Account Aanmaken",
|
||||||
|
"get_started": "Aan de slag",
|
||||||
"confirm_password": "Wachtwoord bevestigen",
|
"confirm_password": "Wachtwoord bevestigen",
|
||||||
"reset_your_password": "Stel uw nieuwe wachtwoord in met de instructies die u per e-mail zijn toegestuurd.",
|
"reset_your_password": "Stel uw nieuwe wachtwoord in met de instructies die u per e-mail zijn toegestuurd.",
|
||||||
"org_banner_instructions": "Upload een afbeelding van {{width}} breedte en {{height}} hoogte.",
|
"org_banner_instructions": "Upload een afbeelding van {{width}} breedte en {{height}} hoogte.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Kleur van evenementtype (Licht Thema)",
|
"light_event_type_color": "Kleur van evenementtype (Licht Thema)",
|
||||||
"dark_event_type_color": "Kleur van evenementtype (Donker Thema)",
|
"dark_event_type_color": "Kleur van evenementtype (Donker Thema)",
|
||||||
"file_not_named": "Bestandsnaam is niet [idOrSlug]/[user]",
|
"file_not_named": "Bestandsnaam is niet [idOrSlug]/[user]",
|
||||||
"create_team": "Maak Team",
|
"create_team": "Team aanmaken",
|
||||||
"name": "Naam",
|
"name": "Naam",
|
||||||
"nameless_team": "Naamloos team",
|
"nameless_team": "Naamloos team",
|
||||||
"oauth_clients": "OAuth-clients",
|
"oauth_clients": "OAuth-clients",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Deelnemers",
|
"attendees": "Deelnemers",
|
||||||
"add_attendees": "Deelnemers toevoegen",
|
"add_attendees": "Deelnemers toevoegen",
|
||||||
"show_advanced_settings": "Toon geavanceerde instellingen",
|
"show_advanced_settings": "Toon geavanceerde instellingen",
|
||||||
|
"hide_advanced_settings": "Geavanceerde instellingen verbergen",
|
||||||
"event_name": "Naam van evenement",
|
"event_name": "Naam van evenement",
|
||||||
"event_name_in_calendar": "Gebeurtenisnaam in agenda",
|
"event_name_in_calendar": "Gebeurtenisnaam in agenda",
|
||||||
"event_name_tooltip": "De naam die in kalenders wordt weergegeven",
|
"event_name_tooltip": "De naam die in kalenders wordt weergegeven",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Contact met Sales",
|
"contact_sales": "Contact met Sales",
|
||||||
"error_404": "Fout 404",
|
"error_404": "Fout 404",
|
||||||
"default": "standaard keuze",
|
"default": "standaard keuze",
|
||||||
"set_to_default": "Zet als standaard keuze",
|
"set_to_default": "Als standaard instellen",
|
||||||
"new_schedule_btn": "Nieuwe planning",
|
"new_schedule_btn": "Nieuwe planning",
|
||||||
"add_new_schedule": "Voeg een nieuwe planning toe",
|
"add_new_schedule": "Voeg een nieuwe planning toe",
|
||||||
"add_new_calendar": "Voeg een nieuwe agenda toe",
|
"add_new_calendar": "Voeg een nieuwe agenda toe",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Uw teamleden kunnen geen andere teamleden zien wanneer dit ingeschakeld is.",
|
"make_team_private_description": "Uw teamleden kunnen geen andere teamleden zien wanneer dit ingeschakeld is.",
|
||||||
"you_cannot_see_team_members": "U kunt niet alle teamleden van een privéteam zien.",
|
"you_cannot_see_team_members": "U kunt niet alle teamleden van een privéteam zien.",
|
||||||
"you_cannot_see_teams_of_org": "Je kunt de teams van een privéorganisatie niet zien.",
|
"you_cannot_see_teams_of_org": "Je kunt de teams van een privéorganisatie niet zien.",
|
||||||
"allow_booker_to_select_duration": "Booker toestaan om een duur te selecteren",
|
"allow_multiple_durations": "Meerdere tijdsduren toestaan",
|
||||||
"impersonate_user_tip": "Alle gebruik van deze functie wordt gecontroleerd.",
|
"impersonate_user_tip": "Alle gebruik van deze functie wordt gecontroleerd.",
|
||||||
"impersonating_user_warning": "Gebruikersnaam \"{{user}}\" wordt geïmiteerd.",
|
"impersonating_user_warning": "Gebruikersnaam \"{{user}}\" wordt geïmiteerd.",
|
||||||
"impersonating_stop_instructions": "Klik hier om te stoppen",
|
"impersonating_stop_instructions": "Klik hier om te stoppen",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Boekingsbevestiging mislukt",
|
"booking_confirmation_failed": "Boekingsbevestiging mislukt",
|
||||||
"not_enough_seats": "Onvoldoende plaatsen",
|
"not_enough_seats": "Onvoldoende plaatsen",
|
||||||
"form_builder_field_already_exists": "Een veld met deze naam bestaat al",
|
"form_builder_field_already_exists": "Een veld met deze naam bestaat al",
|
||||||
|
"guests_field_must_be_multiemail": "Gastveld moet van het type 'Meerdere e-mails' zijn",
|
||||||
"show_on_booking_page": "Weergeven op boekingspagina",
|
"show_on_booking_page": "Weergeven op boekingspagina",
|
||||||
"visit_cancelled_booking": "Je kunt de pagina van de geannuleerde boeking bezoeken",
|
"visit_cancelled_booking": "Je kunt de pagina van de geannuleerde boeking bezoeken",
|
||||||
"get_started_zapier_templates": "Aan de slag met Zapier-sjablonen",
|
"get_started_zapier_templates": "Aan de slag met Zapier-sjablonen",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Vergrendeld door teambeheerder",
|
"locked_by_team_admin": "Vergrendeld door teambeheerder",
|
||||||
"app_not_connected": "U hebt geen {{appName}}-account gekoppeld.",
|
"app_not_connected": "U hebt geen {{appName}}-account gekoppeld.",
|
||||||
"connect_now": "Nu koppelen",
|
"connect_now": "Nu koppelen",
|
||||||
|
"connect_now_unavailable_tooltip": "Direct verbinden is momenteel niet beschikbaar. Boek alstublieft via bovenstaande tijdslots of probeer het later opnieuw.",
|
||||||
"managed_event_dialog_confirm_button_one": "{{count}} lid vervangen en informeren",
|
"managed_event_dialog_confirm_button_one": "{{count}} lid vervangen en informeren",
|
||||||
"managed_event_dialog_confirm_button_other": "{{count}} leden vervangen en informeren",
|
"managed_event_dialog_confirm_button_other": "{{count}} leden vervangen en informeren",
|
||||||
"managed_event_dialog_title_one": "De URL /{{slug}} bestaat al voor {{count}} lid. Wilt u deze vervangen?",
|
"managed_event_dialog_title_one": "De URL /{{slug}} bestaat al voor {{count}} lid. Wilt u deze vervangen?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Door verder te gaan ga je akkoord met onze",
|
"proceeding_agreement": "Door verder te gaan ga je akkoord met onze",
|
||||||
"terms": "Voorwaarden",
|
"terms": "Voorwaarden",
|
||||||
"always_show_x_days": "Altijd {{x}} dagen beschikbaar",
|
"always_show_x_days": "Altijd {{x}} dagen beschikbaar",
|
||||||
|
"always_show_x_days_description": "Verplaatst het {{x}}-daagse boekingsvenster elke dag automatisch vooruit, zodat je agenda altijd de komende {{x}} dagen aan beschikbare tijdslots toont.",
|
||||||
"team_subscription_cancelled_successfully": "Teamabonnement succesvol opgezegd",
|
"team_subscription_cancelled_successfully": "Teamabonnement succesvol opgezegd",
|
||||||
"team_subscription_cancellation_error": "Fout bij het opzeggen van het teamabonnement",
|
"team_subscription_cancellation_error": "Fout bij het opzeggen van het teamabonnement",
|
||||||
"unable_to_subscribe_to_the_platform": "Er is een fout opgetreden bij het proberen te abonneren op het platformplan, probeer het later opnieuw",
|
"unable_to_subscribe_to_the_platform": "Er is een fout opgetreden bij het proberen te abonneren op het platformplan, probeer het later opnieuw",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Eindigt met",
|
"filter_operator_ends_with": "Eindigt met",
|
||||||
"filter_operator_is_empty": "Is leeg",
|
"filter_operator_is_empty": "Is leeg",
|
||||||
"filter_operator_not_empty": "Is niet leeg",
|
"filter_operator_not_empty": "Is niet leeg",
|
||||||
"translate_description_button": "Vertaal titel/beschrijving naar de browsertaal van de bezoeker met behulp van AI",
|
"translate_description_button": "Titel en beschrijving automatisch vertalen",
|
||||||
|
"translate_description_button_description": "Titels en beschrijvingen automatisch vertalen naar de browsertaal van de bezoeker met behulp van AI.",
|
||||||
"rr_distribution_method": "Distributie",
|
"rr_distribution_method": "Distributie",
|
||||||
"rr_distribution_method_description": "Maakt het mogelijk om de distributie te optimaliseren voor maximale beschikbaarheid of voor een meer gebalanceerde toewijzing.",
|
"rr_distribution_method_description": "Maakt het mogelijk om de distributie te optimaliseren voor maximale beschikbaarheid of voor een meer gebalanceerde toewijzing.",
|
||||||
"rr_distribution_method_availability_title": "Maximale beschikbaarheid",
|
"rr_distribution_method_availability_title": "Maximale beschikbaarheid",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Stoelfunctie ondersteunt geen terugkerend evenement. Schakel stoelfunctie uit om het terugkerend te maken.",
|
"seats_doesnt_support_recurring": "Stoelfunctie ondersteunt geen terugkerend evenement. Schakel stoelfunctie uit om het terugkerend te maken.",
|
||||||
"recurring_event_seats_error": "Terugkerend evenement ondersteunt geen stoelfunctie. Schakel stoelfunctie uit of maak het evenement niet-terugkerend.",
|
"recurring_event_seats_error": "Terugkerend evenement ondersteunt geen stoelfunctie. Schakel stoelfunctie uit of maak het evenement niet-terugkerend.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Opgeslagen filters",
|
||||||
"rename": "Hernoemen",
|
"rename": "Hernoemen",
|
||||||
"rename_segment": "Segment hernoemen",
|
"rename_segment": "Segment hernoemen",
|
||||||
"delete_segment": "Segment verwijderen",
|
"delete_segment": "Segment verwijderen",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Keuzelijst",
|
"picklist": "Keuzelijst",
|
||||||
"most_cancelled_bookings": "Meest geannuleerd",
|
"most_cancelled_bookings": "Meest geannuleerd",
|
||||||
"interface_language": "Interfacetaal",
|
"interface_language": "Interfacetaal",
|
||||||
|
"interface_language_description": "Stel je voorkeurstaal in voor de boekingsinterface",
|
||||||
"visitors_browser_language": "Taal van de browser van de bezoeker",
|
"visitors_browser_language": "Taal van de browser van de bezoeker",
|
||||||
"name_or_email": "Naam of e-mail",
|
"name_or_email": "Naam of e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Als er geen contactpersoon wordt gevonden, val dan terug op de lead-eigenaar als deze bestaat",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Als er geen contactpersoon wordt gevonden, val dan terug op de lead-eigenaar als deze bestaat",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Werkstroominstellingen bewerken en wijzigen",
|
"pbac_desc_update_workflows": "Werkstroominstellingen bewerken en wijzigen",
|
||||||
"pbac_desc_delete_workflows": "Werkstromen uit het systeem verwijderen",
|
"pbac_desc_delete_workflows": "Werkstromen uit het systeem verwijderen",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Beschikbaarheid",
|
||||||
|
"pbac_resource_out_of_office": "Afwezigheid",
|
||||||
"pbac_desc_create_webhooks": "Webhooks maken",
|
"pbac_desc_create_webhooks": "Webhooks maken",
|
||||||
"pbac_desc_view_webhooks": "Webhooks bekijken",
|
"pbac_desc_view_webhooks": "Webhooks bekijken",
|
||||||
"pbac_desc_update_webhooks": "Webhooks bijwerken",
|
"pbac_desc_update_webhooks": "Webhooks bijwerken",
|
||||||
"pbac_desc_delete_webhooks": "Webhooks verwijderen",
|
"pbac_desc_delete_webhooks": "Webhooks verwijderen",
|
||||||
|
"pbac_desc_create_availability": "Beschikbaarheid aanmaken",
|
||||||
|
"pbac_desc_view_availability": "Beschikbaarheid bekijken",
|
||||||
|
"pbac_desc_update_availability": "Beschikbaarheid bijwerken",
|
||||||
|
"pbac_desc_delete_availability": "Beschikbaarheid verwijderen",
|
||||||
|
"pbac_desc_create_out_of_office": "Afwezigheid aanmaken",
|
||||||
|
"pbac_desc_view_out_of_office": "Afwezigheid bekijken",
|
||||||
|
"pbac_desc_update_out_of_office": "Afwezigheid bijwerken",
|
||||||
|
"pbac_desc_delete_out_of_office": "Afwezigheid verwijderen",
|
||||||
"pbac_desc_manage_workflows": "Volledige beheertoegang tot alle werkstromen",
|
"pbac_desc_manage_workflows": "Volledige beheertoegang tot alle werkstromen",
|
||||||
"pbac_desc_create_event_types": "Gebeurtenistypes aanmaken",
|
"pbac_desc_create_event_types": "Gebeurtenistypes aanmaken",
|
||||||
"pbac_desc_view_event_types": "Gebeurtenistypes bekijken",
|
"pbac_desc_view_event_types": "Gebeurtenistypes bekijken",
|
||||||
@@ -3560,8 +3578,8 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Load balancing is niet beschikbaar met round robin groepen",
|
"rr_load_balancing_disabled_with_groups": "Load balancing is niet beschikbaar met round robin groepen",
|
||||||
"round_robin_settings_description": "Pas de standaard round robin-instellingen aan voor dit team",
|
"round_robin_settings_description": "Pas de standaard round robin-instellingen aan voor dit team",
|
||||||
"round_robin_settings_updated_successfully": "Round Robin-instellingen succesvol bijgewerkt",
|
"round_robin_settings_updated_successfully": "Round Robin-instellingen succesvol bijgewerkt",
|
||||||
"booking_creation_time": "Tijdstip van boekingscreatie",
|
"booking_creation_time": "Aanmaaktijd boeking",
|
||||||
"meeting_start_time": "Starttijd van de vergadering",
|
"meeting_start_time": "Starttijd vergadering",
|
||||||
"members_affected_by_disabling_delegation_credential": "Getroffen leden",
|
"members_affected_by_disabling_delegation_credential": "Getroffen leden",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Geen leden getroffen door het uitschakelen van delegatiereferentie",
|
"no_members_affected_by_disabling_delegation_credential": "Geen leden getroffen door het uitschakelen van delegatiereferentie",
|
||||||
"download_expense_log": "Download uitgavenlogboek",
|
"download_expense_log": "Download uitgavenlogboek",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Hemmelig Møte",
|
"secret_meeting": "Hemmelig Møte",
|
||||||
"login_instead": "Logg på i stedet",
|
"login_instead": "Logg på i stedet",
|
||||||
"create_account": "Opprett Bruker",
|
"create_account": "Opprett Bruker",
|
||||||
|
"get_started": "Kom i gang",
|
||||||
"confirm_password": "Bekreft passord",
|
"confirm_password": "Bekreft passord",
|
||||||
"reset_your_password": "Sett ditt nye passord med instruksjonene sendt til din e-postadresse.",
|
"reset_your_password": "Sett ditt nye passord med instruksjonene sendt til din e-postadresse.",
|
||||||
"org_banner_instructions": "Vennligst last opp et bilde med {{width}} bredde og {{height}} høyde.",
|
"org_banner_instructions": "Vennligst last opp et bilde med {{width}} bredde og {{height}} høyde.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Deltakere",
|
"attendees": "Deltakere",
|
||||||
"add_attendees": "Legg til deltaker",
|
"add_attendees": "Legg til deltaker",
|
||||||
"show_advanced_settings": "Vis avanserte innstillinger",
|
"show_advanced_settings": "Vis avanserte innstillinger",
|
||||||
|
"hide_advanced_settings": "Skjul avanserte innstillinger",
|
||||||
"event_name": "Hendelses navn",
|
"event_name": "Hendelses navn",
|
||||||
"event_name_in_calendar": "Med hendelsesnavn",
|
"event_name_in_calendar": "Med hendelsesnavn",
|
||||||
"event_name_tooltip": "Navnet som vises i kalenderne",
|
"event_name_tooltip": "Navnet som vises i kalenderne",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Kontakt Salg",
|
"contact_sales": "Kontakt Salg",
|
||||||
"error_404": "Feil 404",
|
"error_404": "Feil 404",
|
||||||
"default": "Standard",
|
"default": "Standard",
|
||||||
"set_to_default": "Sett til Standard",
|
"set_to_default": "Sett som standard",
|
||||||
"new_schedule_btn": "Ny tidsplan",
|
"new_schedule_btn": "Ny tidsplan",
|
||||||
"add_new_schedule": "Legg til ny tidsplan",
|
"add_new_schedule": "Legg til ny tidsplan",
|
||||||
"add_new_calendar": "Legg til en ny kalender",
|
"add_new_calendar": "Legg til en ny kalender",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Medlemmene i teamet ditt vil ikke kunne se andre teammedlemmer når dette er slått på.",
|
"make_team_private_description": "Medlemmene i teamet ditt vil ikke kunne se andre teammedlemmer når dette er slått på.",
|
||||||
"you_cannot_see_team_members": "Du kan ikke se alle teammedlemmene i et privat team.",
|
"you_cannot_see_team_members": "Du kan ikke se alle teammedlemmene i et privat team.",
|
||||||
"you_cannot_see_teams_of_org": "Du kan ikke se teamene til en privat organisasjon.",
|
"you_cannot_see_teams_of_org": "Du kan ikke se teamene til en privat organisasjon.",
|
||||||
"allow_booker_to_select_duration": "La bookeren velge varighet",
|
"allow_multiple_durations": "Tillat flere varigheter",
|
||||||
"impersonate_user_tip": "All bruk av denne funksjonen er revidert.",
|
"impersonate_user_tip": "All bruk av denne funksjonen er revidert.",
|
||||||
"impersonating_user_warning": "Etterligner brukernavnet \"{{user}}\".",
|
"impersonating_user_warning": "Etterligner brukernavnet \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Klikk her for å stoppe",
|
"impersonating_stop_instructions": "Klikk her for å stoppe",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Booking-bekreftelse feilet",
|
"booking_confirmation_failed": "Booking-bekreftelse feilet",
|
||||||
"not_enough_seats": "Ikke nok seter",
|
"not_enough_seats": "Ikke nok seter",
|
||||||
"form_builder_field_already_exists": "Et felt med dette navnet eksisterer allerede",
|
"form_builder_field_already_exists": "Et felt med dette navnet eksisterer allerede",
|
||||||
|
"guests_field_must_be_multiemail": "Gjestfeltet må være av typen 'Flere e-poster'",
|
||||||
"show_on_booking_page": "Vis på bestillingssiden",
|
"show_on_booking_page": "Vis på bestillingssiden",
|
||||||
"visit_cancelled_booking": "Du kan besøke siden for avbestilt booking",
|
"visit_cancelled_booking": "Du kan besøke siden for avbestilt booking",
|
||||||
"get_started_zapier_templates": "Kom i gang med Zapier-maler",
|
"get_started_zapier_templates": "Kom i gang med Zapier-maler",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Låst av teamadministrator",
|
"locked_by_team_admin": "Låst av teamadministrator",
|
||||||
"app_not_connected": "Du har ikke koblet til en {{appName}}-konto.",
|
"app_not_connected": "Du har ikke koblet til en {{appName}}-konto.",
|
||||||
"connect_now": "Koble til nå",
|
"connect_now": "Koble til nå",
|
||||||
|
"connect_now_unavailable_tooltip": "Koble til nå er for øyeblikket utilgjengelig. Vennligst bestill fra tilgjengelige tider ovenfor eller prøv igjen senere.",
|
||||||
"managed_event_dialog_confirm_button_one": "Erstatt og varsle {{count}} medlem",
|
"managed_event_dialog_confirm_button_one": "Erstatt og varsle {{count}} medlem",
|
||||||
"managed_event_dialog_confirm_button_other": "Erstatt og varsle {{count}} medlemmer",
|
"managed_event_dialog_confirm_button_other": "Erstatt og varsle {{count}} medlemmer",
|
||||||
"managed_event_dialog_title_one": "URL-en /{{slug}} eksisterer allerede for {{count}} medlem. Vil du erstatte den?",
|
"managed_event_dialog_title_one": "URL-en /{{slug}} eksisterer allerede for {{count}} medlem. Vil du erstatte den?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Bedrift",
|
"enterprise": "Bedrift",
|
||||||
"enterprise_description": "Oppgrader til Enterprise for å opprette din organisasjon",
|
"enterprise_description": "Oppgrader til Enterprise for å opprette din organisasjon",
|
||||||
"create_your_org": "Opprett din Organisasjon",
|
"create_your_org": "Opprett din Organisasjon",
|
||||||
"create_org": "Opprett Organisasjon",
|
"create_org": "Opprett organisasjon",
|
||||||
"create_your_org_description": "Oppgrader til Organisasjoner og få et underdomene, enhetlig fakturering, innsikt, omfattende whitelabeling og mer",
|
"create_your_org_description": "Oppgrader til Organisasjoner og få et underdomene, enhetlig fakturering, innsikt, omfattende whitelabeling og mer",
|
||||||
"create_your_enterprise_description": "Oppgrader til Enterprise og få tilgang til Active Directory Sync, SCIM Automatisk Brukerprovisjonering, Cal.ai Voice Agents, Admin APIer og mer!",
|
"create_your_enterprise_description": "Oppgrader til Enterprise og få tilgang til Active Directory Sync, SCIM Automatisk Brukerprovisjonering, Cal.ai Voice Agents, Admin APIer og mer!",
|
||||||
"other_payment_app_enabled": "Du kan kun aktivere én betalingsapp per hendelsestype",
|
"other_payment_app_enabled": "Du kan kun aktivere én betalingsapp per hendelsestype",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Ved å fortsette godtar du våre",
|
"proceeding_agreement": "Ved å fortsette godtar du våre",
|
||||||
"terms": "Vilkår",
|
"terms": "Vilkår",
|
||||||
"always_show_x_days": "Alltid {{x}} dager tilgjengelig",
|
"always_show_x_days": "Alltid {{x}} dager tilgjengelig",
|
||||||
|
"always_show_x_days_description": "Flytter automatisk {{x}}-dagers bestillingsvinduet fremover hver dag, slik at kalenderen din alltid viser de neste {{x}} dagene med tilgjengelige tider.",
|
||||||
"team_subscription_cancelled_successfully": "Teamabonnement kansellert",
|
"team_subscription_cancelled_successfully": "Teamabonnement kansellert",
|
||||||
"team_subscription_cancellation_error": "Feil ved kansellering av teamabonnement",
|
"team_subscription_cancellation_error": "Feil ved kansellering av teamabonnement",
|
||||||
"unable_to_subscribe_to_the_platform": "En feil oppstod under forsøket på å abonnere på plattformplanen, vennligst prøv igjen senere",
|
"unable_to_subscribe_to_the_platform": "En feil oppstod under forsøket på å abonnere på plattformplanen, vennligst prøv igjen senere",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Slutter med",
|
"filter_operator_ends_with": "Slutter med",
|
||||||
"filter_operator_is_empty": "Er tom",
|
"filter_operator_is_empty": "Er tom",
|
||||||
"filter_operator_not_empty": "Er ikke tom",
|
"filter_operator_not_empty": "Er ikke tom",
|
||||||
"translate_description_button": "Oversett tittel/beskrivelse til besøkendes nettleserspråk ved hjelp av AI",
|
"translate_description_button": "Auto-oversett tittel og beskrivelse",
|
||||||
|
"translate_description_button_description": "Oversett automatisk titler og beskrivelser til besøkendes nettleserspråk ved hjelp av AI.",
|
||||||
"rr_distribution_method": "Distribusjon",
|
"rr_distribution_method": "Distribusjon",
|
||||||
"rr_distribution_method_description": "Gjør det mulig å optimalisere distribusjonen for maksimal tilgjengelighet eller for å oppnå en mer balansert fordeling.",
|
"rr_distribution_method_description": "Gjør det mulig å optimalisere distribusjonen for maksimal tilgjengelighet eller for å oppnå en mer balansert fordeling.",
|
||||||
"rr_distribution_method_availability_title": "Maksimer tilgjengelighet",
|
"rr_distribution_method_availability_title": "Maksimer tilgjengelighet",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Seterfunksjonen støtter ikke gjentakende arrangement. Deaktiver seterfunksjonen for å gjøre det gjentakende.",
|
"seats_doesnt_support_recurring": "Seterfunksjonen støtter ikke gjentakende arrangement. Deaktiver seterfunksjonen for å gjøre det gjentakende.",
|
||||||
"recurring_event_seats_error": "Gjentakende arrangement støtter ikke seterfunksjonen. Deaktiver seterfunksjonen eller gjør arrangementet ikke-gjentakende.",
|
"recurring_event_seats_error": "Gjentakende arrangement støtter ikke seterfunksjonen. Deaktiver seterfunksjonen eller gjør arrangementet ikke-gjentakende.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Lagrede filtre",
|
||||||
"rename": "Gi nytt navn",
|
"rename": "Gi nytt navn",
|
||||||
"rename_segment": "Gi nytt navn til segment",
|
"rename_segment": "Gi nytt navn til segment",
|
||||||
"delete_segment": "Slett segment",
|
"delete_segment": "Slett segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Valgliste",
|
"picklist": "Valgliste",
|
||||||
"most_cancelled_bookings": "Flest kansellerte bookinger",
|
"most_cancelled_bookings": "Flest kansellerte bookinger",
|
||||||
"interface_language": "Grensesnittspråk",
|
"interface_language": "Grensesnittspråk",
|
||||||
|
"interface_language_description": "Angi ditt foretrukne språk for bestillingsgrensesnittet",
|
||||||
"visitors_browser_language": "Besøkendes nettleserspråk",
|
"visitors_browser_language": "Besøkendes nettleserspråk",
|
||||||
"name_or_email": "Navn eller e-post",
|
"name_or_email": "Navn eller e-post",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hvis ingen kontakt blir funnet, fall tilbake til leadeier hvis den eksisterer",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hvis ingen kontakt blir funnet, fall tilbake til leadeier hvis den eksisterer",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Redigere og endre arbeidsflytinnstillinger",
|
"pbac_desc_update_workflows": "Redigere og endre arbeidsflytinnstillinger",
|
||||||
"pbac_desc_delete_workflows": "Fjerne arbeidsflyter fra systemet",
|
"pbac_desc_delete_workflows": "Fjerne arbeidsflyter fra systemet",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Tilgjengelighet",
|
||||||
|
"pbac_resource_out_of_office": "Ute av kontoret",
|
||||||
"pbac_desc_create_webhooks": "Opprett webhook'er",
|
"pbac_desc_create_webhooks": "Opprett webhook'er",
|
||||||
"pbac_desc_view_webhooks": "Se webhook'er",
|
"pbac_desc_view_webhooks": "Se webhook'er",
|
||||||
"pbac_desc_update_webhooks": "Oppdater webhooks",
|
"pbac_desc_update_webhooks": "Oppdater webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Slett webhooks",
|
"pbac_desc_delete_webhooks": "Slett webhooks",
|
||||||
|
"pbac_desc_create_availability": "Opprett tilgjengelighet",
|
||||||
|
"pbac_desc_view_availability": "Vis tilgjengelighet",
|
||||||
|
"pbac_desc_update_availability": "Oppdater tilgjengelighet",
|
||||||
|
"pbac_desc_delete_availability": "Slett tilgjengelighet",
|
||||||
|
"pbac_desc_create_out_of_office": "Opprett fravær",
|
||||||
|
"pbac_desc_view_out_of_office": "Vis fravær",
|
||||||
|
"pbac_desc_update_out_of_office": "Oppdater fravær",
|
||||||
|
"pbac_desc_delete_out_of_office": "Slett fravær",
|
||||||
"pbac_desc_manage_workflows": "Full administrasjonstilgang til alle arbeidsflyter",
|
"pbac_desc_manage_workflows": "Full administrasjonstilgang til alle arbeidsflyter",
|
||||||
"pbac_desc_create_event_types": "Opprett hendelsestyper",
|
"pbac_desc_create_event_types": "Opprett hendelsestyper",
|
||||||
"pbac_desc_view_event_types": "Vis hendelsestyper",
|
"pbac_desc_view_event_types": "Vis hendelsestyper",
|
||||||
@@ -3560,8 +3578,8 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Lastbalansering er ikke tilgjengelig med round robin-grupper",
|
"rr_load_balancing_disabled_with_groups": "Lastbalansering er ikke tilgjengelig med round robin-grupper",
|
||||||
"round_robin_settings_description": "Tilpass standardinnstillingene for rundgang for dette teamet",
|
"round_robin_settings_description": "Tilpass standardinnstillingene for rundgang for dette teamet",
|
||||||
"round_robin_settings_updated_successfully": "Rundgangsinnstillinger oppdatert",
|
"round_robin_settings_updated_successfully": "Rundgangsinnstillinger oppdatert",
|
||||||
"booking_creation_time": "Tidspunkt for opprettelse av booking",
|
"booking_creation_time": "Tidspunkt for opprettelse av bestilling",
|
||||||
"meeting_start_time": "Møtestarttid",
|
"meeting_start_time": "Møtets starttidspunkt",
|
||||||
"members_affected_by_disabling_delegation_credential": "Berørte medlemmer",
|
"members_affected_by_disabling_delegation_credential": "Berørte medlemmer",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Ingen medlemmer berørt av deaktivering av delegeringslegitimasjon",
|
"no_members_affected_by_disabling_delegation_credential": "Ingen medlemmer berørt av deaktivering av delegeringslegitimasjon",
|
||||||
"download_expense_log": "Last ned utgiftslogg",
|
"download_expense_log": "Last ned utgiftslogg",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Sekretne spotkanie",
|
"secret_meeting": "Sekretne spotkanie",
|
||||||
"login_instead": "Zamiast tego zaloguj się",
|
"login_instead": "Zamiast tego zaloguj się",
|
||||||
"create_account": "Utwórz Konto",
|
"create_account": "Utwórz Konto",
|
||||||
|
"get_started": "Rozpocznij",
|
||||||
"confirm_password": "Potwierdź hasło",
|
"confirm_password": "Potwierdź hasło",
|
||||||
"reset_your_password": "Ustaw nowe hasło zgodnie z instrukcjami wysłanymi na Twój adres e-mail.",
|
"reset_your_password": "Ustaw nowe hasło zgodnie z instrukcjami wysłanymi na Twój adres e-mail.",
|
||||||
"org_banner_instructions": "Proszę przesłać obraz o szerokości {{width}} i wysokości {{height}}.",
|
"org_banner_instructions": "Proszę przesłać obraz o szerokości {{width}} i wysokości {{height}}.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Kolor typu wydarzenia (jasny motyw)",
|
"light_event_type_color": "Kolor typu wydarzenia (jasny motyw)",
|
||||||
"dark_event_type_color": "Kolor typu wydarzenia (ciemny motyw)",
|
"dark_event_type_color": "Kolor typu wydarzenia (ciemny motyw)",
|
||||||
"file_not_named": "Plik nie jest nazwany [idOrSlug]/[user]",
|
"file_not_named": "Plik nie jest nazwany [idOrSlug]/[user]",
|
||||||
"create_team": "Utwórz Zespół",
|
"create_team": "Utwórz zespół",
|
||||||
"name": "Nazwa",
|
"name": "Nazwa",
|
||||||
"nameless_team": "Zespół bez nazwy",
|
"nameless_team": "Zespół bez nazwy",
|
||||||
"oauth_clients": "Klienci OAuth",
|
"oauth_clients": "Klienci OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Uczestnicy",
|
"attendees": "Uczestnicy",
|
||||||
"add_attendees": "Dodaj uczestników",
|
"add_attendees": "Dodaj uczestników",
|
||||||
"show_advanced_settings": "Pokaż ustawienia zaawansowane",
|
"show_advanced_settings": "Pokaż ustawienia zaawansowane",
|
||||||
|
"hide_advanced_settings": "Ukryj zaawansowane ustawienia",
|
||||||
"event_name": "Nazwa wydarzenia",
|
"event_name": "Nazwa wydarzenia",
|
||||||
"event_name_in_calendar": "Nazwa wydarzenia w kalendarzu",
|
"event_name_in_calendar": "Nazwa wydarzenia w kalendarzu",
|
||||||
"event_name_tooltip": "Nazwa, która pojawi się w kalendarzach",
|
"event_name_tooltip": "Nazwa, która pojawi się w kalendarzach",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Skontaktuj się ze sprzedażą",
|
"contact_sales": "Skontaktuj się ze sprzedażą",
|
||||||
"error_404": "Błąd 404",
|
"error_404": "Błąd 404",
|
||||||
"default": "Domyślny",
|
"default": "Domyślny",
|
||||||
"set_to_default": "Ustaw jako domyślny",
|
"set_to_default": "Ustaw jako domyślne",
|
||||||
"new_schedule_btn": "Nowy harmonogram",
|
"new_schedule_btn": "Nowy harmonogram",
|
||||||
"add_new_schedule": "Dodaj nowy harmonogram",
|
"add_new_schedule": "Dodaj nowy harmonogram",
|
||||||
"add_new_calendar": "Dodaj nowy kalendarz",
|
"add_new_calendar": "Dodaj nowy kalendarz",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Gdy to ustawienie jest włączone, członkowie zespołu nie będą mogli zobaczyć innych członków zespołu.",
|
"make_team_private_description": "Gdy to ustawienie jest włączone, członkowie zespołu nie będą mogli zobaczyć innych członków zespołu.",
|
||||||
"you_cannot_see_team_members": "Nie możesz zobaczyć wszystkich członków zespołu prywatnego.",
|
"you_cannot_see_team_members": "Nie możesz zobaczyć wszystkich członków zespołu prywatnego.",
|
||||||
"you_cannot_see_teams_of_org": "Nie możesz zobaczyć zespołów prywatnej organizacji.",
|
"you_cannot_see_teams_of_org": "Nie możesz zobaczyć zespołów prywatnej organizacji.",
|
||||||
"allow_booker_to_select_duration": "Zezwól rezerwującemu na wybranie czasu trwania",
|
"allow_multiple_durations": "Zezwól na wiele czasów trwania",
|
||||||
"impersonate_user_tip": "Wszystkie przypadki użycia tej funkcji są kontrolowane.",
|
"impersonate_user_tip": "Wszystkie przypadki użycia tej funkcji są kontrolowane.",
|
||||||
"impersonating_user_warning": "Podszywanie się pod użytkownika „{{user}}”.",
|
"impersonating_user_warning": "Podszywanie się pod użytkownika „{{user}}”.",
|
||||||
"impersonating_stop_instructions": "Kliknij tutaj, aby zatrzymać",
|
"impersonating_stop_instructions": "Kliknij tutaj, aby zatrzymać",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Potwierdzenie rezerwacji nie powiodło się",
|
"booking_confirmation_failed": "Potwierdzenie rezerwacji nie powiodło się",
|
||||||
"not_enough_seats": "Za mało miejsc",
|
"not_enough_seats": "Za mało miejsc",
|
||||||
"form_builder_field_already_exists": "Pole o tej nazwie już istnieje",
|
"form_builder_field_already_exists": "Pole o tej nazwie już istnieje",
|
||||||
|
"guests_field_must_be_multiemail": "Pole gości musi być typu 'Wiele adresów e-mail'",
|
||||||
"show_on_booking_page": "Pokaż na stronie rezerwacji",
|
"show_on_booking_page": "Pokaż na stronie rezerwacji",
|
||||||
"visit_cancelled_booking": "Możesz odwiedzić stronę anulowanej rezerwacji",
|
"visit_cancelled_booking": "Możesz odwiedzić stronę anulowanej rezerwacji",
|
||||||
"get_started_zapier_templates": "Zacznij korzystać z szablonów Zapier",
|
"get_started_zapier_templates": "Zacznij korzystać z szablonów Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Zablokowane przez administratora zespołu",
|
"locked_by_team_admin": "Zablokowane przez administratora zespołu",
|
||||||
"app_not_connected": "Nie podłączono konta {{appName}}.",
|
"app_not_connected": "Nie podłączono konta {{appName}}.",
|
||||||
"connect_now": "Podłącz teraz",
|
"connect_now": "Podłącz teraz",
|
||||||
|
"connect_now_unavailable_tooltip": "Opcja 'Połącz teraz' jest obecnie niedostępna. Zarezerwuj z powyższych dostępnych terminów lub spróbuj ponownie później.",
|
||||||
"managed_event_dialog_confirm_button_one": "Zastąp i poinformuj {{count}} członka",
|
"managed_event_dialog_confirm_button_one": "Zastąp i poinformuj {{count}} członka",
|
||||||
"managed_event_dialog_confirm_button_other": "Zastąp i powiadom {{count}} członków",
|
"managed_event_dialog_confirm_button_other": "Zastąp i powiadom {{count}} członków",
|
||||||
"managed_event_dialog_title_one": "Adres URL /{{slug}} już istnieje dla {{count}} członka. Czy chcesz go zastąpić?",
|
"managed_event_dialog_title_one": "Adres URL /{{slug}} już istnieje dla {{count}} członka. Czy chcesz go zastąpić?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Przedsiębiorstwo",
|
"enterprise": "Przedsiębiorstwo",
|
||||||
"enterprise_description": "Ulepsz do wersji Enterprise, aby utworzyć swoją Organizację",
|
"enterprise_description": "Ulepsz do wersji Enterprise, aby utworzyć swoją Organizację",
|
||||||
"create_your_org": "Utwórz swoją Organizację",
|
"create_your_org": "Utwórz swoją Organizację",
|
||||||
"create_org": "Utwórz Organizację",
|
"create_org": "Utwórz organizację",
|
||||||
"create_your_org_description": "Ulepsz do wersji Organizations i otrzymaj subdomenę, zintegrowane rozliczenia, Insights, rozbudowane możliwości whitelabelingu i więcej",
|
"create_your_org_description": "Ulepsz do wersji Organizations i otrzymaj subdomenę, zintegrowane rozliczenia, Insights, rozbudowane możliwości whitelabelingu i więcej",
|
||||||
"create_your_enterprise_description": "Ulepsz do wersji Enterprise i uzyskaj dostęp do synchronizacji Active Directory, automatycznego provisioningu użytkowników SCIM, agentów głosowych Cal.ai, API administracyjnych i więcej!",
|
"create_your_enterprise_description": "Ulepsz do wersji Enterprise i uzyskaj dostęp do synchronizacji Active Directory, automatycznego provisioningu użytkowników SCIM, agentów głosowych Cal.ai, API administracyjnych i więcej!",
|
||||||
"other_payment_app_enabled": "Możesz włączyć tylko jedną aplikację płatniczą na typ wydarzenia",
|
"other_payment_app_enabled": "Możesz włączyć tylko jedną aplikację płatniczą na typ wydarzenia",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Kontynuując, zgadzasz się na nasze",
|
"proceeding_agreement": "Kontynuując, zgadzasz się na nasze",
|
||||||
"terms": "Warunki",
|
"terms": "Warunki",
|
||||||
"always_show_x_days": "Zawsze dostępne {{x}} dni",
|
"always_show_x_days": "Zawsze dostępne {{x}} dni",
|
||||||
|
"always_show_x_days_description": "Automatycznie przesuwa {{x}}-dniowe okno rezerwacji każdego dnia, zapewniając, że Twój kalendarz zawsze pokazuje kolejne {{x}} dni dostępnych terminów.",
|
||||||
"team_subscription_cancelled_successfully": "Subskrypcja zespołu została pomyślnie anulowana",
|
"team_subscription_cancelled_successfully": "Subskrypcja zespołu została pomyślnie anulowana",
|
||||||
"team_subscription_cancellation_error": "Błąd podczas anulowania subskrypcji zespołu",
|
"team_subscription_cancellation_error": "Błąd podczas anulowania subskrypcji zespołu",
|
||||||
"unable_to_subscribe_to_the_platform": "Wystąpił błąd podczas próby subskrypcji planu platformy, spróbuj ponownie później",
|
"unable_to_subscribe_to_the_platform": "Wystąpił błąd podczas próby subskrypcji planu platformy, spróbuj ponownie później",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Kończy się na",
|
"filter_operator_ends_with": "Kończy się na",
|
||||||
"filter_operator_is_empty": "Jest pusty",
|
"filter_operator_is_empty": "Jest pusty",
|
||||||
"filter_operator_not_empty": "Nie jest pusty",
|
"filter_operator_not_empty": "Nie jest pusty",
|
||||||
"translate_description_button": "Przetłumacz tytuł/opis na język przeglądarki użytkownika za pomocą AI",
|
"translate_description_button": "Automatycznie przetłumacz tytuł i opis",
|
||||||
|
"translate_description_button_description": "Automatycznie tłumacz tytuły i opisy na język przeglądarki odwiedzającego za pomocą AI.",
|
||||||
"rr_distribution_method": "Dystrybucja",
|
"rr_distribution_method": "Dystrybucja",
|
||||||
"rr_distribution_method_description": "Pozwala zoptymalizować dystrybucję pod kątem maksymalnej dostępności lub bardziej zrównoważonego przydziału spotkań.",
|
"rr_distribution_method_description": "Pozwala zoptymalizować dystrybucję pod kątem maksymalnej dostępności lub bardziej zrównoważonego przydziału spotkań.",
|
||||||
"rr_distribution_method_availability_title": "Maksymalizuj dostępność",
|
"rr_distribution_method_availability_title": "Maksymalizuj dostępność",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Funkcja miejsc nie obsługuje wydarzeń cyklicznych. Wyłącz funkcję miejsc, aby ustawić wydarzenie jako cykliczne.",
|
"seats_doesnt_support_recurring": "Funkcja miejsc nie obsługuje wydarzeń cyklicznych. Wyłącz funkcję miejsc, aby ustawić wydarzenie jako cykliczne.",
|
||||||
"recurring_event_seats_error": "Wydarzenie cykliczne nie obsługuje funkcji miejsc. Wyłącz funkcję miejsc lub ustaw wydarzenie jako jednorazowe.",
|
"recurring_event_seats_error": "Wydarzenie cykliczne nie obsługuje funkcji miejsc. Wyłącz funkcję miejsc lub ustaw wydarzenie jako jednorazowe.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Zapisane filtry",
|
||||||
"rename": "Zmień nazwę",
|
"rename": "Zmień nazwę",
|
||||||
"rename_segment": "Zmień nazwę segmentu",
|
"rename_segment": "Zmień nazwę segmentu",
|
||||||
"delete_segment": "Usuń segment",
|
"delete_segment": "Usuń segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista wyboru",
|
"picklist": "Lista wyboru",
|
||||||
"most_cancelled_bookings": "Najczęściej anulowane rezerwacje",
|
"most_cancelled_bookings": "Najczęściej anulowane rezerwacje",
|
||||||
"interface_language": "Język interfejsu",
|
"interface_language": "Język interfejsu",
|
||||||
|
"interface_language_description": "Ustaw preferowany język interfejsu rezerwacji",
|
||||||
"visitors_browser_language": "Język przeglądarki odwiedzającego",
|
"visitors_browser_language": "Język przeglądarki odwiedzającego",
|
||||||
"name_or_email": "Imię lub email",
|
"name_or_email": "Imię lub email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Jeśli nie znaleziono kontaktu, powróć do właściciela leada, jeśli istnieje",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Jeśli nie znaleziono kontaktu, powróć do właściciela leada, jeśli istnieje",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Edytuj i modyfikuj ustawienia przepływów pracy",
|
"pbac_desc_update_workflows": "Edytuj i modyfikuj ustawienia przepływów pracy",
|
||||||
"pbac_desc_delete_workflows": "Usuń przepływy pracy z systemu",
|
"pbac_desc_delete_workflows": "Usuń przepływy pracy z systemu",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Dostępność",
|
||||||
|
"pbac_resource_out_of_office": "Poza biurem",
|
||||||
"pbac_desc_create_webhooks": "Twórz webhooki",
|
"pbac_desc_create_webhooks": "Twórz webhooki",
|
||||||
"pbac_desc_view_webhooks": "Przeglądaj webhooki",
|
"pbac_desc_view_webhooks": "Przeglądaj webhooki",
|
||||||
"pbac_desc_update_webhooks": "Aktualizuj webhooks",
|
"pbac_desc_update_webhooks": "Aktualizuj webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Usuń webhooks",
|
"pbac_desc_delete_webhooks": "Usuń webhooks",
|
||||||
|
"pbac_desc_create_availability": "Utwórz dostępność",
|
||||||
|
"pbac_desc_view_availability": "Wyświetl dostępność",
|
||||||
|
"pbac_desc_update_availability": "Zaktualizuj dostępność",
|
||||||
|
"pbac_desc_delete_availability": "Usuń dostępność",
|
||||||
|
"pbac_desc_create_out_of_office": "Utwórz status poza biurem",
|
||||||
|
"pbac_desc_view_out_of_office": "Wyświetl status poza biurem",
|
||||||
|
"pbac_desc_update_out_of_office": "Zaktualizuj status poza biurem",
|
||||||
|
"pbac_desc_delete_out_of_office": "Usuń status poza biurem",
|
||||||
"pbac_desc_manage_workflows": "Pełny dostęp do zarządzania wszystkimi przepływami pracy",
|
"pbac_desc_manage_workflows": "Pełny dostęp do zarządzania wszystkimi przepływami pracy",
|
||||||
"pbac_desc_create_event_types": "Tworzenie typów wydarzeń",
|
"pbac_desc_create_event_types": "Tworzenie typów wydarzeń",
|
||||||
"pbac_desc_view_event_types": "Wyświetlanie typów wydarzeń",
|
"pbac_desc_view_event_types": "Wyświetlanie typów wydarzeń",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Reunião secreta",
|
"secret_meeting": "Reunião secreta",
|
||||||
"login_instead": "Em vez disso, faça login",
|
"login_instead": "Em vez disso, faça login",
|
||||||
"create_account": "Criar Conta",
|
"create_account": "Criar Conta",
|
||||||
|
"get_started": "Começar",
|
||||||
"confirm_password": "Confirmar senha",
|
"confirm_password": "Confirmar senha",
|
||||||
"reset_your_password": "Defina sua nova senha com as instruções enviadas para seu endereço de e-mail.",
|
"reset_your_password": "Defina sua nova senha com as instruções enviadas para seu endereço de e-mail.",
|
||||||
"org_banner_instructions": "Faça upload de uma imagem com largura {{width}} e altura {{height}} .",
|
"org_banner_instructions": "Faça upload de uma imagem com largura {{width}} e altura {{height}} .",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Cor do Tipo de Evento (Tema Claro)",
|
"light_event_type_color": "Cor do Tipo de Evento (Tema Claro)",
|
||||||
"dark_event_type_color": "Cor do Tipo de Evento (Tema Escuro)",
|
"dark_event_type_color": "Cor do Tipo de Evento (Tema Escuro)",
|
||||||
"file_not_named": "Arquivo não é [idOrSlug]/[user]",
|
"file_not_named": "Arquivo não é [idOrSlug]/[user]",
|
||||||
"create_team": "Criar Time",
|
"create_team": "Criar equipe",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"nameless_team": "Equipe sem nome",
|
"nameless_team": "Equipe sem nome",
|
||||||
"oauth_clients": "Clientes OAuth",
|
"oauth_clients": "Clientes OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Participantes",
|
"attendees": "Participantes",
|
||||||
"add_attendees": "Adicionar Participantes",
|
"add_attendees": "Adicionar Participantes",
|
||||||
"show_advanced_settings": "Mostrar Configurações Avançadas",
|
"show_advanced_settings": "Mostrar Configurações Avançadas",
|
||||||
|
"hide_advanced_settings": "Ocultar configurações avançadas",
|
||||||
"event_name": "Nome do Evento",
|
"event_name": "Nome do Evento",
|
||||||
"event_name_in_calendar": "Nome do evento no calendário",
|
"event_name_in_calendar": "Nome do evento no calendário",
|
||||||
"event_name_tooltip": "O nome que aparecerá nos calendários",
|
"event_name_tooltip": "O nome que aparecerá nos calendários",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Fale com a equipe de vendas",
|
"contact_sales": "Fale com a equipe de vendas",
|
||||||
"error_404": "Erro 404",
|
"error_404": "Erro 404",
|
||||||
"default": "Padrão",
|
"default": "Padrão",
|
||||||
"set_to_default": "Definir para padrão",
|
"set_to_default": "Definir como padrão",
|
||||||
"new_schedule_btn": "Nova agenda",
|
"new_schedule_btn": "Nova agenda",
|
||||||
"add_new_schedule": "Adicionar nova agenda",
|
"add_new_schedule": "Adicionar nova agenda",
|
||||||
"add_new_calendar": "Adicionar um novo calendário",
|
"add_new_calendar": "Adicionar um novo calendário",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Os membros da sua equipe não poderão ver outros membros da equipe após habilitar isso.",
|
"make_team_private_description": "Os membros da sua equipe não poderão ver outros membros da equipe após habilitar isso.",
|
||||||
"you_cannot_see_team_members": "Você não pode ver todos os membros da equipe de uma equipe privada.",
|
"you_cannot_see_team_members": "Você não pode ver todos os membros da equipe de uma equipe privada.",
|
||||||
"you_cannot_see_teams_of_org": "Você não pode equipes de uma organização privada.",
|
"you_cannot_see_teams_of_org": "Você não pode equipes de uma organização privada.",
|
||||||
"allow_booker_to_select_duration": "Permitir que o reservante selecione a duração",
|
"allow_multiple_durations": "Permitir múltiplas durações",
|
||||||
"impersonate_user_tip": "Todos os usos desse recurso são auditados.",
|
"impersonate_user_tip": "Todos os usos desse recurso são auditados.",
|
||||||
"impersonating_user_warning": "Representando o nome de usuário \"{{user}}\".",
|
"impersonating_user_warning": "Representando o nome de usuário \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Clique aqui para parar",
|
"impersonating_stop_instructions": "Clique aqui para parar",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Falhar ao confirmar reserva",
|
"booking_confirmation_failed": "Falhar ao confirmar reserva",
|
||||||
"not_enough_seats": "Sem assentos suficientes",
|
"not_enough_seats": "Sem assentos suficientes",
|
||||||
"form_builder_field_already_exists": "Já existe um campo com este nome",
|
"form_builder_field_already_exists": "Já existe um campo com este nome",
|
||||||
|
"guests_field_must_be_multiemail": "O campo de convidados deve ser do tipo 'Múltiplos e-mails'",
|
||||||
"show_on_booking_page": "Mostrar na página de reservas",
|
"show_on_booking_page": "Mostrar na página de reservas",
|
||||||
"visit_cancelled_booking": "Você pode visitar a página de reserva cancelada",
|
"visit_cancelled_booking": "Você pode visitar a página de reserva cancelada",
|
||||||
"get_started_zapier_templates": "Comece agora com modelos do Zapier",
|
"get_started_zapier_templates": "Comece agora com modelos do Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bloqueado pelo administrador da equipe",
|
"locked_by_team_admin": "Bloqueado pelo administrador da equipe",
|
||||||
"app_not_connected": "Você não conectou uma conta do {{appName}}.",
|
"app_not_connected": "Você não conectou uma conta do {{appName}}.",
|
||||||
"connect_now": "Conectar agora",
|
"connect_now": "Conectar agora",
|
||||||
|
"connect_now_unavailable_tooltip": "Conectar agora está indisponível no momento. Por favor, agende nos horários acima ou tente novamente mais tarde.",
|
||||||
"managed_event_dialog_confirm_button_one": "Substituir e notificar {{count}} membro",
|
"managed_event_dialog_confirm_button_one": "Substituir e notificar {{count}} membro",
|
||||||
"managed_event_dialog_confirm_button_other": "Substituir e notificar {{count}} membros",
|
"managed_event_dialog_confirm_button_other": "Substituir e notificar {{count}} membros",
|
||||||
"managed_event_dialog_title_one": "O url/{{slug}} já existe para {{count}} membro. Deseja substituir?",
|
"managed_event_dialog_title_one": "O url/{{slug}} já existe para {{count}} membro. Deseja substituir?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Ao prosseguir, você concorda com nossos",
|
"proceeding_agreement": "Ao prosseguir, você concorda com nossos",
|
||||||
"terms": "Termos",
|
"terms": "Termos",
|
||||||
"always_show_x_days": "Sempre {{x}} dias disponíveis",
|
"always_show_x_days": "Sempre {{x}} dias disponíveis",
|
||||||
|
"always_show_x_days_description": "Move automaticamente a janela de agendamento de {{x}} dias para frente a cada dia, garantindo que seu calendário sempre mostre os próximos {{x}} dias de horários disponíveis.",
|
||||||
"team_subscription_cancelled_successfully": "Assinatura da equipe cancelada com sucesso",
|
"team_subscription_cancelled_successfully": "Assinatura da equipe cancelada com sucesso",
|
||||||
"team_subscription_cancellation_error": "Erro ao cancelar a assinatura da equipe",
|
"team_subscription_cancellation_error": "Erro ao cancelar a assinatura da equipe",
|
||||||
"unable_to_subscribe_to_the_platform": "Um erro ocorreu ao tentar adicionar ao plano da plataforma, por favor tente novamente mais tarde",
|
"unable_to_subscribe_to_the_platform": "Um erro ocorreu ao tentar adicionar ao plano da plataforma, por favor tente novamente mais tarde",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Termina com",
|
"filter_operator_ends_with": "Termina com",
|
||||||
"filter_operator_is_empty": "Está vazio",
|
"filter_operator_is_empty": "Está vazio",
|
||||||
"filter_operator_not_empty": "Não está vazio",
|
"filter_operator_not_empty": "Não está vazio",
|
||||||
"translate_description_button": "Traduzir título/descrição para o idioma do navegador do visitante usando IA",
|
"translate_description_button": "Traduzir título e descrição automaticamente",
|
||||||
|
"translate_description_button_description": "Traduz automaticamente títulos e descrições para o idioma do navegador do visitante usando IA.",
|
||||||
"rr_distribution_method": "Distribuição",
|
"rr_distribution_method": "Distribuição",
|
||||||
"rr_distribution_method_description": "Permite otimizar a distribuição para máxima disponibilidade ou buscar uma atribuição mais equilibrada.",
|
"rr_distribution_method_description": "Permite otimizar a distribuição para máxima disponibilidade ou buscar uma atribuição mais equilibrada.",
|
||||||
"rr_distribution_method_availability_title": "Maximizar disponibilidade",
|
"rr_distribution_method_availability_title": "Maximizar disponibilidade",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "O recurso de assentos não suporta eventos recorrentes. Desative o recurso de assentos para torná-lo recorrente.",
|
"seats_doesnt_support_recurring": "O recurso de assentos não suporta eventos recorrentes. Desative o recurso de assentos para torná-lo recorrente.",
|
||||||
"recurring_event_seats_error": "Eventos recorrentes não suportam o recurso de assentos. Desative o recurso de assentos ou torne o evento não recorrente.",
|
"recurring_event_seats_error": "Eventos recorrentes não suportam o recurso de assentos. Desative o recurso de assentos ou torne o evento não recorrente.",
|
||||||
"segment": "Segmento",
|
"segment": "Segmento",
|
||||||
|
"saved_filters": "Filtros salvos",
|
||||||
"rename": "Renomear",
|
"rename": "Renomear",
|
||||||
"rename_segment": "Renomear segmento",
|
"rename_segment": "Renomear segmento",
|
||||||
"delete_segment": "Excluir segmento",
|
"delete_segment": "Excluir segmento",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista de seleção",
|
"picklist": "Lista de seleção",
|
||||||
"most_cancelled_bookings": "Mais cancelamentos",
|
"most_cancelled_bookings": "Mais cancelamentos",
|
||||||
"interface_language": "Idioma da interface",
|
"interface_language": "Idioma da interface",
|
||||||
|
"interface_language_description": "Defina seu idioma preferido para a interface de agendamento",
|
||||||
"visitors_browser_language": "Idioma do navegador do visitante",
|
"visitors_browser_language": "Idioma do navegador do visitante",
|
||||||
"name_or_email": "Nome ou e-mail",
|
"name_or_email": "Nome ou e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se nenhum contato for encontrado, recorrer ao proprietário do lead, se existir",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se nenhum contato for encontrado, recorrer ao proprietário do lead, se existir",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editar e modificar configurações de fluxo de trabalho",
|
"pbac_desc_update_workflows": "Editar e modificar configurações de fluxo de trabalho",
|
||||||
"pbac_desc_delete_workflows": "Remover fluxos de trabalho do sistema",
|
"pbac_desc_delete_workflows": "Remover fluxos de trabalho do sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilidade",
|
||||||
|
"pbac_resource_out_of_office": "Ausente",
|
||||||
"pbac_desc_create_webhooks": "Criar webhooks",
|
"pbac_desc_create_webhooks": "Criar webhooks",
|
||||||
"pbac_desc_view_webhooks": "Visualizar webhooks",
|
"pbac_desc_view_webhooks": "Visualizar webhooks",
|
||||||
"pbac_desc_update_webhooks": "Atualizar webhooks",
|
"pbac_desc_update_webhooks": "Atualizar webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Excluir webhooks",
|
"pbac_desc_delete_webhooks": "Excluir webhooks",
|
||||||
|
"pbac_desc_create_availability": "Criar disponibilidade",
|
||||||
|
"pbac_desc_view_availability": "Visualizar disponibilidade",
|
||||||
|
"pbac_desc_update_availability": "Atualizar disponibilidade",
|
||||||
|
"pbac_desc_delete_availability": "Excluir disponibilidade",
|
||||||
|
"pbac_desc_create_out_of_office": "Criar ausência",
|
||||||
|
"pbac_desc_view_out_of_office": "Visualizar ausência",
|
||||||
|
"pbac_desc_update_out_of_office": "Atualizar ausência",
|
||||||
|
"pbac_desc_delete_out_of_office": "Excluir ausência",
|
||||||
"pbac_desc_manage_workflows": "Acesso completo de gerenciamento a todos os fluxos de trabalho",
|
"pbac_desc_manage_workflows": "Acesso completo de gerenciamento a todos os fluxos de trabalho",
|
||||||
"pbac_desc_create_event_types": "Criar tipos de eventos",
|
"pbac_desc_create_event_types": "Criar tipos de eventos",
|
||||||
"pbac_desc_view_event_types": "Ver tipos de eventos",
|
"pbac_desc_view_event_types": "Ver tipos de eventos",
|
||||||
@@ -3560,7 +3578,7 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "O balanceamento de carga não está disponível com Grupos Round Robin",
|
"rr_load_balancing_disabled_with_groups": "O balanceamento de carga não está disponível com Grupos Round Robin",
|
||||||
"round_robin_settings_description": "Personalize as configurações padrão de rodízio para esta equipe",
|
"round_robin_settings_description": "Personalize as configurações padrão de rodízio para esta equipe",
|
||||||
"round_robin_settings_updated_successfully": "Configurações de rodízio atualizadas com sucesso",
|
"round_robin_settings_updated_successfully": "Configurações de rodízio atualizadas com sucesso",
|
||||||
"booking_creation_time": "Horário de criação da reserva",
|
"booking_creation_time": "Horário de criação do agendamento",
|
||||||
"meeting_start_time": "Horário de início da reunião",
|
"meeting_start_time": "Horário de início da reunião",
|
||||||
"members_affected_by_disabling_delegation_credential": "Membros afetados",
|
"members_affected_by_disabling_delegation_credential": "Membros afetados",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Nenhum membro afetado pela desativação da credencial de delegação",
|
"no_members_affected_by_disabling_delegation_credential": "Nenhum membro afetado pela desativação da credencial de delegação",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Reunião Secreta",
|
"secret_meeting": "Reunião Secreta",
|
||||||
"login_instead": "Inicie sessão em alternativa",
|
"login_instead": "Inicie sessão em alternativa",
|
||||||
"create_account": "Criar Conta",
|
"create_account": "Criar Conta",
|
||||||
|
"get_started": "Começar",
|
||||||
"confirm_password": "Confirmar senha",
|
"confirm_password": "Confirmar senha",
|
||||||
"reset_your_password": "Defina a sua nova palavra-passe seguindo as instruções enviadas para o seu endereço de e-mail.",
|
"reset_your_password": "Defina a sua nova palavra-passe seguindo as instruções enviadas para o seu endereço de e-mail.",
|
||||||
"org_banner_instructions": "Carregue uma imagem com {{width}} de largura e {{height}} de altura.",
|
"org_banner_instructions": "Carregue uma imagem com {{width}} de largura e {{height}} de altura.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Cor do Tipo de Evento (Tema Claro)",
|
"light_event_type_color": "Cor do Tipo de Evento (Tema Claro)",
|
||||||
"dark_event_type_color": "Cor do Tipo de Evento (Tema Escuro)",
|
"dark_event_type_color": "Cor do Tipo de Evento (Tema Escuro)",
|
||||||
"file_not_named": "Ficheiro não é [idOrSlug]/[user]",
|
"file_not_named": "Ficheiro não é [idOrSlug]/[user]",
|
||||||
"create_team": "Criar Equipa",
|
"create_team": "Criar equipa",
|
||||||
"name": "Nome",
|
"name": "Nome",
|
||||||
"nameless_team": "Equipe sem nome",
|
"nameless_team": "Equipe sem nome",
|
||||||
"oauth_clients": "Clientes OAuth",
|
"oauth_clients": "Clientes OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Participantes",
|
"attendees": "Participantes",
|
||||||
"add_attendees": "Adicionar Participantes",
|
"add_attendees": "Adicionar Participantes",
|
||||||
"show_advanced_settings": "Mostrar Configurações Avançadas",
|
"show_advanced_settings": "Mostrar Configurações Avançadas",
|
||||||
|
"hide_advanced_settings": "Ocultar configurações avançadas",
|
||||||
"event_name": "Nome do Evento",
|
"event_name": "Nome do Evento",
|
||||||
"event_name_in_calendar": "Nome do evento no calendário",
|
"event_name_in_calendar": "Nome do evento no calendário",
|
||||||
"event_name_tooltip": "O nome que será mostrado nos calendários",
|
"event_name_tooltip": "O nome que será mostrado nos calendários",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Contacte as vendas",
|
"contact_sales": "Contacte as vendas",
|
||||||
"error_404": "Erro 404",
|
"error_404": "Erro 404",
|
||||||
"default": "Por omissão",
|
"default": "Por omissão",
|
||||||
"set_to_default": "Definir por omissão",
|
"set_to_default": "Definir como padrão",
|
||||||
"new_schedule_btn": "Novo horário",
|
"new_schedule_btn": "Novo horário",
|
||||||
"add_new_schedule": "Adicionar novo horário",
|
"add_new_schedule": "Adicionar novo horário",
|
||||||
"add_new_calendar": "Adicionar um novo calendário",
|
"add_new_calendar": "Adicionar um novo calendário",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Os membros da sua equipa não poderão ver outros membros da equipa quando esta opção estiver ativada.",
|
"make_team_private_description": "Os membros da sua equipa não poderão ver outros membros da equipa quando esta opção estiver ativada.",
|
||||||
"you_cannot_see_team_members": "Não pode ver todos os membros da equipa de uma equipa privada.",
|
"you_cannot_see_team_members": "Não pode ver todos os membros da equipa de uma equipa privada.",
|
||||||
"you_cannot_see_teams_of_org": "Não pode ver as equipas de uma organização privada.",
|
"you_cannot_see_teams_of_org": "Não pode ver as equipas de uma organização privada.",
|
||||||
"allow_booker_to_select_duration": "Permitir que o responsável pela reserva selecione a duração",
|
"allow_multiple_durations": "Permitir múltiplas durações",
|
||||||
"impersonate_user_tip": "Todas as utilizações desta funcionalidade são auditadas.",
|
"impersonate_user_tip": "Todas as utilizações desta funcionalidade são auditadas.",
|
||||||
"impersonating_user_warning": "Representar o nome de utilizador \"{{user}}\".",
|
"impersonating_user_warning": "Representar o nome de utilizador \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Clique aqui para parar",
|
"impersonating_stop_instructions": "Clique aqui para parar",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "A confirmação da reserva falhou",
|
"booking_confirmation_failed": "A confirmação da reserva falhou",
|
||||||
"not_enough_seats": "Não existem lugares suficientes",
|
"not_enough_seats": "Não existem lugares suficientes",
|
||||||
"form_builder_field_already_exists": "Já existe um campo com este nome",
|
"form_builder_field_already_exists": "Já existe um campo com este nome",
|
||||||
|
"guests_field_must_be_multiemail": "O campo de convidados deve ser do tipo 'Múltiplos emails'",
|
||||||
"show_on_booking_page": "Mostrar na página de reservas",
|
"show_on_booking_page": "Mostrar na página de reservas",
|
||||||
"visit_cancelled_booking": "Pode visitar a página da reserva cancelada",
|
"visit_cancelled_booking": "Pode visitar a página da reserva cancelada",
|
||||||
"get_started_zapier_templates": "Comece a utilizar os modelos Zapier",
|
"get_started_zapier_templates": "Comece a utilizar os modelos Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bloqueado pelo administrador da equipa",
|
"locked_by_team_admin": "Bloqueado pelo administrador da equipa",
|
||||||
"app_not_connected": "Não associou uma conta {{appName}}.",
|
"app_not_connected": "Não associou uma conta {{appName}}.",
|
||||||
"connect_now": "Associar agora",
|
"connect_now": "Associar agora",
|
||||||
|
"connect_now_unavailable_tooltip": "Conectar agora está indisponível. Por favor, reserve nos horários acima ou tente novamente mais tarde.",
|
||||||
"managed_event_dialog_confirm_button_one": "Substituir e notificar {{count}} membro",
|
"managed_event_dialog_confirm_button_one": "Substituir e notificar {{count}} membro",
|
||||||
"managed_event_dialog_confirm_button_other": "Substituir e notificar {{count}} membros",
|
"managed_event_dialog_confirm_button_other": "Substituir e notificar {{count}} membros",
|
||||||
"managed_event_dialog_title_one": "A URL /{{slug}} já existe para {{count}} membro. Pretende substituir a mesma?",
|
"managed_event_dialog_title_one": "A URL /{{slug}} já existe para {{count}} membro. Pretende substituir a mesma?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Empresa",
|
"enterprise": "Empresa",
|
||||||
"enterprise_description": "Atualize para Enterprise para criar a sua Organização",
|
"enterprise_description": "Atualize para Enterprise para criar a sua Organização",
|
||||||
"create_your_org": "Crie a sua Organização",
|
"create_your_org": "Crie a sua Organização",
|
||||||
"create_org": "Criar Organização",
|
"create_org": "Criar organização",
|
||||||
"create_your_org_description": "Atualize para Organizations e receba um subdomínio, faturação unificada, Insights, personalização abrangente da imagem corporativa e muito mais",
|
"create_your_org_description": "Atualize para Organizations e receba um subdomínio, faturação unificada, Insights, personalização abrangente da imagem corporativa e muito mais",
|
||||||
"create_your_enterprise_description": "Atualize para Enterprise e tenha acesso à sincronização com Active Directory, aprovisionamento automático de utilizadores com SCIM, agentes de voz Cal.ai, API de Administração e muito mais!",
|
"create_your_enterprise_description": "Atualize para Enterprise e tenha acesso à sincronização com Active Directory, aprovisionamento automático de utilizadores com SCIM, agentes de voz Cal.ai, API de Administração e muito mais!",
|
||||||
"other_payment_app_enabled": "Só pode ativar uma aplicação de pagamento por tipo de evento",
|
"other_payment_app_enabled": "Só pode ativar uma aplicação de pagamento por tipo de evento",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Ao prosseguir, você concorda com nossos",
|
"proceeding_agreement": "Ao prosseguir, você concorda com nossos",
|
||||||
"terms": "Termos",
|
"terms": "Termos",
|
||||||
"always_show_x_days": "Sempre {{x}} dias disponíveis",
|
"always_show_x_days": "Sempre {{x}} dias disponíveis",
|
||||||
|
"always_show_x_days_description": "Move automaticamente a janela de reservas de {{x}} dias para frente a cada dia, garantindo que seu calendário sempre mostre os próximos {{x}} dias de horários disponíveis.",
|
||||||
"team_subscription_cancelled_successfully": "Assinatura da equipe cancelada com sucesso",
|
"team_subscription_cancelled_successfully": "Assinatura da equipe cancelada com sucesso",
|
||||||
"team_subscription_cancellation_error": "Erro ao cancelar a assinatura da equipe",
|
"team_subscription_cancellation_error": "Erro ao cancelar a assinatura da equipe",
|
||||||
"unable_to_subscribe_to_the_platform": "Ocorreu um erro ao tentar subscrever o plano Platform. Tente novamente mais tarde",
|
"unable_to_subscribe_to_the_platform": "Ocorreu um erro ao tentar subscrever o plano Platform. Tente novamente mais tarde",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Termina com",
|
"filter_operator_ends_with": "Termina com",
|
||||||
"filter_operator_is_empty": "Está vazio",
|
"filter_operator_is_empty": "Está vazio",
|
||||||
"filter_operator_not_empty": "Não está vazio",
|
"filter_operator_not_empty": "Não está vazio",
|
||||||
"translate_description_button": "Traduzir título/descrição para o idioma do navegador do visitante usando IA",
|
"translate_description_button": "Traduzir automaticamente título e descrição",
|
||||||
|
"translate_description_button_description": "Traduzir automaticamente títulos e descrições para o idioma do navegador do visitante usando IA.",
|
||||||
"rr_distribution_method": "Distribuição",
|
"rr_distribution_method": "Distribuição",
|
||||||
"rr_distribution_method_description": "Permite otimizar a distribuição para máxima disponibilidade ou para buscar uma atribuição mais equilibrada.",
|
"rr_distribution_method_description": "Permite otimizar a distribuição para máxima disponibilidade ou para buscar uma atribuição mais equilibrada.",
|
||||||
"rr_distribution_method_availability_title": "Maximizar disponibilidade",
|
"rr_distribution_method_availability_title": "Maximizar disponibilidade",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "O recurso de assentos não suporta eventos recorrentes. Desative o recurso de assentos para torná-lo recorrente.",
|
"seats_doesnt_support_recurring": "O recurso de assentos não suporta eventos recorrentes. Desative o recurso de assentos para torná-lo recorrente.",
|
||||||
"recurring_event_seats_error": "Eventos recorrentes não suportam o recurso de assentos. Desative o recurso de assentos ou torne o evento não recorrente.",
|
"recurring_event_seats_error": "Eventos recorrentes não suportam o recurso de assentos. Desative o recurso de assentos ou torne o evento não recorrente.",
|
||||||
"segment": "Segmento",
|
"segment": "Segmento",
|
||||||
|
"saved_filters": "Filtros salvos",
|
||||||
"rename": "Renomear",
|
"rename": "Renomear",
|
||||||
"rename_segment": "Renomear segmento",
|
"rename_segment": "Renomear segmento",
|
||||||
"delete_segment": "Excluir segmento",
|
"delete_segment": "Excluir segmento",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista de seleção",
|
"picklist": "Lista de seleção",
|
||||||
"most_cancelled_bookings": "Mais cancelamentos",
|
"most_cancelled_bookings": "Mais cancelamentos",
|
||||||
"interface_language": "Idioma da interface",
|
"interface_language": "Idioma da interface",
|
||||||
|
"interface_language_description": "Defina seu idioma preferido para a interface de reservas",
|
||||||
"visitors_browser_language": "Idioma do navegador do visitante",
|
"visitors_browser_language": "Idioma do navegador do visitante",
|
||||||
"name_or_email": "Nome ou e-mail",
|
"name_or_email": "Nome ou e-mail",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se nenhum contato for encontrado, recorrer ao proprietário do lead, se existir",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Se nenhum contato for encontrado, recorrer ao proprietário do lead, se existir",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editar e modificar configurações de fluxo de trabalho",
|
"pbac_desc_update_workflows": "Editar e modificar configurações de fluxo de trabalho",
|
||||||
"pbac_desc_delete_workflows": "Remover fluxos de trabalho do sistema",
|
"pbac_desc_delete_workflows": "Remover fluxos de trabalho do sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilidade",
|
||||||
|
"pbac_resource_out_of_office": "Fora do escritório",
|
||||||
"pbac_desc_create_webhooks": "Criar webhooks",
|
"pbac_desc_create_webhooks": "Criar webhooks",
|
||||||
"pbac_desc_view_webhooks": "Visualizar webhooks",
|
"pbac_desc_view_webhooks": "Visualizar webhooks",
|
||||||
"pbac_desc_update_webhooks": "Atualizar webhooks",
|
"pbac_desc_update_webhooks": "Atualizar webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Eliminar webhooks",
|
"pbac_desc_delete_webhooks": "Eliminar webhooks",
|
||||||
|
"pbac_desc_create_availability": "Criar disponibilidade",
|
||||||
|
"pbac_desc_view_availability": "Ver disponibilidade",
|
||||||
|
"pbac_desc_update_availability": "Atualizar disponibilidade",
|
||||||
|
"pbac_desc_delete_availability": "Excluir disponibilidade",
|
||||||
|
"pbac_desc_create_out_of_office": "Criar fora do escritório",
|
||||||
|
"pbac_desc_view_out_of_office": "Ver fora do escritório",
|
||||||
|
"pbac_desc_update_out_of_office": "Atualizar fora do escritório",
|
||||||
|
"pbac_desc_delete_out_of_office": "Excluir fora do escritório",
|
||||||
"pbac_desc_manage_workflows": "Acesso completo de gerenciamento a todos os fluxos de trabalho",
|
"pbac_desc_manage_workflows": "Acesso completo de gerenciamento a todos os fluxos de trabalho",
|
||||||
"pbac_desc_create_event_types": "Criar tipos de evento",
|
"pbac_desc_create_event_types": "Criar tipos de evento",
|
||||||
"pbac_desc_view_event_types": "Ver tipos de evento",
|
"pbac_desc_view_event_types": "Ver tipos de evento",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Întâlnire secretă",
|
"secret_meeting": "Întâlnire secretă",
|
||||||
"login_instead": "Autentifică-te în schimb",
|
"login_instead": "Autentifică-te în schimb",
|
||||||
"create_account": "Creează un cont",
|
"create_account": "Creează un cont",
|
||||||
|
"get_started": "Începeți",
|
||||||
"confirm_password": "Confirmă parola",
|
"confirm_password": "Confirmă parola",
|
||||||
"reset_your_password": "Setați noua parolă cu instrucțiunile trimise la adresa dvs. de e-mail.",
|
"reset_your_password": "Setați noua parolă cu instrucțiunile trimise la adresa dvs. de e-mail.",
|
||||||
"org_banner_instructions": "Te rugăm să încarci o imagine de {{width}} lățime și {{height}} înălțime.",
|
"org_banner_instructions": "Te rugăm să încarci o imagine de {{width}} lățime și {{height}} înălțime.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Culoare tip eveniment (Tema deschisă)",
|
"light_event_type_color": "Culoare tip eveniment (Tema deschisă)",
|
||||||
"dark_event_type_color": "Culoare tip eveniment (Tema închisă)",
|
"dark_event_type_color": "Culoare tip eveniment (Tema închisă)",
|
||||||
"file_not_named": "Fișierul nu este numit [idOrSlug]/[user]",
|
"file_not_named": "Fișierul nu este numit [idOrSlug]/[user]",
|
||||||
"create_team": "Creează o echipă",
|
"create_team": "Creați echipă",
|
||||||
"name": "Nume",
|
"name": "Nume",
|
||||||
"nameless_team": "Echipă fără nume",
|
"nameless_team": "Echipă fără nume",
|
||||||
"oauth_clients": "Clienți OAuth",
|
"oauth_clients": "Clienți OAuth",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Participanții",
|
"attendees": "Participanții",
|
||||||
"add_attendees": "Adăugați participanți",
|
"add_attendees": "Adăugați participanți",
|
||||||
"show_advanced_settings": "Afișează setările avansate",
|
"show_advanced_settings": "Afișează setările avansate",
|
||||||
|
"hide_advanced_settings": "Ascundeți setările avansate",
|
||||||
"event_name": "Denumirea evenimentului",
|
"event_name": "Denumirea evenimentului",
|
||||||
"event_name_in_calendar": "Denumirea evenimentului în calendar",
|
"event_name_in_calendar": "Denumirea evenimentului în calendar",
|
||||||
"event_name_tooltip": "Numele care va apărea în calendare",
|
"event_name_tooltip": "Numele care va apărea în calendare",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Contactați departamentul de vânzări",
|
"contact_sales": "Contactați departamentul de vânzări",
|
||||||
"error_404": "Eroare 404",
|
"error_404": "Eroare 404",
|
||||||
"default": "Implicit",
|
"default": "Implicit",
|
||||||
"set_to_default": "Setare ca implicit",
|
"set_to_default": "Setați ca implicit",
|
||||||
"new_schedule_btn": "Program nou",
|
"new_schedule_btn": "Program nou",
|
||||||
"add_new_schedule": "Adăugați un program nou",
|
"add_new_schedule": "Adăugați un program nou",
|
||||||
"add_new_calendar": "Adăugați un calendar nou",
|
"add_new_calendar": "Adăugați un calendar nou",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Membrii echipei dvs. nu vor putea vedea ceilalți membrii ai echipei atunci când această opțiune este activată.",
|
"make_team_private_description": "Membrii echipei dvs. nu vor putea vedea ceilalți membrii ai echipei atunci când această opțiune este activată.",
|
||||||
"you_cannot_see_team_members": "Nu puteți vedea toți membrii unei echipe private.",
|
"you_cannot_see_team_members": "Nu puteți vedea toți membrii unei echipe private.",
|
||||||
"you_cannot_see_teams_of_org": "Nu poți vedea echipele unei organizații private.",
|
"you_cannot_see_teams_of_org": "Nu poți vedea echipele unei organizații private.",
|
||||||
"allow_booker_to_select_duration": "Permite persoanei care face rezervarea să selecteze durata",
|
"allow_multiple_durations": "Permiteți durate multiple",
|
||||||
"impersonate_user_tip": "Toate utilizările acestei caracteristici sunt verificate.",
|
"impersonate_user_tip": "Toate utilizările acestei caracteristici sunt verificate.",
|
||||||
"impersonating_user_warning": "Reprezentare nume de utilizator „{{user}}”.",
|
"impersonating_user_warning": "Reprezentare nume de utilizator „{{user}}”.",
|
||||||
"impersonating_stop_instructions": "Fă clic aici pentru a înceta",
|
"impersonating_stop_instructions": "Fă clic aici pentru a înceta",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Confirmare rezervare nereușită",
|
"booking_confirmation_failed": "Confirmare rezervare nereușită",
|
||||||
"not_enough_seats": "Nu sunt suficiente locuri",
|
"not_enough_seats": "Nu sunt suficiente locuri",
|
||||||
"form_builder_field_already_exists": "Există deja un câmp cu acest nume",
|
"form_builder_field_already_exists": "Există deja un câmp cu acest nume",
|
||||||
|
"guests_field_must_be_multiemail": "Câmpul pentru invitați trebuie să fie de tipul 'Emailuri multiple'",
|
||||||
"show_on_booking_page": "Afișare pe pagina de rezervare",
|
"show_on_booking_page": "Afișare pe pagina de rezervare",
|
||||||
"visit_cancelled_booking": "Puteți vizita pagina rezervării anulate",
|
"visit_cancelled_booking": "Puteți vizita pagina rezervării anulate",
|
||||||
"get_started_zapier_templates": "Faceți primii pași cu șabloanele Zapier",
|
"get_started_zapier_templates": "Faceți primii pași cu șabloanele Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Blocat de administratorul echipei",
|
"locked_by_team_admin": "Blocat de administratorul echipei",
|
||||||
"app_not_connected": "Nu ați conectat un cont {{appName}}.",
|
"app_not_connected": "Nu ați conectat un cont {{appName}}.",
|
||||||
"connect_now": "Conectați-l acum",
|
"connect_now": "Conectați-l acum",
|
||||||
|
"connect_now_unavailable_tooltip": "Conectarea acum nu este disponibilă momentan. Vă rugăm să rezervați din sloturile de mai sus sau să încercați din nou mai târziu.",
|
||||||
"managed_event_dialog_confirm_button_one": "Înlocuiți și anunțați {{count}} membru",
|
"managed_event_dialog_confirm_button_one": "Înlocuiți și anunțați {{count}} membru",
|
||||||
"managed_event_dialog_confirm_button_other": "Înlocuiți și anunțați {{count}} (de) membri",
|
"managed_event_dialog_confirm_button_other": "Înlocuiți și anunțați {{count}} (de) membri",
|
||||||
"managed_event_dialog_title_one": "URL-ul /{{slug}} există deja pentru {{count}} membru. Doriți să îl înlocuiți?",
|
"managed_event_dialog_title_one": "URL-ul /{{slug}} există deja pentru {{count}} membru. Doriți să îl înlocuiți?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Întreprindere",
|
"enterprise": "Întreprindere",
|
||||||
"enterprise_description": "Faceți upgrade la Enterprise pentru a crea organizația dvs.",
|
"enterprise_description": "Faceți upgrade la Enterprise pentru a crea organizația dvs.",
|
||||||
"create_your_org": "Creați-vă Organizația",
|
"create_your_org": "Creați-vă Organizația",
|
||||||
"create_org": "Creează Organizație",
|
"create_org": "Creați organizație",
|
||||||
"create_your_org_description": "Faceți upgrade la Organizații și primiți un subdomeniu, facturare unificată, Insights, whitelabeling extins și altele",
|
"create_your_org_description": "Faceți upgrade la Organizații și primiți un subdomeniu, facturare unificată, Insights, whitelabeling extins și altele",
|
||||||
"create_your_enterprise_description": "Faceți upgrade la Enterprise și obțineți acces la Active Directory Sync, SCIM Automatic User provisioning, Cal.ai Voice Agents, Admin APIs și altele!",
|
"create_your_enterprise_description": "Faceți upgrade la Enterprise și obțineți acces la Active Directory Sync, SCIM Automatic User provisioning, Cal.ai Voice Agents, Admin APIs și altele!",
|
||||||
"other_payment_app_enabled": "Puteți activa o singură aplicație de plată per tip de eveniment",
|
"other_payment_app_enabled": "Puteți activa o singură aplicație de plată per tip de eveniment",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Continuând, sunteți de acord cu",
|
"proceeding_agreement": "Continuând, sunteți de acord cu",
|
||||||
"terms": "Termenii",
|
"terms": "Termenii",
|
||||||
"always_show_x_days": "Întotdeauna {{x}} zile disponibile",
|
"always_show_x_days": "Întotdeauna {{x}} zile disponibile",
|
||||||
|
"always_show_x_days_description": "Mută automat fereastra de rezervare de {{x}} zile înainte în fiecare zi, asigurându-se că calendarul dumneavoastră arată mereu următoarele {{x}} zile de sloturi disponibile.",
|
||||||
"team_subscription_cancelled_successfully": "Abonamentul echipei a fost anulat cu succes",
|
"team_subscription_cancelled_successfully": "Abonamentul echipei a fost anulat cu succes",
|
||||||
"team_subscription_cancellation_error": "Eroare la anularea abonamentului echipei",
|
"team_subscription_cancellation_error": "Eroare la anularea abonamentului echipei",
|
||||||
"unable_to_subscribe_to_the_platform": "A apărut o eroare în timpul încercării de a te abona la planul platformei, te rugăm să încerci din nou mai târziu",
|
"unable_to_subscribe_to_the_platform": "A apărut o eroare în timpul încercării de a te abona la planul platformei, te rugăm să încerci din nou mai târziu",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Se termină cu",
|
"filter_operator_ends_with": "Se termină cu",
|
||||||
"filter_operator_is_empty": "Este gol",
|
"filter_operator_is_empty": "Este gol",
|
||||||
"filter_operator_not_empty": "Nu este gol",
|
"filter_operator_not_empty": "Nu este gol",
|
||||||
"translate_description_button": "Traduce titlul/descrierea în limba browserului vizitatorului folosind AI",
|
"translate_description_button": "Traduceți automat titlul și descrierea",
|
||||||
|
"translate_description_button_description": "Traduceți automat titlurile și descrierile în limba browserului vizitatorului folosind AI.",
|
||||||
"rr_distribution_method": "Distribuție",
|
"rr_distribution_method": "Distribuție",
|
||||||
"rr_distribution_method_description": "Permite optimizarea distribuției pentru disponibilitate maximă sau pentru o alocare mai echilibrată.",
|
"rr_distribution_method_description": "Permite optimizarea distribuției pentru disponibilitate maximă sau pentru o alocare mai echilibrată.",
|
||||||
"rr_distribution_method_availability_title": "Maximizează disponibilitatea",
|
"rr_distribution_method_availability_title": "Maximizează disponibilitatea",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Funcția de locuri nu acceptă evenimentele recurente. Dezactivați funcția de locuri pentru a face evenimentul recurent.",
|
"seats_doesnt_support_recurring": "Funcția de locuri nu acceptă evenimentele recurente. Dezactivați funcția de locuri pentru a face evenimentul recurent.",
|
||||||
"recurring_event_seats_error": "Evenimentul recurent nu acceptă funcția de locuri. Dezactivați funcția de locuri sau faceți evenimentul nerecurent.",
|
"recurring_event_seats_error": "Evenimentul recurent nu acceptă funcția de locuri. Dezactivați funcția de locuri sau faceți evenimentul nerecurent.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Filtre salvate",
|
||||||
"rename": "Redenumește",
|
"rename": "Redenumește",
|
||||||
"rename_segment": "Redenumește segmentul",
|
"rename_segment": "Redenumește segmentul",
|
||||||
"delete_segment": "Șterge segmentul",
|
"delete_segment": "Șterge segmentul",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Listă de selecție",
|
"picklist": "Listă de selecție",
|
||||||
"most_cancelled_bookings": "Cele mai multe rezervări anulate",
|
"most_cancelled_bookings": "Cele mai multe rezervări anulate",
|
||||||
"interface_language": "Limba interfeței",
|
"interface_language": "Limba interfeței",
|
||||||
|
"interface_language_description": "Setați limba preferată pentru interfața de rezervare",
|
||||||
"visitors_browser_language": "Limba browserului vizitatorului",
|
"visitors_browser_language": "Limba browserului vizitatorului",
|
||||||
"name_or_email": "Nume sau email",
|
"name_or_email": "Nume sau email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Dacă nu este găsit niciun contact, se revine la proprietarul lead-ului dacă acesta există",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Dacă nu este găsit niciun contact, se revine la proprietarul lead-ului dacă acesta există",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Editați și modificați setările fluxurilor de lucru",
|
"pbac_desc_update_workflows": "Editați și modificați setările fluxurilor de lucru",
|
||||||
"pbac_desc_delete_workflows": "Eliminați fluxurile de lucru din sistem",
|
"pbac_desc_delete_workflows": "Eliminați fluxurile de lucru din sistem",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Disponibilitate",
|
||||||
|
"pbac_resource_out_of_office": "În afara biroului",
|
||||||
"pbac_desc_create_webhooks": "Creează webhooks",
|
"pbac_desc_create_webhooks": "Creează webhooks",
|
||||||
"pbac_desc_view_webhooks": "Vizualizează webhooks",
|
"pbac_desc_view_webhooks": "Vizualizează webhooks",
|
||||||
"pbac_desc_update_webhooks": "Actualizează webhooks",
|
"pbac_desc_update_webhooks": "Actualizează webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Șterge webhooks",
|
"pbac_desc_delete_webhooks": "Șterge webhooks",
|
||||||
|
"pbac_desc_create_availability": "Creați disponibilitate",
|
||||||
|
"pbac_desc_view_availability": "Vizualizați disponibilitatea",
|
||||||
|
"pbac_desc_update_availability": "Actualizați disponibilitatea",
|
||||||
|
"pbac_desc_delete_availability": "Ștergeți disponibilitatea",
|
||||||
|
"pbac_desc_create_out_of_office": "Creați absență de la birou",
|
||||||
|
"pbac_desc_view_out_of_office": "Vizualizați absența de la birou",
|
||||||
|
"pbac_desc_update_out_of_office": "Actualizați absența de la birou",
|
||||||
|
"pbac_desc_delete_out_of_office": "Ștergeți absența de la birou",
|
||||||
"pbac_desc_manage_workflows": "Acces complet de gestionare pentru toate fluxurile de lucru",
|
"pbac_desc_manage_workflows": "Acces complet de gestionare pentru toate fluxurile de lucru",
|
||||||
"pbac_desc_create_event_types": "Creați tipuri de evenimente",
|
"pbac_desc_create_event_types": "Creați tipuri de evenimente",
|
||||||
"pbac_desc_view_event_types": "Vizualizați tipuri de evenimente",
|
"pbac_desc_view_event_types": "Vizualizați tipuri de evenimente",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Секретная встреча",
|
"secret_meeting": "Секретная встреча",
|
||||||
"login_instead": "Вход",
|
"login_instead": "Вход",
|
||||||
"create_account": "Создать аккаунт",
|
"create_account": "Создать аккаунт",
|
||||||
|
"get_started": "Начать",
|
||||||
"confirm_password": "Подтвердите пароль",
|
"confirm_password": "Подтвердите пароль",
|
||||||
"reset_your_password": "Задайте новый пароль. Инструкции о том, как это сделать, мы отправили на ваш адрес электронной почты.",
|
"reset_your_password": "Задайте новый пароль. Инструкции о том, как это сделать, мы отправили на ваш адрес электронной почты.",
|
||||||
"org_banner_instructions": "Пожалуйста, загрузите изображение шириной {{width}} и высотой {{height}}.",
|
"org_banner_instructions": "Пожалуйста, загрузите изображение шириной {{width}} и высотой {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Участники",
|
"attendees": "Участники",
|
||||||
"add_attendees": "Добавить участников",
|
"add_attendees": "Добавить участников",
|
||||||
"show_advanced_settings": "Показать дополнительные параметры",
|
"show_advanced_settings": "Показать дополнительные параметры",
|
||||||
|
"hide_advanced_settings": "Скрыть расширенные настройки",
|
||||||
"event_name": "Название события",
|
"event_name": "Название события",
|
||||||
"event_name_in_calendar": "Название события в календаре",
|
"event_name_in_calendar": "Название события в календаре",
|
||||||
"event_name_tooltip": "Название, которое будет отображаться в календарях",
|
"event_name_tooltip": "Название, которое будет отображаться в календарях",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Связаться с отделом продаж",
|
"contact_sales": "Связаться с отделом продаж",
|
||||||
"error_404": "Ошибка 404",
|
"error_404": "Ошибка 404",
|
||||||
"default": "По умолчанию",
|
"default": "По умолчанию",
|
||||||
"set_to_default": "Использовать по умолчанию",
|
"set_to_default": "Установить по умолчанию",
|
||||||
"new_schedule_btn": "Новое расписание",
|
"new_schedule_btn": "Новое расписание",
|
||||||
"add_new_schedule": "Добавить новое расписание",
|
"add_new_schedule": "Добавить новое расписание",
|
||||||
"add_new_calendar": "Добавить новый календарь",
|
"add_new_calendar": "Добавить новый календарь",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Если эта опция включена, участники вашей команды не смогут видеть других участников команды.",
|
"make_team_private_description": "Если эта опция включена, участники вашей команды не смогут видеть других участников команды.",
|
||||||
"you_cannot_see_team_members": "Вы не можете видеть список участников закрытой команды.",
|
"you_cannot_see_team_members": "Вы не можете видеть список участников закрытой команды.",
|
||||||
"you_cannot_see_teams_of_org": "Вы не можете видеть команды приватной организации.",
|
"you_cannot_see_teams_of_org": "Вы не можете видеть команды приватной организации.",
|
||||||
"allow_booker_to_select_duration": "Разрешить пользователю, оформляющему бронирование, выбирать длительность",
|
"allow_multiple_durations": "Разрешить несколько длительностей",
|
||||||
"impersonate_user_tip": "Все варианты использования этой функции проверяются.",
|
"impersonate_user_tip": "Все варианты использования этой функции проверяются.",
|
||||||
"impersonating_user_warning": "Олицетворение пользователя \"{{user}}\".",
|
"impersonating_user_warning": "Олицетворение пользователя \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Нажмите здесь, чтобы закончить",
|
"impersonating_stop_instructions": "Нажмите здесь, чтобы закончить",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Не удалось подтвердить бронирование",
|
"booking_confirmation_failed": "Не удалось подтвердить бронирование",
|
||||||
"not_enough_seats": "Недостаточно мест",
|
"not_enough_seats": "Недостаточно мест",
|
||||||
"form_builder_field_already_exists": "Поле с таким именем уже существует",
|
"form_builder_field_already_exists": "Поле с таким именем уже существует",
|
||||||
|
"guests_field_must_be_multiemail": "Поле гостей должно быть типа 'Несколько адресов электронной почты'",
|
||||||
"show_on_booking_page": "Показывать на странице бронирования",
|
"show_on_booking_page": "Показывать на странице бронирования",
|
||||||
"visit_cancelled_booking": "Вы можете перейти на страницу отмененной встречи",
|
"visit_cancelled_booking": "Вы можете перейти на страницу отмененной встречи",
|
||||||
"get_started_zapier_templates": "Начать работу с шаблонами Zapier",
|
"get_started_zapier_templates": "Начать работу с шаблонами Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Заблокировано администратором команды",
|
"locked_by_team_admin": "Заблокировано администратором команды",
|
||||||
"app_not_connected": "Вы не подключили аккаунт {{appName}}.",
|
"app_not_connected": "Вы не подключили аккаунт {{appName}}.",
|
||||||
"connect_now": "Подключить",
|
"connect_now": "Подключить",
|
||||||
|
"connect_now_unavailable_tooltip": "Функция \"Подключиться сейчас\" в данный момент недоступна. Пожалуйста, выберите время из доступных выше слотов или попробуйте позже.",
|
||||||
"managed_event_dialog_confirm_button_one": "Заменить и уведомить {{count}} участника",
|
"managed_event_dialog_confirm_button_one": "Заменить и уведомить {{count}} участника",
|
||||||
"managed_event_dialog_confirm_button_other": "Заменить и уведомить участников ({{count}})",
|
"managed_event_dialog_confirm_button_other": "Заменить и уведомить участников ({{count}})",
|
||||||
"managed_event_dialog_title_one": "URL-адрес /{{slug}} уже используется {{count}} участником. Заменить?",
|
"managed_event_dialog_title_one": "URL-адрес /{{slug}} уже используется {{count}} участником. Заменить?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Продолжая, вы соглашаетесь с нашими",
|
"proceeding_agreement": "Продолжая, вы соглашаетесь с нашими",
|
||||||
"terms": "Условиями",
|
"terms": "Условиями",
|
||||||
"always_show_x_days": "Всегда доступно {{x}} дней",
|
"always_show_x_days": "Всегда доступно {{x}} дней",
|
||||||
|
"always_show_x_days_description": "Автоматически сдвигает {{x}}-дневное окно бронирования вперед каждый день, чтобы ваш календарь всегда показывал следующие {{x}} дней доступных слотов.",
|
||||||
"team_subscription_cancelled_successfully": "Командная подписка успешно отменена",
|
"team_subscription_cancelled_successfully": "Командная подписка успешно отменена",
|
||||||
"team_subscription_cancellation_error": "Ошибка при отмене командной подписки",
|
"team_subscription_cancellation_error": "Ошибка при отмене командной подписки",
|
||||||
"unable_to_subscribe_to_the_platform": "Произошла ошибка при попытке подписаться на план платформы, пожалуйста, попробуйте позже",
|
"unable_to_subscribe_to_the_platform": "Произошла ошибка при попытке подписаться на план платформы, пожалуйста, попробуйте позже",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Заканчивается на",
|
"filter_operator_ends_with": "Заканчивается на",
|
||||||
"filter_operator_is_empty": "Пусто",
|
"filter_operator_is_empty": "Пусто",
|
||||||
"filter_operator_not_empty": "Не пусто",
|
"filter_operator_not_empty": "Не пусто",
|
||||||
"translate_description_button": "Перевести заголовок/описание на язык браузера посетителя с помощью ИИ",
|
"translate_description_button": "Автоматически перевести заголовок и описание",
|
||||||
|
"translate_description_button_description": "Автоматически переводить заголовки и описания на язык браузера посетителя с помощью ИИ.",
|
||||||
"rr_distribution_method": "Распределение",
|
"rr_distribution_method": "Распределение",
|
||||||
"rr_distribution_method_description": "Позволяет оптимизировать распределение для максимальной доступности или для достижения более сбалансированного назначения",
|
"rr_distribution_method_description": "Позволяет оптимизировать распределение для максимальной доступности или для достижения более сбалансированного назначения",
|
||||||
"rr_distribution_method_availability_title": "Максимизировать доступность",
|
"rr_distribution_method_availability_title": "Максимизировать доступность",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Функция мест не поддерживает повторяющиеся события. Отключите функцию мест, чтобы сделать событие повторяющимся.",
|
"seats_doesnt_support_recurring": "Функция мест не поддерживает повторяющиеся события. Отключите функцию мест, чтобы сделать событие повторяющимся.",
|
||||||
"recurring_event_seats_error": "Повторяющееся событие не поддерживает функцию мест. Отключите функцию мест или сделайте событие неповторяющимся.",
|
"recurring_event_seats_error": "Повторяющееся событие не поддерживает функцию мест. Отключите функцию мест или сделайте событие неповторяющимся.",
|
||||||
"segment": "Сегмент",
|
"segment": "Сегмент",
|
||||||
|
"saved_filters": "Сохраненные фильтры",
|
||||||
"rename": "Переименовать",
|
"rename": "Переименовать",
|
||||||
"rename_segment": "Переименовать сегмент",
|
"rename_segment": "Переименовать сегмент",
|
||||||
"delete_segment": "Удалить сегмент",
|
"delete_segment": "Удалить сегмент",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Список выбора",
|
"picklist": "Список выбора",
|
||||||
"most_cancelled_bookings": "Наибольшее количество отмен",
|
"most_cancelled_bookings": "Наибольшее количество отмен",
|
||||||
"interface_language": "Язык интерфейса",
|
"interface_language": "Язык интерфейса",
|
||||||
|
"interface_language_description": "Выберите предпочитаемый язык интерфейса бронирования",
|
||||||
"visitors_browser_language": "Язык браузера посетителя",
|
"visitors_browser_language": "Язык браузера посетителя",
|
||||||
"name_or_email": "Имя или электронная почта",
|
"name_or_email": "Имя или электронная почта",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Если контакт не найден, использовать владельца лида, если он существует",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Если контакт не найден, использовать владельца лида, если он существует",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Редактирование и изменение настроек рабочих процессов",
|
"pbac_desc_update_workflows": "Редактирование и изменение настроек рабочих процессов",
|
||||||
"pbac_desc_delete_workflows": "Удаление рабочих процессов из системы",
|
"pbac_desc_delete_workflows": "Удаление рабочих процессов из системы",
|
||||||
"pbac_resource_webhook": "Вебхук",
|
"pbac_resource_webhook": "Вебхук",
|
||||||
|
"pbac_resource_availability": "Доступность",
|
||||||
|
"pbac_resource_out_of_office": "Вне офиса",
|
||||||
"pbac_desc_create_webhooks": "Создание вебхуков",
|
"pbac_desc_create_webhooks": "Создание вебхуков",
|
||||||
"pbac_desc_view_webhooks": "Просмотр вебхуков",
|
"pbac_desc_view_webhooks": "Просмотр вебхуков",
|
||||||
"pbac_desc_update_webhooks": "Обновить веб-хуки",
|
"pbac_desc_update_webhooks": "Обновить веб-хуки",
|
||||||
"pbac_desc_delete_webhooks": "Удалить веб-хуки",
|
"pbac_desc_delete_webhooks": "Удалить веб-хуки",
|
||||||
|
"pbac_desc_create_availability": "Создать доступность",
|
||||||
|
"pbac_desc_view_availability": "Просмотреть доступность",
|
||||||
|
"pbac_desc_update_availability": "Обновить доступность",
|
||||||
|
"pbac_desc_delete_availability": "Удалить доступность",
|
||||||
|
"pbac_desc_create_out_of_office": "Создать статус \"Вне офиса\"",
|
||||||
|
"pbac_desc_view_out_of_office": "Просмотреть статус \"Вне офиса\"",
|
||||||
|
"pbac_desc_update_out_of_office": "Обновить статус \"Вне офиса\"",
|
||||||
|
"pbac_desc_delete_out_of_office": "Удалить статус \"Вне офиса\"",
|
||||||
"pbac_desc_manage_workflows": "Полный доступ к управлению всеми рабочими процессами",
|
"pbac_desc_manage_workflows": "Полный доступ к управлению всеми рабочими процессами",
|
||||||
"pbac_desc_create_event_types": "Создание типов событий",
|
"pbac_desc_create_event_types": "Создание типов событий",
|
||||||
"pbac_desc_view_event_types": "Просмотр типов событий",
|
"pbac_desc_view_event_types": "Просмотр типов событий",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Tajné stretnutie",
|
"secret_meeting": "Tajné stretnutie",
|
||||||
"login_instead": "Prihláste sa namiesto toho",
|
"login_instead": "Prihláste sa namiesto toho",
|
||||||
"create_account": "Vytvoriť účet",
|
"create_account": "Vytvoriť účet",
|
||||||
|
"get_started": "Začať",
|
||||||
"confirm_password": "Potvrďte heslo",
|
"confirm_password": "Potvrďte heslo",
|
||||||
"reset_your_password": "Nastavte si nové heslo podľa pokynov zaslaných na vašu e-mailovú adresu.",
|
"reset_your_password": "Nastavte si nové heslo podľa pokynov zaslaných na vašu e-mailovú adresu.",
|
||||||
"org_banner_instructions": "Nahrajte prosím obrázok so šírkou {{width}} a výškou {{height}}.",
|
"org_banner_instructions": "Nahrajte prosím obrázok so šírkou {{width}} a výškou {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Účastníci",
|
"attendees": "Účastníci",
|
||||||
"add_attendees": "Pridať účastníkov",
|
"add_attendees": "Pridať účastníkov",
|
||||||
"show_advanced_settings": "Zobraziť pokročilé nastavenia",
|
"show_advanced_settings": "Zobraziť pokročilé nastavenia",
|
||||||
|
"hide_advanced_settings": "Skryť pokročilé nastavenia",
|
||||||
"event_name": "Názov udalosti",
|
"event_name": "Názov udalosti",
|
||||||
"event_name_in_calendar": "S názvom udalosti",
|
"event_name_in_calendar": "S názvom udalosti",
|
||||||
"event_name_tooltip": "Názov, ktorý sa zobrazí v kalendároch",
|
"event_name_tooltip": "Názov, ktorý sa zobrazí v kalendároch",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Kontaktovať obchodný tím",
|
"contact_sales": "Kontaktovať obchodný tím",
|
||||||
"error_404": "Chyba 404",
|
"error_404": "Chyba 404",
|
||||||
"default": "Predvolené",
|
"default": "Predvolené",
|
||||||
"set_to_default": "Nastaviť na predvolené",
|
"set_to_default": "Nastaviť ako predvolené",
|
||||||
"new_schedule_btn": "Nový rozvrh",
|
"new_schedule_btn": "Nový rozvrh",
|
||||||
"add_new_schedule": "Pridať nový rozvrh",
|
"add_new_schedule": "Pridať nový rozvrh",
|
||||||
"add_new_calendar": "Pridať nový kalendár",
|
"add_new_calendar": "Pridať nový kalendár",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Členovia vášho tímu nebudú môcť vidieť ostatných členov tímu, keď je táto možnosť zapnutá.",
|
"make_team_private_description": "Členovia vášho tímu nebudú môcť vidieť ostatných členov tímu, keď je táto možnosť zapnutá.",
|
||||||
"you_cannot_see_team_members": "Nemôžete vidieť všetkých členov súkromného tímu.",
|
"you_cannot_see_team_members": "Nemôžete vidieť všetkých členov súkromného tímu.",
|
||||||
"you_cannot_see_teams_of_org": "Nemôžete vidieť tímy súkromnej organizácie.",
|
"you_cannot_see_teams_of_org": "Nemôžete vidieť tímy súkromnej organizácie.",
|
||||||
"allow_booker_to_select_duration": "Povoliť rezervujúcemu vybrať si trvanie",
|
"allow_multiple_durations": "Povoliť viacero trvaní",
|
||||||
"impersonate_user_tip": "Všetky použitia tejto funkcie sú auditované.",
|
"impersonate_user_tip": "Všetky použitia tejto funkcie sú auditované.",
|
||||||
"impersonating_user_warning": "Napodobňovanie používateľa \"{{user}}\".",
|
"impersonating_user_warning": "Napodobňovanie používateľa \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Kliknite sem pre zastavenie",
|
"impersonating_stop_instructions": "Kliknite sem pre zastavenie",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Potvrdenie rezervácie zlyhalo",
|
"booking_confirmation_failed": "Potvrdenie rezervácie zlyhalo",
|
||||||
"not_enough_seats": "Nedostatok voľných miest",
|
"not_enough_seats": "Nedostatok voľných miest",
|
||||||
"form_builder_field_already_exists": "Pole s týmto názvom už existuje",
|
"form_builder_field_already_exists": "Pole s týmto názvom už existuje",
|
||||||
|
"guests_field_must_be_multiemail": "Pole pre hostí musí byť typu 'Viacero e-mailov'",
|
||||||
"show_on_booking_page": "Zobraziť na stránke rezervácie",
|
"show_on_booking_page": "Zobraziť na stránke rezervácie",
|
||||||
"visit_cancelled_booking": "Môžete navštíviť stránku zrušenej rezervácie",
|
"visit_cancelled_booking": "Môžete navštíviť stránku zrušenej rezervácie",
|
||||||
"get_started_zapier_templates": "Začnite so šablónami Zapier",
|
"get_started_zapier_templates": "Začnite so šablónami Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Zamknuté tímovým administrátorom",
|
"locked_by_team_admin": "Zamknuté tímovým administrátorom",
|
||||||
"app_not_connected": "Nemáte pripojený účet {{appName}}.",
|
"app_not_connected": "Nemáte pripojený účet {{appName}}.",
|
||||||
"connect_now": "Pripojiť teraz",
|
"connect_now": "Pripojiť teraz",
|
||||||
|
"connect_now_unavailable_tooltip": "Pripojenie teraz je momentálne nedostupné. Prosím, rezervujte si termín z vyššie uvedených časov alebo to skúste neskôr.",
|
||||||
"managed_event_dialog_confirm_button_one": "Nahradiť a upozorniť {{count}} člena",
|
"managed_event_dialog_confirm_button_one": "Nahradiť a upozorniť {{count}} člena",
|
||||||
"managed_event_dialog_confirm_button_other": "Nahradiť a upozorniť {{count}} členov",
|
"managed_event_dialog_confirm_button_other": "Nahradiť a upozorniť {{count}} členov",
|
||||||
"managed_event_dialog_title_one": "URL /{{slug}} už existuje pre {{count}} člena. Chcete ho nahradiť?",
|
"managed_event_dialog_title_one": "URL /{{slug}} už existuje pre {{count}} člena. Chcete ho nahradiť?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Pokračovaním súhlasíte s našimi",
|
"proceeding_agreement": "Pokračovaním súhlasíte s našimi",
|
||||||
"terms": "Podmienkami",
|
"terms": "Podmienkami",
|
||||||
"always_show_x_days": "Vždy {{x}} dní dostupných",
|
"always_show_x_days": "Vždy {{x}} dní dostupných",
|
||||||
|
"always_show_x_days_description": "Automaticky posúva {{x}}-dňové rezervačné okno každý deň dopredu, čím zabezpečuje, že váš kalendár vždy zobrazuje dostupné termíny na nasledujúcich {{x}} dní.",
|
||||||
"team_subscription_cancelled_successfully": "Predplatné tímu bolo úspešne zrušené",
|
"team_subscription_cancelled_successfully": "Predplatné tímu bolo úspešne zrušené",
|
||||||
"team_subscription_cancellation_error": "Chyba pri rušení predplatného tímu",
|
"team_subscription_cancellation_error": "Chyba pri rušení predplatného tímu",
|
||||||
"unable_to_subscribe_to_the_platform": "Pri pokuse o prihlásenie na odber plánu platformy došlo k chybe, skúste to prosím neskôr",
|
"unable_to_subscribe_to_the_platform": "Pri pokuse o prihlásenie na odber plánu platformy došlo k chybe, skúste to prosím neskôr",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Končí na",
|
"filter_operator_ends_with": "Končí na",
|
||||||
"filter_operator_is_empty": "Je prázdne",
|
"filter_operator_is_empty": "Je prázdne",
|
||||||
"filter_operator_not_empty": "Nie je prázdne",
|
"filter_operator_not_empty": "Nie je prázdne",
|
||||||
"translate_description_button": "Preložiť názov/popis do jazyka prehliadača návštevníka pomocou AI",
|
"translate_description_button": "Automaticky preložiť názov a popis",
|
||||||
|
"translate_description_button_description": "Automaticky prekladať názvy a popisy do jazyka prehliadača návštevníka pomocou umelej inteligencie.",
|
||||||
"rr_distribution_method": "Distribúcia",
|
"rr_distribution_method": "Distribúcia",
|
||||||
"rr_distribution_method_description": "Umožňuje optimalizovať distribúciu pre maximálnu dostupnosť alebo pre vyváženejšie prideľovanie.",
|
"rr_distribution_method_description": "Umožňuje optimalizovať distribúciu pre maximálnu dostupnosť alebo pre vyváženejšie prideľovanie.",
|
||||||
"rr_distribution_method_availability_title": "Maximalizovať dostupnosť",
|
"rr_distribution_method_availability_title": "Maximalizovať dostupnosť",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Funkcia miest nepodporuje opakujúce sa udalosti. Vypnite funkciu miest, aby ste mohli nastaviť opakovanie.",
|
"seats_doesnt_support_recurring": "Funkcia miest nepodporuje opakujúce sa udalosti. Vypnite funkciu miest, aby ste mohli nastaviť opakovanie.",
|
||||||
"recurring_event_seats_error": "Opakujúca sa udalosť nepodporuje funkciu miest. Vypnite funkciu miest alebo zmeňte udalosť na neopakujúcu sa.",
|
"recurring_event_seats_error": "Opakujúca sa udalosť nepodporuje funkciu miest. Vypnite funkciu miest alebo zmeňte udalosť na neopakujúcu sa.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Uložené filtre",
|
||||||
"rename": "Premenovať",
|
"rename": "Premenovať",
|
||||||
"rename_segment": "Premenovať segment",
|
"rename_segment": "Premenovať segment",
|
||||||
"delete_segment": "Odstrániť segment",
|
"delete_segment": "Odstrániť segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Zoznam na výber",
|
"picklist": "Zoznam na výber",
|
||||||
"most_cancelled_bookings": "Najviac zrušených",
|
"most_cancelled_bookings": "Najviac zrušených",
|
||||||
"interface_language": "Jazyk rozhrania",
|
"interface_language": "Jazyk rozhrania",
|
||||||
|
"interface_language_description": "Nastavte svoj preferovaný jazyk pre rezervačné rozhranie",
|
||||||
"visitors_browser_language": "Jazyk prehliadača návštevníka",
|
"visitors_browser_language": "Jazyk prehliadača návštevníka",
|
||||||
"name_or_email": "Meno alebo email",
|
"name_or_email": "Meno alebo email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ak sa nenájde žiadny kontakt, použije sa vlastník leadu, ak existuje",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ak sa nenájde žiadny kontakt, použije sa vlastník leadu, ak existuje",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Upravovať a modifikovať nastavenia pracovných postupov",
|
"pbac_desc_update_workflows": "Upravovať a modifikovať nastavenia pracovných postupov",
|
||||||
"pbac_desc_delete_workflows": "Odstraňovať pracovné postupy zo systému",
|
"pbac_desc_delete_workflows": "Odstraňovať pracovné postupy zo systému",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Dostupnosť",
|
||||||
|
"pbac_resource_out_of_office": "Neprítomnosť",
|
||||||
"pbac_desc_create_webhooks": "Vytvárať webhooky",
|
"pbac_desc_create_webhooks": "Vytvárať webhooky",
|
||||||
"pbac_desc_view_webhooks": "Zobraziť webhooky",
|
"pbac_desc_view_webhooks": "Zobraziť webhooky",
|
||||||
"pbac_desc_update_webhooks": "Aktualizovať webhoky",
|
"pbac_desc_update_webhooks": "Aktualizovať webhoky",
|
||||||
"pbac_desc_delete_webhooks": "Odstrániť webhoky",
|
"pbac_desc_delete_webhooks": "Odstrániť webhoky",
|
||||||
|
"pbac_desc_create_availability": "Vytvoriť dostupnosť",
|
||||||
|
"pbac_desc_view_availability": "Zobraziť dostupnosť",
|
||||||
|
"pbac_desc_update_availability": "Aktualizovať dostupnosť",
|
||||||
|
"pbac_desc_delete_availability": "Odstrániť dostupnosť",
|
||||||
|
"pbac_desc_create_out_of_office": "Vytvoriť neprítomnosť",
|
||||||
|
"pbac_desc_view_out_of_office": "Zobraziť neprítomnosť",
|
||||||
|
"pbac_desc_update_out_of_office": "Aktualizovať neprítomnosť",
|
||||||
|
"pbac_desc_delete_out_of_office": "Odstrániť neprítomnosť",
|
||||||
"pbac_desc_manage_workflows": "Úplný prístup k správe všetkých pracovných postupov",
|
"pbac_desc_manage_workflows": "Úplný prístup k správe všetkých pracovných postupov",
|
||||||
"pbac_desc_create_event_types": "Vytvárať typy udalostí",
|
"pbac_desc_create_event_types": "Vytvárať typy udalostí",
|
||||||
"pbac_desc_view_event_types": "Zobraziť typy udalostí",
|
"pbac_desc_view_event_types": "Zobraziť typy udalostí",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Tajni Sastanak",
|
"secret_meeting": "Tajni Sastanak",
|
||||||
"login_instead": "Umesto toga se ulogujte",
|
"login_instead": "Umesto toga se ulogujte",
|
||||||
"create_account": "Napravite Nalog",
|
"create_account": "Napravite Nalog",
|
||||||
|
"get_started": "Započnite",
|
||||||
"confirm_password": "Potvrdite lozinku",
|
"confirm_password": "Potvrdite lozinku",
|
||||||
"reset_your_password": "Podesite novu lozinku pomoću uputstva poslatim na vašu imejl adresu.",
|
"reset_your_password": "Podesite novu lozinku pomoću uputstva poslatim na vašu imejl adresu.",
|
||||||
"org_banner_instructions": "Molimo vas da otpremite sliku širine {{width}} i visine {{height}}.",
|
"org_banner_instructions": "Molimo vas da otpremite sliku širine {{width}} i visine {{height}}.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Boja tipa događaja (svetla tema)",
|
"light_event_type_color": "Boja tipa događaja (svetla tema)",
|
||||||
"dark_event_type_color": "Boja tipa događaja (tamna tema)",
|
"dark_event_type_color": "Boja tipa događaja (tamna tema)",
|
||||||
"file_not_named": "Datoteka nije imenovana [idOrSlug]/[user]",
|
"file_not_named": "Datoteka nije imenovana [idOrSlug]/[user]",
|
||||||
"create_team": "Napravi Tim",
|
"create_team": "Kreiraj tim",
|
||||||
"name": "Ime",
|
"name": "Ime",
|
||||||
"nameless_team": "Tim bez imena",
|
"nameless_team": "Tim bez imena",
|
||||||
"oauth_clients": "OAuth klijenti",
|
"oauth_clients": "OAuth klijenti",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Polaznici",
|
"attendees": "Polaznici",
|
||||||
"add_attendees": "Dodaj polaznike",
|
"add_attendees": "Dodaj polaznike",
|
||||||
"show_advanced_settings": "Pokaži napredna podešavanja",
|
"show_advanced_settings": "Pokaži napredna podešavanja",
|
||||||
|
"hide_advanced_settings": "Sakrij napredna podešavanja",
|
||||||
"event_name": "Ime Događaja",
|
"event_name": "Ime Događaja",
|
||||||
"event_name_in_calendar": "Naziv događaja u kalendaru",
|
"event_name_in_calendar": "Naziv događaja u kalendaru",
|
||||||
"event_name_tooltip": "Ime koje će se pojaviti u kalendarima",
|
"event_name_tooltip": "Ime koje će se pojaviti u kalendarima",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Kontaktirajte tim prodaje",
|
"contact_sales": "Kontaktirajte tim prodaje",
|
||||||
"error_404": "Greška 404",
|
"error_404": "Greška 404",
|
||||||
"default": "Podrazumevano",
|
"default": "Podrazumevano",
|
||||||
"set_to_default": "Postavi na Podrazumevano",
|
"set_to_default": "Postavi kao podrazumevano",
|
||||||
"new_schedule_btn": "Novi raspored",
|
"new_schedule_btn": "Novi raspored",
|
||||||
"add_new_schedule": "Dodaj novi raspored",
|
"add_new_schedule": "Dodaj novi raspored",
|
||||||
"add_new_calendar": "Dodajte novi kalendar",
|
"add_new_calendar": "Dodajte novi kalendar",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Članovi vašeg tima neće moći da vide druge članove tima kada je ovo aktivirano.",
|
"make_team_private_description": "Članovi vašeg tima neće moći da vide druge članove tima kada je ovo aktivirano.",
|
||||||
"you_cannot_see_team_members": "Nećete moći da vidite sve članove privatnog tima.",
|
"you_cannot_see_team_members": "Nećete moći da vidite sve članove privatnog tima.",
|
||||||
"you_cannot_see_teams_of_org": "Ne možete videti timove privatne organizacije.",
|
"you_cannot_see_teams_of_org": "Ne možete videti timove privatne organizacije.",
|
||||||
"allow_booker_to_select_duration": "Dozvoli polazniku da izabere trajanje",
|
"allow_multiple_durations": "Dozvoli višestruka trajanja",
|
||||||
"impersonate_user_tip": "Sva korišćenja ove funkcije se nadgledaju.",
|
"impersonate_user_tip": "Sva korišćenja ove funkcije se nadgledaju.",
|
||||||
"impersonating_user_warning": "Predstavljanje kao korisnik „{{user}}“.",
|
"impersonating_user_warning": "Predstavljanje kao korisnik „{{user}}“.",
|
||||||
"impersonating_stop_instructions": "Kliknite ovde da zaustavite",
|
"impersonating_stop_instructions": "Kliknite ovde da zaustavite",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Potvrda rezervacije nije uspela",
|
"booking_confirmation_failed": "Potvrda rezervacije nije uspela",
|
||||||
"not_enough_seats": "Nema dovoljno mesta",
|
"not_enough_seats": "Nema dovoljno mesta",
|
||||||
"form_builder_field_already_exists": "Polje sa ovim imenom već postoji",
|
"form_builder_field_already_exists": "Polje sa ovim imenom već postoji",
|
||||||
|
"guests_field_must_be_multiemail": "Polje za goste mora biti tipa 'Više e-mail adresa'",
|
||||||
"show_on_booking_page": "Prikaži na stranici zakazivanja",
|
"show_on_booking_page": "Prikaži na stranici zakazivanja",
|
||||||
"visit_cancelled_booking": "Možete posetiti stranicu otkazane rezervacije",
|
"visit_cancelled_booking": "Možete posetiti stranicu otkazane rezervacije",
|
||||||
"get_started_zapier_templates": "Započnite sa Zapier predlošcima",
|
"get_started_zapier_templates": "Započnite sa Zapier predlošcima",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Zaključao administrator tima",
|
"locked_by_team_admin": "Zaključao administrator tima",
|
||||||
"app_not_connected": "Niste povezali {{appName}} nalog.",
|
"app_not_connected": "Niste povezali {{appName}} nalog.",
|
||||||
"connect_now": "Povežite odmah",
|
"connect_now": "Povežite odmah",
|
||||||
|
"connect_now_unavailable_tooltip": "Povezivanje odmah trenutno nije dostupno. Molimo rezervišite iz gornjih termina ili pokušajte kasnije.",
|
||||||
"managed_event_dialog_confirm_button_one": "Zameni i obavesti {{count}} člana",
|
"managed_event_dialog_confirm_button_one": "Zameni i obavesti {{count}} člana",
|
||||||
"managed_event_dialog_confirm_button_other": "Zameni i obavesti {{count}} članova",
|
"managed_event_dialog_confirm_button_other": "Zameni i obavesti {{count}} članova",
|
||||||
"managed_event_dialog_title_one": "URL /{{slug}} već postoji za {{count}} člana. Da li želite da ga zamenite?",
|
"managed_event_dialog_title_one": "URL /{{slug}} već postoji za {{count}} člana. Da li želite da ga zamenite?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Nastavljanjem prihvatate naše",
|
"proceeding_agreement": "Nastavljanjem prihvatate naše",
|
||||||
"terms": "Uslove korišćenja",
|
"terms": "Uslove korišćenja",
|
||||||
"always_show_x_days": "Uvek dostupno {{x}} dana",
|
"always_show_x_days": "Uvek dostupno {{x}} dana",
|
||||||
|
"always_show_x_days_description": "Automatski pomera prozor za rezervacije od {{x}} dana unapred svakog dana, osiguravajući da vaš kalendar uvek prikazuje sledećih {{x}} dana dostupnih termina.",
|
||||||
"team_subscription_cancelled_successfully": "Pretplata tima je uspešno otkazana",
|
"team_subscription_cancelled_successfully": "Pretplata tima je uspešno otkazana",
|
||||||
"team_subscription_cancellation_error": "Greška prilikom otkazivanja pretplate tima",
|
"team_subscription_cancellation_error": "Greška prilikom otkazivanja pretplate tima",
|
||||||
"unable_to_subscribe_to_the_platform": "Došlo je do greške prilikom pokušaja pretplate na platformu, molimo pokušajte ponovo kasnije",
|
"unable_to_subscribe_to_the_platform": "Došlo je do greške prilikom pokušaja pretplate na platformu, molimo pokušajte ponovo kasnije",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Završava sa",
|
"filter_operator_ends_with": "Završava sa",
|
||||||
"filter_operator_is_empty": "Prazno je",
|
"filter_operator_is_empty": "Prazno je",
|
||||||
"filter_operator_not_empty": "Nije prazno",
|
"filter_operator_not_empty": "Nije prazno",
|
||||||
"translate_description_button": "Prevedi naslov/opis na jezik posetiočevog pretraživača koristeći AI",
|
"translate_description_button": "Automatski prevedi naslov i opis",
|
||||||
|
"translate_description_button_description": "Automatski prevedi naslove i opise na jezik posetiočevog pregledača koristeći veštačku inteligenciju.",
|
||||||
"rr_distribution_method": "Distribucija",
|
"rr_distribution_method": "Distribucija",
|
||||||
"rr_distribution_method_description": "Omogućava optimizaciju distribucije za maksimalnu dostupnost ili za balansiranu raspodelu.",
|
"rr_distribution_method_description": "Omogućava optimizaciju distribucije za maksimalnu dostupnost ili za balansiranu raspodelu.",
|
||||||
"rr_distribution_method_availability_title": "Maksimalna dostupnost",
|
"rr_distribution_method_availability_title": "Maksimalna dostupnost",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Funkcija mesta ne podržava događaj koji se ponavlja. Onemogućite funkciju mesta da bi se događaj mogao ponavljati.",
|
"seats_doesnt_support_recurring": "Funkcija mesta ne podržava događaj koji se ponavlja. Onemogućite funkciju mesta da bi se događaj mogao ponavljati.",
|
||||||
"recurring_event_seats_error": "Događaj koji se ponavlja ne podržava funkciju mesta. Onemogućite funkciju mesta ili učinite događaj jednokratnim.",
|
"recurring_event_seats_error": "Događaj koji se ponavlja ne podržava funkciju mesta. Onemogućite funkciju mesta ili učinite događaj jednokratnim.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Sačuvani filteri",
|
||||||
"rename": "Preimenuj",
|
"rename": "Preimenuj",
|
||||||
"rename_segment": "Preimenuj segment",
|
"rename_segment": "Preimenuj segment",
|
||||||
"delete_segment": "Obriši segment",
|
"delete_segment": "Obriši segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Lista za izbor",
|
"picklist": "Lista za izbor",
|
||||||
"most_cancelled_bookings": "Najviše otkazanih",
|
"most_cancelled_bookings": "Najviše otkazanih",
|
||||||
"interface_language": "Jezik interfejsa",
|
"interface_language": "Jezik interfejsa",
|
||||||
|
"interface_language_description": "Postavite željeni jezik za interfejs za rezervacije",
|
||||||
"visitors_browser_language": "Jezik pregledača posetioca",
|
"visitors_browser_language": "Jezik pregledača posetioca",
|
||||||
"name_or_email": "Ime ili email",
|
"name_or_email": "Ime ili email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ako kontakt nije pronađen, vrati se na vlasnika potencijalnog klijenta ako postoji",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Ako kontakt nije pronađen, vrati se na vlasnika potencijalnog klijenta ako postoji",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Uređivanje i modifikovanje podešavanja radnih tokova",
|
"pbac_desc_update_workflows": "Uređivanje i modifikovanje podešavanja radnih tokova",
|
||||||
"pbac_desc_delete_workflows": "Uklanjanje radnih tokova iz sistema",
|
"pbac_desc_delete_workflows": "Uklanjanje radnih tokova iz sistema",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Dostupnost",
|
||||||
|
"pbac_resource_out_of_office": "Van kancelarije",
|
||||||
"pbac_desc_create_webhooks": "Kreirajte webhooks",
|
"pbac_desc_create_webhooks": "Kreirajte webhooks",
|
||||||
"pbac_desc_view_webhooks": "Pregledajte webhooks",
|
"pbac_desc_view_webhooks": "Pregledajte webhooks",
|
||||||
"pbac_desc_update_webhooks": "Ažuriraj webhook-ove",
|
"pbac_desc_update_webhooks": "Ažuriraj webhook-ove",
|
||||||
"pbac_desc_delete_webhooks": "Obriši webhook-ove",
|
"pbac_desc_delete_webhooks": "Obriši webhook-ove",
|
||||||
|
"pbac_desc_create_availability": "Kreiraj dostupnost",
|
||||||
|
"pbac_desc_view_availability": "Pregledaj dostupnost",
|
||||||
|
"pbac_desc_update_availability": "Ažuriraj dostupnost",
|
||||||
|
"pbac_desc_delete_availability": "Obriši dostupnost",
|
||||||
|
"pbac_desc_create_out_of_office": "Kreiraj odsustvo iz kancelarije",
|
||||||
|
"pbac_desc_view_out_of_office": "Pregledaj odsustvo iz kancelarije",
|
||||||
|
"pbac_desc_update_out_of_office": "Ažuriraj odsustvo iz kancelarije",
|
||||||
|
"pbac_desc_delete_out_of_office": "Obriši odsustvo iz kancelarije",
|
||||||
"pbac_desc_manage_workflows": "Puni pristup upravljanju svim radnim tokovima",
|
"pbac_desc_manage_workflows": "Puni pristup upravljanju svim radnim tokovima",
|
||||||
"pbac_desc_create_event_types": "Kreiranje tipova događaja",
|
"pbac_desc_create_event_types": "Kreiranje tipova događaja",
|
||||||
"pbac_desc_view_event_types": "Pregled tipova događaja",
|
"pbac_desc_view_event_types": "Pregled tipova događaja",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Hemligt möte",
|
"secret_meeting": "Hemligt möte",
|
||||||
"login_instead": "Logga in istället",
|
"login_instead": "Logga in istället",
|
||||||
"create_account": "Skapa konto",
|
"create_account": "Skapa konto",
|
||||||
|
"get_started": "Kom igång",
|
||||||
"confirm_password": "Bekräfta lösenord",
|
"confirm_password": "Bekräfta lösenord",
|
||||||
"reset_your_password": "Konfigurera ditt nya lösenord med instruktionerna som skickas till din e-postadress.",
|
"reset_your_password": "Konfigurera ditt nya lösenord med instruktionerna som skickas till din e-postadress.",
|
||||||
"org_banner_instructions": "Vänligen ladda upp en bild med {{width}} bredd och {{height}} höjd.",
|
"org_banner_instructions": "Vänligen ladda upp en bild med {{width}} bredd och {{height}} höjd.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Deltagare",
|
"attendees": "Deltagare",
|
||||||
"add_attendees": "Lägg till deltagare",
|
"add_attendees": "Lägg till deltagare",
|
||||||
"show_advanced_settings": "Visa avancerade inställningar",
|
"show_advanced_settings": "Visa avancerade inställningar",
|
||||||
|
"hide_advanced_settings": "Dölj avancerade inställningar",
|
||||||
"event_name": "Händelsenamn",
|
"event_name": "Händelsenamn",
|
||||||
"event_name_in_calendar": "Händelsenamn i kalender",
|
"event_name_in_calendar": "Händelsenamn i kalender",
|
||||||
"event_name_tooltip": "Namnet som kommer visas i kalendrar",
|
"event_name_tooltip": "Namnet som kommer visas i kalendrar",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Dina teammedlemmar kommer inte att kunna se andra teammedlemmar när detta är aktiverat.",
|
"make_team_private_description": "Dina teammedlemmar kommer inte att kunna se andra teammedlemmar när detta är aktiverat.",
|
||||||
"you_cannot_see_team_members": "Du kan inte se alla teammedlemmar i ett privat team.",
|
"you_cannot_see_team_members": "Du kan inte se alla teammedlemmar i ett privat team.",
|
||||||
"you_cannot_see_teams_of_org": "Du kan inte se teamen i en privat organisation.",
|
"you_cannot_see_teams_of_org": "Du kan inte se teamen i en privat organisation.",
|
||||||
"allow_booker_to_select_duration": "Låt bokaren välja varaktighet",
|
"allow_multiple_durations": "Tillåt flera varaktigheter",
|
||||||
"impersonate_user_tip": "All användning av denna funktion granskas.",
|
"impersonate_user_tip": "All användning av denna funktion granskas.",
|
||||||
"impersonating_user_warning": "Imiterar användarnamn \"{{user}}\".",
|
"impersonating_user_warning": "Imiterar användarnamn \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Klicka här för att sluta",
|
"impersonating_stop_instructions": "Klicka här för att sluta",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Bokningsbekräftelsen misslyckades",
|
"booking_confirmation_failed": "Bokningsbekräftelsen misslyckades",
|
||||||
"not_enough_seats": "Inte tillräckligt med platser",
|
"not_enough_seats": "Inte tillräckligt med platser",
|
||||||
"form_builder_field_already_exists": "Det finns redan ett fält med det här namnet",
|
"form_builder_field_already_exists": "Det finns redan ett fält med det här namnet",
|
||||||
|
"guests_field_must_be_multiemail": "Fältet för gäster måste vara av typen 'Flera e-postadresser'",
|
||||||
"show_on_booking_page": "Visa på bokningssidan",
|
"show_on_booking_page": "Visa på bokningssidan",
|
||||||
"visit_cancelled_booking": "Du kan besöka sidan för avbokade bokningar",
|
"visit_cancelled_booking": "Du kan besöka sidan för avbokade bokningar",
|
||||||
"get_started_zapier_templates": "Kom igång med Zapier-mallar",
|
"get_started_zapier_templates": "Kom igång med Zapier-mallar",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Låst av teamadministratör",
|
"locked_by_team_admin": "Låst av teamadministratör",
|
||||||
"app_not_connected": "Du har inte anslutit ett {{appName}}-konto.",
|
"app_not_connected": "Du har inte anslutit ett {{appName}}-konto.",
|
||||||
"connect_now": "Anslut nu",
|
"connect_now": "Anslut nu",
|
||||||
|
"connect_now_unavailable_tooltip": "Anslut nu är för närvarande inte tillgängligt. Boka från ovanstående tider eller försök igen senare.",
|
||||||
"managed_event_dialog_confirm_button_one": "Ersätt och meddela {{count}} medlem",
|
"managed_event_dialog_confirm_button_one": "Ersätt och meddela {{count}} medlem",
|
||||||
"managed_event_dialog_confirm_button_other": "Ersätt och meddela {{count}} medlemmar",
|
"managed_event_dialog_confirm_button_other": "Ersätt och meddela {{count}} medlemmar",
|
||||||
"managed_event_dialog_title_one": "Webbadressen/{{slug}} finns redan för {{count}} medlem. Vill du ersätta den?",
|
"managed_event_dialog_title_one": "Webbadressen/{{slug}} finns redan för {{count}} medlem. Vill du ersätta den?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Företag",
|
"enterprise": "Företag",
|
||||||
"enterprise_description": "Uppgradera till Enterprise för att skapa din organisation",
|
"enterprise_description": "Uppgradera till Enterprise för att skapa din organisation",
|
||||||
"create_your_org": "Skapa din Organisation",
|
"create_your_org": "Skapa din Organisation",
|
||||||
"create_org": "Skapa Organisation",
|
"create_org": "Skapa organisation",
|
||||||
"create_your_org_description": "Uppgradera till Organisationer och få en subdomän, enhetlig fakturering, Insights, omfattande whitelabeling och mer",
|
"create_your_org_description": "Uppgradera till Organisationer och få en subdomän, enhetlig fakturering, Insights, omfattande whitelabeling och mer",
|
||||||
"create_your_enterprise_description": "Uppgradera till Enterprise och få tillgång till Active Directory Sync, SCIM Automatisk Användarprovisionering, Cal.ai Röstagenter, Admin API:er och mer!",
|
"create_your_enterprise_description": "Uppgradera till Enterprise och få tillgång till Active Directory Sync, SCIM Automatisk Användarprovisionering, Cal.ai Röstagenter, Admin API:er och mer!",
|
||||||
"other_payment_app_enabled": "Du kan bara aktivera en betalningsapp per eventtyp",
|
"other_payment_app_enabled": "Du kan bara aktivera en betalningsapp per eventtyp",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Genom att fortsätta godkänner du våra",
|
"proceeding_agreement": "Genom att fortsätta godkänner du våra",
|
||||||
"terms": "Villkor",
|
"terms": "Villkor",
|
||||||
"always_show_x_days": "Visa alltid {{x}} dagar tillgängliga",
|
"always_show_x_days": "Visa alltid {{x}} dagar tillgängliga",
|
||||||
|
"always_show_x_days_description": "Flyttar automatiskt fram {{x}}-dagars bokningsfönstret varje dag, vilket säkerställer att din kalender alltid visar de kommande {{x}} dagarna med tillgängliga tider.",
|
||||||
"team_subscription_cancelled_successfully": "Teamprenumerationen avbröts framgångsrikt",
|
"team_subscription_cancelled_successfully": "Teamprenumerationen avbröts framgångsrikt",
|
||||||
"team_subscription_cancellation_error": "Fel vid avbokning av teamprenumerationen",
|
"team_subscription_cancellation_error": "Fel vid avbokning av teamprenumerationen",
|
||||||
"unable_to_subscribe_to_the_platform": "Ett fel uppstod vid försök att prenumerera på plattformen, försök igen senare",
|
"unable_to_subscribe_to_the_platform": "Ett fel uppstod vid försök att prenumerera på plattformen, försök igen senare",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Slutar med",
|
"filter_operator_ends_with": "Slutar med",
|
||||||
"filter_operator_is_empty": "Är tom",
|
"filter_operator_is_empty": "Är tom",
|
||||||
"filter_operator_not_empty": "Är inte tom",
|
"filter_operator_not_empty": "Är inte tom",
|
||||||
"translate_description_button": "Översätt titel/beskrivning till besökarens webbläsarspråk med hjälp av AI",
|
"translate_description_button": "Översätt titel och beskrivning automatiskt",
|
||||||
|
"translate_description_button_description": "Översätt automatiskt titlar och beskrivningar till besökarens webbläsarspråk med hjälp av AI.",
|
||||||
"rr_distribution_method": "Distribution",
|
"rr_distribution_method": "Distribution",
|
||||||
"rr_distribution_method_description": "Möjliggör optimering av distributionen för maximal tillgänglighet eller för att uppnå en mer balanserad fördelning.",
|
"rr_distribution_method_description": "Möjliggör optimering av distributionen för maximal tillgänglighet eller för att uppnå en mer balanserad fördelning.",
|
||||||
"rr_distribution_method_availability_title": "Maximera tillgänglighet",
|
"rr_distribution_method_availability_title": "Maximera tillgänglighet",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Platser-funktionen stöder inte återkommande evenemang. Inaktivera platser-funktionen för att göra det återkommande.",
|
"seats_doesnt_support_recurring": "Platser-funktionen stöder inte återkommande evenemang. Inaktivera platser-funktionen för att göra det återkommande.",
|
||||||
"recurring_event_seats_error": "Återkommande evenemang stöder inte platser-funktionen. Inaktivera platser-funktionen eller gör evenemanget icke-återkommande.",
|
"recurring_event_seats_error": "Återkommande evenemang stöder inte platser-funktionen. Inaktivera platser-funktionen eller gör evenemanget icke-återkommande.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Sparade filter",
|
||||||
"rename": "Byt namn",
|
"rename": "Byt namn",
|
||||||
"rename_segment": "Byt namn på segment",
|
"rename_segment": "Byt namn på segment",
|
||||||
"delete_segment": "Ta bort segment",
|
"delete_segment": "Ta bort segment",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Urvalslista",
|
"picklist": "Urvalslista",
|
||||||
"most_cancelled_bookings": "Mest avbokade",
|
"most_cancelled_bookings": "Mest avbokade",
|
||||||
"interface_language": "Gränssnittsspråk",
|
"interface_language": "Gränssnittsspråk",
|
||||||
|
"interface_language_description": "Ange ditt föredragna språk för bokningsgränssnittet",
|
||||||
"visitors_browser_language": "Besökarens webbläsarspråk",
|
"visitors_browser_language": "Besökarens webbläsarspråk",
|
||||||
"name_or_email": "Namn eller e-post",
|
"name_or_email": "Namn eller e-post",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Om ingen kontakt hittas, återgå till lead-ägaren om sådan finns",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Om ingen kontakt hittas, återgå till lead-ägaren om sådan finns",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Redigera och ändra inställningar för arbetsflöden",
|
"pbac_desc_update_workflows": "Redigera och ändra inställningar för arbetsflöden",
|
||||||
"pbac_desc_delete_workflows": "Ta bort arbetsflöden från systemet",
|
"pbac_desc_delete_workflows": "Ta bort arbetsflöden från systemet",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Tillgänglighet",
|
||||||
|
"pbac_resource_out_of_office": "Inte på kontoret",
|
||||||
"pbac_desc_create_webhooks": "Skapa webhooks",
|
"pbac_desc_create_webhooks": "Skapa webhooks",
|
||||||
"pbac_desc_view_webhooks": "Visa webhooks",
|
"pbac_desc_view_webhooks": "Visa webhooks",
|
||||||
"pbac_desc_update_webhooks": "Uppdatera webhooks",
|
"pbac_desc_update_webhooks": "Uppdatera webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Ta bort webhooks",
|
"pbac_desc_delete_webhooks": "Ta bort webhooks",
|
||||||
|
"pbac_desc_create_availability": "Skapa tillgänglighet",
|
||||||
|
"pbac_desc_view_availability": "Visa tillgänglighet",
|
||||||
|
"pbac_desc_update_availability": "Uppdatera tillgänglighet",
|
||||||
|
"pbac_desc_delete_availability": "Ta bort tillgänglighet",
|
||||||
|
"pbac_desc_create_out_of_office": "Skapa frånvaro",
|
||||||
|
"pbac_desc_view_out_of_office": "Visa frånvaro",
|
||||||
|
"pbac_desc_update_out_of_office": "Uppdatera frånvaro",
|
||||||
|
"pbac_desc_delete_out_of_office": "Ta bort frånvaro",
|
||||||
"pbac_desc_manage_workflows": "Fullständig hantering av alla arbetsflöden",
|
"pbac_desc_manage_workflows": "Fullständig hantering av alla arbetsflöden",
|
||||||
"pbac_desc_create_event_types": "Skapa händelsetyper",
|
"pbac_desc_create_event_types": "Skapa händelsetyper",
|
||||||
"pbac_desc_view_event_types": "Visa händelsetyper",
|
"pbac_desc_view_event_types": "Visa händelsetyper",
|
||||||
@@ -3560,7 +3578,7 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Lastbalansering är inte tillgänglig med Round Robin-grupper",
|
"rr_load_balancing_disabled_with_groups": "Lastbalansering är inte tillgänglig med Round Robin-grupper",
|
||||||
"round_robin_settings_description": "Anpassa standardinställningarna för round robin för det här teamet",
|
"round_robin_settings_description": "Anpassa standardinställningarna för round robin för det här teamet",
|
||||||
"round_robin_settings_updated_successfully": "Round robin-inställningarna har uppdaterats",
|
"round_robin_settings_updated_successfully": "Round robin-inställningarna har uppdaterats",
|
||||||
"booking_creation_time": "Tid för bokningens skapande",
|
"booking_creation_time": "Tid för bokningsskapande",
|
||||||
"meeting_start_time": "Mötesstarttid",
|
"meeting_start_time": "Mötesstarttid",
|
||||||
"members_affected_by_disabling_delegation_credential": "Påverkade medlemmar",
|
"members_affected_by_disabling_delegation_credential": "Påverkade medlemmar",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Inga medlemmar påverkas av att inaktivera delegeringsuppgifter",
|
"no_members_affected_by_disabling_delegation_credential": "Inga medlemmar påverkas av att inaktivera delegeringsuppgifter",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Gizli Toplantı",
|
"secret_meeting": "Gizli Toplantı",
|
||||||
"login_instead": "Ya da giriş yapın",
|
"login_instead": "Ya da giriş yapın",
|
||||||
"create_account": "Hesap Oluştur",
|
"create_account": "Hesap Oluştur",
|
||||||
|
"get_started": "Başlayın",
|
||||||
"confirm_password": "Şifreyi onayla",
|
"confirm_password": "Şifreyi onayla",
|
||||||
"reset_your_password": "E-posta adresinize gönderilen talimatlarla yeni şifrenizi belirleyin.",
|
"reset_your_password": "E-posta adresinize gönderilen talimatlarla yeni şifrenizi belirleyin.",
|
||||||
"org_banner_instructions": "Lütfen {{width}} genişlik ve {{height}} yükseklikte bir resim yükleyin.",
|
"org_banner_instructions": "Lütfen {{width}} genişlik ve {{height}} yükseklikte bir resim yükleyin.",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "Etkinlik Türü Rengi (Açık Tema)",
|
"light_event_type_color": "Etkinlik Türü Rengi (Açık Tema)",
|
||||||
"dark_event_type_color": "Etkinlik Türü Rengi (Koyu Tema)",
|
"dark_event_type_color": "Etkinlik Türü Rengi (Koyu Tema)",
|
||||||
"file_not_named": "Dosya adlandırılmamış [idOrSlug]/[user]",
|
"file_not_named": "Dosya adlandırılmamış [idOrSlug]/[user]",
|
||||||
"create_team": "Ekip Oluştur",
|
"create_team": "Ekip oluştur",
|
||||||
"name": "Ad",
|
"name": "Ad",
|
||||||
"nameless_team": "İsimsiz Takım",
|
"nameless_team": "İsimsiz Takım",
|
||||||
"oauth_clients": "OAuth İstemcileri",
|
"oauth_clients": "OAuth İstemcileri",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Katılımcılar",
|
"attendees": "Katılımcılar",
|
||||||
"add_attendees": "Katılımcı ekle",
|
"add_attendees": "Katılımcı ekle",
|
||||||
"show_advanced_settings": "Gelişmiş ayarları göster",
|
"show_advanced_settings": "Gelişmiş ayarları göster",
|
||||||
|
"hide_advanced_settings": "Gelişmiş ayarları gizle",
|
||||||
"event_name": "Etkinlik Adı",
|
"event_name": "Etkinlik Adı",
|
||||||
"event_name_in_calendar": "Takvimdeki etkinlik adı",
|
"event_name_in_calendar": "Takvimdeki etkinlik adı",
|
||||||
"event_name_tooltip": "Takvimlerde görünecek isim",
|
"event_name_tooltip": "Takvimlerde görünecek isim",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Bu özellik açıldığında ekip üyeleriniz diğer ekip üyelerini göremez.",
|
"make_team_private_description": "Bu özellik açıldığında ekip üyeleriniz diğer ekip üyelerini göremez.",
|
||||||
"you_cannot_see_team_members": "Özel bir ekibin tüm ekip üyelerini göremezsiniz.",
|
"you_cannot_see_team_members": "Özel bir ekibin tüm ekip üyelerini göremezsiniz.",
|
||||||
"you_cannot_see_teams_of_org": "Özel bir kuruluşun takımlarını göremezsiniz.",
|
"you_cannot_see_teams_of_org": "Özel bir kuruluşun takımlarını göremezsiniz.",
|
||||||
"allow_booker_to_select_duration": "Rezervasyon yapan kişinin süreyi seçmesine izin ver",
|
"allow_multiple_durations": "Çoklu sürelere izin ver",
|
||||||
"impersonate_user_tip": "Bu özelliğin tüm kullanımları denetlenir.",
|
"impersonate_user_tip": "Bu özelliğin tüm kullanımları denetlenir.",
|
||||||
"impersonating_user_warning": "\"{{user}}\" kullanıcı adı kimliğine bürünme.",
|
"impersonating_user_warning": "\"{{user}}\" kullanıcı adı kimliğine bürünme.",
|
||||||
"impersonating_stop_instructions": "Durdurmak için buraya tıklayın",
|
"impersonating_stop_instructions": "Durdurmak için buraya tıklayın",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Rezervasyon onayı yapılamadı",
|
"booking_confirmation_failed": "Rezervasyon onayı yapılamadı",
|
||||||
"not_enough_seats": "Yeterli yer yok",
|
"not_enough_seats": "Yeterli yer yok",
|
||||||
"form_builder_field_already_exists": "Bu ada sahip bir alan zaten var",
|
"form_builder_field_already_exists": "Bu ada sahip bir alan zaten var",
|
||||||
|
"guests_field_must_be_multiemail": "Misafirler alanı 'Çoklu e-postalar' türünde olmalıdır",
|
||||||
"show_on_booking_page": "Rezervasyon sayfasında göster",
|
"show_on_booking_page": "Rezervasyon sayfasında göster",
|
||||||
"visit_cancelled_booking": "İptal edilen rezervasyon sayfasını ziyaret edebilirsiniz",
|
"visit_cancelled_booking": "İptal edilen rezervasyon sayfasını ziyaret edebilirsiniz",
|
||||||
"get_started_zapier_templates": "Zapier şablonlarını kullanmaya başlayın",
|
"get_started_zapier_templates": "Zapier şablonlarını kullanmaya başlayın",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Ekip yöneticisi tarafından kilitlendi",
|
"locked_by_team_admin": "Ekip yöneticisi tarafından kilitlendi",
|
||||||
"app_not_connected": "{{appName}} hesabı bağlamadınız.",
|
"app_not_connected": "{{appName}} hesabı bağlamadınız.",
|
||||||
"connect_now": "Hemen bağlan",
|
"connect_now": "Hemen bağlan",
|
||||||
|
"connect_now_unavailable_tooltip": "Şu anda hemen bağlan kullanılamıyor. Lütfen yukarıdaki zaman dilimlerinden rezervasyon yapın veya daha sonra tekrar deneyin.",
|
||||||
"managed_event_dialog_confirm_button_one": "{{count}} üyeyi değiştirin ve bilgilendirin",
|
"managed_event_dialog_confirm_button_one": "{{count}} üyeyi değiştirin ve bilgilendirin",
|
||||||
"managed_event_dialog_confirm_button_other": "{{count}} üyeyi değiştirin ve bilgilendirin",
|
"managed_event_dialog_confirm_button_other": "{{count}} üyeyi değiştirin ve bilgilendirin",
|
||||||
"managed_event_dialog_title_one": "URL /{{slug}}, {{count}} üye için zaten mevcut. Bunu değiştirmek istiyor musunuz?",
|
"managed_event_dialog_title_one": "URL /{{slug}}, {{count}} üye için zaten mevcut. Bunu değiştirmek istiyor musunuz?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Kurumsal",
|
"enterprise": "Kurumsal",
|
||||||
"enterprise_description": "Organizasyonunuzu oluşturmak için Enterprise'a yükseltin",
|
"enterprise_description": "Organizasyonunuzu oluşturmak için Enterprise'a yükseltin",
|
||||||
"create_your_org": "Organizasyonunuzu Oluşturun",
|
"create_your_org": "Organizasyonunuzu Oluşturun",
|
||||||
"create_org": "Kuruluş Oluştur",
|
"create_org": "Organizasyon oluştur",
|
||||||
"create_your_org_description": "Organizasyonlara yükseltin ve bir alt alan adı, birleşik faturalama, Insights, kapsamlı beyaz etiketleme ve daha fazlasını alın",
|
"create_your_org_description": "Organizasyonlara yükseltin ve bir alt alan adı, birleşik faturalama, Insights, kapsamlı beyaz etiketleme ve daha fazlasını alın",
|
||||||
"create_your_enterprise_description": "Enterprise'a yükseltin ve Active Directory Sync, SCIM Otomatik Kullanıcı sağlama, Cal.ai Ses Ajanları, Yönetici API'leri ve daha fazlasına erişim kazanın!",
|
"create_your_enterprise_description": "Enterprise'a yükseltin ve Active Directory Sync, SCIM Otomatik Kullanıcı sağlama, Cal.ai Ses Ajanları, Yönetici API'leri ve daha fazlasına erişim kazanın!",
|
||||||
"other_payment_app_enabled": "Her etkinlik türü için yalnızca bir ödeme uygulamasını etkinleştirebilirsiniz",
|
"other_payment_app_enabled": "Her etkinlik türü için yalnızca bir ödeme uygulamasını etkinleştirebilirsiniz",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Devam ederek",
|
"proceeding_agreement": "Devam ederek",
|
||||||
"terms": "Koşullarımızı",
|
"terms": "Koşullarımızı",
|
||||||
"always_show_x_days": "Her zaman {{x}} gün mevcut",
|
"always_show_x_days": "Her zaman {{x}} gün mevcut",
|
||||||
|
"always_show_x_days_description": "{{x}} günlük rezervasyon penceresini her gün otomatik olarak ilerletir, böylece takviminiz her zaman müsait zaman dilimlerinin sonraki {{x}} gününü gösterir.",
|
||||||
"team_subscription_cancelled_successfully": "Takım aboneliği başarıyla iptal edildi",
|
"team_subscription_cancelled_successfully": "Takım aboneliği başarıyla iptal edildi",
|
||||||
"team_subscription_cancellation_error": "Takım aboneliğini iptal ederken hata oluştu",
|
"team_subscription_cancellation_error": "Takım aboneliğini iptal ederken hata oluştu",
|
||||||
"unable_to_subscribe_to_the_platform": "Platform planına abone olmaya çalışırken bir hata oluştu, lütfen daha sonra tekrar deneyin",
|
"unable_to_subscribe_to_the_platform": "Platform planına abone olmaya çalışırken bir hata oluştu, lütfen daha sonra tekrar deneyin",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "İle biter",
|
"filter_operator_ends_with": "İle biter",
|
||||||
"filter_operator_is_empty": "Boş",
|
"filter_operator_is_empty": "Boş",
|
||||||
"filter_operator_not_empty": "Boş değil",
|
"filter_operator_not_empty": "Boş değil",
|
||||||
"translate_description_button": "Başlığı/açıklamayı ziyaretçinin tarayıcı diline yapay zeka kullanarak çevir",
|
"translate_description_button": "Başlık ve açıklamayı otomatik çevir",
|
||||||
|
"translate_description_button_description": "Başlıkları ve açıklamaları ziyaretçinin tarayıcı diline yapay zeka kullanarak otomatik olarak çevirin.",
|
||||||
"rr_distribution_method": "Dağıtım",
|
"rr_distribution_method": "Dağıtım",
|
||||||
"rr_distribution_method_description": "Maksimum kullanılabilirlik için dağıtımı optimize etmeye veya daha dengeli bir atama sağlamaya olanak tanır.",
|
"rr_distribution_method_description": "Maksimum kullanılabilirlik için dağıtımı optimize etmeye veya daha dengeli bir atama sağlamaya olanak tanır.",
|
||||||
"rr_distribution_method_availability_title": "Kullanılabilirliği maksimize et",
|
"rr_distribution_method_availability_title": "Kullanılabilirliği maksimize et",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Koltuk özelliği tekrarlanan etkinliği desteklemiyor. Tekrarlanan etkinlik yapmak için koltuk özelliğini devre dışı bırakın.",
|
"seats_doesnt_support_recurring": "Koltuk özelliği tekrarlanan etkinliği desteklemiyor. Tekrarlanan etkinlik yapmak için koltuk özelliğini devre dışı bırakın.",
|
||||||
"recurring_event_seats_error": "Tekrarlanan etkinlik, koltuk özelliğini desteklemiyor. Koltuk özelliğini devre dışı bırakın veya etkinliği tekrarlanmayan hale getirin.",
|
"recurring_event_seats_error": "Tekrarlanan etkinlik, koltuk özelliğini desteklemiyor. Koltuk özelliğini devre dışı bırakın veya etkinliği tekrarlanmayan hale getirin.",
|
||||||
"segment": "Segment",
|
"segment": "Segment",
|
||||||
|
"saved_filters": "Kaydedilmiş filtreler",
|
||||||
"rename": "Yeniden adlandır",
|
"rename": "Yeniden adlandır",
|
||||||
"rename_segment": "Segmenti yeniden adlandır",
|
"rename_segment": "Segmenti yeniden adlandır",
|
||||||
"delete_segment": "Segmenti sil",
|
"delete_segment": "Segmenti sil",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Seçim listesi",
|
"picklist": "Seçim listesi",
|
||||||
"most_cancelled_bookings": "En Çok İptal Edilen",
|
"most_cancelled_bookings": "En Çok İptal Edilen",
|
||||||
"interface_language": "Arayüz dili",
|
"interface_language": "Arayüz dili",
|
||||||
|
"interface_language_description": "Rezervasyon arayüzü için tercih ettiğiniz dili ayarlayın",
|
||||||
"visitors_browser_language": "Ziyaretçinin tarayıcı dili",
|
"visitors_browser_language": "Ziyaretçinin tarayıcı dili",
|
||||||
"name_or_email": "İsim veya E-posta",
|
"name_or_email": "İsim veya E-posta",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hiçbir kişi bulunamazsa, eğer varsa müşteri adayı sahibine geri dön",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Hiçbir kişi bulunamazsa, eğer varsa müşteri adayı sahibine geri dön",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "İş akışı ayarlarını düzenle ve değiştir",
|
"pbac_desc_update_workflows": "İş akışı ayarlarını düzenle ve değiştir",
|
||||||
"pbac_desc_delete_workflows": "İş akışlarını sistemden kaldır",
|
"pbac_desc_delete_workflows": "İş akışlarını sistemden kaldır",
|
||||||
"pbac_resource_webhook": "Web kancası",
|
"pbac_resource_webhook": "Web kancası",
|
||||||
|
"pbac_resource_availability": "Müsaitlik",
|
||||||
|
"pbac_resource_out_of_office": "Ofis Dışında",
|
||||||
"pbac_desc_create_webhooks": "Web kancaları oluştur",
|
"pbac_desc_create_webhooks": "Web kancaları oluştur",
|
||||||
"pbac_desc_view_webhooks": "Web kancalarını görüntüle",
|
"pbac_desc_view_webhooks": "Web kancalarını görüntüle",
|
||||||
"pbac_desc_update_webhooks": "Web kancalarını güncelle",
|
"pbac_desc_update_webhooks": "Web kancalarını güncelle",
|
||||||
"pbac_desc_delete_webhooks": "Web kancalarını sil",
|
"pbac_desc_delete_webhooks": "Web kancalarını sil",
|
||||||
|
"pbac_desc_create_availability": "Müsaitlik oluştur",
|
||||||
|
"pbac_desc_view_availability": "Müsaitliği görüntüle",
|
||||||
|
"pbac_desc_update_availability": "Müsaitliği güncelle",
|
||||||
|
"pbac_desc_delete_availability": "Müsaitliği sil",
|
||||||
|
"pbac_desc_create_out_of_office": "Ofis dışı oluştur",
|
||||||
|
"pbac_desc_view_out_of_office": "Ofis dışını görüntüle",
|
||||||
|
"pbac_desc_update_out_of_office": "Ofis dışını güncelle",
|
||||||
|
"pbac_desc_delete_out_of_office": "Ofis dışını sil",
|
||||||
"pbac_desc_manage_workflows": "Tüm iş akışlarına tam yönetim erişimi",
|
"pbac_desc_manage_workflows": "Tüm iş akışlarına tam yönetim erişimi",
|
||||||
"pbac_desc_create_event_types": "Etkinlik türleri oluştur",
|
"pbac_desc_create_event_types": "Etkinlik türleri oluştur",
|
||||||
"pbac_desc_view_event_types": "Etkinlik türlerini görüntüle",
|
"pbac_desc_view_event_types": "Etkinlik türlerini görüntüle",
|
||||||
@@ -3560,8 +3578,8 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "Round Robin Grupları ile Yük Dengeleme kullanılamaz",
|
"rr_load_balancing_disabled_with_groups": "Round Robin Grupları ile Yük Dengeleme kullanılamaz",
|
||||||
"round_robin_settings_description": "Bu takım için varsayılan round robin ayarlarını özelleştirin",
|
"round_robin_settings_description": "Bu takım için varsayılan round robin ayarlarını özelleştirin",
|
||||||
"round_robin_settings_updated_successfully": "Round Robin ayarları başarıyla güncellendi",
|
"round_robin_settings_updated_successfully": "Round Robin ayarları başarıyla güncellendi",
|
||||||
"booking_creation_time": "Rezervasyon Oluşturma Zamanı",
|
"booking_creation_time": "Rezervasyon oluşturma zamanı",
|
||||||
"meeting_start_time": "Toplantı Başlangıç Zamanı",
|
"meeting_start_time": "Toplantı başlangıç zamanı",
|
||||||
"members_affected_by_disabling_delegation_credential": "Etkilenen üyeler",
|
"members_affected_by_disabling_delegation_credential": "Etkilenen üyeler",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "Yetkilendirme kimlik bilgisinin devre dışı bırakılmasından etkilenen üye yok",
|
"no_members_affected_by_disabling_delegation_credential": "Yetkilendirme kimlik bilgisinin devre dışı bırakılmasından etkilenen üye yok",
|
||||||
"download_expense_log": "Gider Kaydını İndir",
|
"download_expense_log": "Gider Kaydını İndir",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Таємна нарада",
|
"secret_meeting": "Таємна нарада",
|
||||||
"login_instead": "Натомість увійти",
|
"login_instead": "Натомість увійти",
|
||||||
"create_account": "Створити обліковий запис",
|
"create_account": "Створити обліковий запис",
|
||||||
|
"get_started": "Почати",
|
||||||
"confirm_password": "Підтвердити пароль",
|
"confirm_password": "Підтвердити пароль",
|
||||||
"reset_your_password": "Установіть новий пароль, дотримуючись інструкцій, надісланих на вашу електронну адресу.",
|
"reset_your_password": "Установіть новий пароль, дотримуючись інструкцій, надісланих на вашу електронну адресу.",
|
||||||
"org_banner_instructions": "Будь ласка, завантажте зображення шириною {{width}} і висотою {{height}}.",
|
"org_banner_instructions": "Будь ласка, завантажте зображення шириною {{width}} і висотою {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Учасники",
|
"attendees": "Учасники",
|
||||||
"add_attendees": "Додати учасників",
|
"add_attendees": "Додати учасників",
|
||||||
"show_advanced_settings": "Показати додаткові параметри",
|
"show_advanced_settings": "Показати додаткові параметри",
|
||||||
|
"hide_advanced_settings": "Приховати розширені налаштування",
|
||||||
"event_name": "Тип заходу",
|
"event_name": "Тип заходу",
|
||||||
"event_name_in_calendar": "Назва події в календарі",
|
"event_name_in_calendar": "Назва події в календарі",
|
||||||
"event_name_tooltip": "Ім’я, яке показуватиметься в календарях",
|
"event_name_tooltip": "Ім’я, яке показуватиметься в календарях",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Зв’язатися з відділом продажів",
|
"contact_sales": "Зв’язатися з відділом продажів",
|
||||||
"error_404": "Помилка 404",
|
"error_404": "Помилка 404",
|
||||||
"default": "За замовчуванням",
|
"default": "За замовчуванням",
|
||||||
"set_to_default": "Установити за замовчуванням",
|
"set_to_default": "Встановити за замовчуванням",
|
||||||
"new_schedule_btn": "Створити розклад",
|
"new_schedule_btn": "Створити розклад",
|
||||||
"add_new_schedule": "Додати новий розклад",
|
"add_new_schedule": "Додати новий розклад",
|
||||||
"add_new_calendar": "Додати новий календар",
|
"add_new_calendar": "Додати новий календар",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Учасники вашої команди не зможуть бачити інших учасників, якщо цей параметр ввімкнено.",
|
"make_team_private_description": "Учасники вашої команди не зможуть бачити інших учасників, якщо цей параметр ввімкнено.",
|
||||||
"you_cannot_see_team_members": "Ви не можете бачити всіх учасників приватної команди.",
|
"you_cannot_see_team_members": "Ви не можете бачити всіх учасників приватної команди.",
|
||||||
"you_cannot_see_teams_of_org": "Ви не можете бачити команди приватної організації.",
|
"you_cannot_see_teams_of_org": "Ви не можете бачити команди приватної організації.",
|
||||||
"allow_booker_to_select_duration": "Дозволити автору бронювання вибирати тривалість",
|
"allow_multiple_durations": "Дозволити кілька тривалостей",
|
||||||
"impersonate_user_tip": "Усі випадки використання цієї функції підпадають під аудит.",
|
"impersonate_user_tip": "Усі випадки використання цієї функції підпадають під аудит.",
|
||||||
"impersonating_user_warning": "Виконується роль користувача {{user}}.",
|
"impersonating_user_warning": "Виконується роль користувача {{user}}.",
|
||||||
"impersonating_stop_instructions": "Натисніть тут, щоб зупинити",
|
"impersonating_stop_instructions": "Натисніть тут, щоб зупинити",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Не вдалося підтвердити бронювання",
|
"booking_confirmation_failed": "Не вдалося підтвердити бронювання",
|
||||||
"not_enough_seats": "Недостатньо місць",
|
"not_enough_seats": "Недостатньо місць",
|
||||||
"form_builder_field_already_exists": "Поле з такою назвою вже існує",
|
"form_builder_field_already_exists": "Поле з такою назвою вже існує",
|
||||||
|
"guests_field_must_be_multiemail": "Поле гостей повинно бути типу 'Кілька електронних адрес'",
|
||||||
"show_on_booking_page": "Показувати на сторінці бронювання",
|
"show_on_booking_page": "Показувати на сторінці бронювання",
|
||||||
"visit_cancelled_booking": "Ви можете відвідати сторінку скасованого бронювання",
|
"visit_cancelled_booking": "Ви можете відвідати сторінку скасованого бронювання",
|
||||||
"get_started_zapier_templates": "Розпочніть роботу із шаблонами Zapier",
|
"get_started_zapier_templates": "Розпочніть роботу із шаблонами Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Заблоковано адміністратором команди",
|
"locked_by_team_admin": "Заблоковано адміністратором команди",
|
||||||
"app_not_connected": "Ви не під’єднали обліковий запис {{appName}}.",
|
"app_not_connected": "Ви не під’єднали обліковий запис {{appName}}.",
|
||||||
"connect_now": "Під’єднати",
|
"connect_now": "Під’єднати",
|
||||||
|
"connect_now_unavailable_tooltip": "Підключення зараз недоступне. Будь ласка, забронюйте з доступних слотів вище або спробуйте пізніше.",
|
||||||
"managed_event_dialog_confirm_button_one": "Замінити й повідомити {{count}} учасника",
|
"managed_event_dialog_confirm_button_one": "Замінити й повідомити {{count}} учасника",
|
||||||
"managed_event_dialog_confirm_button_other": "Замінити й повідомити учасників ({{count}})",
|
"managed_event_dialog_confirm_button_other": "Замінити й повідомити учасників ({{count}})",
|
||||||
"managed_event_dialog_title_one": "URL-адреса /{{slug}} вже існує для {{count}} учасника. Замінити її?",
|
"managed_event_dialog_title_one": "URL-адреса /{{slug}} вже існує для {{count}} учасника. Замінити її?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Продовжуючи, ви погоджуєтеся з нашими",
|
"proceeding_agreement": "Продовжуючи, ви погоджуєтеся з нашими",
|
||||||
"terms": "Умовами",
|
"terms": "Умовами",
|
||||||
"always_show_x_days": "Завжди доступно {{x}} днів",
|
"always_show_x_days": "Завжди доступно {{x}} днів",
|
||||||
|
"always_show_x_days_description": "Автоматично переміщує вікно бронювання на {{x}} днів вперед щодня, забезпечуючи, щоб ваш календар завжди показував наступні {{x}} днів доступних слотів.",
|
||||||
"team_subscription_cancelled_successfully": "Підписку команди успішно скасовано",
|
"team_subscription_cancelled_successfully": "Підписку команди успішно скасовано",
|
||||||
"team_subscription_cancellation_error": "Помилка під час скасування підписки команди",
|
"team_subscription_cancellation_error": "Помилка під час скасування підписки команди",
|
||||||
"unable_to_subscribe_to_the_platform": "Сталася помилка під час спроби підписатися на план платформи, будь ласка, спробуйте пізніше",
|
"unable_to_subscribe_to_the_platform": "Сталася помилка під час спроби підписатися на план платформи, будь ласка, спробуйте пізніше",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Закінчується на",
|
"filter_operator_ends_with": "Закінчується на",
|
||||||
"filter_operator_is_empty": "Порожньо",
|
"filter_operator_is_empty": "Порожньо",
|
||||||
"filter_operator_not_empty": "Не порожньо",
|
"filter_operator_not_empty": "Не порожньо",
|
||||||
"translate_description_button": "Перекласти заголовок/опис мовою браузера відвідувача за допомогою ШІ",
|
"translate_description_button": "Автоматичний переклад назви та опису",
|
||||||
|
"translate_description_button_description": "Автоматично перекладати назви та описи мовою браузера відвідувача за допомогою ШІ.",
|
||||||
"rr_distribution_method": "Розподіл",
|
"rr_distribution_method": "Розподіл",
|
||||||
"rr_distribution_method_description": "Дозволяє оптимізувати розподіл для максимальної доступності або для більш збалансованого розподілу завдань.",
|
"rr_distribution_method_description": "Дозволяє оптимізувати розподіл для максимальної доступності або для більш збалансованого розподілу завдань.",
|
||||||
"rr_distribution_method_availability_title": "Максимізувати доступність",
|
"rr_distribution_method_availability_title": "Максимізувати доступність",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Функція місць не підтримує повторювані події. Вимкніть функцію місць, щоб зробити подію повторюваною.",
|
"seats_doesnt_support_recurring": "Функція місць не підтримує повторювані події. Вимкніть функцію місць, щоб зробити подію повторюваною.",
|
||||||
"recurring_event_seats_error": "Повторювана подія не підтримує функцію місць. Вимкніть функцію місць або зробіть подію неповторюваною.",
|
"recurring_event_seats_error": "Повторювана подія не підтримує функцію місць. Вимкніть функцію місць або зробіть подію неповторюваною.",
|
||||||
"segment": "Сегмент",
|
"segment": "Сегмент",
|
||||||
|
"saved_filters": "Збережені фільтри",
|
||||||
"rename": "Перейменувати",
|
"rename": "Перейменувати",
|
||||||
"rename_segment": "Перейменувати сегмент",
|
"rename_segment": "Перейменувати сегмент",
|
||||||
"delete_segment": "Видалити сегмент",
|
"delete_segment": "Видалити сегмент",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Список вибору",
|
"picklist": "Список вибору",
|
||||||
"most_cancelled_bookings": "Найбільше скасованих бронювань",
|
"most_cancelled_bookings": "Найбільше скасованих бронювань",
|
||||||
"interface_language": "Мова інтерфейсу",
|
"interface_language": "Мова інтерфейсу",
|
||||||
|
"interface_language_description": "Встановіть бажану мову для інтерфейсу бронювання",
|
||||||
"visitors_browser_language": "Мова браузера відвідувача",
|
"visitors_browser_language": "Мова браузера відвідувача",
|
||||||
"name_or_email": "Ім'я або електронна пошта",
|
"name_or_email": "Ім'я або електронна пошта",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Якщо контакт не знайдено, використовувати власника ліда, якщо він існує",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Якщо контакт не знайдено, використовувати власника ліда, якщо він існує",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Редагування та зміна налаштувань робочих процесів",
|
"pbac_desc_update_workflows": "Редагування та зміна налаштувань робочих процесів",
|
||||||
"pbac_desc_delete_workflows": "Видалення робочих процесів із системи",
|
"pbac_desc_delete_workflows": "Видалення робочих процесів із системи",
|
||||||
"pbac_resource_webhook": "Вебгук",
|
"pbac_resource_webhook": "Вебгук",
|
||||||
|
"pbac_resource_availability": "Доступність",
|
||||||
|
"pbac_resource_out_of_office": "Поза офісом",
|
||||||
"pbac_desc_create_webhooks": "Створення вебгуків",
|
"pbac_desc_create_webhooks": "Створення вебгуків",
|
||||||
"pbac_desc_view_webhooks": "Перегляд вебгуків",
|
"pbac_desc_view_webhooks": "Перегляд вебгуків",
|
||||||
"pbac_desc_update_webhooks": "Оновлення вебгуків",
|
"pbac_desc_update_webhooks": "Оновлення вебгуків",
|
||||||
"pbac_desc_delete_webhooks": "Видалення вебгуків",
|
"pbac_desc_delete_webhooks": "Видалення вебгуків",
|
||||||
|
"pbac_desc_create_availability": "Створення доступності",
|
||||||
|
"pbac_desc_view_availability": "Перегляд доступності",
|
||||||
|
"pbac_desc_update_availability": "Оновлення доступності",
|
||||||
|
"pbac_desc_delete_availability": "Видалення доступності",
|
||||||
|
"pbac_desc_create_out_of_office": "Створення режиму відсутності в офісі",
|
||||||
|
"pbac_desc_view_out_of_office": "Перегляд режиму відсутності в офісі",
|
||||||
|
"pbac_desc_update_out_of_office": "Оновлення режиму відсутності в офісі",
|
||||||
|
"pbac_desc_delete_out_of_office": "Видалення режиму відсутності в офісі",
|
||||||
"pbac_desc_manage_workflows": "Повний доступ до керування всіма робочими процесами",
|
"pbac_desc_manage_workflows": "Повний доступ до керування всіма робочими процесами",
|
||||||
"pbac_desc_create_event_types": "Створення типів заходів",
|
"pbac_desc_create_event_types": "Створення типів заходів",
|
||||||
"pbac_desc_view_event_types": "Перегляд типів заходів",
|
"pbac_desc_view_event_types": "Перегляд типів заходів",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "Cuộc họp bí mật",
|
"secret_meeting": "Cuộc họp bí mật",
|
||||||
"login_instead": "Đăng nhập",
|
"login_instead": "Đăng nhập",
|
||||||
"create_account": "Tạo tài khoản",
|
"create_account": "Tạo tài khoản",
|
||||||
|
"get_started": "Bắt đầu",
|
||||||
"confirm_password": "Xác nhận mật khẩu",
|
"confirm_password": "Xác nhận mật khẩu",
|
||||||
"reset_your_password": "Thiết lập mật khẩu mới bằng những chỉ dẫn được gửi đến địa chỉ email của bạn.",
|
"reset_your_password": "Thiết lập mật khẩu mới bằng những chỉ dẫn được gửi đến địa chỉ email của bạn.",
|
||||||
"org_banner_instructions": "Vui lòng tải lên hình ảnh có chiều rộng {{width}} và chiều cao {{height}}.",
|
"org_banner_instructions": "Vui lòng tải lên hình ảnh có chiều rộng {{width}} và chiều cao {{height}}.",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "Người tham dự",
|
"attendees": "Người tham dự",
|
||||||
"add_attendees": "Thêm người tham dự",
|
"add_attendees": "Thêm người tham dự",
|
||||||
"show_advanced_settings": "Hiển thị cài đặt nâng cao",
|
"show_advanced_settings": "Hiển thị cài đặt nâng cao",
|
||||||
|
"hide_advanced_settings": "Ẩn cài đặt nâng cao",
|
||||||
"event_name": "Tên sự kiện",
|
"event_name": "Tên sự kiện",
|
||||||
"event_name_in_calendar": "Tên sự kiện trong lịch",
|
"event_name_in_calendar": "Tên sự kiện trong lịch",
|
||||||
"event_name_tooltip": "Tên sẽ xuất hiện trong lịch",
|
"event_name_tooltip": "Tên sẽ xuất hiện trong lịch",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "Liên hệ với bộ phận bán hàng",
|
"contact_sales": "Liên hệ với bộ phận bán hàng",
|
||||||
"error_404": "Lỗi 404",
|
"error_404": "Lỗi 404",
|
||||||
"default": "Mặc định",
|
"default": "Mặc định",
|
||||||
"set_to_default": "Đặt thành Mặc định",
|
"set_to_default": "Đặt làm mặc định",
|
||||||
"new_schedule_btn": "Lịch mới",
|
"new_schedule_btn": "Lịch mới",
|
||||||
"add_new_schedule": "Thêm một lịch mới",
|
"add_new_schedule": "Thêm một lịch mới",
|
||||||
"add_new_calendar": "Thêm một lịch mới",
|
"add_new_calendar": "Thêm một lịch mới",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "Thành viên nhóm của bạn sẽ không thể thấy thành viên nhóm khác khi cái này được bật.",
|
"make_team_private_description": "Thành viên nhóm của bạn sẽ không thể thấy thành viên nhóm khác khi cái này được bật.",
|
||||||
"you_cannot_see_team_members": "Bạn không thể thấy tất cả thành viên nhóm của một nhóm riêng tư.",
|
"you_cannot_see_team_members": "Bạn không thể thấy tất cả thành viên nhóm của một nhóm riêng tư.",
|
||||||
"you_cannot_see_teams_of_org": "Bạn không thể thấy các nhóm của một tổ chức riêng tư.",
|
"you_cannot_see_teams_of_org": "Bạn không thể thấy các nhóm của một tổ chức riêng tư.",
|
||||||
"allow_booker_to_select_duration": "Cho phép người đặt hẹn chọn khoảng thời gian",
|
"allow_multiple_durations": "Cho phép nhiều khoảng thời gian",
|
||||||
"impersonate_user_tip": "Tất cả những lần dùng tính năng này đều bị kiểm toán.",
|
"impersonate_user_tip": "Tất cả những lần dùng tính năng này đều bị kiểm toán.",
|
||||||
"impersonating_user_warning": "Đang mạo danh tên người dùng \"{{user}}\".",
|
"impersonating_user_warning": "Đang mạo danh tên người dùng \"{{user}}\".",
|
||||||
"impersonating_stop_instructions": "Nhấp vào đây để ngừng lại",
|
"impersonating_stop_instructions": "Nhấp vào đây để ngừng lại",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "Xác nhận lịch hẹn không thành công",
|
"booking_confirmation_failed": "Xác nhận lịch hẹn không thành công",
|
||||||
"not_enough_seats": "Không đủ chỗ",
|
"not_enough_seats": "Không đủ chỗ",
|
||||||
"form_builder_field_already_exists": "Đã tồn tại một trường có tên này",
|
"form_builder_field_already_exists": "Đã tồn tại một trường có tên này",
|
||||||
|
"guests_field_must_be_multiemail": "Trường khách mời phải có kiểu 'Nhiều email'",
|
||||||
"show_on_booking_page": "Hiện trên trang lịch hẹn",
|
"show_on_booking_page": "Hiện trên trang lịch hẹn",
|
||||||
"visit_cancelled_booking": "Bạn có thể truy cập trang đặt chỗ đã hủy",
|
"visit_cancelled_booking": "Bạn có thể truy cập trang đặt chỗ đã hủy",
|
||||||
"get_started_zapier_templates": "Bắt đầu với các mẫu Zapier",
|
"get_started_zapier_templates": "Bắt đầu với các mẫu Zapier",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "Bị khoá bởi quản trị viên của nhóm",
|
"locked_by_team_admin": "Bị khoá bởi quản trị viên của nhóm",
|
||||||
"app_not_connected": "Bạn không có tài khoản {{appName}} đã được kết nối.",
|
"app_not_connected": "Bạn không có tài khoản {{appName}} đã được kết nối.",
|
||||||
"connect_now": "Kết nối ngay",
|
"connect_now": "Kết nối ngay",
|
||||||
|
"connect_now_unavailable_tooltip": "Kết nối ngay hiện không khả dụng. Vui lòng đặt lịch từ các khung giờ phía trên hoặc thử lại sau.",
|
||||||
"managed_event_dialog_confirm_button_one": "Thay thế & thông báo cho {{count}} thành viên",
|
"managed_event_dialog_confirm_button_one": "Thay thế & thông báo cho {{count}} thành viên",
|
||||||
"managed_event_dialog_confirm_button_other": "Thay thế & thông báo cho {{count}} thành viên",
|
"managed_event_dialog_confirm_button_other": "Thay thế & thông báo cho {{count}} thành viên",
|
||||||
"managed_event_dialog_title_one": "URL / {{slug}} đã có sẵn cho {{count}} thành viên. Bạn có muốn thay thế nó?",
|
"managed_event_dialog_title_one": "URL / {{slug}} đã có sẵn cho {{count}} thành viên. Bạn có muốn thay thế nó?",
|
||||||
@@ -2688,7 +2692,7 @@
|
|||||||
"enterprise": "Doanh nghiệp",
|
"enterprise": "Doanh nghiệp",
|
||||||
"enterprise_description": "Nâng cấp lên Enterprise để tạo Tổ chức của bạn",
|
"enterprise_description": "Nâng cấp lên Enterprise để tạo Tổ chức của bạn",
|
||||||
"create_your_org": "Tạo Tổ chức của bạn",
|
"create_your_org": "Tạo Tổ chức của bạn",
|
||||||
"create_org": "Tạo Tổ chức",
|
"create_org": "Tạo tổ chức",
|
||||||
"create_your_org_description": "Nâng cấp lên Tổ chức và nhận một tên miền phụ, thanh toán hợp nhất, Insights, nhãn trắng mở rộng và nhiều hơn nữa",
|
"create_your_org_description": "Nâng cấp lên Tổ chức và nhận một tên miền phụ, thanh toán hợp nhất, Insights, nhãn trắng mở rộng và nhiều hơn nữa",
|
||||||
"create_your_enterprise_description": "Nâng cấp lên Enterprise và truy cập vào Đồng bộ Active Directory, SCIM Tự động cung cấp người dùng, Cal.ai Voice Agents, Admin APIs và nhiều hơn nữa!",
|
"create_your_enterprise_description": "Nâng cấp lên Enterprise và truy cập vào Đồng bộ Active Directory, SCIM Tự động cung cấp người dùng, Cal.ai Voice Agents, Admin APIs và nhiều hơn nữa!",
|
||||||
"other_payment_app_enabled": "Bạn chỉ có thể kích hoạt một ứng dụng thanh toán cho mỗi loại sự kiện",
|
"other_payment_app_enabled": "Bạn chỉ có thể kích hoạt một ứng dụng thanh toán cho mỗi loại sự kiện",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "Bằng việc tiếp tục, bạn đồng ý với",
|
"proceeding_agreement": "Bằng việc tiếp tục, bạn đồng ý với",
|
||||||
"terms": "Điều khoản",
|
"terms": "Điều khoản",
|
||||||
"always_show_x_days": "Luôn hiển thị {{x}} ngày có sẵn",
|
"always_show_x_days": "Luôn hiển thị {{x}} ngày có sẵn",
|
||||||
|
"always_show_x_days_description": "Tự động di chuyển cửa sổ đặt lịch {{x}} ngày mỗi ngày, đảm bảo lịch của bạn luôn hiển thị {{x}} ngày tiếp theo có các khung giờ trống.",
|
||||||
"team_subscription_cancelled_successfully": "Đã hủy đăng ký nhóm thành công",
|
"team_subscription_cancelled_successfully": "Đã hủy đăng ký nhóm thành công",
|
||||||
"team_subscription_cancellation_error": "Lỗi khi hủy đăng ký nhóm",
|
"team_subscription_cancellation_error": "Lỗi khi hủy đăng ký nhóm",
|
||||||
"unable_to_subscribe_to_the_platform": "Đã xảy ra lỗi khi cố gắng đăng ký gói nền tảng, vui lòng thử lại sau",
|
"unable_to_subscribe_to_the_platform": "Đã xảy ra lỗi khi cố gắng đăng ký gói nền tảng, vui lòng thử lại sau",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "Kết thúc bằng",
|
"filter_operator_ends_with": "Kết thúc bằng",
|
||||||
"filter_operator_is_empty": "Trống",
|
"filter_operator_is_empty": "Trống",
|
||||||
"filter_operator_not_empty": "Không trống",
|
"filter_operator_not_empty": "Không trống",
|
||||||
"translate_description_button": "Dùng AI để dịch tiêu đề/mô tả sang ngôn ngữ trình duyệt của khách",
|
"translate_description_button": "Tự động dịch tiêu đề và mô tả",
|
||||||
|
"translate_description_button_description": "Tự động dịch tiêu đề và mô tả sang ngôn ngữ trình duyệt của khách truy cập bằng AI.",
|
||||||
"rr_distribution_method": "Phân phối",
|
"rr_distribution_method": "Phân phối",
|
||||||
"rr_distribution_method_description": "Cho phép tối ưu hóa việc phân phối để đạt khả năng sẵn sàng tối đa hoặc hướng đến việc phân công cân bằng hơn.",
|
"rr_distribution_method_description": "Cho phép tối ưu hóa việc phân phối để đạt khả năng sẵn sàng tối đa hoặc hướng đến việc phân công cân bằng hơn.",
|
||||||
"rr_distribution_method_availability_title": "Tối đa hóa khả năng sẵn sàng",
|
"rr_distribution_method_availability_title": "Tối đa hóa khả năng sẵn sàng",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "Tính năng chỗ ngồi không hỗ trợ sự kiện định kỳ. Hãy tắt tính năng chỗ ngồi để đặt thành định kỳ.",
|
"seats_doesnt_support_recurring": "Tính năng chỗ ngồi không hỗ trợ sự kiện định kỳ. Hãy tắt tính năng chỗ ngồi để đặt thành định kỳ.",
|
||||||
"recurring_event_seats_error": "Sự kiện định kỳ không hỗ trợ tính năng chỗ ngồi. Hãy tắt tính năng chỗ ngồi hoặc đặt sự kiện thành không định kỳ.",
|
"recurring_event_seats_error": "Sự kiện định kỳ không hỗ trợ tính năng chỗ ngồi. Hãy tắt tính năng chỗ ngồi hoặc đặt sự kiện thành không định kỳ.",
|
||||||
"segment": "Phân đoạn",
|
"segment": "Phân đoạn",
|
||||||
|
"saved_filters": "Bộ lọc đã lưu",
|
||||||
"rename": "Đổi tên",
|
"rename": "Đổi tên",
|
||||||
"rename_segment": "Đổi tên phân đoạn",
|
"rename_segment": "Đổi tên phân đoạn",
|
||||||
"delete_segment": "Xóa phân đoạn",
|
"delete_segment": "Xóa phân đoạn",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "Danh sách chọn",
|
"picklist": "Danh sách chọn",
|
||||||
"most_cancelled_bookings": "Bị hủy nhiều nhất",
|
"most_cancelled_bookings": "Bị hủy nhiều nhất",
|
||||||
"interface_language": "Ngôn ngữ giao diện",
|
"interface_language": "Ngôn ngữ giao diện",
|
||||||
|
"interface_language_description": "Đặt ngôn ngữ ưa thích cho giao diện đặt lịch",
|
||||||
"visitors_browser_language": "Ngôn ngữ trình duyệt của khách",
|
"visitors_browser_language": "Ngôn ngữ trình duyệt của khách",
|
||||||
"name_or_email": "Tên hoặc email",
|
"name_or_email": "Tên hoặc email",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "Nếu không tìm thấy liên hệ, chuyển sang chủ sở hữu khách hàng tiềm năng nếu tồn tại",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "Nếu không tìm thấy liên hệ, chuyển sang chủ sở hữu khách hàng tiềm năng nếu tồn tại",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "Chỉnh sửa và điều chỉnh cài đặt tiến độ công việc",
|
"pbac_desc_update_workflows": "Chỉnh sửa và điều chỉnh cài đặt tiến độ công việc",
|
||||||
"pbac_desc_delete_workflows": "Xóa tiến độ công việc khỏi hệ thống",
|
"pbac_desc_delete_workflows": "Xóa tiến độ công việc khỏi hệ thống",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "Lịch khả dụng",
|
||||||
|
"pbac_resource_out_of_office": "Vắng mặt",
|
||||||
"pbac_desc_create_webhooks": "Tạo webhooks",
|
"pbac_desc_create_webhooks": "Tạo webhooks",
|
||||||
"pbac_desc_view_webhooks": "Xem webhooks",
|
"pbac_desc_view_webhooks": "Xem webhooks",
|
||||||
"pbac_desc_update_webhooks": "Cập nhật webhooks",
|
"pbac_desc_update_webhooks": "Cập nhật webhooks",
|
||||||
"pbac_desc_delete_webhooks": "Xóa webhooks",
|
"pbac_desc_delete_webhooks": "Xóa webhooks",
|
||||||
|
"pbac_desc_create_availability": "Tạo lịch khả dụng",
|
||||||
|
"pbac_desc_view_availability": "Xem lịch khả dụng",
|
||||||
|
"pbac_desc_update_availability": "Cập nhật lịch khả dụng",
|
||||||
|
"pbac_desc_delete_availability": "Xóa lịch khả dụng",
|
||||||
|
"pbac_desc_create_out_of_office": "Tạo trạng thái vắng mặt",
|
||||||
|
"pbac_desc_view_out_of_office": "Xem trạng thái vắng mặt",
|
||||||
|
"pbac_desc_update_out_of_office": "Cập nhật trạng thái vắng mặt",
|
||||||
|
"pbac_desc_delete_out_of_office": "Xóa trạng thái vắng mặt",
|
||||||
"pbac_desc_manage_workflows": "Quyền truy cập quản lý đầy đủ đối với tất cả tiến độ công việc",
|
"pbac_desc_manage_workflows": "Quyền truy cập quản lý đầy đủ đối với tất cả tiến độ công việc",
|
||||||
"pbac_desc_create_event_types": "Tạo loại sự kiện",
|
"pbac_desc_create_event_types": "Tạo loại sự kiện",
|
||||||
"pbac_desc_view_event_types": "Xem loại sự kiện",
|
"pbac_desc_view_event_types": "Xem loại sự kiện",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "私密会议",
|
"secret_meeting": "私密会议",
|
||||||
"login_instead": "改为登录",
|
"login_instead": "改为登录",
|
||||||
"create_account": "创建账户",
|
"create_account": "创建账户",
|
||||||
|
"get_started": "开始使用",
|
||||||
"confirm_password": "确认密码",
|
"confirm_password": "确认密码",
|
||||||
"reset_your_password": "按照发送到您的电子邮件地址的说明设置新密码。",
|
"reset_your_password": "按照发送到您的电子邮件地址的说明设置新密码。",
|
||||||
"org_banner_instructions": "请上传一张宽为 {{width}},高为 {{height}} 的照片。",
|
"org_banner_instructions": "请上传一张宽为 {{width}},高为 {{height}} 的照片。",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "事件类型颜色(浅色主题)",
|
"light_event_type_color": "事件类型颜色(浅色主题)",
|
||||||
"dark_event_type_color": "事件类型颜色(深色主题)",
|
"dark_event_type_color": "事件类型颜色(深色主题)",
|
||||||
"file_not_named": "文件名不是 [idOrSlug]/[user]",
|
"file_not_named": "文件名不是 [idOrSlug]/[user]",
|
||||||
"create_team": "新建团队",
|
"create_team": "创建团队",
|
||||||
"name": "名称",
|
"name": "名称",
|
||||||
"nameless_team": "未命名团队",
|
"nameless_team": "未命名团队",
|
||||||
"oauth_clients": "OAuth 客户端",
|
"oauth_clients": "OAuth 客户端",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "参与者",
|
"attendees": "参与者",
|
||||||
"add_attendees": "添加参与者",
|
"add_attendees": "添加参与者",
|
||||||
"show_advanced_settings": "显示高级设置",
|
"show_advanced_settings": "显示高级设置",
|
||||||
|
"hide_advanced_settings": "隐藏高级设置",
|
||||||
"event_name": "活动名称",
|
"event_name": "活动名称",
|
||||||
"event_name_in_calendar": "日历中的活动名称",
|
"event_name_in_calendar": "日历中的活动名称",
|
||||||
"event_name_tooltip": "在日历中显示的名称",
|
"event_name_tooltip": "在日历中显示的名称",
|
||||||
@@ -1231,7 +1233,7 @@
|
|||||||
"contact_sales": "联系销售",
|
"contact_sales": "联系销售",
|
||||||
"error_404": "错误 404",
|
"error_404": "错误 404",
|
||||||
"default": "默认值",
|
"default": "默认值",
|
||||||
"set_to_default": "设置为默认值",
|
"set_to_default": "设置为默认",
|
||||||
"new_schedule_btn": "新建时间表",
|
"new_schedule_btn": "新建时间表",
|
||||||
"add_new_schedule": "添加新的时间表",
|
"add_new_schedule": "添加新的时间表",
|
||||||
"add_new_calendar": "添加新日历",
|
"add_new_calendar": "添加新日历",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "开启此选项后,您的团队成员将无法看到其他团队成员。",
|
"make_team_private_description": "开启此选项后,您的团队成员将无法看到其他团队成员。",
|
||||||
"you_cannot_see_team_members": "您无法看到私密团队的所有团队成员。",
|
"you_cannot_see_team_members": "您无法看到私密团队的所有团队成员。",
|
||||||
"you_cannot_see_teams_of_org": "您无法看到私有组织的团队。",
|
"you_cannot_see_teams_of_org": "您无法看到私有组织的团队。",
|
||||||
"allow_booker_to_select_duration": "允许预约者选择时长",
|
"allow_multiple_durations": "允许多种时长",
|
||||||
"impersonate_user_tip": "此功能的所有使用都已审核。",
|
"impersonate_user_tip": "此功能的所有使用都已审核。",
|
||||||
"impersonating_user_warning": "正在模拟用户名“{{user}}”。",
|
"impersonating_user_warning": "正在模拟用户名“{{user}}”。",
|
||||||
"impersonating_stop_instructions": "点击此处停止",
|
"impersonating_stop_instructions": "点击此处停止",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "预约确认失败",
|
"booking_confirmation_failed": "预约确认失败",
|
||||||
"not_enough_seats": "位置不足",
|
"not_enough_seats": "位置不足",
|
||||||
"form_builder_field_already_exists": "具有此名称的字段已存在",
|
"form_builder_field_already_exists": "具有此名称的字段已存在",
|
||||||
|
"guests_field_must_be_multiemail": "访客字段必须为“多个邮箱”类型",
|
||||||
"show_on_booking_page": "在预约页面上显示",
|
"show_on_booking_page": "在预约页面上显示",
|
||||||
"visit_cancelled_booking": "您可以访问已取消的预订页面",
|
"visit_cancelled_booking": "您可以访问已取消的预订页面",
|
||||||
"get_started_zapier_templates": "开始使用 Zapier 模板",
|
"get_started_zapier_templates": "开始使用 Zapier 模板",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "被团队管理员锁定",
|
"locked_by_team_admin": "被团队管理员锁定",
|
||||||
"app_not_connected": "您尚未连接 {{appName}} 帐户。",
|
"app_not_connected": "您尚未连接 {{appName}} 帐户。",
|
||||||
"connect_now": "立即连接",
|
"connect_now": "立即连接",
|
||||||
|
"connect_now_unavailable_tooltip": "“立即连接”当前不可用。请从上方的时间段中预订或稍后再试。",
|
||||||
"managed_event_dialog_confirm_button_one": "替换并通知 {{count}} 个成员",
|
"managed_event_dialog_confirm_button_one": "替换并通知 {{count}} 个成员",
|
||||||
"managed_event_dialog_confirm_button_other": "替换并通知 {{count}} 个成员",
|
"managed_event_dialog_confirm_button_other": "替换并通知 {{count}} 个成员",
|
||||||
"managed_event_dialog_title_one": "{{count}} 个成员已具有链接/{{slug}}。是否要将其替换?",
|
"managed_event_dialog_title_one": "{{count}} 个成员已具有链接/{{slug}}。是否要将其替换?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "继续即表示您同意我们的",
|
"proceeding_agreement": "继续即表示您同意我们的",
|
||||||
"terms": "条款",
|
"terms": "条款",
|
||||||
"always_show_x_days": "始终显示{{x}}天的可用时间",
|
"always_show_x_days": "始终显示{{x}}天的可用时间",
|
||||||
|
"always_show_x_days_description": "自动将{{x}}天的预订窗口每天向前移动,确保您的日历始终显示接下来{{x}}天的可用时间段。",
|
||||||
"team_subscription_cancelled_successfully": "团队订阅已成功取消",
|
"team_subscription_cancelled_successfully": "团队订阅已成功取消",
|
||||||
"team_subscription_cancellation_error": "取消团队订阅时出错",
|
"team_subscription_cancellation_error": "取消团队订阅时出错",
|
||||||
"unable_to_subscribe_to_the_platform": "尝试订阅平台计划时发生错误,请稍后再试",
|
"unable_to_subscribe_to_the_platform": "尝试订阅平台计划时发生错误,请稍后再试",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "结尾为",
|
"filter_operator_ends_with": "结尾为",
|
||||||
"filter_operator_is_empty": "为空",
|
"filter_operator_is_empty": "为空",
|
||||||
"filter_operator_not_empty": "不为空",
|
"filter_operator_not_empty": "不为空",
|
||||||
"translate_description_button": "使用AI将标题/描述自动翻译为访问者浏览器的语言",
|
"translate_description_button": "自动翻译标题和描述",
|
||||||
|
"translate_description_button_description": "使用AI自动将标题和描述翻译为访客浏览器的语言。",
|
||||||
"rr_distribution_method": "分配方式",
|
"rr_distribution_method": "分配方式",
|
||||||
"rr_distribution_method_description": "可以优化分配方式以实现最大可用性或更均衡的任务分配。",
|
"rr_distribution_method_description": "可以优化分配方式以实现最大可用性或更均衡的任务分配。",
|
||||||
"rr_distribution_method_availability_title": "最大化可用性",
|
"rr_distribution_method_availability_title": "最大化可用性",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "座位功能不支持重复活动。禁用座位功能以使其成为重复活动。",
|
"seats_doesnt_support_recurring": "座位功能不支持重复活动。禁用座位功能以使其成为重复活动。",
|
||||||
"recurring_event_seats_error": "重复活动不支持座位功能。请禁用座位功能或将活动设置为非重复。",
|
"recurring_event_seats_error": "重复活动不支持座位功能。请禁用座位功能或将活动设置为非重复。",
|
||||||
"segment": "细分群组",
|
"segment": "细分群组",
|
||||||
|
"saved_filters": "已保存的筛选器",
|
||||||
"rename": "重命名",
|
"rename": "重命名",
|
||||||
"rename_segment": "重命名细分群组",
|
"rename_segment": "重命名细分群组",
|
||||||
"delete_segment": "删除细分群组",
|
"delete_segment": "删除细分群组",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "选择列表",
|
"picklist": "选择列表",
|
||||||
"most_cancelled_bookings": "取消最多的预约",
|
"most_cancelled_bookings": "取消最多的预约",
|
||||||
"interface_language": "界面语言",
|
"interface_language": "界面语言",
|
||||||
|
"interface_language_description": "设置预订界面的首选语言",
|
||||||
"visitors_browser_language": "访客的浏览器语言",
|
"visitors_browser_language": "访客的浏览器语言",
|
||||||
"name_or_email": "姓名或电子邮箱",
|
"name_or_email": "姓名或电子邮箱",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "如果未找到联系人,则在存在潜在客户所有者的情况下回退到潜在客户所有者",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "如果未找到联系人,则在存在潜在客户所有者的情况下回退到潜在客户所有者",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "编辑和修改工作流程设置",
|
"pbac_desc_update_workflows": "编辑和修改工作流程设置",
|
||||||
"pbac_desc_delete_workflows": "从系统中移除工作流程",
|
"pbac_desc_delete_workflows": "从系统中移除工作流程",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "可用性",
|
||||||
|
"pbac_resource_out_of_office": "外出",
|
||||||
"pbac_desc_create_webhooks": "创建 Webhook",
|
"pbac_desc_create_webhooks": "创建 Webhook",
|
||||||
"pbac_desc_view_webhooks": "查看 Webhook",
|
"pbac_desc_view_webhooks": "查看 Webhook",
|
||||||
"pbac_desc_update_webhooks": "更新 Webhooks",
|
"pbac_desc_update_webhooks": "更新 Webhooks",
|
||||||
"pbac_desc_delete_webhooks": "删除 Webhooks",
|
"pbac_desc_delete_webhooks": "删除 Webhooks",
|
||||||
|
"pbac_desc_create_availability": "创建可用性",
|
||||||
|
"pbac_desc_view_availability": "查看可用性",
|
||||||
|
"pbac_desc_update_availability": "更新可用性",
|
||||||
|
"pbac_desc_delete_availability": "删除可用性",
|
||||||
|
"pbac_desc_create_out_of_office": "创建外出",
|
||||||
|
"pbac_desc_view_out_of_office": "查看外出",
|
||||||
|
"pbac_desc_update_out_of_office": "更新外出",
|
||||||
|
"pbac_desc_delete_out_of_office": "删除外出",
|
||||||
"pbac_desc_manage_workflows": "全面管理所有工作流程的权限",
|
"pbac_desc_manage_workflows": "全面管理所有工作流程的权限",
|
||||||
"pbac_desc_create_event_types": "创建活动类型",
|
"pbac_desc_create_event_types": "创建活动类型",
|
||||||
"pbac_desc_view_event_types": "查看活动类型",
|
"pbac_desc_view_event_types": "查看活动类型",
|
||||||
|
|||||||
@@ -321,6 +321,7 @@
|
|||||||
"secret_meeting": "秘密會議",
|
"secret_meeting": "秘密會議",
|
||||||
"login_instead": "或登入",
|
"login_instead": "或登入",
|
||||||
"create_account": "新增帳號",
|
"create_account": "新增帳號",
|
||||||
|
"get_started": "開始使用",
|
||||||
"confirm_password": "確認密碼",
|
"confirm_password": "確認密碼",
|
||||||
"reset_your_password": "按照傳送至您電子郵件地址的指示來重設密碼。",
|
"reset_your_password": "按照傳送至您電子郵件地址的指示來重設密碼。",
|
||||||
"org_banner_instructions": "請上傳寬度為 {{width}} 高度為 {{height}} 的圖片。",
|
"org_banner_instructions": "請上傳寬度為 {{width}} 高度為 {{height}} 的圖片。",
|
||||||
@@ -958,7 +959,7 @@
|
|||||||
"light_event_type_color": "事件類型顏色(淺色主題)",
|
"light_event_type_color": "事件類型顏色(淺色主題)",
|
||||||
"dark_event_type_color": "事件類型顏色(深色主題)",
|
"dark_event_type_color": "事件類型顏色(深色主題)",
|
||||||
"file_not_named": "檔案名稱不叫做 [idOrSlug]/[user]",
|
"file_not_named": "檔案名稱不叫做 [idOrSlug]/[user]",
|
||||||
"create_team": "新增團隊",
|
"create_team": "建立團隊",
|
||||||
"name": "名稱",
|
"name": "名稱",
|
||||||
"nameless_team": "未命名團隊",
|
"nameless_team": "未命名團隊",
|
||||||
"oauth_clients": "OAuth 客戶端",
|
"oauth_clients": "OAuth 客戶端",
|
||||||
@@ -1019,6 +1020,7 @@
|
|||||||
"attendees": "與會者",
|
"attendees": "與會者",
|
||||||
"add_attendees": "加入與會者",
|
"add_attendees": "加入與會者",
|
||||||
"show_advanced_settings": "顯示進階設定",
|
"show_advanced_settings": "顯示進階設定",
|
||||||
|
"hide_advanced_settings": "隱藏進階設定",
|
||||||
"event_name": "活動名稱",
|
"event_name": "活動名稱",
|
||||||
"event_name_in_calendar": "行事曆中的活動名稱",
|
"event_name_in_calendar": "行事曆中的活動名稱",
|
||||||
"event_name_tooltip": "會顯示在行事曆上的名字",
|
"event_name_tooltip": "會顯示在行事曆上的名字",
|
||||||
@@ -1329,7 +1331,7 @@
|
|||||||
"make_team_private_description": "啟用此設定時,團隊成員將無法查看其他團隊成員。",
|
"make_team_private_description": "啟用此設定時,團隊成員將無法查看其他團隊成員。",
|
||||||
"you_cannot_see_team_members": "您無法查看私人團隊的所有團隊成員。",
|
"you_cannot_see_team_members": "您無法查看私人團隊的所有團隊成員。",
|
||||||
"you_cannot_see_teams_of_org": "您無法看到私人組織的團隊。",
|
"you_cannot_see_teams_of_org": "您無法看到私人組織的團隊。",
|
||||||
"allow_booker_to_select_duration": "允許預約者選擇時間長度",
|
"allow_multiple_durations": "允許多種時長",
|
||||||
"impersonate_user_tip": "此功能的所有使用已經過稽核。",
|
"impersonate_user_tip": "此功能的所有使用已經過稽核。",
|
||||||
"impersonating_user_warning": "正在模擬使用者名稱「{{user}}」。",
|
"impersonating_user_warning": "正在模擬使用者名稱「{{user}}」。",
|
||||||
"impersonating_stop_instructions": "按一下此處以停止。",
|
"impersonating_stop_instructions": "按一下此處以停止。",
|
||||||
@@ -2202,6 +2204,7 @@
|
|||||||
"booking_confirmation_failed": "預約確認失敗",
|
"booking_confirmation_failed": "預約確認失敗",
|
||||||
"not_enough_seats": "座位不足",
|
"not_enough_seats": "座位不足",
|
||||||
"form_builder_field_already_exists": "已存在使用此名稱的欄位",
|
"form_builder_field_already_exists": "已存在使用此名稱的欄位",
|
||||||
|
"guests_field_must_be_multiemail": "來賓欄位必須是「多個電子郵件」類型",
|
||||||
"show_on_booking_page": "在預約頁面上顯示",
|
"show_on_booking_page": "在預約頁面上顯示",
|
||||||
"visit_cancelled_booking": "您可以訪問已取消的預訂頁面",
|
"visit_cancelled_booking": "您可以訪問已取消的預訂頁面",
|
||||||
"get_started_zapier_templates": "開始使用 Zapier 範本",
|
"get_started_zapier_templates": "開始使用 Zapier 範本",
|
||||||
@@ -2298,6 +2301,7 @@
|
|||||||
"locked_by_team_admin": "已由團隊管理員鎖定",
|
"locked_by_team_admin": "已由團隊管理員鎖定",
|
||||||
"app_not_connected": "您尚未連結 {{appName}} 帳號。",
|
"app_not_connected": "您尚未連結 {{appName}} 帳號。",
|
||||||
"connect_now": "立即連結",
|
"connect_now": "立即連結",
|
||||||
|
"connect_now_unavailable_tooltip": "立即連接目前無法使用。請從上方時段預訂或稍後再試。",
|
||||||
"managed_event_dialog_confirm_button_one": "取代並通知 {{count}} 位成員",
|
"managed_event_dialog_confirm_button_one": "取代並通知 {{count}} 位成員",
|
||||||
"managed_event_dialog_confirm_button_other": "取代並通知 {{count}} 位成員",
|
"managed_event_dialog_confirm_button_other": "取代並通知 {{count}} 位成員",
|
||||||
"managed_event_dialog_title_one": "{{count}} 位成員已有網址 /{{slug}}。是否要替換?",
|
"managed_event_dialog_title_one": "{{count}} 位成員已有網址 /{{slug}}。是否要替換?",
|
||||||
@@ -2922,6 +2926,7 @@
|
|||||||
"proceeding_agreement": "繼續即表示您同意我們的",
|
"proceeding_agreement": "繼續即表示您同意我們的",
|
||||||
"terms": "條款",
|
"terms": "條款",
|
||||||
"always_show_x_days": "總是顯示 {{x}} 天的可用時間",
|
"always_show_x_days": "總是顯示 {{x}} 天的可用時間",
|
||||||
|
"always_show_x_days_description": "自動將{{x}}天的預訂視窗每天向前移動,確保您的日曆始終顯示接下來{{x}}天的可用時段。",
|
||||||
"team_subscription_cancelled_successfully": "團隊訂閱已成功取消",
|
"team_subscription_cancelled_successfully": "團隊訂閱已成功取消",
|
||||||
"team_subscription_cancellation_error": "取消團隊訂閱時出錯",
|
"team_subscription_cancellation_error": "取消團隊訂閱時出錯",
|
||||||
"unable_to_subscribe_to_the_platform": "嘗試訂閱平台計劃時發生錯誤,請稍後再試",
|
"unable_to_subscribe_to_the_platform": "嘗試訂閱平台計劃時發生錯誤,請稍後再試",
|
||||||
@@ -3143,7 +3148,8 @@
|
|||||||
"filter_operator_ends_with": "結尾為",
|
"filter_operator_ends_with": "結尾為",
|
||||||
"filter_operator_is_empty": "為空",
|
"filter_operator_is_empty": "為空",
|
||||||
"filter_operator_not_empty": "不為空",
|
"filter_operator_not_empty": "不為空",
|
||||||
"translate_description_button": "使用 AI 將標題/描述翻譯成訪客瀏覽器的語言",
|
"translate_description_button": "自動翻譯標題和描述",
|
||||||
|
"translate_description_button_description": "使用 AI 自動將標題和描述翻譯為訪客瀏覽器的語言。",
|
||||||
"rr_distribution_method": "分配方式",
|
"rr_distribution_method": "分配方式",
|
||||||
"rr_distribution_method_description": "可以優化分配方式以達到最大可用性或更平衡的任務分配。",
|
"rr_distribution_method_description": "可以優化分配方式以達到最大可用性或更平衡的任務分配。",
|
||||||
"rr_distribution_method_availability_title": "最大化可用性",
|
"rr_distribution_method_availability_title": "最大化可用性",
|
||||||
@@ -3309,6 +3315,7 @@
|
|||||||
"seats_doesnt_support_recurring": "座位功能不支援重複活動。請停用座位功能以設為重複活動。",
|
"seats_doesnt_support_recurring": "座位功能不支援重複活動。請停用座位功能以設為重複活動。",
|
||||||
"recurring_event_seats_error": "重複活動不支援座位功能。請停用座位功能或將活動設為非重複活動。",
|
"recurring_event_seats_error": "重複活動不支援座位功能。請停用座位功能或將活動設為非重複活動。",
|
||||||
"segment": "區隔",
|
"segment": "區隔",
|
||||||
|
"saved_filters": "已儲存的篩選條件",
|
||||||
"rename": "重新命名",
|
"rename": "重新命名",
|
||||||
"rename_segment": "重新命名區隔",
|
"rename_segment": "重新命名區隔",
|
||||||
"delete_segment": "刪除區隔",
|
"delete_segment": "刪除區隔",
|
||||||
@@ -3338,6 +3345,7 @@
|
|||||||
"picklist": "選項清單",
|
"picklist": "選項清單",
|
||||||
"most_cancelled_bookings": "最多取消的預約",
|
"most_cancelled_bookings": "最多取消的預約",
|
||||||
"interface_language": "介面語言",
|
"interface_language": "介面語言",
|
||||||
|
"interface_language_description": "設定預訂介面的偏好語言",
|
||||||
"visitors_browser_language": "訪客的瀏覽器語言",
|
"visitors_browser_language": "訪客的瀏覽器語言",
|
||||||
"name_or_email": "姓名或電子郵件",
|
"name_or_email": "姓名或電子郵件",
|
||||||
"salesforce_round_robin_skip_fallback_to_lead_owner": "如果找不到聯絡人,則回退到潛在客戶擁有者(如果存在)",
|
"salesforce_round_robin_skip_fallback_to_lead_owner": "如果找不到聯絡人,則回退到潛在客戶擁有者(如果存在)",
|
||||||
@@ -3509,10 +3517,20 @@
|
|||||||
"pbac_desc_update_workflows": "編輯和修改工作流程設定",
|
"pbac_desc_update_workflows": "編輯和修改工作流程設定",
|
||||||
"pbac_desc_delete_workflows": "從系統中移除工作流程",
|
"pbac_desc_delete_workflows": "從系統中移除工作流程",
|
||||||
"pbac_resource_webhook": "Webhook",
|
"pbac_resource_webhook": "Webhook",
|
||||||
|
"pbac_resource_availability": "可用性",
|
||||||
|
"pbac_resource_out_of_office": "不在辦公室",
|
||||||
"pbac_desc_create_webhooks": "建立 Webhook",
|
"pbac_desc_create_webhooks": "建立 Webhook",
|
||||||
"pbac_desc_view_webhooks": "查看 Webhook",
|
"pbac_desc_view_webhooks": "查看 Webhook",
|
||||||
"pbac_desc_update_webhooks": "更新 Webhooks",
|
"pbac_desc_update_webhooks": "更新 Webhooks",
|
||||||
"pbac_desc_delete_webhooks": "刪除 Webhooks",
|
"pbac_desc_delete_webhooks": "刪除 Webhooks",
|
||||||
|
"pbac_desc_create_availability": "建立可用性",
|
||||||
|
"pbac_desc_view_availability": "檢視可用性",
|
||||||
|
"pbac_desc_update_availability": "更新可用性",
|
||||||
|
"pbac_desc_delete_availability": "刪除可用性",
|
||||||
|
"pbac_desc_create_out_of_office": "建立外出辦公",
|
||||||
|
"pbac_desc_view_out_of_office": "檢視外出辦公",
|
||||||
|
"pbac_desc_update_out_of_office": "更新外出辦公",
|
||||||
|
"pbac_desc_delete_out_of_office": "刪除外出辦公",
|
||||||
"pbac_desc_manage_workflows": "完全管理所有工作流程的存取權限",
|
"pbac_desc_manage_workflows": "完全管理所有工作流程的存取權限",
|
||||||
"pbac_desc_create_event_types": "建立活動類型",
|
"pbac_desc_create_event_types": "建立活動類型",
|
||||||
"pbac_desc_view_event_types": "檢視活動類型",
|
"pbac_desc_view_event_types": "檢視活動類型",
|
||||||
@@ -3560,7 +3578,7 @@
|
|||||||
"rr_load_balancing_disabled_with_groups": "負載平衡無法與 Round Robin 群組一起使用",
|
"rr_load_balancing_disabled_with_groups": "負載平衡無法與 Round Robin 群組一起使用",
|
||||||
"round_robin_settings_description": "自訂此團隊的預設輪詢設定",
|
"round_robin_settings_description": "自訂此團隊的預設輪詢設定",
|
||||||
"round_robin_settings_updated_successfully": "輪詢設定已成功更新",
|
"round_robin_settings_updated_successfully": "輪詢設定已成功更新",
|
||||||
"booking_creation_time": "預約建立時間",
|
"booking_creation_time": "預訂建立時間",
|
||||||
"meeting_start_time": "會議開始時間",
|
"meeting_start_time": "會議開始時間",
|
||||||
"members_affected_by_disabling_delegation_credential": "受影響的成員",
|
"members_affected_by_disabling_delegation_credential": "受影響的成員",
|
||||||
"no_members_affected_by_disabling_delegation_credential": "停用委派憑證未影響任何成員",
|
"no_members_affected_by_disabling_delegation_credential": "停用委派憑證未影響任何成員",
|
||||||
|
|||||||
@@ -640,10 +640,6 @@ select:focus {
|
|||||||
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
@apply !border-r-default left-0.5 !border-y-0 !border-l-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.intercom-lightweight-app {
|
|
||||||
@apply z-40 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
[data-radix-popper-content-wrapper] {
|
[data-radix-popper-content-wrapper] {
|
||||||
border: none;
|
border: none;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,17 @@
|
|||||||
import prismaMock from "../../../../tests/libs/__mocks__/prismaMock";
|
import { describe, expect, it, vi } from "vitest";
|
||||||
|
|
||||||
import { describe, expect, it } from "vitest";
|
|
||||||
|
|
||||||
import dayjs from "@calcom/dayjs";
|
import dayjs from "@calcom/dayjs";
|
||||||
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/BookingLimits";
|
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/BookingLimits";
|
||||||
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
|
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
|
||||||
import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder";
|
import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder";
|
||||||
|
|
||||||
|
const mockCountBookingsByEventTypeAndDateRange = vi.fn();
|
||||||
|
vi.mock("@calcom/lib/server/repository/booking", () => ({
|
||||||
|
BookingRepository: vi.fn().mockImplementation(() => ({
|
||||||
|
countBookingsByEventTypeAndDateRange: mockCountBookingsByEventTypeAndDateRange,
|
||||||
|
})),
|
||||||
|
}));
|
||||||
|
|
||||||
type Mockdata = {
|
type Mockdata = {
|
||||||
id: number;
|
id: number;
|
||||||
startDate: Date;
|
startDate: Date;
|
||||||
@@ -25,21 +30,20 @@ const checkBookingLimitsService = getCheckBookingLimitsService();
|
|||||||
|
|
||||||
describe("Check Booking Limits Tests", () => {
|
describe("Check Booking Limits Tests", () => {
|
||||||
it("Should return no errors", async () => {
|
it("Should return no errors", async () => {
|
||||||
prismaMock.booking.count.mockResolvedValue(0);
|
mockCountBookingsByEventTypeAndDateRange.mockResolvedValue(0);
|
||||||
expect(
|
expect(
|
||||||
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
|
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
|
||||||
).resolves.toBeTruthy();
|
).resolves.toBeTruthy();
|
||||||
});
|
});
|
||||||
it("Should throw an error", async () => {
|
it("Should throw an error", async () => {
|
||||||
// Mock there being two a day
|
mockCountBookingsByEventTypeAndDateRange.mockResolvedValue(2);
|
||||||
prismaMock.booking.count.mockResolvedValue(2);
|
|
||||||
expect(
|
expect(
|
||||||
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
|
checkBookingLimitsService.checkBookingLimits(MOCK_DATA.bookingLimits, MOCK_DATA.startDate, MOCK_DATA.id)
|
||||||
).rejects.toThrowError();
|
).rejects.toThrowError();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("Should pass with multiple booking limits", async () => {
|
it("Should pass with multiple booking limits", async () => {
|
||||||
prismaMock.booking.count.mockResolvedValue(0);
|
mockCountBookingsByEventTypeAndDateRange.mockResolvedValue(0);
|
||||||
expect(
|
expect(
|
||||||
checkBookingLimitsService.checkBookingLimits(
|
checkBookingLimitsService.checkBookingLimits(
|
||||||
{
|
{
|
||||||
@@ -52,7 +56,7 @@ describe("Check Booking Limits Tests", () => {
|
|||||||
).resolves.toBeTruthy();
|
).resolves.toBeTruthy();
|
||||||
});
|
});
|
||||||
it("Should pass with multiple booking limits with one undefined", async () => {
|
it("Should pass with multiple booking limits with one undefined", async () => {
|
||||||
prismaMock.booking.count.mockResolvedValue(0);
|
mockCountBookingsByEventTypeAndDateRange.mockResolvedValue(0);
|
||||||
|
|
||||||
expect(
|
expect(
|
||||||
checkBookingLimitsService.checkBookingLimits(
|
checkBookingLimitsService.checkBookingLimits(
|
||||||
@@ -66,7 +70,7 @@ describe("Check Booking Limits Tests", () => {
|
|||||||
).resolves.toBeTruthy();
|
).resolves.toBeTruthy();
|
||||||
});
|
});
|
||||||
it("Should handle multiple limits correctly", async () => {
|
it("Should handle multiple limits correctly", async () => {
|
||||||
prismaMock.booking.count.mockResolvedValue(1);
|
mockCountBookingsByEventTypeAndDateRange.mockResolvedValue(1);
|
||||||
expect(
|
expect(
|
||||||
checkBookingLimitsService.checkBookingLimit({
|
checkBookingLimitsService.checkBookingLimit({
|
||||||
key: "PER_DAY",
|
key: "PER_DAY",
|
||||||
@@ -75,7 +79,7 @@ describe("Check Booking Limits Tests", () => {
|
|||||||
eventId: MOCK_DATA.id,
|
eventId: MOCK_DATA.id,
|
||||||
})
|
})
|
||||||
).resolves.not.toThrow();
|
).resolves.not.toThrow();
|
||||||
prismaMock.booking.count.mockResolvedValue(3);
|
mockCountBookingsByEventTypeAndDateRange.mockResolvedValue(3);
|
||||||
expect(
|
expect(
|
||||||
checkBookingLimitsService.checkBookingLimit({
|
checkBookingLimitsService.checkBookingLimit({
|
||||||
key: "PER_WEEK",
|
key: "PER_WEEK",
|
||||||
|
|||||||
@@ -4,6 +4,14 @@
|
|||||||
"path": "/api/cron/calendar-cache-cleanup",
|
"path": "/api/cron/calendar-cache-cleanup",
|
||||||
"schedule": "0 5 * * *"
|
"schedule": "0 5 * * *"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"path": "/api/cron/calendar-subscriptions",
|
||||||
|
"schedule": "*/5 * * * *"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"path": "/api/cron/calendar-subscriptions-cleanup",
|
||||||
|
"schedule": "0 3 * * *"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"path": "/api/cron/queuedFormResponseCleanup",
|
"path": "/api/cron/queuedFormResponseCleanup",
|
||||||
"schedule": "0 */12 * * *"
|
"schedule": "0 */12 * * *"
|
||||||
|
|||||||
@@ -323,6 +323,7 @@ checksums:
|
|||||||
secret_meeting: b22ebd9b53662182d95749b3a6b5d3de
|
secret_meeting: b22ebd9b53662182d95749b3a6b5d3de
|
||||||
login_instead: 4e7a13b6c39065d8e093c554345ccd7c
|
login_instead: 4e7a13b6c39065d8e093c554345ccd7c
|
||||||
create_account: 3c3c9666b2acc1404eaa89e34e17f946
|
create_account: 3c3c9666b2acc1404eaa89e34e17f946
|
||||||
|
get_started: 5c783951b0100a168bdd2161ff294833
|
||||||
confirm_password: b5cdb861d8deb598b0613d48098b82b6
|
confirm_password: b5cdb861d8deb598b0613d48098b82b6
|
||||||
reset_your_password: 092dcca439cd79d85489ef004cdfdcc1
|
reset_your_password: 092dcca439cd79d85489ef004cdfdcc1
|
||||||
org_banner_instructions: 3d1e6ab6ae5844ff11bfc012cf62de1b
|
org_banner_instructions: 3d1e6ab6ae5844ff11bfc012cf62de1b
|
||||||
@@ -960,7 +961,7 @@ checksums:
|
|||||||
light_event_type_color: 6144591373599b4d1fc43f354c81680c
|
light_event_type_color: 6144591373599b4d1fc43f354c81680c
|
||||||
dark_event_type_color: 2a620447632a5238af304040cb605f41
|
dark_event_type_color: 2a620447632a5238af304040cb605f41
|
||||||
file_not_named: 42e93dfbf3037148e169df7f873bb536
|
file_not_named: 42e93dfbf3037148e169df7f873bb536
|
||||||
create_team: d3cba7d5fb6932d919f9dfad7a1e9d9f
|
create_team: c1d3e864b66be9cb2e15d66509b17a23
|
||||||
name: 9368b5a047572b6051f334af5aa76819
|
name: 9368b5a047572b6051f334af5aa76819
|
||||||
nameless_team: 44bfe8692c3839f1fd10459239409a30
|
nameless_team: 44bfe8692c3839f1fd10459239409a30
|
||||||
oauth_clients: 2227d1efbcc881c6950c9f89defc3ef6
|
oauth_clients: 2227d1efbcc881c6950c9f89defc3ef6
|
||||||
@@ -1021,6 +1022,7 @@ checksums:
|
|||||||
attendees: 44796e0b664cc408404b1bc3bb32752b
|
attendees: 44796e0b664cc408404b1bc3bb32752b
|
||||||
add_attendees: 6346e21f1a597f17b1d8274b01009320
|
add_attendees: 6346e21f1a597f17b1d8274b01009320
|
||||||
show_advanced_settings: e62afb3ad1a9c06c7b4645644c85c8e0
|
show_advanced_settings: e62afb3ad1a9c06c7b4645644c85c8e0
|
||||||
|
hide_advanced_settings: ffa251d7762030b72c12e92f3c69a9b4
|
||||||
event_name: e9ee5be0bc00804cc48c982d2ad1f849
|
event_name: e9ee5be0bc00804cc48c982d2ad1f849
|
||||||
event_name_in_calendar: a0ba9c8a87a892b060579bd2dc697a43
|
event_name_in_calendar: a0ba9c8a87a892b060579bd2dc697a43
|
||||||
event_name_tooltip: a00ef459ffb3ee9c2660b4125a9afdea
|
event_name_tooltip: a00ef459ffb3ee9c2660b4125a9afdea
|
||||||
@@ -1233,7 +1235,7 @@ checksums:
|
|||||||
contact_sales: 94537da970ffef6210bba8fcecb74a8c
|
contact_sales: 94537da970ffef6210bba8fcecb74a8c
|
||||||
error_404: 978c8521afd0be2ce95291e6920c7cfd
|
error_404: 978c8521afd0be2ce95291e6920c7cfd
|
||||||
default: d9c6dc5c412fe94143dfd1d332ec81d4
|
default: d9c6dc5c412fe94143dfd1d332ec81d4
|
||||||
set_to_default: 538a1fe3974e84a1e2726f00c7306598
|
set_to_default: 5b7c2d3d837cfdfc3df1d435fdf2796f
|
||||||
new_schedule_btn: 3ed90ee5292a8df7e1f5b13f0a132df9
|
new_schedule_btn: 3ed90ee5292a8df7e1f5b13f0a132df9
|
||||||
add_new_schedule: d0b75dad5cd4a7da4284262729b5cc49
|
add_new_schedule: d0b75dad5cd4a7da4284262729b5cc49
|
||||||
add_new_calendar: cf55065ffc4259b906d6af6ce45b85cf
|
add_new_calendar: cf55065ffc4259b906d6af6ce45b85cf
|
||||||
@@ -1330,7 +1332,7 @@ checksums:
|
|||||||
make_team_private_description: c48915c8357c3687506183e92e6aa155
|
make_team_private_description: c48915c8357c3687506183e92e6aa155
|
||||||
you_cannot_see_team_members: dc7a5d20692b36f17081d7a10e66821c
|
you_cannot_see_team_members: dc7a5d20692b36f17081d7a10e66821c
|
||||||
you_cannot_see_teams_of_org: c6dafa34320944aa57a5f4053541c092
|
you_cannot_see_teams_of_org: c6dafa34320944aa57a5f4053541c092
|
||||||
allow_booker_to_select_duration: b93ab67d353eb91e9220b6b92fd1533c
|
allow_multiple_durations: 7fac476934a8e73494fb0a2267015082
|
||||||
impersonate_user_tip: b185a4d43f5d2bd47d90c3118a9e6a4a
|
impersonate_user_tip: b185a4d43f5d2bd47d90c3118a9e6a4a
|
||||||
impersonating_user_warning: 2df022b91992a4c819abee8ca62cdf1c
|
impersonating_user_warning: 2df022b91992a4c819abee8ca62cdf1c
|
||||||
impersonating_stop_instructions: fc01010bfe9c16de03a6311a3a146f7c
|
impersonating_stop_instructions: fc01010bfe9c16de03a6311a3a146f7c
|
||||||
@@ -2199,6 +2201,7 @@ checksums:
|
|||||||
booking_confirmation_failed: 59daa6c4b4bcfff03dca5fb3776d5c29
|
booking_confirmation_failed: 59daa6c4b4bcfff03dca5fb3776d5c29
|
||||||
not_enough_seats: f2c120d55643caa712a0ef44350e56af
|
not_enough_seats: f2c120d55643caa712a0ef44350e56af
|
||||||
form_builder_field_already_exists: 623a2903bef6bc840794893083969567
|
form_builder_field_already_exists: 623a2903bef6bc840794893083969567
|
||||||
|
guests_field_must_be_multiemail: 68c9b9f5a64081e10388f75efafce2b3
|
||||||
show_on_booking_page: 515d42a965a615916eb9299f9a8ba698
|
show_on_booking_page: 515d42a965a615916eb9299f9a8ba698
|
||||||
visit_cancelled_booking: 42c1a6ee7e33ca49661c4e4763feb9e3
|
visit_cancelled_booking: 42c1a6ee7e33ca49661c4e4763feb9e3
|
||||||
get_started_zapier_templates: 0adfac5a026b181a55ff131b9572b5ab
|
get_started_zapier_templates: 0adfac5a026b181a55ff131b9572b5ab
|
||||||
@@ -2295,6 +2298,7 @@ checksums:
|
|||||||
locked_by_team_admin: 539128c9579f340b95d58abbfa0880ef
|
locked_by_team_admin: 539128c9579f340b95d58abbfa0880ef
|
||||||
app_not_connected: 25c979da79e9f983e6f5e62dcd1c47bb
|
app_not_connected: 25c979da79e9f983e6f5e62dcd1c47bb
|
||||||
connect_now: ca98ba0375aad91861c150bbc0fff9ec
|
connect_now: ca98ba0375aad91861c150bbc0fff9ec
|
||||||
|
connect_now_unavailable_tooltip: f259b61c574fd191048ddce748b87829
|
||||||
managed_event_dialog_confirm_button_one: 57569c5943d7cf44f3373f763559a0d8
|
managed_event_dialog_confirm_button_one: 57569c5943d7cf44f3373f763559a0d8
|
||||||
managed_event_dialog_confirm_button_other: d665ecde0909425ff7433b417c45ada3
|
managed_event_dialog_confirm_button_other: d665ecde0909425ff7433b417c45ada3
|
||||||
managed_event_dialog_title_one: 11c192644024dffc351d11d9681a81f9
|
managed_event_dialog_title_one: 11c192644024dffc351d11d9681a81f9
|
||||||
@@ -2685,7 +2689,7 @@ checksums:
|
|||||||
enterprise: c70474271708e5b27392fde87462fa26
|
enterprise: c70474271708e5b27392fde87462fa26
|
||||||
enterprise_description: cd3740d29ced9d8f2cd985ffa9677072
|
enterprise_description: cd3740d29ced9d8f2cd985ffa9677072
|
||||||
create_your_org: 0ac13e92f5227b4f08522707f911fd61
|
create_your_org: 0ac13e92f5227b4f08522707f911fd61
|
||||||
create_org: a1164cc2ec95880ee05e9c06bce99bd8
|
create_org: e79a6de15033cc09a320a6f9c294d696
|
||||||
create_your_org_description: d3c2a2cba0ce932123704baca89bb7d4
|
create_your_org_description: d3c2a2cba0ce932123704baca89bb7d4
|
||||||
create_your_enterprise_description: 42426099bf97d49dea548ab03f22e7b9
|
create_your_enterprise_description: 42426099bf97d49dea548ab03f22e7b9
|
||||||
other_payment_app_enabled: 2630450e1fbac6809df1cda21434ef5c
|
other_payment_app_enabled: 2630450e1fbac6809df1cda21434ef5c
|
||||||
@@ -2919,6 +2923,7 @@ checksums:
|
|||||||
proceeding_agreement: ee92983ae1f9fe1ecd3fe0f5e28761db
|
proceeding_agreement: ee92983ae1f9fe1ecd3fe0f5e28761db
|
||||||
terms: c531b88ecfe7dd2337a34f4a9ed18a5f
|
terms: c531b88ecfe7dd2337a34f4a9ed18a5f
|
||||||
always_show_x_days: a37d937d9387b7df0a32bcd89b9c346a
|
always_show_x_days: a37d937d9387b7df0a32bcd89b9c346a
|
||||||
|
always_show_x_days_description: 5b9145d7f9e87a2bb3b5277461ce432e
|
||||||
team_subscription_cancelled_successfully: 7ef5e0dbc3db34b1705086116d38c05c
|
team_subscription_cancelled_successfully: 7ef5e0dbc3db34b1705086116d38c05c
|
||||||
team_subscription_cancellation_error: 9a17ccdcd12aadb0c846f5b5bd207973
|
team_subscription_cancellation_error: 9a17ccdcd12aadb0c846f5b5bd207973
|
||||||
unable_to_subscribe_to_the_platform: 13bcc85e03aff904d15b41ee85f0043f
|
unable_to_subscribe_to_the_platform: 13bcc85e03aff904d15b41ee85f0043f
|
||||||
@@ -3140,7 +3145,8 @@ checksums:
|
|||||||
filter_operator_ends_with: c8a5f60f1bd1d8fa018dbbf49806fb5b
|
filter_operator_ends_with: c8a5f60f1bd1d8fa018dbbf49806fb5b
|
||||||
filter_operator_is_empty: dca87bc415341b1cdf9523f3b795a313
|
filter_operator_is_empty: dca87bc415341b1cdf9523f3b795a313
|
||||||
filter_operator_not_empty: 303caa93dcb79e54d47d4cd7d748a1df
|
filter_operator_not_empty: 303caa93dcb79e54d47d4cd7d748a1df
|
||||||
translate_description_button: 659cad3e743ba1b6a554fc8007a9dbfb
|
translate_description_button: 9fdee8d44e1949e75a5dab7dfa1afcab
|
||||||
|
translate_description_button_description: 129577a213f0c9cb5596bf05b230c9a5
|
||||||
rr_distribution_method: c3d4633236c01f8314a38a9e9c2361f1
|
rr_distribution_method: c3d4633236c01f8314a38a9e9c2361f1
|
||||||
rr_distribution_method_description: e9c1dda0299fbfc979a52a79b57e40c7
|
rr_distribution_method_description: e9c1dda0299fbfc979a52a79b57e40c7
|
||||||
rr_distribution_method_availability_title: 3a7c8afaf3ddb8d262e2a03d7fc8779d
|
rr_distribution_method_availability_title: 3a7c8afaf3ddb8d262e2a03d7fc8779d
|
||||||
@@ -3306,6 +3312,7 @@ checksums:
|
|||||||
seats_doesnt_support_recurring: efd91eb0c11cfedd08d794ff14b96bdd
|
seats_doesnt_support_recurring: efd91eb0c11cfedd08d794ff14b96bdd
|
||||||
recurring_event_seats_error: 39819a065f63e077ac7373274ce416c3
|
recurring_event_seats_error: 39819a065f63e077ac7373274ce416c3
|
||||||
segment: e8908115453de180bbda7478ba4c2d50
|
segment: e8908115453de180bbda7478ba4c2d50
|
||||||
|
saved_filters: f77e3389ad77a947e8be0cd18f8a6328
|
||||||
rename: 6f9413e4606e8d83a010685008fb2931
|
rename: 6f9413e4606e8d83a010685008fb2931
|
||||||
rename_segment: 7b8f51ad24c411fe48045039d357d793
|
rename_segment: 7b8f51ad24c411fe48045039d357d793
|
||||||
delete_segment: 7dd5b27c80d5748c50d17d7ad8a96113
|
delete_segment: 7dd5b27c80d5748c50d17d7ad8a96113
|
||||||
@@ -3335,6 +3342,7 @@ checksums:
|
|||||||
picklist: 5a3d44808605be31190c9956ecc5e412
|
picklist: 5a3d44808605be31190c9956ecc5e412
|
||||||
most_cancelled_bookings: 5470c614919f530c8e308d664c5170fa
|
most_cancelled_bookings: 5470c614919f530c8e308d664c5170fa
|
||||||
interface_language: a5c8eaac0640a27d6805c86b3faecd16
|
interface_language: a5c8eaac0640a27d6805c86b3faecd16
|
||||||
|
interface_language_description: a0189e533e016c6f1ed0118f321df6c7
|
||||||
visitors_browser_language: bda926411281203feec4334bc43a15f6
|
visitors_browser_language: bda926411281203feec4334bc43a15f6
|
||||||
name_or_email: d6e1cc7612211a2f7080b5096510f282
|
name_or_email: d6e1cc7612211a2f7080b5096510f282
|
||||||
salesforce_round_robin_skip_fallback_to_lead_owner: 95ba66cdc5f1422c48d907e9d4d442b9
|
salesforce_round_robin_skip_fallback_to_lead_owner: 95ba66cdc5f1422c48d907e9d4d442b9
|
||||||
@@ -3506,10 +3514,20 @@ checksums:
|
|||||||
pbac_desc_update_workflows: aecf636063ef3b8a5ece8d6381e053f0
|
pbac_desc_update_workflows: aecf636063ef3b8a5ece8d6381e053f0
|
||||||
pbac_desc_delete_workflows: fabfe2406f079e08cf4b017afca797bd
|
pbac_desc_delete_workflows: fabfe2406f079e08cf4b017afca797bd
|
||||||
pbac_resource_webhook: 70f95b2c27f2c3840b500fcaf79ee83c
|
pbac_resource_webhook: 70f95b2c27f2c3840b500fcaf79ee83c
|
||||||
|
pbac_resource_availability: f141aa3e9adb2179806cc1c909b5516a
|
||||||
|
pbac_resource_out_of_office: 9def361368ed730df37c884fe16d72e5
|
||||||
pbac_desc_create_webhooks: 3df3e99c62fd661d12ae4d1817deec6c
|
pbac_desc_create_webhooks: 3df3e99c62fd661d12ae4d1817deec6c
|
||||||
pbac_desc_view_webhooks: c3c1a5113700fc39aff9596d29371ce5
|
pbac_desc_view_webhooks: c3c1a5113700fc39aff9596d29371ce5
|
||||||
pbac_desc_update_webhooks: efe117c23053d397589dd9bc7e18d6dd
|
pbac_desc_update_webhooks: efe117c23053d397589dd9bc7e18d6dd
|
||||||
pbac_desc_delete_webhooks: 7877bc06e4a68ba21abf5ff702416b79
|
pbac_desc_delete_webhooks: 7877bc06e4a68ba21abf5ff702416b79
|
||||||
|
pbac_desc_create_availability: d62c426d46c5c004e1dbf8213ab19434
|
||||||
|
pbac_desc_view_availability: 3f1f2fe1200dd419d6a40cce45ad1d0d
|
||||||
|
pbac_desc_update_availability: 9a50a74361d3e4743932913e2d469f7b
|
||||||
|
pbac_desc_delete_availability: abf16f0da6e59c7322dc0d2348fa9b2c
|
||||||
|
pbac_desc_create_out_of_office: 2c59dbea23e478b2589bc78e8d5c8182
|
||||||
|
pbac_desc_view_out_of_office: 8e3ded73b455934c2f911296593985f3
|
||||||
|
pbac_desc_update_out_of_office: 2c080504ede7215d690888f900c75610
|
||||||
|
pbac_desc_delete_out_of_office: c28cb6f5884ab1db9fa890cea0266b16
|
||||||
pbac_desc_manage_workflows: 9ff3998b178daad7fa575d1277345d31
|
pbac_desc_manage_workflows: 9ff3998b178daad7fa575d1277345d31
|
||||||
pbac_desc_create_event_types: 0a58f83b33aa6ba7784de540b3107bbe
|
pbac_desc_create_event_types: 0a58f83b33aa6ba7784de540b3107bbe
|
||||||
pbac_desc_view_event_types: 390e116e8ff1ec3db6dc5079aaa80b7b
|
pbac_desc_view_event_types: 390e116e8ff1ec3db6dc5079aaa80b7b
|
||||||
@@ -3557,8 +3575,8 @@ checksums:
|
|||||||
rr_load_balancing_disabled_with_groups: 21faa6951621aae82853766c44de7ede
|
rr_load_balancing_disabled_with_groups: 21faa6951621aae82853766c44de7ede
|
||||||
round_robin_settings_description: e3700766347dce87bf8bf5829dd7a6b3
|
round_robin_settings_description: e3700766347dce87bf8bf5829dd7a6b3
|
||||||
round_robin_settings_updated_successfully: d5af777266614256a0f23338515323dc
|
round_robin_settings_updated_successfully: d5af777266614256a0f23338515323dc
|
||||||
booking_creation_time: cb184643434b260638743678fe6e5948
|
booking_creation_time: 8a9f5ebc9b9abc341da5d8205a14425f
|
||||||
meeting_start_time: 99d3983338813d02c553f8f7392aed1f
|
meeting_start_time: 1ee5753b97e6f589e4a0b9538bc6c73c
|
||||||
members_affected_by_disabling_delegation_credential: 693f7323afd369947660403297a3f1dc
|
members_affected_by_disabling_delegation_credential: 693f7323afd369947660403297a3f1dc
|
||||||
no_members_affected_by_disabling_delegation_credential: 0ec66caddbcbe0c935a75e4114ff6629
|
no_members_affected_by_disabling_delegation_credential: 0ec66caddbcbe0c935a75e4114ff6629
|
||||||
download_expense_log: c00ccbe3e58afe31fa0cb6cca10e2b69
|
download_expense_log: c00ccbe3e58afe31fa0cb6cca10e2b69
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
export default {
|
||||||
|
"(apps|packages)/**/*.{js,ts,jsx,tsx}": [
|
||||||
|
"prettier --write",
|
||||||
|
process.env.SKIP_WARNINGS === "1" ? "eslint --fix" : "eslint --fix --max-warnings=0",
|
||||||
|
],
|
||||||
|
"*.json": ["prettier --write"],
|
||||||
|
"packages/prisma/schema.prisma": ["prisma format"],
|
||||||
|
};
|
||||||
+2
-13
@@ -82,7 +82,8 @@
|
|||||||
"docker-stop-api": "docker ps --filter 'ancestor=cal-api' -q | xargs docker stop",
|
"docker-stop-api": "docker ps --filter 'ancestor=cal-api' -q | xargs docker stop",
|
||||||
"changesets-add": "yarn changeset add",
|
"changesets-add": "yarn changeset add",
|
||||||
"changesets-version": "yarn changeset version",
|
"changesets-version": "yarn changeset version",
|
||||||
"changesets-release": "NODE_OPTIONS='--max_old_space_size=12288' turbo run build --filter=@calcom/atoms && yarn changeset publish"
|
"changesets-release": "NODE_OPTIONS='--max_old_space_size=12288' turbo run build --filter=@calcom/atoms && yarn changeset publish",
|
||||||
|
"lint-staged": "lint-staged"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@calcom/eslint-config": "workspace:*",
|
"@calcom/eslint-config": "workspace:*",
|
||||||
@@ -140,18 +141,6 @@
|
|||||||
"dayjs@^1.8.29": "patch:dayjs@npm%3A1.11.4#./.yarn/patches/dayjs-npm-1.11.4-97921cd375.patch",
|
"dayjs@^1.8.29": "patch:dayjs@npm%3A1.11.4#./.yarn/patches/dayjs-npm-1.11.4-97921cd375.patch",
|
||||||
"import-in-the-middle": "1.13.1"
|
"import-in-the-middle": "1.13.1"
|
||||||
},
|
},
|
||||||
"lint-staged": {
|
|
||||||
"(apps|packages)/**/*.{js,ts,jsx,tsx}": [
|
|
||||||
"prettier --write",
|
|
||||||
"eslint --fix --max-warnings=0"
|
|
||||||
],
|
|
||||||
"*.json": [
|
|
||||||
"prettier --write"
|
|
||||||
],
|
|
||||||
"packages/prisma/schema.prisma": [
|
|
||||||
"prisma format"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"engines": {
|
"engines": {
|
||||||
"npm": ">=7.0.0",
|
"npm": ">=7.0.0",
|
||||||
"yarn": "3.4.1"
|
"yarn": "3.4.1"
|
||||||
|
|||||||
@@ -1,28 +1,17 @@
|
|||||||
|
import { CalendarSubscriptionService } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionService";
|
||||||
|
import { CalendarCacheEventRepository } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventRepository";
|
||||||
|
import { CalendarCacheEventService } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheEventService";
|
||||||
|
import { CalendarCacheWrapper } from "@calcom/features/calendar-subscription/lib/cache/CalendarCacheWrapper";
|
||||||
|
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
|
||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import type { Calendar, CalendarClass } from "@calcom/types/Calendar";
|
import { prisma } from "@calcom/prisma";
|
||||||
|
import type { Calendar } from "@calcom/types/Calendar";
|
||||||
import type { CredentialForCalendarService } from "@calcom/types/Credential";
|
import type { CredentialForCalendarService } from "@calcom/types/Credential";
|
||||||
|
|
||||||
import { CalendarServiceMap } from "../calendar.services.generated";
|
import { CalendarServiceMap } from "../calendar.services.generated";
|
||||||
|
|
||||||
interface CalendarApp {
|
|
||||||
lib: {
|
|
||||||
CalendarService: CalendarClass;
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
const log = logger.getSubLogger({ prefix: ["CalendarManager"] });
|
const log = logger.getSubLogger({ prefix: ["CalendarManager"] });
|
||||||
|
|
||||||
/**
|
|
||||||
* @see [Using type predicates](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#using-type-predicates)
|
|
||||||
*/
|
|
||||||
const isCalendarService = (x: unknown): x is CalendarApp =>
|
|
||||||
!!x &&
|
|
||||||
typeof x === "object" &&
|
|
||||||
"lib" in x &&
|
|
||||||
typeof x.lib === "object" &&
|
|
||||||
!!x.lib &&
|
|
||||||
"CalendarService" in x.lib;
|
|
||||||
|
|
||||||
export const getCalendar = async (
|
export const getCalendar = async (
|
||||||
credential: CredentialForCalendarService | null
|
credential: CredentialForCalendarService | null
|
||||||
): Promise<Calendar | null> => {
|
): Promise<Calendar | null> => {
|
||||||
@@ -53,5 +42,39 @@ export const getCalendar = async (
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// check if Calendar Cache is supported and enabled
|
||||||
|
if (CalendarCacheEventService.isCalendarTypeSupported(calendarType)) {
|
||||||
|
log.debug(
|
||||||
|
`Using regular CalendarService for credential ${credential.id} (not Google or Office365 Calendar)`
|
||||||
|
);
|
||||||
|
const featuresRepository = new FeaturesRepository(prisma);
|
||||||
|
const [isCalendarSubscriptionCacheEnabled, isCalendarSubscriptionCacheEnabledForUser] = await Promise.all(
|
||||||
|
[
|
||||||
|
featuresRepository.checkIfFeatureIsEnabledGlobally(
|
||||||
|
CalendarSubscriptionService.CALENDAR_SUBSCRIPTION_CACHE_FEATURE
|
||||||
|
),
|
||||||
|
featuresRepository.checkIfUserHasFeature(
|
||||||
|
credential.userId as number,
|
||||||
|
CalendarSubscriptionService.CALENDAR_SUBSCRIPTION_CACHE_FEATURE
|
||||||
|
),
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
if (isCalendarSubscriptionCacheEnabled && isCalendarSubscriptionCacheEnabledForUser) {
|
||||||
|
log.debug(`Calendar Cache is enabled, using CalendarCacheService for credential ${credential.id}`);
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const originalCalendar = new CalendarService(credential as any);
|
||||||
|
if (originalCalendar) {
|
||||||
|
// return cacheable calendar
|
||||||
|
const calendarCacheEventRepository = new CalendarCacheEventRepository(prisma);
|
||||||
|
return new CalendarCacheWrapper({
|
||||||
|
originalCalendar,
|
||||||
|
calendarCacheEventRepository,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return new CalendarService(credential as any);
|
return new CalendarService(credential as any);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import { vi } from "vitest";
|
|||||||
const featuresRepositoryModuleMock = {
|
const featuresRepositoryModuleMock = {
|
||||||
FeaturesRepository: vi.fn().mockImplementation(() => ({
|
FeaturesRepository: vi.fn().mockImplementation(() => ({
|
||||||
checkIfFeatureIsEnabledGlobally: vi.fn().mockResolvedValue(true),
|
checkIfFeatureIsEnabledGlobally: vi.fn().mockResolvedValue(true),
|
||||||
|
checkIfUserHasFeature: vi.fn().mockResolvedValue(true),
|
||||||
})),
|
})),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -46,8 +46,14 @@ export class PaymentService implements IAbstractPaymentService {
|
|||||||
|
|
||||||
private async getPayment(where: Prisma.PaymentWhereInput) {
|
private async getPayment(where: Prisma.PaymentWhereInput) {
|
||||||
const payment = await prisma.payment.findFirst({ where });
|
const payment = await prisma.payment.findFirst({ where });
|
||||||
if (!payment) throw new Error("Payment not found");
|
// if payment isn't found, return null.
|
||||||
if (!payment.externalId) throw new Error("Payment externalId not found");
|
if (!payment) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// if it is found, but there's no externalId - it indicates invalid state and an error should be thrown.
|
||||||
|
if (!payment.externalId) {
|
||||||
|
throw new Error("Payment externalId not found");
|
||||||
|
}
|
||||||
return { ...payment, externalId: payment.externalId };
|
return { ...payment, externalId: payment.externalId };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -326,13 +332,21 @@ export class PaymentService implements IAbstractPaymentService {
|
|||||||
throw new Error("Method not implemented.");
|
throw new Error("Method not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
async refund(paymentId: Payment["id"]): Promise<Payment> {
|
async refund(paymentId: Payment["id"]): Promise<Payment | null> {
|
||||||
|
const payment = await this.getPayment({
|
||||||
|
id: paymentId,
|
||||||
|
});
|
||||||
|
if (!payment) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (!payment.success) {
|
||||||
|
throw new Error("Unable to refund failed payment");
|
||||||
|
}
|
||||||
|
if (payment.refunded) {
|
||||||
|
// refunded already, bail early as success without throwing an error.
|
||||||
|
return payment;
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const payment = await this.getPayment({
|
|
||||||
id: paymentId,
|
|
||||||
success: true,
|
|
||||||
refunded: false,
|
|
||||||
});
|
|
||||||
const refund = await this.stripe.refunds.create(
|
const refund = await this.stripe.refunds.create(
|
||||||
{
|
{
|
||||||
payment_intent: payment.externalId,
|
payment_intent: payment.externalId,
|
||||||
@@ -399,8 +413,12 @@ export class PaymentService implements IAbstractPaymentService {
|
|||||||
const payment = await this.getPayment({
|
const payment = await this.getPayment({
|
||||||
id: paymentId,
|
id: paymentId,
|
||||||
});
|
});
|
||||||
const stripeAccount = (payment.data as unknown as StripePaymentData).stripeAccount;
|
// no payment found, return false.
|
||||||
|
if (!payment) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stripeAccount = (payment.data as unknown as StripePaymentData).stripeAccount;
|
||||||
if (!stripeAccount) {
|
if (!stripeAccount) {
|
||||||
throw new Error("Stripe account not found");
|
throw new Error("Stripe account not found");
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ export const TeamInviteEmail = (
|
|||||||
<div style={{ display: "flex", justifyContent: "center" }}>
|
<div style={{ display: "flex", justifyContent: "center" }}>
|
||||||
<CallToAction
|
<CallToAction
|
||||||
label={props.language(
|
label={props.language(
|
||||||
props.isCalcomMember ? (props.isAutoJoin ? "login" : "email_user_cta") : "create_your_account"
|
props.isCalcomMember ? (props.isAutoJoin ? "login" : "accept_invite") : "join_now"
|
||||||
)}
|
)}
|
||||||
href={props.joinLink}
|
href={props.joinLink}
|
||||||
endIconName="linkIcon"
|
endIconName="linkIcon"
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ export const config = [
|
|||||||
},
|
},
|
||||||
rules: {
|
rules: {
|
||||||
"turbo/no-undeclared-env-vars": "warn",
|
"turbo/no-undeclared-env-vars": "warn",
|
||||||
|
"@typescript-eslint/no-unused-vars": ["warn", { argsIgnorePattern: "^_", varsIgnorePattern: "^_" }],
|
||||||
},
|
},
|
||||||
languageOptions: {
|
languageOptions: {
|
||||||
ecmaVersion: 2021,
|
ecmaVersion: 2021,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import type { MembershipRole } from "@calcom/prisma/enums";
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
export function checkAdminOrOwner(role: MembershipRole | null | undefined): role is "OWNER" | "ADMIN" {
|
export function checkAdminOrOwner(role: MembershipRole | null | undefined): role is "OWNER" | "ADMIN" {
|
||||||
return role === "OWNER" || role === "ADMIN";
|
return role === MembershipRole.OWNER || role === MembershipRole.ADMIN;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,9 +39,9 @@ import { ProfileRepository } from "@calcom/lib/server/repository/profile";
|
|||||||
import { UserRepository } from "@calcom/lib/server/repository/user";
|
import { UserRepository } from "@calcom/lib/server/repository/user";
|
||||||
import slugify from "@calcom/lib/slugify";
|
import slugify from "@calcom/lib/slugify";
|
||||||
import prisma from "@calcom/prisma";
|
import prisma from "@calcom/prisma";
|
||||||
import type { Membership, Team, UserPermissionRole } from "@calcom/prisma/client";
|
import type { Membership, Team } from "@calcom/prisma/client";
|
||||||
import { CreationSource } from "@calcom/prisma/enums";
|
import { CreationSource } from "@calcom/prisma/enums";
|
||||||
import { IdentityProvider, MembershipRole } from "@calcom/prisma/enums";
|
import { IdentityProvider, MembershipRole, UserPermissionRole } from "@calcom/prisma/enums";
|
||||||
import { teamMetadataSchema, userMetadata } from "@calcom/prisma/zod-utils";
|
import { teamMetadataSchema, userMetadata } from "@calcom/prisma/zod-utils";
|
||||||
|
|
||||||
import { getOrgUsernameFromEmail } from "../signup/utils/getOrgUsernameFromEmail";
|
import { getOrgUsernameFromEmail } from "../signup/utils/getOrgUsernameFromEmail";
|
||||||
@@ -249,7 +249,7 @@ const providers: Provider[] = [
|
|||||||
// authentication success- but does it meet the minimum password requirements?
|
// authentication success- but does it meet the minimum password requirements?
|
||||||
const validateRole = (role: UserPermissionRole) => {
|
const validateRole = (role: UserPermissionRole) => {
|
||||||
// User's role is not "ADMIN"
|
// User's role is not "ADMIN"
|
||||||
if (role !== "ADMIN") return role;
|
if (role !== UserPermissionRole.ADMIN) return role;
|
||||||
// User's identity provider is not "CAL"
|
// User's identity provider is not "CAL"
|
||||||
if (user.identityProvider !== IdentityProvider.CAL) return role;
|
if (user.identityProvider !== IdentityProvider.CAL) return role;
|
||||||
|
|
||||||
|
|||||||
@@ -92,7 +92,6 @@ const BookerComponent = ({
|
|||||||
);
|
);
|
||||||
|
|
||||||
const selectedDate = useBookerStoreContext((state) => state.selectedDate);
|
const selectedDate = useBookerStoreContext((state) => state.selectedDate);
|
||||||
const setSelectedDate = useBookerStoreContext((state) => state.setSelectedDate);
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
shouldShowFormInDialog,
|
shouldShowFormInDialog,
|
||||||
@@ -145,7 +144,14 @@ const BookerComponent = ({
|
|||||||
|
|
||||||
const { bookerFormErrorRef, key, formEmail, bookingForm, errors: formErrors } = bookerForm;
|
const { bookerFormErrorRef, key, formEmail, bookingForm, errors: formErrors } = bookerForm;
|
||||||
|
|
||||||
const { handleBookEvent, errors, loadingStates, expiryTime, instantVideoMeetingUrl } = bookings;
|
const {
|
||||||
|
handleBookEvent,
|
||||||
|
errors,
|
||||||
|
loadingStates,
|
||||||
|
expiryTime,
|
||||||
|
instantVideoMeetingUrl,
|
||||||
|
instantConnectCooldownMs,
|
||||||
|
} = bookings;
|
||||||
|
|
||||||
const watchedCfToken = bookingForm.watch("cfToken");
|
const watchedCfToken = bookingForm.watch("cfToken");
|
||||||
|
|
||||||
@@ -230,9 +236,9 @@ const BookerComponent = ({
|
|||||||
setSelectedTimeslot(slot || null);
|
setSelectedTimeslot(slot || null);
|
||||||
}, [slot, setSelectedTimeslot]);
|
}, [slot, setSelectedTimeslot]);
|
||||||
|
|
||||||
const onSubmit = (timeSlot?: string) => {
|
const onSubmit = (timeSlot?: string) =>
|
||||||
renderConfirmNotVerifyEmailButtonCond ? handleBookEvent(timeSlot) : handleVerifyEmail();
|
renderConfirmNotVerifyEmailButtonCond ? handleBookEvent(timeSlot) : handleVerifyEmail();
|
||||||
};
|
|
||||||
|
|
||||||
const EventBooker = useMemo(() => {
|
const EventBooker = useMemo(() => {
|
||||||
return bookerState === "booking" ? (
|
return bookerState === "booking" ? (
|
||||||
@@ -543,6 +549,7 @@ const BookerComponent = ({
|
|||||||
style={{ animationDelay: "1s" }}>
|
style={{ animationDelay: "1s" }}>
|
||||||
<InstantBooking
|
<InstantBooking
|
||||||
event={event.data}
|
event={event.data}
|
||||||
|
cooldownMs={instantConnectCooldownMs}
|
||||||
onConnectNow={() => {
|
onConnectNow={() => {
|
||||||
onConnectNowInstantMeeting();
|
onConnectNowInstantMeeting();
|
||||||
}}
|
}}
|
||||||
|
|||||||
@@ -3,16 +3,19 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
|||||||
import type { User } from "@calcom/prisma/client";
|
import type { User } from "@calcom/prisma/client";
|
||||||
import { UserAvatarGroupWithOrg } from "@calcom/ui/components/avatar";
|
import { UserAvatarGroupWithOrg } from "@calcom/ui/components/avatar";
|
||||||
import { Button } from "@calcom/ui/components/button";
|
import { Button } from "@calcom/ui/components/button";
|
||||||
|
import { Tooltip } from "@calcom/ui/components/tooltip";
|
||||||
|
|
||||||
interface IInstantBookingProps {
|
interface IInstantBookingProps {
|
||||||
onConnectNow: () => void;
|
onConnectNow: () => void;
|
||||||
event: Pick<BookerEvent, "entity" | "schedulingType"> & {
|
event: Pick<BookerEvent, "entity" | "schedulingType"> & {
|
||||||
subsetOfUsers: (Pick<User, "name" | "username" | "avatarUrl"> & { bookerUrl: string })[];
|
subsetOfUsers: (Pick<User, "name" | "username" | "avatarUrl"> & { bookerUrl: string })[];
|
||||||
};
|
};
|
||||||
|
cooldownMs?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export const InstantBooking = ({ onConnectNow, event }: IInstantBookingProps) => {
|
export const InstantBooking = ({ onConnectNow, event, cooldownMs = 0 }: IInstantBookingProps) => {
|
||||||
const { t } = useLocale();
|
const { t } = useLocale();
|
||||||
|
const disabled = cooldownMs > 0;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className=" bg-default border-subtle mx-2 block items-center gap-3 rounded-xl border p-[6px] text-sm shadow-sm delay-1000 sm:flex">
|
<div className=" bg-default border-subtle mx-2 block items-center gap-3 rounded-xl border p-[6px] text-sm shadow-sm delay-1000 sm:flex">
|
||||||
@@ -33,16 +36,33 @@ export const InstantBooking = ({ onConnectNow, event }: IInstantBookingProps) =>
|
|||||||
</div>
|
</div>
|
||||||
<div>{t("dont_want_to_wait")}</div>
|
<div>{t("dont_want_to_wait")}</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 sm:mt-0">
|
<div className="mt-2 sm:mt-0 flex items-center gap-3">
|
||||||
<Button
|
{disabled ? (
|
||||||
color="primary"
|
<Tooltip content={t("connect_now_unavailable_tooltip") || "Connect now is not available right now."}>
|
||||||
onClick={() => {
|
<span className="inline-flex">
|
||||||
onConnectNow();
|
<Button
|
||||||
}}
|
disabled={disabled}
|
||||||
size="sm"
|
color="primary"
|
||||||
className="w-full justify-center rounded-lg sm:w-auto">
|
onClick={() => {
|
||||||
{t("connect_now")}
|
onConnectNow();
|
||||||
</Button>
|
}}
|
||||||
|
size="sm"
|
||||||
|
className="w-full justify-center rounded-lg sm:w-auto">
|
||||||
|
{t("connect_now")}
|
||||||
|
</Button>
|
||||||
|
</span>
|
||||||
|
</Tooltip>
|
||||||
|
) : (
|
||||||
|
<Button
|
||||||
|
color="primary"
|
||||||
|
onClick={() => {
|
||||||
|
onConnectNow();
|
||||||
|
}}
|
||||||
|
size="sm"
|
||||||
|
className="w-full justify-center rounded-lg sm:w-auto">
|
||||||
|
{t("connect_now")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -114,6 +114,43 @@ export interface IUseBookingErrors {
|
|||||||
export type UseBookingsReturnType = ReturnType<typeof useBookings>;
|
export type UseBookingsReturnType = ReturnType<typeof useBookings>;
|
||||||
|
|
||||||
const STORAGE_KEY = "instantBookingData";
|
const STORAGE_KEY = "instantBookingData";
|
||||||
|
const COOLDOWN_STORAGE_KEY = "instantBookingCooldownByEvent";
|
||||||
|
const COOLDOWN_WINDOW_MS = 10 * 60 * 1000; // 10 minutes
|
||||||
|
|
||||||
|
type InstantBookingCooldownMap = Record<string, number>;
|
||||||
|
|
||||||
|
const readInstantCooldownMap = (): InstantBookingCooldownMap => {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(COOLDOWN_STORAGE_KEY);
|
||||||
|
return raw ? (JSON.parse(raw) as InstantBookingCooldownMap) : {};
|
||||||
|
} catch {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const writeInstantCooldownMap = (map: InstantBookingCooldownMap) => {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(COOLDOWN_STORAGE_KEY, JSON.stringify(map));
|
||||||
|
} catch {
|
||||||
|
// don't do anything
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getInstantCooldownRemainingMs = (eventTypeId?: number | null): number => {
|
||||||
|
if (!eventTypeId) return 0;
|
||||||
|
const map = readInstantCooldownMap();
|
||||||
|
const lastTs = map[String(eventTypeId)];
|
||||||
|
if (!lastTs) return 0;
|
||||||
|
const remaining = lastTs + COOLDOWN_WINDOW_MS - Date.now();
|
||||||
|
return remaining > 0 ? remaining : 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
const setInstantCooldownNow = (eventTypeId?: number | null) => {
|
||||||
|
if (!eventTypeId) return;
|
||||||
|
const map = readInstantCooldownMap();
|
||||||
|
map[String(eventTypeId)] = Date.now();
|
||||||
|
writeInstantCooldownMap(map);
|
||||||
|
};
|
||||||
|
|
||||||
const storeInLocalStorage = ({
|
const storeInLocalStorage = ({
|
||||||
eventTypeId,
|
eventTypeId,
|
||||||
@@ -133,7 +170,6 @@ export const useBookings = ({
|
|||||||
hashedLink,
|
hashedLink,
|
||||||
bookingForm,
|
bookingForm,
|
||||||
metadata,
|
metadata,
|
||||||
teamMemberEmail,
|
|
||||||
isBookingDryRun,
|
isBookingDryRun,
|
||||||
}: IUseBookings) => {
|
}: IUseBookings) => {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
@@ -179,6 +215,8 @@ export const useBookings = ({
|
|||||||
}
|
}
|
||||||
}, [eventTypeId, isInstantMeeting]);
|
}, [eventTypeId, isInstantMeeting]);
|
||||||
|
|
||||||
|
const instantConnectCooldownMs = getInstantCooldownRemainingMs(eventTypeId);
|
||||||
|
|
||||||
const _instantBooking = trpc.viewer.bookings.getInstantBookingLocation.useQuery(
|
const _instantBooking = trpc.viewer.bookings.getInstantBookingLocation.useQuery(
|
||||||
{
|
{
|
||||||
bookingId: bookingId,
|
bookingId: bookingId,
|
||||||
@@ -245,7 +283,7 @@ export const useBookings = ({
|
|||||||
const { uid, paymentUid } = booking;
|
const { uid, paymentUid } = booking;
|
||||||
const fullName = getFullName(bookingForm.getValues("responses.name"));
|
const fullName = getFullName(bookingForm.getValues("responses.name"));
|
||||||
|
|
||||||
const users = !!event.data?.subsetOfHosts?.length
|
const users = event.data?.subsetOfHosts?.length
|
||||||
? event.data?.subsetOfHosts.map((host) => host.user)
|
? event.data?.subsetOfHosts.map((host) => host.user)
|
||||||
: event.data?.subsetOfUsers;
|
: event.data?.subsetOfUsers;
|
||||||
|
|
||||||
@@ -368,6 +406,7 @@ export const useBookings = ({
|
|||||||
expiryTime: responseData.expires,
|
expiryTime: responseData.expires,
|
||||||
bookingId: responseData.bookingId,
|
bookingId: responseData.bookingId,
|
||||||
});
|
});
|
||||||
|
setInstantCooldownNow(eventTypeId);
|
||||||
}
|
}
|
||||||
|
|
||||||
updateQueryParam("bookingId", responseData.bookingId);
|
updateQueryParam("bookingId", responseData.bookingId);
|
||||||
@@ -472,7 +511,19 @@ export const useBookings = ({
|
|||||||
bookingForm,
|
bookingForm,
|
||||||
hashedLink,
|
hashedLink,
|
||||||
metadata,
|
metadata,
|
||||||
handleInstantBooking: createInstantBookingMutation.mutate,
|
handleInstantBooking: (
|
||||||
|
variables: Parameters<typeof createInstantBookingMutation.mutate>[0]
|
||||||
|
) => {
|
||||||
|
const remaining = getInstantCooldownRemainingMs(eventTypeId);
|
||||||
|
if (remaining > 0) {
|
||||||
|
showToast(
|
||||||
|
t("please_try_again_later_or_book_another_slot"),
|
||||||
|
"error"
|
||||||
|
);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
createInstantBookingMutation.mutate(variables);
|
||||||
|
},
|
||||||
handleRecBooking: createRecurringBookingMutation.mutate,
|
handleRecBooking: createRecurringBookingMutation.mutate,
|
||||||
handleBooking: createBookingMutation.mutate,
|
handleBooking: createBookingMutation.mutate,
|
||||||
isBookingDryRun,
|
isBookingDryRun,
|
||||||
@@ -506,5 +557,6 @@ export const useBookings = ({
|
|||||||
errors,
|
errors,
|
||||||
loadingStates,
|
loadingStates,
|
||||||
instantVideoMeetingUrl,
|
instantVideoMeetingUrl,
|
||||||
|
instantConnectCooldownMs,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
// eslint-disable-next-line no-restricted-imports
|
|
||||||
import short, { uuid } from "short-uuid";
|
import short, { uuid } from "short-uuid";
|
||||||
import { v5 as uuidv5 } from "uuid";
|
import { v5 as uuidv5 } from "uuid";
|
||||||
|
|
||||||
@@ -403,6 +403,9 @@ export type BookingHandlerInput = {
|
|||||||
// These used to come from headers but now we're passing them as params
|
// These used to come from headers but now we're passing them as params
|
||||||
hostname?: string;
|
hostname?: string;
|
||||||
forcedSlug?: string;
|
forcedSlug?: string;
|
||||||
|
skipAvailabilityCheck?: boolean;
|
||||||
|
skipEventLimitsCheck?: boolean;
|
||||||
|
skipCalendarSyncTaskCreation?: boolean;
|
||||||
} & PlatformParams;
|
} & PlatformParams;
|
||||||
|
|
||||||
function formatAvailabilitySnapshot(data: {
|
function formatAvailabilitySnapshot(data: {
|
||||||
@@ -437,6 +440,9 @@ async function handler(
|
|||||||
hostname,
|
hostname,
|
||||||
forcedSlug,
|
forcedSlug,
|
||||||
areCalendarEventsEnabled = true,
|
areCalendarEventsEnabled = true,
|
||||||
|
skipAvailabilityCheck = false,
|
||||||
|
skipEventLimitsCheck = false,
|
||||||
|
skipCalendarSyncTaskCreation = false,
|
||||||
} = input;
|
} = input;
|
||||||
|
|
||||||
const isPlatformBooking = !!platformClientId;
|
const isPlatformBooking = !!platformClientId;
|
||||||
@@ -511,7 +517,7 @@ async function handler(
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await verifyCodeUnAuthenticated(bookerEmail, verificationCode);
|
await verifyCodeUnAuthenticated(bookerEmail, verificationCode);
|
||||||
} catch (error) {
|
} catch {
|
||||||
throw new HttpError({
|
throw new HttpError({
|
||||||
statusCode: 400,
|
statusCode: 400,
|
||||||
message: "invalid_verification_code",
|
message: "invalid_verification_code",
|
||||||
@@ -719,12 +725,14 @@ async function handler(
|
|||||||
location,
|
location,
|
||||||
});
|
});
|
||||||
|
|
||||||
const checkBookingAndDurationLimitsService = getCheckBookingAndDurationLimitsService();
|
if (!skipEventLimitsCheck) {
|
||||||
await checkBookingAndDurationLimitsService.checkBookingAndDurationLimits({
|
const checkBookingAndDurationLimitsService = getCheckBookingAndDurationLimitsService();
|
||||||
eventType,
|
await checkBookingAndDurationLimitsService.checkBookingAndDurationLimits({
|
||||||
reqBodyStart: reqBody.start,
|
eventType,
|
||||||
reqBodyRescheduleUid: reqBody.rescheduleUid,
|
reqBodyStart: reqBody.start,
|
||||||
});
|
reqBodyRescheduleUid: reqBody.rescheduleUid,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
let luckyUserResponse;
|
let luckyUserResponse;
|
||||||
let isFirstSeat = true;
|
let isFirstSeat = true;
|
||||||
@@ -798,48 +806,55 @@ async function handler(
|
|||||||
if (isTeamEvent) {
|
if (isTeamEvent) {
|
||||||
// each fixed user must be available
|
// each fixed user must be available
|
||||||
for (const key in fixedUsers) {
|
for (const key in fixedUsers) {
|
||||||
|
if (!skipAvailabilityCheck) {
|
||||||
|
await ensureAvailableUsers(
|
||||||
|
{ ...eventTypeWithUsers, users: [fixedUsers[key]] },
|
||||||
|
{
|
||||||
|
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
|
||||||
|
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
|
||||||
|
timeZone: reqBody.timeZone,
|
||||||
|
originalRescheduledBooking: originalRescheduledBooking ?? null,
|
||||||
|
},
|
||||||
|
loggerWithEventDetails,
|
||||||
|
shouldServeCache
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!skipAvailabilityCheck) {
|
||||||
await ensureAvailableUsers(
|
await ensureAvailableUsers(
|
||||||
{ ...eventTypeWithUsers, users: [fixedUsers[key]] },
|
eventTypeWithUsers,
|
||||||
{
|
{
|
||||||
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
|
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
|
||||||
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
|
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
|
||||||
timeZone: reqBody.timeZone,
|
timeZone: reqBody.timeZone,
|
||||||
originalRescheduledBooking: originalRescheduledBooking ?? null,
|
originalRescheduledBooking,
|
||||||
},
|
},
|
||||||
loggerWithEventDetails,
|
loggerWithEventDetails,
|
||||||
shouldServeCache
|
shouldServeCache
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
eventTypeWithUsers.users[0].credentials;
|
|
||||||
await ensureAvailableUsers(
|
|
||||||
eventTypeWithUsers,
|
|
||||||
{
|
|
||||||
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
|
|
||||||
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
|
|
||||||
timeZone: reqBody.timeZone,
|
|
||||||
originalRescheduledBooking,
|
|
||||||
},
|
|
||||||
loggerWithEventDetails,
|
|
||||||
shouldServeCache
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!input.bookingData.allRecurringDates || input.bookingData.isFirstRecurringSlot) {
|
if (!input.bookingData.allRecurringDates || input.bookingData.isFirstRecurringSlot) {
|
||||||
try {
|
try {
|
||||||
availableUsers = await ensureAvailableUsers(
|
if (!skipAvailabilityCheck) {
|
||||||
{ ...eventTypeWithUsers, users: [...qualifiedRRUsers, ...fixedUsers] as IsFixedAwareUser[] },
|
availableUsers = await ensureAvailableUsers(
|
||||||
{
|
{ ...eventTypeWithUsers, users: [...qualifiedRRUsers, ...fixedUsers] as IsFixedAwareUser[] },
|
||||||
dateFrom: dayjs(reqBody.start).tz(reqBody.timeZone).format(),
|
{
|
||||||
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(),
|
dateFrom: dayjs(reqBody.start).tz(reqBody.timeZone).format(),
|
||||||
timeZone: reqBody.timeZone,
|
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(),
|
||||||
originalRescheduledBooking,
|
timeZone: reqBody.timeZone,
|
||||||
},
|
originalRescheduledBooking,
|
||||||
loggerWithEventDetails,
|
},
|
||||||
shouldServeCache
|
loggerWithEventDetails,
|
||||||
);
|
shouldServeCache
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
availableUsers = [...qualifiedRRUsers, ...fixedUsers] as IsFixedAwareUser[];
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
if (additionalFallbackRRUsers.length) {
|
if (additionalFallbackRRUsers.length) {
|
||||||
loggerWithEventDetails.debug(
|
loggerWithEventDetails.debug(
|
||||||
@@ -851,20 +866,24 @@ async function handler(
|
|||||||
);
|
);
|
||||||
// can happen when contact owner not available for 2 weeks or fairness would block at least 2 weeks
|
// can happen when contact owner not available for 2 weeks or fairness would block at least 2 weeks
|
||||||
// use fallback instead
|
// use fallback instead
|
||||||
availableUsers = await ensureAvailableUsers(
|
if (!skipAvailabilityCheck) {
|
||||||
{
|
availableUsers = await ensureAvailableUsers(
|
||||||
...eventTypeWithUsers,
|
{
|
||||||
users: [...additionalFallbackRRUsers, ...fixedUsers] as IsFixedAwareUser[],
|
...eventTypeWithUsers,
|
||||||
},
|
users: [...additionalFallbackRRUsers, ...fixedUsers] as IsFixedAwareUser[],
|
||||||
{
|
},
|
||||||
dateFrom: dayjs(reqBody.start).tz(reqBody.timeZone).format(),
|
{
|
||||||
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(),
|
dateFrom: dayjs(reqBody.start).tz(reqBody.timeZone).format(),
|
||||||
timeZone: reqBody.timeZone,
|
dateTo: dayjs(reqBody.end).tz(reqBody.timeZone).format(),
|
||||||
originalRescheduledBooking,
|
timeZone: reqBody.timeZone,
|
||||||
},
|
originalRescheduledBooking,
|
||||||
loggerWithEventDetails,
|
},
|
||||||
shouldServeCache
|
loggerWithEventDetails,
|
||||||
);
|
shouldServeCache
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
availableUsers = [...additionalFallbackRRUsers, ...fixedUsers] as IsFixedAwareUser[];
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
loggerWithEventDetails.debug(
|
loggerWithEventDetails.debug(
|
||||||
"Qualified users not available, no fallback users",
|
"Qualified users not available, no fallback users",
|
||||||
@@ -880,7 +899,11 @@ async function handler(
|
|||||||
const nonFixedUsers: IsFixedAwareUser[] = [];
|
const nonFixedUsers: IsFixedAwareUser[] = [];
|
||||||
|
|
||||||
availableUsers.forEach((user) => {
|
availableUsers.forEach((user) => {
|
||||||
user.isFixed ? fixedUserPool.push(user) : nonFixedUsers.push(user);
|
if (user.isFixed) {
|
||||||
|
fixedUserPool.push(user);
|
||||||
|
} else {
|
||||||
|
nonFixedUsers.push(user);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
// Group non-fixed users by their group IDs
|
// Group non-fixed users by their group IDs
|
||||||
@@ -958,17 +981,19 @@ async function handler(
|
|||||||
const start = input.bookingData.allRecurringDates[i].start;
|
const start = input.bookingData.allRecurringDates[i].start;
|
||||||
const end = input.bookingData.allRecurringDates[i].end;
|
const end = input.bookingData.allRecurringDates[i].end;
|
||||||
|
|
||||||
await ensureAvailableUsers(
|
if (!skipAvailabilityCheck) {
|
||||||
{ ...eventTypeWithUsers, users: [newLuckyUser] },
|
await ensureAvailableUsers(
|
||||||
{
|
{ ...eventTypeWithUsers, users: [newLuckyUser] },
|
||||||
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
|
{
|
||||||
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
|
dateFrom: dayjs(start).tz(reqBody.timeZone).format(),
|
||||||
timeZone: reqBody.timeZone,
|
dateTo: dayjs(end).tz(reqBody.timeZone).format(),
|
||||||
originalRescheduledBooking,
|
timeZone: reqBody.timeZone,
|
||||||
},
|
originalRescheduledBooking,
|
||||||
loggerWithEventDetails,
|
},
|
||||||
shouldServeCache
|
loggerWithEventDetails,
|
||||||
);
|
shouldServeCache
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if no error, then lucky user is available for the next slots
|
// if no error, then lucky user is available for the next slots
|
||||||
luckyUsers.push(newLuckyUser);
|
luckyUsers.push(newLuckyUser);
|
||||||
@@ -1000,7 +1025,7 @@ async function handler(
|
|||||||
|
|
||||||
// Filter out host groups that have no hosts in them
|
// Filter out host groups that have no hosts in them
|
||||||
const nonEmptyHostGroups = Object.fromEntries(
|
const nonEmptyHostGroups = Object.fromEntries(
|
||||||
Object.entries(hostGroups).filter(([_groupId, hosts]) => hosts.length > 0)
|
Object.entries(hostGroups).filter(([, hosts]) => hosts.length > 0)
|
||||||
);
|
);
|
||||||
// If there are RR hosts, we need to find a lucky user
|
// If there are RR hosts, we need to find a lucky user
|
||||||
if (
|
if (
|
||||||
@@ -1658,9 +1683,10 @@ async function handler(
|
|||||||
// After polling videoBusyTimes, credentials might have been changed due to refreshment, so query them again.
|
// After polling videoBusyTimes, credentials might have been changed due to refreshment, so query them again.
|
||||||
const credentials = await refreshCredentials(allCredentials);
|
const credentials = await refreshCredentials(allCredentials);
|
||||||
const apps = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps);
|
const apps = eventTypeAppMetadataOptionalSchema.parse(eventType?.metadata?.apps);
|
||||||
const eventManager = !isDryRun
|
const eventManager =
|
||||||
? new EventManager({ ...organizerUser, credentials }, apps)
|
!isDryRun && !skipCalendarSyncTaskCreation
|
||||||
: buildDryRunEventManager();
|
? new EventManager({ ...organizerUser, credentials }, apps)
|
||||||
|
: buildDryRunEventManager();
|
||||||
|
|
||||||
let videoCallUrl;
|
let videoCallUrl;
|
||||||
|
|
||||||
@@ -1721,20 +1747,24 @@ async function handler(
|
|||||||
: evt.responses,
|
: evt.responses,
|
||||||
};
|
};
|
||||||
|
|
||||||
await originalHostEventManager.deleteEventsAndMeetings({
|
if (!skipCalendarSyncTaskCreation) {
|
||||||
event: deletionEvent,
|
await originalHostEventManager.deleteEventsAndMeetings({
|
||||||
bookingReferences: originalRescheduledBooking.references,
|
event: deletionEvent,
|
||||||
});
|
bookingReferences: originalRescheduledBooking.references,
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const updateManager = await eventManager.reschedule(
|
const updateManager = !skipCalendarSyncTaskCreation
|
||||||
evt,
|
? await eventManager.reschedule(
|
||||||
originalRescheduledBooking.uid,
|
evt,
|
||||||
undefined,
|
originalRescheduledBooking.uid,
|
||||||
changedOrganizer,
|
undefined,
|
||||||
previousHostDestinationCalendar,
|
changedOrganizer,
|
||||||
isBookingRequestedReschedule,
|
previousHostDestinationCalendar,
|
||||||
skipDeleteEventsAndMeetings
|
isBookingRequestedReschedule,
|
||||||
);
|
skipDeleteEventsAndMeetings
|
||||||
|
)
|
||||||
|
: placeholderCreatedEvent;
|
||||||
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
|
// This gets overridden when updating the event - to check if notes have been hidden or not. We just reset this back
|
||||||
// to the default description when we are sending the emails.
|
// to the default description when we are sending the emails.
|
||||||
evt.description = eventType.description;
|
evt.description = eventType.description;
|
||||||
@@ -1870,7 +1900,10 @@ async function handler(
|
|||||||
// Create a booking
|
// Create a booking
|
||||||
} else if (isConfirmedByDefault) {
|
} else if (isConfirmedByDefault) {
|
||||||
// Use EventManager to conditionally use all needed integrations.
|
// Use EventManager to conditionally use all needed integrations.
|
||||||
const createManager = areCalendarEventsEnabled ? await eventManager.create(evt) : placeholderCreatedEvent;
|
const createManager =
|
||||||
|
areCalendarEventsEnabled && !skipCalendarSyncTaskCreation
|
||||||
|
? await eventManager.create(evt)
|
||||||
|
: placeholderCreatedEvent;
|
||||||
if (evt.location) {
|
if (evt.location) {
|
||||||
booking.location = evt.location;
|
booking.location = evt.location;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,396 @@
|
|||||||
|
import prismaMock from "../../../../../../tests/libs/__mocks__/prisma";
|
||||||
|
|
||||||
|
import {
|
||||||
|
createBookingScenario,
|
||||||
|
getDate,
|
||||||
|
getGoogleCalendarCredential,
|
||||||
|
TestData,
|
||||||
|
getOrganizer,
|
||||||
|
getBooker,
|
||||||
|
getScenarioData,
|
||||||
|
mockCalendarToHaveNoBusySlots,
|
||||||
|
mockCalendar,
|
||||||
|
} from "@calcom/web/test/utils/bookingScenario/bookingScenario";
|
||||||
|
import { expectBookingToBeInDatabase } from "@calcom/web/test/utils/bookingScenario/expects";
|
||||||
|
import { getMockRequestDataForBooking } from "@calcom/web/test/utils/bookingScenario/getMockRequestDataForBooking";
|
||||||
|
import { setupAndTeardown } from "@calcom/web/test/utils/bookingScenario/setupAndTeardown";
|
||||||
|
|
||||||
|
import { describe, expect, test } from "vitest";
|
||||||
|
|
||||||
|
import { BookingStatus } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
|
import { getNewBookingHandler } from "./getNewBookingHandler";
|
||||||
|
|
||||||
|
setupAndTeardown();
|
||||||
|
|
||||||
|
describe("handleNewBooking - Booking Flags", () => {
|
||||||
|
describe("skipAvailabilityCheck flag", () => {
|
||||||
|
test("should skip availability checks when skipAvailabilityCheck is true", async () => {
|
||||||
|
const handleNewBooking = getNewBookingHandler();
|
||||||
|
const booker = getBooker({
|
||||||
|
email: "booker@example.com",
|
||||||
|
name: "Booker",
|
||||||
|
});
|
||||||
|
|
||||||
|
const organizer = getOrganizer({
|
||||||
|
name: "Organizer",
|
||||||
|
email: "organizer@example.com",
|
||||||
|
id: 101,
|
||||||
|
schedules: [TestData.schedules.IstWorkHours],
|
||||||
|
credentials: [getGoogleCalendarCredential()],
|
||||||
|
selectedCalendars: [TestData.selectedCalendars.google],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
||||||
|
|
||||||
|
await createBookingScenario(
|
||||||
|
getScenarioData({
|
||||||
|
webhooks: [
|
||||||
|
{
|
||||||
|
userId: organizer.id,
|
||||||
|
eventTriggers: ["BOOKING_CREATED"],
|
||||||
|
subscriberUrl: "http://my-webhook.example.com",
|
||||||
|
active: true,
|
||||||
|
eventTypeId: 1,
|
||||||
|
appId: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
eventTypes: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
slotInterval: 45,
|
||||||
|
length: 45,
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
organizer,
|
||||||
|
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
mockCalendarToHaveNoBusySlots("googlecalendar", {
|
||||||
|
create: {
|
||||||
|
uid: "MOCK_ID",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBookingData = getMockRequestDataForBooking({
|
||||||
|
data: {
|
||||||
|
eventTypeId: 1,
|
||||||
|
responses: {
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
location: { optionValue: "", value: "integrations:daily" },
|
||||||
|
},
|
||||||
|
start: `${plus1DateString}T05:00:00.000Z`,
|
||||||
|
end: `${plus1DateString}T05:45:00.000Z`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdBooking = await handleNewBooking({
|
||||||
|
bookingData: mockBookingData,
|
||||||
|
skipAvailabilityCheck: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdBooking.responses).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(createdBooking.status).toBe(BookingStatus.ACCEPTED);
|
||||||
|
|
||||||
|
await expectBookingToBeInDatabase({
|
||||||
|
description: "",
|
||||||
|
location: "integrations:daily",
|
||||||
|
responses: expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
}),
|
||||||
|
uid: createdBooking.uid,
|
||||||
|
eventTypeId: mockBookingData.eventTypeId,
|
||||||
|
status: BookingStatus.ACCEPTED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("skipEventLimitsCheck flag", () => {
|
||||||
|
test("should skip event limits checks when skipEventLimitsCheck is true", async () => {
|
||||||
|
const handleNewBooking = getNewBookingHandler();
|
||||||
|
const booker = getBooker({
|
||||||
|
email: "booker@example.com",
|
||||||
|
name: "Booker",
|
||||||
|
});
|
||||||
|
|
||||||
|
const organizer = getOrganizer({
|
||||||
|
name: "Organizer",
|
||||||
|
email: "organizer@example.com",
|
||||||
|
id: 101,
|
||||||
|
schedules: [TestData.schedules.IstWorkHours],
|
||||||
|
credentials: [getGoogleCalendarCredential()],
|
||||||
|
selectedCalendars: [TestData.selectedCalendars.google],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
||||||
|
|
||||||
|
await createBookingScenario(
|
||||||
|
getScenarioData({
|
||||||
|
eventTypes: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
slotInterval: 45,
|
||||||
|
length: 45,
|
||||||
|
bookingLimits: {
|
||||||
|
PER_DAY: 1,
|
||||||
|
},
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
organizer,
|
||||||
|
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
mockCalendarToHaveNoBusySlots("googlecalendar", {
|
||||||
|
create: {
|
||||||
|
uid: "MOCK_ID",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBookingData = getMockRequestDataForBooking({
|
||||||
|
data: {
|
||||||
|
eventTypeId: 1,
|
||||||
|
responses: {
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
location: { optionValue: "", value: "integrations:daily" },
|
||||||
|
},
|
||||||
|
start: `${plus1DateString}T05:00:00.000Z`,
|
||||||
|
end: `${plus1DateString}T05:45:00.000Z`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdBooking = await handleNewBooking({
|
||||||
|
bookingData: mockBookingData,
|
||||||
|
skipEventLimitsCheck: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdBooking.responses).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(createdBooking.status).toBe(BookingStatus.ACCEPTED);
|
||||||
|
|
||||||
|
await expectBookingToBeInDatabase({
|
||||||
|
description: "",
|
||||||
|
location: "integrations:daily",
|
||||||
|
responses: expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
}),
|
||||||
|
uid: createdBooking.uid,
|
||||||
|
eventTypeId: mockBookingData.eventTypeId,
|
||||||
|
status: BookingStatus.ACCEPTED,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("skipCalendarSyncTaskCreation flag", () => {
|
||||||
|
test("should skip calendar sync when skipCalendarSyncTaskCreation is true", async () => {
|
||||||
|
const handleNewBooking = getNewBookingHandler();
|
||||||
|
const booker = getBooker({
|
||||||
|
email: "booker@example.com",
|
||||||
|
name: "Booker",
|
||||||
|
});
|
||||||
|
|
||||||
|
const organizer = getOrganizer({
|
||||||
|
name: "Organizer",
|
||||||
|
email: "organizer@example.com",
|
||||||
|
id: 101,
|
||||||
|
schedules: [TestData.schedules.IstWorkHours],
|
||||||
|
credentials: [getGoogleCalendarCredential()],
|
||||||
|
selectedCalendars: [TestData.selectedCalendars.google],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
||||||
|
|
||||||
|
await createBookingScenario(
|
||||||
|
getScenarioData({
|
||||||
|
eventTypes: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
slotInterval: 45,
|
||||||
|
length: 45,
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
organizer,
|
||||||
|
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
mockCalendar("googlecalendar", {
|
||||||
|
create: {
|
||||||
|
uid: "MOCK_ID",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBookingData = getMockRequestDataForBooking({
|
||||||
|
data: {
|
||||||
|
eventTypeId: 1,
|
||||||
|
responses: {
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
location: { optionValue: "", value: "integrations:daily" },
|
||||||
|
},
|
||||||
|
start: `${plus1DateString}T05:00:00.000Z`,
|
||||||
|
end: `${plus1DateString}T05:45:00.000Z`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdBooking = await handleNewBooking({
|
||||||
|
bookingData: mockBookingData,
|
||||||
|
skipCalendarSyncTaskCreation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdBooking.responses).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(createdBooking.status).toBe(BookingStatus.ACCEPTED);
|
||||||
|
|
||||||
|
await expectBookingToBeInDatabase({
|
||||||
|
description: "",
|
||||||
|
location: "integrations:daily",
|
||||||
|
responses: expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
}),
|
||||||
|
uid: createdBooking.uid,
|
||||||
|
eventTypeId: mockBookingData.eventTypeId,
|
||||||
|
status: BookingStatus.ACCEPTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const actualBooking = await prismaMock.booking.findFirst({
|
||||||
|
where: { uid: createdBooking.uid },
|
||||||
|
include: { references: true },
|
||||||
|
});
|
||||||
|
expect(actualBooking?.references).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("combined flags", () => {
|
||||||
|
test("should work with multiple flags enabled", async () => {
|
||||||
|
const handleNewBooking = getNewBookingHandler();
|
||||||
|
const booker = getBooker({
|
||||||
|
email: "booker@example.com",
|
||||||
|
name: "Booker",
|
||||||
|
});
|
||||||
|
|
||||||
|
const organizer = getOrganizer({
|
||||||
|
name: "Organizer",
|
||||||
|
email: "organizer@example.com",
|
||||||
|
id: 101,
|
||||||
|
schedules: [TestData.schedules.IstWorkHours],
|
||||||
|
credentials: [getGoogleCalendarCredential()],
|
||||||
|
selectedCalendars: [TestData.selectedCalendars.google],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { dateString: plus1DateString } = getDate({ dateIncrement: 1 });
|
||||||
|
|
||||||
|
await createBookingScenario(
|
||||||
|
getScenarioData({
|
||||||
|
eventTypes: [
|
||||||
|
{
|
||||||
|
id: 1,
|
||||||
|
slotInterval: 45,
|
||||||
|
length: 45,
|
||||||
|
bookingLimits: {
|
||||||
|
PER_DAY: 1,
|
||||||
|
},
|
||||||
|
users: [
|
||||||
|
{
|
||||||
|
id: 101,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
organizer,
|
||||||
|
apps: [TestData.apps["google-calendar"], TestData.apps["daily-video"]],
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
mockCalendar("googlecalendar", {
|
||||||
|
create: {
|
||||||
|
uid: "MOCK_ID",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const mockBookingData = getMockRequestDataForBooking({
|
||||||
|
data: {
|
||||||
|
eventTypeId: 1,
|
||||||
|
responses: {
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
location: { optionValue: "", value: "integrations:daily" },
|
||||||
|
},
|
||||||
|
start: `${plus1DateString}T05:00:00.000Z`,
|
||||||
|
end: `${plus1DateString}T05:45:00.000Z`,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const createdBooking = await handleNewBooking({
|
||||||
|
bookingData: mockBookingData,
|
||||||
|
skipAvailabilityCheck: true,
|
||||||
|
skipEventLimitsCheck: true,
|
||||||
|
skipCalendarSyncTaskCreation: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(createdBooking.responses).toEqual(
|
||||||
|
expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(createdBooking.status).toBe(BookingStatus.ACCEPTED);
|
||||||
|
|
||||||
|
await expectBookingToBeInDatabase({
|
||||||
|
description: "",
|
||||||
|
location: "integrations:daily",
|
||||||
|
responses: expect.objectContaining({
|
||||||
|
email: booker.email,
|
||||||
|
name: booker.name,
|
||||||
|
}),
|
||||||
|
uid: createdBooking.uid,
|
||||||
|
eventTypeId: mockBookingData.eventTypeId,
|
||||||
|
status: BookingStatus.ACCEPTED,
|
||||||
|
});
|
||||||
|
|
||||||
|
const actualBooking = await prismaMock.booking.findFirst({
|
||||||
|
where: { uid: createdBooking.uid },
|
||||||
|
include: { references: true },
|
||||||
|
});
|
||||||
|
expect(actualBooking?.references).toEqual([]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -2,6 +2,7 @@ import { eventTypeMetaDataSchemaWithTypedApps } from "@calcom/app-store/zod-util
|
|||||||
import logger from "@calcom/lib/logger";
|
import logger from "@calcom/lib/logger";
|
||||||
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
|
import { MembershipRepository } from "@calcom/lib/server/repository/membership";
|
||||||
import type { Payment } from "@calcom/prisma/client";
|
import type { Payment } from "@calcom/prisma/client";
|
||||||
|
import { MembershipRole } from "@calcom/prisma/enums";
|
||||||
|
|
||||||
import { handleNoShowFee } from "./handleNoShowFee";
|
import { handleNoShowFee } from "./handleNoShowFee";
|
||||||
import { shouldChargeNoShowCancellationFee } from "./shouldChargeNoShowCancellationFee";
|
import { shouldChargeNoShowCancellationFee } from "./shouldChargeNoShowCancellationFee";
|
||||||
@@ -32,7 +33,10 @@ export const processNoShowFeeOnCancellation = async ({
|
|||||||
teamId: booking.eventType.teamId,
|
teamId: booking.eventType.teamId,
|
||||||
});
|
});
|
||||||
|
|
||||||
if (membership && (membership.role === "ADMIN" || membership.role === "OWNER")) {
|
if (
|
||||||
|
membership &&
|
||||||
|
(membership.role === MembershipRole.OWNER || membership.role === MembershipRole.ADMIN)
|
||||||
|
) {
|
||||||
log.info(
|
log.info(
|
||||||
`Booking ${booking.uid} was cancelled by team admin/owner (${cancelledByUserId}), skipping no-show fee`
|
`Booking ${booking.uid} was cancelled by team admin/owner (${cancelledByUserId}), skipping no-show fee`
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -0,0 +1,115 @@
|
|||||||
|
# Calendar Cache and Sync
|
||||||
|
|
||||||
|
The **Calendar Cache and Sync** feature provides efficient calendar synchronization with intelligent caching to reduce API calls and ensure real-time updates across your Cal.com instance.
|
||||||
|
|
||||||
|
## Feature Overview
|
||||||
|
|
||||||
|
This feature introduces two complementary capabilities:
|
||||||
|
|
||||||
|
- **Calendar Cache**: Stores calendar availability data locally to reduce external API calls and improve performance
|
||||||
|
- **Calendar Sync**: Uses webhooks to automatically listen for calendar updates and apply changes in real-time
|
||||||
|
|
||||||
|
**Key Benefits:**
|
||||||
|
- **Efficiency**: Reduces API calls with optimized caching strategies
|
||||||
|
- **Reliability**: Guarantees updates through webhook event delivery
|
||||||
|
- **Real-Time Sync**: Ensures calendars are always up-to-date with minimal latency
|
||||||
|
- **Scalability**: Supports multiple calendars and handles high-volume updates seamlessly
|
||||||
|
|
||||||
|
**Motivation:**
|
||||||
|
By subscribing to calendars via webhooks and implementing intelligent caching, you gain a smarter, faster, and more resource-friendly way to keep your data in sync. This eliminates the need for constant polling and reduces the load on external calendar APIs while ensuring data consistency.
|
||||||
|
|
||||||
|
## Environment Variables
|
||||||
|
|
||||||
|
- **GOOGLE_WEBHOOK_URL**: Optional, only used for local tests. Default points to application url.
|
||||||
|
- **GOOGLE_WEBHOOK_TOKEN**: Required, token to validate Google Webhook incoming.
|
||||||
|
- **MICROSOFT_WEBHOOK_URL**: Optional, only used for local tests. Default points to application url.
|
||||||
|
- **MICROSOFT_WEBHOOK_TOKEN**: Required, token to validate Microsoft Webhook incoming.
|
||||||
|
|
||||||
|
## Feature Flags
|
||||||
|
|
||||||
|
This feature is controlled by three feature flags that can be enabled independently:
|
||||||
|
|
||||||
|
### 1. calendar-subscription-cache
|
||||||
|
Enables calendar cache recording and usage through calendars. This flag should be managed individually by teams.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO "Feature" ("slug", "enabled", "description", "type", "stale", "lastUsedAt", "createdAt", "updatedAt", "updatedBy")
|
||||||
|
VALUES ('calendar-subscription-cache', false, 'Allow calendar cache to be recorded and used through calendars.', 'OPERATIONAL', false, NULL, NOW(), NOW(), NULL)
|
||||||
|
ON CONFLICT (slug) DO NOTHING;
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. calendar-subscription-sync
|
||||||
|
Enables calendar sync globally for all users regardless of team or organization.
|
||||||
|
|
||||||
|
```sql
|
||||||
|
INSERT INTO "Feature" ("slug", "enabled", "description", "type", "stale", "lastUsedAt", "createdAt", "updatedAt", "updatedBy")
|
||||||
|
VALUES ('calendar-subscription-sync', false, 'Enable calendar sync for all calendars globally.', 'OPERATIONAL', false, NULL, NOW(), NOW(), NULL)
|
||||||
|
ON CONFLICT (slug) DO NOTHING;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enabling Features for Specific Users
|
||||||
|
|
||||||
|
To enable calendar cache features for specific users, add entries to the `UserFeatures` table:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Enable calendar-subscription-cache for user ID 123
|
||||||
|
INSERT INTO "UserFeatures" ("userId", "featureId", "assignedAt", "assignedBy", "updatedAt")
|
||||||
|
VALUES (123, 'calendar-subscription-cache', NOW(), 'admin', NOW())
|
||||||
|
ON CONFLICT ("userId", "featureId") DO NOTHING;
|
||||||
|
|
||||||
|
-- Enable calendar-subscription-sync for user ID 123
|
||||||
|
INSERT INTO "UserFeatures" ("userId", "featureId", "assignedAt", "assignedBy", "updatedAt")
|
||||||
|
VALUES (123, 'calendar-subscription-sync', NOW(), 'admin', NOW())
|
||||||
|
ON CONFLICT ("userId", "featureId") DO NOTHING;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Enabling Features for Specific Teams
|
||||||
|
|
||||||
|
To enable calendar cache features for specific teams, add entries to the `TeamFeatures` table:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Enable calendar-subscription-cache for team ID 456
|
||||||
|
INSERT INTO "TeamFeatures" ("teamId", "featureId", "assignedAt", "assignedBy", "updatedAt")
|
||||||
|
VALUES (456, 'calendar-subscription-cache', NOW(), 'admin', NOW())
|
||||||
|
ON CONFLICT ("teamId", "featureId") DO NOTHING;
|
||||||
|
|
||||||
|
-- Enable calendar-subscription-sync for team ID 456
|
||||||
|
INSERT INTO "TeamFeatures" ("teamId", "featureId", "assignedAt", "assignedBy", "updatedAt")
|
||||||
|
VALUES (456, 'calendar-subscription-sync', NOW(), 'admin', NOW())
|
||||||
|
ON CONFLICT ("teamId", "featureId") DO NOTHING;
|
||||||
|
```
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The calendar cache and sync system consists of several key components:
|
||||||
|
|
||||||
|
### Database Schema
|
||||||
|
- **CalendarCacheEvent Table**: Stores cached calendar events with status tracking
|
||||||
|
- **SelectedCalendar Extensions**: Additional fields for sync state and webhook management including:
|
||||||
|
- `channelId`: Webhook channel identifier
|
||||||
|
- `channelResourceId`: Resource ID for webhook subscriptions
|
||||||
|
- `channelResourceUri`: URI for webhook notifications
|
||||||
|
- `channelKind`: Type of webhook channel
|
||||||
|
- `channelExpiration`: Webhook subscription expiration time
|
||||||
|
- `syncToken`: Token for incremental sync
|
||||||
|
- `syncedAt`: Last successful sync timestamp
|
||||||
|
- `syncErrorAt`: Last sync error timestamp
|
||||||
|
- `syncErrorCount`: Number of consecutive sync errors
|
||||||
|
- `syncSubscribedAt`: Webhook subscription timestamp
|
||||||
|
|
||||||
|
### Core Services
|
||||||
|
- **CalendarCacheEventRepository**: Manages cached event storage and retrieval
|
||||||
|
- **CalendarSubscriptionService**: Orchestrates webhook subscriptions and event processing
|
||||||
|
- **Provider-specific Adapters**: Handle calendar-specific sync logic (Google, Office365)
|
||||||
|
|
||||||
|
### Background Processes
|
||||||
|
- **Cron Jobs**: Automated processes for cache cleanup and calendar watching
|
||||||
|
- **Webhook Handlers**: Real-time event processing for calendar updates
|
||||||
|
|
||||||
|
### Integration Points
|
||||||
|
- **Calendar Providers**: Google Calendar, Office365, and other supported integrations
|
||||||
|
- **Webhook Endpoints**: Receive real-time notifications from calendar providers
|
||||||
|
- **Cache Layer**: Optimized storage for frequently accessed calendar data
|
||||||
|
|
||||||
|
For detailed technical implementation, see:
|
||||||
|
- Database migrations in `packages/prisma/migrations/`
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
import { GoogleCalendarSubscriptionAdapter } from "@calcom/features/calendar-subscription/adapters/GoogleCalendarSubscription.adapter";
|
||||||
|
import { Office365CalendarSubscriptionAdapter } from "@calcom/features/calendar-subscription/adapters/Office365CalendarSubscription.adapter";
|
||||||
|
import type { ICalendarSubscriptionPort } from "@calcom/features/calendar-subscription/lib/CalendarSubscriptionPort.interface";
|
||||||
|
|
||||||
|
export type CalendarSubscriptionProvider = "google_calendar" | "office365_calendar";
|
||||||
|
|
||||||
|
export interface AdapterFactory {
|
||||||
|
get(provider: CalendarSubscriptionProvider): ICalendarSubscriptionPort;
|
||||||
|
getProviders(): CalendarSubscriptionProvider[];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Default adapter factory
|
||||||
|
*/
|
||||||
|
export class DefaultAdapterFactory implements AdapterFactory {
|
||||||
|
private singletons = {
|
||||||
|
google_calendar: new GoogleCalendarSubscriptionAdapter(),
|
||||||
|
office365_calendar: new Office365CalendarSubscriptionAdapter(),
|
||||||
|
} as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the adapter for the given provider
|
||||||
|
*
|
||||||
|
* @param provider
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
get(provider: CalendarSubscriptionProvider): ICalendarSubscriptionPort {
|
||||||
|
const adapter = this.singletons[provider];
|
||||||
|
if (!adapter) {
|
||||||
|
throw new Error(`No adapter found for provider ${provider}`);
|
||||||
|
}
|
||||||
|
return adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns all available providers
|
||||||
|
*
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
getProviders(): CalendarSubscriptionProvider[] {
|
||||||
|
const providers: CalendarSubscriptionProvider[] = ["google_calendar"];
|
||||||
|
return providers;
|
||||||
|
}
|
||||||
|
}
|
||||||
+199
@@ -0,0 +1,199 @@
|
|||||||
|
import type { calendar_v3 } from "@googleapis/calendar";
|
||||||
|
import { v4 as uuid } from "uuid";
|
||||||
|
|
||||||
|
import { CalendarAuth } from "@calcom/app-store/googlecalendar/lib/CalendarAuth";
|
||||||
|
import dayjs from "@calcom/dayjs";
|
||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import type { SelectedCalendar } from "@calcom/prisma/client";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
ICalendarSubscriptionPort,
|
||||||
|
CalendarSubscriptionResult,
|
||||||
|
CalendarSubscriptionEvent,
|
||||||
|
CalendarSubscriptionEventItem,
|
||||||
|
CalendarCredential,
|
||||||
|
} from "../lib/CalendarSubscriptionPort.interface";
|
||||||
|
|
||||||
|
const log = logger.getSubLogger({ prefix: ["GoogleCalendarSubscriptionAdapter"] });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Google Calendar Subscription Adapter
|
||||||
|
*
|
||||||
|
* This adapter uses the Google Calendar API to create and manage calendar subscriptions
|
||||||
|
* @see https://developers.google.com/google-apps/calendar/quickstart/nodejs
|
||||||
|
*/
|
||||||
|
export class GoogleCalendarSubscriptionAdapter implements ICalendarSubscriptionPort {
|
||||||
|
private GOOGLE_WEBHOOK_TOKEN = process.env.GOOGLE_WEBHOOK_TOKEN;
|
||||||
|
private GOOGLE_WEBHOOK_URL = `${
|
||||||
|
process.env.GOOGLE_WEBHOOK_URL || process.env.NEXT_PUBLIC_WEBAPP_URL
|
||||||
|
}/api/webhooks/calendar-subscription/google_calendar`;
|
||||||
|
|
||||||
|
async validate(request: Request): Promise<boolean> {
|
||||||
|
const token = request?.headers?.get("X-Goog-Channel-Token");
|
||||||
|
if (!this.GOOGLE_WEBHOOK_TOKEN) {
|
||||||
|
log.warn("GOOGLE_WEBHOOK_TOKEN not configured");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (token !== this.GOOGLE_WEBHOOK_TOKEN) {
|
||||||
|
log.warn("Invalid webhook token");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async extractChannelId(request: Request): Promise<string | null> {
|
||||||
|
const channelId = request?.headers?.get("X-Goog-Channel-ID");
|
||||||
|
if (!channelId) {
|
||||||
|
log.warn("Missing channel ID in webhook");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return channelId;
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribe(
|
||||||
|
selectedCalendar: SelectedCalendar,
|
||||||
|
credential: CalendarCredential
|
||||||
|
): Promise<CalendarSubscriptionResult> {
|
||||||
|
log.debug("Attempt to subscribe to Google Calendar", { externalId: selectedCalendar.externalId });
|
||||||
|
|
||||||
|
const MONTH_IN_SECONDS = 60 * 60 * 24 * 30;
|
||||||
|
|
||||||
|
const client = await this.getClient(credential);
|
||||||
|
const result = await client.events.watch({
|
||||||
|
calendarId: selectedCalendar.externalId,
|
||||||
|
requestBody: {
|
||||||
|
id: uuid(),
|
||||||
|
type: "web_hook",
|
||||||
|
address: this.GOOGLE_WEBHOOK_URL,
|
||||||
|
token: this.GOOGLE_WEBHOOK_TOKEN,
|
||||||
|
params: {
|
||||||
|
ttl: MONTH_IN_SECONDS.toFixed(0),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const e = result.data?.expiration;
|
||||||
|
const expiration = e ? new Date(/^\d+$/.test(e) ? +e : e) : null;
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider: "google_calendar",
|
||||||
|
id: result.data.id,
|
||||||
|
resourceId: result.data.resourceId,
|
||||||
|
resourceUri: result.data.resourceUri,
|
||||||
|
expiration,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
async unsubscribe(selectedCalendar: SelectedCalendar, credential: CalendarCredential): Promise<void> {
|
||||||
|
log.debug("Attempt to unsubscribe from Google Calendar", { externalId: selectedCalendar.externalId });
|
||||||
|
|
||||||
|
const client = await this.getClient(credential);
|
||||||
|
await client.channels
|
||||||
|
.stop({
|
||||||
|
requestBody: {
|
||||||
|
id: selectedCalendar.channelId as string,
|
||||||
|
resourceId: selectedCalendar.channelResourceId as string,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
.catch((err) => {
|
||||||
|
log.error("Error unsubscribing from Google Calendar", err);
|
||||||
|
throw err;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchEvents(
|
||||||
|
selectedCalendar: SelectedCalendar,
|
||||||
|
credential: CalendarCredential
|
||||||
|
): Promise<CalendarSubscriptionEvent> {
|
||||||
|
log.info("Attempt to fetch events from Google Calendar", { externalId: selectedCalendar.externalId });
|
||||||
|
const client = await this.getClient(credential);
|
||||||
|
|
||||||
|
let syncToken = selectedCalendar.syncToken || undefined;
|
||||||
|
let pageToken;
|
||||||
|
|
||||||
|
const params: calendar_v3.Params$Resource$Events$List = {
|
||||||
|
calendarId: selectedCalendar.externalId,
|
||||||
|
pageToken,
|
||||||
|
singleEvents: true,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (!syncToken) {
|
||||||
|
const now = dayjs();
|
||||||
|
// first sync or unsync (3 months)
|
||||||
|
const threeMonths = now.add(3, "month");
|
||||||
|
|
||||||
|
const timeMinISO = now.toISOString();
|
||||||
|
const timeMaxISO = threeMonths.toISOString();
|
||||||
|
params.timeMin = timeMinISO;
|
||||||
|
params.timeMax = timeMaxISO;
|
||||||
|
} else {
|
||||||
|
// incremental sync
|
||||||
|
params.syncToken = syncToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
const events: calendar_v3.Schema$Event[] = [];
|
||||||
|
do {
|
||||||
|
const { data }: { data: calendar_v3.Schema$Events } = await client.events.list(params);
|
||||||
|
|
||||||
|
syncToken = data.nextSyncToken || syncToken;
|
||||||
|
pageToken = data.nextPageToken ?? null;
|
||||||
|
|
||||||
|
events.push(...(data.items || []));
|
||||||
|
} while (pageToken);
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider: "google_calendar",
|
||||||
|
syncToken: syncToken || null,
|
||||||
|
items: this.parseEvents(events),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseEvents(events: calendar_v3.Schema$Event[]): CalendarSubscriptionEventItem[] {
|
||||||
|
return events
|
||||||
|
.filter((event) => typeof event.id === "string" && !!event.id)
|
||||||
|
.map((event) => {
|
||||||
|
// empty or opaque is busy
|
||||||
|
const busy = !event.transparency || event.transparency === "opaque";
|
||||||
|
|
||||||
|
const start = event.start?.dateTime
|
||||||
|
? new Date(event.start.dateTime)
|
||||||
|
: event.start?.date
|
||||||
|
? new Date(event.start.date)
|
||||||
|
: new Date();
|
||||||
|
|
||||||
|
const end = event.end?.dateTime
|
||||||
|
? new Date(event.end.dateTime)
|
||||||
|
: event.end?.date
|
||||||
|
? new Date(event.end.date)
|
||||||
|
: new Date();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: event.id as string,
|
||||||
|
iCalUID: event.iCalUID ?? null,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
busy,
|
||||||
|
summary: event.summary ?? null,
|
||||||
|
description: event.description ?? null,
|
||||||
|
location: event.location ?? null,
|
||||||
|
kind: event.kind ?? null,
|
||||||
|
etag: event.etag ?? null,
|
||||||
|
status: event.status ?? null,
|
||||||
|
isAllDay: typeof event.start?.date === "string" && !event.start?.dateTime ? true : false,
|
||||||
|
timeZone: event.start?.timeZone ?? null,
|
||||||
|
recurringEventId: event.recurringEventId ?? null,
|
||||||
|
originalStartDate: event.originalStartTime?.dateTime
|
||||||
|
? new Date(event.originalStartTime.dateTime)
|
||||||
|
: event.originalStartTime?.date
|
||||||
|
? new Date(event.originalStartTime.date)
|
||||||
|
: null,
|
||||||
|
createdAt: event.created ? new Date(event.created) : null,
|
||||||
|
updatedAt: event.updated ? new Date(event.updated) : null,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getClient(credential: CalendarCredential) {
|
||||||
|
const auth = new CalendarAuth(credential);
|
||||||
|
return await auth.getClient();
|
||||||
|
}
|
||||||
|
}
|
||||||
+262
@@ -0,0 +1,262 @@
|
|||||||
|
import logger from "@calcom/lib/logger";
|
||||||
|
import type { SelectedCalendar } from "@calcom/prisma/client";
|
||||||
|
|
||||||
|
import type {
|
||||||
|
CalendarSubscriptionEvent,
|
||||||
|
ICalendarSubscriptionPort,
|
||||||
|
CalendarSubscriptionResult,
|
||||||
|
CalendarCredential,
|
||||||
|
CalendarSubscriptionEventItem,
|
||||||
|
} from "../lib/CalendarSubscriptionPort.interface";
|
||||||
|
|
||||||
|
const log = logger.getSubLogger({ prefix: ["MicrosoftCalendarSubscriptionAdapter"] });
|
||||||
|
|
||||||
|
type HttpMethod = "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
|
||||||
|
type GraphClient = { accessToken: string };
|
||||||
|
|
||||||
|
interface MicrosoftGraphEvent {
|
||||||
|
id: string;
|
||||||
|
iCalUId?: string;
|
||||||
|
subject?: string;
|
||||||
|
bodyPreview?: string;
|
||||||
|
location?: { displayName?: string };
|
||||||
|
start?: { dateTime: string; timeZone: string };
|
||||||
|
end?: { dateTime: string; timeZone: string };
|
||||||
|
showAs?: "free" | "tentative" | "busy" | "oof" | "workingElsewhere" | "unknown";
|
||||||
|
isAllDay?: boolean;
|
||||||
|
isCancelled?: boolean;
|
||||||
|
type?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MicrosoftGraphEventsResponse {
|
||||||
|
"@odata.nextLink"?: string;
|
||||||
|
"@odata.deltaLink"?: string;
|
||||||
|
value: MicrosoftGraphEvent[];
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MicrosoftGraphSubscriptionReq {
|
||||||
|
resource: string;
|
||||||
|
changeType: string;
|
||||||
|
notificationUrl: string;
|
||||||
|
expirationDateTime: string;
|
||||||
|
clientState?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface MicrosoftGraphSubscriptionRes {
|
||||||
|
id: string;
|
||||||
|
resource: string;
|
||||||
|
expirationDateTime: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
type AdapterConfig = {
|
||||||
|
baseUrl?: string;
|
||||||
|
webhookToken?: string | null;
|
||||||
|
webhookUrl?: string | null;
|
||||||
|
subscriptionTtlMs?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Office365 Calendar Subscription Adapter
|
||||||
|
*
|
||||||
|
* This adapter uses the Microsoft Graph API to create and manage calendar subscriptions
|
||||||
|
* @see https://docs.microsoft.com/en-us/graph/api/resources/subscription
|
||||||
|
*/
|
||||||
|
export class Office365CalendarSubscriptionAdapter implements ICalendarSubscriptionPort {
|
||||||
|
private readonly baseUrl: string;
|
||||||
|
private readonly webhookToken?: string | null;
|
||||||
|
private readonly webhookUrl?: string | null;
|
||||||
|
private readonly subscriptionTtlMs: number;
|
||||||
|
|
||||||
|
constructor(cfg: AdapterConfig = {}) {
|
||||||
|
this.baseUrl = cfg.baseUrl ?? "https://graph.microsoft.com/v1.0";
|
||||||
|
this.webhookToken = cfg.webhookToken ?? process.env.MICROSOFT_WEBHOOK_TOKEN ?? null;
|
||||||
|
this.webhookUrl = cfg.webhookUrl ?? process.env.MICROSOFT_WEBHOOK_URL ?? null;
|
||||||
|
this.subscriptionTtlMs = cfg.subscriptionTtlMs ?? 3 * 24 * 60 * 60 * 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
async validate(request: Request): Promise<boolean> {
|
||||||
|
// validate handshake
|
||||||
|
let validationToken: string | null = null;
|
||||||
|
if (request?.url) {
|
||||||
|
try {
|
||||||
|
const urlObj = new URL(request.url);
|
||||||
|
validationToken = urlObj.searchParams.get("validationToken");
|
||||||
|
} catch (e) {
|
||||||
|
log.warn("Invalid request URL", { url: request.url });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (validationToken) return true;
|
||||||
|
|
||||||
|
// validate notifications
|
||||||
|
const clientState =
|
||||||
|
request?.headers?.get("clientState") ??
|
||||||
|
(typeof request?.body === "object" && request.body !== null && "clientState" in request.body
|
||||||
|
? (request.body as { clientState?: string }).clientState
|
||||||
|
: undefined);
|
||||||
|
if (!this.webhookToken) {
|
||||||
|
log.warn("MICROSOFT_WEBHOOK_TOKEN missing");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (clientState !== this.webhookToken) {
|
||||||
|
log.warn("Invalid clientState");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
async extractChannelId(request: Request): Promise<string | null> {
|
||||||
|
let id: string | null = null;
|
||||||
|
if (request?.body && typeof request.body === "object" && "subscriptionId" in request.body) {
|
||||||
|
id = (request.body as { subscriptionId?: string }).subscriptionId ?? null;
|
||||||
|
} else if (request?.headers?.get("subscriptionId")) {
|
||||||
|
id = request.headers.get("subscriptionId");
|
||||||
|
}
|
||||||
|
if (!id) {
|
||||||
|
log.warn("subscriptionId missing in webhook");
|
||||||
|
}
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
async subscribe(
|
||||||
|
selectedCalendar: SelectedCalendar,
|
||||||
|
credential: CalendarCredential
|
||||||
|
): Promise<CalendarSubscriptionResult> {
|
||||||
|
if (!this.webhookUrl || !this.webhookToken) {
|
||||||
|
throw new Error("Webhook config missing (MICROSOFT_WEBHOOK_URL/TOKEN)");
|
||||||
|
}
|
||||||
|
|
||||||
|
const expirationDateTime = new Date(Date.now() + this.subscriptionTtlMs).toISOString();
|
||||||
|
|
||||||
|
const body: MicrosoftGraphSubscriptionReq = {
|
||||||
|
resource: `me/calendars/${selectedCalendar.externalId}/events`,
|
||||||
|
changeType: "created,updated,deleted",
|
||||||
|
notificationUrl: this.webhookUrl,
|
||||||
|
expirationDateTime,
|
||||||
|
clientState: this.webhookToken,
|
||||||
|
};
|
||||||
|
|
||||||
|
const client = await this.getGraphClient(credential);
|
||||||
|
const res = await this.request<MicrosoftGraphSubscriptionRes>(client, "POST", "/subscriptions", body);
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider: "office365_calendar",
|
||||||
|
id: res.id,
|
||||||
|
resourceId: res.resource,
|
||||||
|
resourceUri: `${this.baseUrl}/${res.resource}`,
|
||||||
|
expiration: new Date(res.expirationDateTime),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
async unsubscribe(selectedCalendar: SelectedCalendar, credential: CalendarCredential): Promise<void> {
|
||||||
|
const subId = selectedCalendar.channelResourceId;
|
||||||
|
if (!subId) return;
|
||||||
|
|
||||||
|
const client = await this.getGraphClient(credential);
|
||||||
|
await this.request(client, "DELETE", `/subscriptions/${subId}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
async fetchEvents(
|
||||||
|
selectedCalendar: SelectedCalendar,
|
||||||
|
credential: CalendarCredential
|
||||||
|
): Promise<CalendarSubscriptionEvent> {
|
||||||
|
const client = await this.getGraphClient(credential);
|
||||||
|
|
||||||
|
let deltaLink = selectedCalendar.syncToken ?? null;
|
||||||
|
const items: MicrosoftGraphEvent[] = [];
|
||||||
|
|
||||||
|
if (deltaLink) {
|
||||||
|
const path = this.stripBase(deltaLink);
|
||||||
|
const r = await this.request<MicrosoftGraphEventsResponse>(client, "GET", path);
|
||||||
|
items.push(...r.value);
|
||||||
|
deltaLink = r["@odata.deltaLink"] ?? deltaLink;
|
||||||
|
} else {
|
||||||
|
let next: string | null = `/me/calendars/${selectedCalendar.externalId}/events/delta`;
|
||||||
|
while (next) {
|
||||||
|
const r: MicrosoftGraphEventsResponse = await this.request<MicrosoftGraphEventsResponse>(
|
||||||
|
client,
|
||||||
|
"GET",
|
||||||
|
next
|
||||||
|
);
|
||||||
|
items.push(...r.value);
|
||||||
|
deltaLink = r["@odata.deltaLink"] ?? deltaLink;
|
||||||
|
next = r["@odata.nextLink"] ? this.stripBase(r["@odata.nextLink"]) : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
provider: "office365_calendar",
|
||||||
|
syncToken: deltaLink,
|
||||||
|
items: this.parseEvents(items),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private parseEvents(events: MicrosoftGraphEvent[]): CalendarSubscriptionEventItem[] {
|
||||||
|
return events
|
||||||
|
.map((e) => {
|
||||||
|
const busy = e.showAs === "busy" || e.showAs === "tentative" || e.showAs === "oof";
|
||||||
|
const start = e.start?.dateTime ? new Date(e.start.dateTime) : new Date();
|
||||||
|
const end = e.end?.dateTime ? new Date(e.end.dateTime) : new Date();
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: e.id,
|
||||||
|
iCalUID: e.iCalUId ?? null,
|
||||||
|
start,
|
||||||
|
end,
|
||||||
|
busy,
|
||||||
|
etag: null,
|
||||||
|
summary: e.subject ?? null,
|
||||||
|
description: e.bodyPreview ?? null,
|
||||||
|
location: e.location?.displayName ?? null,
|
||||||
|
kind: e.type ?? "microsoftgraph#event",
|
||||||
|
status: e.isCancelled ? "cancelled" : "confirmed",
|
||||||
|
isAllDay: e.isAllDay ?? false,
|
||||||
|
timeZone: e.start?.timeZone ?? null,
|
||||||
|
recurringEventId: null,
|
||||||
|
originalStartDate: null,
|
||||||
|
createdAt: null,
|
||||||
|
updatedAt: null,
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.filter(({ id }) => !!id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private async getGraphClient(credential: CalendarCredential): Promise<GraphClient> {
|
||||||
|
const accessToken = credential.delegatedTo?.serviceAccountKey?.private_key ?? (credential.key as string);
|
||||||
|
if (!accessToken) throw new Error("Missing Microsoft access token");
|
||||||
|
return { accessToken };
|
||||||
|
}
|
||||||
|
|
||||||
|
private stripBase(urlOrPath: string): string {
|
||||||
|
return urlOrPath.startsWith("http") ? urlOrPath.replace(this.baseUrl, "") : urlOrPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async request<T = unknown>(
|
||||||
|
client: GraphClient,
|
||||||
|
method: HttpMethod,
|
||||||
|
endpoint: string,
|
||||||
|
data?: unknown
|
||||||
|
): Promise<T> {
|
||||||
|
const url = endpoint.startsWith("http") ? endpoint : `${this.baseUrl}${endpoint}`;
|
||||||
|
const headers: Record<string, string> = {
|
||||||
|
Authorization: `Bearer ${client.accessToken}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
};
|
||||||
|
|
||||||
|
const init: RequestInit = { method, headers };
|
||||||
|
if (data && (method === "POST" || method === "PUT" || method === "PATCH")) {
|
||||||
|
init.body = JSON.stringify(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
const res = await fetch(url, init);
|
||||||
|
|
||||||
|
if (!res.ok) {
|
||||||
|
const text = await res.text().catch(() => "");
|
||||||
|
log.error("Graph API error", { method, endpoint: this.stripBase(url), status: res.status, text });
|
||||||
|
throw new Error(`Graph ${res.status} ${res.statusText}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (method === "DELETE" || res.status === 204) return {} as T;
|
||||||
|
|
||||||
|
return (await res.json()) as T;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
import { vi } from "vitest";
|
||||||
|
|
||||||
|
export const CalendarAuth = vi.fn().mockImplementation(() => ({
|
||||||
|
getClient: vi.fn().mockResolvedValue({
|
||||||
|
events: {
|
||||||
|
watch: vi.fn(),
|
||||||
|
list: vi.fn(),
|
||||||
|
},
|
||||||
|
channels: {
|
||||||
|
stop: vi.fn(),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
}));
|
||||||
|
|
||||||
|
vi.doMock("@calcom/app-store/googlecalendar/lib/CalendarAuth", () => ({
|
||||||
|
CalendarAuth,
|
||||||
|
}));
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
import { describe, test, expect, beforeEach } from "vitest";
|
||||||
|
|
||||||
|
import { DefaultAdapterFactory } from "../AdaptersFactory";
|
||||||
|
import { GoogleCalendarSubscriptionAdapter } from "../GoogleCalendarSubscription.adapter";
|
||||||
|
import { Office365CalendarSubscriptionAdapter } from "../Office365CalendarSubscription.adapter";
|
||||||
|
|
||||||
|
describe("DefaultAdapterFactory", () => {
|
||||||
|
let factory: DefaultAdapterFactory;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
factory = new DefaultAdapterFactory();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("get", () => {
|
||||||
|
test("should return GoogleCalendarSubscriptionAdapter for google_calendar", () => {
|
||||||
|
const adapter = factory.get("google_calendar");
|
||||||
|
|
||||||
|
expect(adapter).toBeInstanceOf(GoogleCalendarSubscriptionAdapter);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return Office365CalendarSubscriptionAdapter for office365_calendar", () => {
|
||||||
|
const adapter = factory.get("office365_calendar");
|
||||||
|
|
||||||
|
expect(adapter).toBeInstanceOf(Office365CalendarSubscriptionAdapter);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return the same instance for multiple calls (singleton)", () => {
|
||||||
|
const adapter1 = factory.get("google_calendar");
|
||||||
|
const adapter2 = factory.get("google_calendar");
|
||||||
|
|
||||||
|
expect(adapter1).toBe(adapter2);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should throw error for unsupported provider", () => {
|
||||||
|
expect(() => {
|
||||||
|
factory.get("unsupported_calendar" as never);
|
||||||
|
}).toThrow("No adapter found for provider unsupported_calendar");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("getProviders", () => {
|
||||||
|
test("should return all available providers", () => {
|
||||||
|
const providers = factory.getProviders();
|
||||||
|
|
||||||
|
expect(providers).toEqual(["google_calendar"]);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return array with correct length", () => {
|
||||||
|
const providers = factory.getProviders();
|
||||||
|
|
||||||
|
expect(providers).toHaveLength(1);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
+497
@@ -0,0 +1,497 @@
|
|||||||
|
import "../__mocks__/CalendarAuth";
|
||||||
|
|
||||||
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
||||||
|
|
||||||
|
import type { SelectedCalendar } from "@calcom/prisma/client";
|
||||||
|
import type { CredentialForCalendarServiceWithEmail } from "@calcom/types/Credential";
|
||||||
|
|
||||||
|
import { GoogleCalendarSubscriptionAdapter } from "../GoogleCalendarSubscription.adapter";
|
||||||
|
|
||||||
|
vi.mock("uuid", () => ({
|
||||||
|
v4: vi.fn().mockReturnValue("test-uuid"),
|
||||||
|
}));
|
||||||
|
|
||||||
|
const mockSelectedCalendar: SelectedCalendar = {
|
||||||
|
id: "test-calendar-id",
|
||||||
|
userId: 1,
|
||||||
|
credentialId: 1,
|
||||||
|
integration: "google_calendar",
|
||||||
|
externalId: "test@example.com",
|
||||||
|
eventTypeId: null,
|
||||||
|
delegationCredentialId: null,
|
||||||
|
domainWideDelegationCredentialId: null,
|
||||||
|
googleChannelId: null,
|
||||||
|
googleChannelKind: null,
|
||||||
|
googleChannelResourceId: null,
|
||||||
|
googleChannelResourceUri: null,
|
||||||
|
googleChannelExpiration: null,
|
||||||
|
error: null,
|
||||||
|
lastErrorAt: null,
|
||||||
|
watchAttempts: 0,
|
||||||
|
maxAttempts: 3,
|
||||||
|
unwatchAttempts: 0,
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date(),
|
||||||
|
channelId: "test-channel-id",
|
||||||
|
channelKind: "web_hook",
|
||||||
|
channelResourceId: "test-resource-id",
|
||||||
|
channelResourceUri: "test-resource-uri",
|
||||||
|
channelExpiration: new Date(Date.now() + 86400000),
|
||||||
|
syncSubscribedAt: new Date(),
|
||||||
|
syncToken: "test-sync-token",
|
||||||
|
syncedAt: new Date(),
|
||||||
|
syncErrorAt: null,
|
||||||
|
syncErrorCount: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockCredential = {
|
||||||
|
id: 1,
|
||||||
|
key: { access_token: "test-token" },
|
||||||
|
user: { email: "test@example.com" },
|
||||||
|
delegatedTo: null,
|
||||||
|
type: null,
|
||||||
|
teamId: null,
|
||||||
|
} as unknown as CredentialForCalendarServiceWithEmail;
|
||||||
|
|
||||||
|
describe("GoogleCalendarSubscriptionAdapter", () => {
|
||||||
|
let adapter: GoogleCalendarSubscriptionAdapter;
|
||||||
|
let mockClient: {
|
||||||
|
events: {
|
||||||
|
watch: ReturnType<typeof vi.fn>;
|
||||||
|
list: ReturnType<typeof vi.fn>;
|
||||||
|
};
|
||||||
|
channels: {
|
||||||
|
stop: ReturnType<typeof vi.fn>;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
process.env.GOOGLE_WEBHOOK_TOKEN = "test-webhook-token";
|
||||||
|
process.env.GOOGLE_WEBHOOK_URL = "https://example.com";
|
||||||
|
|
||||||
|
mockClient = {
|
||||||
|
events: {
|
||||||
|
watch: vi.fn(),
|
||||||
|
list: vi.fn(),
|
||||||
|
},
|
||||||
|
channels: {
|
||||||
|
stop: vi.fn(),
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const { CalendarAuth } = await import("../__mocks__/CalendarAuth");
|
||||||
|
vi.mocked(CalendarAuth).mockImplementation(() => ({
|
||||||
|
getClient: vi.fn().mockResolvedValue(mockClient),
|
||||||
|
}));
|
||||||
|
|
||||||
|
adapter = new GoogleCalendarSubscriptionAdapter();
|
||||||
|
vi.clearAllMocks();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("validate", () => {
|
||||||
|
test("should validate webhook with correct token", async () => {
|
||||||
|
const mockRequest = {
|
||||||
|
headers: {
|
||||||
|
get: vi.fn().mockReturnValue("test-webhook-token"),
|
||||||
|
},
|
||||||
|
} as unknown as Request;
|
||||||
|
|
||||||
|
const result = await adapter.validate(mockRequest);
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
expect(mockRequest.headers.get).toHaveBeenCalledWith("X-Goog-Channel-Token");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should reject webhook with incorrect token", async () => {
|
||||||
|
const mockRequest = {
|
||||||
|
headers: {
|
||||||
|
get: vi.fn().mockReturnValue("wrong-token"),
|
||||||
|
},
|
||||||
|
} as unknown as Request;
|
||||||
|
|
||||||
|
const result = await adapter.validate(mockRequest);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should reject webhook when token is not configured", async () => {
|
||||||
|
delete process.env.GOOGLE_WEBHOOK_TOKEN;
|
||||||
|
adapter = new GoogleCalendarSubscriptionAdapter();
|
||||||
|
|
||||||
|
const mockRequest = {
|
||||||
|
headers: {
|
||||||
|
get: vi.fn().mockReturnValue("test-webhook-token"),
|
||||||
|
},
|
||||||
|
} as unknown as Request;
|
||||||
|
|
||||||
|
const result = await adapter.validate(mockRequest);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should reject webhook with missing token header", async () => {
|
||||||
|
const mockRequest = {
|
||||||
|
headers: {
|
||||||
|
get: vi.fn().mockReturnValue(null),
|
||||||
|
},
|
||||||
|
} as unknown as Request;
|
||||||
|
|
||||||
|
const result = await adapter.validate(mockRequest);
|
||||||
|
|
||||||
|
expect(result).toBe(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("extractChannelId", () => {
|
||||||
|
test("should extract channel ID from webhook headers", async () => {
|
||||||
|
const mockRequest = {
|
||||||
|
headers: {
|
||||||
|
get: vi.fn().mockReturnValue("test-channel-id"),
|
||||||
|
},
|
||||||
|
} as unknown as Request;
|
||||||
|
|
||||||
|
const result = await adapter.extractChannelId(mockRequest);
|
||||||
|
|
||||||
|
expect(result).toBe("test-channel-id");
|
||||||
|
expect(mockRequest.headers.get).toHaveBeenCalledWith("X-Goog-Channel-ID");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should return null when channel ID is missing", async () => {
|
||||||
|
const mockRequest = {
|
||||||
|
headers: {
|
||||||
|
get: vi.fn().mockReturnValue(null),
|
||||||
|
},
|
||||||
|
} as unknown as Request;
|
||||||
|
|
||||||
|
const result = await adapter.extractChannelId(mockRequest);
|
||||||
|
|
||||||
|
expect(result).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("subscribe", () => {
|
||||||
|
test("should successfully subscribe to calendar", async () => {
|
||||||
|
const mockWatchResponse = {
|
||||||
|
data: {
|
||||||
|
id: "test-channel-id",
|
||||||
|
resourceId: "test-resource-id",
|
||||||
|
resourceUri: "test-resource-uri",
|
||||||
|
expiration: "1640995200000",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.watch.mockResolvedValue(mockWatchResponse);
|
||||||
|
|
||||||
|
const result = await adapter.subscribe(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(mockClient.events.watch).toHaveBeenCalledWith({
|
||||||
|
calendarId: "test@example.com",
|
||||||
|
requestBody: {
|
||||||
|
id: "test-uuid",
|
||||||
|
type: "web_hook",
|
||||||
|
address: "https://example.com/api/webhooks/calendar-subscription/google_calendar",
|
||||||
|
token: "test-webhook-token",
|
||||||
|
params: {
|
||||||
|
ttl: "2592000",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
provider: "google_calendar",
|
||||||
|
id: "test-channel-id",
|
||||||
|
resourceId: "test-resource-id",
|
||||||
|
resourceUri: "test-resource-uri",
|
||||||
|
expiration: new Date(1640995200000),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle expiration as ISO string", async () => {
|
||||||
|
const mockWatchResponse = {
|
||||||
|
data: {
|
||||||
|
id: "test-channel-id",
|
||||||
|
resourceId: "test-resource-id",
|
||||||
|
resourceUri: "test-resource-uri",
|
||||||
|
expiration: "2023-12-01T10:00:00Z",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.watch.mockResolvedValue(mockWatchResponse);
|
||||||
|
|
||||||
|
const result = await adapter.subscribe(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(result.expiration).toEqual(new Date("2023-12-01T10:00:00Z"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle missing expiration", async () => {
|
||||||
|
const mockWatchResponse = {
|
||||||
|
data: {
|
||||||
|
id: "test-channel-id",
|
||||||
|
resourceId: "test-resource-id",
|
||||||
|
resourceUri: "test-resource-uri",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.watch.mockResolvedValue(mockWatchResponse);
|
||||||
|
|
||||||
|
const result = await adapter.subscribe(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(result.expiration).toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("unsubscribe", () => {
|
||||||
|
test("should successfully unsubscribe from calendar", async () => {
|
||||||
|
mockClient.channels.stop.mockResolvedValue({});
|
||||||
|
|
||||||
|
await adapter.unsubscribe(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(mockClient.channels.stop).toHaveBeenCalledWith({
|
||||||
|
requestBody: {
|
||||||
|
id: "test-channel-id",
|
||||||
|
resourceId: "test-resource-id",
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle unsubscribe errors", async () => {
|
||||||
|
const error = new Error("Unsubscribe failed");
|
||||||
|
mockClient.channels.stop.mockRejectedValue(error);
|
||||||
|
|
||||||
|
await expect(adapter.unsubscribe(mockSelectedCalendar, mockCredential)).rejects.toThrow(
|
||||||
|
"Unsubscribe failed"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("fetchEvents", () => {
|
||||||
|
test("should fetch events with sync token", async () => {
|
||||||
|
const mockEventsResponse = {
|
||||||
|
data: {
|
||||||
|
nextSyncToken: "new-sync-token",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-1",
|
||||||
|
iCalUID: "event-1@cal.com",
|
||||||
|
summary: "Test Event",
|
||||||
|
description: "Test Description",
|
||||||
|
location: "Test Location",
|
||||||
|
start: {
|
||||||
|
dateTime: "2023-12-01T10:00:00Z",
|
||||||
|
timeZone: "UTC",
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
dateTime: "2023-12-01T11:00:00Z",
|
||||||
|
},
|
||||||
|
status: "confirmed",
|
||||||
|
transparency: "opaque",
|
||||||
|
kind: "calendar#event",
|
||||||
|
etag: "test-etag",
|
||||||
|
created: "2023-12-01T09:00:00Z",
|
||||||
|
updated: "2023-12-01T09:30:00Z",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.list.mockResolvedValue(mockEventsResponse);
|
||||||
|
|
||||||
|
const result = await adapter.fetchEvents(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(mockClient.events.list).toHaveBeenCalledWith({
|
||||||
|
calendarId: "test@example.com",
|
||||||
|
pageToken: undefined,
|
||||||
|
singleEvents: true,
|
||||||
|
syncToken: "test-sync-token",
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
provider: "google_calendar",
|
||||||
|
syncToken: "new-sync-token",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-1",
|
||||||
|
iCalUID: "event-1@cal.com",
|
||||||
|
start: new Date("2023-12-01T10:00:00Z"),
|
||||||
|
end: new Date("2023-12-01T11:00:00Z"),
|
||||||
|
busy: true,
|
||||||
|
summary: "Test Event",
|
||||||
|
description: "Test Description",
|
||||||
|
location: "Test Location",
|
||||||
|
kind: "calendar#event",
|
||||||
|
etag: "test-etag",
|
||||||
|
status: "confirmed",
|
||||||
|
isAllDay: false,
|
||||||
|
timeZone: "UTC",
|
||||||
|
recurringEventId: null,
|
||||||
|
originalStartDate: null,
|
||||||
|
createdAt: new Date("2023-12-01T09:00:00Z"),
|
||||||
|
updatedAt: new Date("2023-12-01T09:30:00Z"),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should fetch events without sync token (initial sync)", async () => {
|
||||||
|
const calendarWithoutSyncToken = {
|
||||||
|
...mockSelectedCalendar,
|
||||||
|
syncToken: null,
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockEventsResponse = {
|
||||||
|
data: {
|
||||||
|
nextSyncToken: "initial-sync-token",
|
||||||
|
items: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.list.mockResolvedValue(mockEventsResponse);
|
||||||
|
|
||||||
|
const result = await adapter.fetchEvents(calendarWithoutSyncToken, mockCredential);
|
||||||
|
|
||||||
|
expect(mockClient.events.list).toHaveBeenCalledWith({
|
||||||
|
calendarId: "test@example.com",
|
||||||
|
pageToken: undefined,
|
||||||
|
singleEvents: true,
|
||||||
|
timeMin: expect.any(String),
|
||||||
|
timeMax: expect.any(String),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result.syncToken).toBe("initial-sync-token");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle all-day events", async () => {
|
||||||
|
const mockEventsResponse = {
|
||||||
|
data: {
|
||||||
|
nextSyncToken: "new-sync-token",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-1",
|
||||||
|
iCalUID: "event-1@cal.com",
|
||||||
|
summary: "All Day Event",
|
||||||
|
start: {
|
||||||
|
date: "2023-12-01",
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
date: "2023-12-02",
|
||||||
|
},
|
||||||
|
status: "confirmed",
|
||||||
|
transparency: "opaque",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.list.mockResolvedValue(mockEventsResponse);
|
||||||
|
|
||||||
|
const result = await adapter.fetchEvents(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(result.items[0].isAllDay).toBe(true);
|
||||||
|
expect(result.items[0].start).toEqual(new Date("2023-12-01"));
|
||||||
|
expect(result.items[0].end).toEqual(new Date("2023-12-02"));
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle free events (transparent)", async () => {
|
||||||
|
const mockEventsResponse = {
|
||||||
|
data: {
|
||||||
|
nextSyncToken: "new-sync-token",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-1",
|
||||||
|
iCalUID: "event-1@cal.com",
|
||||||
|
summary: "Free Event",
|
||||||
|
start: {
|
||||||
|
dateTime: "2023-12-01T10:00:00Z",
|
||||||
|
},
|
||||||
|
end: {
|
||||||
|
dateTime: "2023-12-01T11:00:00Z",
|
||||||
|
},
|
||||||
|
status: "confirmed",
|
||||||
|
transparency: "transparent",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.list.mockResolvedValue(mockEventsResponse);
|
||||||
|
|
||||||
|
const result = await adapter.fetchEvents(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(result.items[0].busy).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should handle pagination", async () => {
|
||||||
|
const mockEventsResponse1 = {
|
||||||
|
data: {
|
||||||
|
nextPageToken: "page-2",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-1",
|
||||||
|
summary: "Event 1",
|
||||||
|
start: { dateTime: "2023-12-01T10:00:00Z" },
|
||||||
|
end: { dateTime: "2023-12-01T11:00:00Z" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockEventsResponse2 = {
|
||||||
|
data: {
|
||||||
|
nextSyncToken: "final-sync-token",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-2",
|
||||||
|
summary: "Event 2",
|
||||||
|
start: { dateTime: "2023-12-01T12:00:00Z" },
|
||||||
|
end: { dateTime: "2023-12-01T13:00:00Z" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.list
|
||||||
|
.mockResolvedValueOnce(mockEventsResponse1)
|
||||||
|
.mockResolvedValueOnce(mockEventsResponse2);
|
||||||
|
|
||||||
|
const result = await adapter.fetchEvents(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(mockClient.events.list).toHaveBeenCalledTimes(2);
|
||||||
|
expect(result.items).toHaveLength(2);
|
||||||
|
expect(result.syncToken).toBe("final-sync-token");
|
||||||
|
});
|
||||||
|
|
||||||
|
test("should filter out events without ID", async () => {
|
||||||
|
const mockEventsResponse = {
|
||||||
|
data: {
|
||||||
|
nextSyncToken: "new-sync-token",
|
||||||
|
items: [
|
||||||
|
{
|
||||||
|
id: "event-1",
|
||||||
|
summary: "Valid Event",
|
||||||
|
start: { dateTime: "2023-12-01T10:00:00Z" },
|
||||||
|
end: { dateTime: "2023-12-01T11:00:00Z" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
summary: "Invalid Event",
|
||||||
|
start: { dateTime: "2023-12-01T12:00:00Z" },
|
||||||
|
end: { dateTime: "2023-12-01T13:00:00Z" },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: "",
|
||||||
|
summary: "Empty ID Event",
|
||||||
|
start: { dateTime: "2023-12-01T14:00:00Z" },
|
||||||
|
end: { dateTime: "2023-12-01T15:00:00Z" },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
mockClient.events.list.mockResolvedValue(mockEventsResponse);
|
||||||
|
|
||||||
|
const result = await adapter.fetchEvents(mockSelectedCalendar, mockCredential);
|
||||||
|
|
||||||
|
expect(result.items).toHaveLength(1);
|
||||||
|
expect(result.items[0].id).toBe("event-1");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user