Compare commits

...
47 Commits
Author SHA1 Message Date
emrysal da7aeb4a21 chore: release v5.7.9
Post release / release (push) Canceled after 0s
2025-10-06 23:57:19 +00:00
Keith Williams 6197a2a5c5 chore: Upgrade Next.js to 15.5.4 (#24307) 2025-10-06 21:39:26 +00:00
Syed Ali ShahbazandAlex van Andel de186b95c1 fix: create missing migration for dropping obsolete indexes (#24304)
* Create migration.sql

* Drop index if exists, otherwise do nada

---------

Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-10-06 21:33:00 +00:00
Syed Ali Shahbaz 6b24513073 Update how-to-setup-api-in-a-local-instance.mdx (#24300) 2025-10-06 17:39:55 +00:00
Kinbaum 55ea18128a fix: timeZone variable name does not match webhook payload (#24279) 2025-10-06 22:49:28 +05:30
Calcom BotandLingo.dev d5557fa512 feat: update translations via @LingoDotDev (#24301)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-10-06 12:59:10 -03:00
Hariom BalharaandDevin AI 9c97b2aa6d feat: add usernameInOrg field to webhook organizer payload for organization users (#23246)
* feat: add usernameInOrg field to webhook organizer payload for organization users

- Add usernameInOrg field to CalendarEventBuilder organizer interface
- Update handleNewBooking to pass organizerOrganizationProfile.username as usernameInOrg
- Include usernameInOrg in webhook payload generation (sendPayload.ts)
- Add webhook form variable for usernameInOrg with translation
- Update Person type to include usernameInOrg field
- Add test case for organization user webhook verification
- Maintain backward compatibility by keeping existing username field unchanged

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

* feat: add usernameInOrg to additional webhook sending locations

- Update handleCancelBooking.ts to include usernameInOrg in organizer payload
- Update confirm.handler.ts to include usernameInOrg for booking confirmations
- Update getBooking.ts to include usernameInOrg in payment-related webhooks
- Maintain backward compatibility with existing username field
- All webhook sending locations now include organization profile username

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

* fixes

* Add subteam event test for usernameInOrg

* fix eslitn issues

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-10-06 19:51:04 +05:30
sean-brydonandClaude 1e226a40d1 Add next-auth-options.ts to Foundation code owners (#24295)
Added packages/features/auth/lib/next-auth-options.ts to CODEOWNERS for the Foundation team to ensure proper review and oversight of authentication configuration changes.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-authored-by: Claude <noreply@anthropic.com>
2025-10-06 11:55:58 +00:00
Hariom Balhara bbe46eaa7d Restrict the length of emails as per RFC (#24269) 2025-10-06 10:22:58 +00:00
sean-brydon 7799b191ec feat: botid enabled on api/book/event api route (#24207)
* wip

* WIP

* restore event

* testing without instrument client

* Add conditional for botID init

* Bump BotID version + pass in header

* botID yarn lock changes

* feat: Add feature flag checks + tidy up into service

* rely on env var also

* use eventType repo instead of passing in prisma

* remove slug from audit

* rename botId feature to botid

* add unit tests for bot service
2025-10-06 11:16:13 +01:00
Anik Dhabal Babu f3269a3ddf fix: contentType is added in worng format (#24284) 2025-10-06 06:29:54 +00:00
emrysal a4be822636 chore: release v5.7.8
Post release / release (push) Canceled after 0s
2025-10-04 14:20:17 +00:00
Syed Ali Shahbaz 7cd8dbf7d8 chore: Watchlist schema update (#24246)
* watchlist schema update

* changes addressed

* no backfill audit

* fix type

* fix flow of migration

* fix err

* type err fix

* add if exists check in migration
2025-10-04 01:44:37 +01:00
654a865007 refactor: Salesforce getContacts (#24235)
* lint fixes

* Create getContactOrLeadFromEmail method

* Use getContactOrLeadFromEmail in getContacts

* Move normal getContact query

* fix: correct return type in getContactOrLeadFromEmail and add search mock

- Fix getContactOrLeadFromEmail to return single ContactRecord instead of array
- Add conn.search() implementation to salesforceMock for integration tests
- Ensure id field is always string with proper fallbacks for AccountId and Id
- Fix Owner.Email to use undefined instead of null in mock

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* test: add comprehensive tests for getContactOrLeadFromEmail method

- Add 6 new unit tests covering all key scenarios
- Test contact lookup, lead fallback, and preference logic
- Test both roundRobinSkipFallbackToLeadOwner and createEventOnLeadCheckForContact code paths
- Add search mock support to mockConnection in beforeEach
- All 34 tests passing, type-check clean

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

* Implement change to error from #24090

* Address feedback

* Type fix

* test: update SOQL query expectations to include Account.OwnerId

Co-Authored-By: joe@cal.com <j.auyeung419@gmail.com>

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-10-03 14:53:00 -04:00
Alex van AndelandDevin AI 409a0277fb fix: Resolve BillingPlan tree-shaking issue causing runtime error (#24229)
* fix: Convert BillingPlan enum to const object to prevent webpack tree-shaking

TypeScript enums compile to IIFEs which can be incorrectly tree-shaken by webpack
when 'sideEffects: false' is set in package.json. Converting to a const object
with 'as const' avoids the IIFE pattern while maintaining full type safety.

This fixes the 'TRPCError: BillingPlan is not defined' error that occurred in
the hasTeamPlan tRPC handler.

Follows the existing CHECKOUT_SESSION_TYPES pattern in the same file.

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

* fix: Re-export BillingPlan from billing-plans to prevent tree-shaking

The issue was a module initialization order problem. When BillingPlan was
imported from constants.ts into billing-plans.ts and used inside the
BillingPlanService class, webpack's tree-shaker (with sideEffects: false)
couldn't properly track the value dependency across the package boundary.

By re-exporting BillingPlan from the same module that exports
BillingPlanService, we ensure the constant is bundled together with the
class that uses it, preventing tree-shaking.

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

* revert: Restore BillingPlan enum format in constants.ts

The const object conversion didn't fix the tree-shaking issue. The real
fix is re-exporting BillingPlan from billing-plans.ts to ensure it's
part of the same module as BillingPlanService.

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

* fix: Add BillingPlan as private static member to prevent tree-shaking

By making BillingPlan a private static member of BillingPlanService, webpack
now sees it as part of the class definition rather than just used inside
methods. This creates a strong reference that prevents webpack's tree-shaker
from removing the enum initialization IIFE when 'sideEffects: false' is set.

With the previous approach where BillingPlan was only imported and used inside
methods, webpack's static analysis couldn't properly track the enum usage
across package boundaries (@calcom/features -> @calcom/trpc), causing it to
incorrectly determine the enum initialization was unused code.

Fixes the TRPCError: BillingPlan is not defined runtime error.

Thread: https://calendso.slack.com/archives/C08LT9BLEET/p1759420015428149
Co-Authored-By: alex@cal.com <me@alexvanandel.com>

* fix: Clean up BillingPlan references and add explanatory comment

- Remove unnecessary re-export statement
- Use direct BillingPlan references instead of BillingPlanService.BillingPlan
- Add detailed comment explaining webpack tree-shaking workaround
- Keep import from constants.ts and private static member for webpack reference

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

* fix: Remove Slack reference from comment and restore static usage

- Remove Slack URL from explanatory comment
- Restore BillingPlanService.BillingPlan usage throughout class methods
- This static member usage is essential for webpack to track the enum reference

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

* Fix BillingPlan issue by finding root cause

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-10-03 16:37:45 +01:00
Anik Dhabal Babu 7adab1f65f fix: event not showing up on outlook through ics file (#24249) 2025-10-03 13:52:25 +00:00
Calcom BotandLingo.dev 3b92c86582 feat: update translations via @LingoDotDev (#24248)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-10-03 12:42:14 +00:00
Devanshu Sharma de195a1c0f feat: add email filtering to team memberships endpoint (#23923)
* Controller Layer updates.

* adding email filtering and pagination to team memberships endpoint..

* Minor enhancements.

* Improve addressed.

* refactor: update team memberships input to handle comma-separated emails

- Replace array format with comma-separated string handling
- Add proper email validation with BadRequestException
- Remove ArrayMaxSize constraint for better flexibility
- Update API documentation and examples
- Align with codebase patterns from get-managed-users.input.ts

* Morgan suggestions addressed.

* More improvements......
2025-10-03 18:09:47 +05:30
an_ifrah24andDevanshu Sharma d7d2487f6d fix: hydration error warning in scheduleListItem component (#24240)
Co-authored-by: Devanshu Sharma <devanshusharma658@gmail.com>
2025-10-03 17:52:32 +05:30
sean-brydon 3e31945157 Fix nits (#24244) 2025-10-03 11:03:07 +00:00
Lauris Skraucis c11814c342 fix: managed user timeZone validation (#24226)
* fix: managed user timeZone validation

* fix: managed user timeZone validation

* fix: managed user timeZone validation
2025-10-03 08:23:54 +00:00
Syed Ali Shahbaz 60e3244088 Migrate watchlist severity to action(#24195) 2025-10-03 11:18:14 +04:00
Rajiv Sahal a1ac5f36f0 feat: add atoms section inside developing guides (#24220) 2025-10-02 21:59:43 +05:30
Lauris SkraucisandMorgan 6f38d40613 refactor: dont allow scheduling team event type without hosts (#24206)
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-10-02 15:58:42 +00:00
Lauris Skraucis 6b7d22de3f refactor: fail breaking change check on error only (#24221) 2025-10-02 14:42:49 +00:00
Anik Dhabal Babu 2f763dceda fix: Getting an unauthorized error when accessing the workflow tab for a child-managed event (#24219)
* Update get.handler.ts

* fix: Ignore userIds form filter segment if no permission

* add tests

* Update booking-filters.e2e.ts

* Update booking-filters.e2e.ts

* fix test

* fix: got unautorized error when viewing workflows

* fix
2025-10-02 13:38:21 +00:00
Benny Joo 96468c4083 refactor: move @calcom/lib/di folder to @calcom/features (#24199)
* mv di folder

* update imports

* fix

* fix

* fix test
2025-10-02 08:12:06 -03:00
sean-brydonandBenny Joo 858d52563f feat: pbac - org pages check update permissions (#24164)
* pbac - org pages check update permissions

* restore permision service

* fix type check

* more type fixes

---------

Co-authored-by: Benny Joo <sldisek783@gmail.com>
2025-10-02 20:04:21 +09:00
7505b3b271 feat: add missing sensitive fields to redactSensitiveData (#24121)
- Add private_key, encrypted_credentials, tenant_id, client_email, serviceAccountKey to SENSITIVE_FIELDS array
- Add comprehensive test cases for delegation credential sensitive fields
- Prevents logging of sensitive delegation credential data including private keys

Fixes security issue where delegation credential sensitive fields were not being redacted from logs

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: morgan@cal.com <morgan@cal.com>
Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-10-01 20:26:58 -03:00
Calcom BotandLingo.dev 758dc88b55 feat: update translations via @LingoDotDev (#24203)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-10-01 23:24:17 +00:00
Udit Takkar 9be418dc65 chore: update description (#24189) 2025-10-01 23:35:41 +05:30
Volnei Munhoz 2ac310456e fix: Selected calendar delegation credentials (#24190)
* Fix selected calendar delegation credentials
2025-10-01 14:20:36 +00:00
ff264d6f7a fix: allow team with same slug for diff cases (#24029)
* fix: aalow team with slug for diff cases

* addressed review

* fix type error

* update test

* addressed review

* fix test

* Update team.ts

---------

Co-authored-by: Udit Takkar <53316345+Udit-takkar@users.noreply.github.com>
Co-authored-by: Alex van Andel <me@alexvanandel.com>
2025-10-01 14:04:20 +00:00
8019c414e1 chore: CSRF protect forgot-password functionality (#22361)
* chore: CSRF protect forgot-password functionality

* feat: implement conditional sameSite cookie setting for CSRF tokens

- Use WEBAPP_URL to determine secure cookie settings
- Set sameSite to 'none' for HTTPS environments to support cross-origin scenarios
- Fall back to 'lax' for HTTP development environments
- Follows patterns from PRs #23439 and #23556

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

* update

* change

* NIT

* minor

---------

Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Co-authored-by: unknown <adhabal2002@gmail.com>
Co-authored-by: Anik Dhabal Babu <81948346+anikdhabal@users.noreply.github.com>
2025-10-01 13:38:29 +00:00
chauhan_s a3fbe3731e feat: add host validation for team event types (#24170)
* feat: add host validation for team event types

* refactor: remove hosts validator in favor of documentation-based validation approach

* fix: remove HostsOrAssignAllValidator from event type update inputs

* docs: improve clarity of hosts and assignAllTeamMembers field descriptions in event type inputs
2025-10-01 16:15:40 +05:30
sean-brydonandcoderabbitai[bot] e3742dbc66 fix: Always enforce one owner with pbac (#24144)
* Always enforce one owner

* updates from merge

* remove redudant eslint rules

* Update packages/features/pbac/services/pbac-role-manager.service.ts

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-10-01 11:06:18 +01:00
Rajiv SahalandMorgan 83bf717d6d fix: customReplyEmailTo feedback (#23738)
* fix: move validateRoundRobinSlotAvailability to core libraries

* fix: implement PR feedback

* fix: merge conflicts

---------

Co-authored-by: Morgan <33722304+ThyMinimalDev@users.noreply.github.com>
2025-10-01 15:32:32 +05:30
Peer RichelsenandDevin AI 7fcdaa1c46 feat: add created_at field display to admin user edit page (#23805)
Co-authored-by: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
2025-10-01 09:58:20 +00:00
Kartik LabhshetwarandKartik Saini a8154f9405 feat(ui/api-keys): add confirmation dialog for API key deletion (#24118)
* feat(api-keys): add confirmation dialog for API key deletion

* fix: changed text color to subtle

---------

Co-authored-by: Kartik Saini <41051387+kart1ka@users.noreply.github.com>
2025-10-01 09:56:55 +00:00
chauhan_sandLauris Skraucis 52a5afeba5 fix: different calendar hosts (#24000)
* fis: pass the missing `platformClientId` to `handleNewBooking` in api/v2

* Update formatCalendarEvent.ts

* only add the externald in case of same calendar, otherwise add if from the .ics file sent in email

* Revert "fis: pass the missing `platformClientId` to `handleNewBooking` in api/v2"

This reverts commit 030ac0f0981c1135d4973fbdafa62d58e8985831.

* fix: alow the host change logic to run for collective envets as well

* fix: run the changedOrganizer logic for both collective and round-robin events

* Update handleNewBooking.ts

* fix: failing unit test

---------

Co-authored-by: Lauris Skraucis <lauris.skraucis@gmail.com>
2025-10-01 12:15:34 +03:00
Anik Dhabal Babu 43cc40d7fe fix: add permissions to workflows (#24192) 2025-10-01 06:53:08 +00:00
Hariom Balhara 42a98f000e fix: 405 method not allowed error for queuedFormResponseCleanup (#24191) 2025-10-01 04:55:48 +00:00
Joe Au-Yeung f003ef935b Remove safeStringify from graphql errors (#24188) 2025-10-01 09:50:15 +05:30
Volnei Munhoz e2b2a184c8 fix: Calendar Cache sync page (#24182)
* fix

* fix calendar sync page
2025-09-30 17:10:51 +01:00
Calcom BotandLingo.dev 094595c435 feat: update translations via @LingoDotDev (#24178)
Co-authored-by: Lingo.dev <support@lingo.dev>
2025-09-30 13:51:13 +00:00
Benny Joo 4118a86c0d refactor: do a permission check in removeHostsFromEventTypes trpc handler (#24176) 2025-09-30 13:30:12 +00:00
sean-brydon 928d6f3821 chore: PBAC routingform entity permissions to favour pbac (#24130)
* Remove routingform entity permissions to favour pbac

* push typo

* fix types

* update returns

* Remove unused function

* nits

* fix hariom feedback

* fix correct write permissions
2025-09-30 18:55:43 +05:30
241 changed files with 3610 additions and 1116 deletions
+3
View File
@@ -267,6 +267,9 @@ NEXT_PUBLIC_CLOUDFLARE_SITEKEY=
NEXT_PUBLIC_CLOUDFLARE_USE_TURNSTILE_IN_BOOKER=
CLOUDFLARE_TURNSTILE_SECRET=
# 0 = false, 1=true
NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER=
# Set the following value to true if you wish to enable Team Impersonation
NEXT_PUBLIC_TEAM_IMPERSONATION=false
+1
View File
@@ -23,6 +23,7 @@
/packages/app-store/office365calendar/**/* @calcom/Foundation
/packages/app-store/zohocalendar/**/* @calcom/Foundation
/packages/embeds/**/* @calcom/Foundation
/packages/features/auth/lib/next-auth-options.ts @calcom/Foundation
/packages/features/bookings/lib/**/* @calcom/Foundation
/packages/lib/getAggregatedAvailability.ts @calcom/Foundation
/packages/lib/getUserAvailability.ts @calcom/Foundation
+1 -1
View File
@@ -82,7 +82,7 @@ jobs:
with:
base: https://raw.githubusercontent.com/calcom/cal.com/refs/heads/main/docs/api-reference/v2/openapi.json
revision: docs/api-reference/v2/openapi.json
fail-on: WARN
fail-on: ERR
- name: Run Tests
working-directory: apps/api/v2
View File
View File
+1 -1
View File
@@ -1,7 +1,7 @@
import type { NextApiRequest } from "next";
import { z } from "zod";
import { getUserAvailabilityService } from "@calcom/lib/di/containers/GetUserAvailability";
import { getUserAvailabilityService } from "@calcom/features/di/containers/GetUserAvailability";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import prisma from "@calcom/prisma";
+1 -1
View File
@@ -4,7 +4,7 @@ import type { NextApiRequest, NextApiResponse } from "next";
import dayjs from "@calcom/dayjs";
import { isSupportedTimeZone } from "@calcom/lib/dayjs";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import { createContext } from "@calcom/trpc/server/createContext";
+20 -19
View File
@@ -4,6 +4,7 @@ import { purchaseTeamOrOrgSubscription } from "@calcom/features/ee/teams/lib/pay
import { IS_TEAM_BILLING_ENABLED } from "@calcom/lib/constants";
import { HttpError } from "@calcom/lib/http-error";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import { TeamRepository } from "@calcom/lib/server/repository/team";
import prisma from "@calcom/prisma";
import type { Prisma } from "@calcom/prisma/client";
@@ -61,24 +62,24 @@ export async function patchHandler(req: NextApiRequest) {
const { teamId } = schemaQueryTeamId.parse(req.query);
/** Only OWNERS and ADMINS can edit teams */
const _team = await prisma.team.findFirst({
const team = await prisma.team.findFirst({
// eslint-disable-next-line @calcom/eslint/no-prisma-include-true
include: { members: true },
where: { id: teamId, members: { some: { userId, role: { in: ["OWNER", "ADMIN"] } } } },
});
if (!_team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" });
if (!team) throw new HttpError({ statusCode: 401, message: "Unauthorized: OWNER or ADMIN required" });
const slugAlreadyExists = await prisma.team.findFirst({
where: {
slug: {
mode: "insensitive",
equals: data.slug,
},
},
});
if (slugAlreadyExists && data.slug !== _team.slug)
throw new HttpError({ statusCode: 409, message: "Team slug already exists" });
if (data.slug) {
const teamRepository = new TeamRepository(prisma);
const isSlugAvailable = await teamRepository.isSlugAvailableForUpdate({
slug: data.slug,
teamId: team.id,
parentId: team.parentId,
});
if (!isSlugAvailable) {
throw new HttpError({ statusCode: 409, message: "Team slug already exists" });
}
}
// Check if parentId is related to this user
if (data.parentId && data.parentId === teamId) {
@@ -99,16 +100,16 @@ export async function patchHandler(req: NextApiRequest) {
}
let paymentUrl;
if (_team.slug === null && data.slug) {
if (team.slug === null && data.slug) {
data.metadata = {
...(_team.metadata as Prisma.JsonObject),
...(team.metadata as Prisma.JsonObject),
requestedSlug: data.slug,
};
delete data.slug;
if (IS_TEAM_BILLING_ENABLED) {
const checkoutSession = await purchaseTeamOrOrgSubscription({
teamId: _team.id,
seatsUsed: _team.members.length,
teamId: team.id,
seatsUsed: team.members.length,
userId,
pricePerSeat: null,
});
@@ -131,9 +132,9 @@ export async function patchHandler(req: NextApiRequest) {
bookingLimits: data.bookingLimits === null ? {} : data.bookingLimits,
metadata: data.metadata === null ? {} : data.metadata || undefined,
};
const team = await prisma.team.update({ where: { id: teamId }, data: cloneData });
const updatedTeam = await prisma.team.update({ where: { id: teamId }, data: cloneData });
const result = {
team: schemaTeamReadPublic.parse(team),
team: schemaTeamReadPublic.parse(updatedTeam),
paymentUrl,
};
if (!paymentUrl) {
+1 -1
View File
@@ -76,7 +76,7 @@ vi.mock("@calcom/features/watchlist/operations/check-if-users-are-blocked.contro
checkIfUsersAreBlocked: vi.fn().mockResolvedValue(false),
}));
vi.mock("@calcom/lib/di/containers/QualifiedHosts", () => ({
vi.mock("@calcom/features/di/containers/QualifiedHosts", () => ({
getQualifiedHostsService: vi.fn().mockReturnValue({
findQualifiedHostsWithDelegationCredentials: vi.fn().mockResolvedValue({
qualifiedRRHosts: [],
@@ -1,9 +1,8 @@
import prismock from "../../../../../../tests/libs/__mocks__/prisma";
import type { NextApiRequest } from "next";
import { describe, expect, it, beforeEach } from "vitest";
import { WatchlistSeverity } from "@calcom/prisma/enums";
import { isLockedOrBlocked } from "../../../lib/utils/isLockedOrBlocked";
describe("isLockedOrBlocked", () => {
@@ -18,7 +17,6 @@ describe("isLockedOrBlocked", () => {
{
type: "DOMAIN",
value: "blocked.com",
severity: WatchlistSeverity.CRITICAL,
createdById: 1,
},
],
@@ -26,13 +24,13 @@ describe("isLockedOrBlocked", () => {
});
it("should return false if no user in request", async () => {
const req = { userId: null, user: null } as any;
const req = { userId: null, user: null } as unknown as NextApiRequest;
const result = await isLockedOrBlocked(req);
expect(result).toBe(false);
});
it("should return false if user has no email", async () => {
const req = { userId: 123, user: { email: null } } as any;
const req = { userId: 123, user: { email: null } } as unknown as NextApiRequest;
const result = await isLockedOrBlocked(req);
expect(result).toBe(false);
});
@@ -44,7 +42,7 @@ describe("isLockedOrBlocked", () => {
locked: true,
email: "test@example.com",
},
} as any;
} as unknown as NextApiRequest;
const result = await isLockedOrBlocked(req);
expect(result).toBe(true);
@@ -57,7 +55,7 @@ describe("isLockedOrBlocked", () => {
locked: false,
email: "test@blocked.com",
},
} as any;
} as unknown as NextApiRequest;
const result = await isLockedOrBlocked(req);
expect(result).toBe(true);
@@ -70,7 +68,7 @@ describe("isLockedOrBlocked", () => {
locked: false,
email: "test@example.com",
},
} as any;
} as unknown as NextApiRequest;
const result = await isLockedOrBlocked(req);
expect(result).toBe(false);
@@ -83,7 +81,7 @@ describe("isLockedOrBlocked", () => {
locked: false,
email: "test@BLOCKED.COM",
},
} as any;
} as unknown as NextApiRequest;
const result = await isLockedOrBlocked(req);
expect(result).toBe(true);
@@ -65,6 +65,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
let team1EventTypeId: number;
let team2EventTypeId: number;
let phoneOnlyEventTypeId: number;
let collectiveEventWithoutHostsId: number;
let roundRobinEventWithoutHostsId: number;
const team1EventTypeSlug = `team-bookings-event-type-${randomString()}`;
const team2EventTypeSlug = `team-bookings-event-type-${randomString()}`;
@@ -223,6 +225,28 @@ describe("Bookings Endpoints 2024-08-13", () => {
team1EventTypeId = team1EventType.id;
const team1CollectiveEventTypeWithoutHosts = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "COLLECTIVE",
team: {
connect: { id: team1.id },
},
title: `team-bookings-2024-08-13-collective-event-type-without-hosts-${randomString()}`,
slug: `team-bookings-2024-08-13-collective-event-type-without-hosts-${randomString()}`,
length: 60,
});
collectiveEventWithoutHostsId = team1CollectiveEventTypeWithoutHosts.id;
const team1RoundRobinEventTypeWithoutHosts = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "ROUND_ROBIN",
team: {
connect: { id: team1.id },
},
title: `team-bookings-2024-08-13-round-robin-event-type-without-hosts-${randomString()}`,
slug: `team-bookings-2024-08-13-round-robin-event-type-without-hosts-${randomString()}`,
length: 60,
});
roundRobinEventWithoutHostsId = team1RoundRobinEventTypeWithoutHosts.id;
const phoneOnlyEventType = await eventTypesRepositoryFixture.createTeamEventType({
schedulingType: "ROUND_ROBIN",
team: {
@@ -363,6 +387,52 @@ describe("Bookings Endpoints 2024-08-13", () => {
await app.init();
});
describe("cant book event types without hosts", () => {
it("should fail to book a collective event type without hosts", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: collectiveEventWithoutHostsId,
attendee: {
name: "alice",
timeZone: "Europe/Madrid",
email: "alice@gmail.com",
},
};
const response = await request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13);
expect(response.status).toBe(422);
expect(response.body.error.message).toBe(
`Can't book this team event type because it has no hosts. Please, add at least 1 host to event type with id=${collectiveEventWithoutHostsId} belonging to team with id=${team1.id} and try again.`
);
});
it("should fail to book a round robin event type without hosts", async () => {
const body: CreateBookingInput_2024_08_13 = {
start: new Date(Date.UTC(2030, 0, 8, 13, 0, 0)).toISOString(),
eventTypeId: roundRobinEventWithoutHostsId,
attendee: {
name: "alice",
timeZone: "Europe/Madrid",
email: "alice@gmail.com",
},
};
const response = await request(app.getHttpServer())
.post("/v2/bookings")
.send(body)
.set(CAL_API_VERSION_HEADER, VERSION_2024_08_13);
expect(response.status).toBe(422);
expect(response.body.error.message).toBe(
`Can't book this team event type because it has no hosts. Please, add at least 1 host to event type with id=${roundRobinEventWithoutHostsId} belonging to team with id=${team1.id} and try again.`
);
});
});
describe("create team bookings", () => {
it("should create a team 1 booking", async () => {
const body: CreateBookingInput_2024_08_13 = {
@@ -765,8 +835,8 @@ describe("Bookings Endpoints 2024-08-13", () => {
return client;
}
function responseDataIsBooking(data: any): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data && "id" in data;
function responseDataIsBooking(data: unknown): data is BookingOutput_2024_08_13 {
return !Array.isArray(data) && typeof data === "object" && data !== null && data && "id" in data;
}
afterAll(async () => {
@@ -29,6 +29,7 @@ import {
Logger,
NotFoundException,
UnauthorizedException,
UnprocessableEntityException,
} from "@nestjs/common";
import { BadRequestException } from "@nestjs/common";
import { Request } from "express";
@@ -132,6 +133,10 @@ export class BookingsService_2024_08_13 {
);
}
if (eventType.schedulingType === "COLLECTIVE" || eventType.schedulingType === "ROUND_ROBIN") {
await this.checkEventTypeHasHosts(eventType.id);
}
body.eventTypeId = eventType.id;
if ("instant" in body && body.instant) {
@@ -159,6 +164,15 @@ export class BookingsService_2024_08_13 {
}
}
async checkEventTypeHasHosts(eventTypeId: number) {
const eventType = await this.eventTypesRepository.getEventTypeWithHosts(eventTypeId);
if (!eventType?.hosts?.length) {
throw new UnprocessableEntityException(
`Can't book this team event type because it has no hosts. Please, add at least 1 host to event type with id=${eventTypeId} belonging to team with id=${eventType?.teamId} and try again.`
);
}
}
async checkBookingRequiresAuthenticationSetting(
eventType: EventTypeWithOwnerAndTeam,
authUser: AuthOptionalUser,
@@ -0,0 +1,51 @@
import { PrismaReadService } from "@/modules/prisma/prisma-read.service";
import { PrismaWriteService } from "@/modules/prisma/prisma-write.service";
import { Injectable } from "@nestjs/common";
@Injectable()
export class AtomsSecondaryEmailsRepository {
constructor(private readonly dbRead: PrismaReadService, private readonly dbWrite: PrismaWriteService) {}
async getSecondaryEmailsVerified(userId: number) {
return await this.dbRead.prisma.secondaryEmail.findMany({
where: {
userId,
emailVerified: {
not: null,
},
},
});
}
async getExistingSecondaryEmailByUserAndEmail(userId: number, email: string) {
const existingSecondaryEmailRecord = await this.dbRead.prisma.secondaryEmail.findUnique({
where: {
userId_email: { userId, email },
},
});
return existingSecondaryEmailRecord?.email;
}
async getExistingSecondaryEmail(email: string) {
const existingSecondaryEmailRecord = await this.dbRead.prisma.secondaryEmail.findUnique({
where: {
email,
},
});
return existingSecondaryEmailRecord?.email;
}
async addSecondaryEmailVerified(userId: number, email: string) {
const existingSecondaryEmailRecord = await this.dbWrite.prisma.secondaryEmail.create({
data: {
userId,
email,
emailVerified: new Date(),
},
});
return existingSecondaryEmailRecord?.email;
}
}
@@ -1,4 +1,5 @@
import { EventTypesModule_2024_06_14 } from "@/ee/event-types/event-types_2024_06_14/event-types.module";
import { AtomsSecondaryEmailsRepository } from "@/modules/atoms/atoms-secondary-emails.repository";
import { AtomsRepository } from "@/modules/atoms/atoms.repository";
import { AtomsConferencingAppsController } from "@/modules/atoms/controllers/atoms.conferencing-apps.controller";
import { AtomsController } from "@/modules/atoms/controllers/atoms.controller";
@@ -33,6 +34,7 @@ import { Module } from "@nestjs/common";
CredentialsRepository,
UsersRepository,
AtomsRepository,
AtomsSecondaryEmailsRepository,
UsersService,
SchedulesAtomsService,
VerificationAtomsService,
@@ -64,47 +64,4 @@ export class AtomsRepository {
return userTeams;
}
async getSecondaryEmails(userId: number) {
return await this.dbRead.prisma.secondaryEmail.findMany({
where: {
userId,
emailVerified: {
not: null,
},
},
});
}
async getExistingSecondaryEmailByUserAndEmail(userId: number, email: string) {
const existingSecondaryEmailRecord = await this.dbRead.prisma.secondaryEmail.findUnique({
where: {
userId_email: { userId, email },
},
});
return existingSecondaryEmailRecord?.email;
}
async getExistingSecondaryEmail(email: string) {
const existingSecondaryEmailRecord = await this.dbRead.prisma.secondaryEmail.findUnique({
where: {
email,
},
});
return existingSecondaryEmailRecord?.email;
}
async addSecondaryEmail(userId: number, email: string) {
const existingSecondaryEmailRecord = await this.dbWrite.prisma.secondaryEmail.create({
data: {
userId,
email,
emailVerified: new Date(),
},
});
return existingSecondaryEmailRecord?.email;
}
}
@@ -1,10 +1,10 @@
import { AtomsRepository } from "@/modules/atoms/atoms.repository";
import { AtomsSecondaryEmailsRepository } from "@/modules/atoms/atoms-secondary-emails.repository";
import { CheckEmailVerificationRequiredParams } from "@/modules/atoms/inputs/check-email-verification-required-params";
import { GetVerifiedEmailsInput } from "@/modules/atoms/inputs/get-verified-emails-params";
import { SendVerificationEmailInput } from "@/modules/atoms/inputs/send-verification-email.input";
import { VerifyEmailCodeInput } from "@/modules/atoms/inputs/verify-email-code.input";
import { TeamsRepository } from "@/modules/teams/teams/teams.repository";
import { UserWithProfile } from "@/modules/users/users.repository";
import { UsersRepository } from "@/modules/users/users.repository";
import { Injectable, BadRequestException, UnauthorizedException } from "@nestjs/common";
import {
@@ -17,8 +17,8 @@ import {
@Injectable()
export class VerificationAtomsService {
constructor(
private readonly atomsRepository: AtomsRepository,
private readonly teamsRepository: TeamsRepository
private readonly atomsSecondaryEmailsRepository: AtomsSecondaryEmailsRepository,
private readonly usersRepository: UsersRepository
) {}
async checkEmailVerificationRequired(input: CheckEmailVerificationRequiredParams) {
@@ -76,7 +76,8 @@ export class VerificationAtomsService {
if (teamId) {
const verifiedEmails: string[] = [];
const teamMembers = await this.teamsRepository.getTeamMemberEmails(teamId);
const teamMembers = await this.usersRepository.getUserEmailsVerifiedForTeam(teamId);
if (teamMembers.length === 0) {
return verifiedEmails;
@@ -96,7 +97,7 @@ export class VerificationAtomsService {
let verifiedEmails = [userEmailWithoutOauthClientId];
const secondaryEmails = await this.atomsRepository.getSecondaryEmails(userId);
const secondaryEmails = await this.atomsSecondaryEmailsRepository.getSecondaryEmailsVerified(userId);
verifiedEmails = verifiedEmails.concat(
secondaryEmails.map((secondaryEmail) => this.removeClientIdFromEmail(secondaryEmail.email))
);
@@ -113,11 +114,9 @@ export class VerificationAtomsService {
existingPrimaryEmail: string;
email: string;
}): Promise<boolean> {
const existingSecondaryEmail = await this.atomsRepository.getExistingSecondaryEmailByUserAndEmail(
userId,
email
);
const alreadyExistingEmail = await this.atomsRepository.getExistingSecondaryEmail(email);
const existingSecondaryEmail =
await this.atomsSecondaryEmailsRepository.getExistingSecondaryEmailByUserAndEmail(userId, email);
const alreadyExistingEmail = await this.atomsSecondaryEmailsRepository.getExistingSecondaryEmail(email);
if (alreadyExistingEmail) {
throw new BadRequestException("Email already exists");
@@ -127,7 +126,7 @@ export class VerificationAtomsService {
return true;
}
await this.atomsRepository.addSecondaryEmail(userId, email);
await this.atomsSecondaryEmailsRepository.addSecondaryEmailVerified(userId, email);
return true;
}
@@ -705,6 +705,111 @@ describe("OAuth Client Users Endpoints", () => {
.expect(200);
});
describe("managed user time zone", () => {
describe("negative tests", () => {
it("should not allow '' time zone", async () => {
const requestBody = {
email: "whatever2@gmail.com",
timeZone: "",
name: "Bob Smithson",
};
await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set("x-cal-secret-key", oAuthClient.secret)
.send(requestBody)
.expect(400);
});
it("should not allow 'invalid-timezone' time zone", async () => {
const requestBody = {
email: "whatever2@gmail.com",
timeZone: "invalid-timezone",
name: "Bob Smithson",
};
await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set("x-cal-secret-key", oAuthClient.secret)
.send(requestBody)
.expect(400);
});
});
describe("positive tests", () => {
it("should allow null timezone", async () => {
const requestBody = {
email: "whatever1@gmail.com",
timeZone: null,
name: "Bob Smithson",
};
const response = await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set("x-cal-secret-key", oAuthClient.secret)
.send(requestBody)
.expect(201);
const responseBody: CreateManagedUserOutput = response.body;
expect(responseBody.data.user.timeZone).toEqual("Europe/London");
await userRepositoryFixture.delete(responseBody.data.user.id);
});
it("should allow undefined time zone", async () => {
const requestBody = {
email: "whatever3@gmail.com",
timeZone: undefined,
name: "Bob Smithson",
};
const response = await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set("x-cal-secret-key", oAuthClient.secret)
.send(requestBody)
.expect(201);
const responseBody: CreateManagedUserOutput = response.body;
expect(responseBody.data.user.timeZone).toEqual("Europe/London");
await userRepositoryFixture.delete(responseBody.data.user.id);
});
it("should allow valid time zone", async () => {
const requestBody = {
email: "whatever4@gmail.com",
timeZone: "Europe/Rome",
name: "Bob Smithson",
};
const response = await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set("x-cal-secret-key", oAuthClient.secret)
.send(requestBody)
.expect(201);
const responseBody: CreateManagedUserOutput = response.body;
expect(responseBody.data.user.timeZone).toBe("Europe/Rome");
await userRepositoryFixture.delete(responseBody.data.user.id);
});
it("should allow without any time zone", async () => {
const requestBody = {
email: "whatever5@gmail.com",
name: "Bob Smithson",
};
const response = await request(app.getHttpServer())
.post(`/api/v2/oauth-clients/${oAuthClient.id}/users`)
.set("x-cal-secret-key", oAuthClient.secret)
.send(requestBody)
.expect(201);
const responseBody: CreateManagedUserOutput = response.body;
expect(responseBody.data.user.timeZone).toEqual("Europe/London");
await userRepositoryFixture.delete(responseBody.data.user.id);
});
});
});
afterAll(async () => {
await oauthClientRepositoryFixture.delete(oAuthClient.id);
await oauthClientRepositoryFixture.delete(oAuthClientEventTypesDisabled.id);
@@ -712,17 +817,17 @@ describe("OAuth Client Users Endpoints", () => {
try {
await userRepositoryFixture.delete(postResponseData.user.id);
} catch (e) {
// User might have been deleted by the test
console.log(e);
}
try {
await userRepositoryFixture.delete(postResponseData2.user.id);
} catch (e) {
// User might have been deleted by the test
console.log(e);
}
try {
await userRepositoryFixture.delete(platformAdmin.id);
} catch (e) {
// User might have been deleted by the test
console.log(e);
}
await app.close();
});
@@ -931,7 +1036,7 @@ describe("OAuth Client Users Endpoints", () => {
try {
await userRepositoryFixture.delete(postResponseData.user.id);
} catch (e) {
// User might have been deleted by the test
console.log(e);
}
await app.close();
});
@@ -328,6 +328,131 @@ describe("Teams Memberships Endpoints", () => {
return request(app.getHttpServer()).get(`/v2/teams/${team.id}/memberships/123132145`).expect(404);
});
it("should filter memberships by single email", async () => {
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${teamAdminEmail}`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(1);
expect(responseBody.data[0].user.email).toEqual(teamAdminEmail);
expect(responseBody.data[0].userId).toEqual(teamAdmin.id);
expect(responseBody.data[0].role).toEqual("ADMIN");
});
});
it("should filter memberships by multiple emails", async () => {
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${teamAdminEmail},${teamMemberEmail}`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(2);
const emails = responseBody.data.map((membership) => membership.user.email);
expect(emails).toContain(teamAdminEmail);
expect(emails).toContain(teamMemberEmail);
const adminMembership = responseBody.data.find((m) => m.user.email === teamAdminEmail);
const memberMembership = responseBody.data.find((m) => m.user.email === teamMemberEmail);
expect(adminMembership).toBeDefined();
expect(memberMembership).toBeDefined();
expect(adminMembership?.role).toEqual("ADMIN");
expect(memberMembership?.role).toEqual("MEMBER");
});
});
it("should return empty array when filtering by non-existent email", async () => {
const nonExistentEmail = `nonexistent-${randomString()}@test.com`;
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${nonExistentEmail}`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(0);
});
});
it("should return partial results when filtering by mix of existing and non-existent emails", async () => {
const nonExistentEmail = `nonexistent-${randomString()}@test.com`;
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${teamAdminEmail},${nonExistentEmail}`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(1);
expect(responseBody.data[0].user.email).toEqual(teamAdminEmail);
});
});
it("should work with pagination and email filtering combined", async () => {
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${teamAdminEmail},${teamMemberEmail}&skip=1&take=1`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(1);
const returnedEmail = responseBody.data[0].user.email;
expect([teamAdminEmail, teamMemberEmail]).toContain(returnedEmail);
});
});
it("should handle empty emails array gracefully", async () => {
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(2);
});
});
it("should handle URL encoded email addresses in filter", async () => {
const encodedEmail = encodeURIComponent(teamAdminEmail);
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${encodedEmail}`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(1);
expect(responseBody.data[0].user.email).toEqual(teamAdminEmail);
});
});
it("should filter by email and maintain all user properties", async () => {
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${teamMemberEmail}`)
.expect(200)
.then((response) => {
const responseBody: GetTeamMembershipsOutput = response.body;
expect(responseBody.status).toEqual(SUCCESS_STATUS);
expect(responseBody.data.length).toEqual(1);
const membership = responseBody.data[0];
expect(membership.user.email).toEqual(teamMemberEmail);
expect(membership.user.bio).toEqual(teamMember.bio);
expect(membership.user.metadata).toEqual(teamMember.metadata);
expect(membership.user.username).toEqual(teamMember.username);
expect(membership.teamId).toEqual(team.id);
expect(membership.userId).toEqual(teamMember.id);
expect(membership.role).toEqual("MEMBER");
});
});
it("should validate email array size limits", async () => {
const tooManyEmails = Array.from({ length: 21 }, (_, i) => `test${i}@example.com`).join(",");
return request(app.getHttpServer())
.get(`/v2/teams/${team.id}/memberships?emails=${tooManyEmails}`)
.expect(400);
});
afterAll(async () => {
await userRepositoryFixture.deleteByEmail(teamAdmin.email);
await userRepositoryFixture.deleteByEmail(teammateInvitedViaApi.email);
@@ -4,6 +4,7 @@ import { Roles } from "@/modules/auth/decorators/roles/roles.decorator";
import { ApiAuthGuard } from "@/modules/auth/guards/api-auth/api-auth.guard";
import { RolesGuard } from "@/modules/auth/guards/roles/roles.guard";
import { CreateTeamMembershipInput } from "@/modules/teams/memberships/inputs/create-team-membership.input";
import { GetTeamMembershipsInput } from "@/modules/teams/memberships/inputs/get-team-memberships.input";
import { UpdateTeamMembershipInput } from "@/modules/teams/memberships/inputs/update-team-membership.input";
import { CreateTeamMembershipOutput } from "@/modules/teams/memberships/outputs/create-team-membership.output";
import { DeleteTeamMembershipOutput } from "@/modules/teams/memberships/outputs/delete-team-membership.output";
@@ -32,7 +33,6 @@ import { plainToClass } from "class-transformer";
import { SUCCESS_STATUS } from "@calcom/platform-constants";
import { updateNewTeamMemberEventTypes } from "@calcom/platform-libraries/event-types";
import { SkipTakePagination } from "@calcom/platform-types";
@Controller({
path: "/v2/teams/:teamId/memberships",
@@ -85,16 +85,20 @@ export class TeamsMembershipsController {
}
@Get("/")
@ApiOperation({ summary: "Get all memberships" })
@ApiOperation({
summary: "Get all memberships",
description: "Retrieve team memberships with optional filtering by email addresses. Supports pagination.",
})
@Roles("TEAM_ADMIN")
@HttpCode(HttpStatus.OK)
async getTeamMemberships(
@Param("teamId", ParseIntPipe) teamId: number,
@Query() queryParams: SkipTakePagination
@Query() queryParams: GetTeamMembershipsInput
): Promise<GetTeamMembershipsOutput> {
const { skip, take } = queryParams;
const { skip, take, emails } = queryParams;
const orgTeamMemberships = await this.teamsMembershipsService.getPaginatedTeamMemberships(
teamId,
emails,
skip ?? 0,
take ?? 250
);
@@ -0,0 +1,32 @@
import { GetUsersInput } from "@/modules/users/inputs/get-users.input";
import { ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { ArrayMaxSize, ArrayNotEmpty, IsEmail, IsOptional } from "class-validator";
export class GetTeamMembershipsInput extends GetUsersInput {
@IsOptional()
@Transform(({ value }) => {
if (value == null) return undefined;
const rawValues = (Array.isArray(value) ? value : [value]).flatMap((entry) =>
typeof entry === "string" ? entry.split(",") : []
);
const normalized = rawValues
.map((email) => email.trim())
.filter((email) => email.length > 0)
.map((email) => email.toLowerCase());
const deduplicated = [...new Set(normalized)];
return deduplicated.length > 0 ? deduplicated : undefined;
})
@ArrayNotEmpty({ message: "emails cannot be empty." })
@ArrayMaxSize(20, {
message: "emails array cannot contain more than 20 email addresses for team membership filtering",
})
@IsEmail({}, { each: true, message: "Each email must be a valid email address" })
@ApiPropertyOptional({
type: [String],
description:
"Filter team memberships by email addresses. If you want to filter by multiple emails, separate them with a comma (max 20 emails for performance).",
example: "?emails=user1@example.com,user2@example.com",
})
emails?: string[];
}
@@ -23,13 +23,15 @@ export class TeamsMembershipsService {
return teamMembership;
}
async getPaginatedTeamMemberships(teamId: number, skip = 0, take = 250) {
const teamMemberships = await this.teamsMembershipsRepository.findTeamMembershipsPaginated(
async getPaginatedTeamMemberships(teamId: number, emails?: string[], skip = 0, take = 250) {
const emailArray = !emails ? [] : emails;
return await this.teamsMembershipsRepository.findTeamMembershipsPaginatedWithFilters(
teamId,
{ emails: emailArray },
skip,
take
);
return teamMemberships;
}
async getTeamMembership(teamId: number, membershipId: number) {
@@ -6,6 +6,10 @@ import { Injectable } from "@nestjs/common";
import type { Prisma } from "@calcom/prisma/client";
export interface TeamMembershipFilters {
emails?: string[];
}
export const MembershipUserSelect: Prisma.UserSelect = {
username: true,
email: true,
@@ -41,6 +45,30 @@ export class TeamsMembershipsRepository {
});
}
async findTeamMembershipsPaginatedWithFilters(
teamId: number,
filters: TeamMembershipFilters,
skip: number,
take: number
) {
const whereClause: Prisma.MembershipWhereInput = {
teamId: teamId,
};
if (filters.emails && filters.emails.length > 0) {
whereClause.user = {
email: { in: filters.emails },
};
}
return await this.dbRead.prisma.membership.findMany({
where: whereClause,
include: { user: { select: MembershipUserSelect } },
skip,
take,
});
}
async findTeamMembership(teamId: number, membershipId: number) {
return this.dbRead.prisma.membership.findUnique({
where: {
@@ -120,30 +120,4 @@ export class TeamsRepository {
},
});
}
async getTeamMemberEmails(teamId: number) {
return this.dbRead.prisma.user.findMany({
where: {
teams: {
some: {
teamId,
},
},
},
select: {
id: true,
email: true,
secondaryEmails: {
where: {
emailVerified: {
not: null,
},
},
select: {
email: true,
},
},
},
});
}
}
@@ -1,6 +1,7 @@
import { Locales } from "@/lib/enums/locales";
import { CapitalizeTimeZone } from "@/lib/inputs/capitalize-timezone";
import { ApiProperty, ApiPropertyOptional } from "@nestjs/swagger";
import { Transform } from "class-transformer";
import { IsOptional, IsTimeZone, IsString, IsEnum, IsIn, IsUrl, IsObject } from "class-validator";
import { ValidateMetadata } from "@calcom/platform-types";
@@ -29,6 +30,7 @@ export class CreateManagedUserInput {
})
weekStart?: WeekDay;
@Transform(({ value }) => (value === null ? undefined : value))
@IsTimeZone()
@IsOptional()
@CapitalizeTimeZone()
@@ -28,6 +28,7 @@ export class GetUsersInput {
@ApiPropertyOptional({
type: [String],
description: "The email address or an array of email addresses to filter by",
example: ["user1@example.com", "user2@example.com"],
})
emails?: string[];
}
@@ -408,4 +408,30 @@ export class UsersRepository {
},
});
}
async getUserEmailsVerifiedForTeam(teamId: number) {
return this.dbRead.prisma.user.findMany({
where: {
teams: {
some: {
teamId,
},
},
},
select: {
id: true,
email: true,
secondaryEmails: {
where: {
emailVerified: {
not: null,
},
},
select: {
email: true,
},
},
},
});
}
}
@@ -1,8 +1,9 @@
import { _generateMetadata } from "app/_utils";
import OrgAttributesEditPage from "@calcom/ee/organizations/pages/settings/attributes/attributes-edit-view";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrgPerms } from "../../../../actions/validateUserHasOrgPerms";
export const generateMetadata = async ({ params }: { params: Promise<{ id: string }> }) =>
await _generateMetadata(
@@ -14,7 +15,10 @@ export const generateMetadata = async ({ params }: { params: Promise<{ id: strin
);
const OrgAttributesEditPageWrapper = async () => {
await validateUserHasOrgAdmin();
await validateUserHasOrgPerms({
permission: "organization.attributes.update",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
return <OrgAttributesEditPage />;
};
@@ -1,8 +1,9 @@
import { _generateMetadata } from "app/_utils";
import OrgAttributesCreatePage from "@calcom/ee/organizations/pages/settings/attributes/attributes-create-view";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrgPerms } from "../../../actions/validateUserHasOrgPerms";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -14,7 +15,10 @@ export const generateMetadata = async () =>
);
const OrgAttributesCreatePageWrapper = async () => {
await validateUserHasOrgAdmin();
await validateUserHasOrgPerms({
permission: "organization.attributes.create",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
return <OrgAttributesCreatePage />;
};
@@ -7,7 +7,7 @@ import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permi
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrg } from "../../actions/validateUserHasOrg";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -20,7 +20,7 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
const session = await validateUserHasOrgAdmin();
const session = await validateUserHasOrg();
const { canRead, canEdit, canDelete, canCreate } = await getResourcePermissions({
userId: session.user.id,
@@ -2,8 +2,9 @@ import { _generateMetadata, getTranslate } from "app/_utils";
import DelegationCredentialList from "@calcom/features/ee/organizations/pages/settings/delegationCredential";
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrgPerms } from "../../actions/validateUserHasOrgPerms";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -17,7 +18,10 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
await validateUserHasOrgAdmin();
await validateUserHasOrgPerms({
permission: "organization.update",
fallbackRoles: [MembershipRole.OWNER, MembershipRole.ADMIN],
});
return (
<SettingsHeader
@@ -6,7 +6,7 @@ import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permi
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrg } from "../../actions/validateUserHasOrg";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -19,7 +19,7 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
const session = await validateUserHasOrgAdmin();
const session = await validateUserHasOrg();
const { canEdit } = await getResourcePermissions({
userId: session.user.id,
@@ -7,7 +7,7 @@ import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permi
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrg } from "../../actions/validateUserHasOrg";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -21,7 +21,7 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
const session = await validateUserHasOrgAdmin();
const session = await validateUserHasOrg();
if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
return redirect("/settings/profile");
@@ -7,7 +7,7 @@ import { getResourcePermissions } from "@calcom/features/pbac/lib/resource-permi
import SettingsHeader from "@calcom/features/settings/appDir/SettingsHeader";
import { MembershipRole } from "@calcom/prisma/enums";
import { validateUserHasOrgAdmin } from "../../actions/validateUserHasOrgAdmin";
import { validateUserHasOrg } from "../../actions/validateUserHasOrg";
export const generateMetadata = async () =>
await _generateMetadata(
@@ -20,7 +20,7 @@ export const generateMetadata = async () =>
const Page = async () => {
const t = await getTranslate();
const session = await validateUserHasOrgAdmin();
const session = await validateUserHasOrg();
if (!session?.user.id || !session?.user.profile?.organizationId || !session?.user.org) {
return redirect("/settings/organizations/general");
@@ -65,7 +65,7 @@ export function RoleSheet({
onOpenChange,
teamId,
scope = Scope.Organization,
isPrivate = false
isPrivate = false,
}: RoleSheetProps) {
const { t } = useLocale();
const router = useRouter();
@@ -191,6 +191,7 @@ export function RoleSheet({
{...form.register("name")}
placeholder={t("role_name_placeholder")}
disabled={isSystemRole}
maxLength={50}
/>
</div>
<RoleColorPicker
@@ -208,10 +209,13 @@ export function RoleSheet({
<Label className="mb-0">{t("permissions")}</Label>
<div className="flex items-center gap-2">
<Checkbox
id="advanced_mode_checkbox"
checked={form.watch("isAdvancedMode")}
onCheckedChange={(checked: boolean) => form.setValue("isAdvancedMode", checked)}
/>
<span className="text-sm">{t("advanced")}</span>
<label htmlFor="advanced_mode_checkbox" className="text-sm">
{t("advanced")}
</label>
</div>
</div>
<TextField
+17 -1
View File
@@ -1,5 +1,6 @@
import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { parseRequestData } from "app/api/parseRequestData";
import { cookies } from "next/headers";
import type { NextRequest } from "next/server";
import { NextResponse } from "next/server";
import { z } from "zod";
@@ -10,6 +11,7 @@ import prisma from "@calcom/prisma";
import { IdentityProvider } from "@calcom/prisma/enums";
const passwordResetRequestSchema = z.object({
csrfToken: z.string(),
password: z.string().refine(validPassword, () => ({
message: "Password does not meet the requirements",
})),
@@ -18,8 +20,22 @@ const passwordResetRequestSchema = z.object({
async function handler(req: NextRequest) {
const body = await parseRequestData(req);
const {
password: rawPassword,
requestId: rawRequestId,
csrfToken: submittedToken,
} = passwordResetRequestSchema.parse(body);
const cookieStore = await cookies();
const cookieToken = cookieStore.get("calcom.csrf_token")?.value;
if (submittedToken !== cookieToken) {
return NextResponse.json({ error: "Invalid CSRF token" }, { status: 403 });
}
// token verified, delete the cookie / a resubmit on failure requires a new csrf token.
cookieStore.delete("calcom.csrf_token");
const { password: rawPassword, requestId: rawRequestId } = passwordResetRequestSchema.parse(body);
// rate-limited there is a low, very low chance that a password request stays valid long enough
// to brute force 3.8126967e+40 options.
const maybeRequest = await prisma.resetPasswordRequest.findFirstOrThrow({
@@ -2,4 +2,4 @@ import { defaultResponderForAppDir } from "app/api/defaultResponderForAppDir";
import { handleQueuedFormResponseCleanup } from "@calcom/app-store/routing-forms/cron/queuedFormResponseCleanup";
export const POST = defaultResponderForAppDir(handleQueuedFormResponseCleanup);
export const GET = defaultResponderForAppDir(handleQueuedFormResponseCleanup);
+13 -7
View File
@@ -3,19 +3,25 @@ import { NextResponse } from "next/server";
import { WEBAPP_URL } from "@calcom/lib/constants";
export async function GET() {
const token = randomBytes(32).toString("hex");
export async function GET(req: Request) {
const url = new URL(req.url);
const sameSiteParam = url.searchParams.get("sameSite");
const useSecureCookies = WEBAPP_URL.startsWith("https://");
// Validate the param, default to "lax"
let sameSite: "lax" | "strict" | "none" = "lax";
if (sameSiteParam === "strict" || (sameSiteParam === "none" && useSecureCookies)) {
sameSite = sameSiteParam;
}
const token = randomBytes(32).toString("hex");
const res = NextResponse.json({ csrfToken: token });
// We need this cookie to be accessible from embeds where the booking flow is displayed within an iframe on a different origin.
// For third‑party iframe contexts (embeds on other sites), browsers require SameSite=None and Secure to make the cookie available.
// For local development on http://localhost we fall back to SameSite=Lax to avoid requiring https during development.
const useSecureCookies = WEBAPP_URL.startsWith("https://");
res.cookies.set("calcom.csrf_token", token, {
httpOnly: true,
secure: useSecureCookies,
sameSite: useSecureCookies ? "none" : "lax",
sameSite,
path: "/",
});
@@ -266,7 +266,7 @@ export default function CancelBooking(props: Props) {
telemetry.event(telemetryEventTypes.bookingCancelled, collectPageParameters());
const response = await fetch("/api/csrf", { cache: "no-store" });
const response = await fetch("/api/csrf?sameSite=none", { cache: "no-store" });
const { csrfToken } = await response.json();
const res = await fetch("/api/cancel", {
+11
View File
@@ -2,6 +2,7 @@
// The added config here will be used whenever a users loads a page in their browser.
// https://docs.sentry.io/platforms/javascript/guides/nextjs/
import * as Sentry from "@sentry/nextjs";
import { initBotId } from "botid/client/core";
if (process.env.NODE_ENV === "production") {
Sentry.init({
@@ -48,3 +49,13 @@ export function onRouterTransitionStart(url: string, navigationType: "push" | "r
Sentry.captureRouterTransitionStart(url, navigationType);
}
}
process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER === "1" &&
initBotId({
protect: [
{
path: "/api/book/event",
method: "POST",
},
],
});
@@ -1,7 +1,8 @@
"use client";
import Link from "next/link";
import type { CSSProperties } from "react";
import { useEffect, useReducer, type CSSProperties } from "react";
import type { UseFormReturn } from "react-hook-form";
import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
@@ -16,121 +17,176 @@ import type { getServerSideProps } from "@server/lib/auth/forgot-password/[id]/g
export type PageProps = inferSSRProps<typeof getServerSideProps>;
export default function Page({ requestId, isRequestExpired, csrfToken }: PageProps) {
function Success() {
const { t } = useLocale();
const formMethods = useForm<{ new_password: string }>();
const success = formMethods.formState.isSubmitSuccessful;
const loading = formMethods.formState.isSubmitting;
const passwordValue = formMethods.watch("new_password");
const isEmpty = passwordValue?.length === 0;
return (
<>
<div className="space-y-6">
<div>
<h2 className="font-cal text-emphasis mt-6 text-center text-3xl font-extrabold">
{t("password_updated")}
</h2>
</div>
<Button href="/auth/login" className="w-full justify-center">
{t("login")}
</Button>
</div>
</>
);
}
const submitChangePassword = async ({ password, requestId }: { password: string; requestId: string }) => {
function Expired() {
const { t } = useLocale();
return (
<>
<div className="space-y-6">
<div>
<h2 className="font-cal text-emphasis mt-6 text-center text-3xl font-extrabold">{t("whoops")}</h2>
<h2 className="text-emphasis text-center text-3xl font-extrabold">{t("request_is_expired")}</h2>
</div>
<p>{t("request_is_expired_instructions")}</p>
<Link href="/auth/forgot-password" passHref legacyBehavior>
<button
type="button"
className="flex w-full justify-center px-4 py-2 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
{t("try_again")}
</button>
</Link>
</div>
</>
);
}
type FormValues = {
newPassword: string;
csrfToken: string;
};
function PasswordResetForm({
form: formMethods,
requestId,
}: {
form: UseFormReturn<FormValues>;
requestId: string;
}) {
const { t } = useLocale();
const [refreshToken, forceRefresh] = useReducer((x) => x + 1, 0);
const {
watch,
setValue,
setError,
formState: { isSubmitting: loading },
} = formMethods;
useEffect(() => {
fetch("/api/csrf", { cache: "no-store" })
.then((res) => res.json())
.then(({ csrfToken }) => setValue("csrfToken", csrfToken))
.catch(() => setValue("csrfToken", ""));
}, [setValue, refreshToken]);
const submitChangePassword = async ({
password,
requestId,
csrfToken,
}: {
password: string;
requestId: string;
csrfToken: string;
}) => {
const res = await fetch("/api/auth/reset-password", {
method: "POST",
body: JSON.stringify({ requestId, password }),
body: JSON.stringify({ requestId, password, csrfToken }),
headers: {
"Content-Type": "application/json",
},
});
const json = await res.json();
if (!res.ok) return formMethods.setError("new_password", { type: "server", message: json.message });
if (!res.ok) {
// if the request fails, we want to force refresh of the CSRF token - this allows resubmit
forceRefresh();
return setError("newPassword", { type: "server", message: json.message });
}
};
const Success = () => {
return (
<>
<div className="space-y-6">
<div>
<h2 className="font-cal text-emphasis mt-6 text-center text-3xl font-extrabold">
{t("password_updated")}
</h2>
</div>
<Button href="/auth/login" className="w-full justify-center">
{t("login")}
</Button>
</div>
</>
);
};
const passwordValue = watch("newPassword");
const isEmpty = passwordValue?.length === 0;
const Expired = () => {
return (
<Form
className="space-y-6"
form={formMethods}
style={
{
"--cal-brand": "#111827",
"--cal-brand-emphasis": "#101010",
"--cal-brand-text": "white",
"--cal-brand-subtle": "#9CA3AF",
} as CSSProperties
}
handleSubmit={async (values) => {
await submitChangePassword({
password: values.newPassword,
csrfToken: values.csrfToken,
requestId,
});
}}>
<input {...formMethods.register("csrfToken")} name="csrfToken" type="hidden" hidden />
<div className="mt-1">
<PasswordField
{...formMethods.register("newPassword", {
minLength: {
message: t("password_hint_min"),
value: 7, // We don't have user here so we can't check if they are admin or not
},
pattern: {
message: "Should contain a number, uppercase and lowercase letters",
value: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).*$/gm,
},
})}
label={t("new_password")}
/>
</div>
<div>
<Button
loading={loading}
color="primary"
type="submit"
disabled={loading || isEmpty}
className="w-full justify-center">
{t("reset_password")}
</Button>
</div>
</Form>
);
}
export default function Page({ requestId, isRequestExpired }: PageProps) {
const { t } = useLocale();
const formMethods = useForm<FormValues>({
defaultValues: {
newPassword: "",
csrfToken: "",
},
});
const {
formState: { isSubmitSuccessful: success },
} = formMethods;
if (isRequestExpired) {
return (
<>
<div className="space-y-6">
<div>
<h2 className="font-cal text-emphasis mt-6 text-center text-3xl font-extrabold">{t("whoops")}</h2>
<h2 className="text-emphasis text-center text-3xl font-extrabold">{t("request_is_expired")}</h2>
</div>
<p>{t("request_is_expired_instructions")}</p>
<Link href="/auth/forgot-password" passHref legacyBehavior>
<button
type="button"
className="flex w-full justify-center px-4 py-2 text-sm font-medium text-blue-600 focus:outline-none focus:ring-2 focus:ring-black focus:ring-offset-2">
{t("try_again")}
</button>
</Link>
</div>
</>
<AuthContainer showLogo heading={t("reset_password")}>
<Expired />
</AuthContainer>
);
};
}
return (
<AuthContainer showLogo heading={!success ? t("reset_password") : undefined}>
{isRequestExpired && <Expired />}
{!isRequestExpired && !success && (
<>
<Form
className="space-y-6"
form={formMethods}
style={
{
"--cal-brand": "#111827",
"--cal-brand-emphasis": "#101010",
"--cal-brand-text": "white",
"--cal-brand-subtle": "#9CA3AF",
} as CSSProperties
}
handleSubmit={async (values) => {
await submitChangePassword({
password: values.new_password,
requestId,
});
}}>
<input name="csrfToken" type="hidden" defaultValue={csrfToken} hidden />
<div className="mt-1">
<PasswordField
{...formMethods.register("new_password", {
minLength: {
message: t("password_hint_min"),
value: 7, // We don't have user here so we can't check if they are admin or not
},
pattern: {
message: "Should contain a number, uppercase and lowercase letters",
value: /^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?=.*[a-zA-Z]).*$/gm,
},
})}
label={t("new_password")}
/>
</div>
<div>
<Button
loading={loading}
color="primary"
type="submit"
disabled={loading || isEmpty}
className="w-full justify-center">
{t("reset_password")}
</Button>
</div>
</Form>
</>
)}
{!isRequestExpired && success && (
<>
<Success />
</>
)}
{success ? <Success /> : <PasswordResetForm form={formMethods} requestId={requestId} />}
</AuthContainer>
);
}
+6
View File
@@ -1,6 +1,7 @@
require("dotenv").config({ path: "../../.env" });
const englishTranslation = require("./public/static/locales/en/common.json");
const { withAxiom } = require("next-axiom");
const { withBotId } = require("botid/next/config");
const { version } = require("./package.json");
const { PrismaPlugin } = require("@prisma/nextjs-monorepo-workaround-plugin");
const {
@@ -117,6 +118,11 @@ if (process.env.ANALYZE === "true") {
}
plugins.push(withAxiom);
if (process.env.NEXT_PUBLIC_VERCEL_USE_BOTID_IN_BOOKER === "1") {
plugins.push(withBotId);
}
const orgDomainMatcherConfig = {
root: nextJsOrgRewriteConfig.disableRootPathRewrite
? null
+3 -2
View File
@@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "5.7.7",
"version": "5.7.9",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",
@@ -87,6 +87,7 @@
"async": "^3.2.4",
"bcp-47-match": "^2.0.3",
"bcryptjs": "^2.4.3",
"botid": "^1.5.7",
"classnames": "^2.3.1",
"dompurify": "^3.1.7",
"dotenv-cli": "^6.0.0",
@@ -108,7 +109,7 @@
"memory-cache": "^0.2.0",
"micro": "^10.0.1",
"mime-types": "^2.1.35",
"next": "^15.4.5",
"next": "15.5.4",
"next-auth": "^4.22.1",
"next-axiom": "^0.17.0",
"next-collect": "^0.2.1",
+14
View File
@@ -2,11 +2,15 @@ import type { NextApiRequest } from "next";
import { getServerSession } from "@calcom/features/auth/lib/getServerSession";
import handleNewBooking from "@calcom/features/bookings/lib/handleNewBooking";
import { BotDetectionService } from "@calcom/features/bot-detection";
import { FeaturesRepository } from "@calcom/features/flags/features.repository";
import { checkRateLimitAndThrowError } from "@calcom/lib/checkRateLimitAndThrowError";
import getIP from "@calcom/lib/getIP";
import { piiHasher } from "@calcom/lib/server/PiiHasher";
import { checkCfTurnstileToken } from "@calcom/lib/server/checkCfTurnstileToken";
import { defaultResponder } from "@calcom/lib/server/defaultResponder";
import { EventTypeRepository } from "@calcom/lib/server/repository/eventTypeRepository";
import prisma from "@calcom/prisma";
import { CreationSource } from "@calcom/prisma/enums";
async function handler(req: NextApiRequest & { userId?: number }) {
@@ -19,6 +23,16 @@ async function handler(req: NextApiRequest & { userId?: number }) {
});
}
// Check for bot detection using feature flag
const featuresRepository = new FeaturesRepository(prisma);
const eventTypeRepository = new EventTypeRepository(prisma);
const botDetectionService = new BotDetectionService(featuresRepository, eventTypeRepository);
await botDetectionService.checkBotDetection({
eventTypeId: req.body.eventTypeId,
headers: req.headers,
});
await checkRateLimitAndThrowError({
rateLimitingType: "core",
identifier: piiHasher.hash(userIp),
@@ -5,7 +5,7 @@ import { z } from "zod";
import dayjs from "@calcom/dayjs";
import { ZGetRetellLLMSchema } from "@calcom/features/calAIPhone/zod-utils";
import type { TGetRetellLLMSchema } from "@calcom/features/calAIPhone/zod-utils";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { fetcher } from "@calcom/lib/retellAIFetcher";
import { defaultHandler } from "@calcom/lib/server/defaultHandler";
import prisma from "@calcom/prisma";
@@ -70,7 +70,7 @@ test.describe("Forgot password", async () => {
// Wait for page to fully load
await page.waitForSelector("text=Reset Password");
await page.fill('input[name="new_password"]', newPassword);
await page.fill('input[name="newPassword"]', newPassword);
await page.click('button[type="submit"]');
await page.waitForSelector("text=Password updated");
+33 -2
View File
@@ -160,6 +160,7 @@ const createTeamAndAddUser = async (
orgRequestedSlug,
schedulingType,
assignAllTeamMembersForSubTeamEvents,
teamSlug,
}: {
user: { id: number; email: string; username: string | null; role?: MembershipRole };
isUnpublished?: boolean;
@@ -172,12 +173,15 @@ const createTeamAndAddUser = async (
orgRequestedSlug?: string;
schedulingType?: SchedulingType;
assignAllTeamMembersForSubTeamEvents?: boolean;
teamSlug?: string;
},
workerInfo: WorkerInfo
) => {
const slugIndex = index ? `-count-${index}` : "";
const slug =
orgRequestedSlug ?? `${isOrg ? "org" : "team"}-${workerInfo.workerIndex}-${Date.now()}${slugIndex}`;
teamSlug ??
orgRequestedSlug ??
`${isOrg ? "org" : "team"}-${workerInfo.workerIndex}-${Date.now()}${slugIndex}`;
const data: PrismaType.TeamCreateInput = {
name: `user-id-${user.id}'s ${isOrg ? "Org" : "Team"}`,
isOrganization: isOrg,
@@ -283,6 +287,7 @@ export const createUsersFixture = (
schedulingType?: SchedulingType;
teamEventTitle?: string;
teamEventSlug?: string;
teamSlug?: string;
teamEventLength?: number;
isOrg?: boolean;
isOrgVerified?: boolean;
@@ -367,6 +372,7 @@ export const createUsersFixture = (
orgRequestedSlug: scenario.orgRequestedSlug,
schedulingType: scenario.schedulingType,
assignAllTeamMembersForSubTeamEvents: scenario.assignAllTeamMembersForSubTeamEvents,
teamSlug: scenario?.teamSlug,
},
workerInfo
);
@@ -465,7 +471,7 @@ export const createUsersFixture = (
},
data: {
orgProfiles: _user.profiles.length
? {
? {
connect: _user.profiles.map((profile) => ({ id: profile.id })),
}
: {
@@ -711,6 +717,31 @@ const createUserFixture = (user: UserWithIncludes, page: Page) => {
}
return membership;
},
getAllTeamMembership: async () => {
const memberships = await prisma.membership.findMany({
where: {
userId: user.id,
team: {
isOrganization: false,
},
},
include: { team: true, user: true },
});
const filteredMemberships = memberships.map((membership) => ({
...membership,
team: {
...membership.team,
metadata: teamMetadataSchema.parse(membership.team.metadata),
},
}));
if (filteredMemberships.length === 0) {
throw new Error("No team memberships found for user");
}
return filteredMemberships;
},
getOrgMembership: async () => {
const membership = await prisma.membership.findFirstOrThrow({
where: {
+106
View File
@@ -10,6 +10,7 @@ import {
confirmReschedule,
fillStripeTestCheckout,
selectFirstAvailableTimeSlotNextMonth,
submitAndWaitForResponse,
testName,
} from "./lib/testUtils";
@@ -301,3 +302,108 @@ test.describe("Teams - NonOrg", () => {
});
todo("Reschedule a Round Robin EventType booking");
});
test.describe("Team Slug Validation", () => {
test.afterEach(({ users, orgs }) => {
users.deleteAll();
orgs.deleteAll();
});
test("Teams in different organizations can have the same slug", async ({ page, users, orgs }) => {
const org1 = await orgs.create({ name: "Organization 1" });
const org2 = await orgs.create({ name: "Organization 2" });
const owner1 = await users.create(
{
organizationId: org1.id,
roleInOrganization: "OWNER",
},
{
hasTeam: true,
teamSlug: "cal",
teamRole: "OWNER",
}
);
const owner2 = await users.create(
{
organizationId: org2.id,
roleInOrganization: "OWNER",
},
{
hasTeam: true,
teamSlug: "calCom",
teamRole: "OWNER",
}
);
const { team: team1 } = await owner1.getFirstTeamMembership();
await owner1.apiLogin();
await page.goto(`/settings/teams/${team1.id}/profile`);
await page.locator('input[name="slug"]').fill("calCom");
await submitAndWaitForResponse(page, "/api/trpc/teams/update?batch=1", {
action: () => page.locator("[data-testid=update-team-profile]").click(),
});
});
test("Teams within same organization cannot have duplicate slugs", async ({ page, users, orgs }) => {
const org = await orgs.create({ name: "Organization 1" });
const owner = await users.create(
{
organizationId: org.id,
roleInOrganization: "OWNER",
},
{
hasTeam: true,
numberOfTeams: 2,
teamRole: "OWNER",
}
);
const teams = await owner.getAllTeamMembership();
await owner.apiLogin();
await page.goto(`/settings/teams/${teams[0].team.id}/profile`);
if (!teams[1].team.slug) throw new Error("Slug not found for team 2");
await page.locator('input[name="slug"]').fill(teams[1].team.slug);
await submitAndWaitForResponse(page, "/api/trpc/teams/update?batch=1", {
action: () => page.locator("[data-testid=update-team-profile]").click(),
expectedStatusCode: 409,
});
});
test("Teams without organization can have same slug as teams in organizations", async ({
page,
users,
orgs,
}) => {
const org = await orgs.create({ name: "Organization 1" });
const orgOwner = await users.create(
{
organizationId: org.id,
roleInOrganization: "OWNER",
},
{
hasTeam: true,
teamSlug: "calCom",
teamRole: "OWNER",
}
);
const teamOwner = await users.create(
{ username: "pro-user", name: "pro-user" },
{
hasTeam: true,
}
);
const { team } = await teamOwner.getFirstTeamMembership();
await teamOwner.apiLogin();
await page.goto(`/settings/teams/${team.id}/profile`);
await page.locator('input[name="slug"]').fill("calCom");
await submitAndWaitForResponse(page, "/api/trpc/teams/update?batch=1", {
action: () => page.locator("[data-testid=update-team-profile]").click(),
});
});
});
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "لم يتم العثور على مستخدمين متاحين. هل يمكنك تجرِبة وقت آخر؟",
"timeslot_unavailable_book_a_new_time": "الفترة الزمنية المحددة لم تعد متاحة. <0>يرجى اختيار وقت جديد</0>",
"timeslot_unavailable_short": "محجوز",
"just_connected_description": "لقد اتصلت للتو. يرجى الحجز من الفترات المتاحة أعلاه أو إعادة المحاولة لاحقًا.",
"unavailable_timeslot_title": "فترة زمنية غير متاحة",
"booking_time_out_of_bounds_error": "لا يمكن حجز هذا النوع من الفعاليات في هذا الوقت. هل يمكنك تجربة وقت آخر؟",
"request_body_end_time_internal_error": "خطأ داخلي. محتوى الطلب لا يحتوي على وقت النهاية",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "هل تريد بالتأكيد حذف تكوين {{connectionType}}؟ لن يتمكن أعضاء فريقك الذين يستخدمون معلومات تسجيل الدخول إلى {{connectionType}} من الوصول إلى Cal.com بعد الآن.",
"organizer_timezone": "منظم المناطق الزمنية",
"email_user_cta": "عرض الدعوة",
"accept_invite": "قبول الدعوة",
"join_now": "انضم الآن",
"successfully_joined": "تم الانضمام بنجاح",
"email_no_user_invite_heading_subteam": "تمت دعوتك للانضمام إلى فريق من منظمة {{parentTeamName}}",
"email_no_user_invite_heading_org": "تمت دعوتك للانضمام إلى منظمة {{appName}}",
"email_no_user_invite_subheading": "دعاك {{invitedBy}} للانضمام إلى فريقه على {{appName}}. إن {{appName}} هو برنامج جدولة الأحداث الذي يمكّنك أنت وفريقك من جدولة الاجتماعات دون الحاجة إلى المراسلة عبر البريد الإلكتروني.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "مقفل من قبل المشرف",
"app_not_connected": "أنت غير متصل بحساب {{appName}}.",
"connect_now": "اتصل الآن",
"connect_now_unavailable_tooltip": "الاتصال الآن غير متاح حاليًا. يرجى الحجز من الفترات المتاحة أعلاه أو المحاولة مرة أخرى لاحقًا.",
"managed_event_dialog_confirm_button_one": "الاستبدال وإخطار {{count}} عضو",
"managed_event_dialog_confirm_button_other": "الاستبدال وإخطار {{count}} من الأعضاء",
"managed_event_dialog_title_one": "رابط /{{slug}} موجود بالفعل لـ {{count}} عضو. هل تريد استبداله؟",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "تم نسخ رابط الدعوة",
"invite_link_deleted": "تم حذف رابط الدعوة",
"api_key_deleted": "تم حذف مفتاح API",
"delete_api_key_warning": "سيؤدي هذا إلى حذف مفتاح API بشكل دائم. ستفقد أي تطبيقات تستخدم هذا المفتاح الوصول إلى حسابك على الفور. لا يمكن التراجع عن هذا الإجراء.",
"invite_link_updated": "تم حفظ إعدادات رابط الدعوة",
"link_expires_after": "تم تعيين انتهاء صلاحية الروابط بعد...",
"one_day": "1 يوم",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "إدارة الأدوار والصلاحيات لمؤسستك",
"learn_more_permissions": "تعرف على المزيد حول كيفية عمل الصلاحيات",
"role_name": "اسم الدور",
"role_name_required": "اسم الدور مطلوب",
"role_name_placeholder": "مثال: فريق المبيعات، موظفو الدعم",
"role_description": "وصف الدور",
"role_description_placeholder": "صف الغرض من هذا الدور والصلاحيات التي يحتاجها",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "تم إنشاء الدور بنجاح",
"role_updated_successfully": "تم تحديث الدور بنجاح",
"delete_role": "حذف الدور",
"confirm_delete_role": "هل أنت متأكد من رغبتك في حذف هذا الدور؟ لا يمكن التراجع عن هذا الإجراء.",
"confirm_delete_role": "هل أنت متأكد أنك تريد حذف الدور: {{roleName}}. لا يمكن التراجع عن هذا الإجراء.",
"role_deleted_successfully": "تم حذف الدور بنجاح",
"error_creating_role": "خطأ في إنشاء الدور",
"error_updating_role": "خطأ في تحديث الدور",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "البريد الإلكتروني للمنظم",
"webhook_organizer_timezone": "المنطقة الزمنية للمنظم (مثل 'America/New_York'، 'Asia/Kolkata')",
"webhook_organizer_locale": "اللغة المحلية للمنظم (مثل 'en'، 'fr')",
"webhook_organizer_username": "اسم المستخدم للمنظم (مثال: 'john.doe')",
"webhook_organizer_username_in_org": "اسم المستخدم للمنظم في مؤسسته (مثال: 'john.doe')",
"webhook_attendee_name": "اسم الحاضر الأول",
"webhook_attendee_email": "البريد الإلكتروني للحاضر الأول",
"webhook_attendee_timezone": "المنطقة الزمنية للحاضر الأول",
@@ -3750,4 +3757,4 @@
"use_voice": "استخدم الصوت",
"current_voice": "الصوت الحالي",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Mövcud istifadəçilər tapılmadı. Başqa bir vaxt aralığını yoxlaya bilərsinizmi?",
"timeslot_unavailable_book_a_new_time": "Seçilmiş vaxt artıq mövcud deyil. <0>Zəhmət olmasa yeni vaxt seçin</0>",
"timeslot_unavailable_short": "Tutulub",
"just_connected_description": "Siz indicə qoşuldunuz. Zəhmət olmasa, yuxarıdakı slotlardan birini seçin və ya sonra yenidən cəhd edin.",
"unavailable_timeslot_title": "Mövcud olmayan vaxt intervalı",
"booking_time_out_of_bounds_error": "Tədbir növünü bu vaxtda rezerv etmək mümkün deyil. Başqa vaxt aralığını sınamaq istərdinizmi?",
"request_body_end_time_internal_error": "Daxili səhv. Sorğu bədənində bitmə vaxtı yoxdur",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "{{connectionType}} konfiqurasiyasını silmək istədiyinizə əminsinizmi? {{connectionType}} girişi istifadə edən komanda üzvləriniz artıq Cal.com-a daxil ola bilməyəcəklər.",
"organizer_timezone": "Təşkilatçının saat qurşağı",
"email_user_cta": "Dəvəti Görüntülə",
"accept_invite": "Dəvəti qəbul edin",
"join_now": "İndi qoşulun",
"successfully_joined": "Uğurla qoşuldunuz",
"email_no_user_invite_heading_subteam": "{{parentTeamName}} təşkilatının komandasına qoşulmağa dəvət olunmusunuz",
"email_no_user_invite_heading_org": "{{appName}} təşkilatına qoşulmağa dəvət olunmusunuz",
"email_no_user_invite_subheading": "{{invitedBy}} sizi {{appName}} üzərindəki komandasına qoşulmağa dəvət edib. {{appName}} sizə və komandanıza görüşləri e-poçt mübadiləsi olmadan planlaşdırmağa imkan verən tədbir idarəetmə planlayıcısıdır.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Komanda admini tərəfindən kilidlənib",
"app_not_connected": "Siz {{appName}} hesabını bağlamamısınız.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Dəvət linki kopyalandı",
"invite_link_deleted": "Dəvət linki silindi",
"api_key_deleted": "API açarı silindi",
"delete_api_key_warning": "Bu, API açarını həmişəlik siləcək. Bu açardan istifadə edən bütün tətbiqlər dərhal hesabınıza girişi itirəcək. Bu əməliyyat geri qaytarıla bilməz.",
"invite_link_updated": "Dəvət linki parametrləri yadda saxlanıldı",
"link_expires_after": "Linklər bu müddətdən sonra bitir...",
"one_day": "1 gün",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Təşkilatınız üçün rolları və icazələri idarə edin",
"learn_more_permissions": "İcazələrin necə işlədiyini daha ətraflı öyrənin",
"role_name": "Rol adı",
"role_name_required": "Rol adı tələb olunur",
"role_name_placeholder": "məs. Satış Komandası, Dəstək İşçiləri",
"role_description": "Rol təsviri",
"role_description_placeholder": "Bu rolun nə üçün olduğunu və hansı icazələrə ehtiyacı olduğunu təsvir edin",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol uğurla yaradıldı",
"role_updated_successfully": "Rol uğurla yeniləndi",
"delete_role": "Rolu sil",
"confirm_delete_role": "Bu rolu silmək istədiyinizə əminsiniz? Bu əməliyyat geri qaytarıla bilməz.",
"confirm_delete_role": "{{roleName}} rolunu silmək istədiyinizə əminsiniz? Bu əməliyyat geri qaytarıla bilməz.",
"role_deleted_successfully": "Rol uğurla silindi",
"error_creating_role": "Rol yaradılarkən xəta baş verdi",
"error_updating_role": "Rol yenilənərkən xəta baş verdi",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Təşkilatçının e-poçtu",
"webhook_organizer_timezone": "Təşkilatçının saat qurşağı (məs., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Təşkilatçının lokalı (məs., 'en', 'fr')",
"webhook_organizer_username": "Təşkilatçının istifadəçi adı (məsələn, 'john.doe')",
"webhook_organizer_username_in_org": "Təşkilatçının öz təşkilatındakı istifadəçi adı (məsələn, 'john.doe')",
"webhook_attendee_name": "Birinci iştirakçının adı",
"webhook_attendee_email": "Birinci iştirakçının e-poçtu",
"webhook_attendee_timezone": "Birinci iştirakçının saat qurşağı",
@@ -3750,4 +3757,4 @@
"use_voice": "Səsdən istifadə edin",
"current_voice": "Cari səs",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni sətirləri bura əlavə edin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Не са намерени свободни потребители. Бихте ли опитали друг времеви интервал?",
"timeslot_unavailable_book_a_new_time": "Избраният времеви интервал вече не е наличен. <0>Моля, изберете ново време</0>",
"timeslot_unavailable_short": "Заето",
"just_connected_description": "Току-що се свързахте. Моля, резервирайте от наличните часове по-горе или опитайте отново по-късно.",
"unavailable_timeslot_title": "Недостъпен времеви интервал",
"booking_time_out_of_bounds_error": "Този тип събитие не може да бъде резервирано в този час. Бихте ли опитали друг часови интервал?",
"request_body_end_time_internal_error": "Вътрешна грешка. Заявката не съдържа краен час",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Сигурни ли сте, че искате да изтриете {{connectionType}} конфигурацията? Членовете на вашия екип, които използват {{connectionType}} вход, вече няма да имат достъп до Cal.com.",
"organizer_timezone": "Часова зона на организатора",
"email_user_cta": "Преглед на поканата",
"accept_invite": "Приемане на покана",
"join_now": "Присъединете се сега",
"successfully_joined": "Успешно се присъединихте",
"email_no_user_invite_heading_subteam": "Поканени сте да се присъедините към екип от организацията {{parentTeamName}}",
"email_no_user_invite_heading_org": "Поканени сте да се присъедините към организация в {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} ви покани да се присъедините към техния екип в {{appName}}. {{appName}} е платформа за управление на събития, която позволява на вас и вашия екип да планирате срещи без безкрайна размяна на имейли.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Заключено от администратора на екипа",
"app_not_connected": "Не сте свързали {{appName}} акаунт.",
"connect_now": "Свържете сега",
"connect_now_unavailable_tooltip": "Свързването сега не е налично. Моля, резервирайте от горните слотове или опитайте отново по-късно.",
"managed_event_dialog_confirm_button_one": "Замени и уведоми {{count}} член",
"managed_event_dialog_confirm_button_other": "Замени и уведоми {{count}} члена",
"managed_event_dialog_title_one": "URL адресът /{{slug}} вече съществува за {{count}} член. Искате ли да го замените?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Линкът за покана е копиран",
"invite_link_deleted": "Линкът за покана е изтрит",
"api_key_deleted": "API ключът е изтрит",
"delete_api_key_warning": "Това ще изтрие API ключа завинаги. Всички приложения, използващи този ключ, незабавно ще загубят достъп до вашия акаунт. Това действие не може да бъде отменено.",
"invite_link_updated": "Настройките на линка са запазени",
"link_expires_after": "Линковете изтичат след...",
"one_day": "1 ден",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Управлявайте роли и разрешения за вашата организация",
"learn_more_permissions": "Научете повече за това как работят разрешенията",
"role_name": "Име на ролята",
"role_name_required": "Името на ролята е задължително",
"role_name_placeholder": "напр. Търговски екип, Екип за поддръжка",
"role_description": "Описание на ролята",
"role_description_placeholder": "Опишете за какво е тази роля и какви разрешения са ѝ необходими",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Ролята е създадена успешно",
"role_updated_successfully": "Ролята е актуализирана успешно",
"delete_role": "Изтрий роля",
"confirm_delete_role": "Сигурни ли сте, че искате да изтриете тази роля? Това действие не може да бъде отменено.",
"confirm_delete_role": "Сигурни ли сте, че искате да изтриете ролята: {{roleName}}. Това действие не може да бъде отменено.",
"role_deleted_successfully": "Ролята е изтрита успешно",
"error_creating_role": "Грешка при създаване на роля",
"error_updating_role": "Грешка при актуализиране на роля",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Имейл на организатора",
"webhook_organizer_timezone": "Часова зона на организатора (напр. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Локал на организатора (напр. 'en', 'fr')",
"webhook_organizer_username": "Потребителско име на организатора (напр. 'john.doe')",
"webhook_organizer_username_in_org": "Потребителско име на организатора в неговата организация (напр. 'john.doe')",
"webhook_attendee_name": "Име на първия участник",
"webhook_attendee_email": "Имейл на първия участник",
"webhook_attendee_timezone": "Часова зона на първия участник",
@@ -3750,4 +3757,4 @@
"use_voice": "Използвай глас",
"current_voice": "Текущ глас",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавете новите си низове над този ред ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "কোনও উপলভ্য ব্যবহারকারী খুঁজে পাওয়া যায় নি। ",
"timeslot_unavailable_book_a_new_time": "নির্বাচিত সময়সীমা আর উপলব্ধ নেই। <0>অনুগ্রহ করে একটি নতুন সময় নির্বাচন করুন</0>",
"timeslot_unavailable_short": "নেওয়া হয়েছে",
"just_connected_description": "আপনি সবেমাত্র সংযুক্ত হয়েছেন। অনুগ্রহ করে উপরের স্লট থেকে বুক করুন অথবা পরে আবার চেষ্টা করুন।",
"unavailable_timeslot_title": "অনুপলব্ধ সময়সীমা",
"booking_time_out_of_bounds_error": "এই সময়ে ইভেন্ট টাইপ বুক করা যাবে না। আপনি কি অন্য সময়সীমা চেষ্টা করতে পারেন?",
"request_body_end_time_internal_error": "অভ্যন্তরীণ ত্রুটি। ",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "আপনি কি নিশ্চিত যে আপনি এটি মুছতে চান? {{connectionType}} কনফিগারেশন? {{connectionType}} লগইন আর Cal.com অ্যাক্সেস করতে সক্ষম হবে না।",
"organizer_timezone": "আয়োজক টাইমজোন",
"email_user_cta": "আমন্ত্রণ দেখুন",
"accept_invite": "আমন্ত্রণ গ্রহণ করুন",
"join_now": "এখনই যোগ দিন",
"successfully_joined": "সফলভাবে যোগ দেওয়া হয়েছে",
"email_no_user_invite_heading_subteam": "আপনাকে {{parentTeamName}} সংস্থার একটি দলে যোগদানের আমন্ত্রণ জানানো হয়েছে",
"email_no_user_invite_heading_org": "আপনাকে একটি {{appName}} সংস্থায় যোগদানের আমন্ত্রণ জানানো হয়েছে",
"email_no_user_invite_subheading": "{{invitedBy}} আপনাকে তাদের দলে যোগদানের জন্য আমন্ত্রণ জানিয়েছে {{appName}}। {{appName}} ইভেন্ট-জ্যাকলিং শিডিয়ুলার যা আপনাকে এবং আপনার দলকে ইমেল টেনিস ছাড়াই সভাগুলির সময়সূচী করতে সক্ষম করে।",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "টিম অ্যাডমিন দ্বারা লক",
"app_not_connected": "আপনি সংযুক্ত না একটি {{appName}} অ্যাকাউন্ট।",
"connect_now": "এখনই সংযুক্ত করুন",
"connect_now_unavailable_tooltip": "এখন সংযোগ করা বর্তমানে অনুপলব্ধ। অনুগ্রহ করে উপরের স্লট থেকে বুক করুন অথবা পরে আবার চেষ্টা করুন।",
"managed_event_dialog_confirm_button_one": "প্রতিস্থাপন এবং অবহিত {{count}} সদস্য",
"managed_event_dialog_confirm_button_other": "প্রতিস্থাপন এবং অবহিত {{count}} সদস্য",
"managed_event_dialog_title_one": "ইউআরএল /{{slug}} ইতিমধ্যে বিদ্যমান {{count}} সদস্য ",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "আমন্ত্রণ লিঙ্ক অনুলিপি",
"invite_link_deleted": "লিঙ্ক মুছে ফেলা আমন্ত্রণ",
"api_key_deleted": "এপিআই কী মুছে ফেলা হয়েছে",
"delete_api_key_warning": "এটি স্থায়ীভাবে API কী মুছে ফেলবে। এই কী ব্যবহার করে যেকোনো অ্যাপ্লিকেশন অবিলম্বে আপনার অ্যাকাউন্টে অ্যাক্সেস হারাবে। এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না।",
"invite_link_updated": "সংরক্ষণের লিঙ্ক সেটিংসকে আমন্ত্রণ করুন",
"link_expires_after": "লিঙ্কগুলি পরে শেষ হওয়ার জন্য সেট ...",
"one_day": "1 দিন",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "আপনার সংস্থার জন্য রোল এবং অনুমতি পরিচালনা করুন",
"learn_more_permissions": "অনুমতি কীভাবে কাজ করে সে সম্পর্কে আরও জানুন",
"role_name": "ভূমিকার নাম",
"role_name_required": "ভূমিকার নাম আবশ্যক",
"role_name_placeholder": "যেমন বিক্রয় দল, সহায়তা কর্মী",
"role_description": "ভূমিকার বিবরণ",
"role_description_placeholder": "এই ভূমিকাটি কীসের জন্য এবং কোন অনুমতি প্রয়োজন তা বর্ণনা করুন",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "ভূমিকা সফলভাবে তৈরি করা হয়েছে",
"role_updated_successfully": "ভূমিকা সফলভাবে আপডেট করা হয়েছে",
"delete_role": "ভূমিকা মুছুন",
"confirm_delete_role": "আপনি কি নিশ্চিত যে আপনি এই ভূমিকা মুছতে চান? এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না।",
"confirm_delete_role": "আপনি কি নিশ্চিত যে আপনি ভূমিকা মুছতে চান: {{roleName}}। এই কাজটি পূর্বাবস্থায় ফেরানো যাবে না।",
"role_deleted_successfully": "ভূমিকা সফলভাবে মুছে ফেলা হয়েছে",
"error_creating_role": "ভূমিকা তৈরি করতে ত্রুটি",
"error_updating_role": "ভূমিকা আপডেট করতে ত্রুটি",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "আয়োজকের ইমেইল",
"webhook_organizer_timezone": "আয়োজকের টাইমজোন (যেমন, 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "আয়োজকের লোকেল (যেমন, 'en', 'fr')",
"webhook_organizer_username": "আয়োজকের ইউজারনেম (যেমন, 'john.doe')",
"webhook_organizer_username_in_org": "তাদের সংগঠনে আয়োজকের ইউজারনেম (যেমন, 'john.doe')",
"webhook_attendee_name": "প্রথম অংশগ্রহণকারীর নাম",
"webhook_attendee_email": "প্রথম অংশগ্রহণকারীর ইমেইল",
"webhook_attendee_timezone": "প্রথম অংশগ্রহণকারীর টাইমজোন",
@@ -3750,4 +3757,4 @@
"use_voice": "ভয়েস ব্যবহার করুন",
"current_voice": "বর্তমান ভয়েস",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑ুষান্ত"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "No s'han trobat usuaris disponibles. Podries provar una altra franja horària?",
"timeslot_unavailable_book_a_new_time": "La franja horària seleccionada ja no està disponible. <0>Si us plau, selecciona una nova hora</0>",
"timeslot_unavailable_short": "Ocupat",
"just_connected_description": "Acabes de connectar-te. Si us plau, reserva des dels espais de temps disponibles o torna-ho a provar més tard.",
"unavailable_timeslot_title": "Franja horària no disponible",
"booking_time_out_of_bounds_error": "El tipus d'esdeveniment no es pot reservar en aquest moment. Podries provar una altra franja horària?",
"request_body_end_time_internal_error": "Error intern. El cos de la sol·licitud no conté l'hora de finalització",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Estàs segur que vols eliminar la configuració de {{connectionType}}? Els membres del teu equip que utilitzin l'inici de sessió {{connectionType}} ja no podran accedir a Cal.com.",
"organizer_timezone": "Zona horària de l'organitzador",
"email_user_cta": "Veure invitació",
"accept_invite": "Accepta la invitació",
"join_now": "Uneix-te ara",
"successfully_joined": "T'has unit correctament",
"email_no_user_invite_heading_subteam": "Has estat convidat a unir-te a un equip de l'organització {{parentTeamName}}",
"email_no_user_invite_heading_org": "Has estat convidat a unir-te a una organització de {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} t'ha convidat a unir-te al seu equip a {{appName}}. {{appName}} és el programador de reunions que us permet a tu i al teu equip programar trobades sense el ping-pong dels correus electrònics.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Bloquejat per l'administrador de l'equip",
"app_not_connected": "No has connectat cap compte de {{appName}}.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Enllaç d'invitació copiat",
"invite_link_deleted": "Enllaç d'invitació eliminat",
"api_key_deleted": "Clau API eliminada",
"delete_api_key_warning": "Això eliminarà permanentment la clau API. Qualsevol aplicació que utilitzi aquesta clau perdrà immediatament l'accés al teu compte. Aquesta acció no es pot desfer.",
"invite_link_updated": "Configuració de l'enllaç d'invitació desada",
"link_expires_after": "Els enllaços caduquen després de...",
"one_day": "1 dia",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gestiona els rols i permisos per a la teva organització",
"learn_more_permissions": "Aprèn més sobre com funcionen els permisos",
"role_name": "Nom del rol",
"role_name_required": "El nom del rol és obligatori",
"role_name_placeholder": "p. ex. Equip de vendes, Personal de suport",
"role_description": "Descripció del rol",
"role_description_placeholder": "Descriu per a què serveix aquest rol i quins permisos necessita",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol creat correctament",
"role_updated_successfully": "Rol actualitzat correctament",
"delete_role": "Eliminar rol",
"confirm_delete_role": "Estàs segur que vols eliminar aquest rol? Aquesta acció no es pot desfer.",
"confirm_delete_role": "Esteu segur que voleu eliminar el rol: {{roleName}}? Aquesta acció no es pot desfer.",
"role_deleted_successfully": "Rol eliminat correctament",
"error_creating_role": "Error en crear el rol",
"error_updating_role": "Error en actualitzar el rol",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Correu electrònic de l'organitzador",
"webhook_organizer_timezone": "Zona horària de l'organitzador (p. ex., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Configuració regional de l'organitzador (p. ex., 'en', 'fr')",
"webhook_organizer_username": "Nom d'usuari de l'organitzador (p. ex., 'john.doe')",
"webhook_organizer_username_in_org": "Nom d'usuari de l'organitzador a la seva organització (p. ex., 'john.doe')",
"webhook_attendee_name": "Nom del primer assistent",
"webhook_attendee_email": "Correu electrònic del primer assistent",
"webhook_attendee_timezone": "Zona horària del primer assistent",
@@ -3750,4 +3757,4 @@
"use_voice": "Utilitza la veu",
"current_voice": "Veu actual",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Afegiu les vostres noves cadenes a dalt ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nebyli nalezeni žádní dostupní uživatelé. Můžete zkusit jiný časový slot?",
"timeslot_unavailable_book_a_new_time": "Vybraný časový úsek již není k dispozici. <0>Prosím vyberte nový čas</0>",
"timeslot_unavailable_short": "Obsazeno",
"just_connected_description": "Právě jste se připojili. Prosím, rezervujte si termín z výše uvedených časových slotů nebo to zkuste později.",
"unavailable_timeslot_title": "Nedostupný časový slot",
"booking_time_out_of_bounds_error": "Tento typ události nelze v tomto čase rezervovat. Můžete zkusit jiný časový termín?",
"request_body_end_time_internal_error": "Interní chyba. Tělo požadavku neobsahuje čas ukončení",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Opravdu chcete odstranit konfiguraci {{connectionType}}? Členové vašeho týmu, kteří používají přihlášení {{connectionType}}, ztratí přístup k webu Cal.com.",
"organizer_timezone": "Časové pásmo organizátora",
"email_user_cta": "Zobrazit pozvánku",
"accept_invite": "Přijmout pozvánku",
"join_now": "Připojit se nyní",
"successfully_joined": "Úspěšně připojeno",
"email_no_user_invite_heading_subteam": "Byli jste pozváni k připojení do týmu organizace {{parentTeamName}}",
"email_no_user_invite_heading_org": "Byli jste pozváni k připojení do organizace {{appName}}",
"email_no_user_invite_subheading": "Uživatel {{invitedBy}} vás pozval, abyste se připojili k jeho týmu v aplikaci {{appName}}. {{appName}} je plánovač událostí, který vám a vašemu týmu umožňuje plánovat schůzky bez e-mailového ping-pongu.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Uzamčeno správcem týmu",
"app_not_connected": "Nemáte připojený účet {{appName}}.",
"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_other": "Nahradit a upozornit členy {{count}}",
"managed_event_dialog_title_one": "Adresa URL /{{slug}} již existuje pro {{count}} člena. Chcete ji nahradit?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Odkaz pozvánky byl zkopírován",
"invite_link_deleted": "Odkaz pozvánky byl odstraněn",
"api_key_deleted": "Klíč API odstraněn",
"delete_api_key_warning": "Tímto trvale smažete API klíč. Všechny aplikace používající tento klíč okamžitě ztratí přístup k vašemu účtu. Tuto akci nelze vrátit zpět.",
"invite_link_updated": "Nastavení odkazu pozvánky bylo uloženo",
"link_expires_after": "Platnost odkazů vyprší za...",
"one_day": "1 den",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Správa rolí a oprávnění pro vaši organizaci",
"learn_more_permissions": "Zjistěte více o tom, jak fungují oprávnění",
"role_name": "Název role",
"role_name_required": "Název role je povinný",
"role_name_placeholder": "např. Prodejní tým, Podpora",
"role_description": "Popis role",
"role_description_placeholder": "Popište, k čemu tato role slouží a jaká oprávnění potřebuje",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Role byla úspěšně vytvořena",
"role_updated_successfully": "Role byla úspěšně aktualizována",
"delete_role": "Odstranit roli",
"confirm_delete_role": "Opravdu chcete tuto roli odstranit? Tuto akci nelze vrátit zpět.",
"confirm_delete_role": "Opravdu chcete smazat roli: {{roleName}}? Tuto akci nelze vrátit zpět.",
"role_deleted_successfully": "Role byla úspěšně odstraněna",
"error_creating_role": "Chyba při vytváření role",
"error_updating_role": "Chyba při aktualizaci role",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "E-mail organizátora",
"webhook_organizer_timezone": "Časové pásmo organizátora (např. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Lokalizace organizátora (např. 'en', 'fr')",
"webhook_organizer_username": "Uživatelské jméno organizátora (např. 'john.doe')",
"webhook_organizer_username_in_org": "Uživatelské jméno organizátora v jeho organizaci (např. 'john.doe')",
"webhook_attendee_name": "Jméno prvního účastníka",
"webhook_attendee_email": "E-mail prvního účastníka",
"webhook_attendee_timezone": "Časové pásmo prvního účastníka",
@@ -3750,4 +3757,4 @@
"use_voice": "Použít hlas",
"current_voice": "Aktuální hlas",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Ingen tilgængelige brugere fundet. Kan du prøve et andet tidsrum?",
"timeslot_unavailable_book_a_new_time": "Det valgte tidspunkt er ikke længere tilgængeligt. <0>Vælg venligst et nyt tidspunkt</0>",
"timeslot_unavailable_short": "Optaget",
"just_connected_description": "Du har lige forbundet. Book venligst fra de ovenstående tidsrum eller prøv igen senere.",
"unavailable_timeslot_title": "Utilgængeligt tidsrum",
"booking_time_out_of_bounds_error": "Begivenheden kan ikke bookes på dette tidspunkt. Kunne du prøve et andet tidspunkt?",
"request_body_end_time_internal_error": "Intern fejl. Anmodningskroppen indeholder ikke sluttidspunkt",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Er du sikker på, at du vil slette {{connectionType}} konfigurationen? Dine teammedlemmer, der bruger {{connectionType}} login, vil ikke længere kunne få adgang til Cal.com.",
"organizer_timezone": "Arrangørens tidszone",
"email_user_cta": "Vis Invitation",
"accept_invite": "Acceptér invitation",
"join_now": "Deltag nu",
"successfully_joined": "Deltog med succes",
"email_no_user_invite_heading_subteam": "Du er blevet inviteret til at blive medlem af et team i {{parentTeamName}}-organisationen.",
"email_no_user_invite_heading_org": "Du er blevet inviteret til at blive medlem af en {{appName}}-organisation.",
"email_no_user_invite_subheading": "{{invitedBy}} har inviteret dig til at deltage i deres team på {{appName}}. {{appName}} er den begivenheds-jonglerende planlægger, der gør det muligt for dig og dit team at planlægge møder uden e-mail tennis.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Låst af teamadministrator",
"app_not_connected": "Du har ikke forbundet en {{appName}}-konto.",
"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_other": "Erstat og underret {{count}} medlemmer",
"managed_event_dialog_title_one": "URL'en /{{slug}} findes allerede for {{count}} medlem. Vil du erstatte den?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Invitationslink kopieret",
"invite_link_deleted": "Invitationslink slettet",
"api_key_deleted": "API-nøgle slettet",
"delete_api_key_warning": "Dette vil permanent slette API-nøglen. Alle applikationer, der bruger denne nøgle, vil straks miste adgang til din konto. Denne handling kan ikke fortrydes.",
"invite_link_updated": "Linkindstillinger gemt",
"link_expires_after": "Links indstillet til at udløbe efter...",
"one_day": "1 dag",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Administrer roller og tilladelser for din organisation",
"learn_more_permissions": "Lær mere om, hvordan tilladelser fungerer",
"role_name": "Rollenavn",
"role_name_required": "Rollenavn er påkrævet",
"role_name_placeholder": "f.eks. Salgsteam, Supportpersonale",
"role_description": "Rollebekskrivelse",
"role_description_placeholder": "Beskriv, hvad denne rolle er til, og hvilke tilladelser den har brug for",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rolle oprettet med succes",
"role_updated_successfully": "Rolle opdateret med succes",
"delete_role": "Slet rolle",
"confirm_delete_role": "Er du sikker på, at du vil slette denne rolle? Denne handling kan ikke fortrydes.",
"confirm_delete_role": "Er du sikker på, at du vil slette rollen: {{roleName}}? Denne handling kan ikke fortrydes.",
"role_deleted_successfully": "Rolle slettet med succes",
"error_creating_role": "Fejl ved oprettelse af rolle",
"error_updating_role": "Fejl ved opdatering af rolle",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Arrangørens e-mail",
"webhook_organizer_timezone": "Arrangørens tidszone (f.eks. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Arrangørens lokalitet (f.eks. 'en', 'fr')",
"webhook_organizer_username": "Brugernavn for arrangøren (f.eks. 'john.doe')",
"webhook_organizer_username_in_org": "Brugernavn for arrangøren i deres organisation (f.eks. 'john.doe')",
"webhook_attendee_name": "Navn på den første deltager",
"webhook_attendee_email": "E-mail på den første deltager",
"webhook_attendee_timezone": "Tidszone for den første deltager",
@@ -3750,4 +3757,4 @@
"use_voice": "Brug stemme",
"current_voice": "Nuværende stemme",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Tilføj dine nye strenge ovenfor her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Keine verfügbaren Benutzer gefunden. Können Sie es mit einem anderen Zeitfenster versuchen?",
"timeslot_unavailable_book_a_new_time": "Der ausgewählte Zeitraum ist nicht mehr verfügbar. <0>Bitte wähle eine neue Zeit</0>",
"timeslot_unavailable_short": "Belegt",
"just_connected_description": "Sie haben gerade eine Verbindung hergestellt. Bitte buchen Sie aus den obigen Zeitfenstern oder versuchen Sie es später erneut.",
"unavailable_timeslot_title": "Nicht verfügbarer Zeitslot",
"booking_time_out_of_bounds_error": "Der Termin kann zu diesem Zeitpunkt nicht gebucht werden. Möchten Sie einen anderen Zeitslot versuchen?",
"request_body_end_time_internal_error": "Interner Fehler. Request-Body enthält keinen Endzeitpunkt",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Sind Sie sicher, dass Sie die {{connectionType}}-Konfiguration löschen möchten? Ihre Teammitglieder, die sich über {{connectionType}} anmelden, werden keinen Zugriff mehr auf Cal.com haben.",
"organizer_timezone": "Zeitzone des Veranstalters",
"email_user_cta": "Einladung anzeigen",
"accept_invite": "Einladung annehmen",
"join_now": "Jetzt beitreten",
"successfully_joined": "Erfolgreich beigetreten",
"email_no_user_invite_heading_subteam": "Du wurdest eingeladen, einem Team der Organisation {{parentTeamName}} beizutreten",
"email_no_user_invite_heading_org": "Du wurdest eingeladen, einer {{appName}}-Organisation beizutreten",
"email_no_user_invite_subheading": "{{invitedBy}} hat Sie eingeladen, dem Team in {{appName}} beizutreten. {{appName}} ist der Event-Planer, der es Ihnen und Ihrem Team ermöglicht, Meetings ohne ständiges hin und her zu planen.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Vom Team-Admin gesperrt",
"app_not_connected": "Sie haben kein {{appName}}-Konto verbunden.",
"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_other": "{{count}} Mitglieder ersetzen & benachrichtigen",
"managed_event_dialog_title_one": "Die URL /{{slug}} wird bereits von {{count}} Mitglied genutzt. Möchten Sie sie ersetzen?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Einladungslink kopiert",
"invite_link_deleted": "Einladungslink gelöscht",
"api_key_deleted": "API-Schlüssel gelöscht",
"delete_api_key_warning": "Dies wird den API-Schlüssel dauerhaft löschen. Alle Anwendungen, die diesen Schlüssel verwenden, verlieren sofort den Zugriff auf Ihr Konto. Diese Aktion kann nicht rückgängig gemacht werden.",
"invite_link_updated": "Einladungslink-Einstellungen gespeichert",
"link_expires_after": "Links verfallen nach...",
"one_day": "1 Tag",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Verwalte Rollen und Berechtigungen für deine Organisation",
"learn_more_permissions": "Erfahre mehr darüber, wie Berechtigungen funktionieren",
"role_name": "Rollenname",
"role_name_required": "Rollenname ist erforderlich",
"role_name_placeholder": "z.B. Vertriebsteam, Support-Mitarbeiter",
"role_description": "Rollenbeschreibung",
"role_description_placeholder": "Beschreibe, wofür diese Rolle gedacht ist und welche Berechtigungen sie benötigt",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rolle erfolgreich erstellt",
"role_updated_successfully": "Rolle erfolgreich aktualisiert",
"delete_role": "Rolle löschen",
"confirm_delete_role": "Sind Sie sicher, dass Sie diese Rolle löschen möchten? Diese Aktion kann nicht rückgängig gemacht werden.",
"confirm_delete_role": "Sind Sie sicher, dass Sie die Rolle löschen möchten: {{roleName}}. Diese Aktion kann nicht rückgängig gemacht werden.",
"role_deleted_successfully": "Rolle erfolgreich gelöscht",
"error_creating_role": "Fehler beim Erstellen der Rolle",
"error_updating_role": "Fehler beim Aktualisieren der Rolle",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "E-Mail des Organisators",
"webhook_organizer_timezone": "Zeitzone des Organisators (z.B. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Sprache des Organisators (z.B. 'en', 'fr')",
"webhook_organizer_username": "Benutzername des Organisators (z.B. 'john.doe')",
"webhook_organizer_username_in_org": "Benutzername des Organisators in seiner Organisation (z.B. 'john.doe')",
"webhook_attendee_name": "Name des ersten Teilnehmers",
"webhook_attendee_email": "E-Mail des ersten Teilnehmers",
"webhook_attendee_timezone": "Zeitzone des ersten Teilnehmers",
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Δεν βρέθηκαν διαθέσιμοι χρήστες. Θα μπορούσατε να δοκιμάσετε άλλη χρονική θυρίδα;",
"timeslot_unavailable_book_a_new_time": "Η επιλεγμένη χρονοθυρίδα δεν είναι πλέον διαθέσιμη. <0>Παρακαλώ επιλέξτε νέο χρόνο</0>",
"timeslot_unavailable_short": "Κατειλημμένο",
"just_connected_description": "Μόλις συνδεθήκατε. Παρακαλώ κάντε κράτηση από τις παραπάνω διαθέσιμες ώρες ή δοκιμάστε ξανά αργότερα.",
"unavailable_timeslot_title": "Μη διαθέσιμη χρονοθυρίδα",
"booking_time_out_of_bounds_error": "Ο τύπος εκδήλωσης δεν μπορεί να κρατηθεί αυτή τη στιγμή. Θα μπορούσατε να δοκιμάσετε μια άλλη χρονική θυρίδα;",
"request_body_end_time_internal_error": "Εσωτερικό σφάλμα. Το σώμα του αιτήματος δεν περιέχει ώρα λήξης",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τη διαμόρφωση {{connectionType}}; Τα μέλη της ομάδας σας που χρησιμοποιούν σύνδεση {{connectionType}} δεν θα μπορούν πλέον να έχουν πρόσβαση στο Cal.com.",
"organizer_timezone": "Ζώνη ώρας διοργανωτή",
"email_user_cta": "Προβολή πρόσκλησης",
"accept_invite": "Αποδοχή Πρόσκλησης",
"join_now": "Συμμετοχή Τώρα",
"successfully_joined": "Επιτυχής συμμετοχή",
"email_no_user_invite_heading_subteam": "Έχετε προσκληθεί να συμμετάσχετε σε μια ομάδα του οργανισμού {{parentTeamName}}",
"email_no_user_invite_heading_org": "Έχετε προσκληθεί να συμμετάσχετε σε έναν οργανισμό {{appName}}",
"email_no_user_invite_subheading": "Ο/Η {{invitedBy}} σας έχει προσκαλέσει να συμμετάσχετε στην ομάδα τους στο {{appName}}. Το {{appName}} είναι ο προγραμματιστής συναντήσεων που επιτρέπει σε εσάς και την ομάδα σας να κανονίζετε συναντήσεις χωρίς ατελείωτα email.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Κλειδωμένο από τον διαχειριστή ομάδας",
"app_not_connected": "Δεν έχετε συνδέσει λογαριασμό {{appName}}.",
"connect_now": "Σύνδεση τώρα",
"connect_now_unavailable_tooltip": "Η άμεση σύνδεση δεν είναι διαθέσιμη αυτή τη στιγμή. Παρακαλώ κάντε κράτηση από τις παραπάνω διαθέσιμες ώρες ή δοκιμάστε αργότερα.",
"managed_event_dialog_confirm_button_one": "Αντικατάσταση & ειδοποίηση {{count}} μέλους",
"managed_event_dialog_confirm_button_other": "Αντικατάσταση & ειδοποίηση {{count}} μελών",
"managed_event_dialog_title_one": "Το url /{{slug}} υπάρχει ήδη για {{count}} μέλος. Θέλετε να το αντικαταστήσετε;",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Ο σύνδεσμος πρόσκλησης αντιγράφηκε",
"invite_link_deleted": "Ο σύνδεσμος πρόσκλησης διαγράφηκε",
"api_key_deleted": "Το κλειδί API διαγράφηκε",
"delete_api_key_warning": "Αυτό θα διαγράψει μόνιμα το κλειδί API. Οποιεσδήποτε εφαρμογές χρησιμοποιούν αυτό το κλειδί θα χάσουν αμέσως την πρόσβαση στον λογαριασμό σας. Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.",
"invite_link_updated": "Οι ρυθμίσεις του συνδέσμου πρόσκλησης αποθηκεύτηκαν",
"link_expires_after": "Οι σύνδεσμοι λήγουν μετά από...",
"one_day": "1 ημέρα",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Διαχειριστείτε ρόλους και δικαιώματα για τον οργανισμό σας",
"learn_more_permissions": "Μάθετε περισσότερα για το πώς λειτουργούν τα Δικαιώματα",
"role_name": "Όνομα ρόλου",
"role_name_required": "Το όνομα ρόλου είναι υποχρεωτικό",
"role_name_placeholder": "π.χ. Ομάδα Πωλήσεων, Προσωπικό Υποστήριξης",
"role_description": "Περιγραφή ρόλου",
"role_description_placeholder": "Περιγράψτε για ποιο σκοπό προορίζεται αυτός ο ρόλος και ποια δικαιώματα χρειάζεται",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Ο ρόλος δημιουργήθηκε με επιτυχία",
"role_updated_successfully": "Ο ρόλος ενημερώθηκε με επιτυχία",
"delete_role": "Διαγραφή ρόλου",
"confirm_delete_role": "Είστε βέβαιοι ότι θέλετε να διαγράψετε αυτόν τον ρόλο; Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.",
"confirm_delete_role": "Είστε βέβαιοι ότι θέλετε να διαγράψετε τον ρόλο: {{roleName}}; Αυτή η ενέργεια δεν μπορεί να αναιρεθεί.",
"role_deleted_successfully": "Ο ρόλος διαγράφηκε με επιτυχία",
"error_creating_role": "Σφάλμα κατά τη δημιουργία ρόλου",
"error_updating_role": "Σφάλμα κατά την ενημέρωση ρόλου",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email του διοργανωτή",
"webhook_organizer_timezone": "Ζώνη ώρας του διοργανωτή (π.χ., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Τοπικές ρυθμίσεις του διοργανωτή (π.χ., 'en', 'fr')",
"webhook_organizer_username": "Όνομα χρήστη του διοργανωτή (π.χ., 'john.doe')",
"webhook_organizer_username_in_org": "Όνομα χρήστη του διοργανωτή στον οργανισμό του (π.χ., 'john.doe')",
"webhook_attendee_name": "Όνομα του πρώτου συμμετέχοντα",
"webhook_attendee_email": "Email του πρώτου συμμετέχοντα",
"webhook_attendee_timezone": "Ζώνη ώρας του πρώτου συμμετέχοντα",
@@ -3750,4 +3757,4 @@
"use_voice": "Χρήση φωνής",
"current_voice": "Τρέχουσα φωνή",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Προσθέστε τις νέες συμβολοσειρές σας πάνω από εδώ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "No available users found. Could you try another time slot?",
"timeslot_unavailable_book_a_new_time": "The selected time slot is no longer available. <0>Please select a new time</0>",
"timeslot_unavailable_short": "Taken",
"just_connected_description": "You’ve just connected. Please book from above slots or retry later.",
"unavailable_timeslot_title": "Unavailable timeslot",
"booking_time_out_of_bounds_error": "The event type cannot be booked at this time. Could you try another time slot?",
"request_body_end_time_internal_error": "Internal Error. Request body does not contain end time",
@@ -2304,7 +2305,6 @@
"locked_by_team_admin": "Locked by team admin",
"app_not_connected": "You have not connected a {{appName}} account.",
"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_other": "Replace & notify {{count}} members",
"managed_event_dialog_title_one": "The url /{{slug}} already exists for {{count}} member. Do you want to replace it?",
@@ -2386,6 +2386,7 @@
"invite_link_copied": "Invite link copied",
"invite_link_deleted": "Invite link deleted",
"api_key_deleted": "API Key deleted",
"delete_api_key_warning": "This will permanently delete the API key. Any applications using this key will immediately lose access to your account. This action cannot be undone.",
"invite_link_updated": "Invite link settings saved",
"link_expires_after": "Links set to expire after...",
"one_day": "1 day",
@@ -3463,6 +3464,7 @@
"roles_and_permissions_description": "Manage roles and permissions for your organization",
"learn_more_permissions": "Learn more about how Permissions work",
"role_name": "Role name",
"role_name_required": "Role name is required",
"role_name_placeholder": "e.g. Sales Team, Support Staff",
"role_description": "Role description",
"role_description_placeholder": "Describe what this role is for and what permissions it needs",
@@ -3501,7 +3503,7 @@
"role_created_successfully": "Role created successfully",
"role_updated_successfully": "Role updated successfully",
"delete_role": "Delete role",
"confirm_delete_role": "Are you sure you want to delete this role? This action cannot be undone.",
"confirm_delete_role": "Are you sure you want to delete role: {{roleName}}. This action cannot be undone.",
"role_deleted_successfully": "Role deleted successfully",
"error_creating_role": "Error creating role",
"error_updating_role": "Error updating role",
@@ -3675,6 +3677,8 @@
"webhook_organizer_email": "Email of the organizer",
"webhook_organizer_timezone": "Timezone of the organizer (e.g., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Locale of the organizer (e.g., 'en', 'fr')",
"webhook_organizer_username": "Username of the organizer (e.g., 'john.doe')",
"webhook_organizer_username_in_org": "Username of the organizer in their organization (e.g., 'john.doe')",
"webhook_attendee_name": "Name of the first attendee",
"webhook_attendee_email": "Email of the first attendee",
"webhook_attendee_timezone": "Timezone of the first attendee",
@@ -111,6 +111,7 @@
"no_available_users_found_error": "No se encontraron usuarios disponibles. ¿Podrías intentar en otro horario?",
"timeslot_unavailable_book_a_new_time": "El horario seleccionado ya no está disponible. <0>Por favor selecciona un nuevo horario</0>",
"timeslot_unavailable_short": "Ocupado",
"just_connected_description": "Acabas de conectarte. Por favor, reserva desde los espacios disponibles arriba o inténtalo más tarde.",
"unavailable_timeslot_title": "Horario no disponible",
"booking_time_out_of_bounds_error": "El evento no se puede agendar en este momento. ¿Podrías intentar con otro horario?",
"request_body_end_time_internal_error": "Error interno. El cuerpo de la solicitud no contiene la hora de finalización",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "¿Estás seguro de que deseas eliminar la configuración de {{connectionType}}? Los miembros de tu equipo que usan el inicio de sesión de {{connectionType}} ya no podrán acceder a Cal.com.",
"organizer_timezone": "Zona horaria del organizador",
"email_user_cta": "Ver invitación",
"accept_invite": "Aceptar invitación",
"join_now": "Unirse ahora",
"successfully_joined": "Te has unido exitosamente",
"email_no_user_invite_heading_subteam": "Has sido invitado a unirte a un equipo de la organización {{parentTeamName}}",
"email_no_user_invite_heading_org": "Has sido invitado a unirte a una organización de {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} te ha invitado a unirte a su equipo en {{appName}}. {{appName}} es el programador de eventos que te permite a ti y a tu equipo agendar reuniones sin el ir y venir de correos electrónicos.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
"app_not_connected": "No has conectado una cuenta de {{appName}}.",
"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_other": "Reemplazar y notificar a {{count}} miembros",
"managed_event_dialog_title_one": "La URL /{{slug}} ya existe para {{count}} miembro. ¿Quieres reemplazarla?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Enlace de invitación copiado",
"invite_link_deleted": "Enlace de invitación eliminado",
"api_key_deleted": "Clave API eliminada",
"delete_api_key_warning": "Esto eliminará permanentemente la clave API. Cualquier aplicación que use esta clave perderá inmediatamente el acceso a tu cuenta. Esta acción no se puede deshacer.",
"invite_link_updated": "Configuración del enlace de invitación guardada",
"link_expires_after": "Los enlaces están configurados para expirar después de...",
"one_day": "1 día",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Administra roles y permisos para tu organización",
"learn_more_permissions": "Aprende más sobre cómo funcionan los permisos",
"role_name": "Nombre del rol",
"role_name_required": "El nombre del rol es obligatorio",
"role_name_placeholder": "p. ej. Equipo de ventas, Personal de soporte",
"role_description": "Descripción del rol",
"role_description_placeholder": "Describe para qué es este rol y qué permisos necesita",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol creado exitosamente",
"role_updated_successfully": "Rol actualizado exitosamente",
"delete_role": "Eliminar rol",
"confirm_delete_role": "¿Estás seguro de que quieres eliminar este rol? Esta acción no se puede deshacer.",
"confirm_delete_role": "¿Estás seguro de que quieres eliminar el rol: {{roleName}}? Esta acción no se puede deshacer.",
"role_deleted_successfully": "Rol eliminado exitosamente",
"error_creating_role": "Error al crear el rol",
"error_updating_role": "Error al actualizar el rol",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Correo electrónico del organizador",
"webhook_organizer_timezone": "Zona horaria del organizador (por ejemplo, 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Configuración regional del organizador (por ejemplo, 'en', 'fr')",
"webhook_organizer_username": "Nombre de usuario del organizador (p. ej., 'john.doe')",
"webhook_organizer_username_in_org": "Nombre de usuario del organizador en su organización (p. ej., 'john.doe')",
"webhook_attendee_name": "Nombre del primer asistente",
"webhook_attendee_email": "Correo electrónico del primer asistente",
"webhook_attendee_timezone": "Zona horaria del primer asistente",
@@ -3750,4 +3757,4 @@
"use_voice": "Usar voz",
"current_voice": "Voz actual",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agrega tus nuevas cadenas arriba de esta línea ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "No se encontraron usuarios disponibles. ¿Podrías intentar en otro horario?",
"timeslot_unavailable_book_a_new_time": "El horario seleccionado ya no está disponible. <0>Por favor selecciona un nuevo horario</0>",
"timeslot_unavailable_short": "Ocupado",
"just_connected_description": "Acabas de conectarte. Por favor, reserva desde los espacios de arriba o inténtalo más tarde.",
"unavailable_timeslot_title": "Horario no disponible",
"booking_time_out_of_bounds_error": "Este tipo de evento no se puede reservar en este momento. ¿Podrías intentar con otro horario?",
"request_body_end_time_internal_error": "Error interno. El cuerpo de la solicitud no contiene la hora de finalización",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "¿Está seguro de que desea eliminar la configuración de {{connectionType}}? Los miembros de su equipo que utilicen el inicio de sesión {{connectionType}} ya no podrán acceder a Cal.com.",
"organizer_timezone": "Zona horaria del organizador",
"email_user_cta": "Ver la invitación",
"accept_invite": "Aceptar invitación",
"join_now": "Unirse ahora",
"successfully_joined": "Unido con éxito",
"email_no_user_invite_heading_subteam": "Has sido invitado a unirte a un equipo de la organización {{parentTeamName}}",
"email_no_user_invite_heading_org": "Has sido invitado a unirte a una organización de {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} lo invitó a unirse a su equipo en {{appName}}. {{appName}} es el programador de eventos que les permite a usted y a su equipo programar reuniones sin necesidad de enviar correos electrónicos.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
"app_not_connected": "No ha conectado una cuenta de {{appName}}.",
"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_other": "Reemplazar y notificar a {{count}} miembros",
"managed_event_dialog_title_one": "La url /{{slug}} ya existe para {{count}} miembro. ¿Desea reemplazarla?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Enlace de invitación copiado",
"invite_link_deleted": "Enlace de invitación eliminado",
"api_key_deleted": "Clave API eliminada",
"delete_api_key_warning": "Esto eliminará permanentemente la clave API. Cualquier aplicación que use esta clave perderá inmediatamente el acceso a tu cuenta. Esta acción no se puede deshacer.",
"invite_link_updated": "Configuración de enlace de invitación guardada",
"link_expires_after": "Enlaces establecidos para expirar después de...",
"one_day": "1 día",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gestiona roles y permisos para tu organización",
"learn_more_permissions": "Aprende más sobre cómo funcionan los permisos",
"role_name": "Nombre del rol",
"role_name_required": "El nombre del rol es obligatorio",
"role_name_placeholder": "p. ej. Equipo de ventas, Personal de soporte",
"role_description": "Descripción del rol",
"role_description_placeholder": "Describe para qué sirve este rol y qué permisos necesita",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol creado con éxito",
"role_updated_successfully": "Rol actualizado con éxito",
"delete_role": "Eliminar rol",
"confirm_delete_role": "¿Está seguro de que desea eliminar este rol? Esta acción no se puede deshacer.",
"confirm_delete_role": "¿Estás seguro de que quieres eliminar el rol: {{roleName}}? Esta acción no se puede deshacer.",
"role_deleted_successfully": "Rol eliminado con éxito",
"error_creating_role": "Error al crear el rol",
"error_updating_role": "Error al actualizar el rol",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Correo electrónico del organizador",
"webhook_organizer_timezone": "Zona horaria del organizador (p. ej., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Configuración regional del organizador (p. ej., 'en', 'fr')",
"webhook_organizer_username": "Nombre de usuario del organizador (p. ej., 'john.doe')",
"webhook_organizer_username_in_org": "Nombre de usuario del organizador en su organización (p. ej., 'john.doe')",
"webhook_attendee_name": "Nombre del primer asistente",
"webhook_attendee_email": "Correo electrónico del primer asistente",
"webhook_attendee_timezone": "Zona horaria del primer asistente",
@@ -3750,4 +3757,4 @@
"use_voice": "Usar voz",
"current_voice": "Voz actual",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agregue sus nuevas cadenas arriba ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Saadaolevaid kasutajaid ei leitud. Kas saaksite proovida teist ajavahemikku?",
"timeslot_unavailable_book_a_new_time": "Valitud ajavahemik pole enam saadaval. <0>Palun vali uus aeg</0>",
"timeslot_unavailable_short": "Võetud",
"just_connected_description": "Olete just ühenduse loonud. Palun broneerige ülaltoodud ajavahemikest või proovige hiljem uuesti.",
"unavailable_timeslot_title": "Mittesaadaval ajaperiood",
"booking_time_out_of_bounds_error": "Sündmust ei saa sellel ajal broneerida. Kas proovid mõnda teist aega?",
"request_body_end_time_internal_error": "Sisemine viga. Taotluse sisu ei sisalda lõpuaega",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Kas soovite kindlasti konfiguratsiooni {{connectionType}} kustutada? Teie meeskonnaliikmed, kes kasutavad {{connectionType}} sisselogimist, ei pääse enam saidile Cal.com juurde.",
"organizer_timezone": "Korraldaja ajavöönd",
"email_user_cta": "Vaata kutset",
"accept_invite": "Võta kutse vastu",
"join_now": "Liitu kohe",
"successfully_joined": "Liitumine õnnestus",
"email_no_user_invite_heading_subteam": "Teid on kutsutud liituma {{parentTeamName}} organisatsiooni meeskonnaga.",
"email_no_user_invite_heading_org": "Teid on kutsutud liituma {{appName}} organisatsiooniga.",
"email_no_user_invite_subheading": "{{invitedBy}} kutsus teid liituma oma meeskonnaga rakenduses {{appName}}. {{appName}} on sündmustega žongleerimise ajakava, mis võimaldab teil ja teie meeskonnal koosolekuid ajastada ilma meilitenniseta.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Meeskonna administraatori poolt lukustatud",
"app_not_connected": "Te pole rakenduse {{appName}} kontot ühendanud.",
"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_other": "Asenda ja teavitage {{count}} liiget",
"managed_event_dialog_title_one": "URL /{{slug}} on {{count}} liikme jaoks juba olemas. Kas soovite selle asendada?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Kutse link kopeeritud",
"invite_link_deleted": "Kutse link kustutatud",
"api_key_deleted": "API võti kustutatud",
"delete_api_key_warning": "See kustutab API-võtme jäädavalt. Kõik rakendused, mis seda võtit kasutavad, kaotavad kohe juurdepääsu teie kontole. Seda toimingut ei saa tagasi võtta.",
"invite_link_updated": "Kutse lingi seaded on salvestatud",
"link_expires_after": "Lingid aeguma pärast...",
"one_day": "1 päev",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Halda oma organisatsiooni rolle ja õigusi",
"learn_more_permissions": "Lisateave õiguste toimimise kohta",
"role_name": "Rolli nimi",
"role_name_required": "Rolli nimi on kohustuslik",
"role_name_placeholder": "nt Müügimeeskond, Tugipersonal",
"role_description": "Rolli kirjeldus",
"role_description_placeholder": "Kirjeldage, milleks see roll on ja milliseid õigusi see vajab",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Roll loodud edukalt",
"role_updated_successfully": "Roll uuendatud edukalt",
"delete_role": "Kustuta roll",
"confirm_delete_role": "Kas olete kindel, et soovite selle rolli kustutada? Seda toimingut ei saa tagasi võtta.",
"confirm_delete_role": "Kas olete kindel, et soovite rolli kustutada: {{roleName}}? Seda toimingut ei saa tagasi võtta.",
"role_deleted_successfully": "Roll kustutatud edukalt",
"error_creating_role": "Viga rolli loomisel",
"error_updating_role": "Viga rolli uuendamisel",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Korraldaja e-post",
"webhook_organizer_timezone": "Korraldaja ajavöönd (nt 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Korraldaja lokaad (nt 'en', 'fr')",
"webhook_organizer_username": "Korraldaja kasutajanimi (nt 'john.doe')",
"webhook_organizer_username_in_org": "Korraldaja kasutajanimi nende organisatsioonis (nt 'john.doe')",
"webhook_attendee_name": "Esimese osaleja nimi",
"webhook_attendee_email": "Esimese osaleja e-post",
"webhook_attendee_timezone": "Esimese osaleja ajavöönd",
@@ -3750,4 +3757,4 @@
"use_voice": "Kasuta häält",
"current_voice": "Praegune hääl",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Ez da erabiltzaile eskuragarririk aurkitu. Beste denbora tarte bat probatu nahi duzu?",
"timeslot_unavailable_book_a_new_time": "Hautatutako denbora-tartea ez dago jada eskuragarri. <0>Mesedez, hautatu beste ordu bat</0>",
"timeslot_unavailable_short": "Hartuta",
"just_connected_description": "Konektatu berri zara. Mesedez, erreserbatu goiko ordutegietatik edo saiatu berriro geroago.",
"unavailable_timeslot_title": "Denbora-tarte ez erabilgarria",
"booking_time_out_of_bounds_error": "Ezin da gertaera mota hau momentu honetan erreserbatu. Beste denbora-tarte bat probatuko zenuke?",
"request_body_end_time_internal_error": "Barne errorea. Eskaeraren gorputzak ez du amaiera ordua",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Ziur zaude {{connectionType}} konfigurazioa ezabatu nahi duzula? {{connectionType}} saioa hasten duten zure taldekideek ezingo dute Cal.com erabili.",
"organizer_timezone": "Antolatzailearen ordu-zona",
"email_user_cta": "Ikusi gonbidapena",
"accept_invite": "Onartu gonbidapena",
"join_now": "Batu orain",
"successfully_joined": "Arrakastaz batu zara",
"email_no_user_invite_heading_subteam": "{{parentTeamName}} erakundeko talde batera batzeko gonbidapena jaso duzu",
"email_no_user_invite_heading_org": "{{appName}} erakunde batera batzeko gonbidapena jaso duzu",
"email_no_user_invite_subheading": "{{invitedBy}}-k bere taldera gonbidatu zaitu {{appName}}-en. {{appName}} bileren antolaketa errazten duen tresna da, zure taldeak bilerak antolatzeko aukera ematen duena posta elektroniko truke etengabea ekidinez.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Taldeko administratzaileak blokeatuta",
"app_not_connected": "Ez duzu {{appName}} konturik konektatu.",
"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_other": "Ordezkatu eta {{count}} kideei jakinarazi",
"managed_event_dialog_title_one": "/{{slug}} URLa dagoeneko existitzen da {{count}} kiderentzat. Ordezkatu nahi duzu?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Gonbidapen-esteka kopiatuta",
"invite_link_deleted": "Gonbidapen-esteka ezabatuta",
"api_key_deleted": "API gakoa ezabatuta",
"delete_api_key_warning": "Honek API gakoa betiko ezabatuko du. Gako hau erabiltzen duten aplikazio guztiek zure konturako sarbidea galduko dute berehala. Ekintza hau ezin da desegin.",
"invite_link_updated": "Gonbidapen-estekaren ezarpenak gordeta",
"link_expires_after": "Estekak iraungitzeko denbora...",
"one_day": "Egun 1",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Kudeatu zure erakundearen rolak eta baimenak",
"learn_more_permissions": "Ikasi gehiago baimenek nola funtzionatzen duten",
"role_name": "Rolaren izena",
"role_name_required": "Rolaren izena beharrezkoa da",
"role_name_placeholder": "adib. Salmenta taldea, Laguntza langileak",
"role_description": "Rolaren deskribapena",
"role_description_placeholder": "Deskribatu rol hau zertarako den eta zer baimen behar dituen",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rola arrakastaz sortu da",
"role_updated_successfully": "Rola arrakastaz eguneratu da",
"delete_role": "Ezabatu rola",
"confirm_delete_role": "Ziur zaude rol hau ezabatu nahi duzula? Ekintza hau ezin da desegin.",
"confirm_delete_role": "Ziur zaude {{roleName}} rola ezabatu nahi duzula? Ekintza hau ezin da desegin.",
"role_deleted_successfully": "Rola arrakastaz ezabatu da",
"error_creating_role": "Errorea rola sortzerakoan",
"error_updating_role": "Errorea rola eguneratzerakoan",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Antolatzailearen helbide elektronikoa",
"webhook_organizer_timezone": "Antolatzailearen ordu-zona (adib., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Antolatzailearen lokala (adib., 'en', 'fr')",
"webhook_organizer_username": "Antolatzailearen erabiltzaile-izena (adib., 'john.doe')",
"webhook_organizer_username_in_org": "Antolatzailearen erabiltzaile-izena bere erakundean (adib., 'john.doe')",
"webhook_attendee_name": "Lehen parte-hartzailearen izena",
"webhook_attendee_email": "Lehen parte-hartzailearen helbide elektronikoa",
"webhook_attendee_timezone": "Lehen parte-hartzailearen ordu-zona",
@@ -3750,4 +3757,4 @@
"use_voice": "Erabili ahotsa",
"current_voice": "Uneko ahotsa",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Gehitu zure kate berriak honen gainean ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Vapaita käyttäjiä ei löytynyt. Kokeilitko toista ajankohtaa?",
"timeslot_unavailable_book_a_new_time": "Valittu aikaikkunaa ei ole enää saatavilla. <0>Valitse uusi aika</0>",
"timeslot_unavailable_short": "Varattu",
"just_connected_description": "Olet juuri yhdistänyt. Varaa aika yllä olevista ajoista tai yritä myöhemmin uudelleen.",
"unavailable_timeslot_title": "Aika ei ole saatavilla",
"booking_time_out_of_bounds_error": "Tapahtumaa ei voi varata tähän aikaan. Kokeilitko toista ajankohtaa?",
"request_body_end_time_internal_error": "Sisäinen virhe. Pyynnön rungosta puuttuu lopetusaika",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Haluatko varmasti poistaa {{connectionType}}-määritykset? Tiimisi jäsenet, jotka käyttävät {{connectionType}}-kirjautumista, eivät enää pääse käyttämään Cal.comia.",
"organizer_timezone": "Järjestäjän aikavyöhyke",
"email_user_cta": "Näytä kutsu",
"accept_invite": "Hyväksy kutsu",
"join_now": "Liity nyt",
"successfully_joined": "Liittyminen onnistui",
"email_no_user_invite_heading_subteam": "Sinut on kutsuttu liittymään {{parentTeamName}}-organisaation tiimiin",
"email_no_user_invite_heading_org": "Sinut on kutsuttu liittymään {{appName}}-organisaatioon",
"email_no_user_invite_subheading": "{{invitedBy}} on kutsunut sinut liittymään tiimiinsä palvelussa {{appName}}. {{appName}} on tapaamisenhallintakalenteri, jonka avulla sinä ja tiimisi voitte sopia tapaamisia ilman loputtomia sähköposteja.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Tiimin ylläpitäjän lukitsema",
"app_not_connected": "Et ole yhdistänyt {{appName}}-tiliä.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Kutsulinkki kopioitu",
"invite_link_deleted": "Kutsulinkki poistettu",
"api_key_deleted": "API-avain poistettu",
"delete_api_key_warning": "Tämä poistaa API-avaimen pysyvästi. Kaikki sovellukset, jotka käyttävät tätä avainta, menettävät välittömästi pääsyn tilillesi. Tätä toimintoa ei voi peruuttaa.",
"invite_link_updated": "Linkin asetukset tallennettu",
"link_expires_after": "Linkit vanhenevat...",
"one_day": "1 päivä",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Hallitse organisaatiosi rooleja ja käyttöoikeuksia",
"learn_more_permissions": "Lue lisää käyttöoikeuksien toiminnasta",
"role_name": "Roolin nimi",
"role_name_required": "Roolin nimi vaaditaan",
"role_name_placeholder": "esim. Myyntitiimi, Tukihenkilöstö",
"role_description": "Roolin kuvaus",
"role_description_placeholder": "Kuvaile mihin tätä roolia käytetään ja mitä käyttöoikeuksia se tarvitsee",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rooli luotu onnistuneesti",
"role_updated_successfully": "Rooli päivitetty onnistuneesti",
"delete_role": "Poista rooli",
"confirm_delete_role": "Haluatko varmasti poistaa tämän roolin? Tätä toimintoa ei voi kumota.",
"confirm_delete_role": "Haluatko varmasti poistaa roolin: {{roleName}}. Tätä toimintoa ei voi kumota.",
"role_deleted_successfully": "Rooli poistettu onnistuneesti",
"error_creating_role": "Virhe roolin luomisessa",
"error_updating_role": "Virhe roolin päivittämisessä",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Järjestäjän sähköposti",
"webhook_organizer_timezone": "Järjestäjän aikavyöhyke (esim. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Järjestäjän maa-asetus (esim. 'en', 'fr')",
"webhook_organizer_username": "Järjestäjän käyttäjätunnus (esim. 'john.doe')",
"webhook_organizer_username_in_org": "Järjestäjän käyttäjätunnus organisaatiossa (esim. 'john.doe')",
"webhook_attendee_name": "Ensimmäisen osallistujan nimi",
"webhook_attendee_email": "Ensimmäisen osallistujan sähköposti",
"webhook_attendee_timezone": "Ensimmäisen osallistujan aikavyöhyke",
@@ -3750,4 +3757,4 @@
"use_voice": "Käytä ääntä",
"current_voice": "Nykyinen ääni",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Lisää uudet merkkijonot tämän yläpuolelle ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Aucun utilisateur disponible n'a été trouvé. Pourriez-vous essayer un autre créneau horaire ?",
"timeslot_unavailable_book_a_new_time": "Le créneau horaire sélectionné n'est plus disponible. <0>Veuillez sélectionner un nouvel horaire</0>",
"timeslot_unavailable_short": "Pris",
"just_connected_description": "Vous venez de vous connecter. Veuillez réserver parmi les créneaux ci-dessus ou réessayer plus tard.",
"unavailable_timeslot_title": "Créneau indisponible",
"booking_time_out_of_bounds_error": "Le type d'événement ne peut pas être réservé à ce moment. Pourriez-vous essayer un autre créneau horaire ?",
"request_body_end_time_internal_error": "Erreur interne. Le corps de la demande ne contient pas l'heure de fin",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Voulez-vous vraiment supprimer la configuration {{connectionType}} ? Les membres de votre équipe utilisant la connexion {{connectionType}} ne pourront plus accéder à Cal.com.",
"organizer_timezone": "Fuseau horaire de l'organisateur",
"email_user_cta": "Voir l'invitation",
"accept_invite": "Accepter l'invitation",
"join_now": "Rejoindre maintenant",
"successfully_joined": "Rejoint avec succès",
"email_no_user_invite_heading_subteam": "Vous avez été invité à rejoindre une équipe de l'organisation {{parentTeamName}}",
"email_no_user_invite_heading_org": "Vous avez été invité à rejoindre une organisation {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} vous a invité à rejoindre son équipe sur {{appName}}. {{appName}} est le planificateur d'événements qui vous permet à vous et à votre équipe d'organiser des rendez-vous sans échanges d'e-mails.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Verrouillé par l'administrateur de l'équipe",
"app_not_connected": "Vous n'avez pas connecté de compte {{appName}}.",
"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_other": "Remplacer et notifier {{count}} membres",
"managed_event_dialog_title_one": "Le lien /{{slug}} existe déjà pour {{count}} membre. Voulez-vous le remplacer ?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Lien d'invitation copié",
"invite_link_deleted": "Lien d'invitation supprimé",
"api_key_deleted": "Clé API supprimée",
"delete_api_key_warning": "Cela supprimera définitivement la clé API. Toutes les applications utilisant cette clé perdront immédiatement l'accès à votre compte. Cette action ne peut pas être annulée.",
"invite_link_updated": "Paramètres de lien d'invitation enregistrés",
"link_expires_after": "Les liens ont été définis pour expirer après...",
"one_day": "1 jour",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gérer les rôles et les permissions pour votre organisation",
"learn_more_permissions": "En savoir plus sur le fonctionnement des permissions",
"role_name": "Nom du rôle",
"role_name_required": "Le nom du rôle est requis",
"role_name_placeholder": "ex. Équipe commerciale, Personnel de support",
"role_description": "Description du rôle",
"role_description_placeholder": "Décrivez à quoi sert ce rôle et quelles permissions il nécessite",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rôle créé avec succès",
"role_updated_successfully": "Rôle mis à jour avec succès",
"delete_role": "Supprimer le rôle",
"confirm_delete_role": "Êtes-vous sûr de vouloir supprimer ce rôle ? Cette action ne peut pas être annulée.",
"confirm_delete_role": "Êtes-vous sûr de vouloir supprimer le rôle : {{roleName}} ? Cette action ne peut pas être annulée.",
"role_deleted_successfully": "Rôle supprimé avec succès",
"error_creating_role": "Erreur lors de la création du rôle",
"error_updating_role": "Erreur lors de la mise à jour du rôle",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email de l'organisateur",
"webhook_organizer_timezone": "Fuseau horaire de l'organisateur (par ex., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Locale de l'organisateur (par ex., 'en', 'fr')",
"webhook_organizer_username": "Nom d'utilisateur de l'organisateur (par ex., 'john.doe')",
"webhook_organizer_username_in_org": "Nom d'utilisateur de l'organisateur dans son organisation (par ex., 'john.doe')",
"webhook_attendee_name": "Nom du premier participant",
"webhook_attendee_email": "Email du premier participant",
"webhook_attendee_timezone": "Fuseau horaire du premier participant",
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "לא נמצאו משתמשים זמינים. אפשר לנסות ליצור חלון זמן נוסף?",
"timeslot_unavailable_book_a_new_time": "משבצת הזמן שנבחרה אינה זמינה יותר. <0>אנא בחר/י זמן חדש</0>",
"timeslot_unavailable_short": "תפוס",
"just_connected_description": "התחברת זה עתה. אנא הזמן מהמשבצות למעלה או נסה שוב מאוחר יותר.",
"unavailable_timeslot_title": "חלון זמן לא זמין",
"booking_time_out_of_bounds_error": "לא ניתן לקבוע את סוג האירוע בזמן זה. אולי כדאי לנסות משבצת זמן אחרת?",
"request_body_end_time_internal_error": "שגיאה פנימית. גוף הבקשה לא מכיל זמן סיום",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "אתה בטוח שאתה רוצה למחוק את הגדרות {{connectionType}}? חברי הצוות שלך שמשתמשים ב- {{connectionType}} להזדהות לא יוכלו להשתמש בו להכנס ל- Cal.com.",
"organizer_timezone": "מארגן אזורי זמן",
"email_user_cta": "צפה בהזמנה",
"accept_invite": "אישור הזמנה",
"join_now": "הצטרף עכשיו",
"successfully_joined": "הצטרפת בהצלחה",
"email_no_user_invite_heading_subteam": "הוזמנת להצטרף לצוות בארגון {{parentTeamName}}",
"email_no_user_invite_heading_org": "הוזמנת להצטרף לארגון {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} הזמין אותך להצטרף לצוות שלו ב- {{appName}}. {{appName}} הינה מתזמן זימונים שמאפשר לך ולצוות שלך לזמן פגישות בלי כל הפינג פונג במיילים.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "ננעל על ידי מנהל/ת הצוות",
"app_not_connected": "לא קישרת חשבון {{appName}}.",
"connect_now": "להתחבר עכשיו",
"connect_now_unavailable_tooltip": "התחברות מיידית אינה זמינה כרגע. אנא הזמן מהמשבצות למעלה או נסה שוב מאוחר יותר.",
"managed_event_dialog_confirm_button_one": "החלפה ועדכון של חבר/ה {{count}}",
"managed_event_dialog_confirm_button_other": "החלפה ועדכון של {{count}} חברים",
"managed_event_dialog_title_one": "כתובת ה-URL /‏{{slug}} כבר קיימת עבור חבר/ה {{count}}. האם ברצונך להחליף אותה?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "קישור ההזמנה הועתק",
"invite_link_deleted": "קישור ההזמנה נמחק",
"api_key_deleted": "מפתח API נמחק",
"delete_api_key_warning": "פעולה זו תמחק לצמיתות את מפתח ה-API. כל האפליקציות המשתמשות במפתח זה יאבדו מיד גישה לחשבון שלך. לא ניתן לבטל פעולה זו.",
"invite_link_updated": "הגדרות קישור ההזמנה נשמרו",
"link_expires_after": "הקישורים מוגדרים לפוג לאחר...",
"one_day": "יום אחד",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "ניהול תפקידים והרשאות עבור הארגון שלך",
"learn_more_permissions": "למד עוד על אופן פעולת ההרשאות",
"role_name": "שם תפקיד",
"role_name_required": "שם תפקיד הוא שדה חובה",
"role_name_placeholder": "לדוגמה: צוות מכירות, צוות תמיכה",
"role_description": "תיאור תפקיד",
"role_description_placeholder": "תאר למה מיועד תפקיד זה ואילו הרשאות הוא צריך",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "התפקיד נוצר בהצלחה",
"role_updated_successfully": "התפקיד עודכן בהצלחה",
"delete_role": "מחיקת תפקיד",
"confirm_delete_role": "האם אתה בטוח שברצונך למחוק תפקיד זה? פעולה זו אינה ניתנת לביטול.",
"confirm_delete_role": "האם אתה בטוח שברצונך למחוק את התפקיד: {{roleName}}? פעולה זו אינה ניתנת לביטול.",
"role_deleted_successfully": "התפקיד נמחק בהצלחה",
"error_creating_role": "שגיאה ביצירת תפקיד",
"error_updating_role": "שגיאה בעדכון תפקיד",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "אימייל של המארגן/ת",
"webhook_organizer_timezone": "אזור הזמן של המארגן/ת (לדוגמה, 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "שפה של המארגן/ת (לדוגמה, 'en', 'fr')",
"webhook_organizer_username": "שם המשתמש של המארגן (לדוגמה, 'john.doe')",
"webhook_organizer_username_in_org": "שם המשתמש של המארגן בארגון שלהם (לדוגמה, 'john.doe')",
"webhook_attendee_name": "שם המשתתף/ת הראשון/ה",
"webhook_attendee_email": "אימייל של המשתתף/ת הראשון/ה",
"webhook_attendee_timezone": "אזור הזמן של המשתתף/ת הראשון/ה",
@@ -3750,4 +3757,4 @@
"use_voice": "השתמש בקול",
"current_voice": "קול נוכחי",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nem található elérhető felhasználó. Kipróbálhatnál egy másik időpontot?",
"timeslot_unavailable_book_a_new_time": "A kiválasztott időpont már nem elérhető. <0>Kérjük, válassz egy új időpontot</0>",
"timeslot_unavailable_short": "Foglalt",
"just_connected_description": "Éppen most csatlakoztál. Kérjük, foglalj a fenti időpontok közül, vagy próbáld újra később.",
"unavailable_timeslot_title": "Nem elérhető időpont",
"booking_time_out_of_bounds_error": "Az esemény nem foglalható le ebben az időpontban. Kipróbálnál egy másik időpontot?",
"request_body_end_time_internal_error": "Belső hiba. A kérelem törzse nem tartalmazza a befejezési időt",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Biztosan törli a(z) {{connectionType}} konfigurációt? A(z) {{connectionType}} bejelentkezést használó csapattagok többé nem férhetnek hozzá a Cal.com webhelyhez.",
"organizer_timezone": "Szervező időzónája",
"email_user_cta": "Meghívó megtekintése",
"accept_invite": "Meghívó elfogadása",
"join_now": "Csatlakozás most",
"successfully_joined": "Sikeres csatlakozás",
"email_no_user_invite_heading_subteam": "Meghívást kaptál, hogy csatlakozz a {{parentTeamName}} szervezet egyik csapatához",
"email_no_user_invite_heading_org": "Meghívást kaptál, hogy csatlakozz egy {{appName}} szervezethez",
"email_no_user_invite_subheading": "{{invitedBy}} meghívott téged a csapatába a {{appName}} alkalmazásban. Az {{appName}} az eseményzsonglőr ütemező, amellyel Ön és csapata e-mail-tenisz nélkül is ütemezhet értekezleteket.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Csapatadminisztrátor zárolta",
"app_not_connected": "Nem csatlakoztatott {{appName}}-fiókot.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "A meghívó linkje kimásolva",
"invite_link_deleted": "Meghívó link törölve",
"api_key_deleted": "API-kulcs törölve",
"delete_api_key_warning": "Ez véglegesen törli az API kulcsot. Minden alkalmazás, amely ezt a kulcsot használja, azonnal elveszíti a hozzáférést a fiókodhoz. Ez a művelet nem vonható vissza.",
"invite_link_updated": "A meghívólink beállításai mentve",
"link_expires_after": "A linkek lejárnak...",
"one_day": "1 nap",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Kezelje szervezete szerepköreit és jogosultságait",
"learn_more_permissions": "Tudjon meg többet a jogosultságok működéséről",
"role_name": "Szerepkör neve",
"role_name_required": "A szerepkör neve kötelező",
"role_name_placeholder": "pl. Értékesítési csapat, Ügyfélszolgálat",
"role_description": "Szerepkör leírása",
"role_description_placeholder": "Írja le, mire szolgál ez a szerepkör és milyen jogosultságokra van szüksége",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Szerepkör sikeresen létrehozva",
"role_updated_successfully": "Szerepkör sikeresen frissítve",
"delete_role": "Szerepkör törlése",
"confirm_delete_role": "Biztosan törölni szeretné ezt a szerepkört? Ez a művelet nem vonható vissza.",
"confirm_delete_role": "Biztosan törölni szeretné a következő szerepkört: {{roleName}}? Ez a művelet nem vonható vissza.",
"role_deleted_successfully": "Szerepkör sikeresen törölve",
"error_creating_role": "Hiba történt a szerepkör létrehozásakor",
"error_updating_role": "Hiba történt a szerepkör frissítésekor",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "A szervező e-mail címe",
"webhook_organizer_timezone": "A szervező időzónája (pl. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "A szervező területi beállítása (pl. 'en', 'fr')",
"webhook_organizer_username": "A szervező felhasználóneve (pl. 'john.doe')",
"webhook_organizer_username_in_org": "A szervező felhasználóneve a szervezetén belül (pl. 'john.doe')",
"webhook_attendee_name": "Az első résztvevő neve",
"webhook_attendee_email": "Az első résztvevő e-mail címe",
"webhook_attendee_timezone": "Az első résztvevő időzónája",
@@ -3750,4 +3757,4 @@
"use_voice": "Hang használata",
"current_voice": "Jelenlegi hang",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adja hozzá az új karakterláncokat fent ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nessun utente disponibile. Puoi provare un altro intervallo temporale?",
"timeslot_unavailable_book_a_new_time": "La fascia oraria selezionata non è più disponibile. <0>Seleziona un nuovo orario</0>",
"timeslot_unavailable_short": "Occupato",
"just_connected_description": "Ti sei appena connesso. Prenota dagli slot sopra indicati o riprova più tardi.",
"unavailable_timeslot_title": "Intervallo non disponibile",
"booking_time_out_of_bounds_error": "Non è possibile prenotare questo tipo di evento in questo momento. Potresti provare un altro orario?",
"request_body_end_time_internal_error": "Errore interno. La richiesta non contiene l'ora finale",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Eliminare la configurazione {{connectionType}}? I membri del tuo team che utilizzano l'accesso {{connectionType}} non saranno più in grado di accedere a Cal.com.",
"organizer_timezone": "Fuso orario organizzatore",
"email_user_cta": "Visualizza invito",
"accept_invite": "Accetta Invito",
"join_now": "Unisciti Ora",
"successfully_joined": "Iscrizione completata con successo",
"email_no_user_invite_heading_subteam": "Sei stato invitato a unirti a un team dell'organizzazione {{parentTeamName}}",
"email_no_user_invite_heading_org": "Sei stato invitato a unirti a un'organizzazione {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} ti ha invitato a unirti al suo team su {{appName}}. {{appName}} è uno strumento di pianificazione di eventi che permette al tuo team di pianificare le riunioni senza scambiare decine di e-mail.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Bloccato dall'amministratore del team",
"app_not_connected": "Non è stato connesso un account di {{appName}}.",
"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_other": "Sostituisci e invia notifica a {{count}} membri",
"managed_event_dialog_title_one": "L'URL /{{slug}} esiste già per {{count}} membro. Vuoi sostituirlo?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Link d'invito copiato",
"invite_link_deleted": "Link d'invito eliminato",
"api_key_deleted": "Chiave API eliminata",
"delete_api_key_warning": "Questa azione eliminerà permanentemente la chiave API. Tutte le applicazioni che utilizzano questa chiave perderanno immediatamente l'accesso al tuo account. Questa azione non può essere annullata.",
"invite_link_updated": "Impostazioni link d'invito salvate",
"link_expires_after": "La scadenza dei link è impostata dopo...",
"one_day": "1 giorno",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gestisci ruoli e permessi per la tua organizzazione",
"learn_more_permissions": "Scopri di più su come funzionano i permessi",
"role_name": "Nome del ruolo",
"role_name_required": "Il nome del ruolo è obbligatorio",
"role_name_placeholder": "es. Team vendite, Staff di supporto",
"role_description": "Descrizione del ruolo",
"role_description_placeholder": "Descrivi a cosa serve questo ruolo e quali permessi necessita",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Ruolo creato con successo",
"role_updated_successfully": "Ruolo aggiornato con successo",
"delete_role": "Elimina ruolo",
"confirm_delete_role": "Sei sicuro di voler eliminare questo ruolo? Questa azione non può essere annullata.",
"confirm_delete_role": "Sei sicuro di voler eliminare il ruolo: {{roleName}}? Questa azione non può essere annullata.",
"role_deleted_successfully": "Ruolo eliminato con successo",
"error_creating_role": "Errore durante la creazione del ruolo",
"error_updating_role": "Errore durante l'aggiornamento del ruolo",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email dell'organizzatore",
"webhook_organizer_timezone": "Fuso orario dell'organizzatore (es. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Locale dell'organizzatore (es. 'en', 'fr')",
"webhook_organizer_username": "Nome utente dell'organizzatore (es. 'john.doe')",
"webhook_organizer_username_in_org": "Nome utente dell'organizzatore nella propria organizzazione (es. 'john.doe')",
"webhook_attendee_name": "Nome del primo partecipante",
"webhook_attendee_email": "Email del primo partecipante",
"webhook_attendee_timezone": "Fuso orario del primo partecipante",
@@ -3750,4 +3757,4 @@
"use_voice": "Usa voce",
"current_voice": "Voce attuale",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Aggiungi le tue nuove stringhe qui sopra ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "利用可能なユーザーが見つかりませんでした。別の時間帯をお試しいただけますか?",
"timeslot_unavailable_book_a_new_time": "選択した時間枠はもう利用できません。<0>新しい時間を選択してください</0>",
"timeslot_unavailable_short": "予約済み",
"just_connected_description": "接続が完了しました。上記の空き枠から予約するか、後ほど再試行してください。",
"unavailable_timeslot_title": "利用不可能な時間枠",
"booking_time_out_of_bounds_error": "この時間帯では予約できません。別の時間帯をお試しください。",
"request_body_end_time_internal_error": "内部エラー。リクエストボディに終了時間が含まれていません",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "{{connectionType}} の構成を削除してもよろしいですか?{{connectionType}} でのログインを使用しているチームメンバーは、Cal.com にアクセスできなくなります。",
"organizer_timezone": "主催者のタイムゾーン",
"email_user_cta": "招待を表示",
"accept_invite": "招待を受け入れる",
"join_now": "今すぐ参加",
"successfully_joined": "参加に成功しました",
"email_no_user_invite_heading_subteam": "{{parentTeamName}}組織のチームに参加するよう招待されました",
"email_no_user_invite_heading_org": "{{appName}}組織に参加するよう招待されました",
"email_no_user_invite_subheading": "{{invitedBy}} は {{appName}} のチームに参加するようあなたを招待しました。{{appName}} は、イベント調整スケジューラーです。チームと延々とメールのやりとりをすることなくミーティングのスケジュール設定を行うことができます。",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "チームの管理者によりロックされています",
"app_not_connected": "{{appName}} のアカウントに接続していません。",
"connect_now": "今すぐ接続",
"connect_now_unavailable_tooltip": "今すぐ接続は現在利用できません。上記の空き枠から予約するか、後でもう一度お試しください。",
"managed_event_dialog_confirm_button_one": "{{count}} 人のメンバーを置き換えて通知する",
"managed_event_dialog_confirm_button_other": "{{count}} 人のメンバーを置き換えて通知する",
"managed_event_dialog_title_one": "{{count}} 人のメンバーがすでに URL /{{slug}} を使用しています。置き換えますか?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "招待リンクをコピーしました",
"invite_link_deleted": "招待リンクを削除しました",
"api_key_deleted": "API キーを削除しました",
"delete_api_key_warning": "これによりAPIキーは完全に削除されます。このキーを使用しているアプリケーションはすべて、即座にあなたのアカウントへのアクセスを失います。この操作は元に戻すことができません。",
"invite_link_updated": "招待リンクの設定を保存しました",
"link_expires_after": "リンクの期限切れまで...",
"one_day": "1 日",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "組織の役割と権限を管理する",
"learn_more_permissions": "権限の仕組みについて詳しく学ぶ",
"role_name": "役割名",
"role_name_required": "役割名は必須です",
"role_name_placeholder": "例:営業チーム、サポートスタッフ",
"role_description": "役割の説明",
"role_description_placeholder": "この役割の目的と必要な権限について説明してください",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "役割が正常に作成されました",
"role_updated_successfully": "役割が正常に更新されました",
"delete_role": "役割を削除",
"confirm_delete_role": "この役割を削除してもよろしいですか?この操作は元に戻せません。",
"confirm_delete_role": "役割「{{roleName}}」を削除してもよろしいですか?この操作は元に戻せません。",
"role_deleted_successfully": "役割が正常に削除されました",
"error_creating_role": "役割の作成中にエラーが発生しました",
"error_updating_role": "役割の更新中にエラーが発生しました",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "主催者のメール",
"webhook_organizer_timezone": "主催者のタイムゾーン(例:'America/New_York'、'Asia/Kolkata')",
"webhook_organizer_locale": "主催者のロケール(例:'en'、'fr')",
"webhook_organizer_username": "主催者のユーザー名(例:'john.doe')",
"webhook_organizer_username_in_org": "組織内の主催者のユーザー名(例:'john.doe')",
"webhook_attendee_name": "最初の出席者の名前",
"webhook_attendee_email": "最初の出席者のメール",
"webhook_attendee_timezone": "最初の出席者のタイムゾーン",
@@ -3750,4 +3757,4 @@
"use_voice": "音声を使用",
"current_voice": "現在の音声",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ この上に新しい文字列を追加してください ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "រកមិនឃើញអ្នកប្រើដែលអាចប្រើបានទេ។ តើអ្នកអាចសាកល្បងម៉ោងផ្សេងទៀតបានទេ?",
"timeslot_unavailable_book_a_new_time": "ពេលវេលាដែលបានជ្រើសរើសលែងមានទៀតហើយ។ <0>សូមជ្រើសរើសពេលវេលាថ្មី</0>",
"timeslot_unavailable_short": "បានកក់រួច",
"just_connected_description": "អ្នកទើបតែបានភ្ជាប់។ សូមកក់ពេលវេលាពីខាងលើ ឬព្យាយាមម្តងទៀតនៅពេលក្រោយ។",
"unavailable_timeslot_title": "ពេលវេលាមិនអាចប្រើបាន",
"booking_time_out_of_bounds_error": "មិនអាចកក់ប្រភេទព្រឹត្តិការណ៍នេះនៅពេលនេះបានទេ។ សូមព្យាយាមជ្រើសរើសពេលវេលាផ្សេងទៀត។",
"request_body_end_time_internal_error": "កំហុសខាងក្នុង។ សំណើមិនមានពេលវេលាបញ្ចប់",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "តើអ្នកប្រាកដថាអ្នកចង់លុបការកំណត់រចនាសម្ព័ន្ធ {{connectionType}} មែនទេ? សមាជិកក្រុមរបស់អ្នកដែលប្រើការចូល {{connectionType}} នឹងមិនអាចចូលប្រើ Cal.com បានទៀតទេ។",
"organizer_timezone": "ល្វែងម៉ោងរបស់អ្នករៀបចំ",
"email_user_cta": "មើលការអញ្ជើញ",
"accept_invite": "ទទួលយកការអញ្ជើញ",
"join_now": "ចូលរួមឥឡូវនេះ",
"successfully_joined": "បានចូលរួមដោយជោគជ័យ",
"email_no_user_invite_heading_subteam": "អ្នកត្រូវបានអញ្ជើញឱ្យចូលរួមក្នុងក្រុមនៃអង្គការ {{parentTeamName}}",
"email_no_user_invite_heading_org": "អ្នកត្រូវបានអញ្ជើញឱ្យចូលរួមក្នុងអង្គការ {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} បានអញ្ជើញអ្នកឱ្យចូលរួមជាក្រុមរបស់ពួកគេនៅលើ {{appName}}។ {{appName}} គឺជាកម្មវិធីរៀបចំព្រឹត្តិការណ៍ដែលអនុញ្ញាតឱ្យអ្នកនិងក្រុមរបស់អ្នករៀបចំការជួបប្រជុំដោយមិនចាំបាច់ផ្លាស់ប្តូរអ៊ីមែល។",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "បានចាក់សោដោយអ្នកគ្រប់គ្រងក្រុម",
"app_not_connected": "អ្នកមិនបានភ្ជាប់គណនី {{appName}} ទេ។",
"connect_now": "ភ្ជាប់ឥឡូវនេះ",
"connect_now_unavailable_tooltip": "ការភ្ជាប់ឥឡូវនេះមិនអាចប្រើបានទេ។ សូមកក់ពីពេលវេលាខាងលើ ឬព្យាយាមម្តងទៀតនៅពេលក្រោយ។",
"managed_event_dialog_confirm_button_one": "ជំនួស និងជូនដំណឹងសមាជិក {{count}}",
"managed_event_dialog_confirm_button_other": "ជំនួស និងជូនដំណឹងសមាជិក {{count}} នាក់",
"managed_event_dialog_title_one": "URL /{{slug}} មានស្រាប់សម្រាប់សមាជិក {{count}}។ តើអ្នកចង់ជំនួសវាឬ?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "បានចម្លងតំណភ្ជាប់អញ្ជើញ",
"invite_link_deleted": "បានលុបតំណភ្ជាប់អញ្ជើញ",
"api_key_deleted": "បានលុប API Key",
"delete_api_key_warning": "វានឹងលុបកូនសោ API ជាអចិន្ត្រៃយ៍។ កម្មវិធីណាដែលប្រើកូនសោនេះនឹងបាត់បង់ការចូលប្រើគណនីរបស់អ្នកភ្លាមៗ។ សកម្មភាពនេះមិនអាចត្រឡប់ក្រោយវិញបានទេ។",
"invite_link_updated": "បានរក្សាទុកការកំណត់តំណភ្ជាប់អញ្ជើញ",
"link_expires_after": "តំណភ្ជាប់នឹងផុតកំណត់បន្ទាប់ពី...",
"one_day": "1 ថ្ងៃ",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "គ្រប់គ្រងតួនាទី និងការអនុញ្ញាតសម្រាប់អង្គការរបស់អ្នក",
"learn_more_permissions": "ស្វែងយល់បន្ថែមអំពីរបៀបដែលការអនុញ្ញាតដំណើរការ",
"role_name": "ឈ្មោះតួនាទី",
"role_name_required": "តម្រូវឱ្យមានឈ្មោះតួនាទី",
"role_name_placeholder": "ឧ. ក្រុមលក់, បុគ្គលិកគាំទ្រ",
"role_description": "ការពិពណ៌នាអំពីតួនាទី",
"role_description_placeholder": "ពិពណ៌នាអំពីតួនាទីនេះសម្រាប់អ្វី និងការអនុញ្ញាតអ្វីខ្លះដែលវាត្រូវការ",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "បានបង្កើតតួនាទីដោយជោគជ័យ",
"role_updated_successfully": "បានធ្វើបច្ចុប្បន្នភាពតួនាទីដោយជោគជ័យ",
"delete_role": "លុបតួនាទី",
"confirm_delete_role": "តើអ្នកប្រាកដថាចង់លុបតួនាទីនេះមែនទេ? សកម្មភាពនេះមិនអាចត្រឡប់ក្រោយវិញបានទេ។",
"confirm_delete_role": "តើអ្នកប្រាកដថាចង់លុបតួនាទី៖ {{roleName}} ឬ? សកម្មភាពនេះមិនអាចត្រឡប់ក្រោយវិញបានទេ។",
"role_deleted_successfully": "បានលុបតួនាទីដោយជោគជ័យ",
"error_creating_role": "មានបញ្ហាក្នុងការបង្កើតតួនាទី",
"error_updating_role": "មានបញ្ហាក្នុងការធ្វើបច្ចុប្បន្នភាពតួនាទី",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "អ៊ីមែលរបស់អ្នករៀបចំ",
"webhook_organizer_timezone": "តំបន់ពេលវេលារបស់អ្នករៀបចំ (ឧ. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "ភាសាតំបន់របស់អ្នករៀបចំ (ឧ. 'en', 'fr')",
"webhook_organizer_username": "ឈ្មោះអ្នកប្រើប្រាស់របស់អ្នករៀបចំ (ឧ. 'john.doe')",
"webhook_organizer_username_in_org": "ឈ្មោះអ្នកប្រើប្រាស់របស់អ្នករៀបចំនៅក្នុងអង្គភាពរបស់ពួកគេ (ឧ. 'john.doe')",
"webhook_attendee_name": "ឈ្មោះរបស់អ្នកចូលរួមដំបូង",
"webhook_attendee_email": "អ៊ីមែលរបស់អ្នកចូលរួមដំបូង",
"webhook_attendee_timezone": "តំបន់ពេលវេលារបស់អ្នកចូលរួមដំបូង",
@@ -3750,4 +3757,4 @@
"use_voice": "ប្រើសំឡេង",
"current_voice": "សំឡេងបច្ចុប្បន្ន",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ បន្ថែមខ្សែអក្សរថ្មីរបស់អ្នកនៅខាងលើនេះ ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "사용 가능한 사용자를 찾을 수 없습니다. 다른 시간대를 시도해 보시겠습니까?",
"timeslot_unavailable_book_a_new_time": "선택한 시간대는 더 이상 사용할 수 없습니다. <0>새로운 시간을 선택해 주세요</0>",
"timeslot_unavailable_short": "예약됨",
"just_connected_description": "방금 연결되었습니다. 위의 시간대에서 예약하거나 나중에 다시 시도해 주세요.",
"unavailable_timeslot_title": "사용 불가능한 시간대",
"booking_time_out_of_bounds_error": "이 시간에는 이벤트 유형을 예약할 수 없습니다. 다른 시간대를 시도해보시겠습니까?",
"request_body_end_time_internal_error": "내부 오류. 요청 본문에 종료 시간이 포함되어 있지 않습니다.",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "{{connectionType}} 구성을 삭제하시겠습니까? {{connectionType}} 로그인을 사용하는 팀원은 더 이상 Cal.com에 액세스할 수 없습니다.",
"organizer_timezone": "주최자 시간대",
"email_user_cta": "초대 보기",
"accept_invite": "초대 수락",
"join_now": "지금 참여하기",
"successfully_joined": "성공적으로 참여했습니다",
"email_no_user_invite_heading_subteam": "{{parentTeamName}} 조직의 팀에 초대되었습니다",
"email_no_user_invite_heading_org": "{{appName}} 조직에 초대되었습니다",
"email_no_user_invite_subheading": "{{invitedBy}} 님이 귀하를 {{appName}}에서 자신의 팀에 초대했습니다. {{appName}} 앱은 귀하와 귀하의 팀이 이메일을 주고 받지 않고 회의 일정을 잡을 수 있게 해주는 이벤트 정리 스케줄러입니다.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "팀 관리자에 의해 잠김",
"app_not_connected": "{{appName}} 계정을 연결하지 않았습니다.",
"connect_now": "지금 연결",
"connect_now_unavailable_tooltip": "지금 연결하기는 현재 사용할 수 없습니다. 위의 시간대에서 예약하거나 나중에 다시 시도해 주세요.",
"managed_event_dialog_confirm_button_one": "회원 {{count}}명 바꾸기 및 알림",
"managed_event_dialog_confirm_button_other": "회원 {{count}}명 바꾸기 및 알림",
"managed_event_dialog_title_one": "URL /{{slug}}은(는) {{count}}명의 회원에 대해 이미 존재합니다. 바꾸시겠습니까?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "초대 링크가 복사되었습니다",
"invite_link_deleted": "초대 링크가 삭제되었습니다",
"api_key_deleted": "API 키 삭제됨",
"delete_api_key_warning": "이 작업은 API 키를 영구적으로 삭제합니다. 이 키를 사용하는 모든 애플리케이션은 즉시 계정 접근 권한을 잃게 됩니다. 이 작업은 취소할 수 없습니다.",
"invite_link_updated": "초대 링크 설정이 저장되었습니다",
"link_expires_after": "링크 만료 기한...",
"one_day": "1일",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "조직의 역할 및 권한 관리",
"learn_more_permissions": "권한 작동 방식에 대해 자세히 알아보기",
"role_name": "역할 이름",
"role_name_required": "역할 이름이 필요합니다",
"role_name_placeholder": "예: 영업팀, 지원 직원",
"role_description": "역할 설명",
"role_description_placeholder": "이 역할의 용도와 필요한 권한을 설명하세요",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "역할이 성공적으로 생성되었습니다",
"role_updated_successfully": "역할이 성공적으로 업데이트되었습니다",
"delete_role": "역할 삭제",
"confirm_delete_role": "이 역할을 삭제하시겠습니까? 이 작업은 취소할 수 없습니다.",
"confirm_delete_role": "역할을 삭제하시겠습니까: {{roleName}}. 이 작업은 취소할 수 없습니다.",
"role_deleted_successfully": "역할이 성공적으로 삭제되었습니다",
"error_creating_role": "역할 생성 오류",
"error_updating_role": "역할 업데이트 오류",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "주최자 이메일",
"webhook_organizer_timezone": "주최자 시간대(예: 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "주최자 로케일(예: 'en', 'fr')",
"webhook_organizer_username": "주최자의 사용자 이름(예: 'john.doe')",
"webhook_organizer_username_in_org": "조직 내 주최자의 사용자 이름(예: 'john.doe')",
"webhook_attendee_name": "첫 번째 참석자 이름",
"webhook_attendee_email": "첫 번째 참석자 이메일",
"webhook_attendee_timezone": "첫 번째 참석자 시간대",
@@ -3750,4 +3757,4 @@
"use_voice": "음성 사용",
"current_voice": "현재 음성",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 여기에 새 문자열을 추가하세요 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Geen beschikbare gebruikers gevonden. Kun je een ander tijdslot proberen?",
"timeslot_unavailable_book_a_new_time": "Het geselecteerde tijdslot is niet meer beschikbaar. <0>Selecteer alstublieft een nieuwe tijd</0>",
"timeslot_unavailable_short": "Bezet",
"just_connected_description": "Je hebt zojuist verbinding gemaakt. Boek een tijdslot hierboven of probeer het later opnieuw.",
"unavailable_timeslot_title": "Niet-beschikbaar tijdslot",
"booking_time_out_of_bounds_error": "Het afspraaktype kan op dit moment niet worden geboekt. Zou je een ander tijdslot willen proberen?",
"request_body_end_time_internal_error": "Interne fout. Verzoek bevat geen eindtijd",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Weet u zeker dat u de {{connectionType}}-configuratie wilt verwijderen? Uw teamleden die {{connectionType}}-aanmelding gebruiken hebben niet langer toegang tot Cal.com.",
"organizer_timezone": "Tijdzone organisator",
"email_user_cta": "Uitnodiging weergeven",
"accept_invite": "Uitnodiging accepteren",
"join_now": "Nu deelnemen",
"successfully_joined": "Succesvol deelgenomen",
"email_no_user_invite_heading_subteam": "Je bent uitgenodigd om lid te worden van een team binnen de {{parentTeamName}} organisatie",
"email_no_user_invite_heading_org": "Je bent uitgenodigd om lid te worden van een {{appName}} organisatie",
"email_no_user_invite_subheading": "{{invitedBy}} heeft u uitgenodigd om lid te worden van zijn team op {{appName}}. {{appName}} is de gebeurtenissenplanner die u en uw team in staat stelt vergaderingen te plannen zonder heen en weer te e-mailen.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Vergrendeld door teambeheerder",
"app_not_connected": "U hebt geen {{appName}}-account gekoppeld.",
"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_other": "{{count}} leden vervangen en informeren",
"managed_event_dialog_title_one": "De URL /{{slug}} bestaat al voor {{count}} lid. Wilt u deze vervangen?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Uitnodigingslink gekopieerd",
"invite_link_deleted": "Uitnodigingslink verwijderd",
"api_key_deleted": "API-sleutel verwijderd",
"delete_api_key_warning": "Dit zal de API-sleutel permanent verwijderen. Alle applicaties die deze sleutel gebruiken, verliezen onmiddellijk toegang tot je account. Deze actie kan niet ongedaan worden gemaakt.",
"invite_link_updated": "Uitnodigingslinkinstellingen opgeslagen",
"link_expires_after": "Links verlopen na...",
"one_day": "1 dag",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Beheer rollen en rechten voor je organisatie",
"learn_more_permissions": "Meer informatie over hoe rechten werken",
"role_name": "Rolnaam",
"role_name_required": "Rolnaam is vereist",
"role_name_placeholder": "bijv. Verkoopteam, Supportmedewerkers",
"role_description": "Rolbeschrijving",
"role_description_placeholder": "Beschrijf waarvoor deze rol dient en welke rechten deze nodig heeft",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol succesvol aangemaakt",
"role_updated_successfully": "Rol succesvol bijgewerkt",
"delete_role": "Rol verwijderen",
"confirm_delete_role": "Weet u zeker dat u deze rol wilt verwijderen? Deze actie kan niet ongedaan worden gemaakt.",
"confirm_delete_role": "Weet je zeker dat je de rol wilt verwijderen: {{roleName}}. Deze actie kan niet ongedaan worden gemaakt.",
"role_deleted_successfully": "Rol succesvol verwijderd",
"error_creating_role": "Fout bij het aanmaken van rol",
"error_updating_role": "Fout bij het bijwerken van rol",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "E-mail van de organisator",
"webhook_organizer_timezone": "Tijdzone van de organisator (bijv. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Taalinstelling van de organisator (bijv. 'en', 'fr')",
"webhook_organizer_username": "Gebruikersnaam van de organisator (bijv. 'john.doe')",
"webhook_organizer_username_in_org": "Gebruikersnaam van de organisator in hun organisatie (bijv. 'john.doe')",
"webhook_attendee_name": "Naam van de eerste deelnemer",
"webhook_attendee_email": "E-mail van de eerste deelnemer",
"webhook_attendee_timezone": "Tijdzone van de eerste deelnemer",
@@ -3750,4 +3757,4 @@
"use_voice": "Gebruik stem",
"current_voice": "Huidige stem",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Voeg uw nieuwe strings hierboven toe ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Ingen tilgjengelige brukere funnet. Kan du prøve et annet tidspunkt?",
"timeslot_unavailable_book_a_new_time": "Den valgte tidslukken er ikke lenger tilgjengelig. <0>Vennligst velg en ny tid</0>",
"timeslot_unavailable_short": "Opptatt",
"just_connected_description": "Du har nettopp koblet til. Vennligst bestill fra tilgjengelige tider ovenfor eller prøv igjen senere.",
"unavailable_timeslot_title": "Utilgjengelig tidsluke",
"booking_time_out_of_bounds_error": "Avtaletypen kan ikke bestilles på dette tidspunktet. Kan du prøve et annet tidspunkt?",
"request_body_end_time_internal_error": "Intern feil. Forespørselens kropp inneholder ikke sluttid",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Er du sikker på at du vil slette {{connectionType}}-konfigurasjonen? Teammedlemmene dine som bruker {{connectionType}}-innlogging vil ikke lenger kunne få tilgang til Cal.com.",
"organizer_timezone": "Tidssone for arrangør",
"email_user_cta": "Se invitasjon",
"accept_invite": "Aksepter invitasjon",
"join_now": "Bli med nå",
"successfully_joined": "Vellykket tilslutning",
"email_no_user_invite_heading_subteam": "Du har blitt invitert til å bli med i et team i {{parentTeamName}}-organisasjonen",
"email_no_user_invite_heading_org": "Du har blitt invitert til å bli med i en {{appName}}-organisasjon",
"email_no_user_invite_subheading": "{{invitedBy}} har invitert deg til å bli med i teamet deres på {{appName}}. {{appName}} er den hendelsesorganiserende planleggeren som gjør det mulig for deg og teamet ditt å planlegge møter uten e-postfrem og tilbake.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Låst av teamadministrator",
"app_not_connected": "Du har ikke koblet til en {{appName}}-konto.",
"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_other": "Erstatt og varsle {{count}} medlemmer",
"managed_event_dialog_title_one": "URL-en /{{slug}} eksisterer allerede for {{count}} medlem. Vil du erstatte den?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Invitasjonslenke kopiert",
"invite_link_deleted": "Invitasjonslenke slettet",
"api_key_deleted": "API-nøkkel slettet",
"delete_api_key_warning": "Dette vil permanent slette API-nøkkelen. Alle applikasjoner som bruker denne nøkkelen vil umiddelbart miste tilgang til kontoen din. Denne handlingen kan ikke angres.",
"invite_link_updated": "Lenkeinnstillinger lagret",
"link_expires_after": "Lenker satt til å utløpe etter...",
"one_day": "1 dag",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Administrer roller og tillatelser for organisasjonen din",
"learn_more_permissions": "Lær mer om hvordan tillatelser fungerer",
"role_name": "Rollenavn",
"role_name_required": "Rollenavn er påkrevd",
"role_name_placeholder": "f.eks. Salgsteam, Kundestøtte",
"role_description": "Rollebeskrivelse",
"role_description_placeholder": "Beskriv hva denne rollen er for og hvilke tillatelser den trenger",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rolle opprettet",
"role_updated_successfully": "Rolle oppdatert",
"delete_role": "Slett rolle",
"confirm_delete_role": "Er du sikker på at du vil slette denne rollen? Denne handlingen kan ikke angres.",
"confirm_delete_role": "Er du sikker på at du vil slette rollen: {{roleName}}? Denne handlingen kan ikke angres.",
"role_deleted_successfully": "Rolle slettet",
"error_creating_role": "Feil ved oppretting av rolle",
"error_updating_role": "Feil ved oppdatering av rolle",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "E-post til organisator",
"webhook_organizer_timezone": "Tidssone for organisator (f.eks. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Språk for organisator (f.eks. 'en', 'fr')",
"webhook_organizer_username": "Brukernavn til arrangøren (f.eks. 'john.doe')",
"webhook_organizer_username_in_org": "Brukernavn til arrangøren i deres organisasjon (f.eks. 'john.doe')",
"webhook_attendee_name": "Navn på første deltaker",
"webhook_attendee_email": "E-post til første deltaker",
"webhook_attendee_timezone": "Tidssone for første deltaker",
@@ -3750,4 +3757,4 @@
"use_voice": "Bruk stemme",
"current_voice": "Nåværende stemme",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Legg til dine nye strenger over her ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nie znaleziono dostępnych użytkowników. Czy możesz spróbować innego terminu?",
"timeslot_unavailable_book_a_new_time": "Wybrany termin nie jest już dostępny. <0>Proszę wybrać nowy termin</0>",
"timeslot_unavailable_short": "Zajęte",
"just_connected_description": "Właśnie nawiązano połączenie. Proszę zarezerwować termin z dostępnych powyżej lub spróbować ponownie później.",
"unavailable_timeslot_title": "Niedostępny przedział czasowy",
"booking_time_out_of_bounds_error": "Ten typ wydarzenia nie może być zarezerwowany w tym czasie. Czy możesz spróbować inny termin?",
"request_body_end_time_internal_error": "Błąd wewnętrzny. Treść żądania nie zawiera czasu zakończenia",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Czy na pewno chcesz usunąć konfigurację {{connectionType}}? Członkowie zespołu, którzy używają logowania {{connectionType}} nie będą już mogli uzyskać dostępu do Cal.com.",
"organizer_timezone": "Strefa czasowa organizatora",
"email_user_cta": "Wyświetl zaproszenie",
"accept_invite": "Akceptuj zaproszenie",
"join_now": "Dołącz teraz",
"successfully_joined": "Pomyślnie dołączono",
"email_no_user_invite_heading_subteam": "Zostałeś/zostałaś zaproszony/a do dołączenia do zespołu organizacji {{parentTeamName}}.",
"email_no_user_invite_heading_org": "Zostałeś/zostałaś zaproszony/a do dołączenia do organizacji {{appName}}.",
"email_no_user_invite_subheading": "Użytkownik {{invitedBy}} zaprasza Cię do dołączenia do jego zespołu w aplikacji {{appName}}. Aplikacja {{appName}} to terminarz do planowania wydarzeń, który umożliwi Tobie i Twojemu zespołowi planowanie spotkań bez czasochłonnej wymiany wiadomości e-mail.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Zablokowane przez administratora zespołu",
"app_not_connected": "Nie podłączono konta {{appName}}.",
"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_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ć?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Skopiowano link zaproszenia",
"invite_link_deleted": "Usunięto link zaproszenia",
"api_key_deleted": "Klucz API został usunięty",
"delete_api_key_warning": "Spowoduje to trwałe usunięcie klucza API. Wszystkie aplikacje korzystające z tego klucza natychmiast stracą dostęp do Twojego konta. Tej operacji nie można cofnąć.",
"invite_link_updated": "Zapisano ustawienia linku zaproszenia",
"link_expires_after": "Linki wygasną po...",
"one_day": "1 dzień",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Zarządzaj rolami i uprawnieniami w swojej organizacji",
"learn_more_permissions": "Dowiedz się więcej o działaniu uprawnień",
"role_name": "Nazwa roli",
"role_name_required": "Nazwa roli jest wymagana",
"role_name_placeholder": "np. Zespół sprzedaży, Obsługa klienta",
"role_description": "Opis roli",
"role_description_placeholder": "Opisz, do czego służy ta rola i jakie uprawnienia są jej potrzebne",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rola została pomyślnie utworzona",
"role_updated_successfully": "Rola została pomyślnie zaktualizowana",
"delete_role": "Usuń rolę",
"confirm_delete_role": "Czy na pewno chcesz usunąć tę rolę? Tej operacji nie można cofnąć.",
"confirm_delete_role": "Czy na pewno chcesz usunąć rolę: {{roleName}}? Tej akcji nie można cofnąć.",
"role_deleted_successfully": "Rola została pomyślnie usunięta",
"error_creating_role": "Błąd podczas tworzenia roli",
"error_updating_role": "Błąd podczas aktualizowania roli",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email organizatora",
"webhook_organizer_timezone": "Strefa czasowa organizatora (np. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Język organizatora (np. 'en', 'fr')",
"webhook_organizer_username": "Nazwa użytkownika organizatora (np. 'john.doe')",
"webhook_organizer_username_in_org": "Nazwa użytkownika organizatora w jego organizacji (np. 'john.doe')",
"webhook_attendee_name": "Imię pierwszego uczestnika",
"webhook_attendee_email": "Email pierwszego uczestnika",
"webhook_attendee_timezone": "Strefa czasowa pierwszego uczestnika",
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nenhum usuário disponível foi encontrado. Você poderia tentar outra faixa horária?",
"timeslot_unavailable_book_a_new_time": "O horário selecionado não está mais disponível. <0>Por favor, selecione um novo horário</0>",
"timeslot_unavailable_short": "Ocupado",
"just_connected_description": "Você acabou de se conectar. Por favor, reserve um dos horários acima ou tente novamente mais tarde.",
"unavailable_timeslot_title": "Horário indisponível",
"booking_time_out_of_bounds_error": "O tipo de evento não pode ser agendado neste horário. Você poderia tentar outro horário?",
"request_body_end_time_internal_error": "Erro interno. O corpo da solicitação não contém a hora final",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Você tem certeza que deseja remover a configuração de {{connectionType}}? Os membros do seu time que utilizam {{connectionType}} para fazer login não conseguirão acessar o Cal.com.",
"organizer_timezone": "Fuso horário do organizador",
"email_user_cta": "Ver convite",
"accept_invite": "Aceitar convite",
"join_now": "Participar agora",
"successfully_joined": "Participação bem-sucedida",
"email_no_user_invite_heading_subteam": "Você foi convidado para participar de uma equipe da organização {{parentTeamName}}",
"email_no_user_invite_heading_org": "Você foi convidado para participar de uma organização {{appName}}",
"email_no_user_invite_subheading": "Você recebeu um convite de {{invitedBy}} para ingressar em sua equipe em {{appName}}. {{appName}} é um agendador que concilia eventos e permite que sua equipe agende reuniões sem precisar trocar e-mails.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Bloqueado pelo administrador da equipe",
"app_not_connected": "Você não conectou uma conta do {{appName}}.",
"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_other": "Substituir e notificar {{count}} membros",
"managed_event_dialog_title_one": "O url/{{slug}} já existe para {{count}} membro. Deseja substituir?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Link de convite copiado",
"invite_link_deleted": "Link de convite excluído",
"api_key_deleted": "Chave da API excluída",
"delete_api_key_warning": "Isso excluirá permanentemente a chave de API. Qualquer aplicativo que use esta chave perderá imediatamente o acesso à sua conta. Esta ação não pode ser desfeita.",
"invite_link_updated": "Configurações do link de convite salvas",
"link_expires_after": "Link configurado para expirar após...",
"one_day": "1 dia",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gerencie funções e permissões para sua organização",
"learn_more_permissions": "Saiba mais sobre como as permissões funcionam",
"role_name": "Nome da função",
"role_name_required": "Nome da função é obrigatório",
"role_name_placeholder": "ex. Equipe de vendas, Equipe de suporte",
"role_description": "Descrição da função",
"role_description_placeholder": "Descreva para que serve esta função e quais permissões ela precisa",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Função criada com sucesso",
"role_updated_successfully": "Função atualizada com sucesso",
"delete_role": "Excluir função",
"confirm_delete_role": "Tem certeza que deseja excluir esta função? Esta ação não pode ser desfeita.",
"confirm_delete_role": "Tem certeza que deseja excluir a função: {{roleName}}? Esta ação não pode ser desfeita.",
"role_deleted_successfully": "Função excluída com sucesso",
"error_creating_role": "Erro ao criar função",
"error_updating_role": "Erro ao atualizar função",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email do organizador",
"webhook_organizer_timezone": "Fuso horário do organizador (ex.: 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Idioma do organizador (ex.: 'en', 'fr')",
"webhook_organizer_username": "Nome de usuário do organizador (ex.: 'john.doe')",
"webhook_organizer_username_in_org": "Nome de usuário do organizador na organização (ex.: 'john.doe')",
"webhook_attendee_name": "Nome do primeiro participante",
"webhook_attendee_email": "Email do primeiro participante",
"webhook_attendee_timezone": "Fuso horário do primeiro participante",
@@ -3750,4 +3757,4 @@
"use_voice": "Usar voz",
"current_voice": "Voz atual",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adicione suas novas strings aqui em cima ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nenhum usuário disponível foi encontrado. Você poderia tentar outro horário?",
"timeslot_unavailable_book_a_new_time": "O horário selecionado não está mais disponível. <0>Por favor, selecione um novo horário</0>",
"timeslot_unavailable_short": "Ocupado",
"just_connected_description": "Você acabou de se conectar. Por favor, reserve um dos horários acima ou tente novamente mais tarde.",
"unavailable_timeslot_title": "Horário indisponível",
"booking_time_out_of_bounds_error": "O evento não pode ser agendado neste horário. Você poderia tentar outro horário?",
"request_body_end_time_internal_error": "Erro interno. O corpo da solicitação não contém o horário final",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Tem a certeza que pretende eliminar a configuração {{connectionType}}? Os membros da sua equipa que utilizem {{connectionType}} para o início de sessão deixarão de poder aceder a Cal.com.",
"organizer_timezone": "Fuso horário do organizador",
"email_user_cta": "Ver convite",
"accept_invite": "Aceitar convite",
"join_now": "Juntar-se agora",
"successfully_joined": "Juntou-se com sucesso",
"email_no_user_invite_heading_subteam": "Você foi convidado para se juntar a uma equipe da organização {{parentTeamName}}",
"email_no_user_invite_heading_org": "Você foi convidado para se juntar a uma organização {{appName}}",
"email_no_user_invite_subheading": "Recebeu um convite de {{invitedBy}} para fazer parte da respetiva equipa em {{appName}}. {{appName}} é um ferramenta de conciliação e agendamento de eventos que lhe permite a si e à sua equipa agendar reuniões sem o pingue-pongue de e-mails.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Bloqueado pelo administrador da equipa",
"app_not_connected": "Não associou uma conta {{appName}}.",
"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_other": "Substituir e notificar {{count}} membros",
"managed_event_dialog_title_one": "A URL /{{slug}} já existe para {{count}} membro. Pretende substituir a mesma?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Ligação de convite copiada",
"invite_link_deleted": "Ligação do convite copiada",
"api_key_deleted": "Chave de API removida",
"delete_api_key_warning": "Isso excluirá permanentemente a chave de API. Qualquer aplicação que use esta chave perderá imediatamente o acesso à sua conta. Esta ação não pode ser desfeita.",
"invite_link_updated": "Definições da ligação de convite guardadas",
"link_expires_after": "Ligações definidas para expirar depois...",
"one_day": "1 dia",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gerencie funções e permissões para sua organização",
"learn_more_permissions": "Saiba mais sobre como as permissões funcionam",
"role_name": "Nome da função",
"role_name_required": "Nome da função é obrigatório",
"role_name_placeholder": "ex. Equipe de vendas, Equipe de suporte",
"role_description": "Descrição da função",
"role_description_placeholder": "Descreva para que serve esta função e quais permissões ela precisa",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Função criada com sucesso",
"role_updated_successfully": "Função atualizada com sucesso",
"delete_role": "Eliminar função",
"confirm_delete_role": "Tem a certeza que deseja eliminar esta função? Esta ação não pode ser desfeita.",
"confirm_delete_role": "Tem certeza que deseja excluir a função: {{roleName}}? Esta ação não pode ser desfeita.",
"role_deleted_successfully": "Função eliminada com sucesso",
"error_creating_role": "Erro ao criar função",
"error_updating_role": "Erro ao atualizar função",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email do organizador",
"webhook_organizer_timezone": "Fuso horário do organizador (ex., 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Idioma do organizador (ex., 'en', 'fr')",
"webhook_organizer_username": "Nome de usuário do organizador (ex.: 'john.doe')",
"webhook_organizer_username_in_org": "Nome de usuário do organizador na organização (ex.: 'john.doe')",
"webhook_attendee_name": "Nome do primeiro participante",
"webhook_attendee_email": "Email do primeiro participante",
"webhook_attendee_timezone": "Fuso horário do primeiro participante",
@@ -3750,4 +3757,4 @@
"use_voice": "Usar Voz",
"current_voice": "Voz Atual",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nu au fost găsiți utilizatori disponibili. Poți încerca un alt interval orar?",
"timeslot_unavailable_book_a_new_time": "Intervalul orar selectat nu mai este disponibil. <0>Te rugăm să selectezi o nouă oră</0>",
"timeslot_unavailable_short": "Ocupat",
"just_connected_description": "Tocmai v-ați conectat. Vă rugăm să rezervați din intervalele de mai sus sau să încercați din nou mai târziu.",
"unavailable_timeslot_title": "Interval de timp indisponibil",
"booking_time_out_of_bounds_error": "Acest tip de eveniment nu poate fi programat în acest moment. Ai putea încerca alt interval orar?",
"request_body_end_time_internal_error": "Eroare internă. Corpul cererii nu conține ora de sfârșit",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Sigur doriți să ștergeți configurația {{connectionType}}? Membrii echipei dvs. care utilizează conectarea prin {{connectionType}} nu vor mai putea accesa Cal.com.",
"organizer_timezone": "Fusul orar al organizatorului",
"email_user_cta": "Vizualizare invitație",
"accept_invite": "Acceptați invitația",
"join_now": "Alăturați-vă acum",
"successfully_joined": "V-ați alăturat cu succes",
"email_no_user_invite_heading_subteam": "Ai fost invitat să te alături unei echipe din organizația {{parentTeamName}}.",
"email_no_user_invite_heading_org": "Ai fost invitat să te alături unei organizații {{appName}}.",
"email_no_user_invite_subheading": "{{invitedBy}} v-a invitat să faceți parte din echipa sa de pe {{appName}}. {{appName}} este un instrument de planificare a evenimentelor, care vă permite dvs. și echipei dvs. să programați ședințe fără a face ping-pong prin e-mail.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Blocat de administratorul echipei",
"app_not_connected": "Nu ați conectat un cont {{appName}}.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Link de invitație copiat",
"invite_link_deleted": "Link de invitație șters",
"api_key_deleted": "Cheie API ștearsă",
"delete_api_key_warning": "Aceasta va șterge permanent cheia API. Orice aplicații care utilizează această cheie vor pierde imediat accesul la contul dumneavoastră. Această acțiune nu poate fi anulată.",
"invite_link_updated": "Setări link de invitație salvate",
"link_expires_after": "Linkurile vor expira după...",
"one_day": "1 zi",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Gestionează rolurile și permisiunile pentru organizația ta",
"learn_more_permissions": "Află mai multe despre cum funcționează permisiunile",
"role_name": "Nume rol",
"role_name_required": "Numele rolului este obligatoriu",
"role_name_placeholder": "ex. Echipa de vânzări, Personal de suport",
"role_description": "Descriere rol",
"role_description_placeholder": "Descrie scopul acestui rol și ce permisiuni necesită",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol creat cu succes",
"role_updated_successfully": "Rol actualizat cu succes",
"delete_role": "Ștergeți rolul",
"confirm_delete_role": "Sigur doriți să ștergeți acest rol? Această acțiune nu poate fi anulată.",
"confirm_delete_role": "Sigur doriți să ștergeți rolul: {{roleName}}? Această acțiune nu poate fi anulată.",
"role_deleted_successfully": "Rol șters cu succes",
"error_creating_role": "Eroare la crearea rolului",
"error_updating_role": "Eroare la actualizarea rolului",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Emailul organizatorului",
"webhook_organizer_timezone": "Fusul orar al organizatorului (de exemplu, 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Limba organizatorului (de exemplu, 'en', 'fr')",
"webhook_organizer_username": "Numele de utilizator al organizatorului (de exemplu, 'john.doe')",
"webhook_organizer_username_in_org": "Numele de utilizator al organizatorului în organizația sa (de exemplu, 'john.doe')",
"webhook_attendee_name": "Numele primului participant",
"webhook_attendee_email": "Emailul primului participant",
"webhook_attendee_timezone": "Fusul orar al primului participant",
@@ -3750,4 +3757,4 @@
"use_voice": "Folosește vocea",
"current_voice": "Vocea curentă",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adăugați stringurile noi deasupra acestui rând ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Не найдено доступных пользователей. Попробуйте выбрать другое время.",
"timeslot_unavailable_book_a_new_time": "Выбранный временной интервал больше недоступен. <0>Пожалуйста, выберите новое время</0>",
"timeslot_unavailable_short": "Занято",
"just_connected_description": "Вы только что подключились. Пожалуйста, выберите одно из доступных временных окон выше или попробуйте позже.",
"unavailable_timeslot_title": "Недоступный интервал времени",
"booking_time_out_of_bounds_error": "В это время запись на мероприятие невозможна. Попробуйте выбрать другое время?",
"request_body_end_time_internal_error": "Внутренняя ошибка. В теле запроса отсутствует время окончания",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Удалить конфигурацию {{connectionType}}? Участники команды, входящие в Cal.com через {{connectionType}}, больше не смогут получить к нему доступ.",
"organizer_timezone": "Часовой пояс организатора",
"email_user_cta": "Посмотреть приглашение",
"accept_invite": "Принять приглашение",
"join_now": "Присоединиться сейчас",
"successfully_joined": "Успешно присоединились",
"email_no_user_invite_heading_subteam": "Вас пригласили присоединиться к команде организации {{parentTeamName}}.",
"email_no_user_invite_heading_org": "Вас пригласили присоединиться к организации {{appName}}.",
"email_no_user_invite_subheading": "{{invitedBy}} пригласил(-а) вас в команду в {{appName}}. {{appName}} — это гибкий планировщик событий, с помощью которого пользователи и целые команды могут планировать встречи без утомительной переписки по электронной почте.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Заблокировано администратором команды",
"app_not_connected": "Вы не подключили аккаунт {{appName}}.",
"connect_now": "Подключить",
"connect_now_unavailable_tooltip": "Функция \"Подключиться сейчас\" в данный момент недоступна. Пожалуйста, выберите время из доступных выше слотов или попробуйте позже.",
"managed_event_dialog_confirm_button_one": "Заменить и уведомить {{count}} участника",
"managed_event_dialog_confirm_button_other": "Заменить и уведомить участников ({{count}})",
"managed_event_dialog_title_one": "URL-адрес /{{slug}} уже используется {{count}} участником. Заменить?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Ссылка для приглашения скопирована",
"invite_link_deleted": "Ссылка для приглашения удалена",
"api_key_deleted": "Ключ API удален",
"delete_api_key_warning": "Это действие навсегда удалит API-ключ. Все приложения, использующие этот ключ, немедленно потеряют доступ к вашему аккаунту. Это действие нельзя отменить.",
"invite_link_updated": "Настройки ссылки для приглашения сохранены",
"link_expires_after": "Срок действия ссылок истекает через...",
"one_day": "1 день",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Управляйте ролями и разрешениями для вашей организации",
"learn_more_permissions": "Узнайте больше о том, как работают разрешения",
"role_name": "Название роли",
"role_name_required": "Название роли обязательно для заполнения",
"role_name_placeholder": "например, Отдел продаж, Служба поддержки",
"role_description": "Описание роли",
"role_description_placeholder": "Опишите, для чего предназначена эта роль и какие разрешения ей нужны",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Роль успешно создана",
"role_updated_successfully": "Роль успешно обновлена",
"delete_role": "Удалить роль",
"confirm_delete_role": "Вы уверены, что хотите удалить эту роль? Это действие нельзя будет отменить.",
"confirm_delete_role": "Вы уверены, что хотите удалить роль: {{roleName}}? Это действие нельзя будет отменить.",
"role_deleted_successfully": "Роль успешно удалена",
"error_creating_role": "Ошибка при создании роли",
"error_updating_role": "Ошибка при обновлении роли",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Электронная почта организатора",
"webhook_organizer_timezone": "Часовой пояс организатора (например, 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Локаль организатора (например, 'en', 'fr')",
"webhook_organizer_username": "Имя пользователя организатора (например, 'john.doe')",
"webhook_organizer_username_in_org": "Имя пользователя организатора в их организации (например, 'john.doe')",
"webhook_attendee_name": "Имя первого участника",
"webhook_attendee_email": "Электронная почта первого участника",
"webhook_attendee_timezone": "Часовой пояс первого участника",
@@ -3750,4 +3757,4 @@
"use_voice": "Использовать голос",
"current_voice": "Текущий голос",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавьте строки выше ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nenašli sa žiadni dostupní používatelia. Mohli by ste skúsiť iný časový úsek?",
"timeslot_unavailable_book_a_new_time": "Vybraný časový úsek už nie je k dispozícii. <0>Prosím, vyberte si nový čas</0>",
"timeslot_unavailable_short": "Obsadené",
"just_connected_description": "Práve ste sa pripojili. Prosím, rezervujte si termín z vyššie uvedených časových slotov alebo to skúste neskôr.",
"unavailable_timeslot_title": "Nedostupný časový slot",
"booking_time_out_of_bounds_error": "Tento typ udalosti nie je možné v tomto čase rezervovať. Mohli by ste skúsiť iný termín?",
"request_body_end_time_internal_error": "Interná chyba. Telo žiadosti neobsahuje koncový čas",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Ste si istý, že chcete odstrániť {{connectionType}} konfiguráciu? Vaši členovia tímu, ktorí používajú {{connectionType}} prihlásenie, už nebudú mať prístup k Cal.com.",
"organizer_timezone": "Časové pásmo organizátora",
"email_user_cta": "Zobraziť pozvánku",
"accept_invite": "Prijať pozvánku",
"join_now": "Pripojiť sa teraz",
"successfully_joined": "Úspešne pripojené",
"email_no_user_invite_heading_subteam": "Boli ste pozvaní do tímu organizácie {{parentTeamName}}",
"email_no_user_invite_heading_org": "Boli ste pozvaní do organizácie {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} vás pozval/a do svojho tímu v {{appName}}. {{appName}} je plánovač stretnutí, ktorý vám a vášmu tímu umožňuje plánovať stretnutia bez nekonečnej výmeny emailov.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Zamknuté tímovým administrátorom",
"app_not_connected": "Nemáte pripojený účet {{appName}}.",
"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_other": "Nahradiť a upozorniť {{count}} členov",
"managed_event_dialog_title_one": "URL /{{slug}} už existuje pre {{count}} člena. Chcete ho nahradiť?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Odkaz na pozvánku skopírovaný",
"invite_link_deleted": "Odkaz na pozvánku odstránený",
"api_key_deleted": "API kľúč odstránený",
"delete_api_key_warning": "Týmto natrvalo odstránite API kľúč. Všetky aplikácie používajúce tento kľúč okamžite stratia prístup k vášmu účtu. Túto akciu nie je možné vrátiť späť.",
"invite_link_updated": "Nastavenia odkazu na pozvánku uložené",
"link_expires_after": "Odkazy nastavené na vypršanie po...",
"one_day": "1 deň",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Spravujte role a oprávnenia pre vašu organizáciu",
"learn_more_permissions": "Dozvedieť sa viac o tom, ako fungujú oprávnenia",
"role_name": "Názov role",
"role_name_required": "Názov role je povinný",
"role_name_placeholder": "napr. Obchodný tím, Podporný personál",
"role_description": "Popis role",
"role_description_placeholder": "Popíšte, na čo je táto rola určená a aké oprávnenia potrebuje",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rola bola úspešne vytvorená",
"role_updated_successfully": "Rola bola úspešne aktualizovaná",
"delete_role": "Vymazať rolu",
"confirm_delete_role": "Naozaj chcete vymazať túto rolu? Túto akciu nie je možné vrátiť späť.",
"confirm_delete_role": "Naozaj chcete odstrániť rolu: {{roleName}}? Túto akciu nemožno vrátiť späť.",
"role_deleted_successfully": "Rola bola úspešne vymazaná",
"error_creating_role": "Chyba pri vytváraní role",
"error_updating_role": "Chyba pri aktualizácii role",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email organizátora",
"webhook_organizer_timezone": "Časové pásmo organizátora (napr. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Lokalizácia organizátora (napr. 'en', 'fr')",
"webhook_organizer_username": "Používateľské meno organizátora (napr. 'john.doe')",
"webhook_organizer_username_in_org": "Používateľské meno organizátora v jeho organizácii (napr. 'john.doe')",
"webhook_attendee_name": "Meno prvého účastníka",
"webhook_attendee_email": "Email prvého účastníka",
"webhook_attendee_timezone": "Časové pásmo prvého účastníka",
@@ -3750,4 +3757,4 @@
"use_voice": "Použiť hlas",
"current_voice": "Aktuálny hlas",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Pridajte svoje nové reťazce nad túto líniu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Nema dostupnih korisnika. Možete li pokušati drugi termin?",
"timeslot_unavailable_book_a_new_time": "Izabrani termin više nije dostupan. <0>Molimo izaberite novo vreme</0>",
"timeslot_unavailable_short": "Zauzeto",
"just_connected_description": "Upravo ste se povezali. Molimo rezervišite neki od gornjih termina ili pokušajte kasnije.",
"unavailable_timeslot_title": "Nedostupan termin",
"booking_time_out_of_bounds_error": "Ovaj tip događaja nije moguće zakazati u ovo vreme. Da li biste mogli da probate drugi termin?",
"request_body_end_time_internal_error": "Interna greška. Zahtev ne sadrži vreme završetka",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Da li ste sigurni da želite da izbrišete {{connectionType}} konfiguraciju? Članovi vašeg tima koji koriste {{connectionType}} prijavljivanje više neće moći da pristupe Cal.com-u.",
"organizer_timezone": "Vremenska zona organizatora",
"email_user_cta": "Prikaži pozivnicu",
"accept_invite": "Prihvati poziv",
"join_now": "Pridruži se sada",
"successfully_joined": "Uspešno ste se pridružili",
"email_no_user_invite_heading_subteam": "Pozvani ste da se pridružite timu organizacije {{parentTeamName}}",
"email_no_user_invite_heading_org": "Pozvani ste da se pridružite {{appName}} organizaciji",
"email_no_user_invite_subheading": "{{invitedBy}} vas je pozvao/la da se pridružite njihovom timu u {{appName}}. {{appName}} je planer za koordinaciju događaja koji omogućava vama i vašem timu da zakazujete sastanke bez dopisivanja imejlovima.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Zaključao administrator tima",
"app_not_connected": "Niste povezali {{appName}} nalog.",
"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_other": "Zameni i obavesti {{count}} članova",
"managed_event_dialog_title_one": "URL /{{slug}} već postoji za {{count}} člana. Da li želite da ga zamenite?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Link pozivnice je kopiran",
"invite_link_deleted": "Link pozivnice je izbrisan",
"api_key_deleted": "API ključ je obrisan",
"delete_api_key_warning": "Ovo će trajno izbrisati API ključ. Sve aplikacije koje koriste ovaj ključ će odmah izgubiti pristup vašem nalogu. Ova radnja se ne može poništiti.",
"invite_link_updated": "Podešavanja linka pozivnice su sačuvana",
"link_expires_after": "Linkovi su podešeni da isteknu za...",
"one_day": "1 dan",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Upravljajte ulogama i dozvolama za vašu organizaciju",
"learn_more_permissions": "Saznajte više o tome kako funkcionišu dozvole",
"role_name": "Naziv uloge",
"role_name_required": "Naziv uloge je obavezan",
"role_name_placeholder": "npr. Prodajni tim, Osoblje za podršku",
"role_description": "Opis uloge",
"role_description_placeholder": "Opišite čemu služi ova uloga i koje dozvole su joj potrebne",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Uloga uspešno kreirana",
"role_updated_successfully": "Uloga uspešno ažurirana",
"delete_role": "Izbriši ulogu",
"confirm_delete_role": "Da li ste sigurni da želite da izbrišete ovu ulogu? Ova akcija se ne može poništiti.",
"confirm_delete_role": "Da li ste sigurni da želite da izbrišete ulogu: {{roleName}}. Ova radnja se ne može poništiti.",
"role_deleted_successfully": "Uloga uspešno izbrisana",
"error_creating_role": "Greška pri kreiranju uloge",
"error_updating_role": "Greška pri ažuriranju uloge",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email organizatora",
"webhook_organizer_timezone": "Vremenska zona organizatora (npr. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Lokalizacija organizatora (npr. 'en', 'fr')",
"webhook_organizer_username": "Korisničko ime organizatora (npr. 'john.doe')",
"webhook_organizer_username_in_org": "Korisničko ime organizatora u njihovoj organizaciji (npr. 'john.doe')",
"webhook_attendee_name": "Ime prvog učesnika",
"webhook_attendee_email": "Email prvog učesnika",
"webhook_attendee_timezone": "Vremenska zona prvog učesnika",
@@ -3750,4 +3757,4 @@
"use_voice": "Koristi glas",
"current_voice": "Trenutni glas",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodajte svoje nove stringove iznad ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Inga tillgängliga användare hittades. Kan du försöka en annan tidslucka?",
"timeslot_unavailable_book_a_new_time": "Den valda tidsluckan är inte längre tillgänglig. <0>Vänligen välj en ny tid</0>",
"timeslot_unavailable_short": "Upptagen",
"just_connected_description": "Du har precis anslutit. Vänligen boka från de tillgängliga tiderna ovan eller försök igen senare.",
"unavailable_timeslot_title": "Otillgänglig tidslucka",
"booking_time_out_of_bounds_error": "Händelsetypen kan inte bokas vid denna tidpunkt. Kan du prova en annan tid?",
"request_body_end_time_internal_error": "Internt fel. Begäran innehåller inte sluttid",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Vill du verkligen ta bort {{connectionType}}-konfigurationen? Dina teammedlemmar som använder {{connectionType}}-inloggning kan inte längre komma åt Cal.com.",
"organizer_timezone": "Organisatörens tidszon",
"email_user_cta": "Visa inbjudan",
"accept_invite": "Acceptera inbjudan",
"join_now": "Gå med nu",
"successfully_joined": "Gick med framgångsrikt",
"email_no_user_invite_heading_subteam": "Du har blivit inbjuden att gå med i ett team i organisationen {{parentTeamName}}.",
"email_no_user_invite_heading_org": "Du har blivit inbjuden att gå med i en {{appName}}-organisation.",
"email_no_user_invite_subheading": "{{invitedBy}} har bjudit in dig att gå med i sitt team på {{appName}}. {{appName}} är en händelsejonglerande schemaläggare som du och ditt team kan använda för att planera möten utan att skicka e-post fram och tillbaka.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Låst av teamadministratör",
"app_not_connected": "Du har inte anslutit ett {{appName}}-konto.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Inbjudningslänk kopierad",
"invite_link_deleted": "Inbjudningslänk borttagen",
"api_key_deleted": "API nyckel borttagen",
"delete_api_key_warning": "Detta kommer permanent att radera API-nyckeln. Alla applikationer som använder denna nyckel kommer omedelbart att förlora åtkomst till ditt konto. Denna åtgärd kan inte ångras.",
"invite_link_updated": "Inställningar för inbjudningslänk har sparats",
"link_expires_after": "Länkarna upphör att gälla efter ...",
"one_day": "1 dag",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Hantera roller och behörigheter för din organisation",
"learn_more_permissions": "Läs mer om hur behörigheter fungerar",
"role_name": "Rollnamn",
"role_name_required": "Rollnamn är obligatoriskt",
"role_name_placeholder": "t.ex. Säljteam, Supportpersonal",
"role_description": "Rolbeskrivning",
"role_description_placeholder": "Beskriv vad denna roll är till för och vilka behörigheter den behöver",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Roll skapades framgångsrikt",
"role_updated_successfully": "Roll uppdaterades framgångsrikt",
"delete_role": "Radera roll",
"confirm_delete_role": "Är du säker på att du vill radera denna roll? Denna åtgärd kan inte ångras.",
"confirm_delete_role": "Är du säker på att du vill ta bort rollen: {{roleName}}? Den här åtgärden kan inte ångras.",
"role_deleted_successfully": "Roll raderades framgångsrikt",
"error_creating_role": "Fel vid skapande av roll",
"error_updating_role": "Fel vid uppdatering av roll",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Arrangörens e-postadress",
"webhook_organizer_timezone": "Arrangörens tidszon (t.ex. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Arrangörens språkinställning (t.ex. 'en', 'fr')",
"webhook_organizer_username": "Organisatörens användarnamn (t.ex. 'john.doe')",
"webhook_organizer_username_in_org": "Organisatörens användarnamn i deras organisation (t.ex. 'john.doe')",
"webhook_attendee_name": "Första deltagarens namn",
"webhook_attendee_email": "Första deltagarens e-postadress",
"webhook_attendee_timezone": "Första deltagarens tidszon",
@@ -3750,4 +3757,4 @@
"use_voice": "Använd röst",
"current_voice": "Nuvarande röst",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Uygun kullanıcı bulunamadı. Başka bir zaman dilimini deneyebilir misiniz?",
"timeslot_unavailable_book_a_new_time": "Seçilen zaman dilimi artık müsait değil. <0>Lütfen yeni bir zaman seçin</0>",
"timeslot_unavailable_short": "Alındı",
"just_connected_description": "Bağlantı kuruldu. Lütfen yukarıdaki zaman dilimlerinden birini seçin veya daha sonra tekrar deneyin.",
"unavailable_timeslot_title": "Müsait olmayan zaman dilimi",
"booking_time_out_of_bounds_error": "Etkinlik türü şu anda rezerve edilemiyor. Başka bir zaman dilimi deneyebilir misiniz?",
"request_body_end_time_internal_error": "Dahili Hata. İstek gövdesinde bitiş zamanı yok",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "{{connectionType}} yapılandırmasını silmek istediğinizden emin misiniz? {{connectionType}} girişini kullanan ekip üyeleriniz artık Cal.com'a erişemeyecek.",
"organizer_timezone": "Organizatörün saat dilimi",
"email_user_cta": "Daveti görüntüle",
"accept_invite": "Daveti Kabul Et",
"join_now": "Şimdi Katıl",
"successfully_joined": "Başarıyla katıldınız",
"email_no_user_invite_heading_subteam": "{{parentTeamName}} kuruluşunun bir ekibine katılmaya davet edildiniz",
"email_no_user_invite_heading_org": "Bir {{appName}} kuruluşuna katılmaya davet edildiniz",
"email_no_user_invite_subheading": "{{invitedBy}}, sizi {{appName}} ekibine katılmaya davet etti. {{appName}}, size ve ekibinize e-posta iletişimine ihtiyaç duymadan toplantı planlama yapma olanağı sağlayan bir etkinlik planlayıcıdır.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Ekip yöneticisi tarafından kilitlendi",
"app_not_connected": "{{appName}} hesabı bağlamadınız.",
"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_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?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Davet bağlantısı kopyalandı",
"invite_link_deleted": "Davet bağlantısı silindi",
"api_key_deleted": "API Anahtarı silindi",
"delete_api_key_warning": "Bu işlem API anahtarını kalıcı olarak silecektir. Bu anahtarı kullanan tüm uygulamalar hesabınıza erişimi anında kaybedecektir. Bu işlem geri alınamaz.",
"invite_link_updated": "Davet bağlantısı ayarları kaydedildi",
"link_expires_after": "Bağlantıların sona erme süresini şuna ayarla...",
"one_day": "1 gün",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Organizasyonunuz için rolleri ve izinleri yönetin",
"learn_more_permissions": "İzinlerin nasıl çalıştığı hakkında daha fazla bilgi edinin",
"role_name": "Rol adı",
"role_name_required": "Rol adı gereklidir",
"role_name_placeholder": "örn. Satış Ekibi, Destek Personeli",
"role_description": "Rol açıklaması",
"role_description_placeholder": "Bu rolün ne için olduğunu ve hangi izinlere ihtiyaç duyduğunu açıklayın",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Rol başarıyla oluşturuldu",
"role_updated_successfully": "Rol başarıyla güncellendi",
"delete_role": "Rolü sil",
"confirm_delete_role": "Bu rolü silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
"confirm_delete_role": "{{roleName}} rolünü silmek istediğinizden emin misiniz? Bu işlem geri alınamaz.",
"role_deleted_successfully": "Rol başarıyla silindi",
"error_creating_role": "Rol oluşturulurken hata oluştu",
"error_updating_role": "Rol güncellenirken hata oluştu",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Düzenleyenin e-postası",
"webhook_organizer_timezone": "Düzenleyenin saat dilimi (örn. 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Düzenleyenin yerel ayarı (örn. 'en', 'fr')",
"webhook_organizer_username": "Organizatörün kullanıcı adı (örn. 'john.doe')",
"webhook_organizer_username_in_org": "Organizatörün kendi organizasyonundaki kullanıcı adı (örn. 'john.doe')",
"webhook_attendee_name": "İlk katılımcının adı",
"webhook_attendee_email": "İlk katılımcının e-postası",
"webhook_attendee_timezone": "İlk katılımcının saat dilimi",
@@ -3750,4 +3757,4 @@
"use_voice": "Sesi Kullan",
"current_voice": "Mevcut Ses",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni dizelerinizi yukarıya ekleyin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Не знайдено жодного доступного користувача. Чи могли б ви спробувати інший часовий слот?",
"timeslot_unavailable_book_a_new_time": "Вибраний часовий проміжок більше недоступний. <0>Будь ласка, виберіть новий час</0>",
"timeslot_unavailable_short": "Зайнято",
"just_connected_description": "Ви щойно підключилися. Будь ласка, забронюйте один із вільних слотів вище або спробуйте пізніше.",
"unavailable_timeslot_title": "Недоступний часовий проміжок",
"booking_time_out_of_bounds_error": "Цей тип події неможливо забронювати в цей час. Спробуйте інший часовий проміжок?",
"request_body_end_time_internal_error": "Внутрішня помилка. Тіло запиту не містить кінцевого часу",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Справді видалити конфігурацію {{connectionType}}? Учасники вашої команди, які входять через {{connectionType}}, не зможуть ввійти в Cal.com.",
"organizer_timezone": "Часовий пояс організатора",
"email_user_cta": "Переглянути запрошення",
"accept_invite": "Прийняти запрошення",
"join_now": "Приєднатися зараз",
"successfully_joined": "Успішно приєднано",
"email_no_user_invite_heading_subteam": "Вас запросили приєднатися до команди організації {{parentTeamName}}",
"email_no_user_invite_heading_org": "Вас запросили приєднатися до організації {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} запрошує вас приєднатися до команди в {{appName}}. {{appName}} — планувальник подій, який дає змогу вам і вашій команді планувати зустрічі без тривалої переписки електронною поштою.",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "Заблоковано адміністратором команди",
"app_not_connected": "Ви не під’єднали обліковий запис {{appName}}.",
"connect_now": "Під’єднати",
"connect_now_unavailable_tooltip": "Підключення зараз недоступне. Будь ласка, забронюйте з доступних слотів вище або спробуйте пізніше.",
"managed_event_dialog_confirm_button_one": "Замінити й повідомити {{count}} учасника",
"managed_event_dialog_confirm_button_other": "Замінити й повідомити учасників ({{count}})",
"managed_event_dialog_title_one": "URL-адреса /{{slug}} вже існує для {{count}} учасника. Замінити її?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Посилання на запрошення скопійовано",
"invite_link_deleted": "Посилання на запрошення видалено",
"api_key_deleted": "Ключ API видалено",
"delete_api_key_warning": "Це назавжди видалить API-ключ. Будь-які програми, що використовують цей ключ, негайно втратять доступ до вашого облікового запису. Цю дію неможливо скасувати.",
"invite_link_updated": "Параметри посилання на запрошення збережено",
"link_expires_after": "Термін дії посилань минає через...",
"one_day": "1 день",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Керуйте ролями та дозволами для вашої організації",
"learn_more_permissions": "Дізнайтеся більше про те, як працюють дозволи",
"role_name": "Назва ролі",
"role_name_required": "Назва ролі обов'язкова",
"role_name_placeholder": "наприклад, Відділ продажів, Служба підтримки",
"role_description": "Опис ролі",
"role_description_placeholder": "Опишіть, для чого призначена ця роль і які дозволи їй потрібні",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Роль успішно створено",
"role_updated_successfully": "Роль успішно оновлено",
"delete_role": "Видалити роль",
"confirm_delete_role": "Ви впевнені, що хочете видалити цю роль? Цю дію неможливо скасувати.",
"confirm_delete_role": "Ви впевнені, що хочете видалити роль: {{roleName}}? Цю дію неможливо скасувати.",
"role_deleted_successfully": "Роль успішно видалено",
"error_creating_role": "Помилка створення ролі",
"error_updating_role": "Помилка оновлення ролі",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Електронна пошта організатора",
"webhook_organizer_timezone": "Часовий пояс організатора (наприклад, 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Локаль організатора (наприклад, 'en', 'fr')",
"webhook_organizer_username": "Ім'я користувача організатора (наприклад, 'john.doe')",
"webhook_organizer_username_in_org": "Ім'я користувача організатора в їхній організації (наприклад, 'john.doe')",
"webhook_attendee_name": "Ім'я першого учасника",
"webhook_attendee_email": "Електронна пошта першого учасника",
"webhook_attendee_timezone": "Часовий пояс першого учасника",
@@ -3750,4 +3757,4 @@
"use_voice": "Використовувати голос",
"current_voice": "Поточний голос",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
+10 -3
View File
@@ -111,6 +111,7 @@
"no_available_users_found_error": "Không tìm thấy người dùng khả dụng. Bạn có thể thử một khung giờ khác không?",
"timeslot_unavailable_book_a_new_time": "Khung giờ đã chọn không còn khả dụng. <0>Vui lòng chọn thời gian khác</0>",
"timeslot_unavailable_short": "Đã được đặt",
"just_connected_description": "Bạn vừa kết nối. Vui lòng đặt lịch từ các khung giờ phía trên hoặc thử lại sau.",
"unavailable_timeslot_title": "Khung giờ không khả dụng",
"booking_time_out_of_bounds_error": "Không thể đặt lịch sự kiện vào thời điểm này. Bạn có thể thử chọn khung giờ khác không?",
"request_body_end_time_internal_error": "Lỗi nội bộ. Yêu cầu không chứa thời gian kết thúc",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "Bạn có chắc chắn muốn xóa cấu hình {{connectionType}} không? Các thành viên trong nhóm của bạn sử dụng thông tin đăng nhập {{connectionType}} sẽ không thể truy cập vào Cal.com được nữa.",
"organizer_timezone": "Múi giờ của người tổ chức",
"email_user_cta": "Xem lời mời",
"accept_invite": "Chấp Nhận Lời Mời",
"join_now": "Tham Gia Ngay",
"successfully_joined": "Đã tham gia thành công",
"email_no_user_invite_heading_subteam": "Bạn đã được mời tham gia vào một nhóm của tổ chức {{parentTeamName}}",
"email_no_user_invite_heading_org": "Bạn đã được mời tham gia vào tổ chức {{appName}}",
"email_no_user_invite_subheading": "{{invitedBy}} đã mời bạn gia nhập nhóm của họ trên {{appName}}. {{appName}} là công cụ lên lịch sắp xếp sự kiện cho phép bạn và nhóm bạn lên lịch các cuộc gặp mà không cần trao đổi email nhiều.",
@@ -2301,7 +2305,6 @@
"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.",
"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_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ó?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "Liên kết lời mời đã được sao chép",
"invite_link_deleted": "Liên kết lời mời đã được xoá",
"api_key_deleted": "Đã xóa API Key",
"delete_api_key_warning": "Điều này sẽ xóa vĩnh viễn khóa API. Bất kỳ ứng dụng nào đang sử dụng khóa này sẽ ngay lập tức mất quyền truy cập vào tài khoản của bạn. Hành động này không thể hoàn tác.",
"invite_link_updated": "Đã lưu cài đặt cho liên kết lời mời",
"link_expires_after": "Các liên kết được đặt hết hạn sau...",
"one_day": "1 ngày",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "Quản lý vai trò và quyền hạn cho tổ chức của bạn",
"learn_more_permissions": "Tìm hiểu thêm về cách Quyền hạn hoạt động",
"role_name": "Tên vai trò",
"role_name_required": "Tên vai trò là bắt buộc",
"role_name_placeholder": "ví dụ: Đội ngũ Bán hàng, Nhân viên Hỗ trợ",
"role_description": "Mô tả vai trò",
"role_description_placeholder": "Mô tả vai trò này dùng để làm gì và cần những quyền hạn nào",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "Đã tạo vai trò thành công",
"role_updated_successfully": "Đã cập nhật vai trò thành công",
"delete_role": "Xóa vai trò",
"confirm_delete_role": "Bạn có chắc chắn muốn xóa vai trò này không? Hành động này không thể hoàn tác.",
"confirm_delete_role": "Bạn có chắc chắn muốn xóa vai trò: {{roleName}}. Hành động này không thể hoàn tác.",
"role_deleted_successfully": "Đã xóa vai trò thành công",
"error_creating_role": "Lỗi khi tạo vai trò",
"error_updating_role": "Lỗi khi cập nhật vai trò",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "Email của nhà tổ chức",
"webhook_organizer_timezone": "Múi giờ của nhà tổ chức (ví dụ: 'America/New_York', 'Asia/Kolkata')",
"webhook_organizer_locale": "Ngôn ngữ của nhà tổ chức (ví dụ: 'en', 'fr')",
"webhook_organizer_username": "Tên người dùng của người tổ chức (ví dụ: 'john.doe')",
"webhook_organizer_username_in_org": "Tên người dùng của người tổ chức trong tổ chức của họ (ví dụ: 'john.doe')",
"webhook_attendee_name": "Tên của người tham dự đầu tiên",
"webhook_attendee_email": "Email của người tham dự đầu tiên",
"webhook_attendee_timezone": "Múi giờ của người tham dự đầu tiên",
@@ -3750,4 +3757,4 @@
"use_voice": "Sử dụng giọng nói",
"current_voice": "Giọng nói hiện tại",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "找不到可用时间。请尝试另一个时间段。",
"timeslot_unavailable_book_a_new_time": "所选时间段已不可用。<0>请选择新的时间</0>",
"timeslot_unavailable_short": "已被预订",
"just_connected_description": "您已成功连接。请从上方的时间段中选择预订,或稍后重试。",
"unavailable_timeslot_title": "不可用时间段",
"booking_time_out_of_bounds_error": "此时间段无法预订该活动类型。您可以尝试选择其他时间段吗?",
"request_body_end_time_internal_error": "内部错误。请求主体不包含结束时间",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "确定要删除 {{connectionType}} 配置吗?使用 {{connectionType}} 登录的团队成员将无法再访问 Cal.com。",
"organizer_timezone": "组织者时区",
"email_user_cta": "查看邀请",
"accept_invite": "接受邀请",
"join_now": "立即加入",
"successfully_joined": "成功加入",
"email_no_user_invite_heading_subteam": "您已被邀请加入 {{parentTeamName}} 组织的一个团队。",
"email_no_user_invite_heading_org": "您已被邀请加入一个 {{appName}} 组织。",
"email_no_user_invite_subheading": "{{invitedBy}} 已邀请您加入他们在 {{appName}} 上的团队。{{appName}} 是一个活动安排调度程序,让您和您的团队无需通过电子邮件沟通即可安排会议。",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "被团队管理员锁定",
"app_not_connected": "您尚未连接 {{appName}} 帐户。",
"connect_now": "立即连接",
"connect_now_unavailable_tooltip": "“立即连接”当前不可用。请从上方的时间段中预订或稍后再试。",
"managed_event_dialog_confirm_button_one": "替换并通知 {{count}} 个成员",
"managed_event_dialog_confirm_button_other": "替换并通知 {{count}} 个成员",
"managed_event_dialog_title_one": "{{count}} 个成员已具有链接/{{slug}}。是否要将其替换?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "邀请链接已复制",
"invite_link_deleted": "邀请链接已删除",
"api_key_deleted": "API 密钥已删除",
"delete_api_key_warning": "这将永久删除该 API 密钥。任何使用此密钥的应用程序将立即失去对您账户的访问权限。此操作无法撤销。",
"invite_link_updated": "邀请链接设置已保存",
"link_expires_after": "设置链接过期时间为...",
"one_day": "1 天",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "管理您的组织的角色与权限",
"learn_more_permissions": "了解更多关于权限如何工作的内容",
"role_name": "角色名称",
"role_name_required": "角色名称是必填项",
"role_name_placeholder": "例如:销售团队、支持人员",
"role_description": "角色描述",
"role_description_placeholder": "描述此角色的用途及其所需的权限",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "角色创建成功",
"role_updated_successfully": "角色更新成功",
"delete_role": "删除角色",
"confirm_delete_role": "您确定要删除此角色吗?此操作无法撤销。",
"confirm_delete_role": "您确定要删除角色:{{roleName}} 吗?此操作无法撤销。",
"role_deleted_successfully": "角色删除成功",
"error_creating_role": "创建角色时出错",
"error_updating_role": "更新角色时出错",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "组织者的电子邮件",
"webhook_organizer_timezone": "组织者的时区(例如:'America/New_York','Asia/Kolkata')",
"webhook_organizer_locale": "组织者的语言环境(例如:'en','fr')",
"webhook_organizer_username": "组织者的用户名(例如:'john.doe')",
"webhook_organizer_username_in_org": "组织内组织者的用户名(例如:'john.doe')",
"webhook_attendee_name": "第一位参与者的姓名",
"webhook_attendee_email": "第一位参与者的电子邮件",
"webhook_attendee_timezone": "第一位参与者的时区",
@@ -3750,4 +3757,4 @@
"use_voice": "使用语音",
"current_voice": "当前语音",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 在此上方添加您的新字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -111,6 +111,7 @@
"no_available_users_found_error": "未找到可用用戶。您可以嘗試其他時間段嗎?",
"timeslot_unavailable_book_a_new_time": "所選的時段已不可用。<0>請選擇新的時間</0>",
"timeslot_unavailable_short": "已被預訂",
"just_connected_description": "您剛剛已連線。請從上方的時段中預訂,或稍後再試。",
"unavailable_timeslot_title": "不可用時段",
"booking_time_out_of_bounds_error": "此時段無法預約活動。請嘗試選擇其他時段?",
"request_body_end_time_internal_error": "內部錯誤。請求正文不包含結束時間",
@@ -2152,6 +2153,9 @@
"delete_sso_configuration_confirmation_description": "確定要刪除 {{connectionType}} 設定?使用 {{connectionType}} 登入的團隊成員將再也無法存取 Cal.com。",
"organizer_timezone": "主辦者時區",
"email_user_cta": "檢視邀請",
"accept_invite": "接受邀請",
"join_now": "立即加入",
"successfully_joined": "成功加入",
"email_no_user_invite_heading_subteam": "您已被邀請加入 {{parentTeamName}} 組織的團隊",
"email_no_user_invite_heading_org": "您已被邀請加入 {{appName}} 組織",
"email_no_user_invite_subheading": "{{invitedBy}} 已邀請您加入對方在 {{appName}} 上的團隊。{{appName}} 是活動多工排程工具,可讓您和您的團隊無須透過繁複的電子郵件往來就能輕鬆預定會議。",
@@ -2301,7 +2305,6 @@
"locked_by_team_admin": "已由團隊管理員鎖定",
"app_not_connected": "您尚未連結 {{appName}} 帳號。",
"connect_now": "立即連結",
"connect_now_unavailable_tooltip": "立即連接目前無法使用。請從上方時段預訂或稍後再試。",
"managed_event_dialog_confirm_button_one": "取代並通知 {{count}} 位成員",
"managed_event_dialog_confirm_button_other": "取代並通知 {{count}} 位成員",
"managed_event_dialog_title_one": "{{count}} 位成員已有網址 /{{slug}}。是否要替換?",
@@ -2383,6 +2386,7 @@
"invite_link_copied": "邀請連結已複製",
"invite_link_deleted": "邀請連結已刪除",
"api_key_deleted": "API 金鑰已刪除",
"delete_api_key_warning": "這將永久刪除該 API 金鑰。任何使用此金鑰的應用程式將立即失去對您帳戶的存取權限。此操作無法撤銷。",
"invite_link_updated": "邀請連結設定已儲存",
"link_expires_after": "將連結效期設為…",
"one_day": "1 天",
@@ -3460,6 +3464,7 @@
"roles_and_permissions_description": "管理您組織的角色與權限",
"learn_more_permissions": "了解更多關於權限的運作方式",
"role_name": "角色名稱",
"role_name_required": "角色名稱為必填項目",
"role_name_placeholder": "例如:銷售團隊、支援人員",
"role_description": "角色描述",
"role_description_placeholder": "描述此角色的用途及其所需的權限",
@@ -3498,7 +3503,7 @@
"role_created_successfully": "角色已成功建立",
"role_updated_successfully": "角色已成功更新",
"delete_role": "刪除角色",
"confirm_delete_role": "您確定要刪除此角色嗎?此操作無法復原。",
"confirm_delete_role": "您確定要刪除角色:{{roleName}}嗎?此操作無法撤銷。",
"role_deleted_successfully": "角色已成功刪除",
"error_creating_role": "建立角色時出錯",
"error_updating_role": "更新角色時出錯",
@@ -3672,6 +3677,8 @@
"webhook_organizer_email": "主辦者的電子郵件",
"webhook_organizer_timezone": "主辦者的時區(例如:'America/New_York'、'Asia/Kolkata')",
"webhook_organizer_locale": "主辦者的語言區域(例如:'en'、'fr')",
"webhook_organizer_username": "組織者的使用者名稱(例如:'john.doe')",
"webhook_organizer_username_in_org": "組織內部組織者的使用者名稱(例如:'john.doe')",
"webhook_attendee_name": "第一位與會者的姓名",
"webhook_attendee_email": "第一位與會者的電子郵件",
"webhook_attendee_timezone": "第一位與會者的時區",
@@ -3750,4 +3757,4 @@
"use_voice": "使用語音",
"current_voice": "目前的語音",
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 請在此處新增您的字串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
}
}
@@ -1,5 +1,4 @@
import type { GetServerSidePropsContext } from "next";
import { getCsrfToken } from "next-auth/react";
import prisma from "@calcom/prisma";
@@ -28,7 +27,6 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
props: {
isRequestExpired: !resetPasswordRequest,
requestId: id,
csrfToken: await getCsrfToken({ req: context.req }),
},
};
}
+1 -1
View File
@@ -1,7 +1,7 @@
import { describe, expect, it, vi } from "vitest";
import dayjs from "@calcom/dayjs";
import { getCheckBookingLimitsService } from "@calcom/lib/di/containers/BookingLimits";
import { getCheckBookingLimitsService } from "@calcom/features/di/containers/BookingLimits";
import type { IntervalLimit } from "@calcom/lib/intervalLimits/intervalLimitSchema";
import { validateIntervalLimitOrder } from "@calcom/lib/intervalLimits/validateIntervalLimitOrder";
+1 -1
View File
@@ -17,7 +17,7 @@ import {
import { describe, vi, test } from "vitest";
import dayjs from "@calcom/dayjs";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { SchedulingType, type BookingStatus } from "@calcom/prisma/enums";
import { expect, expectedSlotsForSchedule } from "./getSchedule/expects";
@@ -9,7 +9,7 @@ import {
import { describe, test } from "vitest";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { expect, expectedSlotsForSchedule } from "./expects";
import { setupAndTeardown } from "./setupAndTeardown";
@@ -12,7 +12,7 @@ import { expectNoAttemptToGetAvailability } from "../../utils/bookingScenario/ex
import { describe, test } from "vitest";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { MembershipRole } from "@calcom/prisma/enums";
import { expect, expectedSlotsForSchedule } from "./expects";
@@ -8,7 +8,7 @@ import type { ScenarioData } from "../../utils/bookingScenario/bookingScenario";
import { describe, expect, vi, test } from "vitest";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { PeriodType } from "@calcom/prisma/enums";
import { expectedSlotsForSchedule } from "./expects";
@@ -10,7 +10,7 @@ import {
import { describe, test, vi } from "vitest";
import type { z } from "zod";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import type { getScheduleSchema } from "@calcom/trpc/server/routers/viewer/slots/types";
import { expect } from "./expects";
@@ -11,7 +11,7 @@ import type { IncomingMessage } from "http";
import { describe, test, beforeEach, vi } from "vitest";
import type { z } from "zod";
import { getAvailableSlotsService } from "@calcom/lib/di/containers/AvailableSlots";
import { getAvailableSlotsService } from "@calcom/features/di/containers/AvailableSlots";
import { prisma } from "@calcom/prisma";
import type { getScheduleSchema, GetScheduleOptions } from "@calcom/trpc/server/routers/viewer/slots/types";
@@ -29,18 +29,22 @@ import type { BookingStatus } from "@calcom/prisma/enums";
import type { teamMetadataSchema } from "@calcom/prisma/zod-utils";
import type { userMetadataType } from "@calcom/prisma/zod-utils";
import type { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import type { AppMeta } from "@calcom/types/App";
import type {
Calendar,
CalendarEvent,
IntegrationCalendar,
NewCalendarEventType,
EventBusyDate,
} from "@calcom/types/Calendar";
import type { CredentialForCalendarService } from "@calcom/types/Credential";
import type { CredentialPayload } from "@calcom/types/Credential";
import type { VideoApiAdapter } from "@calcom/types/VideoApiAdapter";
import { getMockPaymentService } from "./MockPaymentService";
import type { getMockRequestDataForBooking } from "./getMockRequestDataForBooking";
type NonNullableVideoApiAdapter = NonNullable<VideoApiAdapter>;
vi.mock("@calcom/app-store/calendar.services.generated", () => ({
CalendarServiceMap: {
googlecalendar: Promise.resolve({ default: vi.fn() }),
@@ -50,7 +54,7 @@ vi.mock("@calcom/app-store/calendar.services.generated", () => ({
},
}));
const mockVideoAdapterRegistry: Record<string, any> = {};
const mockVideoAdapterRegistry: Record<string, unknown> = {};
vi.mock("@calcom/app-store/video.adapters.generated", () => ({
VideoApiAdapterMap: new Proxy(
@@ -72,9 +76,7 @@ vi.mock("@calcom/lib/raqb/findTeamMembersMatchingAttributeLogic", () => ({
}));
vi.mock("@calcom/lib/crypto", async (importOriginal) => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
const actual = await importOriginal<any>();
const actual = await importOriginal<typeof import("@calcom/lib/crypto")>();
return {
...actual,
symmetricEncrypt: vi.fn((serviceAccountKey) => serviceAccountKey),
@@ -116,6 +118,17 @@ type InputWorkflow = {
verifiedAt?: Date;
};
type PaymentData = {
// Common payment data fields based on Stripe and other payment providers
paymentIntent?: string;
paymentMethodId?: string;
clientSecret?: string;
customerId?: string;
subscriptionId?: string;
metadata?: Record<string, string>;
[key: string]: unknown; // Allow additional provider-specific fields
};
type InputPayment = {
id?: number;
uid: string;
@@ -126,7 +139,7 @@ type InputPayment = {
currency: string;
success: boolean;
refunded: boolean;
data: Record<string, any>;
data: PaymentData;
externalId: string;
paymentOption?: PaymentOption;
};
@@ -258,7 +271,7 @@ export type InputEventType = {
bookingLimits?: IntervalLimit;
durationLimits?: IntervalLimit;
owner?: number;
metadata?: any;
metadata?: z.infer<typeof EventTypeMetaDataSchema>;
rescheduleWithSameRoundRobinHost?: boolean;
restrictionSchedule?: {
create: {
@@ -296,6 +309,7 @@ type WhiteListedBookingProps = {
email: string;
phoneNumber?: string;
bookingSeat?: AttendeeBookingSeatInput | null;
timeZone?: string;
}[];
references?: (Omit<ReturnType<typeof getMockBookingReference>, "credentialId"> & {
// TODO: Make sure that all references start providing credentialId and then remove this intersection of optional credentialId
@@ -370,13 +384,18 @@ export async function addEventTypesToDb(
"users" | "workflows" | "destinationCalendar" | "schedule"
> & {
id?: number;
users?: any[];
users?: ({ id: number } | undefined)[];
userId?: number;
hosts?: any[];
workflows?: any[];
destinationCalendar?: any;
schedule?: any;
metadata?: any;
hosts?: {
user: InputUser | undefined;
id: number;
}[];
workflows?: Prisma.WorkflowCreateInput[];
destinationCalendar?: {
create: Prisma.DestinationCalendarCreateInput;
};
schedule?: { create: Prisma.ScheduleCreateInput } | null | undefined;
metadata?: z.infer<typeof EventTypeMetaDataSchema>;
team?: { id?: number | null; bookingLimits?: IntervalLimit; includeManagedEventsInLimits?: boolean };
restrictionSchedule?: {
create: {
@@ -559,7 +578,8 @@ export async function addEventTypes(eventTypes: InputEventType[], usersStore: In
};
});
log.silly("TestData: Creating EventType", JSON.stringify(eventTypesWithUsers));
return await addEventTypesToDb(eventTypesWithUsers);
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- Pretty complex type here
return await addEventTypesToDb(eventTypesWithUsers as unknown as any);
}
function addBookingReferencesToDB(bookingReferences: Prisma.BookingReferenceCreateManyInput[]) {
@@ -941,7 +961,6 @@ export async function addUsers(users: InputUser[]) {
}
if (user.profiles) {
newUser.profiles = {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error Not sure why this is not working
createMany: {
data: user.profiles,
@@ -1062,7 +1081,7 @@ export async function createOrganization(orgData: {
export async function createCredentials(
credentialData: {
type: string;
key: any;
key: Prisma.JsonValue;
id?: number;
userId?: number | null;
teamId?: number | null;
@@ -1575,7 +1594,7 @@ export function getOrganizer({
completedOnboarding,
locked,
emailVerified,
};
};
}
export function getScenarioData(
@@ -1603,7 +1622,7 @@ export function getScenarioData(
bookings?: ScenarioData["bookings"];
payment?: ScenarioData["payment"];
},
org?: { id: number | null } | undefined | null
org?: { id: number | null; profileUsername?: string } | undefined | null
) {
if (_users && (usersApartFromOrganizer.length || organizer)) {
throw new Error("When users are provided, usersApartFromOrganizer and organizer should not be provided");
@@ -1617,11 +1636,13 @@ export function getScenarioData(
if (!orgId) {
throw new Error("If org is specified org.id is required");
}
users.forEach((user) => {
const profileUsername = org.profileUsername ?? user.username ?? "";
user.profiles = [
{
organizationId: orgId,
username: user.username || "",
username: profileUsername,
uid: ProfileRepository.generateProfileUid(),
},
];
@@ -1694,7 +1715,7 @@ export const enum BookingLocations {
export type CalendarServiceMethodMockCallBase = {
calendarServiceConstructorArgs: {
credential: CredentialForCalendarService;
credential: CredentialPayload;
};
};
@@ -1710,7 +1731,7 @@ type UpdateEventMethodMockCall = CalendarServiceMethodMockCallBase & {
args: {
uid: string;
event: CalendarEvent;
externalCalendarId?: string;
externalCalendarId?: string | null;
};
};
@@ -1718,7 +1739,7 @@ type DeleteEventMethodMockCall = CalendarServiceMethodMockCallBase & {
args: {
uid: string;
event: CalendarEvent;
externalCalendarId?: string;
externalCalendarId?: string | null;
};
};
@@ -1800,156 +1821,161 @@ export async function mockCalendar(
const calendarServicePromise = CalendarServiceMap[calendarServiceKey];
if (calendarServicePromise) {
const resolvedService = await calendarServicePromise;
vi.mocked(resolvedService.default as any).mockImplementation(function MockCalendarService(
credential: any
) {
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
createEvent: async function (...rest: any[]): Promise<NewCalendarEventType> {
if (calendarData?.creationCrash) {
throw new Error("MockCalendarService.createEvent fake error");
}
const [calEvent, credentialId, externalCalendarId] = rest;
log.debug(
"mockCalendar.createEvent",
JSON.stringify({ calEvent, credentialId, externalCalendarId })
);
createEventCalls.push({
args: {
calEvent,
credentialId,
externalCalendarId,
},
calendarServiceConstructorArgs: {
credential,
},
});
const isGoogleMeetLocation = calEvent?.location === BookingLocations.GoogleMeet;
if (app.type === "google_calendar") {
return Promise.resolve({
type: app.type,
additionalInfo: {
vi.mocked(resolvedService.default).mockImplementation(
// @ts-expect-error - Mock implementation satisfies Calendar interface but TypeScript expects specific calendar service types
function MockCalendarService(credential) {
return {
createEvent: async function (
...rest: Parameters<Calendar["createEvent"]>
): Promise<NewCalendarEventType> {
if (calendarData?.creationCrash) {
throw new Error("MockCalendarService.createEvent fake error");
}
const [calEvent, credentialId, externalCalendarId] = rest;
log.debug(
"mockCalendar.createEvent",
JSON.stringify({ calEvent, credentialId, externalCalendarId })
);
createEventCalls.push({
args: {
calEvent,
credentialId,
externalCalendarId,
},
calendarServiceConstructorArgs: {
credential,
},
});
const isGoogleMeetLocation = calEvent?.location === BookingLocations.GoogleMeet;
if (app.type === "google_calendar") {
return Promise.resolve({
type: app.type,
additionalInfo: {
hangoutLink:
normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink ||
"https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
},
hangoutLink:
normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink ||
"https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
uid: normalizedCalendarData.create?.uid || "GOOGLE_CALENDAR_EVENT_ID",
id: normalizedCalendarData.create?.id || "GOOGLE_CALENDAR_EVENT_ID",
iCalUID:
normalizedCalendarData.create?.iCalUID || calEvent.iCalUID || "GOOGLE_CALENDAR_EVENT_ID",
password: "MOCK_PASSWORD",
url:
normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink ||
"https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
});
} else if (app.type === "office365_calendar") {
return Promise.resolve({
type: app.type,
additionalInfo: {},
uid: normalizedCalendarData.create?.uid || "OFFICE_365_CALENDAR_EVENT_ID",
id: normalizedCalendarData.create?.id || "OFFICE_365_CALENDAR_EVENT_ID",
iCalUID:
normalizedCalendarData.create?.iCalUID ||
calEvent.iCalUID ||
"OFFICE_365_CALENDAR_EVENT_ID",
password: "MOCK_PASSWORD",
url:
normalizedCalendarData.create?.appSpecificData?.office365Calendar?.url ||
"https://UNUSED_URL",
});
} else {
return Promise.resolve({
type: app.type,
additionalInfo: {},
uid: "PROBABLY_UNUSED_UID",
hangoutLink:
(isGoogleMeetLocation
? normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink
: null) || "https://UNUSED_URL",
// A Calendar is always expected to return an id.
id: normalizedCalendarData.create?.id || "FALLBACK_MOCK_CALENDAR_EVENT_ID",
iCalUID: normalizedCalendarData.create?.iCalUID,
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
password: "MOCK_PASSWORD",
url: "https://UNUSED_URL",
});
}
},
updateEvent: async function (
...rest: Parameters<Calendar["updateEvent"]>
): Promise<NewCalendarEventType> {
if (calendarData?.updationCrash) {
throw new Error("MockCalendarService.updateEvent fake error");
}
const [uid, event, externalCalendarId] = rest;
log.silly("mockCalendar.updateEvent", JSON.stringify({ uid, event, externalCalendarId }));
updateEventCalls.push({
args: {
uid,
event,
externalCalendarId,
},
calendarServiceConstructorArgs: {
credential,
},
hangoutLink:
normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink ||
"https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
uid: normalizedCalendarData.create?.uid || "GOOGLE_CALENDAR_EVENT_ID",
id: normalizedCalendarData.create?.id || "GOOGLE_CALENDAR_EVENT_ID",
iCalUID:
normalizedCalendarData.create?.iCalUID || calEvent.iCalUID || "GOOGLE_CALENDAR_EVENT_ID",
password: "MOCK_PASSWORD",
url:
normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink ||
"https://GOOGLE_MEET_URL_IN_CALENDAR_EVENT",
});
} else if (app.type === "office365_calendar") {
return Promise.resolve({
type: app.type,
additionalInfo: {},
uid: normalizedCalendarData.create?.uid || "OFFICE_365_CALENDAR_EVENT_ID",
id: normalizedCalendarData.create?.id || "OFFICE_365_CALENDAR_EVENT_ID",
iCalUID:
normalizedCalendarData.create?.iCalUID || calEvent.iCalUID || "OFFICE_365_CALENDAR_EVENT_ID",
password: "MOCK_PASSWORD",
url:
normalizedCalendarData.create?.appSpecificData?.office365Calendar?.url ||
"https://UNUSED_URL",
});
} else {
const isGoogleMeetLocation = event.location === BookingLocations.GoogleMeet;
return Promise.resolve({
type: app.type,
additionalInfo: {},
uid: "PROBABLY_UNUSED_UID",
hangoutLink:
(isGoogleMeetLocation
? normalizedCalendarData.create?.appSpecificData?.googleCalendar?.hangoutLink
: null) || "https://UNUSED_URL",
// A Calendar is always expected to return an id.
id: normalizedCalendarData.create?.id || "FALLBACK_MOCK_CALENDAR_EVENT_ID",
iCalUID: normalizedCalendarData.create?.iCalUID,
iCalUID: normalizedCalendarData.update?.iCalUID,
id: normalizedCalendarData.update?.uid || "FALLBACK_MOCK_ID",
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
password: "MOCK_PASSWORD",
url: "https://UNUSED_URL",
location: isGoogleMeetLocation ? "https://UNUSED_URL" : undefined,
hangoutLink:
(isGoogleMeetLocation
? normalizedCalendarData.update?.appSpecificData?.googleCalendar?.hangoutLink
: null) || "https://UNUSED_URL",
conferenceData: isGoogleMeetLocation ? event.conferenceData : undefined,
});
}
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
updateEvent: async function (...rest: any[]): Promise<NewCalendarEventType> {
if (calendarData?.updationCrash) {
throw new Error("MockCalendarService.updateEvent fake error");
}
const [uid, event, externalCalendarId] = rest;
log.silly("mockCalendar.updateEvent", JSON.stringify({ uid, event, externalCalendarId }));
// eslint-disable-next-line prefer-rest-params
updateEventCalls.push({
args: {
uid,
event,
externalCalendarId,
},
calendarServiceConstructorArgs: {
credential,
},
});
const isGoogleMeetLocation = event.location === BookingLocations.GoogleMeet;
return Promise.resolve({
type: app.type,
additionalInfo: {},
uid: "PROBABLY_UNUSED_UID",
iCalUID: normalizedCalendarData.update?.iCalUID,
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
id: normalizedCalendarData.update?.uid || "FALLBACK_MOCK_ID",
// Password and URL seems useless for CalendarService, plan to remove them if that's the case
password: "MOCK_PASSWORD",
url: "https://UNUSED_URL",
location: isGoogleMeetLocation ? "https://UNUSED_URL" : undefined,
hangoutLink:
(isGoogleMeetLocation
? normalizedCalendarData.update?.appSpecificData?.googleCalendar?.hangoutLink
: null) || "https://UNUSED_URL",
conferenceData: isGoogleMeetLocation ? event.conferenceData : undefined,
});
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deleteEvent: async (...rest: any[]) => {
log.silly("mockCalendar.deleteEvent", JSON.stringify({ rest }));
// eslint-disable-next-line prefer-rest-params
deleteEventCalls.push({
args: {
uid: rest[0],
event: rest[1],
externalCalendarId: rest[2],
},
calendarServiceConstructorArgs: {
credential,
},
});
},
getAvailability: async (...rest: any[]): Promise<EventBusyDate[]> => {
if (calendarData?.getAvailabilityCrash) {
throw new Error("MockCalendarService.getAvailability fake error");
}
const [dateFrom, dateTo, selectedCalendars, shouldServeCache] = rest;
getAvailabilityCalls.push({
args: {
dateFrom,
dateTo,
selectedCalendars,
shouldServeCache,
},
calendarServiceConstructorArgs: {
credential,
},
});
return new Promise((resolve) => {
resolve(calendarData?.busySlots || []);
});
},
};
});
},
deleteEvent: async (...rest: Parameters<Calendar["deleteEvent"]>) => {
log.silly("mockCalendar.deleteEvent", JSON.stringify({ rest }));
deleteEventCalls.push({
args: {
uid: rest[0],
event: rest[1],
externalCalendarId: rest[2],
},
calendarServiceConstructorArgs: {
credential,
},
});
},
getAvailability: async (
dateFrom: string,
dateTo: string,
selectedCalendars: IntegrationCalendar[],
shouldServeCache?: boolean
): Promise<EventBusyDate[]> => {
if (calendarData?.getAvailabilityCrash) {
throw new Error("MockCalendarService.getAvailability fake error");
}
getAvailabilityCalls.push({
args: {
dateFrom,
dateTo,
selectedCalendars,
shouldServeCache,
},
calendarServiceConstructorArgs: {
credential,
},
});
return new Promise((resolve) => {
resolve(calendarData?.busySlots || []);
});
},
} as Calendar;
}
);
}
return {
@@ -2014,10 +2040,9 @@ export function mockVideoApp({
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const deleteMeetingCalls: any[] = [];
const mockVideoAdapter = (credential: any) => {
const mockVideoAdapter = (credential: unknown) => {
return {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
createMeeting: (...rest: any[]) => {
createMeeting: (...rest: Parameters<NonNullableVideoApiAdapter["createMeeting"]>) => {
if (creationCrash) {
throw new Error("MockVideoApiAdapter.createMeeting fake error");
}
@@ -2031,8 +2056,7 @@ export function mockVideoApp({
...videoMeetingData,
});
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
updateMeeting: async (...rest: any[]) => {
updateMeeting: async (...rest: Parameters<NonNullableVideoApiAdapter["updateMeeting"]>) => {
if (updationCrash) {
throw new Error("MockVideoApiAdapter.updateMeeting fake error");
}
@@ -2053,8 +2077,7 @@ export function mockVideoApp({
...videoMeetingData,
});
},
// eslint-disable-next-line @typescript-eslint/no-explicit-any
deleteMeeting: async (...rest: any[]) => {
deleteMeeting: async (...rest: Parameters<NonNullableVideoApiAdapter["deleteMeeting"]>) => {
log.silly("MockVideoApiAdapter.deleteMeeting", JSON.stringify(rest));
deleteMeetingCalls.push({
credential,
@@ -2116,7 +2139,7 @@ export function mockPaymentApp({
metadataLookupKey: string;
appStoreLookupKey?: string;
}) {
appStoreLookupKey = appStoreLookupKey || metadataLookupKey;
const _appStoreLookupKey = appStoreLookupKey || metadataLookupKey;
const { paymentUid, externalId } = getMockPaymentService();
return {
@@ -2366,7 +2389,7 @@ export const getDefaultBookingFields = ({
required: true,
defaultLabel: "your_name",
},
!!emailField
emailField
? emailField
: {
name: "email",
+20 -7
View File
@@ -306,13 +306,13 @@ export function expectWebhookToHaveBeenCalledWith(
if (parsedBody.payload) {
if (data.payload) {
if (!!data.payload.metadata) {
if (data.payload.metadata) {
expect(parsedBody.payload.metadata).toEqual(expect.objectContaining(data.payload.metadata));
}
if (!!data.payload.responses)
if (data.payload.responses)
expect(parsedBody.payload.responses).toEqual(expect.objectContaining(data.payload.responses));
if (!!data.payload.organizer)
if (data.payload.organizer)
expect(parsedBody.payload.organizer).toEqual(expect.objectContaining(data.payload.organizer));
const { responses: _1, metadata: _2, organizer: _3, ...remainingPayload } = data.payload;
@@ -1031,6 +1031,7 @@ export function expectBookingRequestedWebhookToHaveBeenFired({
}
export function expectBookingCreatedWebhookToHaveBeenFired({
organizer,
booker,
location,
subscriberUrl,
@@ -1039,7 +1040,7 @@ export function expectBookingCreatedWebhookToHaveBeenFired({
isEmailHidden = false,
isAttendeePhoneNumberHidden = false,
}: {
organizer: { email: string; name: string };
organizer: { email: string; name: string; username?: string; usernameInOrg?: string };
booker: { email: string; name: string; attendeePhoneNumber?: string };
subscriberUrl: string;
location: string;
@@ -1048,6 +1049,11 @@ export function expectBookingCreatedWebhookToHaveBeenFired({
isEmailHidden?: boolean;
isAttendeePhoneNumberHidden?: boolean;
}) {
const organizerPayload = {
username: organizer.username,
...(organizer.usernameInOrg ? { usernameInOrg: organizer.usernameInOrg } : null),
};
if (!paidEvent) {
expectWebhookToHaveBeenCalledWith(subscriberUrl, {
triggerEvent: "BOOKING_CREATED",
@@ -1073,6 +1079,7 @@ export function expectBookingCreatedWebhookToHaveBeenFired({
isHidden: false,
},
},
organizer: organizerPayload,
},
});
} else {
@@ -1103,6 +1110,7 @@ export function expectBookingCreatedWebhookToHaveBeenFired({
value: { optionValue: "", value: location },
},
},
organizer: organizerPayload,
},
});
}
@@ -1144,21 +1152,28 @@ export function expectBookingRescheduledWebhookToHaveBeenFired({
}
export function expectBookingCancelledWebhookToHaveBeenFired({
organizer,
booker,
location,
subscriberUrl,
payload,
}: {
organizer: { email: string; name: string };
organizer: { email: string; name: string; username?: string; usernameInOrg?: string };
booker: { email: string; name: string };
subscriberUrl: string;
location: string;
payload?: Record<string, unknown>;
}) {
const organizerPayload = {
username: organizer.username,
...(organizer.usernameInOrg ? { usernameInOrg: organizer.usernameInOrg } : null),
};
expectWebhookToHaveBeenCalledWith(subscriberUrl, {
triggerEvent: "BOOKING_CANCELLED",
payload: {
...payload,
organizer: organizerPayload,
metadata: null,
responses: {
name: {
@@ -1387,14 +1402,12 @@ export function expectSuccessfulVideoMeetingDeletionInCalendar(
export async function expectBookingInDBToBeRescheduledFromTo({ from, to }: { from: any; to: any }) {
// Expect previous booking to be cancelled
await expectBookingToBeInDatabase({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...from,
status: BookingStatus.CANCELLED,
});
// Expect new booking to be created but status would depend on whether the new booking requires confirmation or not.
await expectBookingToBeInDatabase({
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
...to,
});
}
@@ -13,7 +13,7 @@ To test the API in your local instance, you have the following pre-requisites:
You can use the following as staging license key
```
1a1f8138-0bfc-4f37-b4af-1e24fd145839
cal_live_QiMeiCoFjEczVQmY6EJTeiJV
```
</Step>
<Step title="Start development server and create API key">
@@ -0,0 +1,8 @@
---
title: "Add changesets"
---
We use changesets for every PR that affects atoms, be it a fix or feature to add a log describing the fix or feature. Below video showcases how to add a changeset to a PR.
<iframe style={{ width: "100%", maxWidth: "560px" }} height="315" src="https://www.loom.com/embed/2130c801b35248f6b878eb531eb62184?sid=0e2fe07b-59d4-4f31-beb0-7f267154de8b" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen="true"></iframe>
+5
View File
@@ -100,6 +100,11 @@
"icon": "webhook",
"pages": ["developing/guides/automation/webhooks"]
},
{
"group": "Atoms",
"icon": "atom",
"pages": ["developing/guides/atoms/add-changesets"]
},
{
"group": "Email",
"icon": "inbox",
+9 -2
View File
@@ -113,6 +113,7 @@ checksums:
no_available_users_found_error: 12759d781387c445ba29ef601fb19ceb
timeslot_unavailable_book_a_new_time: 7244550019d30a440f190659820cd2e9
timeslot_unavailable_short: 7bc68111973c864f2003d51e2d08c3dd
just_connected_description: d55f8b4c29ea17356eadaaeb37e9ad83
unavailable_timeslot_title: 5c0d248a46971ac0b6b51c63680740bf
booking_time_out_of_bounds_error: a1f0eee998bad55bb2d339682e8ba0e3
request_body_end_time_internal_error: c90ce14f6e59a9d9c516a5cb72df38cf
@@ -2149,6 +2150,9 @@ checksums:
delete_sso_configuration_confirmation_description: 60d81840d21bb15a508bf69275e7d2b4
organizer_timezone: a97fbf27366f1dfe959d64042a2ac442
email_user_cta: e15f010644b32a63c77aa2a1ae4bb885
accept_invite: bd2e50d88d4d949ffeacf472dfcf52ed
join_now: 1d6305a8e38d3c0e2f422c2687bc4efc
successfully_joined: 1d2f5746efab4ec4a8858a150d09ba12
email_no_user_invite_heading_subteam: 5c5df57c46ddddbc43817e8f4897785c
email_no_user_invite_heading_org: 945f9476a671b849876132c67ed83b19
email_no_user_invite_subheading: e0d5166214861fcc3d006318b2cff4ee
@@ -2298,7 +2302,6 @@ checksums:
locked_by_team_admin: 539128c9579f340b95d58abbfa0880ef
app_not_connected: 25c979da79e9f983e6f5e62dcd1c47bb
connect_now: ca98ba0375aad91861c150bbc0fff9ec
connect_now_unavailable_tooltip: f259b61c574fd191048ddce748b87829
managed_event_dialog_confirm_button_one: 57569c5943d7cf44f3373f763559a0d8
managed_event_dialog_confirm_button_other: d665ecde0909425ff7433b417c45ada3
managed_event_dialog_title_one: 11c192644024dffc351d11d9681a81f9
@@ -2380,6 +2383,7 @@ checksums:
invite_link_copied: 4046f23a78e1cd5166671c3fb8a7ea6e
invite_link_deleted: cbad4a7a211c04c6c860402068e89ede
api_key_deleted: 6b63d1a15bea4b41acdd8432e680751a
delete_api_key_warning: c2aabd8e8c70562823de11c42d440f5b
invite_link_updated: d1834e3d59a0a4ef7b1a008772e244e5
link_expires_after: 398b822c0aca1c04947095bd9dbc47f6
one_day: ef1826660893c89de442e0d0c4c327d7
@@ -3457,6 +3461,7 @@ checksums:
roles_and_permissions_description: b61be30059f6de6034d6acb588d3a495
learn_more_permissions: 9be546d0622d5bcb5336c5b417c5f29f
role_name: 6166d263954a8c5e41e988801c2cfb20
role_name_required: 5650dd108c357a78fec90aa755d78367
role_name_placeholder: b138f0b244a5680d2986db2c99b11548
role_description: 1eccd63fc1cb4d162ff7e5353fd72660
role_description_placeholder: d732e6a22a3e3ae60c21ac390e5248f5
@@ -3495,7 +3500,7 @@ checksums:
role_created_successfully: 0c5e659653544f4e549c49deee5da514
role_updated_successfully: 70f6225b70f4bcd15af464da4c838096
delete_role: 877641ea72960f194766086aceb53346
confirm_delete_role: 05f1b95d4506445819c1938c456e73b6
confirm_delete_role: 894b27127b5afeed7e143cf3eaac87cf
role_deleted_successfully: 3e82866aca1a2461c2cfccbad71b2610
error_creating_role: 4d3c36c243381aa68d671ae9c58265cd
error_updating_role: bb0758044b466e260c9bb3704b4d115f
@@ -3669,6 +3674,8 @@ checksums:
webhook_organizer_email: 59df32359598e24c3ad299005b4d70b9
webhook_organizer_timezone: b239e17bc05498a8853715f2b714dbba
webhook_organizer_locale: bec80c8ae6d494b9a0f02d156897b60e
webhook_organizer_username: 9432525b1ab89aefe655e4dfa5aad730
webhook_organizer_username_in_org: 3df45d86d16144002a6019f2db9760b6
webhook_attendee_name: cf1b24a9065f0bddce45c1b9d73bbed4
webhook_attendee_email: 84ba9a09117d2ee1b6ccb1c91dde5598
webhook_attendee_timezone: 28b44fac88a7af5de712b4170d4e4920
@@ -481,7 +481,9 @@ export default class Office365CalendarService implements Calendar {
.map((member) => {
const destinationCalendar =
event.destinationCalendar &&
event.destinationCalendar.find((cal) => cal.userId === member.id);
event.destinationCalendar.find(
(cal) => cal.integration === this.integrationName && cal.userId === member.id
);
return {
emailAddress: {
address: destinationCalendar?.externalId ?? member.email,
@@ -57,7 +57,7 @@ const leadQueryResponse = {
],
};
const ownerQueryResponse = {
const _ownerQueryResponse = {
records: [
{
Id: "owner001",
@@ -88,7 +88,11 @@ vi.mock("./graphql/SalesforceGraphQLClient", () => ({
describe("SalesforceCRMService", () => {
let service: SalesforceCRMService;
let mockConnection: { query: any; sobject: any };
let mockConnection: {
query: ReturnType<typeof vi.fn>;
sobject: ReturnType<typeof vi.fn>;
search: ReturnType<typeof vi.fn>;
};
setupAndTeardown();
@@ -96,6 +100,7 @@ describe("SalesforceCRMService", () => {
mockConnection = {
query: vi.fn(),
sobject: vi.fn(),
search: vi.fn(),
};
const mockCredential: CredentialPayload = {
@@ -334,7 +339,7 @@ describe("SalesforceCRMService", () => {
expect(querySpy).toHaveBeenNthCalledWith(
1,
"SELECT Id, Email, OwnerId, AccountId, Account.Owner.Email, Account.Website FROM Contact WHERE Email = 'test@example.com' AND AccountId != null"
"SELECT Id, Email, OwnerId, AccountId, Account.OwnerId, Account.Owner.Email, Account.Website FROM Contact WHERE Email = 'test@example.com' AND AccountId != null"
);
});
});
@@ -424,7 +429,7 @@ describe("SalesforceCRMService", () => {
expect(querySpy).toHaveBeenNthCalledWith(
1,
"SELECT Id, Email, OwnerId, AccountId, Account.Owner.Email, Account.Website FROM Contact WHERE Email = 'test@example.com' AND AccountId != null"
"SELECT Id, Email, OwnerId, AccountId, Account.OwnerId, Account.Owner.Email, Account.Website FROM Contact WHERE Email = 'test@example.com' AND AccountId != null"
);
});
});
@@ -522,6 +527,224 @@ describe("SalesforceCRMService", () => {
});
});
describe("getContactOrLeadFromEmail via getContacts", () => {
describe("with roundRobinSkipFallbackToLeadOwner enabled", () => {
it("should return contact when contact is found", async () => {
mockAppOptions({
createEventOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipCheckRecordOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipFallbackToLeadOwner: true,
});
const searchSpy = vi.spyOn(mockConnection, "search");
searchSpy.mockResolvedValueOnce({
searchRecords: [
{
Id: "contact001",
Email: "test@example.com",
OwnerId: "owner001",
attributes: { type: "Contact" },
Owner: { Email: "owner@example.com" },
},
],
});
const result = await service.getContacts({
emails: "test@example.com",
forRoundRobinSkip: true,
});
expect(result).toEqual([
{
id: "contact001",
email: "test@example.com",
ownerId: "owner001",
ownerEmail: "owner@example.com",
recordType: "Contact",
},
]);
expect(searchSpy).toHaveBeenCalledWith(
"FIND {test@example.com} IN EMAIL FIELDS RETURNING Lead(Id, Email, OwnerId, Owner.Email), Contact(Id, Email, OwnerId, Owner.Email)"
);
});
it("should return lead when no contact exists but lead is found", async () => {
mockAppOptions({
createEventOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipCheckRecordOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipFallbackToLeadOwner: true,
});
const searchSpy = vi.spyOn(mockConnection, "search");
searchSpy.mockResolvedValueOnce({
searchRecords: [
{
Id: "lead001",
Email: "test@example.com",
OwnerId: "owner001",
attributes: { type: "Lead" },
Owner: { Email: "owner@example.com" },
},
],
});
const result = await service.getContacts({
emails: "test@example.com",
forRoundRobinSkip: true,
});
expect(result).toEqual([
{
id: "lead001",
email: "test@example.com",
ownerId: "owner001",
ownerEmail: "owner@example.com",
recordType: "Lead",
},
]);
expect(searchSpy).toHaveBeenCalled();
});
it("should prefer contact over lead when both exist", async () => {
mockAppOptions({
createEventOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipCheckRecordOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipFallbackToLeadOwner: true,
});
const searchSpy = vi.spyOn(mockConnection, "search");
searchSpy.mockResolvedValueOnce({
searchRecords: [
{
Id: "lead001",
Email: "test@example.com",
OwnerId: "owner002",
attributes: { type: "Lead" },
Owner: { Email: "lead-owner@example.com" },
},
{
Id: "contact001",
Email: "test@example.com",
OwnerId: "owner001",
attributes: { type: "Contact" },
Owner: { Email: "contact-owner@example.com" },
},
],
});
const result = await service.getContacts({
emails: "test@example.com",
forRoundRobinSkip: true,
});
expect(result).toEqual([
{
id: "contact001",
email: "test@example.com",
ownerId: "owner001",
ownerEmail: "contact-owner@example.com",
recordType: "Contact",
},
]);
expect(searchSpy).toHaveBeenCalled();
});
it("should return empty array when no records found", async () => {
mockAppOptions({
createEventOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipCheckRecordOn: SalesforceRecordEnum.CONTACT,
roundRobinSkipFallbackToLeadOwner: true,
});
const searchSpy = vi.spyOn(mockConnection, "search");
searchSpy.mockResolvedValueOnce({
searchRecords: [],
});
const result = await service.getContacts({
emails: "test@example.com",
forRoundRobinSkip: true,
});
expect(result).toEqual([]);
expect(searchSpy).toHaveBeenCalled();
});
});
describe("with createEventOnLeadCheckForContact enabled", () => {
it("should find and return contact when it exists", async () => {
mockAppOptions({
createEventOn: SalesforceRecordEnum.LEAD,
createEventOnLeadCheckForContact: true,
});
const searchSpy = vi.spyOn(mockConnection, "search");
searchSpy.mockResolvedValueOnce({
searchRecords: [
{
Id: "contact001",
Email: "test@example.com",
OwnerId: "owner001",
attributes: { type: "Contact" },
Owner: { Email: "owner@example.com" },
},
],
});
const result = await service.getContacts({
emails: "test@example.com",
});
expect(result).toEqual([
{
id: "contact001",
email: "test@example.com",
recordType: "Contact",
},
]);
expect(searchSpy).toHaveBeenCalled();
});
it("should fallback to lead when contact not found", async () => {
mockAppOptions({
createEventOn: SalesforceRecordEnum.LEAD,
createEventOnLeadCheckForContact: true,
});
const searchSpy = vi.spyOn(mockConnection, "search");
searchSpy.mockResolvedValueOnce({
searchRecords: [
{
Id: "lead001",
Email: "test@example.com",
OwnerId: "owner001",
attributes: { type: "Lead" },
Owner: { Email: "owner@example.com" },
},
],
});
const result = await service.getContacts({
emails: "test@example.com",
});
expect(result).toEqual([
{
id: "lead001",
email: "test@example.com",
recordType: "Lead",
},
]);
expect(searchSpy).toHaveBeenCalled();
});
});
});
describe("createContacts", () => {
describe("createEventOn lead", () => {
describe("createNewContactUnderAccount enabled", () => {

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