Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5109075494 | ||
|
|
52791599e1 | ||
|
|
54db9a42a9 | ||
|
|
ce204d7a98 | ||
|
|
44170b267f | ||
|
|
cb30e5f1e0 | ||
|
|
d6d2aae67f | ||
|
|
b06e46342c | ||
|
|
4cf482f860 | ||
|
|
f2478ea7d3 | ||
|
|
0e110022fb | ||
|
|
ee80577cc3 | ||
|
|
8402dfa3a2 | ||
|
|
4585199b5e | ||
|
|
ae2446f42f | ||
|
|
2f6d109a70 | ||
|
|
7ff884ca7d | ||
|
|
9230b2d2ca | ||
|
|
1a645f3110 | ||
|
|
4eb11e76ea | ||
|
|
e3a5ce5f6e | ||
|
|
12e171823b | ||
|
|
3c723d6f5b | ||
|
|
1482d9a9af | ||
|
|
0efe25feb4 | ||
|
|
602192e026 | ||
|
|
aeb54e6401 | ||
|
|
b8c1d623f3 | ||
|
|
30e4d769e3 | ||
|
|
3babfa7cf3 | ||
|
|
e45ee8fab3 | ||
|
|
3c96e122b4 | ||
|
|
f21a915814 | ||
|
|
3467e2272f | ||
|
|
844592e15e | ||
|
|
bdacb18d5c | ||
|
|
1b5d50c45c | ||
|
|
1c39dbad3d | ||
|
|
e60454750e | ||
|
|
286f696302 | ||
|
|
8deaea3edd | ||
|
|
7d497a621f | ||
|
|
5c8e0483b1 | ||
|
|
3cefa94a94 | ||
|
|
30549d5c14 | ||
|
|
b87972399e | ||
|
|
0784df6a31 | ||
|
|
fc062878ed | ||
|
|
33c1d80a42 | ||
|
|
1064e1181d | ||
|
|
dfd683ffc4 | ||
|
|
0972307998 | ||
|
|
157f633ef2 | ||
|
|
cc1c3b210a | ||
|
|
fcfd47e7d2 | ||
|
|
284c4c9084 | ||
|
|
9fd3594f6c | ||
|
|
531371bf55 |
@@ -0,0 +1,17 @@
|
||||
name: Manually Delete BuildJet Cache
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
cache_key:
|
||||
description: "BuildJet Cache Key to Delete"
|
||||
required: true
|
||||
type: string
|
||||
jobs:
|
||||
manually-delete-buildjet-cache:
|
||||
runs-on: buildjet-2vcpu-ubuntu-2204
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
- uses: buildjet/cache-delete@v1
|
||||
with:
|
||||
cache_key: ${{ inputs.cache_key }}
|
||||
@@ -3,13 +3,17 @@ import type { z } from "zod";
|
||||
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
|
||||
import type { schemaEventTypeBaseBodyParams } from "~/lib/validations/event-type";
|
||||
import type { schemaEventTypeCreateBodyParams } from "~/lib/validations/event-type";
|
||||
|
||||
export default async function checkTeamEventEditPermission(
|
||||
req: NextApiRequest,
|
||||
body: Pick<z.infer<typeof schemaEventTypeBaseBodyParams>, "teamId">
|
||||
body: Pick<z.infer<typeof schemaEventTypeCreateBodyParams>, "teamId" | "userId">
|
||||
) {
|
||||
const { prisma, userId } = req;
|
||||
const { prisma, isAdmin } = req;
|
||||
let userId = req.userId;
|
||||
if (isAdmin && body.userId) {
|
||||
userId = body.userId;
|
||||
}
|
||||
if (body.teamId) {
|
||||
const membership = await prisma.membership.findFirst({
|
||||
where: {
|
||||
|
||||
+16
-7
@@ -2,11 +2,9 @@ import { dir } from "i18next";
|
||||
import { Inter } from "next/font/google";
|
||||
import localFont from "next/font/local";
|
||||
import { headers, cookies } from "next/headers";
|
||||
import Script from "next/script";
|
||||
import React from "react";
|
||||
|
||||
import { getLocale } from "@calcom/features/auth/lib/getLocale";
|
||||
import { IS_PRODUCTION } from "@calcom/lib/constants";
|
||||
|
||||
import { prepareRootMetadata } from "@lib/metadata";
|
||||
|
||||
@@ -70,11 +68,13 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
style={embedColorScheme ? { colorScheme: embedColorScheme as string } : undefined}
|
||||
data-nextjs-router="app">
|
||||
<head nonce={nonce}>
|
||||
{!IS_PRODUCTION && process.env.VERCEL_ENV === "preview" && (
|
||||
// eslint-disable-next-line @next/next/no-sync-scripts
|
||||
<Script
|
||||
data-project-id="KjpMrKTnXquJVKfeqmjdTffVPf1a6Unw2LZ58iE4"
|
||||
src="https://snippet.meticulous.ai/v1/stagingMeticulousSnippet.js"
|
||||
{!!process.env.NEXT_PUBLIC_HEAD_SCRIPTS && (
|
||||
<script
|
||||
nonce={nonce}
|
||||
id="injected-head-scripts"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: process.env.NEXT_PUBLIC_HEAD_SCRIPTS,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<style>{`
|
||||
@@ -98,6 +98,15 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
||||
}
|
||||
: {}
|
||||
}>
|
||||
{!!process.env.NEXT_PUBLIC_BODY_SCRIPTS && (
|
||||
<script
|
||||
nonce={nonce}
|
||||
id="injected-head-scripts"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: process.env.NEXT_PUBLIC_BODY_SCRIPTS,
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
{children}
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -76,6 +76,26 @@ function PageWrapper(props: AppProps) {
|
||||
id="page-status"
|
||||
dangerouslySetInnerHTML={{ __html: `window.CalComPageStatus = '${pageStatus}'` }}
|
||||
/>
|
||||
{process.env.NEXT_PUBLIC_GTM_ID ? (
|
||||
<>
|
||||
<Script
|
||||
id="gtm-init-script"
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `(function (w, d, s, l, i) {
|
||||
w[l] = w[l] || []; w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' });
|
||||
var f = d.getElementsByTagName(s)[0], j = d.createElement(s), dl = l != 'dataLayer' ? '&l=' + l : '';
|
||||
j.async = true; j.src = 'https://www.googletagmanager.com/gtm.js?id=' + i + dl; f.parentNode.insertBefore(j, f);
|
||||
})(window, document, 'script', 'dataLayer', '${process.env.NEXT_PUBLIC_GTM_ID}');`,
|
||||
}}
|
||||
/>
|
||||
<noscript
|
||||
dangerouslySetInnerHTML={{
|
||||
__html: `<iframe src="https://www.googletagmanager.com/ns.html?id=${process.env.NEXT_PUBLIC_GTM_ID}" height="0" width="0" style="display:none;visibility:hidden"></iframe>`,
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
) : null}
|
||||
|
||||
<style jsx global>{`
|
||||
:root {
|
||||
--font-inter: ${interFont.style.fontFamily};
|
||||
|
||||
@@ -5,7 +5,7 @@ import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { getEventLocationTypeFromApp, type EventLocationType } from "@calcom/app-store/locations";
|
||||
import type { CredentialOwner } from "@calcom/app-store/types";
|
||||
import { AppSetDefaultLinkDialog } from "@calcom/features/apps/components/AppSetDefaultLinkDialog";
|
||||
import { BulkEditDefaultConferencingModal } from "@calcom/features/eventtypes/components/BulkEditDefaultConferencingModal";
|
||||
import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { AppCategories } from "@calcom/prisma/enums";
|
||||
import { trpc, type RouterOutputs } from "@calcom/trpc";
|
||||
@@ -154,6 +154,12 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL
|
||||
});
|
||||
|
||||
const { t } = useLocale();
|
||||
const updateLocationsMutation = trpc.viewer.eventTypes.bulkUpdateToDefaultLocation.useMutation({
|
||||
onSuccess: () => {
|
||||
utils.viewer.getUsersDefaultConferencingApp.invalidate();
|
||||
setBulkUpdateModal(false);
|
||||
},
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<List className={listClassName}>
|
||||
@@ -173,7 +179,12 @@ export const AppList = ({ data, handleDisconnect, variant, listClassName }: AppL
|
||||
)}
|
||||
|
||||
{bulkUpdateModal && (
|
||||
<BulkEditDefaultConferencingModal open={bulkUpdateModal} setOpen={setBulkUpdateModal} />
|
||||
<BulkEditDefaultForEventsModal
|
||||
bulkUpdateFunction={updateLocationsMutation.mutate}
|
||||
open={bulkUpdateModal}
|
||||
setOpen={setBulkUpdateModal}
|
||||
isPending={updateLocationsMutation.isPending}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -162,7 +162,7 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
let bookedActions: ActionType[] = [
|
||||
{
|
||||
id: "cancel",
|
||||
label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel"),
|
||||
label: isTabRecurring && isRecurring ? t("cancel_all_remaining") : t("cancel_event"),
|
||||
/* When cancelling we need to let the UI and the API know if the intention is to
|
||||
cancel all remaining bookings or just that booking instance. */
|
||||
href: `/booking/${booking.uid}?cancel=true${
|
||||
@@ -297,6 +297,8 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
},
|
||||
];
|
||||
|
||||
const showPendingPayment = paymentAppData.enabled && booking.payment.length && !booking.paid;
|
||||
|
||||
return (
|
||||
<>
|
||||
<RescheduleDialog
|
||||
@@ -463,7 +465,7 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
{booking.eventType.team.name}
|
||||
</Badge>
|
||||
)}
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
{showPendingPayment && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="orange">
|
||||
{t("pending_payment")}
|
||||
</Badge>
|
||||
@@ -490,8 +492,8 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
{title}
|
||||
<span> </span>
|
||||
|
||||
{paymentAppData.enabled && !booking.paid && booking.payment.length && (
|
||||
<Badge className="me-2 ms-2 hidden sm:inline-flex" variant="orange">
|
||||
{showPendingPayment && (
|
||||
<Badge className="hidden sm:inline-flex" variant="orange">
|
||||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { InfoIcon } from "lucide-react";
|
||||
import dynamic from "next/dynamic";
|
||||
import Link from "next/link";
|
||||
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import type { z } from "zod";
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
allowDisablingAttendeeConfirmationEmails,
|
||||
allowDisablingHostConfirmationEmails,
|
||||
} from "@calcom/features/ee/workflows/lib/allowDisablingStandardEmails";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { FormBuilder } from "@calcom/features/form-builder/FormBuilder";
|
||||
import type { EditableSchema } from "@calcom/features/form-builder/schema";
|
||||
import { BookerLayoutSelector } from "@calcom/features/settings/BookerLayoutSelector";
|
||||
@@ -55,7 +56,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
formMethods.getValues("useEventTypeDestinationCalendarEmail")
|
||||
);
|
||||
const [hashedUrl, setHashedUrl] = useState(eventType.hashedLink?.link);
|
||||
|
||||
const bookingFields: Prisma.JsonObject = {};
|
||||
|
||||
const workflows = eventType.workflows.map((workflowOnEventType) => workflowOnEventType.workflow);
|
||||
@@ -86,6 +86,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
|
||||
useEffect(() => {
|
||||
!hashedUrl && setHashedUrl(generateHashedLink(formMethods.getValues("users")[0]?.id ?? team?.id));
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [formMethods.getValues("users"), hashedUrl, team?.id]);
|
||||
|
||||
const toggleGuests = (enabled: boolean) => {
|
||||
@@ -108,11 +109,11 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
);
|
||||
};
|
||||
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager(
|
||||
formMethods.getValues(),
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
const { isChildrenManagedEventType, isManagedEventType, shouldLockDisableProps } = useLockedFieldsManager({
|
||||
eventType,
|
||||
translate: t,
|
||||
formMethods,
|
||||
});
|
||||
const eventNamePlaceholder = getEventName({
|
||||
...eventNameObject,
|
||||
eventName: formMethods.watch("eventName"),
|
||||
@@ -120,10 +121,14 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
|
||||
const successRedirectUrlLocked = shouldLockDisableProps("successRedirectUrl");
|
||||
const seatsLocked = shouldLockDisableProps("seatsPerTimeSlotEnabled");
|
||||
const requiresBookerEmailVerificationProps = shouldLockDisableProps("requiresBookerEmailVerification");
|
||||
const hideCalendarNotesLocked = shouldLockDisableProps("hideCalendarNotes");
|
||||
const lockTimeZoneToggleOnBookingPageLocked = shouldLockDisableProps("lockTimeZoneToggleOnBookingPage");
|
||||
|
||||
const closeEventNameTip = () => setShowEventNameTip(false);
|
||||
const displayDestinationCalendarSelector =
|
||||
!!connectedCalendarsQuery.data?.connectedCalendars.length && !team;
|
||||
!!connectedCalendarsQuery.data?.connectedCalendars.length && (!team || isChildrenManagedEventType);
|
||||
|
||||
const verifiedSecondaryEmails = [
|
||||
{
|
||||
label: user?.email || "",
|
||||
@@ -181,6 +186,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
<Button
|
||||
color="minimal"
|
||||
size="sm"
|
||||
{...(shouldLockDisableProps("eventName").disabled ? { disabled: true } : {})}
|
||||
aria-label="edit custom name"
|
||||
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
|
||||
onClick={() => setShowEventNameTip((old) => !old)}>
|
||||
@@ -217,7 +223,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
placeholder={
|
||||
selectedSecondaryEmailId === -1 && (
|
||||
<span className="text-default min-w-0 overflow-hidden truncate whitespace-nowrap">
|
||||
<Badge variant="blue">Default</Badge> {user?.email || ""}
|
||||
<Badge variant="blue">{t("default")}</Badge> {user?.email || ""}
|
||||
</span>
|
||||
)
|
||||
}
|
||||
@@ -233,9 +239,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<BookerLayoutSelector fallbackToUserSettings isDark={selectedThemeIsDark} isOuterBorder={true} />
|
||||
|
||||
<div className="border-subtle space-y-6 rounded-lg border p-6">
|
||||
<FormBuilder
|
||||
title={t("booking_questions_title")}
|
||||
@@ -250,7 +254,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<RequiresConfirmationController
|
||||
eventType={eventType}
|
||||
seatsEnabled={seatsEnabled}
|
||||
@@ -258,7 +261,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
requiresConfirmation={requiresConfirmation}
|
||||
onRequiresConfirmation={setRequiresConfirmation}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="requiresBookerEmailVerification"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
@@ -268,14 +270,13 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
|
||||
title={t("requires_booker_email_verification")}
|
||||
data-testid="requires-booker-email-verification"
|
||||
{...shouldLockDisableProps("requiresBookerEmailVerification")}
|
||||
{...requiresBookerEmailVerificationProps}
|
||||
description={t("description_requires_booker_email_verification")}
|
||||
checked={value}
|
||||
onCheckedChange={(e) => onChange(e)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="hideCalendarNotes"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
@@ -283,16 +284,15 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
labelClassName="text-sm"
|
||||
toggleSwitchAtTheEnd={true}
|
||||
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
|
||||
title={t("disable_notes")}
|
||||
data-testid="disable-notes"
|
||||
{...shouldLockDisableProps("hideCalendarNotes")}
|
||||
title={t("disable_notes")}
|
||||
{...hideCalendarNotesLocked}
|
||||
description={t("disable_notes_description")}
|
||||
checked={value}
|
||||
onCheckedChange={(e) => onChange(e)}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
|
||||
<Controller
|
||||
name="successRedirectUrl"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
@@ -339,7 +339,6 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
|
||||
<SettingsToggle
|
||||
labelClassName="text-sm"
|
||||
toggleSwitchAtTheEnd={true}
|
||||
@@ -359,52 +358,53 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
<Info className="ml-1.5 h-4 w-4 cursor-pointer" />
|
||||
</a>
|
||||
}
|
||||
{...shouldLockDisableProps("hashedLinkCheck")}
|
||||
{...shouldLockDisableProps("hashedLink")}
|
||||
description={t("private_link_description", { appName: APP_NAME })}
|
||||
checked={hashedLinkVisible}
|
||||
onCheckedChange={(e) => {
|
||||
formMethods.setValue("hashedLink", e ? hashedUrl : undefined, { shouldDirty: true });
|
||||
setHashedLinkVisible(e);
|
||||
}}>
|
||||
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
|
||||
{!IS_VISUAL_REGRESSION_TESTING && (
|
||||
<TextField
|
||||
disabled
|
||||
name="hashedLink"
|
||||
label={t("private_link_label")}
|
||||
data-testid="generated-hash-url"
|
||||
labelSrOnly
|
||||
type="text"
|
||||
hint={t("private_link_hint")}
|
||||
defaultValue={placeholderHashedLink}
|
||||
addOnSuffix={
|
||||
<Tooltip
|
||||
content={
|
||||
formMethods.getValues("hashedLink") ? t("copy_to_clipboard") : t("enabled_after_update")
|
||||
}>
|
||||
<Button
|
||||
color="minimal"
|
||||
size="sm"
|
||||
type="button"
|
||||
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
|
||||
aria-label="copy link"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(placeholderHashedLink);
|
||||
if (formMethods.getValues("hashedLink")) {
|
||||
showToast(t("private_link_copied"), "success");
|
||||
} else {
|
||||
showToast(t("enabled_after_update_description"), "warning");
|
||||
}
|
||||
}}>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
{!isManagedEventType && (
|
||||
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
|
||||
{!IS_VISUAL_REGRESSION_TESTING && (
|
||||
<TextField
|
||||
disabled
|
||||
name="hashedLink"
|
||||
label={t("private_link_label")}
|
||||
data-testid="generated-hash-url"
|
||||
labelSrOnly
|
||||
type="text"
|
||||
hint={t("private_link_hint")}
|
||||
defaultValue={placeholderHashedLink}
|
||||
addOnSuffix={
|
||||
<Tooltip
|
||||
content={
|
||||
formMethods.getValues("hashedLink") ? t("copy_to_clipboard") : t("enabled_after_update")
|
||||
}>
|
||||
<Button
|
||||
color="minimal"
|
||||
size="sm"
|
||||
type="button"
|
||||
className="hover:stroke-3 hover:text-emphasis min-w-fit !py-0 px-0 hover:bg-transparent"
|
||||
aria-label="copy link"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(placeholderHashedLink);
|
||||
if (formMethods.getValues("hashedLink")) {
|
||||
showToast(t("private_link_copied"), "success");
|
||||
} else {
|
||||
showToast(t("enabled_after_update_description"), "warning");
|
||||
}
|
||||
}}>
|
||||
<Copy className="h-4 w-4" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</SettingsToggle>
|
||||
|
||||
<Controller
|
||||
name="seatsPerTimeSlotEnabled"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
@@ -504,7 +504,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupProps,
|
||||
toggleSwitchAtTheEnd={true}
|
||||
switchContainerClassName="border-subtle rounded-lg border py-6 px-4 sm:px-6"
|
||||
title={t("lock_timezone_toggle_on_booking_page")}
|
||||
{...shouldLockDisableProps("lockTimeZoneToggleOnBookingPage")}
|
||||
{...lockTimeZoneToggleOnBookingPageLocked}
|
||||
description={t("description_lock_timezone_toggle_on_booking_page")}
|
||||
checked={value}
|
||||
onCheckedChange={(e) => onChange(e)}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from "next-i18next";
|
||||
import Link from "next/link";
|
||||
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import type { GetAppData, SetAppData } from "@calcom/app-store/EventTypeAppContext";
|
||||
@@ -8,6 +8,7 @@ import { EventTypeAppCard } from "@calcom/app-store/_components/EventTypeAppCard
|
||||
import type { EventTypeAppCardComponentProps } from "@calcom/app-store/types";
|
||||
import type { EventTypeAppsList } from "@calcom/app-store/utils";
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button, EmptyScreen, Alert } from "@calcom/ui";
|
||||
@@ -74,11 +75,13 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
};
|
||||
};
|
||||
|
||||
const { shouldLockDisableProps, isManagedEventType, isChildrenManagedEventType } = useLockedFieldsManager(
|
||||
const { shouldLockDisableProps, isManagedEventType, isChildrenManagedEventType } = useLockedFieldsManager({
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
translate: t,
|
||||
formMethods,
|
||||
});
|
||||
const appsDisableProps = shouldLockDisableProps("apps", { simple: true });
|
||||
const lockedText = appsDisableProps.isLocked ? "locked" : "unlocked";
|
||||
|
||||
const appsWithTeamCredentials = eventTypeApps?.items.filter((app) => app.teams.length) || [];
|
||||
const cardsForAppsWithTeams = appsWithTeamCredentials.map((app) => {
|
||||
@@ -97,7 +100,6 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
app={app}
|
||||
eventType={eventType}
|
||||
eventTypeFormMetadata={eventTypeFormMetadata}
|
||||
{...shouldLockDisableProps("apps")}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -121,7 +123,7 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
}}
|
||||
eventType={eventType}
|
||||
eventTypeFormMetadata={eventTypeFormMetadata}
|
||||
{...shouldLockDisableProps("apps")}
|
||||
disabled={shouldLockDisableProps("apps").disabled}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -133,12 +135,28 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
<>
|
||||
<div>
|
||||
<div className="before:border-0">
|
||||
{isManagedEventType && (
|
||||
{(isManagedEventType || isChildrenManagedEventType) && (
|
||||
<Alert
|
||||
severity="neutral"
|
||||
severity={appsDisableProps.isLocked ? "neutral" : "green"}
|
||||
className="mb-2"
|
||||
title={t("locked_for_members")}
|
||||
message={t("locked_apps_description")}
|
||||
title={
|
||||
<Trans i18nKey={`${lockedText}_${isManagedEventType ? "for_members" : "by_team_admins"}`}>
|
||||
{lockedText[0].toUpperCase()}
|
||||
{lockedText.slice(1)} {isManagedEventType ? "for members" : "by team admins"}
|
||||
</Trans>
|
||||
}
|
||||
actions={<div className="flex h-full items-center">{appsDisableProps.LockedIcon}</div>}
|
||||
message={
|
||||
<Trans
|
||||
i18nKey={`apps_${lockedText}_${
|
||||
isManagedEventType ? "for_members" : "by_team_admins"
|
||||
}_description`}>
|
||||
{isManagedEventType ? "Members" : "You"}{" "}
|
||||
{appsDisableProps.isLocked
|
||||
? "will be able to see the active apps but will not be able to edit any app settings"
|
||||
: "will be able to see the active apps and will be able to edit any app settings"}
|
||||
</Trans>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{!isPending && !installedApps?.length ? (
|
||||
@@ -147,9 +165,9 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
headline={t("empty_installed_apps_headline")}
|
||||
description={t("empty_installed_apps_description")}
|
||||
buttonRaw={
|
||||
isChildrenManagedEventType && !isManagedEventType ? (
|
||||
appsDisableProps.disabled ? (
|
||||
<Button StartIcon={Lock} color="secondary" disabled>
|
||||
{t("locked_by_admin")}
|
||||
{t("locked_by_team_admin")}
|
||||
</Button>
|
||||
) : (
|
||||
<Button target="_blank" color="secondary" href="/apps">
|
||||
@@ -174,13 +192,12 @@ export const EventAppsTab = ({ eventType }: { eventType: EventType }) => {
|
||||
app={app}
|
||||
eventType={eventType}
|
||||
eventTypeFormMetadata={eventTypeFormMetadata}
|
||||
{...shouldLockDisableProps("apps")}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
{!shouldLockDisableProps("apps").disabled && (
|
||||
{!appsDisableProps.disabled && (
|
||||
<div className="bg-muted mt-6 rounded-md p-8">
|
||||
{!isPending && notInstalledApps?.length ? (
|
||||
<>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { EventTypeSetup, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetup } from "pages/event-types/[type]";
|
||||
import { useState, memo, useEffect } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import type { OptionProps, SingleValueProps } from "react-select";
|
||||
@@ -6,6 +6,7 @@ import { components } from "react-select";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { AvailabilityOption, FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { weekdayNames } from "@calcom/lib/weekday";
|
||||
@@ -17,13 +18,6 @@ import { ExternalLink, Globe } from "@calcom/ui/components/icon";
|
||||
|
||||
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
|
||||
|
||||
export type AvailabilityOption = {
|
||||
label: string;
|
||||
value: number;
|
||||
isDefault: boolean;
|
||||
isManaged?: boolean;
|
||||
};
|
||||
|
||||
const Option = ({ ...props }: OptionProps<AvailabilityOption>) => {
|
||||
const { label, isDefault, isManaged = false } = props.data;
|
||||
const { t } = useLocale();
|
||||
@@ -160,12 +154,10 @@ EventTypeScheduleDetails.displayName = "EventTypeScheduleDetails";
|
||||
|
||||
const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
|
||||
const { t } = useLocale();
|
||||
const { shouldLockIndicator, isManagedEventType, isChildrenManagedEventType } = useLockedFieldsManager(
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
const { watch, setValue, getValues } = useFormContext<FormValues>();
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
const { shouldLockIndicator, shouldLockDisableProps, isManagedEventType, isChildrenManagedEventType } =
|
||||
useLockedFieldsManager({ eventType, translate: t, formMethods });
|
||||
const { watch, setValue, getValues } = formMethods;
|
||||
const watchSchedule = watch("schedule");
|
||||
const [options, setOptions] = useState<AvailabilityOption[]>([]);
|
||||
|
||||
@@ -246,7 +238,7 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
|
||||
<div className="border-subtle rounded-t-md border p-6">
|
||||
<label htmlFor="availability" className="text-default mb-2 block text-sm font-medium leading-none">
|
||||
{t("availability")}
|
||||
{shouldLockIndicator("availability")}
|
||||
{(isManagedEventType || isChildrenManagedEventType) && shouldLockIndicator("availability")}
|
||||
</label>
|
||||
{isPending && <SelectSkeletonLoader />}
|
||||
{!isPending && (
|
||||
@@ -257,6 +249,7 @@ const EventTypeSchedule = ({ eventType }: { eventType: EventTypeSetup }) => {
|
||||
<Select
|
||||
placeholder={t("select")}
|
||||
options={options}
|
||||
isDisabled={shouldLockDisableProps("availability").disabled}
|
||||
isSearchable={false}
|
||||
onChange={(selected) => {
|
||||
field.onChange(selected?.value || null);
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||
import type { FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||
import type { Key } from "react";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import type { UseFormRegisterReturn } from "react-hook-form";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import type { SingleValue } from "react-select";
|
||||
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import type { DurationType } from "@calcom/lib/convertToNewDurationType";
|
||||
import convertToNewDurationType from "@calcom/lib/convertToNewDurationType";
|
||||
@@ -108,7 +110,7 @@ const MinimumBookingNoticeInput = React.forwardRef<
|
||||
);
|
||||
});
|
||||
|
||||
export const EventLimitsTab = () => {
|
||||
export const EventLimitsTab = ({ eventType }: Pick<EventTypeSetupProps, "eventType">) => {
|
||||
const { t, i18n } = useLocale();
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
|
||||
@@ -129,6 +131,18 @@ export const EventLimitsTab = () => {
|
||||
|
||||
const watchPeriodType = formMethods.watch("periodType");
|
||||
|
||||
const { shouldLockIndicator, shouldLockDisableProps } = useLockedFieldsManager({
|
||||
eventType,
|
||||
translate: t,
|
||||
formMethods,
|
||||
});
|
||||
|
||||
const bookingLimitsLocked = shouldLockDisableProps("bookingLimits");
|
||||
const durationLimitsLocked = shouldLockDisableProps("durationLimits");
|
||||
const onlyFirstAvailableSlotLocked = shouldLockDisableProps("onlyShowFirstAvailableSlot");
|
||||
const periodTypeLocked = shouldLockDisableProps("periodType");
|
||||
const offsetStartLockedProps = shouldLockDisableProps("offsetStart");
|
||||
|
||||
const optionsPeriod = [
|
||||
{ value: 0, label: t("business_days") },
|
||||
{ value: 1, label: t("calendar_days") },
|
||||
@@ -147,7 +161,10 @@ export const EventLimitsTab = () => {
|
||||
<div className="border-subtle space-y-6 rounded-lg border p-6">
|
||||
<div className="flex flex-col space-y-4 lg:flex-row lg:space-x-4 lg:space-y-0">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="beforeEventBuffer">{t("before_event")}</Label>
|
||||
<Label htmlFor="beforeBufferTime">
|
||||
{t("before_event")}
|
||||
{shouldLockIndicator("beforeBufferTime")}
|
||||
</Label>
|
||||
<Controller
|
||||
name="beforeEventBuffer"
|
||||
render={({ field: { onChange, value } }) => {
|
||||
@@ -167,6 +184,7 @@ export const EventLimitsTab = () => {
|
||||
onChange={(val) => {
|
||||
if (val) onChange(val.value);
|
||||
}}
|
||||
isDisabled={shouldLockDisableProps("beforeBufferTime").disabled}
|
||||
defaultValue={
|
||||
beforeBufferOptions.find((option) => option.value === value) || beforeBufferOptions[0]
|
||||
}
|
||||
@@ -177,7 +195,10 @@ export const EventLimitsTab = () => {
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="afterEventBuffer">{t("after_event")}</Label>
|
||||
<Label htmlFor="afterBufferTime">
|
||||
{t("after_event")}
|
||||
{shouldLockIndicator("afterBufferTime")}
|
||||
</Label>
|
||||
<Controller
|
||||
name="afterEventBuffer"
|
||||
render={({ field: { onChange, value } }) => {
|
||||
@@ -197,6 +218,7 @@ export const EventLimitsTab = () => {
|
||||
onChange={(val) => {
|
||||
if (val) onChange(val.value);
|
||||
}}
|
||||
isDisabled={shouldLockDisableProps("afterBufferTime").disabled}
|
||||
defaultValue={
|
||||
afterBufferOptions.find((option) => option.value === value) || afterBufferOptions[0]
|
||||
}
|
||||
@@ -209,11 +231,20 @@ export const EventLimitsTab = () => {
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4 lg:flex-row lg:space-x-4 lg:space-y-0">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="minimumBookingNotice">{t("minimum_booking_notice")}</Label>
|
||||
<MinimumBookingNoticeInput {...formMethods.register("minimumBookingNotice")} />
|
||||
<Label htmlFor="minimumBookingNotice">
|
||||
{t("minimum_booking_notice")}
|
||||
{shouldLockIndicator("minimumBookingNotice")}
|
||||
</Label>
|
||||
<MinimumBookingNoticeInput
|
||||
disabled={shouldLockDisableProps("minimumBookingNotice").disabled}
|
||||
{...formMethods.register("minimumBookingNotice")}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<Label htmlFor="slotInterval">{t("slot_interval")}</Label>
|
||||
<Label htmlFor="slotInterval">
|
||||
{t("slot_interval")}
|
||||
{shouldLockIndicator("slotInterval")}
|
||||
</Label>
|
||||
<Controller
|
||||
name="slotInterval"
|
||||
render={() => {
|
||||
@@ -230,6 +261,7 @@ export const EventLimitsTab = () => {
|
||||
return (
|
||||
<Select
|
||||
isSearchable={false}
|
||||
isDisabled={shouldLockDisableProps("slotInterval").disabled}
|
||||
onChange={(val) => {
|
||||
formMethods.setValue("slotInterval", val && (val.value || 0) > 0 ? val.value : null, {
|
||||
shouldDirty: true,
|
||||
@@ -257,6 +289,7 @@ export const EventLimitsTab = () => {
|
||||
toggleSwitchAtTheEnd={true}
|
||||
labelClassName="text-sm"
|
||||
title={t("limit_booking_frequency")}
|
||||
{...bookingLimitsLocked}
|
||||
description={t("limit_booking_frequency_description")}
|
||||
checked={isChecked}
|
||||
onCheckedChange={(active) => {
|
||||
@@ -278,7 +311,12 @@ export const EventLimitsTab = () => {
|
||||
)}
|
||||
childrenClassName="lg:ml-0">
|
||||
<div className="border-subtle rounded-b-lg border border-t-0 p-6">
|
||||
<IntervalLimitsManager propertyName="bookingLimits" defaultLimit={1} step={1} />
|
||||
<IntervalLimitsManager
|
||||
disabled={bookingLimitsLocked.disabled}
|
||||
propertyName="bookingLimits"
|
||||
defaultLimit={1}
|
||||
step={1}
|
||||
/>
|
||||
</div>
|
||||
</SettingsToggle>
|
||||
);
|
||||
@@ -295,6 +333,7 @@ export const EventLimitsTab = () => {
|
||||
title={t("limit_booking_only_first_slot")}
|
||||
description={t("limit_booking_only_first_slot_description")}
|
||||
checked={isChecked}
|
||||
{...onlyFirstAvailableSlotLocked}
|
||||
onCheckedChange={(active) => {
|
||||
onChange(active ?? false);
|
||||
}}
|
||||
@@ -321,6 +360,7 @@ export const EventLimitsTab = () => {
|
||||
childrenClassName="lg:ml-0"
|
||||
title={t("limit_total_booking_duration")}
|
||||
description={t("limit_total_booking_duration_description")}
|
||||
{...durationLimitsLocked}
|
||||
checked={isChecked}
|
||||
onCheckedChange={(active) => {
|
||||
if (active) {
|
||||
@@ -335,6 +375,7 @@ export const EventLimitsTab = () => {
|
||||
<IntervalLimitsManager
|
||||
propertyName="durationLimits"
|
||||
defaultLimit={60}
|
||||
disabled={durationLimitsLocked.disabled}
|
||||
step={15}
|
||||
textFieldSuffix={t("minutes")}
|
||||
/>
|
||||
@@ -359,6 +400,7 @@ export const EventLimitsTab = () => {
|
||||
childrenClassName="lg:ml-0"
|
||||
title={t("limit_future_bookings")}
|
||||
description={t("limit_future_bookings_description")}
|
||||
{...periodTypeLocked}
|
||||
checked={isChecked}
|
||||
onCheckedChange={(bool) => {
|
||||
if (bool && !formMethods.getValues("periodDays")) {
|
||||
@@ -372,7 +414,9 @@ export const EventLimitsTab = () => {
|
||||
onValueChange={(val) =>
|
||||
formMethods.setValue("periodType", val as PeriodType, { shouldDirty: true })
|
||||
}>
|
||||
{PERIOD_TYPES.map((period) => {
|
||||
{PERIOD_TYPES.filter((opt) =>
|
||||
periodTypeLocked.disabled ? watchPeriodType === opt.type : true
|
||||
).map((period) => {
|
||||
if (period.type === "UNLIMITED") return null;
|
||||
return (
|
||||
<div
|
||||
@@ -381,12 +425,14 @@ export const EventLimitsTab = () => {
|
||||
watchPeriodType === "UNLIMITED" && "pointer-events-none opacity-30"
|
||||
)}
|
||||
key={period.type}>
|
||||
<RadioGroup.Item
|
||||
id={period.type}
|
||||
value={period.type}
|
||||
className="min-w-4 bg-default border-default flex h-4 w-4 cursor-pointer items-center rounded-full border focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2">
|
||||
<RadioGroup.Indicator className="after:bg-inverted relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full" />
|
||||
</RadioGroup.Item>
|
||||
{!periodTypeLocked.disabled && (
|
||||
<RadioGroup.Item
|
||||
id={period.type}
|
||||
value={period.type}
|
||||
className="min-w-4 bg-default border-default flex h-4 w-4 cursor-pointer items-center rounded-full border focus:border-2 focus:outline-none ltr:mr-2 rtl:ml-2">
|
||||
<RadioGroup.Indicator className="after:bg-inverted relative flex h-4 w-4 items-center justify-center after:block after:h-2 after:w-2 after:rounded-full" />
|
||||
</RadioGroup.Item>
|
||||
)}
|
||||
|
||||
{period.prefix ? <span>{period.prefix} </span> : null}
|
||||
{period.type === "ROLLING" && (
|
||||
@@ -396,18 +442,20 @@ export const EventLimitsTab = () => {
|
||||
type="number"
|
||||
className="border-default my-0 block w-16 text-sm [appearance:textfield] ltr:mr-2 rtl:ml-2"
|
||||
placeholder="30"
|
||||
disabled={periodTypeLocked.disabled}
|
||||
{...formMethods.register("periodDays", { valueAsNumber: true })}
|
||||
/>
|
||||
<Select
|
||||
options={optionsPeriod}
|
||||
isSearchable={false}
|
||||
onChange={(opt) =>
|
||||
isDisabled={periodTypeLocked.disabled}
|
||||
onChange={(opt) => {
|
||||
formMethods.setValue(
|
||||
"periodCountCalendarDays",
|
||||
opt?.value === 1 ? true : false,
|
||||
{ shouldDirty: true }
|
||||
)
|
||||
}
|
||||
);
|
||||
}}
|
||||
name="periodCoundCalendarDays"
|
||||
value={optionsPeriod.find((opt) => {
|
||||
opt.value ===
|
||||
@@ -429,6 +477,7 @@ export const EventLimitsTab = () => {
|
||||
<DateRangePicker
|
||||
startDate={formMethods.getValues("periodDates").startDate}
|
||||
endDate={formMethods.getValues("periodDates").endDate}
|
||||
disabled={periodTypeLocked.disabled}
|
||||
onDatesChange={({ startDate, endDate }) => {
|
||||
onChange({
|
||||
startDate,
|
||||
@@ -460,6 +509,7 @@ export const EventLimitsTab = () => {
|
||||
childrenClassName="lg:ml-0"
|
||||
title={t("offset_toggle")}
|
||||
description={t("offset_toggle_description")}
|
||||
{...offsetStartLockedProps}
|
||||
checked={offsetToggle}
|
||||
onCheckedChange={(active) => {
|
||||
setOffsetToggle(active);
|
||||
|
||||
@@ -2,7 +2,7 @@ import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import { ErrorMessage } from "@hookform/error-message";
|
||||
import { Trans } from "next-i18next";
|
||||
import Link from "next/link";
|
||||
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useFormContext, useFieldArray } from "react-hook-form";
|
||||
import type { MultiValue } from "react-select";
|
||||
@@ -11,6 +11,7 @@ import type { EventLocationType } from "@calcom/app-store/locations";
|
||||
import { getEventLocationType, MeetLocationType } from "@calcom/app-store/locations";
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import { useOrgBranding } from "@calcom/features/ee/organizations/context/provider";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { md } from "@calcom/lib/markdownIt";
|
||||
@@ -142,12 +143,7 @@ export const EventSetupTab = (
|
||||
);
|
||||
|
||||
const { isChildrenManagedEventType, isManagedEventType, shouldLockIndicator, shouldLockDisableProps } =
|
||||
useLockedFieldsManager(
|
||||
formMethods.getValues(),
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
|
||||
useLockedFieldsManager({ eventType, translate: t, formMethods });
|
||||
const Locations = () => {
|
||||
const { t } = useLocale();
|
||||
const {
|
||||
@@ -183,7 +179,6 @@ export const EventSetupTab = (
|
||||
index: number;
|
||||
}) => {
|
||||
const { eventLocationType, index, ...remainingProps } = props;
|
||||
|
||||
if (eventLocationType?.organizerInputType === "text") {
|
||||
const { defaultValue, ...rest } = remainingProps;
|
||||
|
||||
@@ -200,6 +195,7 @@ export const EventSetupTab = (
|
||||
required
|
||||
onChange={onChange}
|
||||
value={value}
|
||||
{...(shouldLockDisableProps("locations").disabled ? { disabled: true } : {})}
|
||||
className="my-0"
|
||||
{...rest}
|
||||
/>
|
||||
@@ -218,6 +214,7 @@ export const EventSetupTab = (
|
||||
return (
|
||||
<PhoneInput
|
||||
required
|
||||
disabled={shouldLockDisableProps("locations").disabled}
|
||||
placeholder={t(eventLocationType.organizerInputPlaceholder || "")}
|
||||
name={`locations[${index}].${eventLocationType.defaultValueVariable}`}
|
||||
value={value}
|
||||
@@ -233,7 +230,10 @@ export const EventSetupTab = (
|
||||
};
|
||||
|
||||
const [showEmptyLocationSelect, setShowEmptyLocationSelect] = useState(false);
|
||||
const [selectedNewOption, setSelectedNewOption] = useState<SingleValueLocationOption | null>(null);
|
||||
const defaultInitialLocation = defaultValue || null;
|
||||
const [selectedNewOption, setSelectedNewOption] = useState<SingleValueLocationOption | null>(
|
||||
defaultInitialLocation
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="w-full">
|
||||
@@ -288,16 +288,18 @@ export const EventSetupTab = (
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
data-testid={`delete-locations.${index}.type`}
|
||||
className="min-h-9 block h-9 px-2"
|
||||
type="button"
|
||||
onClick={() => remove(index)}
|
||||
aria-label={t("remove")}>
|
||||
<div className="h-4 w-4">
|
||||
<X className="border-l-1 hover:text-emphasis text-subtle h-4 w-4" />
|
||||
</div>
|
||||
</button>
|
||||
{!(shouldLockDisableProps("locations").disabled && isChildrenManagedEventType) && (
|
||||
<button
|
||||
data-testid={`delete-locations.${index}.type`}
|
||||
className="min-h-9 block h-9 px-2"
|
||||
type="button"
|
||||
onClick={() => remove(index)}
|
||||
aria-label={t("remove")}>
|
||||
<div className="h-4 w-4">
|
||||
<X className="border-l-1 hover:text-emphasis text-subtle h-4 w-4" />
|
||||
</div>
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{eventLocationType?.organizerInputType && (
|
||||
@@ -329,6 +331,7 @@ export const EventSetupTab = (
|
||||
<CheckboxField
|
||||
name={`locations[${index}].displayLocationPublicly`}
|
||||
data-testid="display-location"
|
||||
disabled={shouldLockDisableProps("locations").disabled}
|
||||
defaultChecked={defaultLocation?.displayLocationPublicly}
|
||||
description={t("display_location_label")}
|
||||
onChange={(e) => {
|
||||
@@ -417,7 +420,8 @@ export const EventSetupTab = (
|
||||
</a>
|
||||
</p>
|
||||
)}
|
||||
{validLocations.length > 0 && !isManagedEventType && !isChildrenManagedEventType && (
|
||||
{validLocations.length > 0 && !shouldLockDisableProps("locations").disabled && (
|
||||
// && !isChildrenManagedEventType : Add this to hide add-location button only when location is disabled by Admin
|
||||
<li>
|
||||
<Button
|
||||
data-testid="add-location"
|
||||
@@ -444,7 +448,8 @@ export const EventSetupTab = (
|
||||
|
||||
const lengthLockedProps = shouldLockDisableProps("length");
|
||||
const descriptionLockedProps = shouldLockDisableProps("description");
|
||||
|
||||
const urlLockedProps = shouldLockDisableProps("slug");
|
||||
const titleLockedProps = shouldLockDisableProps("title");
|
||||
const urlPrefix = orgBranding
|
||||
? orgBranding?.fullDomain.replace(/^(https?:|)\/\//, "")
|
||||
: `${WEBSITE_URL?.replace(/^(https?:|)\/\//, "")}`;
|
||||
@@ -456,20 +461,22 @@ export const EventSetupTab = (
|
||||
<TextField
|
||||
required
|
||||
label={t("title")}
|
||||
{...shouldLockDisableProps("title")}
|
||||
{...(isManagedEventType || isChildrenManagedEventType ? titleLockedProps : {})}
|
||||
defaultValue={eventType.title}
|
||||
{...formMethods.register("title")}
|
||||
/>
|
||||
<div>
|
||||
<Label>
|
||||
<Label htmlFor="editor">
|
||||
{t("description")}
|
||||
{shouldLockIndicator("description")}
|
||||
{(isManagedEventType || isChildrenManagedEventType) && shouldLockIndicator("description")}
|
||||
</Label>
|
||||
<DescriptionEditor isEditable={!descriptionLockedProps.disabled} />
|
||||
</div>
|
||||
<TextField
|
||||
required
|
||||
label={t("URL")}
|
||||
{...shouldLockDisableProps("slug")}
|
||||
{...(isManagedEventType || isChildrenManagedEventType ? urlLockedProps : {})}
|
||||
defaultValue={eventType.slug}
|
||||
addOnLeading={
|
||||
<>
|
||||
{urlPrefix}/
|
||||
@@ -498,6 +505,7 @@ export const EventSetupTab = (
|
||||
defaultValue={selectedMultipleDuration}
|
||||
name="metadata.multipleDuration"
|
||||
isSearchable={false}
|
||||
isDisabled={lengthLockedProps.disabled}
|
||||
className="h-auto !min-h-[36px] text-sm"
|
||||
options={multipleDurationOptions}
|
||||
value={selectedMultipleDuration}
|
||||
@@ -551,7 +559,8 @@ export const EventSetupTab = (
|
||||
<TextField
|
||||
required
|
||||
type="number"
|
||||
{...lengthLockedProps}
|
||||
data-testid="duration"
|
||||
{...(isManagedEventType || isChildrenManagedEventType ? lengthLockedProps : {})}
|
||||
label={t("duration")}
|
||||
defaultValue={formMethods.getValues("length") ?? 15}
|
||||
{...formMethods.register("length")}
|
||||
@@ -583,15 +592,20 @@ export const EventSetupTab = (
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="border-subtle rounded-lg border p-6">
|
||||
<div>
|
||||
<Skeleton as={Label} loadingClassName="w-16">
|
||||
<Skeleton as={Label} loadingClassName="w-16" htmlFor="locations">
|
||||
{t("location")}
|
||||
{/*improve shouldLockIndicator function to also accept eventType and then conditionally render
|
||||
based on Managed Event type or not.*/}
|
||||
{shouldLockIndicator("locations")}
|
||||
</Skeleton>
|
||||
|
||||
<Controller name="locations" render={() => <Locations />} />
|
||||
<Controller
|
||||
name="locations"
|
||||
control={formMethods.control}
|
||||
defaultValue={eventType.locations || []}
|
||||
render={() => <Locations />}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Trans } from "next-i18next";
|
||||
import Link from "next/link";
|
||||
import type { EventTypeSetupProps, FormValues, Host } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetupProps, Host } from "pages/event-types/[type]";
|
||||
import { useEffect, useRef, useState } from "react";
|
||||
import type { ComponentProps, Dispatch, SetStateAction } from "react";
|
||||
import { Controller, useFormContext, useWatch } from "react-hook-form";
|
||||
@@ -9,6 +9,7 @@ import type { Options } from "react-select";
|
||||
import type { CheckedSelectOption } from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
||||
import CheckedTeamSelect from "@calcom/features/eventtypes/components/CheckedTeamSelect";
|
||||
import ChildrenEventTypeSelect from "@calcom/features/eventtypes/components/ChildrenEventTypeSelect";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { SchedulingType } from "@calcom/prisma/enums";
|
||||
import { Label, Select, SettingsToggle } from "@calcom/ui";
|
||||
|
||||
@@ -2,12 +2,13 @@ import { Webhook as TbWebhook } from "lucide-react";
|
||||
import type { TFunction } from "next-i18next";
|
||||
import { Trans } from "next-i18next";
|
||||
import { useRouter } from "next/navigation";
|
||||
import type { EventTypeSetupProps, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||
import { useMemo, useState, Suspense } from "react";
|
||||
import type { UseFormReturn } from "react-hook-form";
|
||||
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import { EventTypeEmbedButton, EventTypeEmbedDialog } from "@calcom/features/embed/EventTypeEmbed";
|
||||
import type { FormValues, AvailabilityOption } from "@calcom/features/eventtypes/lib/types";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -52,8 +53,6 @@ import {
|
||||
Loader,
|
||||
} from "@calcom/ui/components/icon";
|
||||
|
||||
import type { AvailabilityOption } from "@components/eventtype/EventAvailabilityTab";
|
||||
|
||||
type Props = {
|
||||
children: React.ReactNode;
|
||||
eventType: EventTypeSetupProps["eventType"];
|
||||
@@ -205,6 +204,7 @@ function EventTypeSingleLayout({
|
||||
activeWebhooksNumber,
|
||||
}: Props) {
|
||||
const { t } = useLocale();
|
||||
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
|
||||
const hasPermsToDelete =
|
||||
@@ -213,11 +213,11 @@ function EventTypeSingleLayout({
|
||||
formMethods.getValues("schedulingType") === SchedulingType.MANAGED ||
|
||||
isUserOrganizationAdmin;
|
||||
|
||||
const { isManagedEventType, isChildrenManagedEventType } = useLockedFieldsManager(
|
||||
formMethods.getValues(),
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
const { isManagedEventType, isChildrenManagedEventType } = useLockedFieldsManager({
|
||||
eventType,
|
||||
translate: t,
|
||||
formMethods,
|
||||
});
|
||||
|
||||
const length = formMethods.watch("length");
|
||||
const multipleDuration = formMethods.watch("metadata")?.multipleDuration;
|
||||
|
||||
@@ -4,8 +4,10 @@ import { Trans } from "next-i18next";
|
||||
import Link from "next/link";
|
||||
import type { EventTypeSetupProps } from "pages/event-types/[type]";
|
||||
import { useState } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { WebhookForm } from "@calcom/features/webhooks/components";
|
||||
import type { WebhookFormSubmitData } from "@calcom/features/webhooks/components/WebhookForm";
|
||||
import WebhookListItem from "@calcom/features/webhooks/components/WebhookListItem";
|
||||
@@ -20,6 +22,7 @@ export const EventWebhooksTab = ({ eventType }: Pick<EventTypeSetupProps, "event
|
||||
const { t } = useLocale();
|
||||
|
||||
const utils = trpc.useContext();
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
|
||||
const { data: webhooks } = trpc.viewer.webhook.list.useQuery({ eventTypeId: eventType.id });
|
||||
|
||||
@@ -96,11 +99,11 @@ export const EventWebhooksTab = ({ eventType }: Pick<EventTypeSetupProps, "event
|
||||
);
|
||||
};
|
||||
|
||||
const { shouldLockDisableProps, isChildrenManagedEventType, isManagedEventType } = useLockedFieldsManager(
|
||||
const { shouldLockDisableProps, isChildrenManagedEventType, isManagedEventType } = useLockedFieldsManager({
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
translate: t,
|
||||
formMethods,
|
||||
});
|
||||
const webhookLockedStatus = shouldLockDisableProps("webhooks");
|
||||
|
||||
return (
|
||||
@@ -163,7 +166,7 @@ export const EventWebhooksTab = ({ eventType }: Pick<EventTypeSetupProps, "event
|
||||
buttonRaw={
|
||||
isChildrenManagedEventType && !isManagedEventType ? (
|
||||
<Button StartIcon={Lock} color="secondary" disabled>
|
||||
{t("locked_by_admin")}
|
||||
{t("locked_by_team_admin")}
|
||||
</Button>
|
||||
) : (
|
||||
<NewWebhookButton />
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import type { Webhook } from "@prisma/client";
|
||||
import { useSession } from "next-auth/react";
|
||||
import type { EventTypeSetup, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetup } from "pages/event-types/[type]";
|
||||
import { useState } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import LicenseRequired from "@calcom/features/ee/common/components/LicenseRequired";
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { WebhookForm } from "@calcom/features/webhooks/components";
|
||||
import type { WebhookFormSubmitData } from "@calcom/features/webhooks/components/WebhookForm";
|
||||
import WebhookListItem from "@calcom/features/webhooks/components/WebhookListItem";
|
||||
@@ -33,11 +34,7 @@ export default function InstantEventController({
|
||||
const [instantEventState, setInstantEventState] = useState<boolean>(eventType?.isInstantEvent ?? false);
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager(
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager({ eventType, translate: t, formMethods });
|
||||
|
||||
const instantLocked = shouldLockDisableProps("isInstantEvent");
|
||||
|
||||
@@ -104,6 +101,8 @@ export default function InstantEventController({
|
||||
const InstantMeetingWebhooks = ({ eventType }: { eventType: EventTypeSetup }) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
|
||||
const { data: webhooks } = trpc.viewer.webhook.list.useQuery({
|
||||
eventTypeId: eventType.id,
|
||||
eventTriggers: [WebhookTriggerEvents.INSTANT_MEETING],
|
||||
@@ -179,11 +178,11 @@ const InstantMeetingWebhooks = ({ eventType }: { eventType: EventTypeSetup }) =>
|
||||
);
|
||||
};
|
||||
|
||||
const { shouldLockDisableProps, isChildrenManagedEventType, isManagedEventType } = useLockedFieldsManager(
|
||||
const { shouldLockDisableProps, isChildrenManagedEventType, isManagedEventType } = useLockedFieldsManager({
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
formMethods,
|
||||
translate: t,
|
||||
});
|
||||
const webhookLockedStatus = shouldLockDisableProps("webhooks");
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import type { EventTypeSetup, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetup } from "pages/event-types/[type]";
|
||||
import { useState } from "react";
|
||||
import { useFormContext } from "react-hook-form";
|
||||
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Frequency } from "@calcom/prisma/zod-utils";
|
||||
@@ -31,11 +32,7 @@ export default function RecurringEventController({
|
||||
value: value.toString(),
|
||||
}));
|
||||
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager(
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager({ eventType, translate: t, formMethods });
|
||||
|
||||
const recurringLocked = shouldLockDisableProps("recurringEvent");
|
||||
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
import * as RadioGroup from "@radix-ui/react-radio-group";
|
||||
import type { UnitTypeLongPlural } from "dayjs";
|
||||
import { Trans } from "next-i18next";
|
||||
import type { EventTypeSetup, FormValues } from "pages/event-types/[type]";
|
||||
import type { EventTypeSetup } from "pages/event-types/[type]";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
import { useEffect, useState } from "react";
|
||||
import { Controller, useFormContext } from "react-hook-form";
|
||||
import type z from "zod";
|
||||
|
||||
import useLockedFieldsManager from "@calcom/features/ee/managed-event-types/hooks/useLockedFieldsManager";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
@@ -42,11 +43,7 @@ export default function RequiresConfirmationController({
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [requiresConfirmation]);
|
||||
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager(
|
||||
eventType,
|
||||
t("locked_fields_admin_description"),
|
||||
t("locked_fields_member_description")
|
||||
);
|
||||
const { shouldLockDisableProps } = useLockedFieldsManager({ eventType, translate: t, formMethods });
|
||||
const requiresConfirmationLockedProps = shouldLockDisableProps("requiresConfirmation");
|
||||
|
||||
const options = [
|
||||
|
||||
@@ -96,6 +96,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
isBrandingHidden: team?.hideBranding,
|
||||
isInstantMeeting: eventData.isInstantEvent && queryIsInstantMeeting ? true : false,
|
||||
themeBasis: null,
|
||||
orgBannerUrl: eventData?.team?.parent?.bannerUrl ?? "",
|
||||
},
|
||||
};
|
||||
};
|
||||
|
||||
@@ -90,6 +90,17 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
},
|
||||
}),
|
||||
},
|
||||
include: {
|
||||
parent: {
|
||||
select: {
|
||||
id: true,
|
||||
slug: true,
|
||||
name: true,
|
||||
isPrivate: true,
|
||||
isOrganization: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
if (!unpublishedTeam) return { notFound: true } as const;
|
||||
@@ -103,19 +114,23 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
} as const;
|
||||
}
|
||||
|
||||
const isTeamOrParentOrgPrivate = team.isPrivate || (team.parent?.isOrganization && team.parent?.isPrivate);
|
||||
|
||||
team.eventTypes =
|
||||
team.eventTypes?.map((type) => ({
|
||||
...type,
|
||||
users: type.users.map((user) => ({
|
||||
...user,
|
||||
avatar: `/${user.username}/avatar.png`,
|
||||
})),
|
||||
users: !isTeamOrParentOrgPrivate
|
||||
? type.users.map((user) => ({
|
||||
...user,
|
||||
avatar: `/${user.username}/avatar.png`,
|
||||
}))
|
||||
: [],
|
||||
descriptionAsSafeHTML: markdownToSafeHTML(type.description),
|
||||
})) ?? null;
|
||||
|
||||
const safeBio = markdownToSafeHTML(team.bio) || "";
|
||||
|
||||
const members = !team.isPrivate
|
||||
const members = !isTeamOrParentOrgPrivate
|
||||
? team.members.map((member) => {
|
||||
return {
|
||||
name: member.name,
|
||||
@@ -139,7 +154,13 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
|
||||
|
||||
return {
|
||||
props: {
|
||||
team: { ...serializableTeam, safeBio, members, metadata },
|
||||
team: {
|
||||
...serializableTeam,
|
||||
safeBio,
|
||||
members,
|
||||
metadata,
|
||||
children: isTeamOrParentOrgPrivate ? [] : team.children,
|
||||
},
|
||||
themeBasis: serializableTeam.slug,
|
||||
trpcState: ssr.dehydrate(),
|
||||
markdownStrippedBio,
|
||||
|
||||
@@ -24,7 +24,11 @@ import {
|
||||
useIsEmbed,
|
||||
} from "@calcom/embed-core/embed-iframe";
|
||||
import { Price } from "@calcom/features/bookings/components/event-meta/Price";
|
||||
import { SMS_REMINDER_NUMBER_FIELD, SystemField } from "@calcom/features/bookings/lib/SystemField";
|
||||
import {
|
||||
SMS_REMINDER_NUMBER_FIELD,
|
||||
SystemField,
|
||||
TITLE_FIELD,
|
||||
} from "@calcom/features/bookings/lib/SystemField";
|
||||
import { APP_NAME } from "@calcom/lib/constants";
|
||||
import {
|
||||
formatToLocalizedDate,
|
||||
@@ -91,6 +95,7 @@ export default function Success(props: PageProps) {
|
||||
const pathname = usePathname();
|
||||
const searchParams = useCompatSearchParams();
|
||||
const { eventType, bookingInfo, requiresLoginToUpdate } = props;
|
||||
|
||||
const {
|
||||
allRemainingBookings,
|
||||
isSuccessBookingPage,
|
||||
@@ -345,7 +350,7 @@ export default function Success(props: PageProps) {
|
||||
!giphyImage && !isCancelled && needsConfirmation
|
||||
? "bg-subtle h-12 w-12 rounded-full"
|
||||
: "",
|
||||
isCancelled ? "bg-error h-12 w-12 rounded-full" : ""
|
||||
isCancelled ? "bg-error h-12 w-12 rounded-full " : ""
|
||||
)}>
|
||||
{giphyImage && !needsConfirmation && !isCancelled && (
|
||||
// eslint-disable-next-line @next/next/no-img-element
|
||||
@@ -518,7 +523,13 @@ export default function Success(props: PageProps) {
|
||||
if (!field) return null;
|
||||
const isSystemField = SystemField.safeParse(field.name);
|
||||
// SMS_REMINDER_NUMBER_FIELD is a system field but doesn't have a dedicated place in the UI. So, it would be shown through the following responses list
|
||||
if (isSystemField.success && field.name !== SMS_REMINDER_NUMBER_FIELD) return null;
|
||||
// TITLE is also an identifier for booking question "What is this meeting about?"
|
||||
if (
|
||||
isSystemField.success &&
|
||||
field.name !== SMS_REMINDER_NUMBER_FIELD &&
|
||||
field.name !== TITLE_FIELD
|
||||
)
|
||||
return null;
|
||||
|
||||
const label = field.label || t(field.defaultLabel || "");
|
||||
|
||||
|
||||
@@ -803,11 +803,10 @@ const CreateFirstEventTypeView = ({ slug }: { slug: string }) => {
|
||||
);
|
||||
};
|
||||
|
||||
const CTA = ({ data }: { data: GetByViewerResponse }) => {
|
||||
const CTA = ({ data, isOrganization }: { data: GetByViewerResponse; isOrganization: boolean }) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
if (!data) return null;
|
||||
|
||||
const profileOptions = data.profiles
|
||||
.filter((profile) => !profile.readOnly)
|
||||
.map((profile) => {
|
||||
@@ -825,7 +824,9 @@ const CTA = ({ data }: { data: GetByViewerResponse }) => {
|
||||
data-testid="new-event-type"
|
||||
subtitle={t("create_event_on").toUpperCase()}
|
||||
options={profileOptions}
|
||||
createDialog={() => <CreateEventTypeDialog profileOptions={profileOptions} />}
|
||||
createDialog={() => (
|
||||
<CreateEventTypeDialog profileOptions={profileOptions} isOrganization={isOrganization} />
|
||||
)}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -983,7 +984,7 @@ const EventTypesPage: React.FC & {
|
||||
hideHeadingOnMobile
|
||||
subtitle={t("event_types_page_subtitle")}
|
||||
beforeCTAactions={<Actions />}
|
||||
CTA={<CTA data={data} />}>
|
||||
CTA={<CTA data={data} isOrganization={!!user?.organizationId} />}>
|
||||
<HeadSeo
|
||||
title="Event Types"
|
||||
description="Create events to share for people to book on your calendar."
|
||||
|
||||
@@ -12,8 +12,8 @@ import { z } from "zod";
|
||||
import checkForMultiplePaymentApps from "@calcom/app-store/_utils/payments/checkForMultiplePaymentApps";
|
||||
import { getEventLocationType } from "@calcom/app-store/locations";
|
||||
import { validateCustomEventName } from "@calcom/core/event";
|
||||
import type { EventLocationType } from "@calcom/core/location";
|
||||
import type { ChildrenEventType } from "@calcom/features/eventtypes/components/ChildrenEventTypeSelect";
|
||||
import type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
import { validateIntervalLimitOrder } from "@calcom/lib";
|
||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -22,21 +22,14 @@ import { HttpError } from "@calcom/lib/http-error";
|
||||
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { validateBookerLayouts } from "@calcom/lib/validateBookerLayouts";
|
||||
import type { Prisma } from "@calcom/prisma/client";
|
||||
import type { PeriodType, SchedulingType } from "@calcom/prisma/enums";
|
||||
import type {
|
||||
BookerLayoutSettings,
|
||||
customInputSchema,
|
||||
EventTypeMetaDataSchema,
|
||||
} from "@calcom/prisma/zod-utils";
|
||||
import type { customInputSchema, EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
|
||||
import { eventTypeBookingFields } from "@calcom/prisma/zod-utils";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { IntervalLimit, RecurringEvent } from "@calcom/types/Calendar";
|
||||
import { Form, showToast } from "@calcom/ui";
|
||||
|
||||
import type { AppProps } from "@lib/app-providers";
|
||||
|
||||
import type { AvailabilityOption } from "@components/eventtype/EventAvailabilityTab";
|
||||
import { EventTypeSingleLayout } from "@components/eventtype/EventTypeSingleLayout";
|
||||
|
||||
import { type PageProps } from "~/event-types/views/event-types-single-view.getServerSideProps";
|
||||
@@ -84,74 +77,6 @@ const ManagedEventTypeDialog = dynamic(() => import("@components/eventtype/Manag
|
||||
|
||||
export type Host = { isFixed: boolean; userId: number; priority: number };
|
||||
|
||||
export type FormValues = {
|
||||
id: number;
|
||||
title: string;
|
||||
eventTitle: string;
|
||||
eventName: string;
|
||||
slug: string;
|
||||
isInstantEvent: boolean;
|
||||
length: number;
|
||||
offsetStart: number;
|
||||
description: string;
|
||||
disableGuests: boolean;
|
||||
lockTimeZoneToggleOnBookingPage: boolean;
|
||||
requiresConfirmation: boolean;
|
||||
requiresBookerEmailVerification: boolean;
|
||||
recurringEvent: RecurringEvent | null;
|
||||
schedulingType: SchedulingType | null;
|
||||
hidden: boolean;
|
||||
hideCalendarNotes: boolean;
|
||||
hashedLink: string | undefined;
|
||||
locations: {
|
||||
type: EventLocationType["type"];
|
||||
address?: string;
|
||||
attendeeAddress?: string;
|
||||
link?: string;
|
||||
hostPhoneNumber?: string;
|
||||
displayLocationPublicly?: boolean;
|
||||
phone?: string;
|
||||
hostDefault?: string;
|
||||
credentialId?: number;
|
||||
teamName?: string;
|
||||
}[];
|
||||
customInputs: CustomInputParsed[];
|
||||
schedule: number | null;
|
||||
periodType: PeriodType;
|
||||
periodDays: number;
|
||||
periodCountCalendarDays: boolean;
|
||||
periodDates: { startDate: Date; endDate: Date };
|
||||
seatsPerTimeSlot: number | null;
|
||||
seatsShowAttendees: boolean | null;
|
||||
seatsShowAvailabilityCount: boolean | null;
|
||||
seatsPerTimeSlotEnabled: boolean;
|
||||
scheduleName: string;
|
||||
minimumBookingNotice: number;
|
||||
minimumBookingNoticeInDurationType: number;
|
||||
beforeEventBuffer: number;
|
||||
afterEventBuffer: number;
|
||||
slotInterval: number | null;
|
||||
metadata: z.infer<typeof EventTypeMetaDataSchema>;
|
||||
destinationCalendar: {
|
||||
integration: string;
|
||||
externalId: string;
|
||||
};
|
||||
successRedirectUrl: string;
|
||||
durationLimits?: IntervalLimit;
|
||||
bookingLimits?: IntervalLimit;
|
||||
onlyShowFirstAvailableSlot: boolean;
|
||||
children: ChildrenEventType[];
|
||||
hosts: Host[];
|
||||
bookingFields: z.infer<typeof eventTypeBookingFields>;
|
||||
availability?: AvailabilityOption;
|
||||
bookerLayouts: BookerLayoutSettings;
|
||||
multipleDurationEnabled: boolean;
|
||||
users: EventTypeSetup["users"];
|
||||
assignAllTeamMembers: boolean;
|
||||
useEventTypeDestinationCalendarEmail: boolean;
|
||||
secondaryEmailId?: number;
|
||||
};
|
||||
|
||||
export type CustomInputParsed = typeof customInputSchema._output;
|
||||
|
||||
const querySchema = z.object({
|
||||
@@ -438,7 +363,7 @@ const EventTypePage = (props: EventTypeSetupProps) => {
|
||||
),
|
||||
availability: <EventAvailabilityTab eventType={eventType} isTeamEvent={!!team} />,
|
||||
team: <EventTeamTab teamMembers={teamMembers} team={team} eventType={eventType} />,
|
||||
limits: <EventLimitsTab />,
|
||||
limits: <EventLimitsTab eventType={eventType} />,
|
||||
advanced: <EventAdvancedTab eventType={eventType} team={team} />,
|
||||
instant: <EventInstantTab eventType={eventType} isTeamEvent={!!team} />,
|
||||
recurring: <EventRecurringTab eventType={eventType} />,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "3.8.6",
|
||||
"version": "3.8.9",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
import type { DirectorySyncEvent, DirectorySyncRequest } from "@boxyhq/saml-jackson";
|
||||
import type { NextApiRequest, NextApiResponse } from "next";
|
||||
|
||||
import handleGroupEvents from "@calcom/features/ee/dsync/lib/handleGroupEvents";
|
||||
import handleUserEvents from "@calcom/features/ee/dsync/lib/handleUserEvents";
|
||||
import jackson from "@calcom/features/ee/sso/lib/jackson";
|
||||
import prisma from "@calcom/prisma";
|
||||
|
||||
// This is the handler for the SCIM API requests
|
||||
export default async function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||
const { dsyncController } = await jackson();
|
||||
|
||||
const { method, query, body } = req;
|
||||
|
||||
const [directoryId, path, resourceId] = query.directory as string[];
|
||||
|
||||
// Handle the SCIM API requests
|
||||
const request: DirectorySyncRequest = {
|
||||
method: method as string,
|
||||
directoryId,
|
||||
resourceId,
|
||||
apiSecret: extractAuthToken(req),
|
||||
resourceType: path === "Users" ? "users" : "groups",
|
||||
body: body ? JSON.parse(body) : undefined,
|
||||
query: {
|
||||
count: req.query.count ? parseInt(req.query.count as string) : undefined,
|
||||
startIndex: req.query.startIndex ? parseInt(req.query.startIndex as string) : undefined,
|
||||
filter: req.query.filter as string,
|
||||
},
|
||||
};
|
||||
|
||||
const { status, data } = await dsyncController.requests.handle(request, handleEvents);
|
||||
|
||||
res.status(status).json(data);
|
||||
}
|
||||
|
||||
// Fetch the auth token from the request headers
|
||||
export const extractAuthToken = (req: NextApiRequest): string | null => {
|
||||
const authHeader = req.headers.authorization || null;
|
||||
|
||||
return authHeader ? authHeader.split(" ")[1] : null;
|
||||
};
|
||||
|
||||
// Handle the SCIM events
|
||||
const handleEvents = async (event: DirectorySyncEvent) => {
|
||||
const dSyncData = await prisma.dSyncData.findFirst({
|
||||
where: {
|
||||
directoryId: event.directory_id,
|
||||
},
|
||||
select: {
|
||||
id: true,
|
||||
organizationId: true,
|
||||
},
|
||||
});
|
||||
|
||||
if (!dSyncData) {
|
||||
throw new Error("Directory sync data not found");
|
||||
}
|
||||
|
||||
const { organizationId } = dSyncData;
|
||||
|
||||
if (!organizationId) {
|
||||
throw new Error(`Org ID not found for dsync ${dSyncData.id}`);
|
||||
}
|
||||
|
||||
if (event.event.includes("group")) {
|
||||
handleGroupEvents(event, organizationId);
|
||||
}
|
||||
|
||||
if (event.event === "user.created" || event.event === "user.updated") {
|
||||
await handleUserEvents(event, organizationId);
|
||||
}
|
||||
};
|
||||
@@ -0,0 +1,4 @@
|
||||
import { createNextApiHandler } from "@calcom/trpc/server/createNextApiHandler";
|
||||
import { dsyncRouter } from "@calcom/trpc/server/routers/viewer/dsync/_router";
|
||||
|
||||
export default createNextApiHandler(dsyncRouter);
|
||||
@@ -1,8 +1,9 @@
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import Link from "next/link";
|
||||
import { useRouter, usePathname } from "next/navigation";
|
||||
import { useCallback } from "react";
|
||||
import { useCallback, useState } from "react";
|
||||
|
||||
import { BulkEditDefaultForEventsModal } from "@calcom/features/eventtypes/components/BulkEditDefaultForEventsModal";
|
||||
import { NewScheduleButton, ScheduleListItem } from "@calcom/features/schedules";
|
||||
import Shell from "@calcom/features/shell/Shell";
|
||||
import { AvailabilitySliderTable } from "@calcom/features/timezone-buddy/components/AvailabilitySliderTable";
|
||||
@@ -21,6 +22,7 @@ import SkeletonLoader from "@components/availability/SkeletonLoader";
|
||||
|
||||
export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availability"]["list"]) {
|
||||
const { t } = useLocale();
|
||||
const [bulkUpdateModal, setBulkUpdateModal] = useState(false);
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const meQuery = trpc.viewer.me.useQuery();
|
||||
@@ -65,6 +67,7 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
|
||||
}),
|
||||
"success"
|
||||
);
|
||||
setBulkUpdateModal(true);
|
||||
},
|
||||
onError: (err) => {
|
||||
if (err instanceof HttpError) {
|
||||
@@ -74,6 +77,15 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
|
||||
},
|
||||
});
|
||||
|
||||
const bulkUpdateDefaultAvailabilityMutation =
|
||||
trpc.viewer.availability.schedule.bulkUpdateToDefaultAvailability.useMutation({
|
||||
onSuccess: () => {
|
||||
utils.viewer.availability.list.invalidate();
|
||||
setBulkUpdateModal(false);
|
||||
showToast(t("success"), "success");
|
||||
},
|
||||
});
|
||||
|
||||
const duplicateMutation = trpc.viewer.availability.schedule.duplicate.useMutation({
|
||||
onSuccess: async ({ schedule }) => {
|
||||
await router.push(`/availability/${schedule.id}`);
|
||||
@@ -129,6 +141,14 @@ export function AvailabilityList({ schedules }: RouterOutputs["viewer"]["availab
|
||||
{t("add_a_redirect")}
|
||||
</Link>
|
||||
</div>
|
||||
{bulkUpdateModal && (
|
||||
<BulkEditDefaultForEventsModal
|
||||
isPending={bulkUpdateDefaultAvailabilityMutation.isPending}
|
||||
open={bulkUpdateModal}
|
||||
setOpen={setBulkUpdateModal}
|
||||
bulkUpdateFunction={bulkUpdateDefaultAvailabilityMutation.mutate}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
|
||||
@@ -7,9 +7,9 @@ export type {
|
||||
CustomInputParsed,
|
||||
EventTypeSetup,
|
||||
EventTypeSetupProps,
|
||||
FormValues,
|
||||
Host,
|
||||
} from "~/event-types/views/event-types-single-view";
|
||||
export type { FormValues } from "@calcom/features/eventtypes/lib/types";
|
||||
|
||||
EventTypePageWrapper.PageWrapper = PageWrapper;
|
||||
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
import DirectorySyncTeamView from "@calcom/features/ee/dsync/page/team-dsync-view";
|
||||
|
||||
import type { CalPageWrapper } from "@components/PageWrapper";
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
const Page = DirectorySyncTeamView as CalPageWrapper;
|
||||
Page.PageWrapper = PageWrapper;
|
||||
|
||||
export default Page;
|
||||
@@ -0,0 +1,9 @@
|
||||
import PrivacyView from "@calcom/features/ee/organizations/pages/settings/privacy";
|
||||
|
||||
import type { CalPageWrapper } from "@components/PageWrapper";
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
const Page = PrivacyView as CalPageWrapper;
|
||||
Page.PageWrapper = PageWrapper;
|
||||
|
||||
export default Page;
|
||||
@@ -1,22 +1,63 @@
|
||||
"use client";
|
||||
|
||||
import Head from "next/head";
|
||||
import { useRouter } from "next/navigation";
|
||||
import { z } from "zod";
|
||||
|
||||
import { CreateANewTeamForm } from "@calcom/features/ee/teams/components";
|
||||
import { HOSTED_CAL_FEATURES } from "@calcom/lib/constants";
|
||||
import { getSafeRedirectUrl } from "@calcom/lib/getSafeRedirectUrl";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { useParamsWithFallback } from "@calcom/lib/hooks/useParamsWithFallback";
|
||||
import { telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import type { RouterOutputs } from "@calcom/trpc/react";
|
||||
import { WizardLayout } from "@calcom/ui";
|
||||
|
||||
import PageWrapper from "@components/PageWrapper";
|
||||
|
||||
const querySchema = z.object({
|
||||
returnTo: z.string().optional(),
|
||||
slug: z.string().optional(),
|
||||
});
|
||||
|
||||
const CreateNewTeamPage = () => {
|
||||
const { t } = useLocale();
|
||||
const params = useParamsWithFallback();
|
||||
const parsedQuery = querySchema.safeParse(params);
|
||||
const router = useRouter();
|
||||
const telemetry = useTelemetry();
|
||||
|
||||
const isTeamBillingEnabledClient = !!process.env.NEXT_PUBLIC_STRIPE_PUBLIC_KEY && HOSTED_CAL_FEATURES;
|
||||
const flag = isTeamBillingEnabledClient
|
||||
? {
|
||||
telemetryEvent: telemetryEventTypes.team_checkout_session_created,
|
||||
submitLabel: "checkout",
|
||||
}
|
||||
: {
|
||||
telemetryEvent: telemetryEventTypes.team_created,
|
||||
submitLabel: "continue",
|
||||
};
|
||||
|
||||
const returnToParam =
|
||||
(parsedQuery.success ? getSafeRedirectUrl(parsedQuery.data.returnTo) : "/teams") || "/teams";
|
||||
|
||||
const onSuccess = (data: RouterOutputs["viewer"]["teams"]["create"]) => {
|
||||
telemetry.event(flag.telemetryEvent);
|
||||
router.push(data.url);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{t("create_new_team")}</title>
|
||||
<meta name="description" content={t("create_new_team_description")} />
|
||||
</Head>
|
||||
<CreateANewTeamForm />
|
||||
<CreateANewTeamForm
|
||||
slug={parsedQuery.success ? parsedQuery.data.slug : ""}
|
||||
onCancel={() => router.push(returnToParam)}
|
||||
submitLabel={flag.submitLabel}
|
||||
onSuccess={onSuccess}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -20,6 +20,7 @@ import { useFlagMap } from "@calcom/features/flags/context/provider";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { APP_NAME, URL_PROTOCOL_REGEX, IS_CALCOM, WEBAPP_URL, WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||
import { pushGTMEvent } from "@calcom/lib/gtm";
|
||||
import { useCompatSearchParams } from "@calcom/lib/hooks/useCompatSearchParams";
|
||||
import { useDebounce } from "@calcom/lib/hooks/useDebounce";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -164,7 +165,6 @@ export default function Signup({
|
||||
const [premiumUsername, setPremiumUsername] = useState(false);
|
||||
const [usernameTaken, setUsernameTaken] = useState(false);
|
||||
const [isGoogleLoading, setIsGoogleLoading] = useState(false);
|
||||
|
||||
const searchParams = useCompatSearchParams();
|
||||
const telemetry = useTelemetry();
|
||||
const { t, i18n } = useLocale();
|
||||
@@ -223,6 +223,9 @@ export default function Signup({
|
||||
})
|
||||
.then(handleErrorsAndStripe)
|
||||
.then(async () => {
|
||||
if (process.env.NEXT_PUBLIC_GTM_ID)
|
||||
pushGTMEvent("create_account", { email: data.email, user: data.username, lang: data.language });
|
||||
|
||||
telemetry.event(telemetryEventTypes.signup, collectPageParameters());
|
||||
const verifyOrGettingStarted = flags["email-verification"] ? "auth/verify-email" : "getting-started";
|
||||
const callBackUrl = `${
|
||||
|
||||
@@ -50,6 +50,8 @@ function TeamPage({
|
||||
const isBioEmpty = !team.bio || !team.bio.replace("<p><br></p>", "").length;
|
||||
const metadata = teamMetadataSchema.parse(team.metadata);
|
||||
|
||||
const teamOrOrgIsPrivate = team.isPrivate || (team?.parent?.isOrganization && team.parent?.isPrivate);
|
||||
|
||||
useEffect(() => {
|
||||
telemetry.event(
|
||||
telemetryEventTypes.pageView,
|
||||
@@ -194,11 +196,17 @@ function TeamPage({
|
||||
)}
|
||||
</div>
|
||||
{team.isOrganization ? (
|
||||
<SubTeams />
|
||||
!teamOrOrgIsPrivate ? (
|
||||
<SubTeams />
|
||||
) : (
|
||||
<div className="w-full text-center">
|
||||
<h2 className="text-emphasis font-semibold">{t("you_cannot_see_teams_of_org")}</h2>
|
||||
</div>
|
||||
)
|
||||
) : (
|
||||
<>
|
||||
{(showMembers.isOn || !team.eventTypes?.length) &&
|
||||
(team.isPrivate ? (
|
||||
(teamOrOrgIsPrivate ? (
|
||||
<div className="w-full text-center">
|
||||
<h2 data-testid="you-cannot-see-team-members" className="text-emphasis font-semibold">
|
||||
{t("you_cannot_see_team_members")}
|
||||
@@ -212,7 +220,7 @@ function TeamPage({
|
||||
<EventTypes eventTypes={team.eventTypes} />
|
||||
|
||||
{/* Hide "Book a team member button when team is private or hideBookATeamMember is true" */}
|
||||
{!team.hideBookATeamMember && !team.isPrivate && (
|
||||
{!team.hideBookATeamMember && !teamOrOrgIsPrivate && (
|
||||
<div>
|
||||
<div className="relative mt-12">
|
||||
<div className="absolute inset-0 flex items-center" aria-hidden="true">
|
||||
|
||||
@@ -25,6 +25,7 @@ export const getMultipleDurationValue = (
|
||||
if (multipleDurationConfig.includes(Number(queryDuration))) return Number(queryDuration);
|
||||
return defaultValue;
|
||||
};
|
||||
|
||||
export default function Type({
|
||||
slug,
|
||||
user,
|
||||
@@ -34,6 +35,7 @@ export default function Type({
|
||||
isBrandingHidden,
|
||||
eventData,
|
||||
isInstantMeeting,
|
||||
orgBannerUrl,
|
||||
}: PageProps) {
|
||||
const searchParams = useSearchParams();
|
||||
|
||||
@@ -66,6 +68,7 @@ export default function Type({
|
||||
searchParams?.get("duration"),
|
||||
eventData.length
|
||||
)}
|
||||
orgBannerUrl={orgBannerUrl}
|
||||
/>
|
||||
</main>
|
||||
);
|
||||
|
||||
@@ -107,6 +107,10 @@ testBothFutureAndLegacyRoutes.describe("Event Types tests", () => {
|
||||
|
||||
expect(formTitle).toBe(firstTitle);
|
||||
expect(formSlug).toContain(firstSlug);
|
||||
|
||||
const test = await page.getByTestId("continue").click();
|
||||
const toast = await page.waitForSelector('[data-testid="toast-success"]');
|
||||
expect(toast).toBeTruthy();
|
||||
});
|
||||
|
||||
test("edit first event", async ({ page }) => {
|
||||
|
||||
@@ -11,9 +11,9 @@ import {
|
||||
selectFirstAvailableTimeSlotNextMonth,
|
||||
} from "./lib/testUtils";
|
||||
|
||||
test.afterEach(({ users }) => users.deleteAll());
|
||||
test.afterAll(({ users }) => users.deleteAll());
|
||||
|
||||
test.describe("Managed Event Types tests", () => {
|
||||
test.describe("Managed Event Types", () => {
|
||||
test("Can create managed event type", async ({ page, users }) => {
|
||||
// Creating the owner user of the team
|
||||
const adminUser = await users.create();
|
||||
@@ -61,7 +61,7 @@ test.describe("Managed Event Types tests", () => {
|
||||
await expect(page.locator('input[name="title"]')).toBeEditable();
|
||||
await expect(page.locator('input[name="slug"]')).toBeEditable();
|
||||
await expect(page.locator('input[name="length"]')).toBeEditable();
|
||||
await adminUser.logout();
|
||||
await adminUser.apiLogin();
|
||||
});
|
||||
|
||||
await test.step("Managed event type exists for added member", async () => {
|
||||
@@ -73,7 +73,7 @@ test.describe("Managed Event Types tests", () => {
|
||||
await page.locator('button[data-testid^="accept-invitation"]').click();
|
||||
await page.getByText("Member").waitFor();
|
||||
|
||||
await memberUser.logout();
|
||||
await page.goto("/auth/logout");
|
||||
|
||||
// Coming back as team owner to assign member user to managed event
|
||||
await adminUser.apiLogin();
|
||||
@@ -107,7 +107,6 @@ test.describe("Managed Event Types tests", () => {
|
||||
|
||||
await test.step("Managed event type has locked fields for added member", async () => {
|
||||
await adminUser.logout();
|
||||
|
||||
// Coming back as member user to see if there is a managed event present after assignment
|
||||
await memberUser.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
@@ -118,6 +117,90 @@ test.describe("Managed Event Types tests", () => {
|
||||
await expect(page.locator('input[name="title"]')).not.toBeEditable();
|
||||
await expect(page.locator('input[name="slug"]')).not.toBeEditable();
|
||||
await expect(page.locator('input[name="length"]')).not.toBeEditable();
|
||||
|
||||
await page.goto("/auth/logout");
|
||||
});
|
||||
|
||||
await test.step("Managed event type provides discrete field lock/unlock state for admin", async () => {
|
||||
await adminUser.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
|
||||
await page.getByTestId("event-types").locator('a[title="managed"]').click();
|
||||
await page.waitForURL("event-types/**");
|
||||
|
||||
// Locked by default
|
||||
const titleLockIndicator = page.getByTestId("locked-indicator-title");
|
||||
await expect(titleLockIndicator).toBeVisible();
|
||||
await expect(titleLockIndicator.locator("[data-state='checked']")).toHaveCount(1);
|
||||
|
||||
// Proceed to unlock and check that it got unlocked
|
||||
titleLockIndicator.click();
|
||||
await expect(titleLockIndicator.locator("[data-state='checked']")).toHaveCount(0);
|
||||
await expect(titleLockIndicator.locator("[data-state='unchecked']")).toHaveCount(1);
|
||||
|
||||
// Save changes
|
||||
await page.locator('[type="submit"]').click();
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
await page.goto("/auth/logout");
|
||||
});
|
||||
|
||||
await test.step("Managed event type shows discretionally unlocked field to member", async () => {
|
||||
await memberUser.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
|
||||
await page.getByTestId("event-types").locator('a[title="managed"]').click();
|
||||
await page.waitForURL("event-types/**");
|
||||
|
||||
await expect(page.locator('input[name="title"]')).toBeEditable();
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
await page.goto("/auth/logout");
|
||||
});
|
||||
|
||||
await test.step("Managed event type should only update the unlocked fields modified by Admin", async () => {
|
||||
await memberUser.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
|
||||
await page.getByTestId("event-types").locator('a[title="managed"]').click();
|
||||
await page.waitForURL("event-types/**");
|
||||
|
||||
await expect(page.locator('input[name="title"]')).toBeEditable();
|
||||
await page.locator('input[name="title"]').fill(`Managed Event Title`);
|
||||
// Save changes
|
||||
await page.locator('[type="submit"]').click();
|
||||
await page.getByTestId("toast-success").waitFor();
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
await page.goto("/auth/logout");
|
||||
|
||||
await adminUser.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
await page.getByTestId("event-types").locator('a[title="managed"]').click();
|
||||
await page.waitForURL("event-types/**");
|
||||
|
||||
await page.locator('input[name="length"]').fill(`45`);
|
||||
// Save changes
|
||||
await page.locator('[type="submit"]').click();
|
||||
await page.getByTestId("toast-success").waitFor();
|
||||
await page.waitForLoadState("networkidle");
|
||||
|
||||
await page.goto("/auth/logout");
|
||||
|
||||
await memberUser.apiLogin();
|
||||
await page.goto("/event-types");
|
||||
|
||||
await page.getByTestId("event-types").locator('a[title="Managed Event Title"]').click();
|
||||
await page.waitForURL("event-types/**");
|
||||
|
||||
//match length
|
||||
expect(await page.locator("[data-testid=duration]").getAttribute("value")).toBe("45");
|
||||
//ensure description didn't update
|
||||
expect(await page.locator(`input[name="title"]`).getAttribute("value")).toBe(`Managed Event Title`);
|
||||
await page.locator('input[name="title"]').fill(`managed`);
|
||||
// Save changes
|
||||
await page.locator('[type="submit"]').click();
|
||||
await page.getByTestId("toast-success").waitFor();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -10,6 +10,8 @@ test.beforeEach(async ({ page, users, bookingPage }) => {
|
||||
|
||||
await page.goto("/event-types");
|
||||
await bookingPage.goToEventType(teamEventTitle);
|
||||
await page.getByTestId("location-select").click();
|
||||
await page.locator(`text="Cal Video (Global)"`).click();
|
||||
await bookingPage.goToTab("event_advanced_tab_title");
|
||||
});
|
||||
|
||||
@@ -19,7 +21,6 @@ test.describe("Check advanced options in a managed team event type", () => {
|
||||
await bookingPage.checkRequiresBookerEmailVerification();
|
||||
await bookingPage.checkHideNotes();
|
||||
await bookingPage.checkRedirectOnBooking();
|
||||
await bookingPage.checkEnablePrivateUrl();
|
||||
await bookingPage.checkLockTimezone();
|
||||
await bookingPage.updateEventType();
|
||||
await bookingPage.goToEventTypesPage();
|
||||
@@ -32,7 +33,6 @@ test.describe("Check advanced options in a managed team event type", () => {
|
||||
await bookingPage.checkRequiresBookerEmailVerification();
|
||||
await bookingPage.checkHideNotes();
|
||||
await bookingPage.checkRedirectOnBooking();
|
||||
await bookingPage.checkEnablePrivateUrl();
|
||||
await bookingPage.toggleOfferSeats();
|
||||
await bookingPage.checkLockTimezone();
|
||||
await bookingPage.updateEventType();
|
||||
|
||||
@@ -33,17 +33,16 @@ test.describe("UploadAvatar", async () => {
|
||||
|
||||
const response = await prisma.avatar.findUniqueOrThrow({
|
||||
where: {
|
||||
teamId_userId: {
|
||||
teamId_userId_isBanner: {
|
||||
userId: user.id,
|
||||
teamId: 0,
|
||||
isBanner: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
// todo: remove this; ideally the organization-avatar is updated the moment
|
||||
// 'Settings updated succesfully' is saved.
|
||||
await page.waitForLoadState("networkidle");
|
||||
const avatar = page.getByTestId("profile-upload-avatar").locator("img");
|
||||
// Wait for the avatar image with the http src to appear instead of the data uri
|
||||
const avatar = page.getByTestId("profile-upload-avatar").locator("img[src^=http]");
|
||||
|
||||
const src = await avatar.getAttribute("src");
|
||||
|
||||
|
||||
@@ -275,6 +275,44 @@ testBothFutureAndLegacyRoutes.describe("Teams - NonOrg", (routeVariant) => {
|
||||
await expect(page.locator('[data-testid="you-cannot-see-team-members"]')).toBeVisible();
|
||||
await expect(page.locator('[data-testid="team-members-container"]')).toBeHidden();
|
||||
});
|
||||
test("Email Embeds slots are loading for team event types", async ({ page, users }) => {
|
||||
const teamMatesObj = [
|
||||
{ name: "teammate-1" },
|
||||
{ name: "teammate-2" },
|
||||
{ name: "teammate-3" },
|
||||
{ name: "teammate-4" },
|
||||
];
|
||||
|
||||
const owner = await users.create(
|
||||
{ username: "pro-user", name: "pro-user" },
|
||||
{
|
||||
hasTeam: true,
|
||||
teammates: teamMatesObj,
|
||||
schedulingType: SchedulingType.COLLECTIVE,
|
||||
}
|
||||
);
|
||||
|
||||
await owner.apiLogin();
|
||||
const { team } = await owner.getFirstTeamMembership();
|
||||
const {
|
||||
title: teamEventTitle,
|
||||
slug: teamEventSlug,
|
||||
id: teamEventId,
|
||||
} = await owner.getFirstTeamEvent(team.id);
|
||||
|
||||
await page.goto("/event-types");
|
||||
|
||||
await page.getByTestId(`event-type-options-${teamEventId}`).first().click();
|
||||
await page.getByTestId("embed").click();
|
||||
await page.getByTestId("email").click();
|
||||
await page.getByTestId("incrementMonth").click();
|
||||
|
||||
await expect(page.getByTestId("no-slots-available")).toBeHidden();
|
||||
|
||||
// Check Team Url
|
||||
const availableTimesUrl = await page.getByTestId("see_all_available_times").getAttribute("href");
|
||||
await expect(availableTimesUrl).toContain(`/team/${team.slug}/${teamEventSlug}`);
|
||||
});
|
||||
|
||||
todo("Create a Round Robin with different leastRecentlyBooked hosts");
|
||||
todo("Reschedule a Collective EventType booking");
|
||||
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "يجب أن يكون لديك جدول واحد على الأقل",
|
||||
"default_conferencing_bulk_description": "تحديث المواقع لأنواع الأحداث المحددة",
|
||||
"locked_for_members": "مقفل للأعضاء",
|
||||
"locked_apps_description": "سيتمكن الأعضاء من رؤية التطبيقات النشطة، ولكن لن يتمكنوا من تعديل أي إعدادات للتطبيق",
|
||||
"locked_webhooks_description": "سيكون الأعضاء قادرين على رؤية webhooks النشطة، ولكن من دون القدرة على تعديل أي إعدادات Webhook",
|
||||
"locked_workflows_description": "سيكون الأعضاء قادرين على رؤية مسارات العمل النشطة، ولكن من دون القدرة على تعديل أي إعدادات لمسار العمل",
|
||||
"locked_by_admin": "مقفل من قبل المشرف",
|
||||
"apps_locked_for_members_description": "سيتمكن الأعضاء من رؤية التطبيقات النشطة، ولكن لن يتمكنوا من تعديل أي إعدادات للتطبيق",
|
||||
"locked_by_team_admin": "مقفل من قبل المشرف",
|
||||
"app_not_connected": "أنت غير متصل بحساب {{appName}}.",
|
||||
"connect_now": "اتصل الآن",
|
||||
"managed_event_dialog_confirm_button_one": "الاستبدال وإخطار {{count}} عضو",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "أتمتة سير العمل",
|
||||
"scheduling_for_your_team": "أتمتة سير العمل",
|
||||
"no_members_found": "لم يُعثر على أعضاء",
|
||||
"directory_sync": "مزامنة الدليل",
|
||||
"event_setup_length_error": "إعداد الفعالية: يجب أن تكون المدة لدقيقة على الأقل.",
|
||||
"availability_schedules": "جدولة التوافر",
|
||||
"unauthorized": "غير مصرح به",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "عملية انضمام ودعم هندسي مخصصين",
|
||||
"need_help": "هل تحتاج إلى مساعدة؟",
|
||||
"show_more": "إظهار المزيد",
|
||||
"privacy": "الخصوصية",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ أضف السلاسل الجديدة أعلاه هنا ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
{}
|
||||
{}
|
||||
@@ -1 +1 @@
|
||||
{}
|
||||
{}
|
||||
@@ -21,4 +21,4 @@
|
||||
"load_more_results": "Carrega més resultats",
|
||||
"integration_meeting_id": "{{integrationName}} Identificador de la reunió: {{meetingId}}",
|
||||
"confirmed_event_type_subject": "Confirmat: {{eventType}} amb {{name}} el {{date}}"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "Musíte mít alespoň jeden rozvrh",
|
||||
"default_conferencing_bulk_description": "Aktualizujte místa pro vybrané typy událostí",
|
||||
"locked_for_members": "Zamčeno pro členy",
|
||||
"locked_apps_description": "Členové uvidí aktivní aplikace, ale nastavení aplikací upravovat nemohou",
|
||||
"locked_webhooks_description": "Členové uvidí aktivní webhooky, ale nastavení webhooků upravovat nemohou",
|
||||
"locked_workflows_description": "Členové uvidí aktivní pracovní postupy, ale nastavení pracovních postupů upravovat nemohou",
|
||||
"locked_by_admin": "Uzamčeno správcem týmu",
|
||||
"apps_locked_for_members_description": "Členové uvidí aktivní aplikace, ale nastavení aplikací upravovat nemohou",
|
||||
"locked_by_team_admin": "Uzamčeno správcem týmu",
|
||||
"app_not_connected": "Nemáte připojený účet {{appName}}.",
|
||||
"connect_now": "Připojte se",
|
||||
"managed_event_dialog_confirm_button_one": "Nahradit a upozornit {{count}} člena",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "Automatizace pracovních postupů",
|
||||
"scheduling_for_your_team": "Automatizace pracovních postupů",
|
||||
"no_members_found": "Nenalezeni žádní členové",
|
||||
"directory_sync": "Synchronizace adresářů",
|
||||
"event_setup_length_error": "Nastavení události: Doba trvání musí být alespoň 1 minuta.",
|
||||
"availability_schedules": "Plány dostupnosti",
|
||||
"unauthorized": "Neautorizováno",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Vyhrazená podpora zaškolovací a inženýrská podpora",
|
||||
"need_help": "Potřebujete pomoc?",
|
||||
"show_more": "Zobrazit více",
|
||||
"privacy": "Ochrana soukromí",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Přidejte své nové řetězce nahoru ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1535,4 +1535,4 @@
|
||||
"confirm_your_details": "Bekræft dine oplysninger",
|
||||
"overlay_my_calendar": "Vis min kalender",
|
||||
"need_help": "Brug for hjælp?"
|
||||
}
|
||||
}
|
||||
@@ -808,6 +808,7 @@
|
||||
"label": "Bezeichnung",
|
||||
"placeholder": "Platzhalter",
|
||||
"display_add_to_calendar_organizer": "„Zum Kalender hinzufügen“-E-Mail als Organisator anzeigen",
|
||||
"reconnect_calendar_to_use": "Bitte beachten Sie, dass Sie möglicherweise die Verbindung trennen und anschließend Ihr „Zum Kalender hinzufügen“-Konto erneut verbinden müssen, um diese Funktion nutzen zu können.",
|
||||
"type": "Typ",
|
||||
"edit": "Bearbeiten",
|
||||
"add_input": "Eingabe hinzufügen",
|
||||
@@ -1474,6 +1475,8 @@
|
||||
"copy_link_to_form": "Link in Formular kopieren",
|
||||
"theme": "Theme",
|
||||
"theme_applies_note": "Dies gilt nur für Ihre öffentlichen Buchungsseiten",
|
||||
"app_theme": "Dashboard-Theme",
|
||||
"app_theme_applies_note": "Dies gilt nur für Ihr eingeloggtes Dashboard",
|
||||
"theme_system": "Systemstandard",
|
||||
"add_a_team": "Team hinzufügen",
|
||||
"add_webhook_description": "Erhalten Sie Meetingdaten in Echtzeit, wenn etwas in {{appName}} passiert",
|
||||
@@ -1599,6 +1602,7 @@
|
||||
"attendee_email_info": "Die E-Mail-Adresse der buchenden Person",
|
||||
"kbar_search_placeholder": "Geben Sie einen Befehl ein oder suchen Sie ...",
|
||||
"invalid_credential": "Oh nein! Es sieht so aus, als ob die Berechtigung abgelaufen ist oder entzogen wurden. Bitte erneut installieren.",
|
||||
"invalid_credential_action": "App neu installieren",
|
||||
"reschedule_reason": "Verlegungsgrund",
|
||||
"choose_common_schedule_team_event": "Gemeinsamen Zeitplan auswählen",
|
||||
"choose_common_schedule_team_event_description": "Aktivieren Sie dies, wenn Sie einen gemeinsamen Zeitplan zwischen Hosts verwenden möchten. Wenn deaktiviert, ist jeder Host basierend auf seinen Standardplan buchbar.",
|
||||
@@ -1884,10 +1888,8 @@
|
||||
"requires_at_least_one_schedule": "Sie müssen mindestens einen Verfügbarkeitsplan haben",
|
||||
"default_conferencing_bulk_description": "Orte für die ausgewählten Termintypen aktualisieren",
|
||||
"locked_for_members": "Für Mitglieder gesperrt",
|
||||
"locked_apps_description": "Mitglieder können die aktiven Apps sehen, können aber keine App-Einstellungen anpassen",
|
||||
"locked_webhooks_description": "Mitglieder können die aktiven Webhooks sehen, können aber keine Webhook-Einstellungen anpassen",
|
||||
"locked_workflows_description": "Mitglieder können die aktiven Workflows sehen, können aber keine Workflow-Einstellungen anpassen",
|
||||
"locked_by_admin": "Vom Team-Admin gesperrt",
|
||||
"apps_locked_for_members_description": "Mitglieder können die aktiven Apps sehen, können aber keine App-Einstellungen anpassen",
|
||||
"locked_by_team_admin": "Vom Team-Admin gesperrt",
|
||||
"app_not_connected": "Sie haben kein {{appName}}-Konto verbunden.",
|
||||
"connect_now": "Jetzt verbinden",
|
||||
"managed_event_dialog_confirm_button_one": "{{count}} Mitglied ersetzen & benachrichtigen",
|
||||
@@ -2136,6 +2138,7 @@
|
||||
"scheduling_for_your_team": "Workflow-Automatisierung",
|
||||
"scheduling_for_your_team_description": "Planen Sie Ihr Team mit kollektiver Terminplanung und Round-Robin-Terminplanung",
|
||||
"no_members_found": "Keine Mitglieder gefunden",
|
||||
"directory_sync": "Verzeichnissynchronisierung",
|
||||
"event_setup_length_error": "Ereignis-Einrichtung: Die Dauer muss mindestens 1 Minute betragen.",
|
||||
"availability_schedules": "Verfügbarkeitspläne",
|
||||
"unauthorized": "Nicht authorisiert",
|
||||
@@ -2262,5 +2265,13 @@
|
||||
"field_identifiers_as_variables": "Verwenden Sie Feldbezeichner als Variablen für Ihre benutzerdefinierte Ereignisumleitung",
|
||||
"field_identifiers_as_variables_with_example": "Verwenden Sie Feldbezeichner als Variablen für Ihre benutzerdefinierte Ereignisumleitung (z. B. {{variable}})",
|
||||
"account_already_linked": "Konto ist bereits verknüpft",
|
||||
"privacy": "Datenschutz",
|
||||
"primary": "Primär",
|
||||
"make_primary": "Zur Primäradresse machen",
|
||||
"add_email": "E-Mail-Adresse hinzufügen",
|
||||
"add_email_description": "Fügen Sie eine E-Mail-Adresse hinzu, um die Ihre primäre E-Mail-Adresse ersetzt, oder als alternative E-Mail in Ihren Ereignistypen verwendet wird.",
|
||||
"confirm_email": "Bestätigen Sie Ihre E-Mail-Adresse",
|
||||
"confirm_email_description": "Wir haben eine E-Mail an <strong>{{email}}</strong> gesendet. Klicken Sie auf den Link in der E-Mail, um diese Adresse zu bestätigen.",
|
||||
"send_event_details_to": "Termindetails senden an",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Fügen Sie Ihre neuen Code-Zeilen über dieser hinzu ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -278,4 +278,4 @@
|
||||
"location_variable": "Τοποθεσία",
|
||||
"additional_notes_variable": "Πρόσθετες σημειώσεις",
|
||||
"already_have_account": "Έχετε ήδη λογαριασμό;"
|
||||
}
|
||||
}
|
||||
@@ -13,17 +13,17 @@
|
||||
"reset_password_subject": "{{appName}}: Reset password instructions",
|
||||
"verify_email_subject": "{{appName}}: Verify your account",
|
||||
"check_your_email": "Check your email",
|
||||
"old_email_address":"Old Email",
|
||||
"new_email_address":"New Email",
|
||||
"old_email_address": "Old Email",
|
||||
"new_email_address": "New Email",
|
||||
"verify_email_page_body": "We've sent an email to {{email}}. It is important to verify your email address to guarantee the best email and calendar deliverability from {{appName}}.",
|
||||
"verify_email_banner_body": "Verify your email address to guarantee the best email and calendar deliverability",
|
||||
"verify_email_email_header": "Verify your email address",
|
||||
"verify_email_email_button": "Verify email",
|
||||
"verify_email_change_description":"You have recently requested to change the email address you use to log into your {{appName}} account. Please click the button below to confirm your new email address.",
|
||||
"verify_email_change_success_toast":"Updated your email to {{email}}",
|
||||
"verify_email_change_failure_toast":"Failed to update email.",
|
||||
"change_of_email":"Verify your new {{appName}} email address",
|
||||
"change_of_email_toast":"We have sent a verification link to {{email}}. We will update your email address once you click this link.",
|
||||
"verify_email_change_description": "You have recently requested to change the email address you use to log into your {{appName}} account. Please click the button below to confirm your new email address.",
|
||||
"verify_email_change_success_toast": "Updated your email to {{email}}",
|
||||
"verify_email_change_failure_toast": "Failed to update email.",
|
||||
"change_of_email": "Verify your new {{appName}} email address",
|
||||
"change_of_email_toast": "We have sent a verification link to {{email}}. We will update your email address once you click this link.",
|
||||
"copy_somewhere_safe": "Save this API key somewhere safe. You will not be able to view it again.",
|
||||
"verify_email_email_body": "Please verify your email address by clicking the button below.",
|
||||
"verify_email_by_code_email_body": "Please verify your email address by using the below code.",
|
||||
@@ -63,8 +63,8 @@
|
||||
"a_refund_failed": "A refund failed",
|
||||
"awaiting_payment_subject": "Awaiting Payment: {{title}} on {{date}}",
|
||||
"meeting_awaiting_payment": "Your meeting is awaiting payment",
|
||||
"dark_theme_contrast_error":"Dark Theme color doesn't pass contrast check. We recommend you change this colour so your buttons will be more visible.",
|
||||
"light_theme_contrast_error":"Light Theme color doesn't pass contrast check. We recommend you change this colour so your buttons will be more visible.",
|
||||
"dark_theme_contrast_error": "Dark Theme color doesn't pass contrast check. We recommend you change this colour so your buttons will be more visible.",
|
||||
"light_theme_contrast_error": "Light Theme color doesn't pass contrast check. We recommend you change this colour so your buttons will be more visible.",
|
||||
"payment_not_created_error": "Payment could not be created",
|
||||
"couldnt_charge_card_error": "Could not charge card for Payment",
|
||||
"no_available_users_found_error": "No available users found. Could you try another time slot?",
|
||||
@@ -119,7 +119,7 @@
|
||||
"requested_to_reschedule_subject_attendee": "Action Required Reschedule: Please book a new time for {{eventType}} with {{name}}",
|
||||
"hi_user_name": "Hi {{name}}",
|
||||
"ics_event_title": "{{eventType}} with {{name}}",
|
||||
"please_book_a_time_sometime_later":"Oops, we couldn't connect you this time. Please schedule a future call instead",
|
||||
"please_book_a_time_sometime_later": "Oops, we couldn't connect you this time. Please schedule a future call instead",
|
||||
"new_event_subject": "New event: {{attendeeName}} - {{date}} - {{eventType}}",
|
||||
"join_by_entrypoint": "Join by {{entryPoint}}",
|
||||
"notes": "Notes",
|
||||
@@ -131,21 +131,22 @@
|
||||
"invitee_timezone": "Invitee Time Zone",
|
||||
"time_left": "Time left",
|
||||
"event_type": "Event Type",
|
||||
"duplicate_event_type": "Duplicate Event Type",
|
||||
"enter_meeting": "Enter Meeting",
|
||||
"video_call_provider": "Video call provider",
|
||||
"meeting_id": "Meeting ID",
|
||||
"meeting_password": "Meeting Password",
|
||||
"meeting_url": "Meeting URL",
|
||||
"meeting_url_not_found":"Meeting URL not found",
|
||||
"token_not_found":"Token not found",
|
||||
"some_other_host_already_accepted_the_meeting":"Some other host already accepted the meeting. Do you still want to join? <1>Continue to Meeting</1>",
|
||||
"meeting_url_not_found": "Meeting URL not found",
|
||||
"token_not_found": "Token not found",
|
||||
"some_other_host_already_accepted_the_meeting": "Some other host already accepted the meeting. Do you still want to join? <1>Continue to Meeting</1>",
|
||||
"meeting_request_rejected": "Your meeting request has been rejected",
|
||||
"rejected_event_type_with_organizer": "Rejected: {{eventType}} with {{organizer}} on {{date}}",
|
||||
"hi": "Hi",
|
||||
"join_team": "Join team",
|
||||
"manage_this_team": "Manage this team",
|
||||
"team_info": "Team Info",
|
||||
"join_meeting":"Join Meeting",
|
||||
"join_meeting": "Join Meeting",
|
||||
"request_another_invitation_email": "If you prefer not to use {{toEmail}} as your {{appName}} email or already have a {{appName}} account, please request another invitation to that email.",
|
||||
"you_have_been_invited": "You have been invited to join the team {{teamName}}",
|
||||
"user_invited_you": "{{user}} invited you to join the {{entity}} {{team}} on {{appName}}",
|
||||
@@ -251,6 +252,7 @@
|
||||
"confirm_auth_change": "This will change the way you log in",
|
||||
"confirm_auth_email_change": "Changing the email address will disconnect your current authentication method to log in to Cal.com. We will ask you to verify your new email address. Moving forward, you will be logged out and use your new email address to log in instead of your current authentication method after setting your password by following the instructions that will be sent to your mail.",
|
||||
"reset_your_password": "Set your new password with the instructions sent to your email address.",
|
||||
"org_banner_instructions": "Please upload image of {{width}} width and {{height}} height.",
|
||||
"email_change": "Log back in with your new email address and password.",
|
||||
"create_your_account": "Create your account",
|
||||
"create_your_calcom_account": "Create your Cal.com account",
|
||||
@@ -282,7 +284,7 @@
|
||||
"available_apps_lower_case": "Available apps",
|
||||
"available_apps_desc": "View popular apps below and explore more in our <1>App Store</1>",
|
||||
"fixed_host_helper": "Add anyone who needs to attend the event. <1>Learn more</1>",
|
||||
"round_robin_helper":"People in the group take turns and only one person will show up for the event.",
|
||||
"round_robin_helper": "People in the group take turns and only one person will show up for the event.",
|
||||
"check_email_reset_password": "Check your email. We sent you a link to reset your password.",
|
||||
"finish": "Finish",
|
||||
"organization_general_description": "Manage settings for your team language and timezone",
|
||||
@@ -291,9 +293,9 @@
|
||||
"nearly_there_instructions": "Last thing, a brief description about you and a photo really helps you get bookings and let people know who they’re booking with.",
|
||||
"set_availability_instructions": "Define ranges of time when you are available on a recurring basis. You can create more of these later and assign them to different calendars.",
|
||||
"set_availability": "Set your availability",
|
||||
"set_availbility_description":"Set schedules for the times you want to be booked.",
|
||||
"share_a_link_or_embed":"Share a link or embed",
|
||||
"share_a_link_or_embed_description":"Share your {{appName}} link or embed on your site.",
|
||||
"set_availbility_description": "Set schedules for the times you want to be booked.",
|
||||
"share_a_link_or_embed": "Share a link or embed",
|
||||
"share_a_link_or_embed_description": "Share your {{appName}} link or embed on your site.",
|
||||
"availability_settings": "Availability Settings",
|
||||
"continue_without_calendar": "Continue without calendar",
|
||||
"continue_with": "Continue with {{appName}}",
|
||||
@@ -318,8 +320,8 @@
|
||||
"when": "When",
|
||||
"where": "Where",
|
||||
"add_to_calendar": "Add to calendar",
|
||||
"add_to_calendar_description":"Select where to add events when you’re booked.",
|
||||
"add_events_to":"Add events to",
|
||||
"add_to_calendar_description": "Select where to add events when you’re booked.",
|
||||
"add_events_to": "Add events to",
|
||||
"add_another_calendar": "Add another calendar",
|
||||
"other": "Other",
|
||||
"email_sign_in_subject": "Your sign-in link for {{appName}}",
|
||||
@@ -448,7 +450,7 @@
|
||||
"browse_api_documentation": "Browse our API documentation",
|
||||
"leverage_our_api": "Leverage our API for full control and customizability.",
|
||||
"create_webhook": "Create Webhook",
|
||||
"instant_meeting":"Instant Meeting Created",
|
||||
"instant_meeting": "Instant Meeting Created",
|
||||
"booking_cancelled": "Booking Canceled",
|
||||
"booking_rescheduled": "Booking Rescheduled",
|
||||
"recording_ready": "Recording Download Link Ready",
|
||||
@@ -563,7 +565,7 @@
|
||||
"enter_number_between_range": "Please enter a number between 1 and {{maxOccurences}}",
|
||||
"email_address": "Email address",
|
||||
"enter_valid_email": "Please enter a valid email",
|
||||
"please_schedule_future_call":" Please schedule a future call if we're not available in {{seconds}} seconds",
|
||||
"please_schedule_future_call": " Please schedule a future call if we're not available in {{seconds}} seconds",
|
||||
"location": "Location",
|
||||
"address": "Address",
|
||||
"enter_address": "Enter address",
|
||||
@@ -637,8 +639,8 @@
|
||||
"hide_book_a_team_member": "Hide Book a Team Member Button",
|
||||
"hide_book_a_team_member_description": "Hide Book a Team Member Button from your public pages.",
|
||||
"danger_zone": "Danger zone",
|
||||
"account_deletion_cannot_be_undone":"Be Careful. Account deletion cannot be undone.",
|
||||
"team_deletion_cannot_be_undone":"Be Careful. Team deletion cannot be undone.",
|
||||
"account_deletion_cannot_be_undone": "Be Careful. Account deletion cannot be undone.",
|
||||
"team_deletion_cannot_be_undone": "Be Careful. Team deletion cannot be undone.",
|
||||
"back": "Back",
|
||||
"cancel": "Cancel",
|
||||
"cancel_all_remaining": "Cancel all remaining",
|
||||
@@ -702,10 +704,12 @@
|
||||
"add_members": "Add members...",
|
||||
"no_assigned_members": "No assigned members",
|
||||
"assigned_to": "Assigned to",
|
||||
"you_must_be_logged_in_to":"You must be logged in to {{url}}",
|
||||
"you_must_be_logged_in_to": "You must be logged in to {{url}}",
|
||||
"start_assigning_members_above": "Start assigning members above",
|
||||
"locked_fields_admin_description": "Members will not be able to edit this",
|
||||
"locked_fields_member_description": "This option was locked by the team admin",
|
||||
"locked_fields_admin_description": "Members can not edit",
|
||||
"unlocked_fields_admin_description": "Members can edit",
|
||||
"locked_fields_member_description": "Locked by the team admin",
|
||||
"unlocked_fields_member_description": "Unlocked by team admin",
|
||||
"url": "URL",
|
||||
"hidden": "Hidden",
|
||||
"readonly": "Readonly",
|
||||
@@ -807,7 +811,7 @@
|
||||
"additional_input_description": "Require scheduler to input additional inputs prior the booking is confirmed",
|
||||
"label": "Label",
|
||||
"placeholder": "Placeholder",
|
||||
"display_add_to_calendar_organizer":"Display 'Add to calendar' email as the organizer",
|
||||
"display_add_to_calendar_organizer": "Display 'Add to calendar' email as the organizer",
|
||||
"reconnect_calendar_to_use": "Please note that you may need to disconnect and then reconnect your 'Add to Calendar' account to use this feature.",
|
||||
"type": "Type",
|
||||
"edit": "Edit",
|
||||
@@ -817,9 +821,9 @@
|
||||
"requires_confirmation_description": "The booking needs to be manually confirmed before it is pushed to the integrations and a confirmation mail is sent.",
|
||||
"recurring_event": "Recurring Event",
|
||||
"recurring_event_description": "People can subscribe for recurring events",
|
||||
"cannot_be_used_with_paid_event_types":"It cannot be used with paid event types",
|
||||
"warning_payment_instant_meeting_event":"Instant Meetings are not supported with recurring events and payment apps yet",
|
||||
"warning_instant_meeting_experimental":"Experimental: Instant Meeting Events are currently experimental.",
|
||||
"cannot_be_used_with_paid_event_types": "It cannot be used with paid event types",
|
||||
"warning_payment_instant_meeting_event": "Instant Meetings are not supported with recurring events and payment apps yet",
|
||||
"warning_instant_meeting_experimental": "Experimental: Instant Meeting Events are currently experimental.",
|
||||
"starting": "Starting",
|
||||
"disable_guests": "Disable Guests",
|
||||
"disable_guests_description": "Disable adding additional guests while booking.",
|
||||
@@ -903,7 +907,7 @@
|
||||
"toggle_calendars_conflict": "Toggle the calendars you want to check for conflicts to prevent double bookings.",
|
||||
"connect_additional_calendar": "Connect additional calendar",
|
||||
"calendar_updated_successfully": "Calendar updated successfully",
|
||||
"check_here":"Check here",
|
||||
"check_here": "Check here",
|
||||
"conferencing": "Conferencing",
|
||||
"calendar": "Calendar",
|
||||
"payments": "Payments",
|
||||
@@ -1052,7 +1056,7 @@
|
||||
"confirm_delete_api_key": "Revoke this API key",
|
||||
"revoke_api_key": "Revoke API key",
|
||||
"api_key_copied": "API key copied!",
|
||||
"api_key_expires_on":"The API key will expire on",
|
||||
"api_key_expires_on": "The API key will expire on",
|
||||
"delete_api_key_confirm_title": "Permanently remove this API key from your account?",
|
||||
"copy": "Copy",
|
||||
"expire_date": "Expiration date",
|
||||
@@ -1075,12 +1079,13 @@
|
||||
"user_impersonation_heading": "User Impersonation",
|
||||
"user_impersonation_description": "Allows our support team to temporarily sign in as you to help us quickly resolve any issues you report to us.",
|
||||
"team_impersonation_description": "Allows your team Owners/Admins to temporarily sign in as you.",
|
||||
"cal_signup_description":"Free for individuals. Team plans for collaborative features.",
|
||||
"cal_signup_description": "Free for individuals. Team plans for collaborative features.",
|
||||
"make_org_private": "Make organization private",
|
||||
"make_org_private_description": "Your organization members won't be able to see other organization members when this is turned on.",
|
||||
"make_team_private": "Make team private",
|
||||
"make_team_private_description": "Your team members won't be able to see other team members when this is turned on.",
|
||||
"you_cannot_see_team_members": "You cannot see all the team members of a private team.",
|
||||
"you_cannot_see_teams_of_org":"You cannot see teams of a private organization.",
|
||||
"allow_booker_to_select_duration": "Allow booker to select duration",
|
||||
"impersonate_user_tip": "All uses of this feature is audited.",
|
||||
"impersonating_user_warning": "Impersonating username \"{{user}}\".",
|
||||
@@ -1272,7 +1277,7 @@
|
||||
"recordings_are_part_of_the_teams_plan": "Recordings are part of the teams plan",
|
||||
"team_feature_teams": "This is a Team feature. Upgrade to Team to see your team's availability.",
|
||||
"team_feature_workflows": "This is a Team feature. Upgrade to Team to automate your event notifications and reminders with Workflows.",
|
||||
"show_eventtype_on_profile": "Show on profile",
|
||||
"show_eventtype_on_profile": "Show on Profile",
|
||||
"embed": "Embed",
|
||||
"new_username": "New username",
|
||||
"current_username": "Current username",
|
||||
@@ -1301,7 +1306,7 @@
|
||||
"default_calendar_selected": "Default calendar",
|
||||
"hide_from_profile": "Hide from profile",
|
||||
"event_setup_tab_title": "Event Setup",
|
||||
"availability_not_found_in_schedule_error":"No availability found in schedule",
|
||||
"availability_not_found_in_schedule_error": "No availability found in schedule",
|
||||
"event_limit_tab_title": "Limits",
|
||||
"event_limit_tab_description": "How often you can be booked",
|
||||
"event_advanced_tab_description": "Calendar settings & more...",
|
||||
@@ -1322,13 +1327,13 @@
|
||||
"do_this": "Do this",
|
||||
"turn_off": "Turn off",
|
||||
"turn_on": "Turn on",
|
||||
"cancelled_bookings_cannot_be_rescheduled":"Canceled bookings cannot be rescheduled",
|
||||
"cancelled_bookings_cannot_be_rescheduled": "Canceled bookings cannot be rescheduled",
|
||||
"settings_updated_successfully": "Settings updated successfully",
|
||||
"error_updating_settings": "Error updating settings",
|
||||
"personal_cal_url": "My personal {{appName}} URL",
|
||||
"bio_hint": "A few sentences about yourself. this will appear on your personal url page.",
|
||||
"user_has_no_bio": "This user has not added a bio yet.",
|
||||
"bio":"Bio",
|
||||
"bio": "Bio",
|
||||
"delete_account_modal_title": "Delete Account",
|
||||
"confirm_delete_account_modal": "Are you sure you want to delete your {{appName}} account?",
|
||||
"delete_my_account": "Delete my account",
|
||||
@@ -1339,14 +1344,14 @@
|
||||
"select_calendars": "Select which calendars you want to check for conflicts to prevent double bookings.",
|
||||
"check_for_conflicts": "Check for conflicts",
|
||||
"view_recordings": "View recordings",
|
||||
"check_for_recordings":"Check for recordings",
|
||||
"check_for_recordings": "Check for recordings",
|
||||
"adding_events_to": "Adding events to",
|
||||
"follow_system_preferences": "Follow system preferences",
|
||||
"custom_brand_colors": "Custom brand colors",
|
||||
"customize_your_brand_colors": "Customize your own brand colour into your booking page.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "Removes any {{appName}} related brandings, i.e. 'Powered by {{appName}}.'",
|
||||
"instant_meeting_with_title":"Instant Meeting with {{name}}",
|
||||
"instant_meeting_with_title": "Instant Meeting with {{name}}",
|
||||
"profile_picture": "Profile Picture",
|
||||
"upload": "Upload",
|
||||
"add_profile_photo": "Add profile photo",
|
||||
@@ -1616,6 +1621,7 @@
|
||||
"test_routing": "Test Routing",
|
||||
"payment_app_disabled": "An admin has disabled a payment app",
|
||||
"edit_event_type": "Edit event type",
|
||||
"only_admin_can_see_members_of_org": "This Organization is private, and only the organization's admin or owner can view its members.",
|
||||
"collective_scheduling": "Collective Scheduling",
|
||||
"make_it_easy_to_book": "Make it easy to book your team when everyone is available.",
|
||||
"find_the_best_person": "Find the best person available and cycle through your team.",
|
||||
@@ -1643,7 +1649,7 @@
|
||||
"enable_apps": "Enable Apps",
|
||||
"enable_apps_description": "Enable apps that users can integrate with {{appName}}",
|
||||
"purchase_license": "Purchase a License",
|
||||
"already_have_account":"Already have an account?",
|
||||
"already_have_account": "Already have an account?",
|
||||
"already_have_key": "I already have a key:",
|
||||
"already_have_key_suggestion": "Please copy your existing CALCOM_LICENSE_KEY environment variable here.",
|
||||
"app_is_enabled": "{{appName}} is enabled",
|
||||
@@ -1712,7 +1718,7 @@
|
||||
"minimum_round_robin_hosts_count": "Number of hosts required to attend",
|
||||
"hosts": "Hosts",
|
||||
"upgrade_to_enable_feature": "You need to create a team to enable this feature. Click to create a team.",
|
||||
"orgs_upgrade_to_enable_feature" : "You need to upgrade to our enterprise plan to enable this feature.",
|
||||
"orgs_upgrade_to_enable_feature": "You need to upgrade to our enterprise plan to enable this feature.",
|
||||
"new_attendee": "New Attendee",
|
||||
"awaiting_approval": "Awaiting Approval",
|
||||
"requires_google_calendar": "This app requires a Google Calendar connection",
|
||||
@@ -1888,10 +1894,16 @@
|
||||
"requires_at_least_one_schedule": "You are required to have at least one schedule",
|
||||
"default_conferencing_bulk_description": "Update the locations for the selected event types",
|
||||
"locked_for_members": "Locked for members",
|
||||
"locked_apps_description": "Members will be able to see the active apps but will not be able to edit any app settings",
|
||||
"locked_webhooks_description": "Members will be able to see the active webhooks but will not be able to edit any webhook settings",
|
||||
"locked_workflows_description": "Members will be able to see the active workflows but will not be able to edit any workflow settings",
|
||||
"locked_by_admin": "Locked by team admin",
|
||||
"unlocked_for_members": "Unlocked for members",
|
||||
"apps_locked_for_members_description": "Members will be able to see the active apps but will not be able to edit any app settings",
|
||||
"apps_unlocked_for_members_description": "Members will be able to see the active apps and will be able to edit any app settings",
|
||||
"apps_locked_by_team_admins_description": "You will be able to see the active apps but will not be able to edit any app settings",
|
||||
"apps_unlocked_by_team_admins_description": "You will be able to see the active apps and will be able to edit any app settings",
|
||||
"workflows_locked_for_members_description": "Members can not add their personal workflows to this event type. Members will be able to see the active team workflows but will not be able to edit any workflow settings.",
|
||||
"workflows_unlocked_for_members_description": "Members will be able to add their personal workflows to this event type. Members will be able to see the active team workflows but will not be able to edit any workflow settings.",
|
||||
"workflows_locked_by_team_admins_description": "You will be able to see the active team workflows but will not be able to edit any workflow settings or add your personal workflows to this event type.",
|
||||
"workflows_unlocked_by_team_admins_description": "You will be able to enable/disable personal workflows on this event type. You will be able to see the active team workflows but will not be able to edit any team workflow settings.",
|
||||
"locked_by_team_admin": "Locked by team admin",
|
||||
"app_not_connected": "You have not connected a {{appName}} account.",
|
||||
"connect_now": "Connect now",
|
||||
"managed_event_dialog_confirm_button_one": "Replace & notify {{count}} member",
|
||||
@@ -1947,8 +1959,8 @@
|
||||
"insights_no_data_found_for_filter": "No data found for the selected filter or selected dates.",
|
||||
"acknowledge_booking_no_show_fee": "I acknowledge that if I do not attend this event that a {{amount, currency}} no show fee will be applied to my card.",
|
||||
"card_details": "Card details",
|
||||
"something_went_wrong_on_our_end":"Something went wrong on our end. Get in touch with our support team, and we’ll get it fixed right away for you.",
|
||||
"please_provide_following_text_to_suppport":"Please provide the following text when contacting support to better help you",
|
||||
"something_went_wrong_on_our_end": "Something went wrong on our end. Get in touch with our support team, and we’ll get it fixed right away for you.",
|
||||
"please_provide_following_text_to_suppport": "Please provide the following text when contacting support to better help you",
|
||||
"seats_and_no_show_fee_error": "Currently cannot enable seats and charge a no-show fee",
|
||||
"complete_your_booking": "Complete your booking",
|
||||
"complete_your_booking_subject": "Complete your booking: {{title}} on {{date}}",
|
||||
@@ -1957,7 +1969,7 @@
|
||||
"edit_invite_link": "Edit link settings",
|
||||
"invite_link_copied": "Invite link copied",
|
||||
"invite_link_deleted": "Invite link deleted",
|
||||
"api_key_deleted":"API Key deleted",
|
||||
"api_key_deleted": "API Key deleted",
|
||||
"invite_link_updated": "Invite link settings saved",
|
||||
"link_expires_after": "Links set to expire after...",
|
||||
"one_day": "1 day",
|
||||
@@ -1997,7 +2009,7 @@
|
||||
"connect_google_workspace": "Connect Google Workspace",
|
||||
"google_workspace_admin_tooltip": "You must be a Workspace Admin to use this feature",
|
||||
"first_event_type_webhook_description": "Create your first webhook for this event type",
|
||||
"create_instant_meeting_webhook_description":"Create your first webhook with 'Instant Meeting Created' as trigger for this event type",
|
||||
"create_instant_meeting_webhook_description": "Create your first webhook with 'Instant Meeting Created' as trigger for this event type",
|
||||
"install_app_on": "Install app on",
|
||||
"create_for": "Create for",
|
||||
"currency": "Currency",
|
||||
@@ -2030,8 +2042,8 @@
|
||||
"verify_email_organization": "Verify your email to create an organization",
|
||||
"code_provided_invalid": "The code provided is not valid, try again",
|
||||
"email_already_used": "Email already being used",
|
||||
"organization_admin_invited_heading":"You've been invited to join {{orgName}}",
|
||||
"organization_admin_invited_body":"Join your team at {{orgName}} and start focusing on meeting, not making meetings!",
|
||||
"organization_admin_invited_heading": "You've been invited to join {{orgName}}",
|
||||
"organization_admin_invited_body": "Join your team at {{orgName}} and start focusing on meeting, not making meetings!",
|
||||
"duplicated_slugs_warning": "The following teams couldn't be created due to duplicated slugs: {{slugs}}",
|
||||
"team_names_empty": "Team names can't be empty",
|
||||
"team_names_repeated": "Team names can't be repeated",
|
||||
@@ -2062,8 +2074,8 @@
|
||||
"add_times_to_your_email": "Select a few available times and embed them in your Email",
|
||||
"select_time": "Select Time",
|
||||
"select_date": "Select Date",
|
||||
"connecting_you_to_someone":"We are connecting you to someone.",
|
||||
"please_do_not_close_this_tab":"Please do not close this tab",
|
||||
"connecting_you_to_someone": "We are connecting you to someone.",
|
||||
"please_do_not_close_this_tab": "Please do not close this tab",
|
||||
"see_all_available_times": "See all available times",
|
||||
"org_team_names_example_1": "e.g. Marketing Team",
|
||||
"org_team_names_example_2": "e.g. Sales Team",
|
||||
@@ -2072,21 +2084,22 @@
|
||||
"org_team_names_example_5": "e.g. Data Analytics Team",
|
||||
"org_max_team_warnings": "You will be able to add more teams later on.",
|
||||
"what_is_this_meeting_about": "What is this meeting about?",
|
||||
"add_to_team":"Add to team",
|
||||
"add_to_team": "Add to team",
|
||||
"remove_users_from_org": "Remove users from organization",
|
||||
"remove_users_from_org_confirm":"Are you sure you want to remove {{userCount}} users from this organization?",
|
||||
"user_has_no_schedules":"This user has not setup any schedules yet",
|
||||
"user_isnt_in_any_teams":"This user is not in any teams",
|
||||
"remove_users_from_org_confirm": "Are you sure you want to remove {{userCount}} users from this organization?",
|
||||
"user_has_no_schedules": "This user has not setup any schedules yet",
|
||||
"user_isnt_in_any_teams": "This user is not in any teams",
|
||||
"requires_booker_email_verification": "Requires booker email verification",
|
||||
"description_requires_booker_email_verification": "To ensure booker's email verification before scheduling events",
|
||||
"requires_confirmation_mandatory": "Text messages can only be sent to attendees when event type requires confirmation.",
|
||||
"organizations": "Organizations",
|
||||
"upload_cal_video_logo":"Upload Cal Video Logo",
|
||||
"update_cal_video_logo":"Update Cal Video Logo",
|
||||
"cal_video_logo_upload_instruction":"To ensure your logo is visible against Cal video's dark background, please upload a light-colored image in PNG or SVG format to maintain transparency.",
|
||||
"upload_cal_video_logo": "Upload Cal Video Logo",
|
||||
"update_cal_video_logo": "Update Cal Video Logo",
|
||||
"upload_banner": "Upload Banner",
|
||||
"cal_video_logo_upload_instruction": "To ensure your logo is visible against Cal video's dark background, please upload a light-colored image in PNG or SVG format to maintain transparency.",
|
||||
"org_admin_other_teams": "Other teams",
|
||||
"org_admin_other_teams_description": "Here you can see teams inside your organization that you are not part of. You can add yourself to them if needed.",
|
||||
"not_part_of_org":"You are not part of any organization",
|
||||
"not_part_of_org": "You are not part of any organization",
|
||||
"no_other_teams_found": "No other teams found",
|
||||
"no_other_teams_found_description": "There are no other teams in this organization.",
|
||||
"attendee_first_name_variable": "Attendee first name",
|
||||
@@ -2098,7 +2111,7 @@
|
||||
"most_popular_events": "Most Popular Events",
|
||||
"summary_of_events_for_your_team_for_the_last_30_days": "Here's your summary of popular events for your team {{teamName}} for the last 30 days",
|
||||
"me": "Me",
|
||||
"monthly_digest_email":"Monthly Digest Email",
|
||||
"monthly_digest_email": "Monthly Digest Email",
|
||||
"monthly_digest_email_for_teams": "Monthly digest email for teams",
|
||||
"verify_team_tooltip": "Verify your team to enable sending messages to attendees",
|
||||
"member_removed": "Member removed",
|
||||
@@ -2132,34 +2145,52 @@
|
||||
"seat_options_doesnt_multiple_durations": "Seat option doesn't support multiple durations",
|
||||
"include_calendar_event": "Include calendar event",
|
||||
"oAuth": "OAuth",
|
||||
"recently_added":"Recently added",
|
||||
"connect_all_calendars":"Connect all your calendars",
|
||||
"connect_all_calendars_description":"{{appName}} reads availability from all your existing calendars.",
|
||||
"workflow_automation":"Workflow automation",
|
||||
"workflow_automation_description":"Personalise your scheduling experience with workflows",
|
||||
"scheduling_for_your_team":"Workflow automation",
|
||||
"scheduling_for_your_team_description":"Schedule for your team with collective and round-robin scheduling",
|
||||
"recently_added": "Recently added",
|
||||
"connect_all_calendars": "Connect all your calendars",
|
||||
"connect_all_calendars_description": "{{appName}} reads availability from all your existing calendars.",
|
||||
"workflow_automation": "Workflow automation",
|
||||
"workflow_automation_description": "Personalise your scheduling experience with workflows",
|
||||
"scheduling_for_your_team": "Workflow automation",
|
||||
"scheduling_for_your_team_description": "Schedule for your team with collective and round-robin scheduling",
|
||||
"no_members_found": "No members found",
|
||||
"event_setup_length_error":"Event Setup: The duration must be at least 1 minute.",
|
||||
"availability_schedules":"Availability Schedules",
|
||||
"unauthorized":"Unauthorized",
|
||||
"directory_sync": "Directory Sync",
|
||||
"directory_name": "Directory Name",
|
||||
"directory_provider": "Directory Provider",
|
||||
"directory_scim_url": "SCIM Base URL",
|
||||
"directory_scim_token": "SCIM Bearer Token",
|
||||
"directory_scim_url_copied": "SCIM Base URL copied",
|
||||
"directory_scim_token_copied": "SCIM Bearer Token copied",
|
||||
"directory_sync_info_description": "Your Identity Provider will ask for the following information to configure SCIM. Follow the instructions to finish the setup.",
|
||||
"directory_sync_configure": "Configure Directory Sync",
|
||||
"directory_sync_configure_description": "Choose an identity provider to configure directory for your team.",
|
||||
"directory_sync_title": "Configure an identity provider to get started with SCIM.",
|
||||
"directory_sync_created": "Directory sync connection created.",
|
||||
"directory_sync_description": "Provision and de-provision users with your directory provider.",
|
||||
"directory_sync_deleted": "Directory sync connection deleted.",
|
||||
"directory_sync_delete_connection": "Delete Connection",
|
||||
"directory_sync_delete_title": "Delete Directory Sync Connection",
|
||||
"directory_sync_delete_description": "Are you sure you want to delete this directory sync connection?",
|
||||
"directory_sync_delete_confirmation": "This action cannot be undone. This will permanently delete the directory sync connection.",
|
||||
"event_setup_length_error": "Event Setup: The duration must be at least 1 minute.",
|
||||
"availability_schedules": "Availability Schedules",
|
||||
"unauthorized": "Unauthorized",
|
||||
"access_cal_account": "{{clientName}} would like access to your {{appName}} account",
|
||||
"select_account_team": "Select account or team",
|
||||
"allow_client_to": "This will allow {{clientName}} to",
|
||||
"associate_with_cal_account":"Associate you with your personal info from {{clientName}}",
|
||||
"see_personal_info":"See your personal info, including any personal info you've made publicly available",
|
||||
"see_primary_email_address":"See your primary email address",
|
||||
"connect_installed_apps":"Connect to your installed apps",
|
||||
"associate_with_cal_account": "Associate you with your personal info from {{clientName}}",
|
||||
"see_personal_info": "See your personal info, including any personal info you've made publicly available",
|
||||
"see_primary_email_address": "See your primary email address",
|
||||
"connect_installed_apps": "Connect to your installed apps",
|
||||
"access_event_type": "Read, edit, delete your event-types",
|
||||
"access_availability": "Read, edit, delete your availability",
|
||||
"access_bookings": "Read, edit, delete your bookings",
|
||||
"allow_client_to_do": "Allow {{clientName}} to do this?",
|
||||
"oauth_access_information": "By clicking allow, you allow this app to use your information in accordance with their terms of service and privacy policy. You can remove access in the {{appName}} App Store.",
|
||||
"allow": "Allow",
|
||||
"view_only_edit_availability_not_onboarded":"This user has not completed onboarding. You will not be able to set their availability until they have completed onboarding.",
|
||||
"view_only_edit_availability":"You are viewing this user's availability. You can only edit your own availability.",
|
||||
"you_can_override_calendar_in_advanced_tab":"You can override this on a per-event basis in Advanced settings in each event type.",
|
||||
"edit_users_availability":"Edit user's availability: {{username}}",
|
||||
"view_only_edit_availability_not_onboarded": "This user has not completed onboarding. You will not be able to set their availability until they have completed onboarding.",
|
||||
"view_only_edit_availability": "You are viewing this user's availability. You can only edit your own availability.",
|
||||
"you_can_override_calendar_in_advanced_tab": "You can override this on a per-event basis in Advanced settings in each event type.",
|
||||
"edit_users_availability": "Edit user's availability: {{username}}",
|
||||
"resend_invitation": "Resend invitation",
|
||||
"invitation_resent": "The invitation was resent.",
|
||||
"saml_sso": "SAML",
|
||||
@@ -2168,20 +2199,22 @@
|
||||
"add_new_client": "Add new Client",
|
||||
"this_app_is_not_setup_already": "This app has not been setup yet",
|
||||
"as_csv": "as CSV",
|
||||
"overlay_my_calendar":"Overlay my calendar",
|
||||
"overlay_my_calendar_toc":"By connecting to your calendar, you accept our privacy policy and terms of use. You may revoke access at any time.",
|
||||
"view_overlay_calendar_events":"View your calendar events to prevent clashed booking.",
|
||||
"join_event_location":"Join {{eventLocationType}}",
|
||||
"troubleshooting":"Troubleshooting",
|
||||
"calendars_were_checking_for_conflicts":"Calendars we’re checking for conflicts",
|
||||
"availabilty_schedules":"Availability schedules",
|
||||
"manage_calendars":"Manage calendars",
|
||||
"manage_availability_schedules":"Manage availability schedules",
|
||||
"overlay_my_calendar": "Overlay my calendar",
|
||||
"overlay_my_calendar_toc": "By connecting to your calendar, you accept our privacy policy and terms of use. You may revoke access at any time.",
|
||||
"view_overlay_calendar_events": "View your calendar events to prevent clashed booking.",
|
||||
"join_event_location": "Join {{eventLocationType}}",
|
||||
"troubleshooting": "Troubleshooting",
|
||||
"calendars_were_checking_for_conflicts": "Calendars we’re checking for conflicts",
|
||||
"availabilty_schedules": "Availability schedules",
|
||||
"manage_calendars": "Manage calendars",
|
||||
"manage_availability_schedules": "Manage availability schedules",
|
||||
"locked": "Locked",
|
||||
"unlocked": "Unlocked",
|
||||
"lock_timezone_toggle_on_booking_page": "Lock timezone on booking page",
|
||||
"description_lock_timezone_toggle_on_booking_page" : "To lock the timezone on booking page, useful for in-person events.",
|
||||
"description_lock_timezone_toggle_on_booking_page": "To lock the timezone on booking page, useful for in-person events.",
|
||||
"event_setup_multiple_payment_apps_error": "You can only have one payment app enabled per event type.",
|
||||
"number_in_international_format": "Please enter number in international format.",
|
||||
"install_calendar":"Install Calendar",
|
||||
"install_calendar": "Install Calendar",
|
||||
"branded_subdomain": "Branded Subdomain",
|
||||
"branded_subdomain_description": "Get your own branded subdomain, such as acme.cal.com",
|
||||
"org_insights": "Organization-wide Insights",
|
||||
@@ -2235,8 +2268,8 @@
|
||||
"booking_redirect_email_subject": "Booking redirect notification",
|
||||
"booking_redirect_email_title": "Booking Redirect Notification",
|
||||
"booking_redirect_email_description": "You have received a booking redirection from {{toName}} so their profile links will be redirect to yours for the time interval: ",
|
||||
"success_accept_booking_redirect":"You have accepted this booking redirect request.",
|
||||
"success_reject_booking_redirect":"You have rejected this booking redirect request.",
|
||||
"success_accept_booking_redirect": "You have accepted this booking redirect request.",
|
||||
"success_reject_booking_redirect": "You have rejected this booking redirect request.",
|
||||
"copy_link_booking_redirect_request": "Copy link to share request",
|
||||
"booking_redirect_request_title": "Booking Redirect Request",
|
||||
"select_team_member": "Select team member",
|
||||
@@ -2245,7 +2278,7 @@
|
||||
"redirect_team_disabled": "Redirect your profile to another team member (Team plan required)",
|
||||
"out_of_office_unavailable_list": "Out of office unavailability list",
|
||||
"success_deleted_entry_out_of_office": "Successfully deleted entry",
|
||||
"temporarily_out_of_office":"Temporarily Out-Of-Office?",
|
||||
"temporarily_out_of_office": "Temporarily Out-Of-Office?",
|
||||
"add_a_redirect": "Add a redirect",
|
||||
"create_entry": "Create entry",
|
||||
"time_range": "Time range",
|
||||
@@ -2253,7 +2286,7 @@
|
||||
"redirect_to": "Redirect to",
|
||||
"having_trouble_finding_time": "Having trouble finding a time?",
|
||||
"show_more": "Show more",
|
||||
"assignment_description":"Schedule meetings when everyone is available or rotate through members on your team",
|
||||
"assignment_description": "Schedule meetings when everyone is available or rotate through members on your team",
|
||||
"lowest": "lowest",
|
||||
"low": "low",
|
||||
"medium": "medium",
|
||||
@@ -2266,6 +2299,11 @@
|
||||
"field_identifiers_as_variables": "Use field identifiers as variables for your custom event redirect",
|
||||
"field_identifiers_as_variables_with_example": "Use field identifiers as variables for your custom event redirect (e.g. {{variable}})",
|
||||
"account_already_linked": "Account is already linked",
|
||||
"privacy_organization_description": "Manage privacy settings for your organization",
|
||||
"privacy": "Privacy",
|
||||
"team_will_be_under_org": "New teams will be under your organization",
|
||||
"add_group_name": "Add group name",
|
||||
"group_name": "Group Name",
|
||||
"routers": "Routers",
|
||||
"primary": "Primary",
|
||||
"make_primary": "Make primary",
|
||||
@@ -2275,4 +2313,4 @@
|
||||
"confirm_email_description": "We sent an email to <strong>{{email}}</strong>. Click the link in the email to verify this address.",
|
||||
"send_event_details_to": "Send event details to",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1822,10 +1822,8 @@
|
||||
"requires_at_least_one_schedule": "Es necesario tener al menos un horario",
|
||||
"default_conferencing_bulk_description": "Actualizar las ubicaciones para los tipos de eventos seleccionados",
|
||||
"locked_for_members": "Bloqueado para miembros",
|
||||
"locked_apps_description": "Los miembros podrán ver las aplicaciones activas, pero no podrán editar ninguna configuración de la aplicación",
|
||||
"locked_webhooks_description": "Los miembros podrán ver los webhooks activos, pero no podrán editar ninguna configuración de webhooks",
|
||||
"locked_workflows_description": "Los miembros podrán ver los flujos de trabajo activos, pero no podrán editar ninguna configuración de flujo de trabajo",
|
||||
"locked_by_admin": "Bloqueado por el administrador del equipo",
|
||||
"apps_locked_for_members_description": "Los miembros podrán ver las aplicaciones activas, pero no podrán editar ninguna configuración de la aplicación",
|
||||
"locked_by_team_admin": "Bloqueado por el administrador del equipo",
|
||||
"app_not_connected": "No ha conectado una cuenta de {{appName}}.",
|
||||
"connect_now": "Conectar ahora",
|
||||
"managed_event_dialog_confirm_button_one": "Reemplazar y notificar a {{count}} miembro",
|
||||
@@ -2060,6 +2058,7 @@
|
||||
"workflow_automation": "Automatización del flujo de trabajo",
|
||||
"scheduling_for_your_team": "Automatización del flujo de trabajo",
|
||||
"no_members_found": "No se encontraron miembros",
|
||||
"directory_sync": "Directory Sync",
|
||||
"event_setup_length_error": "Configuración del evento: la duración debe ser de al menos 1 minuto.",
|
||||
"availability_schedules": "Horarios de disponibilidad",
|
||||
"unauthorized": "Sin autorización",
|
||||
@@ -2096,5 +2095,6 @@
|
||||
"extensive_whitelabeling": "Asistencia dedicada en materia de incorporación e ingeniería",
|
||||
"need_help": "¿Necesita ayuda?",
|
||||
"show_more": "Mostrar más",
|
||||
"privacy": "Privacidad",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Agregue sus nuevas cadenas arriba ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -761,4 +761,4 @@
|
||||
"radio": "Irratia",
|
||||
"all_bookings_filter_label": "Erreserba guztiak",
|
||||
"timezone_variable": "Ordu-eremua"
|
||||
}
|
||||
}
|
||||
@@ -251,6 +251,7 @@
|
||||
"confirm_auth_change": "Cela changera la façon dont vous vous connectez",
|
||||
"confirm_auth_email_change": "La modification de l'adresse e-mail déconnectera votre méthode d'authentification actuelle pour vous connecter à Cal.com. Nous vous demanderons de vérifier votre nouvelle adresse e-mail. Ensuite, vous serez déconnecté et devrez utiliser votre nouvelle adresse e-mail pour vous connecter après avoir défini votre mot de passe en suivant les instructions qui seront envoyées à votre adresse e-mail.",
|
||||
"reset_your_password": "Définissez votre nouveau mot de passe avec les instructions envoyées à votre adresse e-mail.",
|
||||
"org_banner_instructions": "Veuillez télécharger une image d'une largeur de {{width}} et d'une hauteur de {{height}}.",
|
||||
"email_change": "Reconnectez-vous avec votre nouvelle adresse e-mail et votre nouveau mot de passe.",
|
||||
"create_your_account": "Créez votre compte",
|
||||
"create_your_calcom_account": "Créez votre compte Cal.com",
|
||||
@@ -808,6 +809,7 @@
|
||||
"label": "Libellé",
|
||||
"placeholder": "Texte de substitution",
|
||||
"display_add_to_calendar_organizer": "Afficher l'e-mail « Ajouter au calendrier » en tant qu'organisateur",
|
||||
"reconnect_calendar_to_use": "Veuillez noter que vous devrez peut-être vous déconnecter puis reconnecter votre compte 'Ajouter au calendrier' pour utiliser cette fonctionnalité.",
|
||||
"type": "Type",
|
||||
"edit": "Modifier",
|
||||
"add_input": "Ajouter un champ",
|
||||
@@ -1474,6 +1476,8 @@
|
||||
"copy_link_to_form": "Copier le lien vers le formulaire",
|
||||
"theme": "Apparence",
|
||||
"theme_applies_note": "Ceci s'applique uniquement à vos pages publiques de réservation.",
|
||||
"app_theme": "Thème du tableau de bord",
|
||||
"app_theme_applies_note": "Cela s'applique uniquement à votre tableau de bord connecté",
|
||||
"theme_system": "Automatique",
|
||||
"add_a_team": "Ajouter une équipe",
|
||||
"add_webhook_description": "Recevez les données de rendez-vous en temps réel lorsque quelque chose se passe sur {{appName}}.",
|
||||
@@ -1599,6 +1603,7 @@
|
||||
"attendee_email_info": "Adresse e-mail du participant",
|
||||
"kbar_search_placeholder": "Saisissez une commande ou une recherche...",
|
||||
"invalid_credential": "Oh non ! On dirait que l’autorisation a expiré ou a été révoquée. Veuillez la réinstaller.",
|
||||
"invalid_credential_action": "Réinstaller l'application",
|
||||
"reschedule_reason": "Raison de la replanification",
|
||||
"choose_common_schedule_team_event": "Choisissez un horaire commun",
|
||||
"choose_common_schedule_team_event_description": "Activez cette option si vous souhaitez utiliser des disponibilités communes entre les hôtes. Si cette option est désactivée, chaque hôte recevra les réservations en fonction de ses disponibilités par défaut.",
|
||||
@@ -1884,10 +1889,8 @@
|
||||
"requires_at_least_one_schedule": "Vous devez avoir au moins un planning",
|
||||
"default_conferencing_bulk_description": "Mettez à jour les emplacements pour les types d'événements sélectionnés.",
|
||||
"locked_for_members": "Verrouillé pour les membres",
|
||||
"locked_apps_description": "Les membres pourront voir les applications actives, mais ne pourront pas modifier leurs paramètres.",
|
||||
"locked_webhooks_description": "Les membres pourront voir les webhooks actifs, mais ne pourront pas modifier leurs paramètres.",
|
||||
"locked_workflows_description": "Les membres pourront voir les workflows actifs, mais ne pourront pas modifier leurs paramètres.",
|
||||
"locked_by_admin": "Verrouillé par l'administrateur de l'équipe",
|
||||
"apps_locked_for_members_description": "Les membres pourront voir les applications actives, mais ne pourront pas modifier leurs paramètres.",
|
||||
"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",
|
||||
"managed_event_dialog_confirm_button_one": "Remplacer et notifier {{count}} membre",
|
||||
@@ -2079,6 +2082,7 @@
|
||||
"organizations": "Organisations",
|
||||
"upload_cal_video_logo": "Téléverser un logo vidéo Cal",
|
||||
"update_cal_video_logo": "Mettre à jour un logo vidéo Cal",
|
||||
"upload_banner": "Télécharger une bannière",
|
||||
"cal_video_logo_upload_instruction": "Pour assurer que votre logo reste visible sur le fond sombre de la vidéo Cal, veuillez téléverser une image de couleur claire au format PNG ou SVG afin de maintenir la transparence.",
|
||||
"org_admin_other_teams": "Autres équipes",
|
||||
"org_admin_other_teams_description": "Ici, vous pouvez voir les équipes au sein de votre organisation dont vous ne faites pas partie. Vous pouvez vous y ajouter si nécessaire.",
|
||||
@@ -2136,6 +2140,24 @@
|
||||
"scheduling_for_your_team": "Automatisation de workflows",
|
||||
"scheduling_for_your_team_description": "Planifiez pour votre équipe grâce à la planification collective et par permutation circulaire",
|
||||
"no_members_found": "Aucun membre trouvé",
|
||||
"directory_sync": "Synchronisation des dossiers",
|
||||
"directory_name": "Nom du répertoire",
|
||||
"directory_provider": "Fournisseur du répertoire",
|
||||
"directory_scim_url": "URL de base SCIM",
|
||||
"directory_scim_token": "Jeton du porteur SCIM",
|
||||
"directory_scim_url_copied": "URL de base SCIM copiée",
|
||||
"directory_scim_token_copied": "Le jeton SCIM Bearer a été copié",
|
||||
"directory_sync_info_description": "Votre Fournisseur d'identité vous demandera les informations suivantes pour configurer SCIM. Suivez les instructions pour terminer l'installation.",
|
||||
"directory_sync_configure": "Configurer la synchronisation du répertoire",
|
||||
"directory_sync_configure_description": "Choisissez un fournisseur d'identité pour configurer le répertoire de votre équipe.",
|
||||
"directory_sync_title": "Configurer un fournisseur d'identité pour commencer avec SCIM.",
|
||||
"directory_sync_created": "Connexion de synchronisation du répertoire créée.",
|
||||
"directory_sync_description": "Fourniture et dispense des utilisateurs avec votre fournisseur de répertoire.",
|
||||
"directory_sync_deleted": "Connexion de synchronisation du répertoire supprimé.",
|
||||
"directory_sync_delete_connection": "Supprimer la connexion",
|
||||
"directory_sync_delete_title": "Supprimer la connexion de synchronisation de répertoire",
|
||||
"directory_sync_delete_description": "Êtes-vous sûr de vouloir supprimer cette connexion de synchronisation de répertoire?",
|
||||
"directory_sync_delete_confirmation": "Cette action ne peut pas être annulée. Cela supprimera définitivement la connexion de synchronisation du répertoire.",
|
||||
"event_setup_length_error": "Configuration de l'événement : la durée doit être d'au moins 1 minute.",
|
||||
"availability_schedules": "Horaires de disponibilité",
|
||||
"unauthorized": "Non autorisé",
|
||||
@@ -2262,5 +2284,14 @@
|
||||
"field_identifiers_as_variables": "Utiliser les identifiants de champs comme variables pour la redirection de votre événement personnalisé",
|
||||
"field_identifiers_as_variables_with_example": "Utiliser les identifiants de champs comme variables pour la redirection de votre événement personnalisé (par ex. : {{variable}})",
|
||||
"account_already_linked": "Le compte est déjà associé",
|
||||
"privacy": "Confidentialité",
|
||||
"routers": "Routeurs",
|
||||
"primary": "Primaire",
|
||||
"make_primary": "Rendre primaire",
|
||||
"add_email": "Ajouter un e-mail",
|
||||
"add_email_description": "Ajoutez une adresse e-mail pour remplacer votre adresse principale ou pour utiliser comme adresse de courriel alternative sur vos types d'événements.",
|
||||
"confirm_email": "Confirmez votre adresse e-mail",
|
||||
"confirm_email_description": "Nous avons envoyé un e-mail à <strong>{{email}}</strong>. Cliquez sur le lien dans l'e-mail pour vérifier cette adresse.",
|
||||
"send_event_details_to": "Envoyer les détails de l'événement à",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Ajoutez vos nouvelles chaînes ci-dessus ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -131,6 +131,7 @@
|
||||
"invitee_timezone": "אזור הזמן של האדם שהוזמן",
|
||||
"time_left": "זמן שנותר",
|
||||
"event_type": "סוג האירוע",
|
||||
"duplicate_event_type": "שכפול סוג אירוע",
|
||||
"enter_meeting": "להצטרף לפגישה",
|
||||
"video_call_provider": "ספק שיחות וידאו",
|
||||
"meeting_id": "מזהה הפגישה",
|
||||
@@ -251,6 +252,7 @@
|
||||
"confirm_auth_change": "זה ישנה את דרך ההתחברות שלך",
|
||||
"confirm_auth_email_change": "שינוי כתובת הדוא\"ל ינתק את שיטת האימות הנוכחית שמשמשת אותך לכניסה אל Cal.com. נבקש ממך לאמת את כתובת הדוא\"ל החדשה שלך. מעכשיו, המערכת תנתק אותך מהחשבון ויהיה עליך להשתמש בכתובת הדוא\"ל החדשה שלך כדי להתחבר, במקום בשיטת האימות הנוכחית שלך לאחר שתגדיר/י סיסמה לפי ההוראות שיישלחו אליך בדוא\"ל.",
|
||||
"reset_your_password": "הגדר/י את הסיסמה החדשה שלך לפי ההוראות שנשלחו אל כתובת הדוא\"ל שלך.",
|
||||
"org_banner_instructions": "נא להעלות תמונה ברוחב {{width}} וגובה {{height}}.",
|
||||
"email_change": "התחבר/י שוב עם כתובת הדוא\"ל החדשה והסיסמה.",
|
||||
"create_your_account": "צור את החשבון שלך",
|
||||
"create_your_calcom_account": "יצירת החשבון שלך ב־Cal.com",
|
||||
@@ -705,7 +707,9 @@
|
||||
"you_must_be_logged_in_to": "חובה להיכנס אל {{url}}",
|
||||
"start_assigning_members_above": "התחל/י להקצות חברים למעלה",
|
||||
"locked_fields_admin_description": "חברים לא יוכלו לערוך את זה",
|
||||
"unlocked_fields_admin_description": "חברים יכולים לערוך",
|
||||
"locked_fields_member_description": "מנהל הצוות נעל את האפשרות הזו",
|
||||
"unlocked_fields_member_description": "שוחרר על ידי הנהלת הצוות",
|
||||
"url": "כתובת URL",
|
||||
"hidden": "מוסתר",
|
||||
"readonly": "לקריאה בלבד",
|
||||
@@ -1888,10 +1892,12 @@
|
||||
"requires_at_least_one_schedule": "אתה חייב שיהיה לך לפחות לוח זמנים אחד",
|
||||
"default_conferencing_bulk_description": "עדכן את המיקומים עבור סוגי הארועים שנבחרו",
|
||||
"locked_for_members": "נעול לחברים",
|
||||
"locked_apps_description": "החברים יוכלו לראות את האפליקציות הפעילות, אבל לא יוכלו לערוך את הגדרות האפליקציה",
|
||||
"locked_webhooks_description": "החברים יוכלו לראות את רכיבי ה-Webhook הפעילים, אבל לא יוכלו לערוך הגדרות של רכיבי Webhook",
|
||||
"locked_workflows_description": "החברים יוכלו לראות את תהליכי העבודה הפעילים, אבל לא יוכלו לערוך הגדרות של תהליכי עבודה",
|
||||
"locked_by_admin": "ננעל על ידי מנהל/ת הצוות",
|
||||
"unlocked_for_members": "שוחרר לטובת החברים",
|
||||
"apps_locked_for_members_description": "החברים יוכלו לראות את האפליקציות הפעילות, אבל לא יוכלו לערוך את הגדרות האפליקציה",
|
||||
"apps_unlocked_for_members_description": "החברים יוכלו לראות את היישומים הפעילים ויוכלו לערוך את כל הגדרות היישומים",
|
||||
"apps_locked_by_team_admins_description": "יתאפשר לך לראות את היישומים הפעילים, אבל לא לערוך הגדרות יישומים כלשהן",
|
||||
"apps_unlocked_by_team_admins_description": "יתאפשר לך לראות את היישומים הפעילים ולערוך את כל הגדרות היישומים",
|
||||
"locked_by_team_admin": "ננעל על ידי מנהל/ת הצוות",
|
||||
"app_not_connected": "לא קישרת חשבון {{appName}}.",
|
||||
"connect_now": "להתחבר עכשיו",
|
||||
"managed_event_dialog_confirm_button_one": "החלפה ועדכון של חבר/ה {{count}}",
|
||||
@@ -2083,6 +2089,7 @@
|
||||
"organizations": "ארגונים",
|
||||
"upload_cal_video_logo": "העלאת סרטון לוגו ל־Cal",
|
||||
"update_cal_video_logo": "עדכון סרטון לוגו ל־Cal",
|
||||
"upload_banner": "העלאת כרזה",
|
||||
"cal_video_logo_upload_instruction": "כדי לוודא שהלוגו שלך גלוי כנגד הרקע הכהה של הסרטון של Cal, נא להעלות תמונה בצבעים בהירים מהסוגים PNG או SVG כדי שהחלקים המתאימים יישארו שקופים.",
|
||||
"org_admin_other_teams": "צוותים אחרים",
|
||||
"org_admin_other_teams_description": "כאן תוכל/י לראות צוותים בארגון שאינך שייך/ת אליהם. יש לך אפשרות להוסיף את עצמך, במקרה הצורך.",
|
||||
@@ -2140,6 +2147,10 @@
|
||||
"scheduling_for_your_team": "אוטומצית תהליך עבודה",
|
||||
"scheduling_for_your_team_description": "עריכת תזמון לצוות שלך באופן שיתופי ולפי התור",
|
||||
"no_members_found": "לא נמצא אף חבר",
|
||||
"directory_sync": "סנכרון ספריות",
|
||||
"directory_name": "שם הספרייה",
|
||||
"directory_provider": "ספק ספרייה",
|
||||
"directory_sync_configure": "הגדרת סנכרון ספרייה",
|
||||
"event_setup_length_error": "הגדרת אירוע: משך הזמן חייב להיות לפחות דקה אחת.",
|
||||
"availability_schedules": "לוחות זמנים לזמינוּת",
|
||||
"unauthorized": "אין הרשאה",
|
||||
@@ -2177,6 +2188,8 @@
|
||||
"availabilty_schedules": "לוחות זמנים לזמינוּת",
|
||||
"manage_calendars": "ניהול לוחות שנה",
|
||||
"manage_availability_schedules": "ניהול לוחות זמנים לזמינות",
|
||||
"locked": "נעול",
|
||||
"unlocked": "משוחרר",
|
||||
"lock_timezone_toggle_on_booking_page": "נעילת אזור הזמן בדף ההזמנות",
|
||||
"description_lock_timezone_toggle_on_booking_page": "כדי לנעול את אזור הזמן בדף ההזמנות – שימושי לאירועים אישיים.",
|
||||
"event_setup_multiple_payment_apps_error": "יכול להיות רק יישומון תשלום אחד פעיל לכל סוג אירוע.",
|
||||
@@ -2266,6 +2279,11 @@
|
||||
"field_identifiers_as_variables": "להשתמש במזהי השדות כמשתנים להפניות האירועים בהתאמה אישית",
|
||||
"field_identifiers_as_variables_with_example": "להשתמש במזהי השדות כמשתנים להפניות האירועים בהתאמה אישית (למשל: {{variable}})",
|
||||
"account_already_linked": "החשבון כבר מקושר",
|
||||
"privacy": "פרטיות",
|
||||
"team_will_be_under_org": "צוותים חדשים יהיו תחת הארגון שלך",
|
||||
"add_group_name": "הוספת שם קבוצה",
|
||||
"group_name": "שם קבוצה",
|
||||
"routers": "נתבים",
|
||||
"primary": "עיקרי",
|
||||
"make_primary": "הפיכה לעיקרי",
|
||||
"add_email": "הוספת כתובת דוא״ל",
|
||||
@@ -2274,4 +2292,4 @@
|
||||
"confirm_email_description": "שלחנו הודעה בדוא״ל אל <strong>{{email}}</strong>. נא ללחוץ על הקישור בהודעה כדי לאמת את הכתובת הזאת.",
|
||||
"send_event_details_to": "שליחת פרטי האירוע אל",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -335,4 +335,4 @@
|
||||
"user_dynamic_booking_disabled": "Neki od korisnika u grupi trenutno su onemogućili dinamičke grupne rezervacije",
|
||||
"full_name": "Puno ime",
|
||||
"already_have_account": "Već imate račun?"
|
||||
}
|
||||
}
|
||||
@@ -73,6 +73,7 @@
|
||||
"new_event_subject": "Új esemény: {{attendeeName}} - {{date}} - {{eventType}}",
|
||||
"notes": "Jegyzetek",
|
||||
"need_to_make_a_change": "Változtatnál valamit?",
|
||||
"new_event_scheduled_recurring": "Egy új ismétlődő időpont le lett foglalva.",
|
||||
"meeting_id": "Megbeszélés azonosítója",
|
||||
"meeting_password": "Megbeszélés jelszava",
|
||||
"meeting_url": "Megbeszélés URL címe",
|
||||
@@ -146,6 +147,7 @@
|
||||
"user_needs_to_confirm_or_reject_booking": "{{user}} még mindig vissza kell igazolni vagy elutasítania a foglalást.",
|
||||
"user_needs_to_confirm_or_reject_booking_recurring": "{{user}} résztvevőnek még mindig vissza kell igazolnia vagy elutasítania a foglalást az ismétlődő találkozóra.",
|
||||
"meeting_is_scheduled": "Találkozó sikeresen lefoglalva",
|
||||
"meeting_is_scheduled_recurring": "Az ismétlődő időpontod le lett foglalva",
|
||||
"booking_submitted": "A foglalásod be lett küldve",
|
||||
"booking_confirmed": "A foglalásod vissza lett igazolva",
|
||||
"try_again": "Próbáld újra",
|
||||
@@ -208,6 +210,7 @@
|
||||
"friday": "Péntek",
|
||||
"saturday": "Szombat",
|
||||
"sunday": "Vasárnap",
|
||||
"additional_guests": "További résztvevők",
|
||||
"your_name": "Neved",
|
||||
"email_address": "Email cím",
|
||||
"location": "Helyszín",
|
||||
@@ -222,6 +225,7 @@
|
||||
"in_person_meeting": "Személyes találkozó",
|
||||
"in_person": "Személyesen (Szervező címén)",
|
||||
"phone_number": "Telefonszám",
|
||||
"organizer_phone_number": "Szervező telefonszáma",
|
||||
"reschedule": "Átütmezés",
|
||||
"reschedule_this": "Inkább időpontmódosítás",
|
||||
"or": "VAGY",
|
||||
@@ -244,6 +248,7 @@
|
||||
"share": "Megosztás",
|
||||
"title": "Cím",
|
||||
"description": "Leírás",
|
||||
"apps_status": "Alkalmazások állapota",
|
||||
"duration": "Hossz",
|
||||
"multiple_duration_mins": "{{count}} $t(minute_timeUnit)",
|
||||
"minutes": "Perc",
|
||||
@@ -253,16 +258,21 @@
|
||||
"your_email": "Email címed",
|
||||
"language": "Nyelv",
|
||||
"timezone": "Időzóna",
|
||||
"every_for_freq": "Minden {{freq}}",
|
||||
"repeats_every": "Ismétlődik minden",
|
||||
"occurrence_other": "alkalomig",
|
||||
"weekly_one": "hét",
|
||||
"weekly_other": "hét",
|
||||
"monthly_one": "hónap",
|
||||
"monthly_other": "hónap",
|
||||
"yearly_one": "év",
|
||||
"yearly_other": "év",
|
||||
"plus_more": "{{count}} további alkalom",
|
||||
"name": "Név",
|
||||
"logo": "Logó",
|
||||
"error": "Hiba",
|
||||
"meeting_with_user": "{Event type title} {Organiser} és {Scheduler} közt",
|
||||
"display_add_to_calendar_organizer": "Jelenítse meg a 'Hozzáadás naptárhoz' emailt mint szervező",
|
||||
"type": "Típus",
|
||||
"edit": "Szerkesztés",
|
||||
"starting": "Kezdés",
|
||||
@@ -335,17 +345,23 @@
|
||||
"hour_one": "{{count}} óra",
|
||||
"hour_other": "{{count}} óra",
|
||||
"new_seat_subject": "Új résztvevő {{name}} itt: {{eventType}}, {{date}}",
|
||||
"organizer_name_variable": "Szervező név",
|
||||
"cancelled_bookings_cannot_be_rescheduled": "Lemondott foglalásokat nem lehet átütemezni",
|
||||
"email_subject": "Email tárgy",
|
||||
"event_type_not_found": "Eseménytípus nem található",
|
||||
"location_variable": "Helyszín",
|
||||
"additional_notes_variable": "Egyéb jegyzetek",
|
||||
"additional_notes_info": "A találkozó egyéb megjegyzései",
|
||||
"organizer_name_info": "Szervező neve",
|
||||
"require_additional_notes": "Kötelező kitölteni az egyéb megjegyzéseket",
|
||||
"require_additional_notes_description": "Kötelező kitölteni az egyéb megjegyzéeseket a foglaláshoz",
|
||||
"invalid_credential": "Úgy tűnik a jogosultságok lejártak vagy vissza lettek vonva a következőhöz: {{appName}}.",
|
||||
"invalid_credential_action": "Alkalmazás újratelepítése",
|
||||
"reschedule_reason": "Időpontmódosítás oka",
|
||||
"reason": "Ok",
|
||||
"email_subject_slug_replacement": "Egy csapat adminisztrátor kicserélte az eseményed /{{slug}}",
|
||||
"app_disabled_subject": "{{appName}} le lett tiltva",
|
||||
"organizer_timezone": "Szervező időzónája",
|
||||
"event_no_longer_attending_subject": "Már nem vesz részt itt: {{title}}, {{date}}",
|
||||
"attendee_no_longer_attending_subject": "Egy résztvevő nem vesz részt itt: {{title}}, {{date}}",
|
||||
"attendee_no_longer_attending_subtitle": "{{name}} le lett mondva. Ez azt jelenti, hogy egy hely felszabadult erre az időpontra",
|
||||
@@ -361,11 +377,18 @@
|
||||
"events_rescheduled": "Átütemezett események",
|
||||
"no_show_fee_charged_email_subject": "Meg nem jelenési díj {{amount, currency}} levonva a következőért: {{title}}, {{date}}",
|
||||
"complete_your_booking_subject": "Fejezd be a foglalásod: {{title}}, {{date}}",
|
||||
"google_new_spam_policy": "Google új levélszemét szabályzata megakadályozhatja, hogy megérkezzenek az email és naptár értesítések erről a foglalásról.",
|
||||
"resolve": "Megoldás",
|
||||
"what_is_this_meeting_about": "Miről szól ez a találkozó?",
|
||||
"admin_org_notification_email_subject": "Új szervezet létrehozva: függőben",
|
||||
"overlay_my_calendar": "Összenézem a naptárammal",
|
||||
"overlay_my_calendar_toc": "A naptár csatlakoztatással Ön elfogadja az adatkezelési- és a felhasználási szabályzatunkat. Bármikor visszavonhatja a hozzáférést.",
|
||||
"view_overlay_calendar_events": "Nézd meg a naptáreseményeidet, hogy megelőzd az ütköző foglalásokat.",
|
||||
"need_help": "Segítségre van szükséged?",
|
||||
"booking_redirect_email_subject": "Foglalás átirányítás értesítő",
|
||||
"team_will_be_under_org": "Új csapatok a szervezeted alá fognak tartozni",
|
||||
"add_group_name": "Adj hozzá csapatnevet",
|
||||
"group_name": "Csapat név",
|
||||
"primary": "Elsődleges",
|
||||
"make_primary": "Legyen elsődleges",
|
||||
"add_email": "Email hozzáadása",
|
||||
@@ -373,4 +396,4 @@
|
||||
"confirm_email": "Igazold az email címed",
|
||||
"confirm_email_description": "Küldtünk egy emailt a <strong>{{email}}</strong> címre. Kattints a linkre a levélben, hogy igazold az email címed.",
|
||||
"send_event_details_to": "Esemény részleteinek küldése a következőnek"
|
||||
}
|
||||
}
|
||||
@@ -127,4 +127,4 @@
|
||||
"incorrect_2fa_code": "Kode dua faktor salah.",
|
||||
"no_account_exists": "Tidak ada akun dengan email tersebut.",
|
||||
"need_help": "Butuh bantuan?"
|
||||
}
|
||||
}
|
||||
@@ -808,6 +808,7 @@
|
||||
"label": "Etichetta",
|
||||
"placeholder": "Segnaposto",
|
||||
"display_add_to_calendar_organizer": "Visualizza l'email \"Aggiungi al calendario\" come organizzatore",
|
||||
"reconnect_calendar_to_use": "Nota che potrebbe essere necessario disconnettere e poi riconnettere l'account \"Aggiungi al calendario\" per utilizzare questa funzione.",
|
||||
"type": "Tipo",
|
||||
"edit": "Modifica",
|
||||
"add_input": "Aggiungi un input",
|
||||
@@ -1474,6 +1475,8 @@
|
||||
"copy_link_to_form": "Copia il link del modulo",
|
||||
"theme": "Tema",
|
||||
"theme_applies_note": "Si applica solo alle pagine di prenotazione pubbliche",
|
||||
"app_theme": "Tema Pannello di controllo",
|
||||
"app_theme_applies_note": "Questo riguarda solo il pannello di controllo per cui hai effettuato l'accesso",
|
||||
"theme_system": "Predefinito di sistema",
|
||||
"add_a_team": "Aggiungi un team",
|
||||
"add_webhook_description": "Ricevi in tempo reale le informazioni sulle riunioni quando succede qualcosa su {{appName}}",
|
||||
@@ -1599,6 +1602,7 @@
|
||||
"attendee_email_info": "E-mail della persona che prenota",
|
||||
"kbar_search_placeholder": "Digita un comando o esegui una ricerca...",
|
||||
"invalid_credential": "Errore! Sembra che l'autorizzazione sia scaduta o che sia stata revocata. Prova a reinstallare.",
|
||||
"invalid_credential_action": "Reinstalla app",
|
||||
"reschedule_reason": "Motivo della riprogrammazione",
|
||||
"choose_common_schedule_team_event": "Scegli un programma comune",
|
||||
"choose_common_schedule_team_event_description": "Abilita questa opzione se desideri utilizzare un programma comune per tutti gli organizzatori. Quando l'opzione è disabilitata, ciascun organizzatore verrà prenotato in base al proprio programma predefinito.",
|
||||
@@ -1884,10 +1888,8 @@
|
||||
"requires_at_least_one_schedule": "È necessario avere almeno un programma",
|
||||
"default_conferencing_bulk_description": "Aggiorna le posizioni per i tipi di eventi selezionati",
|
||||
"locked_for_members": "Bloccato per i membri",
|
||||
"locked_apps_description": "I membri potranno vedere le app attive, ma non potranno modificare le impostazioni delle app",
|
||||
"locked_webhooks_description": "I membri potranno vedere i webhook attivi, ma non potranno modificare le impostazioni dei webhook",
|
||||
"locked_workflows_description": "I membri potranno vedere i flussi di lavoro attivi, ma non potranno modificare le impostazioni dei flussi di lavoro",
|
||||
"locked_by_admin": "Bloccato dall'amministratore del team",
|
||||
"apps_locked_for_members_description": "I membri potranno vedere le app attive, ma non potranno modificare le impostazioni delle app",
|
||||
"locked_by_team_admin": "Bloccato dall'amministratore del team",
|
||||
"app_not_connected": "Non è stato connesso un account di {{appName}}.",
|
||||
"connect_now": "Connetti ora",
|
||||
"managed_event_dialog_confirm_button_one": "Sostituisci e invia notifica a {{count}} membro",
|
||||
@@ -2136,6 +2138,7 @@
|
||||
"scheduling_for_your_team": "Automazione dei flussi di lavoro",
|
||||
"scheduling_for_your_team_description": "Usa la programmazione collettiva e round robin per il tuo team",
|
||||
"no_members_found": "Nessun membro trovato",
|
||||
"directory_sync": "Directory Sync",
|
||||
"event_setup_length_error": "Impostazione evento: la durata deve essere di almeno 1 minuto.",
|
||||
"availability_schedules": "Calendario disponibilità",
|
||||
"unauthorized": "Non autorizzato",
|
||||
@@ -2262,5 +2265,13 @@
|
||||
"field_identifiers_as_variables": "Usa gli identificatori di campo come variabili per il reindirizzamento dell'evento personalizzato",
|
||||
"field_identifiers_as_variables_with_example": "Usa gli identificatori di campo come variabili per il reindirizzamento dell'evento personalizzato (es: {{variable}})",
|
||||
"account_already_linked": "L'account è già collegato",
|
||||
"privacy": "Privacy",
|
||||
"primary": "Principale",
|
||||
"make_primary": "Rendi principale",
|
||||
"add_email": "Aggiungi Email",
|
||||
"add_email_description": "Aggiungi un indirizzo email con cui sostituire quello principale o da utilizzare come email alternativa per i tipi di evento.",
|
||||
"confirm_email": "Conferma la tua email",
|
||||
"confirm_email_description": "Abbiamo inviato un'email a <strong>{{email}}</strong>. Clicca sul link nell'email per verificare questo indirizzo.",
|
||||
"send_event_details_to": "Invia dettagli evento a",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Aggiungi le tue nuove stringhe qui sopra ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "少なくとも 1 件のスケジュールが必要です",
|
||||
"default_conferencing_bulk_description": "選択したイベントの種類の場所を更新します",
|
||||
"locked_for_members": "メンバーに対してロックされています",
|
||||
"locked_apps_description": "メンバーは有効なアプリを確認できますが、アプリの設定を編集することはできません",
|
||||
"locked_webhooks_description": "メンバーは有効なウェブフックを確認できますが、ウェブフックの設定を編集することはできません",
|
||||
"locked_workflows_description": "メンバーは有効なワークフローを確認できますが、ワークフローの設定を編集することはできません",
|
||||
"locked_by_admin": "チームの管理者によりロックされています",
|
||||
"apps_locked_for_members_description": "メンバーは有効なアプリを確認できますが、アプリの設定を編集することはできません",
|
||||
"locked_by_team_admin": "チームの管理者によりロックされています",
|
||||
"app_not_connected": "{{appName}} のアカウントに接続していません。",
|
||||
"connect_now": "今すぐ接続",
|
||||
"managed_event_dialog_confirm_button_one": "{{count}} 人のメンバーを置き換えて通知する",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "ワークフローの自動化",
|
||||
"scheduling_for_your_team": "ワークフローの自動化",
|
||||
"no_members_found": "メンバーが見つかりません",
|
||||
"directory_sync": "Directory Sync",
|
||||
"event_setup_length_error": "イベント設定:時間は 1 分以上でなくてはいけません。",
|
||||
"availability_schedules": "空き状況一覧",
|
||||
"unauthorized": "権限がありません",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "専用のオンボーディングサポートとエンジニアリングサポート",
|
||||
"need_help": "サポートが必要ですか?",
|
||||
"show_more": "さらに表示",
|
||||
"privacy": "プライバシー",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ この上に新しい文字列を追加してください ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -253,4 +253,4 @@
|
||||
"user": "អ្នកប្រើប្រាស់",
|
||||
"general_description": "គ្រប់គ្រងការកំណត់សម្រាប់ភាសា និងល្វែងម៉ោងរបស់អ្នក។",
|
||||
"timezone_variable": "ល្វែងម៉ោង"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "적어도 하나 이상의 일정이 필요합니다",
|
||||
"default_conferencing_bulk_description": "선택한 이벤트 타입의 위치 업데이트",
|
||||
"locked_for_members": "회원에 대해 잠김",
|
||||
"locked_apps_description": "회원은 활성 앱을 볼 수 있지만 앱 설정을 편집할 수는 없습니다",
|
||||
"locked_webhooks_description": "회원은 활성 웹훅을 볼 수 있지만 웹훅 설정을 편집할 수는 없습니다",
|
||||
"locked_workflows_description": "회원은 활성 워크플로를 볼 수 있지만 워크플로 설정을 편집할 수는 없습니다",
|
||||
"locked_by_admin": "팀 관리자에 의해 잠김",
|
||||
"apps_locked_for_members_description": "회원은 활성 앱을 볼 수 있지만 앱 설정을 편집할 수는 없습니다",
|
||||
"locked_by_team_admin": "팀 관리자에 의해 잠김",
|
||||
"app_not_connected": "{{appName}} 계정을 연결하지 않았습니다.",
|
||||
"connect_now": "지금 연결",
|
||||
"managed_event_dialog_confirm_button_one": "회원 {{count}}명 바꾸기 및 알림",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "워크플로 자동화",
|
||||
"scheduling_for_your_team": "워크플로 자동화",
|
||||
"no_members_found": "구성원 없음",
|
||||
"directory_sync": "디렉토리 동기화",
|
||||
"event_setup_length_error": "이벤트 설정: 지속 시간은 1분 이상이어야 합니다.",
|
||||
"availability_schedules": "사용 가능한 일정",
|
||||
"unauthorized": "미승인",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "전담 온보딩 및 엔지니어링 지원",
|
||||
"need_help": "도움이 필요하세요?",
|
||||
"show_more": "더 보기",
|
||||
"privacy": "개인정보보호",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 여기에 새 문자열을 추가하세요 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -126,4 +126,4 @@
|
||||
"confirm_password": "Apstiprināt paroli",
|
||||
"confirm_auth_change": "Šis mainīs veidu, kā jūs autorizējaties",
|
||||
"already_have_account": "Vai jums jau ir konts?"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "U moet ten minste één planning hebben",
|
||||
"default_conferencing_bulk_description": "Werk de locaties voor de geselecteerde gebeurtenistypen bij",
|
||||
"locked_for_members": "Vergrendeld voor leden",
|
||||
"locked_apps_description": "Leden kunnen de actieve apps zien, maar kunnen geen appinstellingen bewerken",
|
||||
"locked_webhooks_description": "Leden kunnen de actieve webhooks zien, maar kunnen geen webhookinstellingen bewerken",
|
||||
"locked_workflows_description": "Leden kunnen de actieve werkstromen zien, maar kunnen geen werkstroominstellingen bewerken",
|
||||
"locked_by_admin": "Vergrendeld door teambeheerder",
|
||||
"apps_locked_for_members_description": "Leden kunnen de actieve apps zien, maar kunnen geen appinstellingen bewerken",
|
||||
"locked_by_team_admin": "Vergrendeld door teambeheerder",
|
||||
"app_not_connected": "U hebt geen {{appName}}-account gekoppeld.",
|
||||
"connect_now": "Nu koppelen",
|
||||
"managed_event_dialog_confirm_button_one": "{{count}} lid vervangen en informeren",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "Automatisering van de werkstroom",
|
||||
"scheduling_for_your_team": "Automatisering van de werkstroom",
|
||||
"no_members_found": "Geen leden gevonden",
|
||||
"directory_sync": "Mapsynchronisatie",
|
||||
"event_setup_length_error": "Gebeurtenisconfiguratie: de duur moet minimaal 1 minuut zijn.",
|
||||
"availability_schedules": "Beschikbaarheidsschema's",
|
||||
"unauthorized": "Ongeautoriseerd",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Speciale onboarding en technische ondersteuning",
|
||||
"need_help": "Hulp nodig?",
|
||||
"show_more": "Meer weergeven",
|
||||
"privacy": "Privacy",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Voeg uw nieuwe strings hierboven toe ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1389,4 +1389,4 @@
|
||||
"booking_confirmation_failed": "Booking-bekreftelse feilet",
|
||||
"timezone_variable": "Tidssone",
|
||||
"need_help": "Trenger du hjelp?"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "Musisz mieć co najmniej jeden harmonogram",
|
||||
"default_conferencing_bulk_description": "Aktualizuj lokalizacje wybranych typów wydarzeń",
|
||||
"locked_for_members": "Zablokowany dla członków",
|
||||
"locked_apps_description": "Członkowie będą mogli sprawdzić aktywne aplikacje, ale nie będą mogli edytować ich ustawień",
|
||||
"locked_webhooks_description": "Członkowie będą mogli sprawdzić aktywne webhooki, ale nie będą mogli edytować ich ustawień",
|
||||
"locked_workflows_description": "Członkowie będą mogli sprawdzić aktywne przepływy pracy, ale nie będą mogli edytować ich ustawień",
|
||||
"locked_by_admin": "Zablokowane przez administratora zespołu",
|
||||
"apps_locked_for_members_description": "Członkowie będą mogli sprawdzić aktywne aplikacje, ale nie będą mogli edytować ich ustawień",
|
||||
"locked_by_team_admin": "Zablokowane przez administratora zespołu",
|
||||
"app_not_connected": "Nie podłączono konta {{appName}}.",
|
||||
"connect_now": "Podłącz teraz",
|
||||
"managed_event_dialog_confirm_button_one": "Zastąp i poinformuj {{count}} członka",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "Automatyzacja przepływu pracy",
|
||||
"scheduling_for_your_team": "Automatyzacja przepływu pracy",
|
||||
"no_members_found": "Nie znaleziono członków",
|
||||
"directory_sync": "Synchronizacja katalogu",
|
||||
"event_setup_length_error": "Konfiguracja wydarzenia: czas trwania musi wynosić co najmniej minutę.",
|
||||
"availability_schedules": "Harmonogramy dostępności",
|
||||
"unauthorized": "Brak autoryzacji",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Dedykowane wsparcie w zakresie wdrożenia i obsługi technicznej",
|
||||
"need_help": "Potrzebujesz pomocy?",
|
||||
"show_more": "Wyświetl więcej",
|
||||
"privacy": "Prywatność",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodaj nowe ciągi powyżej ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -131,6 +131,7 @@
|
||||
"invitee_timezone": "Fuso horário do participante",
|
||||
"time_left": "Tempo restante",
|
||||
"event_type": "Tipo do evento",
|
||||
"duplicate_event_type": "Tipo de evento duplicado",
|
||||
"enter_meeting": "Entrar na reunião",
|
||||
"video_call_provider": "Fornecedor de videochamada",
|
||||
"meeting_id": "ID da reunião",
|
||||
@@ -251,6 +252,7 @@
|
||||
"confirm_auth_change": "Isso alterará a forma como você faz login",
|
||||
"confirm_auth_email_change": "Ao alterar o endereço de e-mail, seu método de autenticação atual será desconectado para entrar no Cal.com. Será solicitado que você verifique seu novo endereço de e-mail. Em seguida, você sairá e seu novo endereço de e-mail será usado para entrar em vez do seu método de autenticação atual, depois que sua senha for definida segundo as instruções enviadas por e-mail.",
|
||||
"reset_your_password": "Defina sua nova senha com as instruções enviadas para seu endereço de e-mail.",
|
||||
"org_banner_instructions": "Faça upload de uma imagem com largura {{width}} e altura {{height}} .",
|
||||
"email_change": "Volte a entrar com seu novo endereço de e-mail e senha.",
|
||||
"create_your_account": "Criar sua conta",
|
||||
"create_your_calcom_account": "Crie sua conta Cal.com",
|
||||
@@ -807,6 +809,7 @@
|
||||
"label": "Etiqueta",
|
||||
"placeholder": "Preenchimento",
|
||||
"display_add_to_calendar_organizer": "Exibir o e-mail 'Adicionar ao calendário' como organizador",
|
||||
"reconnect_calendar_to_use": "Observe que pode ser necessário desconectar e reconectar sua conta 'Adicionar ao calendário' para usar esse recurso.",
|
||||
"type": "Tipo",
|
||||
"edit": "Editar",
|
||||
"add_input": "Adicionar um campo",
|
||||
@@ -815,6 +818,9 @@
|
||||
"requires_confirmation_description": "A reserva precisa ser confirmada manualmente antes de ser enviada para as integrações e ser enviado um email de confirmação.",
|
||||
"recurring_event": "Evento recorrente",
|
||||
"recurring_event_description": "As pessoas podem se inscrever em eventos recorrentes",
|
||||
"cannot_be_used_with_paid_event_types": "Não pode ser usado com eventos pagos",
|
||||
"warning_payment_instant_meeting_event": "Reuniões instantâneas ainda não são compatíveis com eventos recorrentes e aplicativos de pagamento",
|
||||
"warning_instant_meeting_experimental": "Experimental: reunião instantânea são atualmente experimentais.",
|
||||
"starting": "Iniciando",
|
||||
"disable_guests": "Desativar participantes",
|
||||
"disable_guests_description": "Desativar a adição de participantes adicionais durante a reserva.",
|
||||
@@ -823,6 +829,7 @@
|
||||
"private_link_label": "Link privado",
|
||||
"private_link_hint": "Seu link privado será reaproveitado após cada uso",
|
||||
"copy_private_link": "Copiar link privado",
|
||||
"copy_private_link_to_event": "Copiar link privado para evento",
|
||||
"private_link_description": "Gere um URL privado para compartilhar sem expor seu nome de usuário do {{appName}}",
|
||||
"invitees_can_schedule": "Os convidados podem agendar",
|
||||
"date_range": "Intervalo de Datas",
|
||||
@@ -882,6 +889,7 @@
|
||||
"next_step": "Ignorar passo",
|
||||
"prev_step": "Passo anterior",
|
||||
"install": "Instalar",
|
||||
"start_paid_trial": "Comece o teste gratuito",
|
||||
"installed": "Instalado",
|
||||
"active_install_one": "{{count}} instalação ativa",
|
||||
"active_install_other": "{{count}} instalações ativas",
|
||||
@@ -896,6 +904,7 @@
|
||||
"toggle_calendars_conflict": "Ative/desative os calendários que você quiser para verificar conflitos e evitar reservas repetidas.",
|
||||
"connect_additional_calendar": "Conectar calendário adicional",
|
||||
"calendar_updated_successfully": "Calendário atualizado com êxito",
|
||||
"check_here": "Verifique aqui",
|
||||
"conferencing": "Conferência",
|
||||
"calendar": "Calendário",
|
||||
"payments": "Pagamentos",
|
||||
@@ -1067,6 +1076,8 @@
|
||||
"user_impersonation_heading": "Representação do usuário",
|
||||
"user_impersonation_description": "Permite que a nossa equipe de suporte entre temporariamente como você para ajudar a resolver rapidamente os problemas relatados.",
|
||||
"team_impersonation_description": "Permite que os administradores da sua equipe façam login como você.",
|
||||
"make_org_private": "Tornar a organização privada",
|
||||
"make_org_private_description": "Os membros da sua organização não poderão ver outros membros da organização quando esta opção estiver ativada.",
|
||||
"make_team_private": "Tornar equipe privada",
|
||||
"make_team_private_description": "Os membros da sua equipe não poderão ver outros membros da equipe após habilitar isso.",
|
||||
"you_cannot_see_team_members": "Você não poderá ver todos os membros da equipe de uma equipe privada.",
|
||||
@@ -1126,6 +1137,7 @@
|
||||
"developer_documentation": "Documentação do desenvolvedor",
|
||||
"get_in_touch": "Entrar em contato",
|
||||
"contact_support": "Fale com o suporte",
|
||||
"premium_support": "Suporte Premium",
|
||||
"community_support": "Suporte da comunidade",
|
||||
"feedback": "Comentário",
|
||||
"submitted_feedback": "Agradecemos o seu comentário!",
|
||||
@@ -1289,6 +1301,7 @@
|
||||
"default_calendar_selected": "Calendário padrão",
|
||||
"hide_from_profile": "Ocultar do perfil",
|
||||
"event_setup_tab_title": "Configuração de eventos",
|
||||
"availability_not_found_in_schedule_error": "Nenhuma disponibilidade encontrada na programação",
|
||||
"event_limit_tab_title": "Limites",
|
||||
"event_limit_tab_description": "Com que frequência você pode receber reserva",
|
||||
"event_advanced_tab_description": "Configurações de calendário e muito mais...",
|
||||
@@ -1309,6 +1322,7 @@
|
||||
"do_this": "Faça isso",
|
||||
"turn_off": "Desligar",
|
||||
"turn_on": "Ligar",
|
||||
"cancelled_bookings_cannot_be_rescheduled": "Reservas canceladas não podem ser remarcadas",
|
||||
"settings_updated_successfully": "Configurações atualizadas com sucesso",
|
||||
"error_updating_settings": "Erro ao atualizar configurações",
|
||||
"personal_cal_url": "Meu URL pessoal da {{appName}}",
|
||||
@@ -1332,6 +1346,7 @@
|
||||
"customize_your_brand_colors": "Personalizar a cor da sua marca na página de reservas.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "Remover qualquer marca relacionada a {{appName}}, ex.: \"Desenvolvido pela {{appName}}\".",
|
||||
"instant_meeting_with_title": "Reunião instantânea com {{name}}",
|
||||
"profile_picture": "Imagem do perfil",
|
||||
"upload": "Enviar",
|
||||
"add_profile_photo": "Adicionar foto do perfil",
|
||||
@@ -1387,6 +1402,7 @@
|
||||
"event_name_info": "O nome do tipo de evento",
|
||||
"event_date_info": "A data do evento",
|
||||
"event_time_info": "O horário inicial do evento",
|
||||
"event_type_not_found": "Tipo de evento não encontrado",
|
||||
"location_variable": "Local",
|
||||
"location_info": "O local do evento",
|
||||
"additional_notes_variable": "Observações adicionais",
|
||||
@@ -1429,6 +1445,7 @@
|
||||
"slot_length": "Extensão do espaço",
|
||||
"booking_appearance": "Aparência da reserva",
|
||||
"appearance_team_description": "Gerenciar as configurações de aparência da reserva da sua equipe",
|
||||
"appearance_org_description": "Gerencie as configurações da aparência da sua organização",
|
||||
"only_owner_change": "Apenas o proprietário desta equipe pode fazer alterações na reserva da equipe ",
|
||||
"team_disable_cal_branding_description": "Remover qualquer marca relacionada a {{appName}}, ex.: \"Desenvolvido pela {{appName}}\"",
|
||||
"invited_by_team": "{{teamName}} enviou um convite para juntar-se à equipe como {{role}}",
|
||||
@@ -1458,6 +1475,8 @@
|
||||
"copy_link_to_form": "Copiar link para formulário",
|
||||
"theme": "Tema",
|
||||
"theme_applies_note": "Isto só se aplica às suas páginas de reservas públicas",
|
||||
"app_theme": "Tema do painel",
|
||||
"app_theme_applies_note": "Isso se aplica apenas ao seu painel conectado",
|
||||
"theme_system": "Padrão do sistema",
|
||||
"add_a_team": "Adicionar uma equipe",
|
||||
"add_webhook_description": "Receber dados de reunião em tempo real quando algo acontecer na {{appName}}",
|
||||
@@ -1482,6 +1501,7 @@
|
||||
"password_updated": "Senha atualizada!",
|
||||
"pending_payment": "Pagamento pendente",
|
||||
"pending_invites": "Convites pendentes",
|
||||
"pending_organization_invites": "Convites pendentes da organização",
|
||||
"not_on_cal": "Não está na {{appName}}",
|
||||
"no_calendar_installed": "Nenhum calendário instalado",
|
||||
"no_calendar_installed_description": "Você ainda não conectou nenhum de seus calendários",
|
||||
@@ -1493,6 +1513,8 @@
|
||||
"report_app": "Denunciar aplicativo",
|
||||
"limit_booking_frequency": "Limite frequência de reserva",
|
||||
"limit_booking_frequency_description": "Limite quantas vezes este evento pode ser reservado",
|
||||
"limit_booking_only_first_slot": "Limite a reserva apenas no primeiro horário",
|
||||
"limit_booking_only_first_slot_description": "Permitir que apenas o primeiro horário de cada dia seja reservado",
|
||||
"limit_total_booking_duration": "Limitar duração total de reserva",
|
||||
"limit_total_booking_duration_description": "Limite de quanto tempo este evento pode ser reservado",
|
||||
"add_limit": "Adicione limite",
|
||||
@@ -1560,10 +1582,14 @@
|
||||
"your_org_disbanded_successfully": "Sua organização foi desfeita com êxito",
|
||||
"error_creating_team": "Erro ao criar equipe",
|
||||
"you": "Você",
|
||||
"or_continue_with": "Ou continue com",
|
||||
"resend_email": "Reenviar e-mail",
|
||||
"member_already_invited": "O membro já foi convidado",
|
||||
"already_in_use_error": "Este nome de usuário já está em uso",
|
||||
"enter_email_or_username": "Insira um e-mail ou nome de usuário",
|
||||
"enter_email": "Insira um e-mail",
|
||||
"enter_emails": "Insira e-mails",
|
||||
"too_many_invites": "Você está limitado a convidar no máximo {{nbUsers}} usuários de uma só vez.",
|
||||
"team_name_taken": "Este nome já está em uso",
|
||||
"must_enter_team_name": "É preciso inserir um nome de equipe",
|
||||
"team_url_required": "É preciso inserir uma URL de equipe",
|
||||
@@ -1576,6 +1602,7 @@
|
||||
"attendee_email_info": "O e-mail da pessoa que fez a reserva",
|
||||
"kbar_search_placeholder": "Digite um comando ou procure...",
|
||||
"invalid_credential": "Ah, não! Parece que a permissão expirou ou foi revogada. Reinstale novamente.",
|
||||
"invalid_credential_action": "Reinstale o aplicativo",
|
||||
"reschedule_reason": "Motivo de reagendamento",
|
||||
"choose_common_schedule_team_event": "Escolha uma agenda compartilhada",
|
||||
"choose_common_schedule_team_event_description": "Ative esta opção se você quiser usar uma agenda compartilhada entre os hosts. Ao desativar esta opção, cada host será reservado conforme seu horário padrão.",
|
||||
@@ -1643,6 +1670,7 @@
|
||||
"individual": "Individual",
|
||||
"all_bookings_filter_label": "Todas as reservas",
|
||||
"all_users_filter_label": "Todos os usuários",
|
||||
"all_event_types_filter_label": "Todos os tipos de eventos",
|
||||
"your_bookings_filter_label": "Suas reservas",
|
||||
"meeting_url_variable": "URL da reunião",
|
||||
"meeting_url_info": "O URL da conferência de reunião do evento",
|
||||
@@ -1739,9 +1767,11 @@
|
||||
"organizer_timezone": "Fuso horário do organizador",
|
||||
"email_user_cta": "Ver convite",
|
||||
"email_no_user_invite_heading_team": "Você recebeu um convite para ingressar em um time do {{appName}}",
|
||||
"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ê recebeu um convite para ingressar em uma organização do {{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.",
|
||||
"email_user_invite_subheading_team": "Você recebeu um convite de {{invitedBy}} para ingressar na equipe \"{{teamName}}\" em {{appName}}. {{appName}} é um agendador que concilia eventos e permite que sua equipe agende reuniões sem precisar trocar e-mails.",
|
||||
"email_user_invite_subheading_subteam": "{{invitedBy}} convidou você para se juntar à equipe {{teamName}} na organização {{parentTeamName}} em {{appName}} . {{appName}} é o agendador de eventos que permite que você e sua equipe agendem reuniões sem o envio de e-mail.",
|
||||
"email_user_invite_subheading_org": "Você recebeu um convite de {{invitedBy}} para ingressar na organização \"{{teamName}}\" em {{appName}}. {{appName}} é um agendador que concilia eventos e permite que sua organização agende reuniões sem precisar trocar e-mails.",
|
||||
"email_no_user_invite_steps_intro": "Orientaremos ao longo de alguns passos rápidos para que você comece logo a agendar eventos com {{entity}} sem preocupação.",
|
||||
"email_no_user_step_one": "Escolha seu nome de usuário",
|
||||
@@ -1858,10 +1888,8 @@
|
||||
"requires_at_least_one_schedule": "Você precisa ter pelo menos uma agenda",
|
||||
"default_conferencing_bulk_description": "Atualize as localizações para os tipos de evento selecionados",
|
||||
"locked_for_members": "Bloqueado para membros",
|
||||
"locked_apps_description": "Os membros poderão ver os aplicativos ativos, mas não poderão editar as configurações",
|
||||
"locked_webhooks_description": "Os membros poderão ver os webhooks ativos, mas não poderão editar as configurações",
|
||||
"locked_workflows_description": "Os membros poderão ver os fluxos de trabalho ativos, mas não poderão editar as configurações",
|
||||
"locked_by_admin": "Bloqueado pelo administrador da equipe",
|
||||
"apps_locked_for_members_description": "Os membros poderão ver os aplicativos ativos, mas não poderão editar as configurações",
|
||||
"locked_by_team_admin": "Bloqueado pelo administrador da equipe",
|
||||
"app_not_connected": "Você não conectou uma conta do {{appName}}.",
|
||||
"connect_now": "Conectar agora",
|
||||
"managed_event_dialog_confirm_button_one": "Substituir e notificar {{count}} membro",
|
||||
@@ -1874,7 +1902,9 @@
|
||||
"review_event_type": "Revisar tipo de evento",
|
||||
"looking_for_more_analytics": "Procurando mais análises?",
|
||||
"looking_for_more_insights": "Procurando mais Insights?",
|
||||
"filters": "Filtros",
|
||||
"add_filter": "Adicionar filtro",
|
||||
"remove_filters": "Limpar todos os filtros",
|
||||
"select_user": "Selecionar usuário",
|
||||
"select_event_type": "Selecionar tipo de evento",
|
||||
"select_date_range": "Selecionar intervalo de data",
|
||||
@@ -1965,6 +1995,7 @@
|
||||
"connect_google_workspace": "Conectar com o Google Workspace",
|
||||
"google_workspace_admin_tooltip": "Você deve ser administrador do Google Workspace para usar este recurso",
|
||||
"first_event_type_webhook_description": "Crie seu primeiro webhook para este tipo de evento",
|
||||
"create_instant_meeting_webhook_description": "Crie seu primeiro webhook com 'Reunião instantânea criada' como gatilho para este tipo de evento",
|
||||
"install_app_on": "Instalar aplicativo em",
|
||||
"create_for": "Criar para",
|
||||
"currency": "Moeda",
|
||||
@@ -2029,6 +2060,8 @@
|
||||
"add_times_to_your_email": "Selecione alguns horários disponíveis e incorpore-os no seu e-mail",
|
||||
"select_time": "Selecione o horário",
|
||||
"select_date": "Selecione a data",
|
||||
"connecting_you_to_someone": "Estamos conectando você a alguém.",
|
||||
"please_do_not_close_this_tab": "Por favor, não feche esta aba",
|
||||
"see_all_available_times": "Veja todos os horários disponíveis",
|
||||
"org_team_names_example_1": "ex.: Time de Marketing",
|
||||
"org_team_names_example_2": "ex: Time de Vendas",
|
||||
@@ -2046,8 +2079,13 @@
|
||||
"description_requires_booker_email_verification": "Para assegurar a verificação do e-mail do reservante antes de agendar eventos",
|
||||
"requires_confirmation_mandatory": "Mensagens de texto só podem ser enviadas a participantes quando este tipo de evento exigir confirmação.",
|
||||
"organizations": "Organizações",
|
||||
"upload_cal_video_logo": "Carregar Logotipo do Vídeo Cal",
|
||||
"update_cal_video_logo": "Atualizar logotipo do vídeo Cal",
|
||||
"upload_banner": "Carregar Banner",
|
||||
"cal_video_logo_upload_instruction": "Para garantir que seu logotipo fique visível no fundo escuro do vídeo Cal, carregue uma imagem de cor clara em formato PNG ou SVG para manter a transparência.",
|
||||
"org_admin_other_teams": "Outros times",
|
||||
"org_admin_other_teams_description": "Aqui você pode conferir os times da sua organização dos quais você não faz parte. Se necessário, você pode se adicionar a eles.",
|
||||
"not_part_of_org": "Você não faz parte de nenhuma organização",
|
||||
"no_other_teams_found": "Nenhum outro time encontrado",
|
||||
"no_other_teams_found_description": "Não há outros times nesta organização.",
|
||||
"attendee_first_name_variable": "Nome do participante",
|
||||
@@ -2083,7 +2121,9 @@
|
||||
"org_error_processing": "Ocorreu um erro ao processar esta organização",
|
||||
"orgs_page_description": "Uma lista de todas as organizações. Ao aceitar uma organização, todos os usuários com aquele domínio de e-mail poderão entrar SEM a verificação do e-mail.",
|
||||
"unverified": "Sem verificação",
|
||||
"verified": "Verificado",
|
||||
"dns_missing": "DNS ausente",
|
||||
"dns_configured": "DNS configurado",
|
||||
"mark_dns_configured": "Marcar como DNS configurado",
|
||||
"value": "Valor",
|
||||
"your_organization_updated_sucessfully": "Sua organização foi atualizada com êxito",
|
||||
@@ -2093,9 +2133,29 @@
|
||||
"oAuth": "OAuth",
|
||||
"recently_added": "Adicionou recentemente",
|
||||
"connect_all_calendars": "Conecte todos os seus calendários",
|
||||
"connect_all_calendars_description": "{{appName}} lê a disponibilidade de todos os seus calendários existentes.",
|
||||
"workflow_automation": "Automação de fluxo de trabalho",
|
||||
"workflow_automation_description": "Personalize sua experiência de agendamento com fluxos de trabalho",
|
||||
"scheduling_for_your_team": "Automação de fluxo de trabalho",
|
||||
"scheduling_for_your_team_description": "Agende para sua equipe com agendamento coletivo e round-robin",
|
||||
"no_members_found": "Nenhum membro encontrado",
|
||||
"directory_sync": "Sincronização de diretório",
|
||||
"directory_name": "Nome do diretório",
|
||||
"directory_provider": "Provedor de diretório",
|
||||
"directory_scim_url": "URL base do SCIM",
|
||||
"directory_scim_token": "Bearer Token SCIM",
|
||||
"directory_scim_url_copied": "URL base do SCIM copiado",
|
||||
"directory_scim_token_copied": "Bearer Token SCIM copiado",
|
||||
"directory_sync_info_description": "Seu provedor de identidade solicitará as seguintes informações para configurar o SCIM. Siga as instruções para finalizar a configuração.",
|
||||
"directory_sync_configure": "Configurar sincronização de diretório",
|
||||
"directory_sync_configure_description": "Escolha um provedor de identidade para configurar o diretório da sua equipe.",
|
||||
"directory_sync_title": "Configure um provedor de identidade para começar a usar o SCIM.",
|
||||
"directory_sync_created": "Conexão de sincronização de diretório criada.",
|
||||
"directory_sync_deleted": "Conexão de sincronização de diretório excluída.",
|
||||
"directory_sync_delete_connection": "Excluir conexão",
|
||||
"directory_sync_delete_title": "Excluir conexão de sincronização de diretório",
|
||||
"directory_sync_delete_description": "Tem certeza de que deseja excluir esta conexão de sincronização de diretório?",
|
||||
"directory_sync_delete_confirmation": "Essa ação não pode ser desfeita. Isso excluirá permanentemente a conexão de sincronização de diretório.",
|
||||
"event_setup_length_error": "Configuração do evento: deve ter pelo menos 1 minuto de duração.",
|
||||
"availability_schedules": "Agendamentos de disponibilidade",
|
||||
"unauthorized": "Não autorizado",
|
||||
@@ -2127,10 +2187,109 @@
|
||||
"overlay_my_calendar": "Sobrepor meu calendário",
|
||||
"overlay_my_calendar_toc": "Ao conectar-se à seu Calendário, você aceita nossa política de privacidade e termos de uso. Você pode revogar o acesso a qualquer momento.",
|
||||
"view_overlay_calendar_events": "Veja seus eventos do calendário para evitar conflito nos agendamentos.",
|
||||
"join_event_location": "Junte-se a {{eventLocationType}}",
|
||||
"troubleshooting": "Solução de problemas",
|
||||
"calendars_were_checking_for_conflicts": "Calendários que estamos verificando em busca de conflitos",
|
||||
"availabilty_schedules": "Horários de disponibilidade",
|
||||
"manage_calendars": "Gerenciar calendários",
|
||||
"manage_availability_schedules": "Gerenciar agendas de disponibilidade",
|
||||
"lock_timezone_toggle_on_booking_page": "Bloquear fuso horário na página de reserva",
|
||||
"description_lock_timezone_toggle_on_booking_page": "Bloquear o fuso horário na página de reservas, útil para eventos presenciais.",
|
||||
"event_setup_multiple_payment_apps_error": "Você só pode ter um aplicativo de pagamento ativado por tipo de evento.",
|
||||
"number_in_international_format": "Insira o número em formato internacional.",
|
||||
"install_calendar": "Instalar Calendário",
|
||||
"branded_subdomain": "Subdomínio próprio",
|
||||
"branded_subdomain_description": "Obtenha seu próprio subdomínio, como acme.cal.com",
|
||||
"org_insights": "Insights de toda a organização",
|
||||
"org_insights_description": "Entenda como toda a sua organização está gastando o tempo",
|
||||
"extensive_whitelabeling": "Marca própria abrangente",
|
||||
"extensive_whitelabeling_description": "Personalize sua experiência de agendamento com seu próprio logotipo, cores e muito mais",
|
||||
"unlimited_teams": "Equipes ilimitadas",
|
||||
"unlimited_teams_description": "Adicione quantas subequipes forem necessárias à sua organização",
|
||||
"unified_billing": "Faturamento Unificado",
|
||||
"unified_billing_description": "Adicione um único cartão de crédito para pagar todas as assinaturas da sua equipe",
|
||||
"advanced_managed_events": "Tipos de eventos gerenciados avançados",
|
||||
"advanced_managed_events_description": "Adicione um único cartão de crédito para pagar todas as assinaturas da sua equipe",
|
||||
"enterprise_description": "Atualize para Enterprise para criar sua organização",
|
||||
"create_your_org": "Crie sua organização",
|
||||
"create_your_org_description": "Atualize para Enterprise e receba um subdomínio, faturamento unificado, insights, whitelabeling abrangente e muito mais",
|
||||
"other_payment_app_enabled": "Você só pode ativar um aplicativo de pagamento por tipo de evento",
|
||||
"admin_delete_organization_description": "<ul><li>As equipes que são membros desta organização também serão excluídas junto com seus tipos de eventos</li><li> Os usuários que faziam parte da organização não serão excluídos e seus tipos de eventos também permanecerão intactos.</li><li> Os nomes de usuário seriam alterados para que existam fora da organização</li></ul>",
|
||||
"admin_delete_organization_title": "Excluir {{organizationName}} ?",
|
||||
"published": "Publicado",
|
||||
"unpublished": "Não publicado",
|
||||
"publish": "Publicar",
|
||||
"org_publish_error": "Não foi possível publicar a organização",
|
||||
"troubleshooter_tooltip": "Abra o solucionador de problemas e descubra o que há de errado com sua programação",
|
||||
"need_help": "Precisa de ajuda?",
|
||||
"troubleshooter": "Solucionador de problemas",
|
||||
"please_install_a_calendar": "Instale um calendário",
|
||||
"instant_tab_title": "Reserva instantânea",
|
||||
"instant_event_tab_description": "Deixe as pessoas reservarem imediatamente",
|
||||
"uprade_to_create_instant_bookings": "Atualize para Enterprise e permita que os convidados participem de uma chamada instantânea na qual os participantes possam participar diretamente. Isso é apenas para tipos de eventos de equipe",
|
||||
"dont_want_to_wait": "Não quer esperar?",
|
||||
"meeting_started": "Reunião iniciada",
|
||||
"pay_and_book": "Pague para reservar",
|
||||
"booking_not_found_error": "Não foi possível encontrar a reserva",
|
||||
"booking_seats_full_error": "A reserva de assentos está lotada",
|
||||
"missing_payment_credential_error": "Credenciais de pagamento ausentes",
|
||||
"missing_payment_app_id_error": "ID do aplicativo de pagamento ausente",
|
||||
"not_enough_available_seats_error": "A reserva não tem assentos disponíveis suficientes",
|
||||
"user_redirect_title": "{{username}} está ausente por um breve período de tempo.",
|
||||
"user_redirect_description": "Enquanto isso, {{profile.username}} será responsável por todas as novas reuniões agendadas em nome de {{username}} .",
|
||||
"out_of_office": "Ausente",
|
||||
"out_of_office_description": "Configure ações em seu perfil enquanto estiver ausente.",
|
||||
"send_request": "Enviar pedido",
|
||||
"start_date_and_end_date_required": "A data de início e a data de término são obrigatórias",
|
||||
"start_date_must_be_before_end_date": "A data de início deve ser anterior à data de término",
|
||||
"start_date_must_be_in_the_future": "A data de início deve ser no futuro",
|
||||
"user_not_found": "Usuário não encontrado",
|
||||
"out_of_office_entry_already_exists": "Ausência já existente",
|
||||
"out_of_office_id_required": "ID de ausência obrigatório",
|
||||
"booking_redirect_infinite_not_allowed": "Já existe um redirecionamento de reserva desse usuário para você.",
|
||||
"success_entry_created": "Entrada criada com sucesso",
|
||||
"booking_redirect_email_subject": "Notificação de redirecionamento de reserva",
|
||||
"booking_redirect_email_title": "Notificação de redirecionamento de reserva",
|
||||
"booking_redirect_email_description": "Você recebeu um redirecionamento de reserva de {{toName}} para que os links do perfil deles sejam redirecionados para o seu durante o intervalo de tempo: ",
|
||||
"success_accept_booking_redirect": "Você aceitou esta solicitação de redirecionamento de reserva.",
|
||||
"success_reject_booking_redirect": "Você rejeitou esta solicitação de redirecionamento de reserva.",
|
||||
"copy_link_booking_redirect_request": "Copiar link para solicitação de compartilhamento",
|
||||
"booking_redirect_request_title": "Solicitação de redirecionamento de reserva",
|
||||
"select_team_member": "Selecione o membro da equipe",
|
||||
"going_away_title": "Indo embora? Basta marcar o link do seu perfil como indisponível por um período de tempo.",
|
||||
"redirect_team_enabled": "Redirecione seu perfil para outro membro da equipe",
|
||||
"redirect_team_disabled": "Redirecione seu perfil para outro membro da equipe (é necessário plano de equipe)",
|
||||
"out_of_office_unavailable_list": "Lista de indisponibilidade",
|
||||
"success_deleted_entry_out_of_office": "Entrada excluída com sucesso",
|
||||
"temporarily_out_of_office": "Temporariamente fora do escritório?",
|
||||
"add_a_redirect": "Adicionar um redirecionamento",
|
||||
"create_entry": "Criar entrada",
|
||||
"time_range": "Intervalo de tempo",
|
||||
"automatically_add_all_team_members": "Adicione todos os membros da equipe, incluindo futuros membros",
|
||||
"redirect_to": "Redirecionar para",
|
||||
"having_trouble_finding_time": "Está tendo problemas para encontrar um horário?",
|
||||
"show_more": "Mostrar mais",
|
||||
"assignment_description": "Agende reuniões quando todos estiverem disponíveis ou alterne entre os membros da sua equipe",
|
||||
"lowest": "mais baixo",
|
||||
"low": "baixo",
|
||||
"medium": "médio",
|
||||
"high": "alto",
|
||||
"Highest": "mais alto",
|
||||
"send_booker_to": "Enviar reserva para",
|
||||
"set_priority": "Definir prioridade",
|
||||
"priority_for_user": "Prioridade para {{userName}}",
|
||||
"change_priority": "alterar prioridade",
|
||||
"field_identifiers_as_variables": "Use identificadores de campo como variáveis para seu redirecionamento de evento personalizado",
|
||||
"field_identifiers_as_variables_with_example": "Use identificadores de campo como variáveis para seu redirecionamento de evento personalizado (por exemplo {{variable}} )",
|
||||
"account_already_linked": "A conta já está vinculada",
|
||||
"privacy": "Privacidade",
|
||||
"routers": "Roteadores",
|
||||
"primary": "Primário",
|
||||
"make_primary": "Tornar primário",
|
||||
"add_email": "Adicionar e-mail",
|
||||
"add_email_description": "Adicione um endereço de e-mail para substituir seu e-mail principal ou para usar como e-mail alternativo em seus tipos de eventos.",
|
||||
"confirm_email": "Confirme seu email",
|
||||
"confirm_email_description": "Enviamos um e-mail para <strong>{{email}}</strong> . Clique no link do e-mail para verificar este endereço.",
|
||||
"send_event_details_to": "Envie detalhes do evento para",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adicione suas novas strings aqui em cima ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -808,6 +808,7 @@
|
||||
"label": "Etiqueta",
|
||||
"placeholder": "Preenchimento",
|
||||
"display_add_to_calendar_organizer": "Mostrar o e-mail 'Adicionar ao calendário' como organizador",
|
||||
"reconnect_calendar_to_use": "Note que poderá ser necessário desligar e voltar a ligar a sua conta 'Adicionar ao Calendário' para utilizar esta funcionalidade.",
|
||||
"type": "Tipo",
|
||||
"edit": "Editar",
|
||||
"add_input": "Adicionar um Campo",
|
||||
@@ -1474,6 +1475,8 @@
|
||||
"copy_link_to_form": "Copiar ligação para o formulário",
|
||||
"theme": "Tema",
|
||||
"theme_applies_note": "Isto só se aplica às suas páginas de reservas públicas",
|
||||
"app_theme": "Tema do Painel",
|
||||
"app_theme_applies_note": "Isto aplica-se apenas ao seu painel de administração quando tem a sessão iniciada",
|
||||
"theme_system": "Predefinição do sistema",
|
||||
"add_a_team": "Adicionar uma equipa",
|
||||
"add_webhook_description": "Receba os dados da reunião em tempo real quando algo acontecer em {{appName}}",
|
||||
@@ -1599,6 +1602,7 @@
|
||||
"attendee_email_info": "O e-mail do responsável pela reserva",
|
||||
"kbar_search_placeholder": "Digite um comando ou pesquise...",
|
||||
"invalid_credential": "Oh não! Parece que a permissão expirou ou foi revogada. Por favor, reinstale novamente.",
|
||||
"invalid_credential_action": "Reinstalar aplicação",
|
||||
"reschedule_reason": "Motivo do reagendamento",
|
||||
"choose_common_schedule_team_event": "Escolha uma agenda comum",
|
||||
"choose_common_schedule_team_event_description": "Ative esta opção se quiser utilizar uma agenda comum entre anfitriões. Quando desativada, cada anfitrião será reservado com base na respetiva agenda pré-definida.",
|
||||
@@ -1884,10 +1888,8 @@
|
||||
"requires_at_least_one_schedule": "É necessário que tenha pelo menos uma agenda",
|
||||
"default_conferencing_bulk_description": "Atualizar os locais para os tipos de evento selecionados",
|
||||
"locked_for_members": "Bloqueado para os membros",
|
||||
"locked_apps_description": "Os membros poderão ver as aplicações ativas, mas não poderão editar quaisquer configurações das aplicações",
|
||||
"locked_webhooks_description": "Os membros poderão ver os webhooks ativos, mas não poderão editar quaisquer configurações dos webhooks",
|
||||
"locked_workflows_description": "Os membros poderão ver os fluxos de trabalho ativos, mas não poderão editar quaisquer configurações dos fluxos de trabalho",
|
||||
"locked_by_admin": "Bloqueado pelo administrador da equipa",
|
||||
"apps_locked_for_members_description": "Os membros poderão ver as aplicações ativas, mas não poderão editar quaisquer configurações das aplicações",
|
||||
"locked_by_team_admin": "Bloqueado pelo administrador da equipa",
|
||||
"app_not_connected": "Não associou uma conta {{appName}}.",
|
||||
"connect_now": "Associar agora",
|
||||
"managed_event_dialog_confirm_button_one": "Substituir e notificar {{count}} membro",
|
||||
@@ -2136,6 +2138,7 @@
|
||||
"scheduling_for_your_team": "Automação de fluxo de trabalho",
|
||||
"scheduling_for_your_team_description": "Faça o agendamento para a sua equipa, com agendamento coletivo e distribuição equilibrada",
|
||||
"no_members_found": "Nenhum membro encontrado",
|
||||
"directory_sync": "Sincronização do diretório",
|
||||
"event_setup_length_error": "Configuração do Evento: a duração deve ser de, pelo menos, 1 minuto.",
|
||||
"availability_schedules": "Horários de Disponibilidade",
|
||||
"unauthorized": "Não autorizado",
|
||||
@@ -2262,5 +2265,13 @@
|
||||
"field_identifiers_as_variables": "Utilize identificadores de campo como variáveis para o seu redirecionamento de evento personalizado",
|
||||
"field_identifiers_as_variables_with_example": "Utilize identificadores de campo como variáveis para o seu redirecionamento de evento personalizado (ex.: {{variable}})",
|
||||
"account_already_linked": "A conta já está vinculada",
|
||||
"privacy": "Privacidade",
|
||||
"primary": "Primário",
|
||||
"make_primary": "Tornar primário",
|
||||
"add_email": "Adicionar e-mail",
|
||||
"add_email_description": "Adicione um endereço de e-mail para substituir o seu primário ou para utilizar como um e-mail alternativo nos seus tipos de evento.",
|
||||
"confirm_email": "Confirme o seu e-mail",
|
||||
"confirm_email_description": "Enviámos um e-mail para <strong>{{email}}</strong>. Clique na ligação nesse e-mail para verificar este endereço.",
|
||||
"send_event_details_to": "Enviar detalhes do evento para",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "Este necesar să aveți cel puțin un program",
|
||||
"default_conferencing_bulk_description": "Actualizați locațiile pentru tipurile de evenimente selectate",
|
||||
"locked_for_members": "Blocat pentru membri",
|
||||
"locked_apps_description": "Membrii vor putea vedea aplicațiile active, însă nu le vor putea modifica deloc setările",
|
||||
"locked_webhooks_description": "Membrii vor putea vedea webhook-urile active, însă nu le vor putea modifica deloc setările",
|
||||
"locked_workflows_description": "Membrii vor putea vedea fluxurile de lucru active, însă nu le vor putea modifica deloc setările",
|
||||
"locked_by_admin": "Blocat de administratorul echipei",
|
||||
"apps_locked_for_members_description": "Membrii vor putea vedea aplicațiile active, însă nu le vor putea modifica deloc setările",
|
||||
"locked_by_team_admin": "Blocat de administratorul echipei",
|
||||
"app_not_connected": "Nu ați conectat un cont {{appName}}.",
|
||||
"connect_now": "Conectați-l acum",
|
||||
"managed_event_dialog_confirm_button_one": "Înlocuiți și anunțați {{count}} membru",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "Automatizarea fluxului de lucru",
|
||||
"scheduling_for_your_team": "Automatizarea fluxului de lucru",
|
||||
"no_members_found": "Nu s-a găsit niciun membru",
|
||||
"directory_sync": "Sincronizare director",
|
||||
"event_setup_length_error": "Configurare eveniment: Durata trebuie să fie de cel puțin 1 minut.",
|
||||
"availability_schedules": "Programe de disponibilitate",
|
||||
"unauthorized": "Neautorizat",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Asistență dedicată pentru integrare și inginerie",
|
||||
"need_help": "Aveți nevoie de ajutor?",
|
||||
"show_more": "Afișare mai mult",
|
||||
"privacy": "Confidențialitate",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Adăugați stringurile noi deasupra acestui rând ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1824,10 +1824,8 @@
|
||||
"requires_at_least_one_schedule": "У вас должно быть по крайней мере одно расписание",
|
||||
"default_conferencing_bulk_description": "Обновить местоположение для выбранных типов событий",
|
||||
"locked_for_members": "Заблокировано для участников",
|
||||
"locked_apps_description": "Участники смогут видеть активные приложения, но не смогут редактировать их настройки",
|
||||
"locked_webhooks_description": "Участники смогут видеть активные вебхуки, но не смогут редактировать их настройки",
|
||||
"locked_workflows_description": "Участники смогут видеть активные рабочие процессы, но не смогут редактировать их настройки",
|
||||
"locked_by_admin": "Заблокировано администратором команды",
|
||||
"apps_locked_for_members_description": "Участники смогут видеть активные приложения, но не смогут редактировать их настройки",
|
||||
"locked_by_team_admin": "Заблокировано администратором команды",
|
||||
"app_not_connected": "Вы не подключили аккаунт {{appName}}.",
|
||||
"connect_now": "Подключить",
|
||||
"managed_event_dialog_confirm_button_one": "Заменить и уведомить {{count}} участника",
|
||||
@@ -2062,6 +2060,7 @@
|
||||
"workflow_automation": "Автоматизация при помощи рабочих процессов",
|
||||
"scheduling_for_your_team": "Автоматизация при помощи рабочих процессов",
|
||||
"no_members_found": "Участники не найдены",
|
||||
"directory_sync": "Синхронизация каталогов",
|
||||
"event_setup_length_error": "Настройка события: продолжительность должна быть не менее 1 минуты.",
|
||||
"availability_schedules": "График с информацией о доступности",
|
||||
"unauthorized": "Неавторизован(а)",
|
||||
@@ -2098,5 +2097,6 @@
|
||||
"extensive_whitelabeling": "Индивидуальная техподдержка и помощь при онбординге",
|
||||
"need_help": "Нужна помощь?",
|
||||
"show_more": "Показать больше",
|
||||
"privacy": "Конфиденциальность",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Добавьте строки выше ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -29,4 +29,4 @@
|
||||
"organizer": "Organizátor",
|
||||
"need_to_reschedule_or_cancel": "Preplánovať alebo zrušiť?",
|
||||
"no_options_available": "Nie sú k dispozícii žiadne možnosti"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "Potrebno je da imate barem jedan raspored",
|
||||
"default_conferencing_bulk_description": "Ažuriranje lokacija za izabrane tipove događaja",
|
||||
"locked_for_members": "Zaključano za članove",
|
||||
"locked_apps_description": "Članovi će moći da vide aktivne aplikacije, ali neće moći da uređuju podešavanja aplikacije",
|
||||
"locked_webhooks_description": "Članovi će moći da vide aktivne webhook-ove, ali neće moći da uređuju podešavanja webhook-ova",
|
||||
"locked_workflows_description": "Članovi će moći da vide aktivne radne tokove, ali neće moći da uređuju podešavanja radnih tokova",
|
||||
"locked_by_admin": "Zaključao administrator tima",
|
||||
"apps_locked_for_members_description": "Članovi će moći da vide aktivne aplikacije, ali neće moći da uređuju podešavanja aplikacije",
|
||||
"locked_by_team_admin": "Zaključao administrator tima",
|
||||
"app_not_connected": "Niste povezali {{appName}} nalog.",
|
||||
"connect_now": "Povežite odmah",
|
||||
"managed_event_dialog_confirm_button_one": "Zameni i obavesti {{count}} člana",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "Automatizacija radnog toka",
|
||||
"scheduling_for_your_team": "Automatizacija radnog toka",
|
||||
"no_members_found": "Članovi nisu pronađeni",
|
||||
"directory_sync": "Sinhronizacija za Directory",
|
||||
"event_setup_length_error": "Podešavanje događaja: Trajanje mora da bude barem 1 minut.",
|
||||
"availability_schedules": "Raspored dostupnosti",
|
||||
"unauthorized": "Neovlašćeno",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Posvećena podrška za uvodnu obuku i inženjering",
|
||||
"need_help": "Potrebna vam je pomoć?",
|
||||
"show_more": "Prikaži više",
|
||||
"privacy": "Privatnost",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Dodajte svoje nove stringove iznad ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "Du måste ha minst ett schema",
|
||||
"default_conferencing_bulk_description": "Uppdatera platserna för de valda händelsetyperna",
|
||||
"locked_for_members": "Låst för medlemmar",
|
||||
"locked_apps_description": "Medlemmarna kan se de aktiva apparna men kan inte ändra appinställningarna",
|
||||
"locked_webhooks_description": "Medlemmarna kan se aktiva webhooks men kan inte redigera inställningar för webhooks",
|
||||
"locked_workflows_description": "Medlemmarna kan se aktiva arbetsflöden men kan inte ändra några arbetsflödesinställningar",
|
||||
"locked_by_admin": "Låst av teamadministratör",
|
||||
"apps_locked_for_members_description": "Medlemmarna kan se de aktiva apparna men kan inte ändra appinställningarna",
|
||||
"locked_by_team_admin": "Låst av teamadministratör",
|
||||
"app_not_connected": "Du har inte anslutit ett {{appName}}-konto.",
|
||||
"connect_now": "Anslut nu",
|
||||
"managed_event_dialog_confirm_button_one": "Ersätt och meddela {{count}} medlem",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "Automatisering av arbetsflöde",
|
||||
"scheduling_for_your_team": "Automatisering av arbetsflöde",
|
||||
"no_members_found": "Inga medlemmar hittades",
|
||||
"directory_sync": "Katalogsynkronisering",
|
||||
"event_setup_length_error": "Konfigurering av evenemang: Längden måste vara minst 1 minut.",
|
||||
"availability_schedules": "Scheman för tillgänglighet",
|
||||
"unauthorized": "Obehörig",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Dedikerad registrering och teknisk support",
|
||||
"need_help": "Behöver du hjälp?",
|
||||
"show_more": "Visa mer",
|
||||
"privacy": "Sekretess",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -156,7 +156,7 @@
|
||||
"select_all": "அனைத்தையும் தேர்வுசெய்",
|
||||
"default_conferencing_bulk_title": "தற்போதுள்ள நிகழ்வு வகைகளை மொத்தமாகப் புதுப்பிக்கவும்",
|
||||
"default_conferencing_bulk_description": "தேர்ந்தெடுக்கப்பட்ட நிகழ்வு வகைகளுக்கான இருப்பிடங்களைப் புதுப்பிக்கவும்",
|
||||
"locked_by_admin": "குழு நிர்வாகியால் பூட்டப்பட்டது",
|
||||
"locked_by_team_admin": "குழு நிர்வாகியால் பூட்டப்பட்டது",
|
||||
"looking_for_more_analytics": "மேலும் பகுப்பாய்வுகளைத் தேடுகிறீர்களா?",
|
||||
"add_filter": "வடிகட்டிச் சேர்க்கவும்",
|
||||
"select_user": "பயனரைத் தேர்ந்தெடுக்கவும்",
|
||||
@@ -173,4 +173,4 @@
|
||||
"from_last_period": "கடந்த காலத்திலிருந்து",
|
||||
"from_to_date_period": "{{startDate}} லிருந்து {{endDate}} வரை",
|
||||
"event_trends": "நிகழ்வுப் போக்குகள்"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "En az bir plana sahip olmanız gerekiyor",
|
||||
"default_conferencing_bulk_description": "Seçilen etkinlik türleri için konumları güncelleyin",
|
||||
"locked_for_members": "Üyeler için kilitli",
|
||||
"locked_apps_description": "Üyeler aktif uygulamaları görebilir, ancak hiçbir uygulama ayarını düzenleyemez",
|
||||
"locked_webhooks_description": "Üyeler aktif web kancalarını görebilir, ancak hiçbir web kancası ayarını düzenleyemez",
|
||||
"locked_workflows_description": "Üyeler aktif iş akışlarını görebilir, ancak hiçbir iş akışı ayarını düzenleyemez",
|
||||
"locked_by_admin": "Ekip yöneticisi tarafından kilitlendi",
|
||||
"apps_locked_for_members_description": "Üyeler aktif uygulamaları görebilir, ancak hiçbir uygulama ayarını düzenleyemez",
|
||||
"locked_by_team_admin": "Ekip yöneticisi tarafından kilitlendi",
|
||||
"app_not_connected": "{{appName}} hesabı bağlamadınız.",
|
||||
"connect_now": "Hemen bağlan",
|
||||
"managed_event_dialog_confirm_button_one": "{{count}} üyeyi değiştirin ve bilgilendirin",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "İş akışı otomasyonu",
|
||||
"scheduling_for_your_team": "İş akışı otomasyonu",
|
||||
"no_members_found": "Üye bulunamadı",
|
||||
"directory_sync": "Dizin Senkronizasyonu",
|
||||
"event_setup_length_error": "Etkinlik Kurulumu: Süre en az 1 dakika olmalıdır.",
|
||||
"availability_schedules": "Müsaitlik Planları",
|
||||
"unauthorized": "Yetkisiz",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "Özel işe alıştırma ve mühendislik desteği",
|
||||
"need_help": "Yardıma mı ihtiyacınız var?",
|
||||
"show_more": "Daha fazla göster",
|
||||
"privacy": "Gizlilik",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Yeni dizelerinizi yukarıya ekleyin ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1855,10 +1855,8 @@
|
||||
"requires_at_least_one_schedule": "У вас має бути принаймні один розклад",
|
||||
"default_conferencing_bulk_description": "Оновіть розташування для вибраних типів заходів",
|
||||
"locked_for_members": "Заблоковано для учасників",
|
||||
"locked_apps_description": "Учасники бачитимуть активні додатки, але не зможуть змінювати їх налаштування",
|
||||
"locked_webhooks_description": "Учасники бачитимуть активні вебгуки, але не зможуть змінювати їх налаштування",
|
||||
"locked_workflows_description": "Учасники бачитимуть активні робочі процеси, але не зможуть змінювати їх налаштування",
|
||||
"locked_by_admin": "Заблоковано адміністратором команди",
|
||||
"apps_locked_for_members_description": "Учасники бачитимуть активні додатки, але не зможуть змінювати їх налаштування",
|
||||
"locked_by_team_admin": "Заблоковано адміністратором команди",
|
||||
"app_not_connected": "Ви не під’єднали обліковий запис {{appName}}.",
|
||||
"connect_now": "Під’єднати",
|
||||
"managed_event_dialog_confirm_button_one": "Замінити й повідомити {{count}} учасника",
|
||||
@@ -2093,6 +2091,7 @@
|
||||
"workflow_automation": "Автоматизація робочого процесу",
|
||||
"scheduling_for_your_team": "Автоматизація робочого процесу",
|
||||
"no_members_found": "Учасників не знайдено",
|
||||
"directory_sync": "Directory Sync",
|
||||
"event_setup_length_error": "Налаштування заходу: мінімальна тривалість — 1 хвилина.",
|
||||
"availability_schedules": "Розклад доступності",
|
||||
"unauthorized": "Неавторизовано",
|
||||
@@ -2129,5 +2128,6 @@
|
||||
"extensive_whitelabeling": "Технічна підтримка й підтримка під час ознайомлення",
|
||||
"need_help": "Потрібна допомога?",
|
||||
"show_more": "Більше",
|
||||
"privacy": "Політика конфіденційності",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1827,10 +1827,8 @@
|
||||
"requires_at_least_one_schedule": "Bạn cần phải có ít nhất một lịch biểu",
|
||||
"default_conferencing_bulk_description": "Cập nhật địa điểm cho những loại sự kiện đã chọn",
|
||||
"locked_for_members": "Bị khoá đối với thành viên",
|
||||
"locked_apps_description": "Các thành viên có thể thấy được những ứng dụng đang hoạt động nhưng không thể sửa bất kỳ thiết lập nào của ứng dụng",
|
||||
"locked_webhooks_description": "Các thành viên có thể thấy được những webhook đang hoạt động nhưng không thể sửa bất kỳ thiết lập nào của webhook",
|
||||
"locked_workflows_description": "Các thành viên có thể thấy được những tiến độ công việc đang hoạt động nhưng không thể sửa bất kỳ thiết lập nào của tiến độ công việc",
|
||||
"locked_by_admin": "Bị khoá bởi quản trị viên của nhóm",
|
||||
"apps_locked_for_members_description": "Các thành viên có thể thấy được những ứng dụng đang hoạt động nhưng không thể sửa bất kỳ thiết lập nào của ứng dụng",
|
||||
"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",
|
||||
"managed_event_dialog_confirm_button_one": "Thay thế & thông báo cho {{count}} thành viên",
|
||||
@@ -2068,6 +2066,7 @@
|
||||
"workflow_automation": "Tự động hoá dòng công việc",
|
||||
"scheduling_for_your_team": "Tự động hoá dòng công việc",
|
||||
"no_members_found": "Không tìm thấy thành viên nào",
|
||||
"directory_sync": "Đồng bộ thư mục",
|
||||
"event_setup_length_error": "Thiết lập sự kiện: Thời lượng phải ít nhất 1 phút.",
|
||||
"availability_schedules": "Lịch trống",
|
||||
"unauthorized": "Chưa được uỷ quyền",
|
||||
@@ -2105,5 +2104,6 @@
|
||||
"need_help": "Cần hỗ trợ?",
|
||||
"instant_tab_title": "Đặt Cuộc Họp Ngay",
|
||||
"show_more": "Hiện thêm",
|
||||
"privacy": "Quyền riêng tư",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ Add your new strings above here ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -13,10 +13,16 @@
|
||||
"reset_password_subject": "{{appName}}: 重置密码教程",
|
||||
"verify_email_subject": "{{appName}}:验证您的账户",
|
||||
"check_your_email": "查看您的电子邮件",
|
||||
"new_email_address": "新电子邮箱地址",
|
||||
"verify_email_page_body": "我们已向 {{email}} 发送了一封电子邮件。请务必验证您的电子邮件地址,以确保让 {{appName}} 发挥最佳的电子邮件和日历递送能力。",
|
||||
"verify_email_banner_body": "验证您的电子邮件地址以确保最佳的电子邮件和日历递送能力",
|
||||
"verify_email_email_header": "验证您的电子邮件地址",
|
||||
"verify_email_email_button": "验证电子邮件",
|
||||
"verify_email_change_description": "您最近更改了登陆您的帐户 {{appName}} 时所使用的电子邮箱地址。 请点击下方按钮以确认您的新邮箱地址。",
|
||||
"verify_email_change_success_toast": "将您的电子邮箱地址变更为 {{email}}",
|
||||
"verify_email_change_failure_toast": "更新电子邮箱地址时出现了错误。",
|
||||
"change_of_email": "请确认您为您的帐户 {{appName}} 设置的新的邮箱地址",
|
||||
"change_of_email_toast": "我们向您的电子邮箱 {{email}} 发送了一封确认邮件。 在您点击了确认邮件中的链接后, 我们将为您更新您的电子邮箱地址。",
|
||||
"copy_somewhere_safe": "将此 API 密钥保存在安全的地方。您将无法再次查看它。",
|
||||
"verify_email_email_body": "请点击下面的按钮来验证您的电子邮件地址。",
|
||||
"verify_email_by_code_email_body": "请使用下面的代码验证您的电子邮件地址。",
|
||||
@@ -56,10 +62,15 @@
|
||||
"a_refund_failed": "退款失败",
|
||||
"awaiting_payment_subject": "等待付款:{{title}} 于 {{date}}",
|
||||
"meeting_awaiting_payment": "您有会议待付款",
|
||||
"dark_theme_contrast_error": "暗黑模式未能通过对比度检测。我们建议您修改当前的颜色设置以便使您的按钮更容易分辨。",
|
||||
"light_theme_contrast_error": "浅色模式未能通过对比度检测。我们建议您修改当前的颜色设置以便使您的按钮更容易分辨。",
|
||||
"payment_not_created_error": "付款创建失败",
|
||||
"couldnt_charge_card_error": "无法修改支付卡",
|
||||
"no_available_users_found_error": "找不到可用时间。请尝试另一个时间段。",
|
||||
"request_body_end_time_internal_error": "内部错误。请求主体不包含结束时间",
|
||||
"create_calendar_event_error": "无法在组织者的日历中建立新的日历项目",
|
||||
"update_calendar_event_error": "无法更新日历事件。",
|
||||
"delete_calendar_event_error": "无法删除日历项目",
|
||||
"help": "帮助",
|
||||
"price": "价格",
|
||||
"paid": "已付款",
|
||||
@@ -1853,10 +1864,8 @@
|
||||
"requires_at_least_one_schedule": "您需要至少有一个日程安排",
|
||||
"default_conferencing_bulk_description": "更新选定活动类型的位置",
|
||||
"locked_for_members": "已为成员锁定",
|
||||
"locked_apps_description": "成员将能够查看活动的应用,但不能编辑任何应用设置",
|
||||
"locked_webhooks_description": "成员将能够查看活动的 Webhook,但不能编辑任何 Webhook 设置",
|
||||
"locked_workflows_description": "成员将能够查看活动的工作流程,但不能编辑任何工作流程设置",
|
||||
"locked_by_admin": "被团队管理员锁定",
|
||||
"apps_locked_for_members_description": "成员将能够查看活动的应用,但不能编辑任何应用设置",
|
||||
"locked_by_team_admin": "被团队管理员锁定",
|
||||
"app_not_connected": "您尚未连接 {{appName}} 帐户。",
|
||||
"connect_now": "立即连接",
|
||||
"managed_event_dialog_confirm_button_one": "替换并通知 {{count}} 个成员",
|
||||
@@ -2091,6 +2100,7 @@
|
||||
"workflow_automation": "工作流程自动化",
|
||||
"scheduling_for_your_team": "工作流程自动化",
|
||||
"no_members_found": "未找到成员",
|
||||
"directory_sync": "目录同步",
|
||||
"event_setup_length_error": "获得设置:持续时间必须至少为 1 分钟。",
|
||||
"availability_schedules": "可预约时间表",
|
||||
"unauthorized": "未经授权",
|
||||
@@ -2127,5 +2137,6 @@
|
||||
"extensive_whitelabeling": "专门的入门和工程支持",
|
||||
"need_help": "需要帮助?",
|
||||
"show_more": "显示更多",
|
||||
"privacy": "隐私",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 在此上方添加您的新字符串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -1823,10 +1823,8 @@
|
||||
"requires_at_least_one_schedule": "您必須擁有至少一個行程表",
|
||||
"default_conferencing_bulk_description": "更新所選活動類型的地點",
|
||||
"locked_for_members": "已為成員鎖定",
|
||||
"locked_apps_description": "成員將能查看啟用的應用程式,但無法編輯任何應用程式設定",
|
||||
"locked_webhooks_description": "成員將能查看啟用的 Webhook,但無法編輯任何 Webhook 設定",
|
||||
"locked_workflows_description": "成員將能查看啟用的工作流程,但無法編輯任何工作流程設定",
|
||||
"locked_by_admin": "已由團隊管理員鎖定",
|
||||
"apps_locked_for_members_description": "成員將能查看啟用的應用程式,但無法編輯任何應用程式設定",
|
||||
"locked_by_team_admin": "已由團隊管理員鎖定",
|
||||
"app_not_connected": "您尚未連結 {{appName}} 帳號。",
|
||||
"connect_now": "立即連結",
|
||||
"managed_event_dialog_confirm_button_one": "取代並通知 {{count}} 位成員",
|
||||
@@ -2061,6 +2059,7 @@
|
||||
"workflow_automation": "工作流程自動化",
|
||||
"scheduling_for_your_team": "工作流程自動化",
|
||||
"no_members_found": "找不到成員",
|
||||
"directory_sync": "Directory Sync",
|
||||
"event_setup_length_error": "活動設定:持續時間至少必須為 1 分鐘。",
|
||||
"availability_schedules": "可預約時間行程表",
|
||||
"unauthorized": "未授權",
|
||||
@@ -2097,5 +2096,6 @@
|
||||
"extensive_whitelabeling": "專屬的入門和工程支援",
|
||||
"need_help": "需要協助?",
|
||||
"show_more": "顯示更多",
|
||||
"privacy": "隱私權",
|
||||
"ADD_NEW_STRINGS_ABOVE_THIS_LINE_TO_PREVENT_MERGE_CONFLICTS": "↑↑↑↑↑↑↑↑↑↑↑↑↑ 請在此處新增您的字串 ↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑↑"
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
--cal-bg-info: hsla(218,83%,98%,1);
|
||||
--cal-bg-success: hsla(134,76%,94%,1);
|
||||
--cal-bg-attention: hsla(37, 86%, 92%, 1);
|
||||
--cal-bg-error: hsla(3,66,93,1);
|
||||
--cal-bg-error: hsla(3,66%,93%,1);
|
||||
--cal-bg-dark-error: hsla(2, 55%, 30%, 1);
|
||||
|
||||
/* Borders */
|
||||
@@ -95,7 +95,7 @@
|
||||
|
||||
@layer base {
|
||||
* {
|
||||
@apply border-default
|
||||
@apply border-default
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { expect, it } from "vitest";
|
||||
import { expect, beforeEach, afterEach, it, vi, describe } from "vitest";
|
||||
|
||||
import { filterByCities, addCitiesToDropdown, handleOptionLabel } from "@calcom/lib/timezone";
|
||||
|
||||
@@ -33,12 +33,22 @@ const option = {
|
||||
altName: "Pacific Standard Time",
|
||||
};
|
||||
|
||||
it("should return empty array for an empty string", () => {
|
||||
expect(filterByCities("", cityData)).toMatchInlineSnapshot(`[]`);
|
||||
});
|
||||
describe("getTimezone", () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers().setSystemTime(new Date("2020-01-01"));
|
||||
});
|
||||
|
||||
it("should filter cities for a valid city name", () => {
|
||||
expect(filterByCities("San Francisco", cityData)).toMatchInlineSnapshot(`
|
||||
afterEach(() => {
|
||||
vi.runOnlyPendingTimers();
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it("should return empty array for an empty string", () => {
|
||||
expect(filterByCities("", cityData)).toMatchInlineSnapshot(`[]`);
|
||||
});
|
||||
|
||||
it("should filter cities for a valid city name", () => {
|
||||
expect(filterByCities("San Francisco", cityData)).toMatchInlineSnapshot(`
|
||||
[
|
||||
{
|
||||
"city": "San Francisco",
|
||||
@@ -58,10 +68,10 @@ it("should filter cities for a valid city name", () => {
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it("should return appropriate timezone(s) for a given city name array", () => {
|
||||
expect(addCitiesToDropdown(cityData)).toMatchInlineSnapshot(`
|
||||
it("should return appropriate timezone(s) for a given city name array", () => {
|
||||
expect(addCitiesToDropdown(cityData)).toMatchInlineSnapshot(`
|
||||
{
|
||||
"America/Argentina/Cordoba": "San Francisco",
|
||||
"America/El_Salvador": "San Francisco Gotera",
|
||||
@@ -70,12 +80,17 @@ it("should return appropriate timezone(s) for a given city name array", () => {
|
||||
"America/Sao_Paulo": "Sao Francisco do Sul",
|
||||
}
|
||||
`);
|
||||
});
|
||||
});
|
||||
|
||||
it("should render city name as option label if cityData is not empty", () => {
|
||||
expect(handleOptionLabel(option, cityData)).toMatchInlineSnapshot(`"San Francisco GMT -8:00"`);
|
||||
});
|
||||
it("should render city name as option label if cityData is not empty", () => {
|
||||
expect(handleOptionLabel(option, cityData)).toMatchInlineSnapshot(`"San Francisco GMT -8:00"`);
|
||||
vi.setSystemTime(new Date("2020-06-01"));
|
||||
expect(handleOptionLabel(option, cityData)).toMatchInlineSnapshot(`"San Francisco GMT -7:00"`);
|
||||
});
|
||||
|
||||
it("should return timezone as option label if cityData is empty", () => {
|
||||
expect(handleOptionLabel(option, [])).toMatchInlineSnapshot(`"America/Los Angeles GMT -8:00"`);
|
||||
it("should return timezone as option label if cityData is empty", () => {
|
||||
expect(handleOptionLabel(option, [])).toMatchInlineSnapshot(`"America/Los Angeles GMT -8:00"`);
|
||||
vi.setSystemTime(new Date("2020-06-01"));
|
||||
expect(handleOptionLabel(option, [])).toMatchInlineSnapshot(`"America/Los Angeles GMT -7:00"`);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -11,7 +11,9 @@ import type { CompleteEventType, CompleteWorkflowsOnEventTypes } from "@calcom/p
|
||||
|
||||
const mockFindFirstEventType = (data?: Partial<CompleteEventType>) => {
|
||||
const eventType = buildEventType(data as Partial<EventType>);
|
||||
// const { scheduleId, destinationCalendar, ...restEventType } = eventType;
|
||||
prismaMock.eventType.findFirst.mockResolvedValue(eventType as EventType);
|
||||
|
||||
return eventType;
|
||||
};
|
||||
|
||||
@@ -52,7 +54,10 @@ describe("handleChildrenEventTypes", () => {
|
||||
});
|
||||
|
||||
it("Returns message 'No managed event metadata'", async () => {
|
||||
mockFindFirstEventType();
|
||||
mockFindFirstEventType({
|
||||
metadata: {},
|
||||
locations: [],
|
||||
});
|
||||
const result = await updateChildrenEventTypes({
|
||||
eventTypeId: 1,
|
||||
oldEventType: { children: [], team: { name: "" } },
|
||||
@@ -136,6 +141,8 @@ describe("handleChildrenEventTypes", () => {
|
||||
...evType,
|
||||
parentId: 1,
|
||||
users: { connect: [{ id: 4 }] },
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
requiresBookerEmailVerification: false,
|
||||
bookingLimits: undefined,
|
||||
durationLimits: undefined,
|
||||
recurringEvent: undefined,
|
||||
@@ -183,10 +190,14 @@ describe("handleChildrenEventTypes", () => {
|
||||
bookingLimits: undefined,
|
||||
},
|
||||
});
|
||||
const { profileId, ...rest } = evType;
|
||||
expect(prismaMock.eventType.update).toHaveBeenCalledWith({
|
||||
data: {
|
||||
hidden: false,
|
||||
bookingLimits: undefined,
|
||||
...rest,
|
||||
locations: [],
|
||||
scheduleId: null,
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
requiresBookerEmailVerification: false,
|
||||
hashedLink: { create: { link: expect.any(String) } },
|
||||
},
|
||||
where: {
|
||||
@@ -291,7 +302,10 @@ describe("handleChildrenEventTypes", () => {
|
||||
durationLimits: undefined,
|
||||
recurringEvent: undefined,
|
||||
hashedLink: undefined,
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
requiresBookerEmailVerification: false,
|
||||
userId: 4,
|
||||
workflows: undefined,
|
||||
},
|
||||
});
|
||||
expect(result.newUserIds).toEqual([4]);
|
||||
@@ -334,11 +348,13 @@ describe("handleChildrenEventTypes", () => {
|
||||
length: 30,
|
||||
},
|
||||
});
|
||||
const { profileId, ...rest } = evType;
|
||||
expect(prismaMock.eventType.update).toHaveBeenCalledWith({
|
||||
data: {
|
||||
hidden: false,
|
||||
length: 30,
|
||||
hashedLink: undefined,
|
||||
...rest,
|
||||
locations: [],
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
requiresBookerEmailVerification: false,
|
||||
},
|
||||
where: {
|
||||
userId_parentId: {
|
||||
@@ -403,6 +419,8 @@ describe("handleChildrenEventTypes", () => {
|
||||
recurringEvent: undefined,
|
||||
hashedLink: undefined,
|
||||
locations: [],
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
requiresBookerEmailVerification: false,
|
||||
parentId: 1,
|
||||
userId: 5,
|
||||
users: {
|
||||
@@ -417,9 +435,14 @@ describe("handleChildrenEventTypes", () => {
|
||||
},
|
||||
},
|
||||
});
|
||||
const { profileId, ...rest } = evType;
|
||||
if ("workflows" in rest) delete rest.workflows;
|
||||
expect(prismaMock.eventType.update).toHaveBeenCalledWith({
|
||||
data: {
|
||||
hidden: false,
|
||||
...rest,
|
||||
locations: [],
|
||||
lockTimeZoneToggleOnBookingPage: false,
|
||||
requiresBookerEmailVerification: false,
|
||||
hashedLink: undefined,
|
||||
},
|
||||
where: {
|
||||
|
||||
@@ -240,7 +240,9 @@ export default class GoogleCalendarService implements Calendar {
|
||||
eventId: calEventRaw.existingRecurringEvent.recurringEventId,
|
||||
});
|
||||
if (recurringEventInstances.data.items) {
|
||||
const calComEventStartTime = dayjs(calEventRaw.startTime).format();
|
||||
const calComEventStartTime = dayjs(calEventRaw.startTime)
|
||||
.tz(calEventRaw.organizer.timeZone)
|
||||
.format();
|
||||
for (let i = 0; i < recurringEventInstances.data.items.length; i++) {
|
||||
const instance = recurringEventInstances.data.items[i];
|
||||
const instanceStartTime = dayjs(instance.start?.dateTime)
|
||||
|
||||
@@ -21,6 +21,7 @@ export default function RoutingNavBar({
|
||||
},
|
||||
{
|
||||
name: "Reporting",
|
||||
target: "_blank",
|
||||
href: `${appUrl}/reporting/${form?.id}`,
|
||||
className: "pointer-events-none opacity-30 lg:pointer-events-auto lg:opacity-100",
|
||||
},
|
||||
|
||||
@@ -166,18 +166,16 @@ const Reporter = ({ form }: { form: inferSSRProps<typeof getServerSideProps>["fo
|
||||
[]
|
||||
);
|
||||
return (
|
||||
<div className="flex flex-col-reverse md:flex-row">
|
||||
<div className="cal-query-builder bg-default w-full ltr:mr-2 rtl:ml-2">
|
||||
<Query
|
||||
{...config}
|
||||
value={query.state.tree}
|
||||
onChange={(immutableTree, config) => {
|
||||
onChange(immutableTree, config as QueryBuilderUpdatedConfig);
|
||||
}}
|
||||
renderBuilder={renderBuilder}
|
||||
/>
|
||||
<Result formId={form.id} jsonLogicQuery={jsonLogicQuery as JsonLogicQuery} />
|
||||
</div>
|
||||
<div className="cal-query-builder bg-default fixed inset-0 w-full overflow-scroll pt-12 ltr:mr-2 rtl:ml-2 sm:pt-0">
|
||||
<Query
|
||||
{...config}
|
||||
value={query.state.tree}
|
||||
onChange={(immutableTree, config) => {
|
||||
onChange(immutableTree, config as QueryBuilderUpdatedConfig);
|
||||
}}
|
||||
renderBuilder={renderBuilder}
|
||||
/>
|
||||
<Result formId={form.id} jsonLogicQuery={jsonLogicQuery as JsonLogicQuery} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -261,6 +261,7 @@ test.describe("Routing Forms", () => {
|
||||
["custom-page", ""],
|
||||
]);
|
||||
|
||||
await page.goto(`apps/routing-forms/route-builder/${routingForm.id}`);
|
||||
const [download] = await Promise.all([
|
||||
// Start waiting for the download
|
||||
page.waitForEvent("download"),
|
||||
@@ -399,6 +400,7 @@ async function fillSeededForm(page: Page, routingFormId: string) {
|
||||
await gotoRoutingLink({ page, formId: routingFormId });
|
||||
await page.fill('[data-testid="form-field-Test field"]', "event-routing");
|
||||
page.click('button[type="submit"]');
|
||||
|
||||
await page.waitForURL((url) => {
|
||||
return url.pathname.endsWith("/pro/30min");
|
||||
});
|
||||
|
||||
@@ -16,7 +16,7 @@ export function UserFieldsResponses(props: { calEvent: CalendarEvent; t: TFuncti
|
||||
labelValueMap[key] !== "" ? (
|
||||
<Info
|
||||
key={key}
|
||||
label={key}
|
||||
label={t(key)}
|
||||
description={
|
||||
typeof labelValueMap[key] === "boolean"
|
||||
? labelValueMap[key]
|
||||
|
||||
@@ -66,6 +66,7 @@ const BookerComponent = ({
|
||||
bookerLayout,
|
||||
schedule,
|
||||
verifyCode,
|
||||
orgBannerUrl,
|
||||
}: BookerProps & WrappedBookerProps) => {
|
||||
const [bookerState, setBookerState] = useBookerStore((state) => [state.state, state.setState], shallow);
|
||||
const selectedDate = useBookerStore((state) => state.selectedDate);
|
||||
@@ -338,6 +339,15 @@ const BookerComponent = ({
|
||||
<BookerSection
|
||||
area="meta"
|
||||
className="max-w-screen flex w-full flex-col md:w-[var(--booker-meta-width)]">
|
||||
{orgBannerUrl && (
|
||||
<img
|
||||
loading="eager"
|
||||
className="-mb-9 h-40 max-h-40 rounded-tl-md sm:max-h-24"
|
||||
alt="org banner"
|
||||
src={orgBannerUrl}
|
||||
/>
|
||||
)}
|
||||
|
||||
<EventMeta event={event.data} isPending={event.isPending} />
|
||||
{layout !== BookerLayouts.MONTH_VIEW &&
|
||||
!(layout === "mobile" && bookerState === "booking") && (
|
||||
|
||||
@@ -51,7 +51,6 @@ export const BookEventForm = ({
|
||||
const timeslot = useBookerStore((state) => state.selectedTimeslot);
|
||||
const username = useBookerStore((state) => state.username);
|
||||
const isInstantMeeting = useBookerStore((state) => state.isInstantMeeting);
|
||||
const [expiryTime, setExpiryTime] = useState<Date | undefined>();
|
||||
|
||||
const [responseVercelIdHeader] = useState<string | null>(null);
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -88,7 +88,7 @@ export function Header({
|
||||
const isSameYear = () => {
|
||||
return selectedDate.format("YYYY") === endDate.format("YYYY");
|
||||
};
|
||||
const formattedMonth = new Intl.DateTimeFormat(i18n.language, { month: "short" });
|
||||
const formattedMonth = new Intl.DateTimeFormat(i18n.language ?? "en", { month: "short" });
|
||||
const FormattedSelectedDateRange = () => {
|
||||
return (
|
||||
<h3 className="min-w-[150px] text-base font-semibold leading-4">
|
||||
|
||||
@@ -124,7 +124,7 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
|
||||
date: responseData?.startTime?.toString() || "",
|
||||
duration: validDuration,
|
||||
organizer: {
|
||||
name: users?.[0].name || "Nameless",
|
||||
name: users?.[0]?.name || "Nameless",
|
||||
email: responseData?.userPrimaryEmail || responseData.user?.email || "Email-less",
|
||||
timeZone: responseData.user?.timeZone || "Europe/London",
|
||||
},
|
||||
@@ -137,7 +137,7 @@ export const useBookings = ({ event, hashedLink, bookingForm, metadata }: IUseBo
|
||||
date: responseData?.startTime?.toString() || "",
|
||||
duration: validDuration,
|
||||
organizer: {
|
||||
name: users?.[0].name || "Nameless",
|
||||
name: users?.[0]?.name || "Nameless",
|
||||
email: responseData?.userPrimaryEmail || responseData.user?.email || "Email-less",
|
||||
timeZone: responseData.user?.timeZone || "Europe/London",
|
||||
},
|
||||
|
||||
@@ -16,6 +16,7 @@ import type { GetBookingType } from "../lib/get-booking";
|
||||
export interface BookerProps {
|
||||
eventSlug: string;
|
||||
username: string;
|
||||
orgBannerUrl?: string | null;
|
||||
|
||||
/**
|
||||
* Whether is a team or org, we gather basic info from both
|
||||
|
||||
@@ -194,7 +194,9 @@ export const AvailableTimes = ({
|
||||
<div className={classNames("text-default flex flex-col", className)}>
|
||||
<div className="h-full pb-4">
|
||||
{!slots.length && (
|
||||
<div className="bg-subtle border-subtle flex h-full flex-col items-center rounded-md border p-6 dark:bg-transparent">
|
||||
<div
|
||||
data-testId="no-slots-available"
|
||||
className="bg-subtle border-subtle flex h-full flex-col items-center rounded-md border p-6 dark:bg-transparent">
|
||||
<CalendarX2 className="text-muted mb-2 h-4 w-4" />
|
||||
<p className={classNames("text-muted", showTimeFormatToggle ? "-mt-1 text-lg" : "text-sm")}>
|
||||
{t("all_booked_today")}
|
||||
|
||||
@@ -12,3 +12,4 @@ export const SystemField = z.enum([
|
||||
]);
|
||||
|
||||
export const SMS_REMINDER_NUMBER_FIELD = "smsReminderNumber";
|
||||
export const TITLE_FIELD = "title";
|
||||
|
||||
@@ -864,6 +864,7 @@ export const findBookingQuery = async (bookingId: number) => {
|
||||
description: true,
|
||||
status: true,
|
||||
responses: true,
|
||||
metadata: true,
|
||||
user: {
|
||||
select: {
|
||||
name: true,
|
||||
@@ -1518,7 +1519,7 @@ async function handler(
|
||||
eventDescription: eventType.description,
|
||||
price: paymentAppData.price,
|
||||
currency: eventType.currency,
|
||||
length: eventType.length,
|
||||
length: reqEventLength,
|
||||
};
|
||||
|
||||
const teamId = await getTeamIdFromEventType({ eventType });
|
||||
@@ -2069,6 +2070,7 @@ async function handler(
|
||||
calEvent: getPiiFreeCalendarEvent(evt),
|
||||
})
|
||||
);
|
||||
|
||||
await sendScheduledEmails(
|
||||
{
|
||||
...evt,
|
||||
@@ -2111,6 +2113,10 @@ async function handler(
|
||||
await sendAttendeeRequestEmail({ ...evt, additionalNotes }, attendeesList[0]);
|
||||
}
|
||||
|
||||
if (booking.location?.startsWith("http")) {
|
||||
videoCallUrl = booking.location;
|
||||
}
|
||||
|
||||
const metadata = videoCallUrl
|
||||
? {
|
||||
videoCallUrl: getVideoCallUrlFromCalEvent(evt) || videoCallUrl,
|
||||
@@ -2213,10 +2219,6 @@ async function handler(
|
||||
|
||||
loggerWithEventDetails.debug(`Booking ${organizerUser.username} completed`);
|
||||
|
||||
if (booking.location?.startsWith("http")) {
|
||||
videoCallUrl = booking.location;
|
||||
}
|
||||
|
||||
// We are here so, booking doesn't require payment and booking is also created in DB already, through createBooking call
|
||||
if (isConfirmedByDefault) {
|
||||
try {
|
||||
|
||||
@@ -96,8 +96,10 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
|
||||
|
||||
// If the resultBooking is defined we should trigger workflows else, trigger in handleNewBooking
|
||||
if (resultBooking) {
|
||||
// Obtain event metadata that includes videoCallUrl
|
||||
const metadata = evt.videoCallData?.url ? { videoCallUrl: evt.videoCallData.url } : undefined;
|
||||
const metadata = {
|
||||
...(typeof resultBooking.metadata === "object" && resultBooking.metadata),
|
||||
...reqBodyMetadata,
|
||||
};
|
||||
try {
|
||||
await scheduleWorkflowReminders({
|
||||
workflows: eventType.workflows,
|
||||
@@ -126,7 +128,7 @@ const handleSeats = async (newSeatedBookingObject: NewSeatedBookingObject) => {
|
||||
rescheduleEndTime: originalRescheduledBooking?.endTime
|
||||
? dayjs(originalRescheduledBooking?.endTime).utc().format()
|
||||
: undefined,
|
||||
metadata: { ...metadata, ...reqBodyMetadata },
|
||||
metadata,
|
||||
eventTypeId,
|
||||
status: "ACCEPTED",
|
||||
smsReminderNumber: seatedBooking?.smsReminderNumber || undefined,
|
||||
|
||||
@@ -187,7 +187,9 @@ describe("handleSeats", () => {
|
||||
body: mockBookingData,
|
||||
});
|
||||
|
||||
await handleNewBooking(req);
|
||||
const createdBooking = await handleNewBooking(req);
|
||||
|
||||
expect(createdBooking.metadata).toHaveProperty("videoCallUrl");
|
||||
|
||||
const handleSeatsCall = spy.mock.calls[0][0];
|
||||
|
||||
|
||||
@@ -0,0 +1,114 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import {
|
||||
AppSkeletonLoader as SkeletonLoader,
|
||||
Button,
|
||||
Dialog,
|
||||
DialogClose,
|
||||
DialogContent,
|
||||
DialogFooter,
|
||||
DialogTrigger,
|
||||
Label,
|
||||
showToast,
|
||||
EmptyScreen,
|
||||
} from "@calcom/ui";
|
||||
import { AlertTriangle, Trash2 } from "@calcom/ui/components/icon";
|
||||
|
||||
import CreateDirectory from "./CreateDirectory";
|
||||
import DirectoryInfo from "./DirectoryInfo";
|
||||
import GroupTeamMappingTable from "./GroupTeamMappingTable";
|
||||
|
||||
const ConfigureDirectorySync = ({ organizationId }: { organizationId: number | null }) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const [deleteDirectoryOpen, setDeleteDirectoryOpen] = useState(false);
|
||||
|
||||
const { data, isLoading, isError, error } = trpc.viewer.dsync.get.useQuery({ organizationId });
|
||||
|
||||
const deleteMutation = trpc.viewer.dsync.delete.useMutation({
|
||||
async onSuccess() {
|
||||
showToast(t("directory_sync_deleted"), "success");
|
||||
await utils.viewer.dsync.invalidate();
|
||||
setDeleteDirectoryOpen(false);
|
||||
},
|
||||
});
|
||||
|
||||
if (isLoading) {
|
||||
return <SkeletonLoader />;
|
||||
}
|
||||
|
||||
const directory = data ? data[0] : null;
|
||||
|
||||
const onDeleteConfirmation = (e: Event | React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!directory) {
|
||||
return;
|
||||
}
|
||||
|
||||
deleteMutation.mutate({ organizationId, directoryId: directory.id });
|
||||
};
|
||||
|
||||
if (error || isError) {
|
||||
return (
|
||||
<div>
|
||||
<EmptyScreen
|
||||
headline="Error"
|
||||
description={error.message || "Error getting dsync data"}
|
||||
Icon={AlertTriangle}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
{!directory ? (
|
||||
<CreateDirectory orgId={organizationId} />
|
||||
) : (
|
||||
<>
|
||||
<DirectoryInfo directory={directory} />
|
||||
<div className="mt-4">
|
||||
<GroupTeamMappingTable />
|
||||
</div>
|
||||
|
||||
<hr className="border-subtle my-6" />
|
||||
<Label>{t("danger_zone")}</Label>
|
||||
{/* Delete directory sync connection */}
|
||||
<Dialog open={deleteDirectoryOpen} onOpenChange={setDeleteDirectoryOpen}>
|
||||
<DialogTrigger asChild>
|
||||
<Button color="destructive" className="mt-1" StartIcon={Trash2}>
|
||||
{t("directory_sync_delete_connection")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<DialogContent
|
||||
title={t("directory_sync_delete_title")}
|
||||
description={t("directory_sync_delete_description")}
|
||||
type="creation"
|
||||
Icon={AlertTriangle}>
|
||||
<>
|
||||
<div className="mb-10">
|
||||
<p className="text-default mb-4">{t("directory_sync_delete_confirmation")}</p>
|
||||
</div>
|
||||
<DialogFooter showDivider>
|
||||
<DialogClose />
|
||||
<Button
|
||||
color="primary"
|
||||
data-testid="delete-account-confirm"
|
||||
onClick={onDeleteConfirmation}
|
||||
loading={deleteMutation.isPending}>
|
||||
{t("directory_sync_delete_connection")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfigureDirectorySync;
|
||||
@@ -0,0 +1,126 @@
|
||||
import { useState } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import {
|
||||
Button,
|
||||
Dialog,
|
||||
DialogContent,
|
||||
SelectField,
|
||||
Form,
|
||||
TextField,
|
||||
DialogFooter,
|
||||
showToast,
|
||||
} from "@calcom/ui";
|
||||
|
||||
import { directoryProviders } from "../lib/directoryProviders";
|
||||
|
||||
const defaultValues = {
|
||||
name: "",
|
||||
provider: directoryProviders[0].value,
|
||||
};
|
||||
|
||||
const CreateDirectory = ({ orgId }: { orgId: number | null }) => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const form = useForm({ defaultValues });
|
||||
const [openModal, setOpenModal] = useState(false);
|
||||
|
||||
const mutation = trpc.viewer.dsync.create.useMutation({
|
||||
async onSuccess() {
|
||||
showToast(t("directory_sync_created"), "success");
|
||||
await utils.viewer.dsync.invalidate();
|
||||
setOpenModal(false);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="flex flex-col sm:flex-row">
|
||||
<div>
|
||||
<p className="text-default text-sm font-normal leading-6 dark:text-gray-300">
|
||||
{t("directory_sync_title")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 pt-3 sm:ml-auto sm:pl-3 sm:pt-0">
|
||||
<Button color="primary" onClick={() => setOpenModal(true)}>
|
||||
{t("configure")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Dialog open={openModal} onOpenChange={setOpenModal}>
|
||||
<DialogContent type="creation">
|
||||
<Form
|
||||
form={form}
|
||||
handleSubmit={(values) => {
|
||||
mutation.mutate({
|
||||
...values,
|
||||
organizationId: orgId,
|
||||
});
|
||||
}}>
|
||||
<div className="mb-5 mt-1">
|
||||
<h2 className="font-semi-bold font-cal text-emphasis text-xl tracking-wide">
|
||||
{t("directory_sync_configure")}
|
||||
</h2>
|
||||
<p className="mt-1 text-sm text-gray-500">{t("directory_sync_configure_description")}</p>
|
||||
</div>
|
||||
<fieldset className="space-y-6 py-2">
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field: { value } }) => (
|
||||
<TextField
|
||||
name="title"
|
||||
label={t("directory_name")}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
form.setValue("name", e?.target.value);
|
||||
}}
|
||||
type="text"
|
||||
required
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="provider"
|
||||
render={() => (
|
||||
<SelectField
|
||||
name="provider"
|
||||
label={t("directory_provider")}
|
||||
options={directoryProviders}
|
||||
placeholder={t("choose_directory_provider")}
|
||||
defaultValue={directoryProviders[0]}
|
||||
onChange={(option) => {
|
||||
if (option) {
|
||||
form.setValue("provider", option.value);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</fieldset>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
type="button"
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
setOpenModal(false);
|
||||
}}
|
||||
tabIndex={-1}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button type="submit" loading={form.formState.isSubmitting || mutation.isPending}>
|
||||
{t("save")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</Form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateDirectory;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { CreateANewTeamForm } from "@calcom/features/ee/teams/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Dialog, DialogContent } from "@calcom/ui";
|
||||
|
||||
interface CreateTeamDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
}
|
||||
|
||||
const CreateTeamDialog = (props: CreateTeamDialogProps) => {
|
||||
const { open, onOpenChange } = props;
|
||||
const { t } = useLocale();
|
||||
|
||||
const utils = trpc.useContext();
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={onOpenChange}>
|
||||
<DialogContent type="creation" title={t("create_new_team")} description={t("team_will_be_under_org")}>
|
||||
<CreateANewTeamForm
|
||||
inDialog
|
||||
submitLabel="Create"
|
||||
onCancel={() => onOpenChange(false)}
|
||||
onSuccess={async () => {
|
||||
await utils.viewer.dsync.teamGroupMapping.get.invalidate();
|
||||
await utils.viewer.teams.list.invalidate();
|
||||
onOpenChange(false);
|
||||
}}
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateTeamDialog;
|
||||
@@ -0,0 +1,63 @@
|
||||
import type { Directory } from "@boxyhq/saml-jackson";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Button, showToast, Label, Tooltip } from "@calcom/ui";
|
||||
import { Clipboard } from "@calcom/ui/components/icon";
|
||||
|
||||
const DirectoryInfo = ({ directory }: { directory: Directory }) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
<div className="space-y-8">
|
||||
<p className="text-default text-sm font-normal leading-6 dark:text-gray-300">
|
||||
{t("directory_sync_info_description")}
|
||||
</p>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex">
|
||||
<Label>{t("directory_scim_url")}</Label>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<code className="bg-subtle text-default w-full truncate rounded-md rounded-r-none py-[6px] pl-2 pr-2 align-middle font-mono">
|
||||
{directory.scim.endpoint}
|
||||
</code>
|
||||
<Tooltip side="top" content={t("copy_to_clipboard")}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(`${directory.scim.endpoint}`);
|
||||
showToast(t("directory_scim_url_copied"), "success");
|
||||
}}
|
||||
type="button"
|
||||
className="rounded-l-none text-base"
|
||||
StartIcon={Clipboard}>
|
||||
{t("copy")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
<div className="flex">
|
||||
<Label>{t("directory_scim_token")}</Label>
|
||||
</div>
|
||||
<div className="flex">
|
||||
<code className="bg-subtle text-default w-full truncate rounded-md rounded-r-none py-[6px] pl-2 pr-2 align-middle font-mono">
|
||||
{directory.scim.secret}
|
||||
</code>
|
||||
<Tooltip side="top" content={t("copy_to_clipboard")}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(`${directory.scim.secret}`);
|
||||
showToast(t("directory_scim_token_copied"), "success");
|
||||
}}
|
||||
type="button"
|
||||
className="rounded-l-none text-base"
|
||||
StartIcon={Clipboard}>
|
||||
{t("copy")}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default DirectoryInfo;
|
||||
@@ -0,0 +1,117 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Badge, TextField, showToast } from "@calcom/ui";
|
||||
import { X, Plus } from "@calcom/ui/components/icon";
|
||||
|
||||
interface GroupNameCellProps {
|
||||
groupNames: string[];
|
||||
teamId: number;
|
||||
directoryId: string;
|
||||
}
|
||||
|
||||
const GroupNameCell = ({ groupNames, teamId, directoryId }: GroupNameCellProps) => {
|
||||
const [showTextInput, setShowTextInput] = useState(false);
|
||||
const [newGroupName, setNewGroupName] = useState("");
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
|
||||
const createMutation = trpc.viewer.dsync.teamGroupMapping.create.useMutation({
|
||||
onSuccess: (data) => {
|
||||
utils.viewer.dsync.teamGroupMapping.get.setData(undefined, (prev) => {
|
||||
if (prev) {
|
||||
const teamIndex = prev.teamGroupMapping.findIndex((team) => team.id === teamId);
|
||||
prev.teamGroupMapping[teamIndex].groupNames.push(data.newGroupName);
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
setShowTextInput(false);
|
||||
setNewGroupName("");
|
||||
showToast(`Group added`, "success");
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(`Error adding group name${error.message}`, "error");
|
||||
},
|
||||
});
|
||||
|
||||
const deleteMutation = trpc.viewer.dsync.teamGroupMapping.delete.useMutation({
|
||||
onSuccess: (data) => {
|
||||
utils.viewer.dsync.teamGroupMapping.get.setData(undefined, (prev) => {
|
||||
if (prev) {
|
||||
const teamIndex = prev.teamGroupMapping.findIndex((team) => team.id === teamId);
|
||||
const indexToRemove = prev.teamGroupMapping[teamIndex].groupNames.indexOf(data.deletedGroupName);
|
||||
prev.teamGroupMapping[teamIndex].groupNames.splice(indexToRemove, 1);
|
||||
}
|
||||
return prev;
|
||||
});
|
||||
showToast(`Group removed`, "success");
|
||||
},
|
||||
onError: (error) => {
|
||||
showToast(`Error removing group name${error.message}`, "error");
|
||||
},
|
||||
});
|
||||
|
||||
const addGroupName = (groupName: string) => {
|
||||
if (groupNames.some((name: string) => name === groupName)) {
|
||||
showToast(`Group name already added`, "error");
|
||||
return;
|
||||
}
|
||||
|
||||
createMutation.mutate({ teamId: teamId, name: groupName, directoryId: directoryId });
|
||||
};
|
||||
|
||||
const removeGroupName = (groupName: string) => {
|
||||
deleteMutation.mutate({
|
||||
teamId: teamId,
|
||||
groupName: groupName,
|
||||
});
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex items-center space-x-4">
|
||||
{groupNames.map((name) => (
|
||||
<Badge variant="gray" size="lg" key={name} className="h-8 py-4">
|
||||
<div className="flex items-center space-x-2 ">
|
||||
<p>{name}</p>
|
||||
<div className="hover:bg-emphasis rounded p-1">
|
||||
<X className="h-4 w-4 stroke-[3px]" onClick={() => removeGroupName(name)} />
|
||||
</div>
|
||||
</div>
|
||||
</Badge>
|
||||
))}
|
||||
<Badge variant="gray" size="lg" className={classNames(!showTextInput && "hover:bg-emphasis")}>
|
||||
<div
|
||||
className="flex items-center space-x-1"
|
||||
onClick={() => {
|
||||
if (!showTextInput) setShowTextInput(true);
|
||||
}}>
|
||||
{showTextInput ? (
|
||||
<TextField
|
||||
autoFocus
|
||||
className="mb-0 h-6"
|
||||
onBlur={() => {
|
||||
if (!newGroupName) setShowTextInput(false);
|
||||
}}
|
||||
onChange={(e) => setNewGroupName(e.target.value)}
|
||||
value={newGroupName}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
addGroupName(newGroupName);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<p>{t("add_group_name")}</p>
|
||||
)}
|
||||
<div className={classNames("rounded p-1", showTextInput && "hover:bg-emphasis ml-2")}>
|
||||
<Plus className="h-4 w-4 stroke-[3px]" onClick={() => addGroupName(newGroupName)} />
|
||||
</div>
|
||||
</div>
|
||||
</Badge>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupNameCell;
|
||||
@@ -0,0 +1,60 @@
|
||||
import type { ColumnDef } from "@tanstack/react-table";
|
||||
import { useRef, useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { DataTable, Button } from "@calcom/ui";
|
||||
|
||||
import CreateTeamDialog from "./CreateTeamDialog";
|
||||
import GroupNameCell from "./GroupNameCell";
|
||||
|
||||
interface TeamGroupMapping {
|
||||
name: string;
|
||||
id: number;
|
||||
groupNames: string[];
|
||||
directoryId: string;
|
||||
}
|
||||
|
||||
const GroupTeamMappingTable = () => {
|
||||
const { t } = useLocale();
|
||||
const [createTeamDialogOpen, setCreateTeamDialogOpen] = useState(false);
|
||||
|
||||
const { data } = trpc.viewer.dsync.teamGroupMapping.get.useQuery();
|
||||
|
||||
const tableContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
const columns: ColumnDef<TeamGroupMapping>[] = [
|
||||
{
|
||||
id: "name",
|
||||
header: t("team"),
|
||||
cell: ({ row }) => {
|
||||
const { name } = row.original;
|
||||
|
||||
return <p>{name}</p>;
|
||||
},
|
||||
},
|
||||
{
|
||||
id: "group",
|
||||
header: t("group_name"),
|
||||
cell: ({ row }) => {
|
||||
const { id, groupNames, directoryId } = row.original;
|
||||
|
||||
return <GroupNameCell groupNames={groupNames} teamId={id} directoryId={directoryId} />;
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<DataTable
|
||||
data={data ? data.teamGroupMapping : []}
|
||||
tableContainerRef={tableContainerRef}
|
||||
columns={columns}
|
||||
tableCTA={<Button onClick={() => setCreateTeamDialogOpen(true)}>Create team</Button>}
|
||||
/>
|
||||
<CreateTeamDialog open={createTeamDialogOpen} onOpenChange={setCreateTeamDialogOpen} />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default GroupTeamMappingTable;
|
||||
@@ -0,0 +1,67 @@
|
||||
import type { TFunction } from "next-i18next";
|
||||
|
||||
import { ProfileRepository } from "@calcom/lib/server/repository/profile";
|
||||
import slugify from "@calcom/lib/slugify";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { MembershipRole } from "@calcom/prisma/enums";
|
||||
import type { getTeamOrThrow } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/utils";
|
||||
import { sendSignupToOrganizationEmail } from "@calcom/trpc/server/routers/viewer/teams/inviteMember/utils";
|
||||
|
||||
const createUserAndInviteToOrg = async ({
|
||||
userEmail,
|
||||
org,
|
||||
translation,
|
||||
}: {
|
||||
userEmail: string;
|
||||
org: Awaited<ReturnType<typeof getTeamOrThrow>>;
|
||||
translation: TFunction;
|
||||
}) => {
|
||||
const orgId = org.id;
|
||||
const [emailUser, emailDomain] = userEmail.split("@");
|
||||
const username = slugify(`${emailUser}-${emailDomain.split(".")[0]}`);
|
||||
await prisma.user.create({
|
||||
data: {
|
||||
username,
|
||||
email: userEmail,
|
||||
// name: event.data?.givenName,
|
||||
// Assume verified since coming from directory
|
||||
verified: true,
|
||||
invitedTo: orgId,
|
||||
organizationId: orgId,
|
||||
teams: {
|
||||
create: {
|
||||
teamId: orgId,
|
||||
role: MembershipRole.MEMBER,
|
||||
accepted: true,
|
||||
},
|
||||
},
|
||||
profiles: {
|
||||
createMany: {
|
||||
data: [
|
||||
{
|
||||
uid: ProfileRepository.generateProfileUid(),
|
||||
username,
|
||||
organizationId: orgId,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
await sendSignupToOrganizationEmail({
|
||||
usernameOrEmail: userEmail,
|
||||
team: org,
|
||||
translation,
|
||||
inviterName: org.name,
|
||||
input: {
|
||||
teamId: orgId,
|
||||
role: MembershipRole.MEMBER,
|
||||
usernameOrEmail: userEmail,
|
||||
language: "en",
|
||||
isOrg: true,
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
export default createUserAndInviteToOrg;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user