Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
50151403c4 | ||
|
|
7aa131f349 | ||
|
|
d88bdc368b | ||
|
|
255938fda0 | ||
|
|
5766d6f5f4 | ||
|
|
9d299c6d5f | ||
|
|
f49783b453 | ||
|
|
246fce3ab8 | ||
|
|
82f262f60d | ||
|
|
da8d2da368 | ||
|
|
f01058e073 | ||
|
|
8e369d3fb5 | ||
|
|
11c44db609 | ||
|
|
9d911cbb59 | ||
|
|
30a887df6f | ||
|
|
62fea25eb9 | ||
|
|
d43d8700d0 | ||
|
|
e7ed20ea27 | ||
|
|
5e12b5defe | ||
|
|
d241a522fb | ||
|
+2 |
ae929f27b0 | ||
|
|
369d7c4e28 | ||
|
|
4438ca2a63 | ||
|
|
75589db569 | ||
|
|
3752ac767a | ||
|
+3 |
f3e2cf5075 | ||
|
|
92a29593aa | ||
|
|
367c5d89cc | ||
|
|
f21f36547c | ||
|
|
d3f8d954e7 | ||
|
|
27372d624d | ||
|
|
a5d2f2444a | ||
|
|
6d45e1c7c7 | ||
|
|
48419469a6 | ||
|
|
045ebb6e01 | ||
|
|
c78ea5de0b | ||
|
|
d1a787315c | ||
|
|
ce1f1f3c47 | ||
|
|
2f8bd3c07a | ||
|
|
d2f27a0463 | ||
|
|
9134ad8c5e | ||
|
|
506036e751 | ||
|
|
5e80f5056d | ||
|
|
9304ca1415 | ||
|
|
fb246fdee6 | ||
|
|
7b9e77f43a | ||
|
|
47df21322d | ||
|
|
6017806763 | ||
|
|
b347f29d6c | ||
|
|
c26cf74aec | ||
|
|
760ed74a11 | ||
|
|
145fb09b73 | ||
|
|
dd14e827e1 | ||
|
|
246c90c10f | ||
|
|
a42f703c1d | ||
|
|
7256dbf84f | ||
|
|
f5faebad67 | ||
|
+3 |
be15affa5d | ||
|
|
58dd5308ac | ||
|
|
14a6c5a03f | ||
|
|
5af20f5a7b | ||
|
|
8478363a23 | ||
|
|
19b3157c94 | ||
|
|
d4dd451c26 | ||
|
|
6c046f6507 | ||
|
|
a792d31939 | ||
|
|
fc997739b4 | ||
|
|
542d9aa3c6 | ||
|
|
8f3e088db7 | ||
|
|
699313ac70 | ||
|
|
fca7087e7d | ||
|
|
a6b16490c0 | ||
|
|
5da735c418 | ||
|
|
f9ef883f3d | ||
|
|
9ddbaf0966 | ||
|
|
0695ce7edb | ||
|
|
74f0d39997 | ||
|
|
85ab3fb164 | ||
|
|
ab417ac267 | ||
|
|
24635af730 | ||
|
|
682f0c66ff | ||
|
|
ace27ca84e | ||
|
|
ce41397517 | ||
|
|
81aba55710 | ||
|
|
c5fc3a7e8e | ||
|
|
f628ac4366 | ||
|
|
6e2e252c49 | ||
|
|
10d3a126aa | ||
|
|
341c39cb6b | ||
|
|
91909281bc | ||
|
|
f14b13a1ba | ||
|
|
ca859c871c | ||
|
|
6e49a3e1c3 | ||
|
|
830f42bf9a | ||
|
|
0830523b84 | ||
|
|
612b293a70 | ||
|
|
3ecfa75f86 | ||
|
|
631e974769 | ||
|
|
42c128b790 | ||
|
|
7d1065d228 | ||
|
|
e2747f89d0 | ||
|
|
a1d4589e66 | ||
|
|
7d2f87a13e | ||
|
|
4e7200a44b | ||
|
|
52959fe257 | ||
|
|
61b010a60a | ||
|
|
4e6e484d8b | ||
|
|
0a2de203a6 | ||
|
|
48ee7a956c | ||
|
|
ae46d615ef | ||
|
|
9b6d5d2ff8 | ||
|
|
ec0352da20 | ||
|
|
7e65d5d61d | ||
|
|
f855f24d5c | ||
|
|
c2057c4cc2 | ||
|
|
1640264e40 | ||
|
|
579fd399c3 | ||
|
|
1de00d46bf | ||
|
|
01db3ad14e | ||
|
|
c1cb02a9d1 | ||
|
|
344710d1f9 | ||
|
|
6f0b4f94e6 | ||
|
|
c6ad2de2f9 | ||
|
|
9ae3a0eaba | ||
|
|
2f3d7318a4 | ||
|
|
6e71de7ca3 | ||
|
|
7d277ad89f | ||
|
|
ac276a9654 | ||
|
|
588748d78e | ||
|
|
25711ef18e | ||
|
|
440f53714b | ||
|
|
22f6d8ab9b | ||
|
|
57f0d115cf | ||
|
|
23bc6aa600 | ||
|
|
abdb196f56 | ||
|
|
8c0e2b5129 | ||
|
|
cf32287109 | ||
|
|
2ef8e230c5 | ||
|
|
546d0d50c4 | ||
|
|
33e8198779 | ||
|
|
e7dd0ff3e9 | ||
|
|
1b2707d4f0 | ||
|
|
00071bcdf0 | ||
|
|
fc5cc5b8bc | ||
|
|
1e79a55825 | ||
|
|
4645bbcb75 | ||
|
|
95b903110f | ||
|
|
2917455324 | ||
|
|
d71765848c | ||
|
|
cf7b7d7d3d | ||
|
|
96aaa36751 | ||
|
|
52e74245a4 | ||
|
|
cc03b132e6 |
@@ -8,6 +8,8 @@ on:
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
env:
|
||||
NODE_OPTIONS: "--max-old-space-size=8192"
|
||||
jobs:
|
||||
check-types:
|
||||
runs-on: ubuntu-latest
|
||||
@@ -20,8 +22,13 @@ jobs:
|
||||
|
||||
- name: Use Node 16.x
|
||||
uses: actions/setup-node@v3
|
||||
|
||||
with:
|
||||
node-version: 16.x
|
||||
cache: "yarn"
|
||||
|
||||
- name: Show info
|
||||
run: node -e "console.log(require('v8').getHeapStatistics())"
|
||||
|
||||
- run: yarn --frozen-lockfile
|
||||
- run: yarn type-check
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
<a href="https://news.ycombinator.com/item?id=26817795"><img src="https://img.shields.io/badge/Hacker%20News-311-%23FF6600" alt="Hacker News"></a>
|
||||
<a href="https://github.com/calcom/cal.com/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-AGPLv3-purple" alt="License"></a>
|
||||
<a href="https://github.com/calcom/cal.com/pulse"><img src="https://img.shields.io/github/commit-activity/m/calcom/cal.com" alt="Commits-per-month"></a>
|
||||
<a href="https://cal.com/pricing"><img src="https://img.shields.io/badge/Pricing-%2412%2Fmonth-brightgreen" alt="Pricing"></a>
|
||||
<a href="https://cal.com/pricing"><img src="https://img.shields.io/badge/Pricing-Free-brightgreen" alt="Pricing"></a>
|
||||
<a href="https://jitsu.com?utm_source=github/calcom/cal.com"><img src="https://img.shields.io/badge/Metrics_tracked_by-JITSU-AA00FF?logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA4AAAAOCAYAAAAfSC3RAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAACKSURBVHgBrZDRCYAwDEQv6gCOoKO4hOCXI9QVnEZwiY5iF5GaVClaBNtioCSUvCR3tMJaxIfZgW4AGUoEPVwgPZoS0Dmgg3NBVDFNbMIsmYCak3J1jDk9iCQvsKJvkzr71N81Gj6vDT/LU2P6RhY63jcafk3YJEbgeZpiFyc/5HJKv8Ef273NSfABGbQfUZhnOSAAAAAASUVORK5CYII=" alt="Jitsu Tracked"></a>
|
||||
<img src="https://api.checklyhq.com/v1/badges/checks/5e048048-1b51-47ba-9209-60607507622e?responseTime=true" alt="Checkly Availability" />
|
||||
<a href="https://hub.docker.com/r/calendso/calendso"><img src="https://img.shields.io/docker/pulls/calendso/calendso"></a>
|
||||
|
||||
+1
-1
Submodule apps/api updated: fcb9ca6ace...7e9226fabc
@@ -6,7 +6,7 @@ title: Vercel
|
||||
|
||||
## Requirements
|
||||
|
||||
You need a PostgresDB database hosted somewhere. [Heroku](https://www.heroku.com) and [Supabase](https://supabase.com/) offer great free options.
|
||||
You need a PostgresDB database hosted somewhere. [Supabase](https://supabase.com/) offers a great free option.
|
||||
|
||||
## Getting Started
|
||||
|
||||
|
||||
@@ -15,14 +15,15 @@
|
||||
"dependencies": {
|
||||
"@calcom/dayjs": "*",
|
||||
"@calcom/ui": "*",
|
||||
"@radix-ui/react-avatar": "^0.1.4",
|
||||
"@radix-ui/react-collapsible": "^0.1.0",
|
||||
"@radix-ui/react-dialog": "^0.1.0",
|
||||
"@radix-ui/react-dropdown-menu": "^0.1.1",
|
||||
"@radix-ui/react-id": "^0.1.0",
|
||||
"@radix-ui/react-radio-group": "^0.1.1",
|
||||
"@radix-ui/react-slider": "^0.1.1",
|
||||
"@radix-ui/react-switch": "^0.1.1",
|
||||
"@radix-ui/react-avatar": "^1.0.0",
|
||||
"@radix-ui/react-collapsible": "^1.0.0",
|
||||
"@radix-ui/react-dialog": "^1.0.0",
|
||||
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
||||
"@radix-ui/react-id": "^1.0.0",
|
||||
"@radix-ui/react-popover": "^1.0.0",
|
||||
"@radix-ui/react-radio-group": "^1.0.0",
|
||||
"@radix-ui/react-slider": "^1.0.0",
|
||||
"@radix-ui/react-switch": "^1.0.0",
|
||||
"@radix-ui/react-tooltip": "^1.0.0",
|
||||
"next": "^12.2.5",
|
||||
"next-transpile-modules": "^9.0.0",
|
||||
|
||||
@@ -426,7 +426,7 @@ paths:
|
||||
- installed: true
|
||||
type: caldav_calendar
|
||||
credential: null
|
||||
title: CalDav Server
|
||||
title: CalDav (Beta)
|
||||
imageSrc: integrations/caldav.svg
|
||||
description: For personal and business calendars
|
||||
__N_SSP: true
|
||||
|
||||
@@ -1,15 +1,14 @@
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||
import classNames from "classnames";
|
||||
import { useRouter } from "next/router";
|
||||
import { NextRouter, useRouter } from "next/router";
|
||||
import { createRef, forwardRef, MutableRefObject, RefObject, useRef, useState } from "react";
|
||||
import { components, ControlProps } from "react-select";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { InputLeading, Label, TextArea, TextField } from "@calcom/ui/form/fields";
|
||||
import { HorizontalTabs, showToast } from "@calcom/ui/v2";
|
||||
import { Button, Switch } from "@calcom/ui/v2";
|
||||
import { Button, HorizontalTabs, showToast, Switch } from "@calcom/ui/v2";
|
||||
import { Dialog, DialogClose, DialogContent } from "@calcom/ui/v2/core/Dialog";
|
||||
|
||||
import { EMBED_LIB_URL, WEBAPP_URL } from "@lib/config/constants";
|
||||
|
||||
@@ -46,6 +45,23 @@ const getDimension = (dimension: string) => {
|
||||
return dimension;
|
||||
};
|
||||
|
||||
const goto = (router: NextRouter, searchParams: Record<string, string>) => {
|
||||
const newQuery = new URLSearchParams(router.asPath.split("?")[1]);
|
||||
Object.keys(searchParams).forEach((key) => {
|
||||
newQuery.set(key, searchParams[key]);
|
||||
});
|
||||
router.push(`${router.asPath.split("?")[0]}?${newQuery.toString()}`, undefined, {
|
||||
shallow: true,
|
||||
});
|
||||
};
|
||||
|
||||
const removeQueryParams = (router: NextRouter, queryParams: string[]) => {
|
||||
queryParams.forEach((param) => {
|
||||
delete router.query[param];
|
||||
});
|
||||
router.push(`${router.asPath.split("?")[0]}?${router.query.toString()}`);
|
||||
};
|
||||
|
||||
/**
|
||||
* It allows us to show code with certain reusable blocks indented according to the block variable placement
|
||||
* So, if you add a variable ${abc} with indentation of 4 spaces, it will automatically indent all newlines in `abc` with the same indent before constructing the final string
|
||||
@@ -452,7 +468,7 @@ const embeds: {
|
||||
const tabs = [
|
||||
{
|
||||
name: "HTML",
|
||||
embedTabName: "embed-code",
|
||||
href: "embedTabName=embed-code",
|
||||
icon: Icon.FiCode,
|
||||
type: "code",
|
||||
Component: forwardRef<
|
||||
@@ -503,7 +519,7 @@ ${getEmbedTypeSpecificString({ embedFramework: "HTML", embedType, calLink, previ
|
||||
},
|
||||
{
|
||||
name: "React",
|
||||
embedTabName: "embed-react",
|
||||
href: "embedTabName=embed-react",
|
||||
icon: Icon.FiCode,
|
||||
type: "code",
|
||||
Component: forwardRef<
|
||||
@@ -543,8 +559,8 @@ ${getEmbedTypeSpecificString({ embedFramework: "react", embedType, calLink, prev
|
||||
},
|
||||
{
|
||||
name: "Preview",
|
||||
embedTabName: "embed-preview",
|
||||
icon: Icon.FiEye,
|
||||
href: "embedTabName=embed-preview",
|
||||
icon: Icon.FiTrello,
|
||||
type: "iframe",
|
||||
Component: forwardRef<
|
||||
HTMLIFrameElement | HTMLTextAreaElement | null,
|
||||
@@ -590,7 +606,7 @@ const ChooseEmbedTypesDialogContent = () => {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
return (
|
||||
<DialogContent size="lg">
|
||||
<DialogContent type="creation" useOwnActionButtons size="lg">
|
||||
<div className="mb-4">
|
||||
<h3 className="text-lg font-bold leading-6 text-gray-900" id="modal-title">
|
||||
{t("how_you_want_add_cal_site")}
|
||||
@@ -606,11 +622,8 @@ const ChooseEmbedTypesDialogContent = () => {
|
||||
key={index}
|
||||
data-testid={embed.type}
|
||||
onClick={() => {
|
||||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
embedType: embed.type,
|
||||
},
|
||||
goto(router, {
|
||||
embedType: embed.type,
|
||||
});
|
||||
}}>
|
||||
<div className="order-none box-border flex-none rounded-sm border border-solid bg-white">
|
||||
@@ -635,6 +648,13 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const iframeRef = useRef<HTMLIFrameElement>(null);
|
||||
const s = (href: string) => {
|
||||
const searchParams = new URLSearchParams(router.asPath.split("?")[1] || "");
|
||||
const [a, b] = href.split("=");
|
||||
searchParams.set(a, b);
|
||||
return `${router.asPath.split("?")[0]}?${searchParams.toString()}`;
|
||||
};
|
||||
const parsedTabs = tabs.map((t) => ({ ...t, href: s(t.href) }));
|
||||
const embedCodeRefs: Record<typeof tabs[0]["name"], RefObject<HTMLTextAreaElement>> = {};
|
||||
tabs
|
||||
.filter((tab) => tab.type === "code")
|
||||
@@ -661,28 +681,13 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
});
|
||||
|
||||
const close = () => {
|
||||
const noPopupQuery = {
|
||||
...router.query,
|
||||
};
|
||||
|
||||
delete noPopupQuery.dialog;
|
||||
|
||||
queryParamsForDialog.forEach((queryParam) => {
|
||||
delete noPopupQuery[queryParam];
|
||||
});
|
||||
|
||||
router.push({
|
||||
query: noPopupQuery,
|
||||
});
|
||||
removeQueryParams(router, ["dialog", ...queryParamsForDialog]);
|
||||
};
|
||||
|
||||
// Use embed-code as default tab
|
||||
if (!router.query.embedTabName) {
|
||||
router.query.embedTabName = "embed-code";
|
||||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
},
|
||||
goto(router, {
|
||||
embedTabName: "embed-code",
|
||||
});
|
||||
}
|
||||
|
||||
@@ -779,20 +784,13 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
];
|
||||
|
||||
return (
|
||||
<DialogContent size="lg">
|
||||
<DialogContent size="xl" className="p-0.5" type="creation" useOwnActionButtons>
|
||||
<div className="flex">
|
||||
<div className="flex w-1/3 flex-col bg-white p-6">
|
||||
<div className="flex w-1/3 flex-col bg-gray-50 p-8">
|
||||
<h3 className="mb-2 flex text-xl font-bold leading-6 text-gray-900" id="modal-title">
|
||||
<button
|
||||
onClick={() => {
|
||||
const newQuery = { ...router.query };
|
||||
delete newQuery.embedType;
|
||||
delete newQuery.embedTabName;
|
||||
router.push({
|
||||
query: {
|
||||
...newQuery,
|
||||
},
|
||||
});
|
||||
removeQueryParams(router, ["embedType", "embedTabName"]);
|
||||
}}>
|
||||
<Icon.FiArrowLeft className="mr-4 w-4" />
|
||||
</button>
|
||||
@@ -1040,14 +1038,14 @@ const EmbedTypeCodeAndPreviewDialogContent = ({
|
||||
</Collapsible>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex w-2/3 flex-col p-6">
|
||||
<HorizontalTabs tabNameKey="embedTabName" data-testid="embed-tabs" tabs={tabs} />
|
||||
<div className="flex w-2/3 flex-col p-8">
|
||||
<HorizontalTabs data-testid="embed-tabs" tabs={parsedTabs} linkProps={{ shallow: true }} />
|
||||
{tabs.map((tab) => {
|
||||
return (
|
||||
<div
|
||||
key={tab.embedTabName}
|
||||
key={tab.href}
|
||||
className={classNames(
|
||||
router.query.embedTabName === tab.embedTabName ? "flex flex-grow flex-col" : "hidden"
|
||||
router.query.embedTabName === tab.href.split("=")[1] ? "flex flex-grow flex-col" : "hidden"
|
||||
)}>
|
||||
<div className="flex h-[55vh] flex-grow flex-col">
|
||||
{tab.type === "code" ? (
|
||||
@@ -1133,19 +1131,10 @@ export const EmbedButton = <T extends React.ElementType>({
|
||||
const router = useRouter();
|
||||
className = classNames(className, "hidden lg:inline-flex");
|
||||
const openEmbedModal = () => {
|
||||
const query = {
|
||||
...router.query,
|
||||
goto(router, {
|
||||
dialog: "embed",
|
||||
embedUrl,
|
||||
};
|
||||
router.push(
|
||||
{
|
||||
pathname: router.pathname,
|
||||
query,
|
||||
},
|
||||
undefined,
|
||||
{ shallow: true }
|
||||
);
|
||||
});
|
||||
};
|
||||
const Component = as ?? Button;
|
||||
|
||||
|
||||
@@ -10,7 +10,9 @@ export function useViewerI18n() {
|
||||
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
|
||||
* We intend to not cache i18n query
|
||||
**/
|
||||
context: { skipBatch: true },
|
||||
trpc: {
|
||||
context: { skipBatch: true },
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -44,6 +44,11 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
const mutation = trpc.useMutation(["viewer.bookings.confirm"], {
|
||||
onSuccess: () => {
|
||||
setRejectionDialogIsOpen(false);
|
||||
showToast(t("booking_confirmation_success"), "success");
|
||||
utils.invalidateQueries("viewer.bookings");
|
||||
},
|
||||
onError: () => {
|
||||
showToast(t("booking_confirmation_failed"), "error");
|
||||
utils.invalidateQueries("viewer.bookings");
|
||||
},
|
||||
});
|
||||
@@ -117,8 +122,7 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
},
|
||||
{
|
||||
id: "edit_booking",
|
||||
label: t("edit_booking"),
|
||||
icon: Icon.FiEdit,
|
||||
label: t("edit"),
|
||||
actions: [
|
||||
{
|
||||
id: "reschedule",
|
||||
@@ -262,73 +266,89 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<tr className="flex hover:bg-neutral-50">
|
||||
<td className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:w-48" onClick={onClick}>
|
||||
<tr className="flex flex-col hover:bg-neutral-50 sm:flex-row">
|
||||
<td className="hidden align-top ltr:pl-6 rtl:pr-6 sm:table-cell sm:min-w-[10rem]" onClick={onClick}>
|
||||
<div className="cursor-pointer py-4">
|
||||
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
|
||||
<div className="text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} -{" "}
|
||||
{dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")}
|
||||
</div>
|
||||
<div className="text-sm text-gray-400">
|
||||
{booking.recurringBookings &&
|
||||
booking.eventType?.recurringEvent?.freq &&
|
||||
(booking.listingStatus === "recurring" || booking.listingStatus === "cancelled") && (
|
||||
<div className="underline decoration-gray-400 decoration-dashed underline-offset-2">
|
||||
<div className="flex">
|
||||
<Tooltip
|
||||
content={recurringStrings.map((aDate, key) => (
|
||||
<p key={key}>{aDate}</p>
|
||||
))}>
|
||||
<div className="text-gray-600 dark:text-white">
|
||||
<Icon.FiRefreshCcw
|
||||
strokeWidth="3"
|
||||
className="float-left mr-1 mt-1.5 inline-block h-3 w-3 text-gray-400"
|
||||
/>
|
||||
<p className="mt-1 pl-5 text-xs">
|
||||
{booking.status === BookingStatus.ACCEPTED
|
||||
? `${t("event_remaining", {
|
||||
count: recurringDates.length,
|
||||
})}`
|
||||
: getEveryFreqFor({
|
||||
t,
|
||||
recurringEvent: booking.eventType.recurringEvent,
|
||||
recurringCount: booking.recurringBookings.length,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className={"flex-1 px-4" + (isRejected ? " line-through" : "")} onClick={onClick}>
|
||||
<div className="cursor-pointer py-4">
|
||||
|
||||
{isPending && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
|
||||
{t("unconfirmed")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
{booking.eventType?.team && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="gray">
|
||||
{booking.eventType.team.name}
|
||||
</Badge>
|
||||
)}
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
<Badge variant="orange" className="mb-2 ltr:mr-2 rtl:ml-2">
|
||||
<Badge className="ltr:mr-2 rtl:ml-2" variant="orange">
|
||||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
|
||||
<div className="mt-2 text-sm text-gray-400">
|
||||
<RecurringBookingsTooltip
|
||||
booking={booking}
|
||||
recurringStrings={recurringStrings}
|
||||
recurringDates={recurringDates}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td className={"w-full px-4" + (isRejected ? " line-through" : "")} onClick={onClick}>
|
||||
{/* Time and Badges for mobile */}
|
||||
<div className="w-full pt-4 pb-2 sm:hidden">
|
||||
<div className="flex w-full items-center justify-between sm:hidden">
|
||||
<div className="text-sm leading-6 text-gray-900">{startTime}</div>
|
||||
<div className="pr-2 text-sm text-gray-500">
|
||||
{dayjs(booking.startTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")} -{" "}
|
||||
{dayjs(booking.endTime).format(user && user.timeFormat === 12 ? "h:mma" : "HH:mm")}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{isPending && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="orange">
|
||||
{t("unconfirmed")}
|
||||
</Badge>
|
||||
)}
|
||||
{booking.eventType?.team && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="gray">
|
||||
{booking.eventType.team.name}
|
||||
</Badge>
|
||||
)}
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
<Badge className="ltr:mr-2 rtl:ml-2 sm:hidden" variant="orange">
|
||||
{t("pending_payment")}
|
||||
</Badge>
|
||||
)}
|
||||
<div className="text-sm text-gray-400 sm:hidden">
|
||||
<RecurringBookingsTooltip
|
||||
booking={booking}
|
||||
recurringStrings={recurringStrings}
|
||||
recurringDates={recurringDates}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="cursor-pointer py-4">
|
||||
<div
|
||||
title={booking.title}
|
||||
className={classNames(
|
||||
"max-w-56 truncate text-sm font-medium leading-6 text-neutral-900 md:max-w-max",
|
||||
"max-w-10/12 sm:max-w-56 text-sm font-medium leading-6 text-neutral-900 md:max-w-full",
|
||||
isCancelled ? "line-through" : ""
|
||||
)}>
|
||||
{booking.title}
|
||||
<span> </span>
|
||||
{booking.eventType?.team && <Badge variant="gray">{booking.eventType.team.name}</Badge>}
|
||||
|
||||
{!!booking?.eventType?.price && !booking.paid && (
|
||||
<Tag className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">Pending payment</Tag>
|
||||
)}
|
||||
{isPending && (
|
||||
<Badge variant="orange" className="hidden ltr:ml-2 rtl:mr-2 sm:inline-flex">
|
||||
{t("unconfirmed")}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
{booking.description && (
|
||||
<div
|
||||
@@ -351,8 +371,7 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
)}
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td className="whitespace-nowrap py-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4">
|
||||
<td className="py-4 pl-4 text-right text-sm font-medium ltr:pr-4 rtl:pl-4 sm:pl-0">
|
||||
{isUpcoming && !isCancelled ? (
|
||||
<>
|
||||
{isPending && user?.id === booking.user?.id && <TableActions actions={pendingActions} />}
|
||||
@@ -372,6 +391,54 @@ function BookingListItem(booking: BookingItemProps) {
|
||||
);
|
||||
}
|
||||
|
||||
interface RecurringBookingsTooltipProps {
|
||||
booking: BookingItemProps;
|
||||
recurringStrings: string[];
|
||||
recurringDates: Date[];
|
||||
}
|
||||
|
||||
const RecurringBookingsTooltip = ({
|
||||
booking,
|
||||
recurringStrings,
|
||||
recurringDates,
|
||||
}: RecurringBookingsTooltipProps) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
(booking.recurringBookings &&
|
||||
booking.eventType?.recurringEvent?.freq &&
|
||||
(booking.listingStatus === "recurring" || booking.listingStatus === "cancelled") && (
|
||||
<div className="underline decoration-gray-400 decoration-dashed underline-offset-2">
|
||||
<div className="flex">
|
||||
<Tooltip
|
||||
content={recurringStrings.map((aDate, key) => (
|
||||
<p key={key}>{aDate}</p>
|
||||
))}>
|
||||
<div className="text-gray-600 dark:text-white">
|
||||
<Icon.FiRefreshCcw
|
||||
strokeWidth="3"
|
||||
className="float-left mr-1 mt-1.5 inline-block h-3 w-3 text-gray-400"
|
||||
/>
|
||||
<p className="mt-1 pl-5 text-xs">
|
||||
{booking.status === BookingStatus.ACCEPTED
|
||||
? `${t("event_remaining", {
|
||||
count: recurringDates.length,
|
||||
})}`
|
||||
: getEveryFreqFor({
|
||||
t,
|
||||
recurringEvent: booking.eventType.recurringEvent,
|
||||
recurringCount: booking.recurringBookings.length,
|
||||
})}
|
||||
</p>
|
||||
</div>
|
||||
</Tooltip>
|
||||
</div>
|
||||
</div>
|
||||
)) ||
|
||||
null
|
||||
);
|
||||
};
|
||||
|
||||
interface UserProps {
|
||||
id: number;
|
||||
name: string | null;
|
||||
|
||||
@@ -33,7 +33,7 @@ const TimeOptions: FC<Props> = ({ onToggle24hClock, onSelectTimeZone, timeFormat
|
||||
};
|
||||
|
||||
return selectedTimeZone !== "" ? (
|
||||
<div className="max-w-80 dark:border-darkgray-300 dark:bg-darkgray-200 absolute z-10 w-full rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm">
|
||||
<div className="dark:border-darkgray-300 dark:bg-darkgray-200 rounded-sm border border-gray-200 bg-white px-4 pt-4 pb-3 shadow-sm">
|
||||
<div className="mb-4 flex">
|
||||
<div className="text-sm font-medium text-gray-600 dark:text-white">{t("time_options")}</div>
|
||||
<div className="ml-auto flex items-center">
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
// Get router variables
|
||||
import { EventType } from "@prisma/client";
|
||||
import { SchedulingType } from "@prisma/client";
|
||||
import * as Collapsible from "@radix-ui/react-collapsible";
|
||||
import * as Popover from "@radix-ui/react-popover";
|
||||
import { TFunction } from "next-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { useReducer, useEffect, useMemo, useState } from "react";
|
||||
@@ -24,8 +24,7 @@ import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import notEmpty from "@calcom/lib/notEmpty";
|
||||
import { getRecurringFreq } from "@calcom/lib/recurringStrings";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
import { detectBrowserTimeFormat, getIs24hClockFromLocalStorage } from "@calcom/lib/timeFormat";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import DatePicker from "@calcom/ui/v2/modules/booker/DatePicker";
|
||||
@@ -79,7 +78,7 @@ const useSlots = ({
|
||||
usernameList: string[];
|
||||
timeZone?: string;
|
||||
}) => {
|
||||
const { data, isLoading, isIdle } = trpc.useQuery(
|
||||
const { data, isLoading, isPaused } = trpc.useQuery(
|
||||
[
|
||||
"viewer.public.slots.getSchedule",
|
||||
{
|
||||
@@ -101,8 +100,8 @@ const useSlots = ({
|
||||
}
|
||||
}, [data]);
|
||||
|
||||
// The very first time isIdle is set if auto-fetch is disabled, so isIdle should also be considered a loading state.
|
||||
return { slots: cachedSlots, isLoading: isLoading || isIdle };
|
||||
// The very first time isPaused is set if auto-fetch is disabled, so isPaused should also be considered a loading state.
|
||||
return { slots: cachedSlots, isLoading: isLoading || isPaused };
|
||||
};
|
||||
|
||||
const SlotPicker = ({
|
||||
@@ -176,7 +175,7 @@ const SlotPicker = ({
|
||||
<DatePicker
|
||||
isLoading={isLoading}
|
||||
className={classNames(
|
||||
"mt-8 w-full px-4 sm:mt-0 sm:min-w-[455px] md:px-5",
|
||||
"mt-8 w-full px-4 pb-4 sm:mt-0 sm:min-w-[455px] md:px-5",
|
||||
selectedDate
|
||||
? "sm:dark:border-darkgray-200 border-gray-200 sm:w-1/2 sm:border-r sm:p-4 sm:pr-6 md:w-1/3"
|
||||
: "sm:p-4"
|
||||
@@ -225,7 +224,7 @@ function TimezoneDropdown({
|
||||
const [isTimeOptionsOpen, setIsTimeOptionsOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
handleToggle24hClock(localStorage.getItem("timeOption.is24hClock") === "true");
|
||||
handleToggle24hClock(!!getIs24hClockFromLocalStorage());
|
||||
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
@@ -241,8 +240,8 @@ function TimezoneDropdown({
|
||||
};
|
||||
|
||||
return (
|
||||
<Collapsible.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen} className="flex">
|
||||
<Collapsible.Trigger className="min-w-32 dark:text-darkgray-600 mb-2 -ml-2 px-2 text-left text-gray-600">
|
||||
<Popover.Root open={isTimeOptionsOpen} onOpenChange={setIsTimeOptionsOpen}>
|
||||
<Popover.Trigger className="min-w-32 dark:text-darkgray-600 radix-state-open:bg-gray-200 dark:radix-state-open:bg-darkgray-200 group relative mb-2 -ml-2 inline-block rounded-md px-2 py-2 text-left text-gray-600">
|
||||
<p className="text-sm font-medium">
|
||||
<Icon.FiGlobe className="mr-[10px] ml-[2px] -mt-[2px] inline-block h-4 w-4" />
|
||||
{timeZone}
|
||||
@@ -252,15 +251,20 @@ function TimezoneDropdown({
|
||||
<Icon.FiChevronDown className="ml-1 inline-block h-4 w-4" />
|
||||
)}
|
||||
</p>
|
||||
</Collapsible.Trigger>
|
||||
<Collapsible.Content>
|
||||
<TimeOptions
|
||||
onSelectTimeZone={handleSelectTimeZone}
|
||||
onToggle24hClock={handleToggle24hClock}
|
||||
timeFormat={timeFormat}
|
||||
/>
|
||||
</Collapsible.Content>
|
||||
</Collapsible.Root>
|
||||
</Popover.Trigger>
|
||||
<Popover.Portal>
|
||||
<Popover.Content
|
||||
hideWhenDetached
|
||||
align="start"
|
||||
className="animate-fade-in-up absolute left-0 top-2 w-80 max-w-[calc(100vw_-_1.5rem)]">
|
||||
<TimeOptions
|
||||
onSelectTimeZone={handleSelectTimeZone}
|
||||
onToggle24hClock={handleToggle24hClock}
|
||||
timeFormat={timeFormat}
|
||||
/>
|
||||
</Popover.Content>
|
||||
</Popover.Portal>
|
||||
</Popover.Root>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -393,7 +397,9 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
|
||||
<div
|
||||
style={availabilityDatePickerEmbedStyles}
|
||||
className={classNames(
|
||||
isBackgroundTransparent ? "" : "dark:bg-darkgray-100 sm:dark:border-darkgray-300 bg-white",
|
||||
isBackgroundTransparent
|
||||
? ""
|
||||
: "dark:bg-darkgray-100 sm:dark:border-darkgray-300 bg-white pb-4 md:pb-0",
|
||||
"border-bookinglightest overflow-hidden rounded-md md:border",
|
||||
isEmbed ? "mx-auto" : maxWidth
|
||||
)}>
|
||||
@@ -415,7 +421,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
|
||||
<h1 className="text-bookingdark dark:text-darkgray-900 mb-4 break-words text-xl font-semibold">
|
||||
{eventType.title}
|
||||
</h1>
|
||||
<div className="flex flex-col space-y-3">
|
||||
<div className="flex flex-col items-start space-y-3">
|
||||
{eventType?.description && (
|
||||
<div className="dark:text-darkgray-600 flex py-1 text-sm font-medium text-gray-600">
|
||||
<div>
|
||||
@@ -499,7 +505,7 @@ const AvailabilityPage = ({ profile, eventType }: Props) => {
|
||||
<div className="overflow-hidden sm:flex">
|
||||
<div
|
||||
className={
|
||||
"sm:dark:border-darkgray-200 hidden overflow-hidden border-gray-200 p-5 sm:border-r md:flex md:flex-col " +
|
||||
"sm:dark:border-darkgray-200 hidden border-gray-200 p-5 sm:border-r md:flex md:flex-col " +
|
||||
(isAvailableTimesVisible ? "sm:w-1/3" : recurringEventCount ? "sm:w-2/3" : "sm:w-1/2")
|
||||
}>
|
||||
<UserAvatars
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { EventTypeCustomInputType, WorkflowActions } from "@prisma/client";
|
||||
import { SchedulingType } from "@prisma/client";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { isValidPhoneNumber } from "libphonenumber-js";
|
||||
import { useSession } from "next-auth/react";
|
||||
import Head from "next/head";
|
||||
@@ -9,7 +10,6 @@ import { useEffect, useMemo, useState, useReducer } from "react";
|
||||
import { Controller, useForm, useWatch } from "react-hook-form";
|
||||
import { FormattedNumber, IntlProvider } from "react-intl";
|
||||
import { ReactMultiEmail } from "react-multi-email";
|
||||
import { useMutation } from "react-query";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
@@ -34,11 +34,11 @@ import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { Button } from "@calcom/ui/Button";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
import PhoneInput from "@calcom/ui/form/PhoneInputLazy";
|
||||
import { EmailInput, Form } from "@calcom/ui/form/fields";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
|
||||
import { asStringOrNull } from "@lib/asStringOrNull";
|
||||
import { timeZone } from "@lib/clock";
|
||||
@@ -199,16 +199,12 @@ const BookingPage = ({
|
||||
|
||||
// There should only exists one default userData variable for primaryAttendee.
|
||||
const defaultUserValues = {
|
||||
email: booking?.attendees[0].email
|
||||
? booking.attendees[0].email
|
||||
email: rescheduleUid
|
||||
? booking?.attendees[0].email
|
||||
: router.query.email
|
||||
? (router.query.email as string)
|
||||
: "",
|
||||
name: booking?.attendees[0].name
|
||||
? booking.attendees[0].name
|
||||
: router.query.name
|
||||
? (router.query.name as string)
|
||||
: "",
|
||||
name: rescheduleUid ? booking?.attendees[0].name : router.query.name ? (router.query.name as string) : "",
|
||||
};
|
||||
|
||||
const defaultValues = () => {
|
||||
@@ -449,7 +445,6 @@ const BookingPage = ({
|
||||
<div
|
||||
className={classNames(
|
||||
"main overflow-hidden",
|
||||
isEmbed ? "" : "border border-gray-200",
|
||||
isBackgroundTransparent ? "" : "dark:border-1 dark:bg-darkgray-100 bg-white",
|
||||
"dark:border-darkgray-300 rounded-md sm:border"
|
||||
)}>
|
||||
@@ -595,9 +590,7 @@ const BookingPage = ({
|
||||
required
|
||||
className={classNames(
|
||||
inputClassName,
|
||||
bookingForm.formState.errors.email
|
||||
? "border-red-700 focus:ring-red-700"
|
||||
: " border-gray-300 dark:border-gray-900"
|
||||
bookingForm.formState.errors.email && "!focus:ring-red-700 !border-red-700"
|
||||
)}
|
||||
placeholder="you@example.com"
|
||||
type="search" // Disables annoying 1password intrusive popup (non-optimal, I know I know...)
|
||||
@@ -740,60 +733,58 @@ const BookingPage = ({
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
{!eventType.disableGuests && (
|
||||
{!eventType.disableGuests && guestToggle && (
|
||||
<div className="mb-4">
|
||||
{guestToggle && (
|
||||
<div>
|
||||
<label
|
||||
htmlFor="guests"
|
||||
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
|
||||
{t("guests")}
|
||||
</label>
|
||||
{!disableInput && (
|
||||
<Controller
|
||||
control={bookingForm.control}
|
||||
name="guests"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<ReactMultiEmail
|
||||
className="relative"
|
||||
placeholder="guest@example.com"
|
||||
emails={value}
|
||||
onChange={onChange}
|
||||
getLabel={(
|
||||
email: string,
|
||||
index: number,
|
||||
removeEmail: (index: number) => void
|
||||
) => {
|
||||
return (
|
||||
<div data-tag key={index} className="cursor-pointer">
|
||||
{email}
|
||||
{!disableInput && (
|
||||
<span data-tag-handle onClick={() => removeEmail(index)}>
|
||||
×
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{/* Custom code when guest emails should not be editable */}
|
||||
{disableInput && guestListEmails && guestListEmails.length > 0 && (
|
||||
<div data-tag className="react-multi-email">
|
||||
{/* // @TODO: user owners are appearing as guest here when should be only user input */}
|
||||
{guestListEmails.map((email, index) => {
|
||||
return (
|
||||
<div key={index} className="cursor-pointer">
|
||||
<span data-tag>{email}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div>
|
||||
<label
|
||||
htmlFor="guests"
|
||||
className="mb-1 block text-sm font-medium text-gray-700 dark:text-white">
|
||||
{t("guests")}
|
||||
</label>
|
||||
{!disableInput && (
|
||||
<Controller
|
||||
control={bookingForm.control}
|
||||
name="guests"
|
||||
render={({ field: { onChange, value } }) => (
|
||||
<ReactMultiEmail
|
||||
className="relative"
|
||||
placeholder={<span className="dark:text-darkgray-600">guest@example.com</span>}
|
||||
emails={value}
|
||||
onChange={onChange}
|
||||
getLabel={(
|
||||
email: string,
|
||||
index: number,
|
||||
removeEmail: (index: number) => void
|
||||
) => {
|
||||
return (
|
||||
<div data-tag key={index} className="cursor-pointer">
|
||||
{email}
|
||||
{!disableInput && (
|
||||
<span data-tag-handle onClick={() => removeEmail(index)}>
|
||||
×
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{/* Custom code when guest emails should not be editable */}
|
||||
{disableInput && guestListEmails && guestListEmails.length > 0 && (
|
||||
<div data-tag className="react-multi-email">
|
||||
{/* // @TODO: user owners are appearing as guest here when should be only user input */}
|
||||
{guestListEmails.map((email, index) => {
|
||||
return (
|
||||
<div key={index} className="cursor-pointer">
|
||||
<span data-tag>{email}</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
{isSmsReminderNumberNeeded && selectedLocationType !== LocationType.Phone && (
|
||||
@@ -849,27 +840,28 @@ const BookingPage = ({
|
||||
</div>
|
||||
|
||||
<div className="flex justify-end space-x-2 rtl:space-x-reverse">
|
||||
{!guestToggle && (
|
||||
{!eventType.disableGuests && !guestToggle && (
|
||||
<Button
|
||||
type="button"
|
||||
color="secondary"
|
||||
color="minimalSecondary"
|
||||
size="icon"
|
||||
tooltip={t("additional_guests")}
|
||||
StartIcon={Icon.FiUserPlus}
|
||||
onClick={() => setGuestToggle(!guestToggle)}
|
||||
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mr-auto rounded-md">
|
||||
<Icon.FiUserPlus className="mr-2" />
|
||||
{t("additional_guests")}
|
||||
</Button>
|
||||
className="mr-auto"
|
||||
/>
|
||||
)}
|
||||
<Button
|
||||
color="minimal"
|
||||
type="button"
|
||||
onClick={() => router.back()}
|
||||
// We override this for this component only for now - as we don't support darkmode everywhere in the app
|
||||
className="dark:border-none">
|
||||
className="dark:hover:bg-darkgray-200 dark:border-none dark:text-white">
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast rounded-md"
|
||||
className="dark:bg-darkmodebrand dark:text-darkmodebrandcontrast dark:hover:border-darkmodebrandcontrast mr-auto dark:border-transparent"
|
||||
data-testid={rescheduleUid ? "confirm-reschedule-button" : "confirm-book-button"}
|
||||
loading={mutation.isLoading || recurringMutation.isLoading}>
|
||||
{rescheduleUid ? t("reschedule") : t("confirm")}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { RescheduleResponse } from "pages/api/book/request-reschedule";
|
||||
import React, { useState, Dispatch, SetStateAction } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
|
||||
@@ -14,10 +14,11 @@ const CalendarItem = (props: ICalendarItem) => {
|
||||
const { title, imageSrc, type } = props;
|
||||
const { t } = useLocale();
|
||||
return (
|
||||
<div className="flex flex-row items-center p-5">
|
||||
<img src={imageSrc} alt={title} className="h-8 w-8" />
|
||||
<p className="mx-3 text-sm font-bold">{title}</p>
|
||||
|
||||
<div className="flex flex-row items-center justify-between p-5">
|
||||
<div className="flex items-center space-x-3">
|
||||
<img src={imageSrc} alt={title} className="h-8 w-8" />
|
||||
<p className="text-sm font-bold">{title}</p>
|
||||
</div>
|
||||
<InstallAppButtonWithoutPlanCheck
|
||||
type={type}
|
||||
render={(buttonProps) => (
|
||||
@@ -29,8 +30,7 @@ const CalendarItem = (props: ICalendarItem) => {
|
||||
// Save cookie key to return url step
|
||||
document.cookie = `return-to=${window.location.href};path=/;max-age=3600;SameSite=Lax`;
|
||||
buttonProps && buttonProps.onClick && buttonProps?.onClick(event);
|
||||
}}
|
||||
className="ml-auto rounded-md border border-gray-200 py-[10px] px-4 text-sm font-bold">
|
||||
}}>
|
||||
{t("connect")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMutation } from "react-query";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
|
||||
@@ -31,7 +31,8 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
|
||||
return (
|
||||
<>
|
||||
{/* Already connected calendars */}
|
||||
{firstCalendar &&
|
||||
{!queryConnectedCalendars.isLoading &&
|
||||
firstCalendar &&
|
||||
firstCalendar.integration &&
|
||||
firstCalendar.integration.title &&
|
||||
firstCalendar.integration.imageSrc && (
|
||||
@@ -75,7 +76,7 @@ const ConnectedCalendars = (props: IConnectCalendarsProps) => {
|
||||
</List>
|
||||
)}
|
||||
|
||||
{queryConnectedCalendars.isLoading && (
|
||||
{queryIntegrations.isLoading && (
|
||||
<ul className="divide-y divide-gray-200 rounded-md border border-gray-200 bg-white p-0 dark:bg-black">
|
||||
{[0, 0, 0, 0].map((_item, index) => {
|
||||
return (
|
||||
|
||||
@@ -149,7 +149,7 @@ const UserProfile = (props: IUserProfile) => {
|
||||
}}
|
||||
/>
|
||||
{errors.bio && (
|
||||
<p data-testid="required" className="text-xs italic text-red-500">
|
||||
<p data-testid="required" className="py-2 text-xs text-red-500">
|
||||
{t("required")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -85,7 +85,7 @@ const UserSettings = (props: IUserSettingsProps) => {
|
||||
className="w-full rounded-md border border-gray-300 text-sm"
|
||||
/>
|
||||
{errors.name && (
|
||||
<p data-testid="required" className="text-xs italic text-red-500">
|
||||
<p data-testid="required" className="py-2 text-xs text-red-500">
|
||||
{t("required")}
|
||||
</p>
|
||||
)}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { Fragment } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -185,8 +185,8 @@ function ConnectedCalendarsList(props: Props) {
|
||||
) : (
|
||||
<Alert
|
||||
severity="warning"
|
||||
title={t("calendar_error")}
|
||||
message={item.error?.message}
|
||||
title={t("something_went_wrong")}
|
||||
message={t("calendar_error")}
|
||||
actions={
|
||||
<DisconnectIntegration
|
||||
id={item.credentialId}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import { useState } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { ButtonBaseProps } from "@calcom/ui/Button";
|
||||
|
||||
@@ -3,9 +3,9 @@ import { useRef, useState } from "react";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Alert } from "@calcom/ui/v2/core/Alert";
|
||||
import { Dialog, DialogContent, DialogFooter } from "@calcom/ui/v2/core/Dialog";
|
||||
|
||||
interface Props {
|
||||
isOpen: boolean;
|
||||
@@ -28,15 +28,14 @@ export default function TeamCreate(props: Props) {
|
||||
},
|
||||
});
|
||||
|
||||
const createTeam = (e: React.FormEvent<HTMLFormElement>) => {
|
||||
e.preventDefault();
|
||||
const createTeam = () => {
|
||||
createTeamMutation.mutate({ name: nameRef?.current?.value });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Dialog open={props.isOpen} onOpenChange={props.onClose}>
|
||||
<DialogContent>
|
||||
<DialogContent type="creation" actionText={t("create_new_team")} actionOnClick={createTeam}>
|
||||
<div className="mb-4 sm:flex sm:items-start">
|
||||
<div className="bg-brand text-brandcontrast dark:bg-darkmodebrand dark:text-darkmodebrandcontrast mx-auto flex h-12 w-12 flex-shrink-0 items-center justify-center rounded-full bg-opacity-5 sm:mx-0 sm:h-10 sm:w-10">
|
||||
<Icon.FiUsers className="text-brandcontrast h-6 w-6" />
|
||||
@@ -50,7 +49,7 @@ export default function TeamCreate(props: Props) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<form onSubmit={createTeam}>
|
||||
<form>
|
||||
<div className="mb-4">
|
||||
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
|
||||
{t("name")}
|
||||
@@ -66,18 +65,6 @@ export default function TeamCreate(props: Props) {
|
||||
/>
|
||||
</div>
|
||||
{errorMessage && <Alert severity="error" title={errorMessage} />}
|
||||
<DialogFooter>
|
||||
<Button type="button" color="secondary" onClick={props.onClose}>
|
||||
{t("cancel")}
|
||||
</Button>
|
||||
<Button
|
||||
type="submit"
|
||||
color="primary"
|
||||
className="ltr:ml-2 rtl:mr-2"
|
||||
data-testid="create-new-team-button">
|
||||
{t("create_team")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</form>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
import TeamListItem from "./TeamListItem";
|
||||
|
||||
|
||||
@@ -4,19 +4,21 @@ import Link from "next/link";
|
||||
import classNames from "@calcom/lib/classNames";
|
||||
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import ButtonGroup from "@calcom/ui/v2/core/ButtonGroup";
|
||||
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
|
||||
import { Dialog, DialogTrigger } from "@calcom/ui/v2/core/Dialog";
|
||||
import Dropdown, {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
} from "@calcom/ui/Dropdown";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Tooltip } from "@calcom/ui/Tooltip";
|
||||
DropdownItem,
|
||||
} from "@calcom/ui/v2/core/Dropdown";
|
||||
import { Tooltip } from "@calcom/ui/v2/core/Tooltip";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
import Avatar from "@components/ui/Avatar";
|
||||
|
||||
@@ -84,7 +86,7 @@ export default function TeamListItem(props: Props) {
|
||||
!isInvitee && "group hover:bg-neutral-50"
|
||||
)}>
|
||||
{!isInvitee ? (
|
||||
<Link href={"/settings/teams/" + team.id}>
|
||||
<Link href={"/settings/teams/" + team.id + "/profile"}>
|
||||
<a className="flex-grow cursor-pointer truncate text-sm" title={`${team.name}`}>
|
||||
{teamInfo}
|
||||
</a>
|
||||
@@ -112,7 +114,6 @@ export default function TeamListItem(props: Props) {
|
||||
<DropdownMenuItem>
|
||||
<Button
|
||||
color="minimal"
|
||||
size="sm"
|
||||
className="w-full rounded-none font-medium"
|
||||
StartIcon={Icon.FiCheck}
|
||||
onClick={acceptInvite}>
|
||||
@@ -121,8 +122,7 @@ export default function TeamListItem(props: Props) {
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem>
|
||||
<Button
|
||||
color="warn"
|
||||
size="sm"
|
||||
color="destructive"
|
||||
className="w-full rounded-none font-medium"
|
||||
StartIcon={Icon.FiX}
|
||||
onClick={declineInvite}>
|
||||
@@ -137,111 +137,103 @@ export default function TeamListItem(props: Props) {
|
||||
{!isInvitee && (
|
||||
<div className="flex space-x-2 rtl:space-x-reverse">
|
||||
<TeamRole role={team.role} />
|
||||
|
||||
<Tooltip side="top" content={t("copy_link_team")}>
|
||||
<Button
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(process.env.NEXT_PUBLIC_WEBSITE_URL + "/team/" + team.slug);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}
|
||||
className="h-10 w-10 transition-none"
|
||||
size="icon"
|
||||
color="minimal"
|
||||
type="button">
|
||||
<Icon.FiLink className="h-5 w-5 group-hover:text-gray-600" />
|
||||
</Button>
|
||||
</Tooltip>
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="button" color="minimal" size="icon" StartIcon={Icon.FiMoreHorizontal} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent hidden={hideDropdown}>
|
||||
{isAdmin && (
|
||||
<ButtonGroup combined>
|
||||
<Tooltip content={t("copy_link_team")}>
|
||||
<Button
|
||||
color="secondary"
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(
|
||||
process.env.NEXT_PUBLIC_WEBSITE_URL + "/team/" + team.slug
|
||||
);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}
|
||||
size="icon"
|
||||
StartIcon={Icon.FiLink}
|
||||
combined
|
||||
/>
|
||||
</Tooltip>
|
||||
<Dropdown>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button type="button" color="secondary" size="icon" StartIcon={Icon.FiMoreHorizontal} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent hidden={hideDropdown}>
|
||||
{isAdmin && (
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem
|
||||
type="button"
|
||||
href={"/settings/teams/" + team.id + "/profile"}
|
||||
StartIcon={Icon.FiEdit2}>
|
||||
{t("edit_team") as string}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem>
|
||||
<Link href={"/settings/teams/" + team.id}>
|
||||
<a>
|
||||
<Button
|
||||
color="minimal"
|
||||
size="sm"
|
||||
className="w-full rounded-none font-medium"
|
||||
StartIcon={Icon.FiEdit2}>
|
||||
{t("edit_team")}
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
<DropdownItem
|
||||
type="button"
|
||||
target="_blank"
|
||||
href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/${team.slug}`}
|
||||
StartIcon={Icon.FiExternalLink}>
|
||||
{t("preview_team") as string}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
<DropdownMenuItem>
|
||||
<Link href={`${process.env.NEXT_PUBLIC_WEBSITE_URL}/team/${team.slug}`} passHref={true}>
|
||||
<a target="_blank">
|
||||
<Button
|
||||
color="minimal"
|
||||
size="sm"
|
||||
className="w-full rounded-none font-medium"
|
||||
StartIcon={Icon.FiExternalLink}>
|
||||
{t("preview_team")}
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
{isOwner && (
|
||||
<DropdownMenuItem>
|
||||
<Dialog open={hideDropdown} onOpenChange={setHideDropdown}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
color="warn"
|
||||
size="sm"
|
||||
className="w-full rounded-none font-medium"
|
||||
StartIcon={Icon.FiTrash}>
|
||||
{t("disband_team")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
variety="danger"
|
||||
title={t("disband_team")}
|
||||
confirmBtnText={t("confirm_disband_team")}
|
||||
isLoading={props.isLoading}
|
||||
onConfirm={() => {
|
||||
props.onActionSelect("disband");
|
||||
}}>
|
||||
{t("disband_team_confirmation_message")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{!isOwner && (
|
||||
<DropdownMenuItem>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
color="warn"
|
||||
size="lg"
|
||||
StartIcon={Icon.FiLogOut}
|
||||
className="w-full rounded-none"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
{isOwner && (
|
||||
<DropdownMenuItem>
|
||||
<Dialog open={hideDropdown} onOpenChange={setHideDropdown}>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}
|
||||
color="destructive"
|
||||
className="rounded-none px-3 font-normal"
|
||||
StartIcon={Icon.FiTrash}>
|
||||
{t("disband_team")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
variety="danger"
|
||||
title={t("disband_team")}
|
||||
confirmBtnText={t("confirm_disband_team")}
|
||||
isLoading={props.isLoading}
|
||||
onConfirm={() => {
|
||||
props.onActionSelect("disband");
|
||||
}}>
|
||||
{t("leave_team")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
variety="danger"
|
||||
title={t("leave_team")}
|
||||
confirmBtnText={t("confirm_leave_team")}
|
||||
onConfirm={declineInvite}>
|
||||
{t("leave_team_confirmation_message")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
{t("disband_team_confirmation_message")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
|
||||
{!isOwner && (
|
||||
<DropdownMenuItem>
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>
|
||||
<Button
|
||||
type="button"
|
||||
color="destructive"
|
||||
size="lg"
|
||||
StartIcon={Icon.FiLogOut}
|
||||
className="w-full rounded-none"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
}}>
|
||||
{t("leave_team")}
|
||||
</Button>
|
||||
</DialogTrigger>
|
||||
<ConfirmationDialogContent
|
||||
variety="danger"
|
||||
title={t("leave_team")}
|
||||
confirmBtnText={t("confirm_leave_team")}
|
||||
onConfirm={declineInvite}>
|
||||
{t("leave_team_confirmation_message")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
</DropdownMenuItem>
|
||||
)}
|
||||
</DropdownMenuContent>
|
||||
</Dropdown>
|
||||
</ButtonGroup>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
@@ -1,10 +1,7 @@
|
||||
import Link from "next/link";
|
||||
import { TeamPageProps } from "pages/team/[slug]";
|
||||
import React from "react";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Avatar } from "@calcom/ui/v2";
|
||||
|
||||
import { useLocale } from "@lib/hooks/useLocale";
|
||||
@@ -13,46 +10,46 @@ type TeamType = TeamPageProps["team"];
|
||||
type MembersType = TeamType["members"];
|
||||
type MemberType = MembersType[number];
|
||||
|
||||
const Team = ({ team }: TeamPageProps) => {
|
||||
const Member = ({ member, teamName }: { member: MemberType; teamName: string | null }) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const Member = ({ member }: { member: MemberType }) => {
|
||||
return (
|
||||
<Link key={member.id} href={`/${member.username}`}>
|
||||
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex h-full flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
|
||||
<Avatar
|
||||
size="md"
|
||||
alt={member.name || ""}
|
||||
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
|
||||
/>
|
||||
<section className="line-clamp-4 mt-2 w-full space-y-1">
|
||||
<p className="font-medium text-neutral-900 dark:text-white">{member.name}</p>
|
||||
<p className="line-clamp-3 overflow-ellipsis text-sm font-normal text-neutral-500 dark:text-white">
|
||||
{member.bio || t("user_from_team", { user: member.name, team: team.name })}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Link key={member.id} href={`/${member.username}`}>
|
||||
<div className="sm:min-w-80 sm:max-w-80 dark:bg-darkgray-200 dark:hover:bg-darkgray-300 group flex min-h-full w-[90%] flex-col space-y-2 rounded-md bg-white p-4 hover:cursor-pointer hover:bg-gray-50 ">
|
||||
<Avatar
|
||||
size="md"
|
||||
alt={member.name || ""}
|
||||
imageSrc={WEBAPP_URL + "/" + member.username + "/avatar.png"}
|
||||
/>
|
||||
<section className="line-clamp-4 mt-2 w-full space-y-1">
|
||||
<p className="font-medium text-neutral-900 dark:text-white">{member.name}</p>
|
||||
<p className="line-clamp-3 overflow-ellipsis text-sm font-normal text-neutral-500 dark:text-white">
|
||||
{member.bio || t("user_from_team", { user: member.name, team: teamName })}
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
</Link>
|
||||
);
|
||||
};
|
||||
|
||||
const Members = ({ members }: { members: MembersType }) => {
|
||||
if (!members || members.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="lg:min-w-lg mx-auto flex min-w-full max-w-5xl flex-wrap justify-center gap-x-6 gap-y-6">
|
||||
{members.map((member) => {
|
||||
return member.username !== null && <Member key={member.id} member={member} />;
|
||||
})}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
const Members = ({ members, teamName }: { members: MembersType; teamName: string | null }) => {
|
||||
if (!members || members.length === 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<section className="lg:min-w-lg mx-auto flex min-w-full max-w-5xl flex-wrap justify-center gap-x-6 gap-y-6">
|
||||
{members.map((member) => {
|
||||
return member.username !== null && <Member key={member.id} member={member} teamName={teamName} />;
|
||||
})}
|
||||
</section>
|
||||
);
|
||||
};
|
||||
|
||||
const Team = ({ team }: TeamPageProps) => {
|
||||
return (
|
||||
<div>
|
||||
<Members members={team.members} />
|
||||
<Members members={team.members} teamName={team.name} />
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,50 +1,48 @@
|
||||
import { useState } from "react";
|
||||
import classNames from "classnames";
|
||||
import React, { useState } from "react";
|
||||
import { ControllerRenderProps } from "react-hook-form";
|
||||
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
|
||||
const EditableHeading = ({
|
||||
title,
|
||||
const EditableHeading = function EditableHeading({
|
||||
value,
|
||||
onChange,
|
||||
placeholder = "",
|
||||
readOnly = false,
|
||||
isReady,
|
||||
...passThroughProps
|
||||
}: {
|
||||
title: string;
|
||||
onChange?: (value: string) => void;
|
||||
placeholder?: string;
|
||||
readOnly?: boolean;
|
||||
}) => {
|
||||
isReady?: boolean;
|
||||
} & Omit<JSX.IntrinsicElements["input"], "name" | "onChange"> &
|
||||
ControllerRenderProps) {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const enableEditing = () => !readOnly && setIsEditing(true);
|
||||
const enableEditing = () => setIsEditing(true);
|
||||
return (
|
||||
<div className="group relative cursor-pointer" onClick={enableEditing}>
|
||||
{!isEditing ? (
|
||||
<>
|
||||
<h1
|
||||
style={{ fontSize: 22, letterSpacing: "-0.0009em" }}
|
||||
className="inline pl-0 normal-case text-gray-900 focus:text-black group-hover:text-gray-500">
|
||||
{title}
|
||||
</h1>
|
||||
{!readOnly ? (
|
||||
<Icon.FiEdit2 className="ml-1 -mt-1 inline h-4 w-4 text-gray-700 group-hover:text-gray-500" />
|
||||
) : null}
|
||||
</>
|
||||
) : (
|
||||
<div style={{ marginBottom: -11 }}>
|
||||
<div className="flex items-center">
|
||||
<label className="min-w-8 relative inline-block">
|
||||
<span className="whitespace-pre text-xl tracking-normal text-transparent">{value} </span>
|
||||
{!isEditing && isReady && (
|
||||
<Icon.FiEdit2 className="ml-1 inline h-4 w-4 align-top text-gray-700 group-hover:text-gray-500" />
|
||||
)}
|
||||
<input
|
||||
{...passThroughProps}
|
||||
type="text"
|
||||
autoFocus
|
||||
style={{ top: -6, fontSize: 22 }}
|
||||
value={value}
|
||||
required
|
||||
className="relative h-10 w-full cursor-pointer border-none bg-transparent pl-0 text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
|
||||
placeholder={placeholder}
|
||||
defaultValue={title}
|
||||
className={classNames(
|
||||
"absolute top-0 left-0 w-full cursor-pointer border-none bg-transparent p-0 align-top text-xl text-gray-900 hover:text-gray-700 focus:text-black focus:outline-none focus:ring-0"
|
||||
)}
|
||||
onFocus={(e) => {
|
||||
setIsEditing(true);
|
||||
passThroughProps.onFocus && passThroughProps.onFocus(e);
|
||||
}}
|
||||
onBlur={(e) => {
|
||||
setIsEditing(false);
|
||||
onChange && onChange(e.target.value);
|
||||
passThroughProps.onBlur && passThroughProps.onBlur(e);
|
||||
}}
|
||||
onChange={(e) => onChange && onChange(e.target.value)}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
import React, { FC } from "react";
|
||||
|
||||
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
|
||||
import Dropdown, {
|
||||
DropdownMenuContent,
|
||||
DropdownMenuItem,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuPortal,
|
||||
} from "@calcom/ui/Dropdown";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
@@ -43,36 +48,32 @@ const DropdownActions = ({
|
||||
) : (
|
||||
<DropdownMenuTrigger asChild>{actionTrigger}</DropdownMenuTrigger>
|
||||
)}
|
||||
<DropdownMenuContent portalled>
|
||||
{actions.map((action) => (
|
||||
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
className="w-full rounded-none font-normal"
|
||||
href={action.href}
|
||||
StartIcon={action.icon}
|
||||
onClick={action.onClick || defaultAction}
|
||||
data-testid={action.id}>
|
||||
{action.label}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent>
|
||||
{actions.map((action) => (
|
||||
<DropdownMenuItem key={action.id} className="focus-visible:outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
className="w-full rounded-none font-normal"
|
||||
href={action.href}
|
||||
StartIcon={action.icon}
|
||||
onClick={action.onClick || defaultAction}
|
||||
data-testid={action.id}>
|
||||
{action.label}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
))}
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</Dropdown>
|
||||
);
|
||||
};
|
||||
|
||||
const TableActions: FC<Props> = ({ actions }) => {
|
||||
const mobileActions = actions.flatMap((action) => {
|
||||
if (action.actions) {
|
||||
return action.actions;
|
||||
}
|
||||
return action;
|
||||
});
|
||||
return (
|
||||
<>
|
||||
<div className="hidden space-x-2 rtl:space-x-reverse lg:flex">
|
||||
<div className="flex space-x-2 rtl:space-x-reverse">
|
||||
{actions.map((action) => {
|
||||
const button = (
|
||||
<Button
|
||||
@@ -93,9 +94,6 @@ const TableActions: FC<Props> = ({ actions }) => {
|
||||
return <DropdownActions key={action.id} actions={action.actions} actionTrigger={button} />;
|
||||
})}
|
||||
</div>
|
||||
<div className="inline-block text-left lg:hidden">
|
||||
<DropdownActions actions={mobileActions} />
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -5,14 +5,15 @@ import { MutableRefObject, useCallback, useEffect, useState } from "react";
|
||||
|
||||
import { getPremiumPlanMode, getPremiumPlanPriceValue } from "@calcom/app-store/stripepayment/lib/utils";
|
||||
import { fetchUsername } from "@calcom/lib/fetchUsername";
|
||||
import hasKeyInMetadata from "@calcom/lib/hasKeyInMetadata";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { User } from "@calcom/prisma/client";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Icon, StarIconSolid } from "@calcom/ui/Icon";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
import { Input, Label } from "@calcom/ui/v2";
|
||||
|
||||
export enum UsernameChangeStatusEnum {
|
||||
@@ -25,7 +26,7 @@ interface ICustomUsernameProps {
|
||||
currentUsername: string | undefined;
|
||||
setCurrentUsername: (value: string | undefined) => void;
|
||||
inputUsernameValue: string | undefined;
|
||||
usernameRef: MutableRefObject<HTMLInputElement>;
|
||||
usernameRef: MutableRefObject<HTMLInputElement | null>;
|
||||
setInputUsernameValue: (value: string) => void;
|
||||
onSuccessMutation?: () => void;
|
||||
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
|
||||
@@ -43,9 +44,8 @@ interface ICustomUsernameProps {
|
||||
| "plan"
|
||||
| "brandColor"
|
||||
| "darkBrandColor"
|
||||
| "metadata"
|
||||
| "timeFormat"
|
||||
| "allowDynamicBooking"
|
||||
| "metadata"
|
||||
>;
|
||||
readonly?: boolean;
|
||||
}
|
||||
@@ -62,24 +62,13 @@ const obtainNewUsernameChangeCondition = ({
|
||||
if (!userIsPremium && isNewUsernamePremium && !stripeCustomer?.paidForPremium) {
|
||||
return UsernameChangeStatusEnum.UPGRADE;
|
||||
}
|
||||
|
||||
if (userIsPremium && !isNewUsernamePremium && getPremiumPlanMode() === "subscription") {
|
||||
return UsernameChangeStatusEnum.DOWNGRADE;
|
||||
}
|
||||
return UsernameChangeStatusEnum.NORMAL;
|
||||
};
|
||||
|
||||
const useIsUsernamePremium = (username: string) => {
|
||||
const [isCurrentUsernamePremium, setIsCurrentUsernamePremium] = useState(false);
|
||||
useEffect(() => {
|
||||
(async () => {
|
||||
if (!username) return;
|
||||
const { data } = await fetchUsername(username);
|
||||
setIsCurrentUsernamePremium(data.premium);
|
||||
})();
|
||||
}, [username]);
|
||||
return isCurrentUsernamePremium;
|
||||
};
|
||||
|
||||
const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
const { t } = useLocale();
|
||||
const {
|
||||
@@ -91,6 +80,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
onSuccessMutation,
|
||||
onErrorMutation,
|
||||
readonly: disabled,
|
||||
user,
|
||||
} = props;
|
||||
const [usernameIsAvailable, setUsernameIsAvailable] = useState(false);
|
||||
const [markAsError, setMarkAsError] = useState(false);
|
||||
@@ -98,13 +88,13 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
const { paymentStatus: recentAttemptPaymentStatus } = router.query;
|
||||
const [openDialogSaveUsername, setOpenDialogSaveUsername] = useState(false);
|
||||
const { data: stripeCustomer } = trpc.useQuery(["viewer.stripeCustomer"]);
|
||||
const isCurrentUsernamePremium = useIsUsernamePremium(currentUsername || "");
|
||||
const isCurrentUsernamePremium =
|
||||
user && user.metadata && hasKeyInMetadata(user, "isPremium") ? !!user.metadata.isPremium : false;
|
||||
const [isInputUsernamePremium, setIsInputUsernamePremium] = useState(false);
|
||||
|
||||
const debouncedApiCall = useCallback(
|
||||
debounce(async (username) => {
|
||||
debounce(async (username: string) => {
|
||||
const { data } = await fetchUsername(username);
|
||||
setMarkAsError(!data.available && username !== currentUsername);
|
||||
setMarkAsError(!data.available && !!currentUsername && username !== currentUsername);
|
||||
setIsInputUsernamePremium(data.premium);
|
||||
setUsernameIsAvailable(data.available);
|
||||
}, 150),
|
||||
@@ -183,7 +173,9 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
onClick={() => {
|
||||
if (currentUsername) {
|
||||
setInputUsernameValue(currentUsername);
|
||||
usernameRef.current.value = currentUsername;
|
||||
if (usernameRef.current) {
|
||||
usernameRef.current.value = currentUsername;
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{t("cancel")}
|
||||
@@ -202,6 +194,20 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
}
|
||||
};
|
||||
|
||||
let paymentMsg = !currentUsername ? (
|
||||
<span className="text-xs text-orange-400">
|
||||
You need to reserve your premium username for {getPremiumPlanPriceValue()}
|
||||
</span>
|
||||
) : null;
|
||||
|
||||
if (recentAttemptPaymentStatus && recentAttemptPaymentStatus !== "paid") {
|
||||
paymentMsg = (
|
||||
<span className="text-sm text-red-500">
|
||||
Your payment could not be completed. Your username is still not reserved
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<div className="flex justify-items-center">
|
||||
@@ -211,7 +217,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
<span
|
||||
className={classNames(
|
||||
isInputUsernamePremium ? "border-1 border-orange-400 " : "",
|
||||
"hidden items-center rounded-l-md border border-r-0 border-gray-300 border-r-gray-300 bg-gray-50 px-3 text-sm text-gray-500 md:inline-flex"
|
||||
"hidden h-9 items-center rounded-l-md border border-r-0 border-gray-300 border-r-gray-300 bg-gray-50 px-3 text-sm text-gray-500 md:inline-flex"
|
||||
)}>
|
||||
{process.env.NEXT_PUBLIC_WEBSITE_URL.replace("https://", "").replace("http://", "")}/
|
||||
</span>
|
||||
@@ -237,6 +243,8 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
value={inputUsernameValue}
|
||||
onChange={(event) => {
|
||||
event.preventDefault();
|
||||
// Reset payment status
|
||||
delete router.query.paymentStatus;
|
||||
setInputUsernameValue(event.target.value);
|
||||
}}
|
||||
data-testid="username-input"
|
||||
@@ -248,7 +256,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
isInputUsernamePremium ? "text-orange-400" : "",
|
||||
usernameIsAvailable ? "" : ""
|
||||
)}>
|
||||
{isInputUsernamePremium ? <StarIconSolid className="mt-[4px] w-6" /> : <></>}
|
||||
{isInputUsernamePremium ? <StarIconSolid className="mt-[2px] w-6" /> : <></>}
|
||||
{!isInputUsernamePremium && usernameIsAvailable ? <Icon.FiCheck className="mt-2 w-6" /> : <></>}
|
||||
</span>
|
||||
</div>
|
||||
@@ -260,17 +268,7 @@ const PremiumTextfield = (props: ICustomUsernameProps) => {
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{paymentRequired ? (
|
||||
recentAttemptPaymentStatus && recentAttemptPaymentStatus !== "paid" ? (
|
||||
<span className="text-sm text-red-500">
|
||||
Your payment could not be completed. Your username is still not reserved
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-xs text-orange-400">
|
||||
You need to reserve your premium username for {getPremiumPlanPriceValue()}
|
||||
</span>
|
||||
)
|
||||
) : null}
|
||||
{paymentMsg}
|
||||
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
|
||||
|
||||
{usernameIsAvailable && (
|
||||
|
||||
@@ -7,16 +7,15 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Dialog, DialogClose, DialogContent, DialogHeader } from "@calcom/ui/Dialog";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Input, Label } from "@calcom/ui/v2";
|
||||
import { Input, Label, Button } from "@calcom/ui/v2";
|
||||
|
||||
interface ICustomUsernameProps {
|
||||
currentUsername: string | undefined;
|
||||
setCurrentUsername: (value: string | undefined) => void;
|
||||
inputUsernameValue: string | undefined;
|
||||
usernameRef: MutableRefObject<HTMLInputElement>;
|
||||
usernameRef: MutableRefObject<HTMLInputElement | null>;
|
||||
setInputUsernameValue: (value: string) => void;
|
||||
onSuccessMutation?: () => void;
|
||||
onErrorMutation?: (error: TRPCClientErrorLike<AppRouter>) => void;
|
||||
@@ -73,15 +72,14 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
||||
},
|
||||
});
|
||||
|
||||
const ActionButtons = (props: { index: string }) => {
|
||||
const { index } = props;
|
||||
const ActionButtons = () => {
|
||||
return usernameIsAvailable && currentUsername !== inputUsernameValue ? (
|
||||
<div className="flex flex-row">
|
||||
<Button
|
||||
type="button"
|
||||
className="mx-2"
|
||||
onClick={() => setOpenDialogSaveUsername(true)}
|
||||
data-testid={`update-username-btn-${index}`}>
|
||||
data-testid="update-username-btn">
|
||||
{t("update")}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -91,7 +89,9 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
||||
onClick={() => {
|
||||
if (currentUsername) {
|
||||
setInputUsernameValue(currentUsername);
|
||||
usernameRef.current.value = currentUsername;
|
||||
if (usernameRef.current) {
|
||||
usernameRef.current.value = currentUsername;
|
||||
}
|
||||
}
|
||||
}}>
|
||||
{t("cancel")}
|
||||
@@ -122,7 +122,7 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
||||
autoCapitalize="none"
|
||||
autoCorrect="none"
|
||||
className={classNames(
|
||||
"mb-0 mt-0 rounded-md rounded-l-none",
|
||||
"mb-0 mt-0 h-6 rounded-md rounded-l-none",
|
||||
markAsError
|
||||
? "focus:shadow-0 focus:ring-shadow-0 border-red-500 focus:border-red-500 focus:outline-none focus:ring-0"
|
||||
: ""
|
||||
@@ -137,20 +137,20 @@ const UsernameTextfield = (props: ICustomUsernameProps) => {
|
||||
{currentUsername !== inputUsernameValue && (
|
||||
<div className="absolute right-[2px] top-0 flex flex-row">
|
||||
<span className={classNames("mx-2 py-2")}>
|
||||
{usernameIsAvailable ? <Icon.FiCheck className="mt-[4px] w-6" /> : <></>}
|
||||
{usernameIsAvailable ? <Icon.FiCheck className="mt-[2px] w-6" /> : <></>}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="hidden md:inline">
|
||||
<ActionButtons index="desktop" />
|
||||
<ActionButtons />
|
||||
</div>
|
||||
</div>
|
||||
{markAsError && <p className="mt-1 text-xs text-red-500">Username is already taken</p>}
|
||||
|
||||
{usernameIsAvailable && currentUsername !== inputUsernameValue && (
|
||||
<div className="mt-2 flex justify-end sm:hidden">
|
||||
<ActionButtons index="mobile" />
|
||||
<div className="mt-2 flex justify-end md:hidden">
|
||||
<ActionButtons />
|
||||
</div>
|
||||
)}
|
||||
<Dialog open={openDialogSaveUsername}>
|
||||
|
||||
+206
-219
@@ -1,6 +1,6 @@
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect, useState } from "react";
|
||||
import React, { useState } from "react";
|
||||
|
||||
import useAddAppMutation from "@calcom/app-store/_utils/useAddAppMutation";
|
||||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
@@ -9,9 +9,8 @@ import classNames from "@calcom/lib/classNames";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { App as AppType } from "@calcom/types/App";
|
||||
import Badge from "@calcom/ui/Badge";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { showToast } from "@calcom/ui/v2";
|
||||
import { showToast, SkeletonText } from "@calcom/ui/v2";
|
||||
import { Button, SkeletonButton, Shell } from "@calcom/ui/v2";
|
||||
import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/DisconnectIntegration";
|
||||
|
||||
@@ -37,7 +36,6 @@ const Component = ({
|
||||
images,
|
||||
}: Parameters<typeof App>[0]) => {
|
||||
const { t } = useLocale();
|
||||
const { data: user } = trpc.useQuery(["viewer.me"]);
|
||||
const hasImages = images && images.length > 0;
|
||||
const router = useRouter();
|
||||
|
||||
@@ -66,226 +64,213 @@ const Component = ({
|
||||
const allowedMultipleInstalls = categories.indexOf("calendar") > -1;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="px-4 pb-3 pt-3 md:px-8 lg:px-0 lg:pt-0">
|
||||
<Link href="/apps">
|
||||
<a className="text-md hover:text-brand-400 inline-flex rounded-sm py-2 font-semibold text-gray-900">
|
||||
<Icon.FiArrowLeft className="mr-2 h-6 w-6" /> {t("app_store")}
|
||||
</a>
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
|
||||
{hasImages && (
|
||||
<div className="flex-2 mb-4 -ml-4 -mr-4 flex w-auto snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:rounded-md">
|
||||
{images &&
|
||||
images.map((img) => (
|
||||
<img
|
||||
key={img}
|
||||
src={img}
|
||||
alt={`Screenshot of app ${name}`}
|
||||
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={classNames(
|
||||
"sticky top-0 -mt-4 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
|
||||
hasImages && "lg:ml-8"
|
||||
)}>
|
||||
<div className="mb-8 flex pt-4">
|
||||
<header>
|
||||
<div className="mb-4 flex items-center">
|
||||
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} />
|
||||
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1>
|
||||
</div>
|
||||
<h2 className="text-sm font-medium text-gray-600">
|
||||
<Link href={`categories/${categories[0]}`}>
|
||||
<a className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800">
|
||||
{categories[0]}
|
||||
</a>
|
||||
</Link>{" "}
|
||||
• {t("published_by", { author })}
|
||||
</h2>
|
||||
</header>
|
||||
</div>
|
||||
{!appCredentials.isLoading ? (
|
||||
isGlobal ||
|
||||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
|
||||
<div className="flex space-x-3">
|
||||
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
|
||||
{existingCredentials.length > 0
|
||||
? t("active_install", { count: existingCredentials.length })
|
||||
: t("default")}
|
||||
</Button>
|
||||
{!isGlobal && (
|
||||
<InstallAppButton
|
||||
type={type}
|
||||
isProOnly={isProOnly}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
if (useDefaultComponent) {
|
||||
props = {
|
||||
onClick: () => {
|
||||
mutation.mutate({ type, variant, slug });
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
};
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
StartIcon={Icon.FiPlus}
|
||||
{...props}
|
||||
// @TODO: Overriding color and size prevent us from
|
||||
// having to duplicate InstallAppButton for now.
|
||||
color="primary"
|
||||
size="base"
|
||||
data-testid="install-app-button">
|
||||
{t("install_another")}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : existingCredentials.length > 0 ? (
|
||||
<DisconnectIntegration
|
||||
label={t("disconnect")}
|
||||
credentialId={existingCredentials[0]}
|
||||
onSuccess={() => {
|
||||
router.replace("/apps/installed");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InstallAppButton
|
||||
type={type}
|
||||
isProOnly={isProOnly}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
if (useDefaultComponent) {
|
||||
props = {
|
||||
onClick: () => {
|
||||
mutation.mutate({ type, variant, slug });
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
};
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
data-testid="install-app-button"
|
||||
{...props}
|
||||
// @TODO: Overriding color and size prevent us from
|
||||
// having to duplicate InstallAppButton for now.
|
||||
color="primary"
|
||||
size="base">
|
||||
{t("install_app")}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
<div className="relative flex-1 flex-col items-start justify-start px-4 md:flex md:px-8 lg:flex-row lg:px-0">
|
||||
{hasImages && (
|
||||
<div className="flex-2 mb-4 -ml-4 -mr-4 flex min-h-[450px] w-auto snap-x snap-mandatory flex-row overflow-auto whitespace-nowrap bg-gray-100 p-4 md:mb-8 md:-ml-8 md:-mr-8 md:p-8 lg:mx-0 lg:mb-0 lg:max-w-2xl lg:flex-col lg:rounded-md">
|
||||
{images ? (
|
||||
images.map((img) => (
|
||||
<img
|
||||
key={img}
|
||||
src={img}
|
||||
alt={`Screenshot of app ${name}`}
|
||||
className="mr-4 h-auto max-h-80 max-w-[90%] snap-center rounded-md object-contain last:mb-0 md:max-h-min lg:mb-4 lg:mr-0 lg:max-w-full"
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<SkeletonButton className="h-10 w-24" />
|
||||
<SkeletonText />
|
||||
)}
|
||||
{price !== 0 && (
|
||||
<span className="block text-right">
|
||||
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
|
||||
{feeType === "monthly" && "/" + t("month")}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="prose prose-sm mt-8 space-x-2">{body}</div>
|
||||
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4>
|
||||
<span>
|
||||
{price === 0 ? (
|
||||
"Free"
|
||||
) : (
|
||||
<>
|
||||
{Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
useGrouping: false,
|
||||
}).format(price)}
|
||||
{feeType === "monthly" && "/" + t("month")}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
|
||||
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4>
|
||||
<ul className="prose-sm -ml-1 -mr-1 leading-5">
|
||||
{docs && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-sm font-normal text-black no-underline hover:underline"
|
||||
href={docs}>
|
||||
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" />
|
||||
{t("documentation")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{website && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={website}>
|
||||
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
{website.replace("https://", "")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{email && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={"mailto:" + email}>
|
||||
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
|
||||
{email}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{tos && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={tos}>
|
||||
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
{t("terms_of_service")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{privacy && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={privacy}>
|
||||
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
{t("privacy_policy")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<hr className="my-8" />
|
||||
<span className="leading-1 block text-xs text-gray-500">
|
||||
Every app published on the Cal.com App Store is open source and thoroughly tested via peer
|
||||
reviews. Nevertheless, Cal.com, Inc. does not endorse or certify these apps unless they are
|
||||
published by Cal.com. If you encounter inappropriate content or behaviour please report it.
|
||||
</span>
|
||||
<a className="mt-2 block text-xs text-red-500" href="mailto:help@cal.com">
|
||||
<Icon.FiFlag className="inline h-3 w-3" /> Report App
|
||||
</a>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className={classNames(
|
||||
"sticky top-0 -mt-4 max-w-xl flex-1 pb-12 text-sm lg:pb-0",
|
||||
hasImages && "lg:ml-8"
|
||||
)}>
|
||||
<div className="mb-8 flex pt-4">
|
||||
<header>
|
||||
<div className="mb-4 flex items-center">
|
||||
<img className="min-h-16 min-w-16 h-16 w-16" src={logo} alt={name} />
|
||||
<h1 className="font-cal ml-4 text-3xl text-gray-900">{name}</h1>
|
||||
</div>
|
||||
<h2 className="text-sm font-medium text-gray-600">
|
||||
<Link href={`categories/${categories[0]}`}>
|
||||
<a className="rounded-md bg-gray-100 p-1 text-xs capitalize text-gray-800">{categories[0]}</a>
|
||||
</Link>{" "}
|
||||
• {t("published_by", { author })}
|
||||
</h2>
|
||||
</header>
|
||||
</div>
|
||||
{!appCredentials.isLoading ? (
|
||||
isGlobal ||
|
||||
(existingCredentials.length > 0 && allowedMultipleInstalls ? (
|
||||
<div className="flex space-x-3">
|
||||
<Button StartIcon={Icon.FiCheck} color="secondary" disabled>
|
||||
{existingCredentials.length > 0
|
||||
? t("active_install", { count: existingCredentials.length })
|
||||
: t("default")}
|
||||
</Button>
|
||||
{!isGlobal && (
|
||||
<InstallAppButton
|
||||
type={type}
|
||||
isProOnly={isProOnly}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
if (useDefaultComponent) {
|
||||
props = {
|
||||
onClick: () => {
|
||||
mutation.mutate({ type, variant, slug });
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
};
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
StartIcon={Icon.FiPlus}
|
||||
{...props}
|
||||
// @TODO: Overriding color and size prevent us from
|
||||
// having to duplicate InstallAppButton for now.
|
||||
color="primary"
|
||||
size="base"
|
||||
data-testid="install-app-button">
|
||||
{t("install_another")}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
) : existingCredentials.length > 0 ? (
|
||||
<DisconnectIntegration
|
||||
label={t("disconnect")}
|
||||
credentialId={existingCredentials[0]}
|
||||
onSuccess={() => {
|
||||
router.replace("/apps/installed");
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<InstallAppButton
|
||||
type={type}
|
||||
isProOnly={isProOnly}
|
||||
render={({ useDefaultComponent, ...props }) => {
|
||||
if (useDefaultComponent) {
|
||||
props = {
|
||||
onClick: () => {
|
||||
mutation.mutate({ type, variant, slug });
|
||||
},
|
||||
loading: mutation.isLoading,
|
||||
};
|
||||
}
|
||||
return (
|
||||
<Button
|
||||
data-testid="install-app-button"
|
||||
{...props}
|
||||
// @TODO: Overriding color and size prevent us from
|
||||
// having to duplicate InstallAppButton for now.
|
||||
color="primary"
|
||||
size="base">
|
||||
{t("install_app")}
|
||||
</Button>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
))
|
||||
) : (
|
||||
<SkeletonButton className="h-10 w-24" />
|
||||
)}
|
||||
{price !== 0 && (
|
||||
<span className="block text-right">
|
||||
{feeType === "usage-based" ? commission + "% + " + priceInDollar + "/booking" : priceInDollar}
|
||||
{feeType === "monthly" && "/" + t("month")}
|
||||
</span>
|
||||
)}
|
||||
|
||||
<div className="prose prose-sm mt-8">{body}</div>
|
||||
<h4 className="mt-8 font-semibold text-gray-900 ">{t("pricing")}</h4>
|
||||
<span>
|
||||
{price === 0 ? (
|
||||
"Free"
|
||||
) : (
|
||||
<>
|
||||
{Intl.NumberFormat("en-US", {
|
||||
style: "currency",
|
||||
currency: "USD",
|
||||
useGrouping: false,
|
||||
}).format(price)}
|
||||
{feeType === "monthly" && "/" + t("month")}
|
||||
</>
|
||||
)}
|
||||
</span>
|
||||
|
||||
<h4 className="mt-8 mb-2 font-semibold text-gray-900 ">{t("learn_more")}</h4>
|
||||
<ul className="prose-sm -ml-1 -mr-1 leading-5">
|
||||
{docs && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="text-sm font-normal text-black no-underline hover:underline"
|
||||
href={docs}>
|
||||
<Icon.FiBookOpen className="mr-1 -mt-1 inline h-4 w-4 text-gray-500" />
|
||||
{t("documentation")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{website && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={website}>
|
||||
<Icon.FiExternalLink className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
{website.replace("https://", "")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{email && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={"mailto:" + email}>
|
||||
<Icon.FiMail className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
|
||||
{email}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{tos && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={tos}>
|
||||
<Icon.FiFile className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
{t("terms_of_service")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{privacy && (
|
||||
<li>
|
||||
<a
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="font-normal text-black no-underline hover:underline"
|
||||
href={privacy}>
|
||||
<Icon.FiShield className="mr-1 -mt-px inline h-4 w-4 text-gray-500" />
|
||||
{t("privacy_policy")}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
<hr className="my-8" />
|
||||
<span className="leading-1 block text-xs text-gray-500">{t("every_app_published")}</span>
|
||||
<a className="mt-2 block text-xs text-red-500" href="mailto:help@cal.com">
|
||||
<Icon.FiFlag className="inline h-3 w-3" /> {t("report_app")}
|
||||
</a>
|
||||
</div>
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -312,8 +297,10 @@ export default function App(props: {
|
||||
isProOnly: AppType["isProOnly"];
|
||||
images?: string[];
|
||||
}) {
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
<Shell large isPublic>
|
||||
<Shell large isPublic heading={t("app_store")} backPath="/apps">
|
||||
{props.licenseRequired ? (
|
||||
<LicenseRequired>
|
||||
<Component {...props} />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
import Link from "next/link";
|
||||
import { Fragment } from "react";
|
||||
import { useMutation } from "react-query";
|
||||
|
||||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
@@ -153,7 +154,7 @@ function ConnectedCalendarsList(props: Props) {
|
||||
return null;
|
||||
}
|
||||
return (
|
||||
<List className="flex flex-col gap-6">
|
||||
<List className="flex flex-col gap-6" noBorderTreatment>
|
||||
{data.connectedCalendars.map((item) => (
|
||||
<Fragment key={item.credentialId}>
|
||||
{item.calendars ? (
|
||||
@@ -163,7 +164,7 @@ function ConnectedCalendarsList(props: Props) {
|
||||
logo={item.integration.logo}
|
||||
description={item.primary?.externalId || "No external Id"}
|
||||
actions={
|
||||
<div className="w-32">
|
||||
<div className="flex w-32 justify-end">
|
||||
<DisconnectIntegration
|
||||
credentialId={item.credentialId}
|
||||
trashIcon
|
||||
@@ -193,10 +194,16 @@ function ConnectedCalendarsList(props: Props) {
|
||||
) : (
|
||||
<Alert
|
||||
severity="warning"
|
||||
title={t("calendar_error")}
|
||||
message={item.error?.message}
|
||||
title={t("something_went_wrong")}
|
||||
message={
|
||||
<span>
|
||||
<Link href={"/apps/" + item.integration.slug}>{item.integration.name}</Link>:{" "}
|
||||
{t("calendar_error")}
|
||||
</span>
|
||||
}
|
||||
iconClassName="h-10 w-10 ml-2 mr-1 mt-0.5"
|
||||
actions={
|
||||
<div className="w-32">
|
||||
<div className="flex w-32 justify-end md:pr-1">
|
||||
<DisconnectIntegration
|
||||
credentialId={item.credentialId}
|
||||
trashIcon
|
||||
@@ -251,7 +258,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
|
||||
<ShellSubHeading
|
||||
title={t("calendar")}
|
||||
subtitle={t("installed_app_calendar_description")}
|
||||
className="flex items-center"
|
||||
className="mb-0 flex flex-wrap items-center gap-4 md:mb-3 md:gap-0"
|
||||
actions={
|
||||
<div className="flex flex-col xl:flex-row xl:space-x-5">
|
||||
{!!data.connectedCalendars.length && (
|
||||
@@ -263,7 +270,7 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
|
||||
}
|
||||
/>
|
||||
<div className="flex justify-between rounded-md border border-gray-200 bg-gray-50 p-4">
|
||||
<div className="flex w-full items-center gap-4">
|
||||
<div className="flex w-full flex-col items-start gap-4 md:flex-row md:items-center">
|
||||
<div className="relative rounded-md border border-gray-200 bg-white p-1.5">
|
||||
<Icon.FiCalendar className="h-8 w-8" strokeWidth="1" />
|
||||
<Icon.FiPlus
|
||||
@@ -271,11 +278,11 @@ export function CalendarListContainer(props: { heading?: boolean; fromOnboarding
|
||||
strokeWidth="4"
|
||||
/>
|
||||
</div>
|
||||
<div className="w-6/12">
|
||||
<div className="md:w-6/12">
|
||||
<h1 className="text-sm font-semibold">{t("create_events_on")}</h1>
|
||||
<p className="text-sm font-normal">{t("set_calendar")}</p>
|
||||
</div>
|
||||
<div className="flex w-6/12 justify-end">
|
||||
<div className="flex justify-end md:w-6/12">
|
||||
<DestinationCalendarSelector
|
||||
onChange={mutation.mutate}
|
||||
hidePlaceholder
|
||||
|
||||
@@ -51,7 +51,7 @@ const DestinationCalendarSelector = ({
|
||||
if (selected) {
|
||||
setSelectedOption({
|
||||
value: `${selected.integration}:${selected.externalId}`,
|
||||
label: `<span class="font-semibold">${selected.name}</span> (${selected.externalId})` || "",
|
||||
label: `${selected.name} (${selected.externalId})` || "",
|
||||
});
|
||||
}
|
||||
}, [query.data?.connectedCalendars, value]);
|
||||
@@ -66,7 +66,7 @@ const DestinationCalendarSelector = ({
|
||||
options: (selectedCalendar.calendars ?? [])
|
||||
.filter((cal) => cal.readOnly === false)
|
||||
.map((cal) => ({
|
||||
label: `<span class="font-semibold">${cal.name}</span> (${cal.externalId})` || "",
|
||||
label: `${cal.name} (${cal.externalId})` || "",
|
||||
value: `${cal.integration}:${cal.externalId}`,
|
||||
})),
|
||||
})) ?? [];
|
||||
@@ -83,9 +83,6 @@ const DestinationCalendarSelector = ({
|
||||
title={`${t("select_destination_calendar")}: ${selectedOption?.label || ""}`}>
|
||||
<Select
|
||||
name="primarySelectedCalendar"
|
||||
formatOptionLabel={function (data) {
|
||||
return <span dangerouslySetInnerHTML={{ __html: data.label }} />;
|
||||
}}
|
||||
placeholder={
|
||||
!hidePlaceholder ? (
|
||||
`${t("select_destination_calendar")}`
|
||||
|
||||
@@ -32,7 +32,7 @@ function IntegrationListItem(props: {
|
||||
<ListItem
|
||||
expanded={!!props.children}
|
||||
className={classNames(
|
||||
"my-0 flex-col rounded-none border-0 transition-colors duration-500",
|
||||
"my-0 flex-col border transition-colors duration-500 first:rounded-t-md last:rounded-b-md",
|
||||
highlight ? "bg-yellow-100" : ""
|
||||
)}>
|
||||
<div className={classNames("flex w-full flex-1 items-center space-x-2 p-4 rtl:space-x-reverse")}>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import classNames from "classnames";
|
||||
import React from "react";
|
||||
|
||||
import { SkeletonText } from "@calcom/ui/v2";
|
||||
@@ -32,9 +33,13 @@ function SkeletonItem() {
|
||||
);
|
||||
}
|
||||
|
||||
export const AvailabilitySelectSkeletonLoader = () => {
|
||||
export const SelectSkeletonLoader = ({ className }: { className?: string }) => {
|
||||
return (
|
||||
<li className="group flex w-full items-center justify-between rounded-sm border border-gray-200 px-[10px] py-3">
|
||||
<li
|
||||
className={classNames(
|
||||
"group flex w-full items-center justify-between rounded-sm border border-gray-200 px-[10px] py-3",
|
||||
className
|
||||
)}>
|
||||
<div className="flex-grow truncate text-sm">
|
||||
<div className="flex justify-between">
|
||||
<SkeletonText className="h-4 w-32" />
|
||||
|
||||
@@ -11,7 +11,7 @@ import Button from "@calcom/ui/v2/core/Button";
|
||||
import Select from "@calcom/ui/v2/core/form/Select";
|
||||
import { SkeletonText } from "@calcom/ui/v2/core/skeleton";
|
||||
|
||||
import { AvailabilitySelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader";
|
||||
import { SelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader";
|
||||
|
||||
type AvailabilityOption = {
|
||||
label: string;
|
||||
@@ -30,7 +30,7 @@ const AvailabilitySelect = ({
|
||||
}) => {
|
||||
const { data, isLoading } = trpc.useQuery(["viewer.availability.list"]);
|
||||
if (isLoading) {
|
||||
return <AvailabilitySelectSkeletonLoader />;
|
||||
return <SelectSkeletonLoader />;
|
||||
}
|
||||
|
||||
const schedules = data?.schedules || [];
|
||||
|
||||
@@ -19,6 +19,7 @@ import {
|
||||
DialogContent,
|
||||
Label,
|
||||
showToast,
|
||||
Skeleton,
|
||||
Switch,
|
||||
TextField,
|
||||
Tooltip,
|
||||
@@ -37,10 +38,10 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
const connectedCalendarsQuery = trpc.useQuery(["viewer.connectedCalendars"]);
|
||||
const formMethods = useFormContext<FormValues>();
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const [showEventNameTip, setShowEventNameTip] = useState(false);
|
||||
const [hashedLinkVisible, setHashedLinkVisible] = useState(!!eventType.hashedLink);
|
||||
const [hashedUrl, setHashedUrl] = useState(eventType.hashedLink?.link);
|
||||
const [seatsInputVisible, setSeatsInputVisible] = useState(!!eventType.seatsPerTimeSlot);
|
||||
const [customInputs, setCustomInputs] = useState<EventTypeCustomInput[]>(
|
||||
eventType.customInputs.sort((a, b) => a.id - b.id) || []
|
||||
);
|
||||
@@ -49,7 +50,7 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
const placeholderHashedLink = `${CAL_URL}/d/${hashedUrl}/${eventType.slug}`;
|
||||
|
||||
const animationRef = useRef(null);
|
||||
const seatsEnabled = !!eventType.seatsPerTimeSlot;
|
||||
const seatsEnabled = formMethods.getValues("seatsPerTimeSlotEnabled");
|
||||
|
||||
useEffect(() => {
|
||||
animationRef.current && autoAnimate(animationRef.current);
|
||||
@@ -136,8 +137,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">{t("additional_inputs")}</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("additional_input_description")}</p>
|
||||
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
|
||||
{t("additional_inputs")}
|
||||
</Skeleton>
|
||||
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
|
||||
{t("additional_input_description")}
|
||||
</Skeleton>
|
||||
</div>
|
||||
</div>
|
||||
<ul className="my-4" ref={animationRef}>
|
||||
@@ -182,12 +187,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
fitToHeight={true}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">
|
||||
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
|
||||
{t("requires_confirmation")}
|
||||
</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">
|
||||
</Skeleton>
|
||||
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
|
||||
{t("requires_confirmation_description")}
|
||||
</p>
|
||||
</Skeleton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -207,8 +212,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
disabled={seatsEnabled}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">{t("disable_guests")}</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("disable_guests_description")}</p>
|
||||
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
|
||||
{t("disable_guests")}
|
||||
</Skeleton>
|
||||
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
|
||||
{t("disable_guests_description")}
|
||||
</Skeleton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -228,8 +237,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
onCheckedChange={(e) => onChange(e)}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">{t("disable_notes")}</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("disable_notes_description")}</p>
|
||||
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
|
||||
{t("disable_notes")}
|
||||
</Skeleton>
|
||||
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
|
||||
{t("disable_notes_description")}
|
||||
</Skeleton>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
@@ -253,8 +266,12 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
}}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">{t("private_link")}</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("private_link_description")}</p>
|
||||
<Skeleton as={Label} className="text-sm font-semibold leading-none text-black">
|
||||
{t("private_link")}
|
||||
</Skeleton>
|
||||
<Skeleton as="p" className="-mt-2 text-sm leading-normal text-gray-600">
|
||||
{t("private_link_description")}
|
||||
</Skeleton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -293,6 +310,59 @@ export const EventAdvancedTab = ({ eventType, team }: Pick<EventTypeSetupInfered
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<hr />
|
||||
<Controller
|
||||
name="seatsPerTimeSlotEnabled"
|
||||
control={formMethods.control}
|
||||
defaultValue={!!eventType.seatsPerTimeSlot}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<div className="flex space-x-3">
|
||||
<Switch
|
||||
name="seatsPerTimeSlotEnabled"
|
||||
checked={value}
|
||||
onCheckedChange={(e) => {
|
||||
setSeatsInputVisible(e);
|
||||
// Enabling seats will disable guests and requiring confimation until fully supported
|
||||
if (e) {
|
||||
formMethods.setValue("disableGuests", true);
|
||||
formMethods.setValue("requiresConfirmation", false);
|
||||
formMethods.setValue("seatsPerTimeSlot", 2);
|
||||
} else {
|
||||
formMethods.setValue("seatsPerTimeSlot", null);
|
||||
}
|
||||
onChange(e);
|
||||
}}
|
||||
fitToHeight={true}
|
||||
/>
|
||||
<div className="flex flex-col">
|
||||
<Label className="text-sm font-semibold leading-none text-black">{t("offer_seats")}</Label>
|
||||
<p className="-mt-2 text-sm leading-normal text-gray-600">{t("offer_seats_description")}</p>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
{seatsInputVisible && (
|
||||
<Controller
|
||||
name="seatsPerTimeSlot"
|
||||
control={formMethods.control}
|
||||
defaultValue={eventType.seatsPerTimeSlot}
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<div className="">
|
||||
<TextField
|
||||
required
|
||||
name="seatsPerTimeSlot"
|
||||
label={t("number_of_seats")}
|
||||
type="number"
|
||||
defaultValue={value || 2}
|
||||
addOnSuffix={<>{t("seats")}</>}
|
||||
onChange={(e) => {
|
||||
onChange(Number(e.target.value));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
)}
|
||||
{showEventNameTip && (
|
||||
<Dialog open={showEventNameTip} onOpenChange={setShowEventNameTip}>
|
||||
<DialogContent
|
||||
|
||||
@@ -27,13 +27,13 @@ const AppCard = ({
|
||||
}) => {
|
||||
return (
|
||||
<div className="mb-4 rounded-md border border-gray-200 p-8">
|
||||
<div className="mb-6 flex w-full">
|
||||
<div className="flex w-full">
|
||||
<img src={logo} alt={name} className="mr-3 h-auto w-[42px] rounded-sm" />
|
||||
<div className="flex flex-col">
|
||||
<span className="font-semibold leading-none text-black">{name}</span>
|
||||
<p className="pt-2 text-sm font-normal text-gray-600">{description}</p>
|
||||
</div>
|
||||
<div className="ml-auto">
|
||||
<div className="ml-auto flex items-center">
|
||||
<Switch onCheckedChange={switchOnClick} checked={switchChecked} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useFormContext, Controller, useWatch } from "react-hook-form";
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { PeriodType } from "@calcom/prisma/client";
|
||||
import { Select, Switch, Label, Input } from "@calcom/ui/v2";
|
||||
import { Select, Switch, Label, Input, MinutesField } from "@calcom/ui/v2";
|
||||
import DateRangePicker from "@calcom/ui/v2/core/form/date-range-picker/DateRangePicker";
|
||||
|
||||
export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">) => {
|
||||
@@ -113,35 +113,12 @@ export const EventLimitsTab = (props: Pick<EventTypeSetupInfered, "eventType">)
|
||||
</div>
|
||||
<div className="flex flex-col space-y-4 pt-4 lg:flex-row lg:space-y-0 lg:space-x-4">
|
||||
<div className="w-full">
|
||||
<Label htmlFor="minimumBookingNotice">{t("minimum_booking_notice")} </Label>
|
||||
<Controller
|
||||
name="minimumBookingNotice"
|
||||
control={formMethods.control}
|
||||
defaultValue={eventType.minimumBookingNotice || 0}
|
||||
render={({ field: { onChange, value } }) => {
|
||||
const minimumBookingOptions = [
|
||||
{
|
||||
label: t("event_buffer_default"),
|
||||
value: 0,
|
||||
},
|
||||
...[5, 10, 15, 20, 30, 45, 60, 90, 120].map((minutes) => ({
|
||||
label: minutes + " " + t("minutes"),
|
||||
value: minutes,
|
||||
})),
|
||||
];
|
||||
return (
|
||||
<Select
|
||||
isSearchable={false}
|
||||
onChange={(val) => {
|
||||
if (val) onChange(val.value);
|
||||
}}
|
||||
defaultValue={
|
||||
minimumBookingOptions.find((option) => option.value === value) || minimumBookingOptions[0]
|
||||
}
|
||||
options={minimumBookingOptions}
|
||||
/>
|
||||
);
|
||||
}}
|
||||
<MinutesField
|
||||
required
|
||||
label={t("minimum_booking_notice")}
|
||||
type="number"
|
||||
placeholder="120"
|
||||
{...formMethods.register("minimumBookingNotice", { valueAsNumber: true })}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
|
||||
@@ -10,7 +10,7 @@ import { getEventLocationType, EventLocationType } from "@calcom/app-store/locat
|
||||
import { CAL_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Select, Label, TextField, Button } from "@calcom/ui/v2";
|
||||
import { Select, Label, TextField, Button, Skeleton } from "@calcom/ui/v2";
|
||||
|
||||
import { slugify } from "@lib/slugify";
|
||||
|
||||
@@ -222,7 +222,9 @@ export const EventSetupTab = (
|
||||
}}
|
||||
/>
|
||||
<div>
|
||||
<Label>{t("location")}</Label>
|
||||
<Skeleton as={Label} loadingClassName="w-16">
|
||||
{t("location")}
|
||||
</Skeleton>
|
||||
<Controller
|
||||
name="locations"
|
||||
control={formMethods.control}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { TFunction } from "next-i18next";
|
||||
import { useRouter } from "next/router";
|
||||
import { EventTypeSetupInfered, FormValues } from "pages/v2/event-types/[type]";
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { Loader } from "react-feather";
|
||||
import { UseFormReturn } from "react-hook-form";
|
||||
|
||||
@@ -9,20 +10,18 @@ import { CAL_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { HttpError } from "@calcom/lib/http-error";
|
||||
import { trpc, TRPCClientError } from "@calcom/trpc/react";
|
||||
import ConfirmationDialogContent from "@calcom/ui/ConfirmationDialogContent";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import {
|
||||
Button,
|
||||
ButtonGroup,
|
||||
Tooltip,
|
||||
showToast,
|
||||
VerticalTabItemProps,
|
||||
VerticalTabs,
|
||||
HorizontalTabs,
|
||||
Switch,
|
||||
Label,
|
||||
HorizontalTabItemProps,
|
||||
showToast,
|
||||
Switch,
|
||||
Tooltip,
|
||||
VerticalTabs,
|
||||
} from "@calcom/ui/v2";
|
||||
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
|
||||
import { Dialog } from "@calcom/ui/v2/core/Dialog";
|
||||
import Dropdown, {
|
||||
DropdownMenuContent,
|
||||
@@ -31,6 +30,7 @@ import Dropdown, {
|
||||
} from "@calcom/ui/v2/core/Dropdown";
|
||||
import Shell from "@calcom/ui/v2/core/Shell";
|
||||
import VerticalDivider from "@calcom/ui/v2/core/VerticalDivider";
|
||||
import { Skeleton } from "@calcom/ui/v2/core/skeleton";
|
||||
|
||||
import { ClientSuspense } from "@components/ClientSuspense";
|
||||
import { EmbedButton, EmbedDialog } from "@components/Embed";
|
||||
@@ -46,6 +46,59 @@ type Props = {
|
||||
formMethods: UseFormReturn<FormValues>;
|
||||
};
|
||||
|
||||
function getNavigation(props: {
|
||||
t: TFunction;
|
||||
eventType: Props["eventType"];
|
||||
enabledAppsNumber: number;
|
||||
enabledWorkflowsNumber: number;
|
||||
}) {
|
||||
const { eventType, t, enabledAppsNumber, enabledWorkflowsNumber } = props;
|
||||
return [
|
||||
{
|
||||
name: "event_setup_tab_title",
|
||||
href: `/event-types/${eventType.id}?tabName=setup`,
|
||||
icon: Icon.FiLink,
|
||||
info: `${eventType.length} Mins`, // TODO: Get this from props
|
||||
},
|
||||
{
|
||||
name: "availability",
|
||||
href: `/event-types/${eventType.id}?tabName=availability`,
|
||||
icon: Icon.FiCalendar,
|
||||
info: `Working Hours`, // TODO: Get this from props
|
||||
},
|
||||
{
|
||||
name: "event_limit_tab_title",
|
||||
href: `/event-types/${eventType.id}?tabName=limits`,
|
||||
icon: Icon.FiClock,
|
||||
info: `event_limit_tab_description`,
|
||||
},
|
||||
{
|
||||
name: "event_advanced_tab_title",
|
||||
href: `/event-types/${eventType.id}?tabName=advanced`,
|
||||
icon: Icon.FiSliders,
|
||||
info: `event_advanced_tab_description`,
|
||||
},
|
||||
{
|
||||
name: "recurring",
|
||||
href: `/event-types/${eventType.id}?tabName=recurring`,
|
||||
icon: Icon.FiRepeat,
|
||||
info: `recurring_event_tab_description`,
|
||||
},
|
||||
{
|
||||
name: "apps",
|
||||
href: `/event-types/${eventType.id}?tabName=apps`,
|
||||
icon: Icon.FiGrid,
|
||||
info: `${enabledAppsNumber} ${t("active")}`,
|
||||
},
|
||||
{
|
||||
name: "workflows",
|
||||
href: `/event-types/${eventType.id}?tabName=workflows`,
|
||||
icon: Icon.FiZap,
|
||||
info: `${enabledWorkflowsNumber} ${t("active")}`,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
function EventTypeSingleLayout({
|
||||
children,
|
||||
eventType,
|
||||
@@ -57,8 +110,8 @@ function EventTypeSingleLayout({
|
||||
formMethods,
|
||||
}: Props) {
|
||||
const utils = trpc.useContext();
|
||||
const router = useRouter();
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
|
||||
const hasPermsToDelete = currentUserMembership?.role !== "MEMBER" || !currentUserMembership;
|
||||
@@ -67,6 +120,7 @@ function EventTypeSingleLayout({
|
||||
onSuccess: async () => {
|
||||
await utils.invalidateQueries(["viewer.eventTypes"]);
|
||||
showToast(t("event_type_deleted_successfully"), "success");
|
||||
await router.push("/event-types");
|
||||
setDeleteDialogOpen(false);
|
||||
},
|
||||
onError: (err) => {
|
||||
@@ -82,73 +136,17 @@ function EventTypeSingleLayout({
|
||||
|
||||
// Define tab navigation here
|
||||
const EventTypeTabs = useMemo(() => {
|
||||
const navigation: (VerticalTabItemProps & HorizontalTabItemProps)[] = [
|
||||
{
|
||||
name: "event_setup_tab_title",
|
||||
tabName: "setup",
|
||||
icon: Icon.FiLink,
|
||||
info: `${eventType.length} Mins`, // TODO: Get this from props
|
||||
},
|
||||
{
|
||||
name: "availability",
|
||||
tabName: "availability",
|
||||
icon: Icon.FiCalendar,
|
||||
info: `Working Hours`, // TODO: Get this from props
|
||||
},
|
||||
{
|
||||
name: "event_limit_tab_title",
|
||||
tabName: "limits",
|
||||
icon: Icon.FiClock,
|
||||
info: `event_limit_tab_description`,
|
||||
},
|
||||
{
|
||||
name: "event_advanced_tab_title",
|
||||
tabName: "advanced",
|
||||
icon: Icon.FiSliders,
|
||||
info: `event_advanced_tab_description`,
|
||||
},
|
||||
{
|
||||
name: "recurring",
|
||||
tabName: "recurring",
|
||||
icon: Icon.FiRepeat,
|
||||
info: `recurring_event_tab_description`,
|
||||
},
|
||||
{
|
||||
name: "apps",
|
||||
tabName: "apps",
|
||||
icon: Icon.FiGrid,
|
||||
info: `${enabledAppsNumber} ${t("active")}`,
|
||||
},
|
||||
{
|
||||
name: "workflows",
|
||||
tabName: "workflows",
|
||||
icon: Icon.FiZap,
|
||||
info: `${enabledWorkflowsNumber} ${t("active")}`,
|
||||
},
|
||||
];
|
||||
|
||||
const navigation = getNavigation({ t, eventType, enabledAppsNumber, enabledWorkflowsNumber });
|
||||
// If there is a team put this navigation item within the tabs
|
||||
if (team)
|
||||
navigation.splice(2, 0, {
|
||||
name: "scheduling_type",
|
||||
tabName: "team",
|
||||
href: `/event-types/${eventType.id}?tabName=team`,
|
||||
icon: Icon.FiUsers,
|
||||
info: eventType.schedulingType === "COLLECTIVE" ? "collective" : "round_robin",
|
||||
});
|
||||
return navigation;
|
||||
}, [eventType, enabledAppsNumber, team]);
|
||||
|
||||
useEffect(() => {
|
||||
// Default to the first in the list
|
||||
if (!router.query.tabName) {
|
||||
router.push({
|
||||
query: {
|
||||
...router.query,
|
||||
tabName: EventTypeTabs[0].tabName,
|
||||
},
|
||||
});
|
||||
}
|
||||
}, [EventTypeTabs, router]);
|
||||
}, [t, eventType, enabledAppsNumber, enabledWorkflowsNumber, team]);
|
||||
|
||||
const permalink = `${CAL_URL}/${team ? `team/${team.slug}` : eventType.users[0].username}/${
|
||||
eventType.slug
|
||||
@@ -159,15 +157,18 @@ function EventTypeSingleLayout({
|
||||
return (
|
||||
<Shell
|
||||
backPath="/event-types"
|
||||
title={t("event_type_title", { eventTypeTitle: eventType.title })}
|
||||
title={eventType.title + " | " + t("event_type")}
|
||||
heading={eventType.title}
|
||||
subtitle={eventType.description || ""}
|
||||
CTA={
|
||||
<div className="flex items-center justify-end">
|
||||
<div className="flex items-center rounded-md px-2 sm:hover:bg-gray-100">
|
||||
<Label htmlFor="hiddenSwitch" className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
|
||||
<Skeleton
|
||||
as={Label}
|
||||
htmlFor="hiddenSwitch"
|
||||
className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
|
||||
{t("hide_from_profile")}
|
||||
</Label>
|
||||
</Skeleton>
|
||||
<Switch
|
||||
id="hiddenSwitch"
|
||||
defaultChecked={formMethods.getValues("hidden")}
|
||||
@@ -208,6 +209,7 @@ function EventTypeSingleLayout({
|
||||
StartIcon={Icon.FiCode}
|
||||
color="secondary"
|
||||
size="icon"
|
||||
tooltip={t("embed")}
|
||||
/>
|
||||
<Button
|
||||
color="secondary"
|
||||
@@ -265,10 +267,15 @@ function EventTypeSingleLayout({
|
||||
<ClientSuspense fallback={<Loader />}>
|
||||
<div className="-mt-2 flex flex-col xl:flex-row xl:space-x-8">
|
||||
<div className="hidden xl:block">
|
||||
<VerticalTabs className="primary-navigation" tabs={EventTypeTabs} sticky />
|
||||
<VerticalTabs
|
||||
className="primary-navigation"
|
||||
tabs={EventTypeTabs}
|
||||
sticky
|
||||
linkProps={{ shallow: true }}
|
||||
/>
|
||||
</div>
|
||||
<div className="p-2 md:mx-0 md:p-0 xl:hidden">
|
||||
<HorizontalTabs tabNameKey="tabName" tabs={EventTypeTabs} />
|
||||
<HorizontalTabs tabs={EventTypeTabs} linkProps={{ shallow: true }} />
|
||||
</div>
|
||||
<div className="w-full ltr:mr-2 rtl:ml-2">
|
||||
<div
|
||||
@@ -292,7 +299,7 @@ function EventTypeSingleLayout({
|
||||
e.preventDefault();
|
||||
deleteMutation.mutate({ id: eventType.id });
|
||||
}}>
|
||||
{t("delete_event_type_description") as string}
|
||||
{t("delete_event_type_description")}
|
||||
</ConfirmationDialogContent>
|
||||
</Dialog>
|
||||
<EmbedDialog />
|
||||
|
||||
@@ -6,7 +6,7 @@ function SkeletonLoader() {
|
||||
<SkeletonContainer>
|
||||
<div className="mb-4 flex items-center">
|
||||
<SkeletonAvatar className="h-8 w-8" />
|
||||
<div className="space-y-1">
|
||||
<div className="flex flex-col space-y-1">
|
||||
<SkeletonText className="h-4 w-16" />
|
||||
<SkeletonText className="h-4 w-24" />
|
||||
</div>
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useMutation } from "react-query";
|
||||
import { useMutation } from "@tanstack/react-query";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
@@ -12,6 +12,7 @@ export function CalendarSwitch(props: {
|
||||
externalId: string;
|
||||
title: string;
|
||||
defaultSelected: boolean;
|
||||
isSelected: boolean;
|
||||
}) {
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -69,7 +70,7 @@ export function CalendarSwitch(props: {
|
||||
key={props.externalId}
|
||||
name="enabled"
|
||||
label={props.title}
|
||||
defaultChecked={props.defaultSelected}
|
||||
defaultChecked={props.isSelected}
|
||||
onCheckedChange={(isOn: boolean) => {
|
||||
mutation.mutate({ isOn });
|
||||
}}
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
import { ReactNode } from "react";
|
||||
import {
|
||||
QueryObserverIdleResult,
|
||||
QueryObserverLoadingErrorResult,
|
||||
QueryObserverLoadingResult,
|
||||
QueryObserverRefetchErrorResult,
|
||||
QueryObserverSuccessResult,
|
||||
UseQueryResult,
|
||||
} from "react-query";
|
||||
} from "@tanstack/react-query";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import type { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import type { UseTRPCQueryOptions } from "@calcom/trpc/react";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type {
|
||||
inferHandlerInput,
|
||||
@@ -22,6 +20,8 @@ import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Loader from "@calcom/ui/Loader";
|
||||
|
||||
import type { UseTRPCQueryOptions } from "@trpc/react/shared";
|
||||
|
||||
type ErrorLike = {
|
||||
message: string;
|
||||
};
|
||||
@@ -34,7 +34,6 @@ interface QueryCellOptionsBase<TData, TError extends ErrorLike> {
|
||||
query: QueryObserverLoadingErrorResult<TData, TError> | QueryObserverRefetchErrorResult<TData, TError>
|
||||
) => JSXElementOrNull;
|
||||
loading?: (query: QueryObserverLoadingResult<TData, TError> | null) => JSXElementOrNull;
|
||||
idle?: (query: QueryObserverIdleResult<TData, TError>) => JSXElementOrNull;
|
||||
}
|
||||
|
||||
interface QueryCellOptionsNoEmpty<TData, TError extends ErrorLike>
|
||||
@@ -84,14 +83,11 @@ export function QueryCell<TData, TError extends ErrorLike>(
|
||||
);
|
||||
}
|
||||
|
||||
if (query.status === "idle") {
|
||||
return opts.idle?.(query) ?? StatusLoader;
|
||||
}
|
||||
// impossible state
|
||||
return null;
|
||||
}
|
||||
|
||||
type inferProcedures<TObj extends ProcedureRecord<any, any, any, any, any, any>> = {
|
||||
type inferProcedures<TObj extends ProcedureRecord> = {
|
||||
[TPath in keyof TObj]: {
|
||||
input: inferProcedureInput<TObj[TPath]>;
|
||||
output: inferProcedureOutput<TObj[TPath]>;
|
||||
|
||||
@@ -9,7 +9,7 @@ import { ComponentProps, ReactNode } from "react";
|
||||
|
||||
import DynamicHelpscoutProvider from "@calcom/features/ee/support/lib/helpscout/providerDynamic";
|
||||
import DynamicIntercomProvider from "@calcom/features/ee/support/lib/intercom/providerDynamic";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { trpc, proxy } from "@calcom/trpc/react";
|
||||
import { MetaProvider } from "@calcom/ui/v2/core/Meta";
|
||||
|
||||
import usePublicPage from "@lib/hooks/usePublicPage";
|
||||
@@ -38,7 +38,8 @@ const CustomI18nextProvider = (props: AppPropsWithChildren) => {
|
||||
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
|
||||
* We intend to not cache i18n query
|
||||
**/
|
||||
const { i18n, locale } = trpc.useQuery(["viewer.public.i18n"], { context: { skipBatch: true } }).data ?? {
|
||||
const { i18n, locale } = trpc.useQuery(["viewer.public.i18n"], { trpc: { context: { skipBatch: true } } })
|
||||
.data ?? {
|
||||
locale: "en",
|
||||
};
|
||||
|
||||
@@ -54,7 +55,7 @@ const CustomI18nextProvider = (props: AppPropsWithChildren) => {
|
||||
};
|
||||
|
||||
const AppProviders = (props: AppPropsWithChildren) => {
|
||||
const session = trpc.useQuery(["viewer.public.session"]).data;
|
||||
const session = proxy.public.session.useQuery().data;
|
||||
// No need to have intercom on public pages - Good for Page Performance
|
||||
const isPublicPage = usePublicPage();
|
||||
const isThemeSupported =
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// handles logic related to user clock display using 24h display / timeZone options.
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { isBrowserLocale24h } from "@calcom/lib/timeFormat";
|
||||
import {
|
||||
getIs24hClockFromLocalStorage,
|
||||
isBrowserLocale24h,
|
||||
setIs24hClockInLocalStorage,
|
||||
} from "@calcom/lib/timeFormat";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
|
||||
interface TimeOptions {
|
||||
@@ -20,8 +24,8 @@ const initClock = () => {
|
||||
return;
|
||||
}
|
||||
// This only sets browser locale if there's no preference on localStorage.
|
||||
if (!localStorage.getItem("timeOption.is24hClock")) set24hClock(isBrowserLocale24h());
|
||||
timeOptions.is24hClock = localStorage.getItem("timeOption.is24hClock") === "true";
|
||||
if (getIs24hClockFromLocalStorage() === null) set24hClock(isBrowserLocale24h());
|
||||
timeOptions.is24hClock = !!getIs24hClockFromLocalStorage();
|
||||
timeOptions.inviteeTimeZone = localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess();
|
||||
};
|
||||
|
||||
@@ -32,7 +36,7 @@ const is24h = (is24hClock?: boolean) => {
|
||||
};
|
||||
|
||||
const set24hClock = (is24hClock: boolean) => {
|
||||
localStorage.setItem("timeOption.is24hClock", is24hClock.toString());
|
||||
setIs24hClockInLocalStorage(is24hClock);
|
||||
timeOptions.is24hClock = is24hClock;
|
||||
};
|
||||
|
||||
|
||||
@@ -9,10 +9,10 @@ export const WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP = {
|
||||
WebhookTriggerEvents.BOOKING_RESCHEDULED,
|
||||
WebhookTriggerEvents.MEETING_ENDED,
|
||||
] as const,
|
||||
routing_forms: [WebhookTriggerEvents.FORM_SUBMITTED] as ["FORM_SUBMITTED"],
|
||||
"routing-forms": [WebhookTriggerEvents.FORM_SUBMITTED] as ["FORM_SUBMITTED"],
|
||||
};
|
||||
|
||||
export const WEBHOOK_TRIGGER_EVENTS = [
|
||||
...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP.core,
|
||||
...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP.routing_forms,
|
||||
...WEBHOOK_TRIGGER_EVENTS_GROUPED_BY_APP["routing-forms"],
|
||||
] as const;
|
||||
|
||||
+12
-1
@@ -7,6 +7,7 @@ import { extendEventData, nextCollectBasicSettings } from "@calcom/lib/telemetry
|
||||
|
||||
const V2_WHITELIST = [
|
||||
"/settings/admin",
|
||||
"/settings/billing",
|
||||
"/settings/developer/webhooks",
|
||||
"/settings/developer/api-keys",
|
||||
"/settings/my-account",
|
||||
@@ -17,13 +18,15 @@ const V2_WHITELIST = [
|
||||
"/event-types",
|
||||
"/workflows",
|
||||
"/apps",
|
||||
"/teams",
|
||||
"/success",
|
||||
"/auth/login",
|
||||
];
|
||||
|
||||
// For pages
|
||||
// - which has V1 versions being modified as V2
|
||||
const V2_BLACKLIST = ["/apps/routing_forms/"];
|
||||
// - Add routing_forms to keep old links working
|
||||
const V2_BLACKLIST = ["/apps/routing_forms/", "/apps/routing-forms/", "/apps/typeform/"];
|
||||
|
||||
const middleware: NextMiddleware = async (req) => {
|
||||
const url = req.nextUrl;
|
||||
@@ -42,6 +45,13 @@ const middleware: NextMiddleware = async (req) => {
|
||||
return NextResponse.redirect(req.nextUrl);
|
||||
}
|
||||
}
|
||||
|
||||
// Don't 404 old routing_forms links
|
||||
if (url.pathname.startsWith("/apps/routing_forms")) {
|
||||
url.pathname = url.pathname.replace("/apps/routing_forms", "/apps/routing-forms");
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
|
||||
/** Display available V2 pages */
|
||||
if (
|
||||
!V2_BLACKLIST.some((p) => url.pathname.startsWith(p)) &&
|
||||
@@ -51,6 +61,7 @@ const middleware: NextMiddleware = async (req) => {
|
||||
url.pathname = `/v2${url.pathname}`;
|
||||
return NextResponse.rewrite(url);
|
||||
}
|
||||
|
||||
return NextResponse.next();
|
||||
};
|
||||
|
||||
|
||||
@@ -135,11 +135,11 @@ const nextConfig = {
|
||||
},
|
||||
{
|
||||
source: "/forms/:formId",
|
||||
destination: "/apps/routing_forms/routing-link/:formId",
|
||||
destination: "/apps/routing-forms/routing-link/:formId",
|
||||
},
|
||||
{
|
||||
source: "/router",
|
||||
destination: "/apps/routing_forms/router",
|
||||
destination: "/apps/routing-forms/router",
|
||||
},
|
||||
/* TODO: have these files being served from another deployment or CDN {
|
||||
source: "/embed/embed.js",
|
||||
@@ -159,6 +159,11 @@ const nextConfig = {
|
||||
destination: "/settings/my-account/profile",
|
||||
permanent: true,
|
||||
},
|
||||
{
|
||||
source: "/settings/teams",
|
||||
destination: "/teams",
|
||||
permanent: true,
|
||||
},
|
||||
/* V2 testers get redirected to the new settings */
|
||||
{
|
||||
source: "/settings/profile",
|
||||
|
||||
+12
-11
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@calcom/web",
|
||||
"version": "2.0.0",
|
||||
"version": "2.0.4",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"analyze": "ANALYZE=true next build",
|
||||
@@ -45,18 +45,20 @@
|
||||
"@hookform/resolvers": "^2.9.7",
|
||||
"@next-auth/prisma-adapter": "^1.0.4",
|
||||
"@next/bundle-analyzer": "12.1.6",
|
||||
"@radix-ui/react-avatar": "^0.1.4",
|
||||
"@radix-ui/react-collapsible": "^0.1.0",
|
||||
"@radix-ui/react-dialog": "^0.1.0",
|
||||
"@radix-ui/react-dropdown-menu": "^0.1.1",
|
||||
"@radix-ui/react-id": "^0.1.0",
|
||||
"@radix-ui/react-radio-group": "^0.1.1",
|
||||
"@radix-ui/react-slider": "^0.1.1",
|
||||
"@radix-ui/react-switch": "^0.1.1",
|
||||
"@radix-ui/react-toggle-group": "^0.1.5",
|
||||
"@radix-ui/react-avatar": "^1.0.0",
|
||||
"@radix-ui/react-collapsible": "^1.0.0",
|
||||
"@radix-ui/react-dialog": "^1.0.0",
|
||||
"@radix-ui/react-dropdown-menu": "^1.0.0",
|
||||
"@radix-ui/react-id": "^1.0.0",
|
||||
"@radix-ui/react-popover": "^1.0.0",
|
||||
"@radix-ui/react-radio-group": "^1.0.0",
|
||||
"@radix-ui/react-slider": "^1.0.0",
|
||||
"@radix-ui/react-switch": "^1.0.0",
|
||||
"@radix-ui/react-toggle-group": "^1.0.0",
|
||||
"@radix-ui/react-tooltip": "^1.0.0",
|
||||
"@stripe/react-stripe-js": "^1.10.0",
|
||||
"@stripe/stripe-js": "^1.35.0",
|
||||
"@tanstack/react-query": "^4.3.9",
|
||||
"@vercel/edge-functions-ui": "^0.2.1",
|
||||
"@wojtekmaj/react-daterange-picker": "^3.3.1",
|
||||
"accept-language-parser": "^1.5.0",
|
||||
@@ -105,7 +107,6 @@
|
||||
"react-live-chat-loader": "^2.7.3",
|
||||
"react-multi-email": "^0.5.3",
|
||||
"react-phone-number-input": "^3.2.7",
|
||||
"react-query": "^3.39.2",
|
||||
"react-select": "^5.4.0",
|
||||
"react-timezone-select": "^1.3.2",
|
||||
"react-use-intercom": "1.5.1",
|
||||
|
||||
+29
-31
@@ -73,7 +73,7 @@ export default function Custom404() {
|
||||
<ul role="list" className="mt-4">
|
||||
<li className="border-2 border-green-500 px-4 py-2">
|
||||
<a
|
||||
href={`${WEBSITE_URL}/pricing?infra`}
|
||||
href="https://console.cal.com"
|
||||
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
|
||||
@@ -190,12 +190,10 @@ export default function Custom404() {
|
||||
{t("check_spelling_mistakes_or_go_back")}
|
||||
</span>
|
||||
) : isCalcom ? (
|
||||
<Link href={url}>
|
||||
<a className="mt-2 inline-block text-lg">
|
||||
{t("the_username")} <strong className="text-blue-500">cal.com{username}</strong>{" "}
|
||||
{t("is_still_available")} <span className="text-blue-500">{t("register_now")}</span>.
|
||||
</a>
|
||||
</Link>
|
||||
<a target="_blank" href={url} className="mt-2 inline-block text-lg" rel="noreferrer">
|
||||
{t("the_username")} <strong className="text-blue-500">cal.com{username}</strong>{" "}
|
||||
{t("is_still_available")} <span className="text-blue-500">{t("register_now")}</span>.
|
||||
</a>
|
||||
) : (
|
||||
<span className="mt-2 inline-block text-lg">
|
||||
{t("the_username")}{" "}
|
||||
@@ -211,31 +209,31 @@ export default function Custom404() {
|
||||
{!isSubpage && isCalcom && (
|
||||
<ul role="list" className="mt-4">
|
||||
<li className="border-2 border-green-500 px-4 py-2">
|
||||
<Link href={url}>
|
||||
<a className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
|
||||
<Icon.FiCheck className="h-6 w-6 text-green-500" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
|
||||
<span className="focus:outline-none">
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
{t("register")} <strong className="text-green-500">{username}</strong>
|
||||
</span>
|
||||
<a
|
||||
href={url}
|
||||
target="_blank"
|
||||
className="relative flex items-start space-x-4 py-6 rtl:space-x-reverse"
|
||||
rel="noreferrer">
|
||||
<div className="flex-shrink-0">
|
||||
<span className="flex h-12 w-12 items-center justify-center rounded-lg bg-green-50">
|
||||
<Icon.FiCheck className="h-6 w-6 text-green-500" aria-hidden="true" />
|
||||
</span>
|
||||
</div>
|
||||
<div className="min-w-0 flex-1">
|
||||
<h3 className="text-base font-medium text-gray-900">
|
||||
<span className="rounded-sm focus-within:ring-2 focus-within:ring-gray-500 focus-within:ring-offset-2">
|
||||
<span className="focus:outline-none">
|
||||
<span className="absolute inset-0" aria-hidden="true" />
|
||||
{t("register")} <strong className="text-green-500">{username}</strong>
|
||||
</span>
|
||||
</h3>
|
||||
<p className="text-base text-gray-500">
|
||||
{t("claim_username_and_schedule_events")}
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
</a>
|
||||
</Link>
|
||||
</span>
|
||||
</h3>
|
||||
<p className="text-base text-gray-500">{t("claim_username_and_schedule_events")}</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 self-center">
|
||||
<Icon.FiChevronRight className="h-5 w-5 text-gray-400" aria-hidden="true" />
|
||||
</div>
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
)}
|
||||
|
||||
@@ -115,7 +115,7 @@ const providers: Provider[] = [
|
||||
};
|
||||
},
|
||||
}),
|
||||
// ImpersonationProvider,
|
||||
ImpersonationProvider,
|
||||
];
|
||||
|
||||
if (IS_GOOGLE_LOGIN_ENABLED) {
|
||||
|
||||
@@ -76,7 +76,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
||||
});
|
||||
|
||||
// get user's credentials + their connected integrations
|
||||
const calendarCredentials = getCalendarCredentials(user.credentials, user.id);
|
||||
const calendarCredentials = getCalendarCredentials(user.credentials);
|
||||
// get all the connected integrations' calendars (from third party)
|
||||
const connectedCalendars = await getConnectedCalendars(calendarCredentials, user.selectedCalendars);
|
||||
const calendars = connectedCalendars.flatMap((c) => c.calendars).filter(notEmpty);
|
||||
|
||||
@@ -327,26 +327,29 @@ async function handler(req: NextApiRequest) {
|
||||
|
||||
if (!users) throw new HttpError({ statusCode: 404, message: "eventTypeUser.notFound" });
|
||||
|
||||
const availableUsers = await ensureAvailableUsers(
|
||||
{
|
||||
...eventType,
|
||||
users,
|
||||
},
|
||||
{
|
||||
dateFrom: reqBody.start,
|
||||
dateTo: reqBody.end,
|
||||
}
|
||||
);
|
||||
if (!eventType.seatsPerTimeSlot) {
|
||||
const availableUsers = await ensureAvailableUsers(
|
||||
{
|
||||
...eventType,
|
||||
users,
|
||||
},
|
||||
{
|
||||
dateFrom: reqBody.start,
|
||||
dateTo: reqBody.end,
|
||||
}
|
||||
);
|
||||
|
||||
// Assign to only one user when ROUND_ROBIN
|
||||
if (eventType.schedulingType === SchedulingType.ROUND_ROBIN) {
|
||||
users = [await getLuckyUser("MAXIMIZE_AVAILABILITY", { availableUsers, eventTypeId: eventType.id })];
|
||||
} else {
|
||||
// excluding ROUND_ROBIN, all users have availability required.
|
||||
if (availableUsers.length !== users.length) {
|
||||
throw new Error("Some users are unavailable for booking.");
|
||||
// Add an if conditional if there are no seats on the event type
|
||||
// Assign to only one user when ROUND_ROBIN
|
||||
if (eventType.schedulingType === SchedulingType.ROUND_ROBIN) {
|
||||
users = [await getLuckyUser("MAXIMIZE_AVAILABILITY", { availableUsers, eventTypeId: eventType.id })];
|
||||
} else {
|
||||
// excluding ROUND_ROBIN, all users have availability required.
|
||||
if (availableUsers.length !== users.length) {
|
||||
throw new Error("Some users are unavailable for booking.");
|
||||
}
|
||||
users = availableUsers;
|
||||
}
|
||||
users = availableUsers;
|
||||
}
|
||||
|
||||
console.log("available users", users);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
|
||||
import RoutingFormsRoutingConfig from "@calcom/app-store/ee/routing_forms/pages/app-routing.config";
|
||||
import RoutingFormsRoutingConfig from "@calcom/app-store/ee/routing-forms/pages/app-routing.config";
|
||||
import TypeformRoutingConfig from "@calcom/app-store/typeform/pages/app-routing.config";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { AppGetServerSideProps } from "@calcom/types/AppGetServerSideProps";
|
||||
@@ -28,7 +28,7 @@ type NotFound = {
|
||||
|
||||
// TODO: It is a candidate for apps.*.generated.*
|
||||
const AppsRouting = {
|
||||
routing_forms: RoutingFormsRoutingConfig,
|
||||
"routing-forms": RoutingFormsRoutingConfig,
|
||||
typeform: TypeformRoutingConfig,
|
||||
};
|
||||
|
||||
|
||||
@@ -1,17 +1,16 @@
|
||||
import { GetServerSidePropsContext } from "next";
|
||||
import { signOut, useSession } from "next-auth/react";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
import { useEffect } from "react";
|
||||
|
||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import Button from "@calcom/ui/Button";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
|
||||
import { inferSSRProps } from "@lib/types/inferSSRProps";
|
||||
|
||||
import AuthContainer from "@components/ui/AuthContainer";
|
||||
import AuthContainer from "@components/v2/ui/AuthContainer";
|
||||
|
||||
import { ssrInit } from "@server/lib/ssr";
|
||||
|
||||
@@ -30,7 +29,7 @@ export default function Logout(props: Props) {
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
<AuthContainer title={t("logged_out")} description={t("youve_been_logged_out")}>
|
||||
<AuthContainer title={t("logged_out")} description={t("youve_been_logged_out")} showLogo>
|
||||
<div className="mb-4">
|
||||
<div className="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-green-100">
|
||||
<Icon.FiCheck className="h-6 w-6 text-green-600" />
|
||||
@@ -44,9 +43,9 @@ export default function Logout(props: Props) {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<Link href="/auth/login" passHref>
|
||||
<Button className="flex w-full justify-center"> {t("go_back_login")}</Button>
|
||||
</Link>
|
||||
<Button href="/auth/login" passHref className="flex w-full justify-center">
|
||||
{t("go_back_login")}
|
||||
</Button>
|
||||
</AuthContainer>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { CheckIcon } from "@heroicons/react/solid";
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import classNames from "classnames";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
import { Controller, FormProvider, useForm } from "react-hook-form";
|
||||
@@ -9,7 +10,7 @@ import { isPasswordValid } from "@calcom/lib/auth";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { inferSSRProps } from "@calcom/types/inferSSRProps";
|
||||
import { TextField, EmailField, PasswordField } from "@calcom/ui/v2";
|
||||
import { TextField, EmailField, PasswordField, Label } from "@calcom/ui/v2";
|
||||
import WizardForm from "@calcom/ui/v2/core/WizardForm";
|
||||
|
||||
const StepDone = () => {
|
||||
@@ -85,6 +86,8 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
|
||||
}
|
||||
}, onError);
|
||||
|
||||
const longWebsiteUrl = process.env.NEXT_PUBLIC_WEBSITE_URL.length > 30;
|
||||
|
||||
return (
|
||||
<FormProvider {...formMethods}>
|
||||
<form id="setup-step-1" name="setup-step-1" className="space-y-4" onSubmit={onSubmit}>
|
||||
@@ -93,22 +96,36 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => {
|
||||
name="username"
|
||||
control={formMethods.control}
|
||||
render={({ field: { onBlur, onChange, value } }) => (
|
||||
<TextField
|
||||
addOnLeading={
|
||||
<span className="items-centerpx-3 inline-flex rounded-none text-sm text-gray-500">
|
||||
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
|
||||
</span>
|
||||
}
|
||||
value={value || ""}
|
||||
className="my-0"
|
||||
onBlur={onBlur}
|
||||
name="username"
|
||||
onChange={async (e) => {
|
||||
onChange(e.target.value);
|
||||
formMethods.setValue("username", e.target.value);
|
||||
await formMethods.trigger("username");
|
||||
}}
|
||||
/>
|
||||
<>
|
||||
<Label htmlFor="username" className={classNames(longWebsiteUrl && "mb-0")}>
|
||||
<span className="block">{t("username")}</span>
|
||||
{longWebsiteUrl && (
|
||||
<small className="items-centerpx-3 mt-2 inline-flex rounded-t-md border border-b-0 border-gray-300 bg-gray-100 py-1 px-3 text-gray-500">
|
||||
{process.env.NEXT_PUBLIC_WEBSITE_URL}
|
||||
</small>
|
||||
)}
|
||||
</Label>
|
||||
<TextField
|
||||
addOnLeading={
|
||||
!longWebsiteUrl && (
|
||||
<span className="items-centerpx-3 inline-flex rounded-none text-sm text-gray-500">
|
||||
{process.env.NEXT_PUBLIC_WEBSITE_URL}/
|
||||
</span>
|
||||
)
|
||||
}
|
||||
id="username"
|
||||
labelSrOnly={true}
|
||||
value={value || ""}
|
||||
className={classNames("my-0", longWebsiteUrl && "rounded-t-none")}
|
||||
onBlur={onBlur}
|
||||
name="username"
|
||||
onChange={async (e) => {
|
||||
onChange(e.target.value);
|
||||
formMethods.setValue("username", e.target.value);
|
||||
await formMethods.trigger("username");
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
* @deprecated modifications to this file should be v2 only
|
||||
* Use `/apps/web/pages/v2/availability/[schedule].tsx` instead
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
import { GetStaticPaths, GetStaticProps } from "next";
|
||||
import { useRouter } from "next/router";
|
||||
import { useState } from "react";
|
||||
|
||||
@@ -51,7 +51,6 @@ export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availab
|
||||
key={schedule.id}
|
||||
schedule={schedule}
|
||||
deleteFunction={deleteMutation.mutate}
|
||||
isDeleting={deleteMutation.isLoading}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -28,6 +28,7 @@ const descriptionByStatus: Record<BookingListingStatus, string> = {
|
||||
recurring: "recurring_bookings",
|
||||
past: "past_bookings",
|
||||
cancelled: "cancelled_bookings",
|
||||
unconfirmed: "unconfirmed_bookings",
|
||||
};
|
||||
|
||||
const querySchema = z.object({
|
||||
@@ -91,7 +92,7 @@ export default function Bookings() {
|
||||
{query.status === "error" && (
|
||||
<Alert severity="error" title={t("something_went_wrong")} message={query.error.message} />
|
||||
)}
|
||||
{(query.status === "loading" || query.status === "idle") && <SkeletonLoader />}
|
||||
{(query.status === "loading" || query.isPaused) && <SkeletonLoader />}
|
||||
{query.status === "success" && !isEmpty && (
|
||||
<>
|
||||
<div className="mt-6 overflow-hidden rounded-sm border border-b border-gray-200">
|
||||
|
||||
@@ -1,3 +1,9 @@
|
||||
/**
|
||||
* @deprecated modifications to this file should be v2 only
|
||||
* Use `apps/web/pages/v2/event-types/[type].tsx` instead
|
||||
*/
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
// @ts-nocheck
|
||||
import { zodResolver } from "@hookform/resolvers/zod";
|
||||
import { EventTypeCustomInput, MembershipRole, PeriodType, Prisma, SchedulingType } from "@prisma/client";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@radix-ui/react-collapsible";
|
||||
@@ -16,15 +22,16 @@ import { v5 as uuidv5 } from "uuid";
|
||||
import { z } from "zod";
|
||||
|
||||
import { SelectGifInput } from "@calcom/app-store/giphy/components";
|
||||
import { getEventLocationType, EventLocationType } from "@calcom/app-store/locations";
|
||||
import { EventLocationType, getEventLocationType } from "@calcom/app-store/locations";
|
||||
import { StripeData } from "@calcom/app-store/stripepayment/lib/server";
|
||||
import getApps, { getLocationOptions } from "@calcom/app-store/utils";
|
||||
import { LocationObject, LocationType } from "@calcom/core/location";
|
||||
import { LocationObject } from "@calcom/core/location";
|
||||
import { parseRecurringEvent } from "@calcom/lib";
|
||||
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { TRPCClientError } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { RecurringEvent } from "@calcom/types/Calendar";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
@@ -64,7 +71,6 @@ import * as RadioArea from "@components/ui/form/radio-area";
|
||||
import WebhookListContainer from "@components/webhook/WebhookListContainer";
|
||||
|
||||
import { getTranslation } from "@server/lib/i18n";
|
||||
import { TRPCClientError } from "@trpc/client";
|
||||
|
||||
const RainbowInstallForm = dynamic(() => import("@calcom/rainbow/components/RainbowInstallForm"), {
|
||||
suspense: true,
|
||||
@@ -108,6 +114,7 @@ export type FormValues = {
|
||||
periodCountCalendarDays: "1" | "0";
|
||||
periodDates: { startDate: Date; endDate: Date };
|
||||
seatsPerTimeSlot: number | null;
|
||||
seatsPerTimeSlotEnabled: boolean;
|
||||
minimumBookingNotice: number;
|
||||
beforeBufferTime: number;
|
||||
afterBufferTime: number;
|
||||
|
||||
@@ -13,6 +13,7 @@ import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { TRPCClientError } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
import Badge from "@calcom/ui/Badge";
|
||||
@@ -23,6 +24,7 @@ import Dropdown, {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuPortal,
|
||||
} from "@calcom/ui/Dropdown";
|
||||
import EmptyScreen from "@calcom/ui/EmptyScreen";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
@@ -42,8 +44,6 @@ import { LinkText } from "@components/ui/LinkText";
|
||||
import NoCalendarConnectedAlert from "@components/ui/NoCalendarConnectedAlert";
|
||||
import SkeletonLoader from "@components/v2/eventtype/SkeletonLoader";
|
||||
|
||||
import { TRPCClientError } from "@trpc/react";
|
||||
|
||||
type EventTypeGroups = inferQueryOutput<"viewer.eventTypes">["eventTypeGroups"];
|
||||
type EventTypeGroupProfile = EventTypeGroups[number]["profile"];
|
||||
interface EventTypeListHeadingProps {
|
||||
@@ -346,36 +346,70 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
|
||||
<Button type="button" size="icon" color="minimal" StartIcon={Icon.FiMoreHorizontal} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent portalled>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Link href={calLink}>
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Link href={calLink}>
|
||||
<Button
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
color="minimal"
|
||||
size="sm"
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
className="w-full rounded-none">
|
||||
{t("preview") as string}
|
||||
</Button>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
rel="noreferrer"
|
||||
target="_blank"
|
||||
type="button"
|
||||
color="minimal"
|
||||
size="sm"
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
className="w-full rounded-none">
|
||||
{t("preview") as string}
|
||||
className="w-full rounded-none text-left"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiClipboard}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(calLink);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}>
|
||||
{t("copy_link") as string}
|
||||
</Button>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
size="sm"
|
||||
className="w-full rounded-none text-left"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiClipboard}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(calLink);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}>
|
||||
{t("copy_link") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
{isNativeShare ? (
|
||||
</DropdownMenuItem>
|
||||
{isNativeShare ? (
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
size="sm"
|
||||
className="w-full rounded-none"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiUpload}
|
||||
onClick={() => {
|
||||
navigator
|
||||
.share({
|
||||
title: t("share"),
|
||||
text: t("share_event"),
|
||||
url: calLink,
|
||||
})
|
||||
.then(() => showToast(t("link_shared"), "success"))
|
||||
.catch(() => showToast(t("failed"), "error"));
|
||||
}}>
|
||||
{t("share") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
href={"/event-types/" + type.id}
|
||||
color="minimal"
|
||||
className="w-full rounded-none"
|
||||
StartIcon={Icon.FiEdit2}>
|
||||
{t("edit") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
@@ -383,59 +417,27 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
size="sm"
|
||||
className="w-full rounded-none"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiUpload}
|
||||
onClick={() => {
|
||||
navigator
|
||||
.share({
|
||||
title: t("share"),
|
||||
text: t("share_event"),
|
||||
url: calLink,
|
||||
})
|
||||
.then(() => showToast(t("link_shared"), "success"))
|
||||
.catch(() => showToast(t("failed"), "error"));
|
||||
}}>
|
||||
{t("share") as string}
|
||||
StartIcon={Icon.FiCopy}
|
||||
onClick={() => openModal(group, type)}>
|
||||
{t("duplicate") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
size="sm"
|
||||
href={"/event-types/" + type.id}
|
||||
color="minimal"
|
||||
className="w-full rounded-none"
|
||||
StartIcon={Icon.FiEdit2}>
|
||||
{t("edit") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
size="sm"
|
||||
className="w-full rounded-none"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiCopy}
|
||||
onClick={() => openModal(group, type)}>
|
||||
{t("duplicate") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeleteDialogOpen(true);
|
||||
setDeleteDialogTypeId(type.id);
|
||||
}}
|
||||
color="warn"
|
||||
size="sm"
|
||||
StartIcon={Icon.FiTrash}
|
||||
className="w-full rounded-none">
|
||||
{t("delete") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeleteDialogOpen(true);
|
||||
setDeleteDialogTypeId(type.id);
|
||||
}}
|
||||
color="warn"
|
||||
size="sm"
|
||||
StartIcon={Icon.FiTrash}
|
||||
className="w-full rounded-none">
|
||||
{t("delete") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
@@ -540,6 +542,7 @@ const EventTypesPage = () => {
|
||||
<title>Home | Cal.com</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<Shell
|
||||
heading={t("event_types_page_title") as string}
|
||||
subtitle={t("event_types_page_subtitle") as string}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
import UserV2OptInBanner from "@calcom/features/users/components/UserV2OptInBanner";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { MobileNavigationMoreItems } from "@calcom/ui/Shell";
|
||||
import { Shell } from "@calcom/ui/v2";
|
||||
|
||||
@@ -21,6 +21,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import type { AppRouter } from "@calcom/trpc/server/routers/_app";
|
||||
import { Alert } from "@calcom/ui/Alert";
|
||||
@@ -39,8 +40,6 @@ import Schedule from "@components/availability/Schedule";
|
||||
import { CalendarListContainer } from "@components/integrations/CalendarListContainer";
|
||||
import { UsernameAvailability } from "@components/ui/UsernameAvailability";
|
||||
|
||||
import { TRPCClientErrorLike } from "@trpc/client";
|
||||
|
||||
// Embed isn't applicable to onboarding, so ignore the rule
|
||||
/* eslint-disable @calcom/eslint/avoid-web-storage */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { useQuery } from "@tanstack/react-query";
|
||||
import React from "react";
|
||||
import { useQuery } from "react-query";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ export default function Settings() {
|
||||
const { data: routingForms } = trpc.useQuery([
|
||||
"viewer.appById",
|
||||
{
|
||||
appId: "routing_forms",
|
||||
appId: "routing-forms",
|
||||
},
|
||||
]);
|
||||
|
||||
@@ -19,7 +19,7 @@ export default function Settings() {
|
||||
<WebhookListContainer title="Event Webhooks" subtitle={t("receive_cal_meeting_data")} />
|
||||
{routingForms && (
|
||||
<WebhookListContainer
|
||||
appId="routing_forms"
|
||||
appId="routing-forms"
|
||||
title="Routing Webhooks"
|
||||
subtitle="Receive Routing Form responses at a specified URL, in real-time, when a Routing Form is submitted"
|
||||
/>
|
||||
|
||||
@@ -17,6 +17,7 @@ import TimezoneSelect, { ITimezone } from "react-timezone-select";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import showToast from "@calcom/lib/notification";
|
||||
import { setIs24hClockInLocalStorage } from "@calcom/lib/timeFormat";
|
||||
import prisma from "@calcom/prisma";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
@@ -229,7 +230,7 @@ function SettingsView(props: ComponentProps<typeof Settings> & { localeProp: str
|
||||
// Write time format to localStorage if available
|
||||
// Embed isn't applicable to profile pages. So ignore the rule
|
||||
// eslint-disable-next-line @calcom/eslint/avoid-web-storage
|
||||
window.localStorage.setItem("timeOption.is24hClock", selectedTimeFormat.value === 12 ? "false" : "true");
|
||||
setIs24hClockInLocalStorage(selectedTimeFormat.value === 24);
|
||||
|
||||
// TODO: Add validation
|
||||
|
||||
@@ -575,7 +576,7 @@ function SettingsView(props: ComponentProps<typeof Settings> & { localeProp: str
|
||||
* i18n should never be clubbed with other queries, so that it's caching can be managed independently.
|
||||
* We intend to not cache i18n query
|
||||
**/
|
||||
const WithQuery = withQuery(["viewer.public.i18n"], { context: { skipBatch: true } });
|
||||
const WithQuery = withQuery(["viewer.public.i18n"], { trpc: { context: { skipBatch: true } } });
|
||||
|
||||
export default function Settings(props: Props) {
|
||||
const { t } = useLocale();
|
||||
|
||||
@@ -26,7 +26,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { isBrowserLocale24h } from "@calcom/lib/timeFormat";
|
||||
import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
import prisma from "@calcom/prisma";
|
||||
import Button from "@calcom/ui/Button";
|
||||
@@ -203,7 +203,7 @@ export default function Success(props: SuccessProps) {
|
||||
// TODO: Add payment details
|
||||
});
|
||||
setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()));
|
||||
setIs24h(!!localStorage.getItem("timeOption.is24hClock"));
|
||||
setIs24h(!!getIs24hClockFromLocalStorage());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [eventType, needsConfirmation]);
|
||||
|
||||
|
||||
@@ -18,21 +18,25 @@ export default function Apps({ apps }: InferGetStaticPropsType<typeof getStaticP
|
||||
|
||||
return (
|
||||
<>
|
||||
<Shell isPublic large>
|
||||
<div className="text-md flex items-center gap-1 px-4 pb-3 pt-3 font-semibold md:px-8 lg:px-0 lg:pt-0">
|
||||
<Link href="/apps">
|
||||
<a className="inline-flex items-center justify-start gap-1 rounded-sm py-2 text-gray-900">
|
||||
<Icon.FiArrowLeft className="h-4 w-4" />
|
||||
{isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "}
|
||||
</a>
|
||||
</Link>
|
||||
{category && (
|
||||
<span className="flex gap-1 text-gray-600">
|
||||
<span> / </span>
|
||||
{t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<Shell
|
||||
isPublic
|
||||
backPath="/apps"
|
||||
heading={
|
||||
<>
|
||||
<Link href="/apps">
|
||||
<a className="inline-flex items-center justify-start gap-1 rounded-sm py-2 text-gray-900">
|
||||
{isLocaleReady ? t("app_store") : <SkeletonText className="h-4 w-24" />}{" "}
|
||||
</a>
|
||||
</Link>
|
||||
{category && (
|
||||
<span className="gap-1 text-gray-600">
|
||||
<span> / </span>
|
||||
{t("category_apps", { category: category[0].toUpperCase() + category?.slice(1) })}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
}
|
||||
large>
|
||||
<div className="mb-16">
|
||||
<div className="grid-col-1 grid grid-cols-1 gap-3 md:grid-cols-3">
|
||||
{apps.map((app) => {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useRouter } from "next/router";
|
||||
import { InferGetServerSidePropsType } from "next";
|
||||
import z from "zod";
|
||||
|
||||
import { InstallAppButton } from "@calcom/app-store/components";
|
||||
@@ -6,6 +6,7 @@ import { InstalledAppVariants } from "@calcom/app-store/utils";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { App } from "@calcom/types/App";
|
||||
import { AppGetServerSidePropsContext } from "@calcom/types/AppGetServerSideProps";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import SkeletonLoader from "@calcom/ui/apps/SkeletonLoader";
|
||||
import { Alert } from "@calcom/ui/v2/core/Alert";
|
||||
@@ -90,7 +91,7 @@ interface IntegrationsContainerProps {
|
||||
|
||||
const IntegrationsList = ({ data }: { data: inferQueryOutput<"viewer.integrations"> }) => {
|
||||
return (
|
||||
<List>
|
||||
<List noBorderTreatment>
|
||||
{data.items.map((item) => (
|
||||
<IntegrationListItem
|
||||
name={item.name}
|
||||
@@ -100,7 +101,7 @@ const IntegrationsList = ({ data }: { data: inferQueryOutput<"viewer.integration
|
||||
logo={item.logo}
|
||||
description={item.description}
|
||||
actions={
|
||||
<div className="flex w-32 justify-center">
|
||||
<div className="flex w-32 justify-end">
|
||||
<ConnectOrDisconnectIntegrationButton
|
||||
credentialIds={item.credentialIds}
|
||||
type={item.type}
|
||||
@@ -177,12 +178,8 @@ const querySchema = z.object({
|
||||
category: z.nativeEnum(InstalledAppVariants),
|
||||
});
|
||||
|
||||
export default function InstalledApps() {
|
||||
export default function InstalledApps({ category }: InferGetServerSidePropsType<typeof getServerSideProps>) {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const { category } = router.isReady
|
||||
? querySchema.parse(router.query)
|
||||
: { category: InstalledAppVariants.calendar as const };
|
||||
|
||||
return (
|
||||
<InstalledAppsLayout heading={t("installed_apps")} subtitle={t("manage_your_connected_apps")}>
|
||||
@@ -202,3 +199,30 @@ export default function InstalledApps() {
|
||||
</InstalledAppsLayout>
|
||||
);
|
||||
}
|
||||
|
||||
// Server side rendering
|
||||
export async function getServerSideProps(ctx: AppGetServerSidePropsContext) {
|
||||
// get return-to cookie and redirect if needed
|
||||
const { cookies } = ctx.req;
|
||||
if (cookies && cookies["return-to"]) {
|
||||
const returnTo = cookies["return-to"];
|
||||
if (returnTo) {
|
||||
ctx.res.setHeader("Set-Cookie", "return-to=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT");
|
||||
return {
|
||||
redirect: {
|
||||
destination: `${returnTo}`,
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
const params = querySchema.safeParse(ctx.params);
|
||||
|
||||
if (!params.success) return { notFound: true };
|
||||
|
||||
return {
|
||||
props: {
|
||||
category: params.data.category,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -78,7 +78,7 @@ export default function Login({
|
||||
|
||||
const LoginFooter = (
|
||||
<a href={`${WEBSITE_URL}/signup`} className="text-brand-500 font-medium">
|
||||
{t("i_dont_have_an_account")}
|
||||
{t("dont_have_an_account")}
|
||||
</a>
|
||||
);
|
||||
|
||||
@@ -140,7 +140,7 @@ export default function Login({
|
||||
{...form.register("email")}
|
||||
/>
|
||||
<div className="relative">
|
||||
<div className="absolute right-0 -top-[6px]">
|
||||
<div className="absolute right-0 -top-[6px] z-10">
|
||||
<Link href="/auth/forgot-password">
|
||||
<a tabIndex={-1} className="text-sm font-medium text-gray-600">
|
||||
{t("forgot")}
|
||||
|
||||
@@ -9,6 +9,7 @@ import { availabilityAsString } from "@calcom/lib/availability";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { stringOrNumber } from "@calcom/prisma/zod-utils";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import useMeQuery from "@calcom/trpc/react/hooks/useMeQuery";
|
||||
import type { Schedule as ScheduleType } from "@calcom/types/schedule";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import TimezoneSelect from "@calcom/ui/form/TimezoneSelect";
|
||||
@@ -18,11 +19,12 @@ import Switch from "@calcom/ui/v2/core/Switch";
|
||||
import VerticalDivider from "@calcom/ui/v2/core/VerticalDivider";
|
||||
import { Form, Label } from "@calcom/ui/v2/core/form/fields";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
import { SkeletonText } from "@calcom/ui/v2/core/skeleton";
|
||||
import { Skeleton, SkeletonText } from "@calcom/ui/v2/core/skeleton";
|
||||
|
||||
import { HttpError } from "@lib/core/http/error";
|
||||
|
||||
import EditableHeading from "@components/ui/EditableHeading";
|
||||
import { SelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader";
|
||||
|
||||
const querySchema = z.object({
|
||||
schedule: stringOrNumber,
|
||||
@@ -39,11 +41,12 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
const { t, i18n } = useLocale();
|
||||
const router = useRouter();
|
||||
const utils = trpc.useContext();
|
||||
const me = useMeQuery();
|
||||
|
||||
const { data, isLoading } = trpc.useQuery(["viewer.availability.schedule", { scheduleId: schedule }]);
|
||||
|
||||
const form = useForm<AvailabilityFormValues>();
|
||||
const { control, reset, setValue } = form;
|
||||
const { control, reset } = form;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isLoading && data) {
|
||||
@@ -79,22 +82,35 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
return (
|
||||
<Shell
|
||||
backPath="/availability"
|
||||
title={t("availability_title", { availabilityTitle: data?.schedule.name })}
|
||||
title={data?.schedule.name && data.schedule.name + " | " + t("availability")}
|
||||
heading={
|
||||
<EditableHeading title={data?.schedule.name || ""} onChange={(name) => setValue("name", name)} />
|
||||
<Controller
|
||||
control={form.control}
|
||||
name="name"
|
||||
render={({ field }) => <EditableHeading isReady={!isLoading} {...field} />}
|
||||
/>
|
||||
}
|
||||
subtitle={
|
||||
data ? (
|
||||
data.schedule.availability.map((availability) => (
|
||||
<span key={availability.id}>
|
||||
{availabilityAsString(availability, { locale: i18n.language })}
|
||||
<br />
|
||||
</span>
|
||||
))
|
||||
) : (
|
||||
<SkeletonText className="h-4 w-48" />
|
||||
)
|
||||
}
|
||||
subtitle={data?.schedule.availability.map((availability) => (
|
||||
<span key={availability.id}>
|
||||
{availabilityAsString(availability, { locale: i18n.language })}
|
||||
<br />
|
||||
</span>
|
||||
))}
|
||||
CTA={
|
||||
<div className="flex items-center justify-end">
|
||||
<div className="flex items-center rounded-md px-2 sm:hover:bg-gray-100">
|
||||
<Label htmlFor="hiddenSwitch" className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
|
||||
<Skeleton
|
||||
as={Label}
|
||||
htmlFor="hiddenSwitch"
|
||||
className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
|
||||
{t("set_to_default")}
|
||||
</Label>
|
||||
</Skeleton>
|
||||
<Switch
|
||||
id="hiddenSwitch"
|
||||
disabled={isLoading}
|
||||
@@ -132,7 +148,17 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
<h3 className="mb-5 text-base font-medium leading-6 text-gray-900">
|
||||
{t("change_start_end")}
|
||||
</h3>
|
||||
<Schedule control={control} name="schedule" />
|
||||
{typeof me.data?.weekStart === "string" && (
|
||||
<Schedule
|
||||
control={control}
|
||||
name="schedule"
|
||||
weekStart={
|
||||
["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"].indexOf(
|
||||
me.data?.weekStart
|
||||
) as 0 | 1 | 2 | 3 | 4 | 5 | 6
|
||||
}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
<div className="min-w-40 col-span-3 space-y-2 lg:col-span-1">
|
||||
@@ -147,11 +173,11 @@ export default function Availability({ schedule }: { schedule: number }) {
|
||||
value ? (
|
||||
<TimezoneSelect
|
||||
value={value}
|
||||
className="focus:border-brand mt-1 block rounded-md border-gray-300 text-sm"
|
||||
className="focus:border-brand mt-1 block w-72 rounded-md border-gray-300 text-sm"
|
||||
onChange={(timezone) => onChange(timezone.value)}
|
||||
/>
|
||||
) : (
|
||||
<SkeletonText className="h-6 w-full" />
|
||||
<SelectSkeletonLoader className="w-72" />
|
||||
)
|
||||
}
|
||||
/>
|
||||
|
||||
@@ -21,16 +21,32 @@ export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availab
|
||||
const meQuery = trpc.useQuery(["viewer.me"]);
|
||||
|
||||
const deleteMutation = trpc.useMutation("viewer.availability.schedule.delete", {
|
||||
onSuccess: async () => {
|
||||
await utils.invalidateQueries(["viewer.availability.list"]);
|
||||
showToast(t("schedule_deleted_successfully"), "success");
|
||||
onMutate: async ({ scheduleId }) => {
|
||||
await utils.cancelQuery(["viewer.availability.list"]);
|
||||
const previousValue = utils.getQueryData(["viewer.availability.list"]);
|
||||
if (previousValue) {
|
||||
const filteredValue = previousValue.schedules.filter(({ id }) => id !== scheduleId);
|
||||
utils.setQueryData(["viewer.availability.list"], { ...previousValue, schedules: filteredValue });
|
||||
}
|
||||
|
||||
return { previousValue };
|
||||
},
|
||||
onError: (err) => {
|
||||
|
||||
onError: (err, variables, context) => {
|
||||
if (context?.previousValue) {
|
||||
utils.setQueryData(["viewer.availability.list"], context.previousValue);
|
||||
}
|
||||
if (err instanceof HttpError) {
|
||||
const message = `${err.statusCode}: ${err.message}`;
|
||||
showToast(message, "error");
|
||||
}
|
||||
},
|
||||
onSettled: () => {
|
||||
utils.invalidateQueries(["viewer.availability.list"]);
|
||||
},
|
||||
onSuccess: () => {
|
||||
showToast(t("schedule_deleted_successfully"), "success");
|
||||
},
|
||||
});
|
||||
|
||||
// Adds smooth delete button - item fades and old item slides into place
|
||||
@@ -61,7 +77,6 @@ export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availab
|
||||
key={schedule.id}
|
||||
schedule={schedule}
|
||||
deleteFunction={deleteMutation.mutate}
|
||||
isDeleting={deleteMutation.isLoading}
|
||||
/>
|
||||
))}
|
||||
</ul>
|
||||
|
||||
@@ -20,13 +20,14 @@ import SkeletonLoader from "@components/v2/bookings/SkeletonLoader";
|
||||
type BookingListingStatus = inferQueryInput<"viewer.bookings">["status"];
|
||||
type BookingOutput = inferQueryOutput<"viewer.bookings">["bookings"][0];
|
||||
|
||||
const validStatuses = ["upcoming", "recurring", "past", "cancelled"] as const;
|
||||
const validStatuses = ["upcoming", "recurring", "past", "cancelled", "unconfirmed"] as const;
|
||||
|
||||
const descriptionByStatus: Record<BookingListingStatus, string> = {
|
||||
upcoming: "upcoming_bookings",
|
||||
recurring: "recurring_bookings",
|
||||
past: "past_bookings",
|
||||
cancelled: "cancelled_bookings",
|
||||
unconfirmed: "unconfirmed_bookings",
|
||||
};
|
||||
|
||||
const querySchema = z.object({
|
||||
@@ -89,11 +90,11 @@ export default function Bookings() {
|
||||
|
||||
return (
|
||||
<BookingLayout heading={t("bookings")} subtitle={t("bookings_description")}>
|
||||
<div className="flex w-full flex-1 flex-col" ref={animationParentRef}>
|
||||
<div className="flex w-full flex-col" ref={animationParentRef}>
|
||||
{query.status === "error" && (
|
||||
<Alert severity="error" title={t("something_went_wrong")} message={query.error.message} />
|
||||
)}
|
||||
{(query.status === "loading" || query.status === "idle") && <SkeletonLoader />}
|
||||
{(query.status === "loading" || query.isPaused) && <SkeletonLoader />}
|
||||
{query.status === "success" && !isEmpty && (
|
||||
<div className="pt-2 xl:pt-0">
|
||||
<WipeMyCalActionButton bookingStatus={status} bookingsEmpty={isEmpty} />
|
||||
@@ -102,7 +103,7 @@ export default function Bookings() {
|
||||
*/}
|
||||
|
||||
<div className="overflow-hidden rounded-md border border-gray-200">
|
||||
<table className="w-full">
|
||||
<table className="w-full max-w-full table-fixed">
|
||||
<tbody className="divide-y divide-gray-200 bg-white" data-testid="bookings">
|
||||
{query.data.pages.map((page, index) => (
|
||||
<Fragment key={index}>
|
||||
|
||||
@@ -70,6 +70,7 @@ export type FormValues = {
|
||||
periodCountCalendarDays: "1" | "0";
|
||||
periodDates: { startDate: Date; endDate: Date };
|
||||
seatsPerTimeSlot: number | null;
|
||||
seatsPerTimeSlotEnabled: boolean;
|
||||
minimumBookingNotice: number;
|
||||
beforeBufferTime: number;
|
||||
afterBufferTime: number;
|
||||
@@ -96,7 +97,8 @@ export type EventTypeSetupInfered = inferSSRProps<typeof getServerSideProps>;
|
||||
const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||
const { t } = useLocale();
|
||||
|
||||
const { eventType, locationOptions, team, teamMembers } = props;
|
||||
const { eventType: dbEventType, locationOptions, team, teamMembers } = props;
|
||||
const [eventType, setEventType] = useState(dbEventType);
|
||||
const animationParentRef = useRef(null);
|
||||
const router = useRouter();
|
||||
const { tabName } = querySchema.parse(router.query);
|
||||
@@ -106,7 +108,8 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||
}, [animationParentRef]);
|
||||
|
||||
const updateMutation = trpc.useMutation("viewer.eventTypes.update", {
|
||||
onSuccess: async ({ eventType }) => {
|
||||
onSuccess: async ({ eventType: newEventType }) => {
|
||||
setEventType({ ...eventType, slug: newEventType.slug });
|
||||
showToast(
|
||||
t("event_type_updated_successfully", {
|
||||
eventTypeTitle: eventType.title,
|
||||
@@ -153,6 +156,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||
endDate: periodDates.endDate,
|
||||
},
|
||||
schedulingType: eventType.schedulingType,
|
||||
minimumBookingNotice: eventType.minimumBookingNotice,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -220,6 +224,9 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
|
||||
locations,
|
||||
blockchainId,
|
||||
smartContractAddress,
|
||||
// We don't need to send it to the backend
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
seatsPerTimeSlotEnabled,
|
||||
...input
|
||||
} = values;
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { UserPlan } from "@prisma/client";
|
||||
import { useAutoAnimate } from "@formkit/auto-animate/react";
|
||||
import Head from "next/head";
|
||||
import Link from "next/link";
|
||||
import { useRouter } from "next/router";
|
||||
@@ -7,6 +7,7 @@ import React, { Fragment, useEffect, useState } from "react";
|
||||
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { inferQueryOutput, trpc } from "@calcom/trpc/react";
|
||||
import { TRPCClientError } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Badge, Button, ButtonGroup, Dialog, EmptyScreen, showToast, Switch, Tooltip } from "@calcom/ui/v2";
|
||||
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
|
||||
@@ -16,6 +17,7 @@ import Dropdown, {
|
||||
DropdownMenuItem,
|
||||
DropdownMenuSeparator,
|
||||
DropdownMenuTrigger,
|
||||
DropdownMenuPortal,
|
||||
} from "@calcom/ui/v2/core/Dropdown";
|
||||
import Shell from "@calcom/ui/v2/core/Shell";
|
||||
import CreateEventTypeButton from "@calcom/ui/v2/modules/event-types/CreateEventType";
|
||||
@@ -29,8 +31,6 @@ import Avatar from "@components/ui/Avatar";
|
||||
import AvatarGroup from "@components/ui/AvatarGroup";
|
||||
import SkeletonLoader from "@components/v2/eventtype/SkeletonLoader";
|
||||
|
||||
import { TRPCClientError } from "@trpc/react";
|
||||
|
||||
type EventTypeGroups = inferQueryOutput<"viewer.eventTypes">["eventTypeGroups"];
|
||||
type EventTypeGroupProfile = EventTypeGroups[number]["profile"];
|
||||
|
||||
@@ -52,7 +52,7 @@ const Item = ({ type, group, readOnly }: { type: EventType; group: EventTypeGrou
|
||||
const { t } = useLocale();
|
||||
|
||||
return (
|
||||
<Link href={`/event-types/${type.id}`}>
|
||||
<Link href={`/event-types/${type.id}?tabName=setup`}>
|
||||
<a
|
||||
className="flex-grow truncate text-sm"
|
||||
title={`${type.title} ${type.description ? `– ${type.description}` : ""}`}>
|
||||
@@ -82,6 +82,7 @@ const MemoizedItem = React.memo(Item);
|
||||
export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeListProps): JSX.Element => {
|
||||
const { t } = useLocale();
|
||||
const router = useRouter();
|
||||
const [parent] = useAutoAnimate<HTMLUListElement>();
|
||||
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||
const [deleteDialogTypeId, setDeleteDialogTypeId] = useState(0);
|
||||
const utils = trpc.useContext();
|
||||
@@ -91,8 +92,8 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
await utils.cancelQuery(["viewer.eventTypes"]);
|
||||
await utils.invalidateQueries(["viewer.eventTypes"]);
|
||||
},
|
||||
onSettled: async () => {
|
||||
await utils.invalidateQueries(["viewer.eventTypes"]);
|
||||
onSettled: () => {
|
||||
utils.invalidateQueries(["viewer.eventTypes"]);
|
||||
},
|
||||
});
|
||||
|
||||
@@ -107,7 +108,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
},
|
||||
});
|
||||
|
||||
function moveEventType(index: number, increment: 1 | -1) {
|
||||
async function moveEventType(index: number, increment: 1 | -1) {
|
||||
const newList = [...types];
|
||||
|
||||
const type = types[index];
|
||||
@@ -117,24 +118,19 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
newList[index + increment] = type;
|
||||
}
|
||||
|
||||
utils.cancelQuery(["viewer.eventTypes"]);
|
||||
utils.setQueryData(["viewer.eventTypes"], (data) => {
|
||||
// tRPC is very strict with the return signature...
|
||||
if (!data)
|
||||
return {
|
||||
eventTypeGroups: [],
|
||||
profiles: [],
|
||||
viewer: { canAddEvents: true, plan: UserPlan.PRO },
|
||||
};
|
||||
return {
|
||||
...data,
|
||||
eventTypesGroups: [
|
||||
...data.eventTypeGroups.slice(0, groupIndex),
|
||||
await utils.cancelQuery(["viewer.eventTypes"]);
|
||||
|
||||
const previousValue = utils.getQueryData(["viewer.eventTypes"]);
|
||||
if (previousValue) {
|
||||
utils.setQueryData(["viewer.eventTypes"], {
|
||||
...previousValue,
|
||||
eventTypeGroups: [
|
||||
...previousValue.eventTypeGroups.slice(0, groupIndex),
|
||||
{ ...group, eventTypes: newList },
|
||||
...data.eventTypeGroups.slice(groupIndex + 1),
|
||||
...previousValue.eventTypeGroups.slice(groupIndex + 1),
|
||||
],
|
||||
};
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
mutation.mutate({
|
||||
ids: newList.map((type) => type.id),
|
||||
@@ -201,7 +197,7 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
const lastItem = types[types.length - 1];
|
||||
return (
|
||||
<div className="mb-16 flex overflow-hidden rounded-md border border-gray-200 bg-white">
|
||||
<ul className="w-full divide-y divide-neutral-200" data-testid="event-types">
|
||||
<ul ref={parent} className="!static w-full divide-y divide-neutral-200" data-testid="event-types">
|
||||
{types.map((type, index) => {
|
||||
const embedLink = `${group.profile.slug}/${type.slug}`;
|
||||
const calLink = `${CAL_URL}/${embedLink}`;
|
||||
@@ -296,8 +292,9 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
<DropdownMenuItem>
|
||||
<DropdownItem
|
||||
type="button"
|
||||
href={"/event-types/" + type.id}
|
||||
StartIcon={Icon.FiEdit2}>
|
||||
data-testid={"event-type-edit-" + type.id}
|
||||
StartIcon={Icon.FiEdit2}
|
||||
onClick={() => router.push("/event-types/" + type.id)}>
|
||||
{t("edit") as string}
|
||||
</DropdownItem>
|
||||
</DropdownMenuItem>
|
||||
@@ -347,90 +344,92 @@ export const EventTypeList = ({ group, groupIndex, readOnly, types }: EventTypeL
|
||||
<DropdownMenuTrigger asChild data-testid={"event-type-options-" + type.id}>
|
||||
<Button type="button" size="icon" color="secondary" StartIcon={Icon.FiMoreHorizontal} />
|
||||
</DropdownMenuTrigger>
|
||||
<DropdownMenuContent portalled>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Link href={calLink}>
|
||||
<a target="_blank">
|
||||
<DropdownMenuPortal>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Link href={calLink}>
|
||||
<a target="_blank">
|
||||
<Button
|
||||
color="minimal"
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
className="w-full rounded-none">
|
||||
{t("preview") as string}
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
className="w-full rounded-none text-left"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiClipboard}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(calLink);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}>
|
||||
{t("copy_link") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
{isNativeShare ? (
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
StartIcon={Icon.FiExternalLink}
|
||||
className="w-full rounded-none">
|
||||
{t("preview") as string}
|
||||
className="w-full rounded-none"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiUpload}
|
||||
onClick={() => {
|
||||
navigator
|
||||
.share({
|
||||
title: t("share"),
|
||||
text: t("share_event"),
|
||||
url: calLink,
|
||||
})
|
||||
.then(() => showToast(t("link_shared"), "success"))
|
||||
.catch(() => showToast(t("failed"), "error"));
|
||||
}}>
|
||||
{t("share") as string}
|
||||
</Button>
|
||||
</a>
|
||||
</Link>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
className="w-full rounded-none text-left"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiClipboard}
|
||||
onClick={() => {
|
||||
navigator.clipboard.writeText(calLink);
|
||||
showToast(t("link_copied"), "success");
|
||||
}}>
|
||||
{t("copy_link") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
{isNativeShare ? (
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
onClick={() => router.push("/event-types/" + type.id)}
|
||||
color="minimal"
|
||||
className="w-full rounded-none"
|
||||
StartIcon={Icon.FiEdit}>
|
||||
{t("edit") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
className="w-full rounded-none"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiUpload}
|
||||
onClick={() => {
|
||||
navigator
|
||||
.share({
|
||||
title: t("share"),
|
||||
text: t("share_event"),
|
||||
url: calLink,
|
||||
})
|
||||
.then(() => showToast(t("link_shared"), "success"))
|
||||
.catch(() => showToast(t("failed"), "error"));
|
||||
}}>
|
||||
{t("share") as string}
|
||||
StartIcon={Icon.FiCopy}
|
||||
onClick={() => openModal(group, type)}>
|
||||
{t("duplicate") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
) : null}
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
href={"/event-types/" + type.id}
|
||||
color="minimal"
|
||||
className="w-full"
|
||||
StartIcon={Icon.FiEdit}>
|
||||
{t("edit") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
type="button"
|
||||
color="minimal"
|
||||
className="w-full rounded-none"
|
||||
data-testid={"event-type-duplicate-" + type.id}
|
||||
StartIcon={Icon.FiCopy}
|
||||
onClick={() => openModal(group, type)}>
|
||||
{t("duplicate") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeleteDialogOpen(true);
|
||||
setDeleteDialogTypeId(type.id);
|
||||
}}
|
||||
color="destructive"
|
||||
StartIcon={Icon.FiTrash}
|
||||
className="w-full rounded-none">
|
||||
{t("delete") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
<DropdownMenuSeparator className="h-px bg-gray-200" />
|
||||
<DropdownMenuItem className="outline-none">
|
||||
<Button
|
||||
onClick={() => {
|
||||
setDeleteDialogOpen(true);
|
||||
setDeleteDialogTypeId(type.id);
|
||||
}}
|
||||
color="destructive"
|
||||
StartIcon={Icon.FiTrash}
|
||||
className="w-full rounded-none">
|
||||
{t("delete") as string}
|
||||
</Button>
|
||||
</DropdownMenuItem>
|
||||
</DropdownMenuContent>
|
||||
</DropdownMenuPortal>
|
||||
</Dropdown>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import { useState } from "react";
|
||||
import { HelpScout, useChat } from "react-live-chat-loader";
|
||||
|
||||
import { classNames } from "@calcom/lib";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Button } from "@calcom/ui/v2";
|
||||
import Meta from "@calcom/ui/v2/core/Meta";
|
||||
import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout";
|
||||
|
||||
interface CtaRowProps {
|
||||
title: string;
|
||||
description: string;
|
||||
children: React.ReactNode;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
const CtaRow = ({ title, description, className, children }: CtaRowProps) => {
|
||||
return (
|
||||
<>
|
||||
<section className={classNames("flex flex-col sm:flex-row", className)}>
|
||||
<div>
|
||||
<h2 className="font-medium">{title}</h2>
|
||||
<p>{description}</p>
|
||||
</div>
|
||||
<div className="flex-shrink-0 pt-3 sm:ml-auto sm:pt-0 sm:pl-3">{children}</div>
|
||||
</section>
|
||||
<hr className="border-neutral-200" />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const BillingView = () => {
|
||||
const { t } = useLocale();
|
||||
const { data: user } = trpc.useQuery(["viewer.me"]);
|
||||
const isPro = user?.plan === "PRO";
|
||||
const [, loadChat] = useChat();
|
||||
const [showChat, setShowChat] = useState(false);
|
||||
|
||||
const onContactSupportClick = () => {
|
||||
setShowChat(true);
|
||||
loadChat({ open: true });
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Meta title={t("billing")} description={t("manage_billing_description")} />
|
||||
<div className="space-y-6 text-sm sm:space-y-8">
|
||||
{!isPro && (
|
||||
<CtaRow title={t("billing_freeplan_title")} description={t("billing_freeplan_description")}>
|
||||
<form target="_blank" method="POST" action="/api/upgrade">
|
||||
<Button type="submit" EndIcon={Icon.FiExternalLink}>
|
||||
{t("billing_freeplan_cta")}
|
||||
</Button>
|
||||
</form>
|
||||
</CtaRow>
|
||||
)}
|
||||
|
||||
<CtaRow
|
||||
className={classNames(!isPro && "pointer-events-none opacity-30")}
|
||||
title={t("billing_manage_details_title")}
|
||||
description={t("billing_manage_details_description")}>
|
||||
<Button
|
||||
color={isPro ? "primary" : "secondary"}
|
||||
href="/api/integrations/stripepayment/portal"
|
||||
target="_blank"
|
||||
EndIcon={Icon.FiExternalLink}>
|
||||
{t("billing_portal")}
|
||||
</Button>
|
||||
</CtaRow>
|
||||
|
||||
<CtaRow title={t("billing_help_title")} description={t("billing_help_description")}>
|
||||
<Button color="secondary" onClick={onContactSupportClick}>
|
||||
{t("billing_help_cta")}
|
||||
</Button>
|
||||
</CtaRow>
|
||||
{showChat && <HelpScout color="#292929" icon="message" horizontalPosition="right" zIndex="1" />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
BillingView.getLayout = getLayout;
|
||||
|
||||
export default BillingView;
|
||||
@@ -39,16 +39,16 @@ const ApiKeysView = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Meta title="api_keys" description="webhooks_description" />
|
||||
<Meta title="API Keys" description="API keys allow other apps to communicate with Cal.com" />
|
||||
|
||||
<LicenseRequired>
|
||||
<>
|
||||
{isLoading && <SkeletonLoader />}
|
||||
<div>
|
||||
{data?.length ? (
|
||||
{isLoading ? null : data?.length ? (
|
||||
<>
|
||||
<div className="mt-6 mb-8 rounded-md border">
|
||||
{data?.map((apiKey, index) => (
|
||||
{data.map((apiKey, index) => (
|
||||
<ApiKeyListItem
|
||||
key={apiKey.id}
|
||||
apiKey={apiKey}
|
||||
|
||||
@@ -165,7 +165,7 @@ const AppearanceView = () => {
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Button color="primary" className="mt-8">
|
||||
<Button type="submit" color="primary" className="mt-8">
|
||||
{t("update")}
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
@@ -7,6 +7,7 @@ import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui";
|
||||
import { Alert } from "@calcom/ui/v2";
|
||||
import Badge from "@calcom/ui/v2/core/Badge";
|
||||
import EmptyScreen from "@calcom/ui/v2/core/EmptyScreen";
|
||||
import Meta from "@calcom/ui/v2/core/Meta";
|
||||
@@ -23,7 +24,7 @@ import { CalendarSwitch } from "@components/v2/settings/CalendarSwitch";
|
||||
const SkeletonLoader = () => {
|
||||
return (
|
||||
<SkeletonContainer>
|
||||
<div className="mt-6 mb-8 space-y-6 divide-y">
|
||||
<div className="mt-6 mb-8 space-y-6">
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
<SkeletonText className="h-8 w-full" />
|
||||
@@ -89,7 +90,30 @@ const CalendarsView = () => {
|
||||
<List>
|
||||
{data.connectedCalendars.map((item) => (
|
||||
<Fragment key={item.credentialId}>
|
||||
{item.calendars && (
|
||||
{item.error && item.error.message && (
|
||||
<Alert
|
||||
severity="warning"
|
||||
key={item.credentialId}
|
||||
title={t("calendar_connection_fail")}
|
||||
message={item.error.message}
|
||||
className="mb-4 mt-4"
|
||||
actions={
|
||||
<>
|
||||
{/* @TODO: add a reconnect button, that calls add api and delete old credential */}
|
||||
<DisconnectIntegration
|
||||
credentialId={item.credentialId}
|
||||
trashIcon
|
||||
onSuccess={() => query.refetch()}
|
||||
buttonProps={{
|
||||
className: "border border-gray-300 py-[2px]",
|
||||
color: "secondary",
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
{item?.error === undefined && item.calendars && (
|
||||
<ListItem expanded className="flex-col">
|
||||
<div className="flex w-full flex-1 items-center space-x-3 pb-5 pl-1 pt-1 rtl:space-x-reverse">
|
||||
{
|
||||
@@ -132,6 +156,7 @@ const CalendarsView = () => {
|
||||
externalId={cal.externalId}
|
||||
title={cal.name || "Nameless calendar"}
|
||||
type={item.integration.type}
|
||||
isSelected={cal.isSelected}
|
||||
defaultSelected={cal.externalId === data?.destinationCalendar?.externalId}
|
||||
/>
|
||||
))}
|
||||
@@ -153,6 +178,22 @@ const CalendarsView = () => {
|
||||
/>
|
||||
);
|
||||
}}
|
||||
error={() => {
|
||||
return (
|
||||
<Alert
|
||||
message={
|
||||
<Trans i18nKey="fetching_calendars_error">
|
||||
An error ocurred while fetching your Calendars.
|
||||
<a className="cursor-pointer underline" onClick={() => query.refetch()}>
|
||||
try again
|
||||
</a>
|
||||
.
|
||||
</Trans>
|
||||
}
|
||||
severity="error"
|
||||
/>
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</>
|
||||
);
|
||||
|
||||
@@ -51,7 +51,7 @@ const ConferencingLayout = () => {
|
||||
|
||||
return (
|
||||
<div className="w-full bg-white sm:mx-0 xl:mt-0">
|
||||
<Meta title="conferencing" description="conferencing_description" />
|
||||
<Meta title="Conferencing" description="Add your favourite video conferencing apps for your meetings" />
|
||||
<List roundContainer={true}>
|
||||
{apps?.items &&
|
||||
apps.items
|
||||
|
||||
@@ -3,6 +3,7 @@ import { useMemo } from "react";
|
||||
import { useForm, Controller } from "react-hook-form";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { setIs24hClockInLocalStorage } from "@calcom/lib/timeFormat";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Button } from "@calcom/ui/v2/core/Button";
|
||||
import Meta from "@calcom/ui/v2/core/Meta";
|
||||
@@ -35,7 +36,7 @@ interface GeneralViewProps {
|
||||
localeProp: string;
|
||||
}
|
||||
|
||||
const WithQuery = withQuery(["viewer.public.i18n"], { context: { skipBatch: true } });
|
||||
const WithQuery = withQuery(["viewer.public.i18n"], { trpc: { context: { skipBatch: true } } });
|
||||
|
||||
const GeneralQueryView = () => {
|
||||
return (
|
||||
@@ -110,6 +111,8 @@ const GeneralView = ({ localeProp }: GeneralViewProps) => {
|
||||
<Form
|
||||
form={formMethods}
|
||||
handleSubmit={(values) => {
|
||||
setIs24hClockInLocalStorage(values.timeFormat.value === 24);
|
||||
|
||||
mutation.mutate({
|
||||
...values,
|
||||
locale: values.locale.value,
|
||||
@@ -188,7 +191,7 @@ const GeneralView = ({ localeProp }: GeneralViewProps) => {
|
||||
</>
|
||||
)}
|
||||
/>
|
||||
<Button color="primary" className="mt-8">
|
||||
<Button color="primary" type="submit" className="mt-8">
|
||||
<>{t("update")}</>
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
import crypto from "crypto";
|
||||
import { signOut } from "next-auth/react";
|
||||
import { useRef, useState, BaseSyntheticEvent } from "react";
|
||||
import { useRef, useState, BaseSyntheticEvent, useEffect } from "react";
|
||||
import { Controller, useForm } from "react-hook-form";
|
||||
|
||||
import { ErrorCode } from "@calcom/lib/auth";
|
||||
import { WEBSITE_URL } from "@calcom/lib/constants";
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { TRPCClientErrorLike } from "@calcom/trpc/client";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
@@ -21,6 +22,7 @@ import showToast from "@calcom/ui/v2/core/notifications";
|
||||
import { SkeletonContainer, SkeletonText, SkeletonButton, SkeletonAvatar } from "@calcom/ui/v2/core/skeleton";
|
||||
|
||||
import TwoFactor from "@components/auth/TwoFactor";
|
||||
import { UsernameAvailability } from "@components/ui/UsernameAvailability";
|
||||
|
||||
const SkeletonLoader = () => {
|
||||
return (
|
||||
@@ -47,6 +49,7 @@ interface DeleteAccountValues {
|
||||
const ProfileView = () => {
|
||||
const { t } = useLocale();
|
||||
const utils = trpc.useContext();
|
||||
const usernameRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
const { data: user, isLoading } = trpc.useQuery(["viewer.me"]);
|
||||
const mutation = trpc.useMutation("viewer.updateProfile", {
|
||||
@@ -58,10 +61,16 @@ const ProfileView = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const [confirmPasswordOpen, setConfirmPasswordOpen] = useState(false);
|
||||
const [confirmPasswordErrorMessage, setConfirmPasswordDeleteErrorMessage] = useState("");
|
||||
const [deleteAccountOpen, setDeleteAccountOpen] = useState(false);
|
||||
const [hasDeleteErrors, setHasDeleteErrors] = useState(false);
|
||||
const [deleteErrorMessage, setDeleteErrorMessage] = useState("");
|
||||
|
||||
const [currentUsername, setCurrentUsername] = useState<string | undefined>(user?.username || undefined);
|
||||
const [inputUsernameValue, setInputUsernameValue] = useState(currentUsername);
|
||||
useEffect(() => {
|
||||
if (user?.username) setCurrentUsername(user?.username);
|
||||
}, [user?.username]);
|
||||
const form = useForm<DeleteAccountValues>();
|
||||
|
||||
const emailMd5 = crypto
|
||||
@@ -81,6 +90,16 @@ const ProfileView = () => {
|
||||
}
|
||||
};
|
||||
|
||||
const confirmPasswordMutation = trpc.useMutation("viewer.auth.verifyPassword", {
|
||||
onSuccess() {
|
||||
mutation.mutate(formMethods.getValues());
|
||||
setConfirmPasswordOpen(false);
|
||||
},
|
||||
onError() {
|
||||
setConfirmPasswordDeleteErrorMessage(t("incorrect_password"));
|
||||
},
|
||||
});
|
||||
|
||||
const onDeleteMeErrorMutation = (error: TRPCClientErrorLike<AppRouter>) => {
|
||||
setHasDeleteErrors(true);
|
||||
setDeleteErrorMessage(errorMessages[error.message]);
|
||||
@@ -93,6 +112,12 @@ const ProfileView = () => {
|
||||
},
|
||||
});
|
||||
|
||||
const onConfirmPassword = (e: Event | React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
e.preventDefault();
|
||||
const password = passwordRef.current.value;
|
||||
confirmPasswordMutation.mutate({ passwordInput: password });
|
||||
};
|
||||
|
||||
const onConfirmButton = (e: Event | React.MouseEvent<HTMLElement, MouseEvent>) => {
|
||||
e.preventDefault();
|
||||
const totpCode = form.getValues("totpCode");
|
||||
@@ -105,15 +130,26 @@ const ProfileView = () => {
|
||||
deleteMeMutation.mutate({ password, totpCode });
|
||||
};
|
||||
|
||||
const formMethods = useForm({
|
||||
defaultValues: {
|
||||
avatar: user?.avatar || "",
|
||||
username: user?.username || "",
|
||||
name: user?.name || "",
|
||||
email: user?.email || "",
|
||||
bio: user?.bio || "",
|
||||
},
|
||||
});
|
||||
const formMethods = useForm<{
|
||||
avatar?: string;
|
||||
username?: string;
|
||||
name?: string;
|
||||
email?: string;
|
||||
bio?: string;
|
||||
}>();
|
||||
|
||||
const { reset } = formMethods;
|
||||
|
||||
useEffect(() => {
|
||||
if (user)
|
||||
reset({
|
||||
avatar: user?.avatar || "",
|
||||
username: user?.username || "",
|
||||
name: user?.name || "",
|
||||
email: user?.email || "",
|
||||
bio: user?.bio || "",
|
||||
});
|
||||
}, [reset, user]);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
const passwordRef = useRef<HTMLInputElement>(null!);
|
||||
@@ -126,15 +162,27 @@ const ProfileView = () => {
|
||||
[ErrorCode.InternalServerError]: `${t("something_went_wrong")} ${t("please_try_again_and_contact_us")}`,
|
||||
[ErrorCode.ThirdPartyIdentityProviderEnabled]: t("account_created_with_identity_provider"),
|
||||
};
|
||||
const onSuccessfulUsernameUpdate = async () => {
|
||||
showToast(t("settings_updated_successfully"), "success");
|
||||
await utils.invalidateQueries(["viewer.me"]);
|
||||
};
|
||||
|
||||
if (isLoading) return <SkeletonLoader />;
|
||||
const onErrorInUsernameUpdate = () => {
|
||||
showToast(t("error_updating_settings"), "error");
|
||||
};
|
||||
|
||||
if (isLoading || !user) return <SkeletonLoader />;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Form
|
||||
form={formMethods}
|
||||
handleSubmit={(values) => {
|
||||
mutation.mutate(values);
|
||||
if (values.email !== user?.email) {
|
||||
setConfirmPasswordOpen(true);
|
||||
} else {
|
||||
mutation.mutate(values);
|
||||
}
|
||||
}}>
|
||||
<Meta title="Profile" description="Manage settings for your cal profile" />
|
||||
<div className="flex items-center">
|
||||
@@ -159,73 +207,28 @@ const ProfileView = () => {
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
<Controller
|
||||
control={formMethods.control}
|
||||
name="username"
|
||||
render={({ field: { value } }) => (
|
||||
<div className="mt-8">
|
||||
<TextField
|
||||
data-testid="username-input"
|
||||
name="username"
|
||||
label={t("personal_cal_url")}
|
||||
addOnLeading="https://cal.com/"
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
formMethods.setValue("username", e?.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={formMethods.control}
|
||||
name="name"
|
||||
render={({ field: { value, onChange } }) => (
|
||||
<div className="mt-8">
|
||||
<TextField
|
||||
label={t("full_name")}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
onChange(e?.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={formMethods.control}
|
||||
name="email"
|
||||
render={({ field: { value } }) => (
|
||||
<div className="mt-8">
|
||||
<TextField
|
||||
name="email"
|
||||
label={t("email")}
|
||||
value={value}
|
||||
hint={t("change_email_hint")}
|
||||
onChange={(e) => {
|
||||
formMethods.setValue("email", e?.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<Controller
|
||||
control={formMethods.control}
|
||||
name="bio"
|
||||
render={({ field: { value } }) => (
|
||||
<div className="mt-8">
|
||||
<TextField
|
||||
name="bio"
|
||||
label={t("about")}
|
||||
hint={t("bio_hint")}
|
||||
value={value}
|
||||
onChange={(e) => {
|
||||
formMethods.setValue("bio", e?.target.value);
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
<div className="mt-8">
|
||||
<UsernameAvailability
|
||||
currentUsername={currentUsername}
|
||||
setCurrentUsername={setCurrentUsername}
|
||||
inputUsernameValue={inputUsernameValue}
|
||||
usernameRef={usernameRef}
|
||||
setInputUsernameValue={setInputUsernameValue}
|
||||
onSuccessMutation={onSuccessfulUsernameUpdate}
|
||||
onErrorMutation={onErrorInUsernameUpdate}
|
||||
user={user}
|
||||
/>
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<TextField label={t("full_name")} {...formMethods.register("name")} />
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<TextField label={t("email")} hint={t("change_email_hint")} {...formMethods.register("email")} />
|
||||
</div>
|
||||
<div className="mt-8">
|
||||
<TextField label={t("about")} hint={t("bio_hint")} {...formMethods.register("bio")} />
|
||||
</div>
|
||||
|
||||
<Button color="primary" className="mt-8" type="submit" loading={mutation.isLoading}>
|
||||
{t("update")}
|
||||
</Button>
|
||||
@@ -279,6 +282,32 @@ const ProfileView = () => {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</Form>
|
||||
|
||||
{/* If changing email, confirm password */}
|
||||
<Dialog open={confirmPasswordOpen} onOpenChange={setConfirmPasswordOpen}>
|
||||
<DialogContent
|
||||
title={t("confirm_password")}
|
||||
description={t("confirm_password_change_email")}
|
||||
type="creation"
|
||||
actionText={t("confirm")}
|
||||
Icon={Icon.FiAlertTriangle}
|
||||
actionOnClick={(e) => e && onConfirmPassword(e)}>
|
||||
<>
|
||||
<PasswordField
|
||||
data-testid="password"
|
||||
name="password"
|
||||
id="password"
|
||||
type="password"
|
||||
autoComplete="current-password"
|
||||
required
|
||||
label="Password"
|
||||
ref={passwordRef}
|
||||
/>
|
||||
|
||||
{confirmPasswordErrorMessage && <Alert severity="error" title={confirmPasswordErrorMessage} />}
|
||||
</>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -7,7 +7,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import Meta from "@calcom/ui/v2/core/Meta";
|
||||
import { Form, TextField } from "@calcom/ui/v2/core/form/fields";
|
||||
import { Form, PasswordField } from "@calcom/ui/v2/core/form/fields";
|
||||
import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout";
|
||||
import showToast from "@calcom/ui/v2/core/notifications";
|
||||
|
||||
@@ -48,7 +48,7 @@ const PasswordView = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<Meta title="Password" description="Manage settings for your account passwords" />
|
||||
<Meta title={t("password")} description={t("password_description")} />
|
||||
{user && user.identityProvider !== IdentityProvider.CAL ? (
|
||||
<div>
|
||||
<div className="mt-6">
|
||||
@@ -65,22 +65,17 @@ const PasswordView = () => {
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<Form<ChangePasswordFormValues> form={formMethods} handleSubmit={handleSubmit}>
|
||||
<Form form={formMethods} handleSubmit={handleSubmit}>
|
||||
<div className="max-w-[38rem] sm:flex sm:space-x-4">
|
||||
<div className="flex-grow">
|
||||
<TextField {...register("oldPassword")} label={t("old_password")} type="password" />
|
||||
<PasswordField {...register("oldPassword")} label={t("old_password")} />
|
||||
</div>
|
||||
<div className="flex-grow">
|
||||
<TextField
|
||||
{...register("newPassword")}
|
||||
label={t("new_password")}
|
||||
type="password"
|
||||
placeholder={t("secure_password")}
|
||||
/>
|
||||
<PasswordField {...register("newPassword")} label={t("new_password")} />
|
||||
</div>
|
||||
</div>
|
||||
<p className="text-sm text-gray-600">
|
||||
<Trans i18nKey="valid_password">
|
||||
<Trans i18nKey="invalid_password_hint">
|
||||
Password must be at least at least 7 characters, mix of uppercase & lowercase letters, and
|
||||
contain at least 1 number
|
||||
</Trans>
|
||||
@@ -89,8 +84,8 @@ const PasswordView = () => {
|
||||
<Button
|
||||
color="primary"
|
||||
className="mt-8"
|
||||
disabled={isSubmitting || mutation.isLoading}
|
||||
onClick={() => handleSubmit(formMethods.getValues())}>
|
||||
type="submit"
|
||||
disabled={isSubmitting || mutation.isLoading}>
|
||||
{t("update")}
|
||||
</Button>
|
||||
</Form>
|
||||
|
||||
@@ -69,10 +69,9 @@ const CreateNewTeamPage = () => {
|
||||
data-testid="onboarding"
|
||||
key={router.asPath}>
|
||||
<Head>
|
||||
<title>{t("create_new_team")}</title>
|
||||
<title>Create a new Team</title>
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<div className="mx-auto px-4 py-24">
|
||||
<div className="relative">
|
||||
<div className="sm:mx-auto sm:w-full sm:max-w-[600px]">
|
||||
|
||||
@@ -26,7 +26,7 @@ import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import useTheme from "@calcom/lib/hooks/useTheme";
|
||||
import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
|
||||
import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calcom/lib/telemetry";
|
||||
import { isBrowserLocale24h } from "@calcom/lib/timeFormat";
|
||||
import { getIs24hClockFromLocalStorage, isBrowserLocale24h } from "@calcom/lib/timeFormat";
|
||||
import { localStorage } from "@calcom/lib/webstorage";
|
||||
import prisma from "@calcom/prisma";
|
||||
import Button from "@calcom/ui/Button";
|
||||
@@ -201,7 +201,7 @@ export default function Success(props: SuccessProps) {
|
||||
// TODO: Add payment details
|
||||
});
|
||||
setDate(date.tz(localStorage.getItem("timeOption.preferredTimeZone") || dayjs.tz.guess()));
|
||||
setIs24h(!!localStorage.getItem("timeOption.is24hClock"));
|
||||
setIs24h(!!getIs24hClockFromLocalStorage());
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [eventType, needsConfirmation]);
|
||||
|
||||
@@ -263,7 +263,7 @@ export default function Success(props: SuccessProps) {
|
||||
{userIsOwner && !isEmbed && (
|
||||
<div className="mt-2 ml-4 -mb-4">
|
||||
<Link href={eventType.recurringEvent?.count ? "/bookings/recurring" : "/bookings/upcoming"}>
|
||||
<a className="mt-2 inline-flex px-1 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-800">
|
||||
<a className="mt-2 inline-flex px-1 py-2 text-sm text-gray-500 hover:bg-gray-100 hover:text-gray-800 dark:hover:bg-transparent dark:hover:text-white">
|
||||
<Icon.FiChevronLeft className="h-5 w-5" /> {t("back_to_bookings")}
|
||||
</a>
|
||||
</Link>
|
||||
@@ -411,7 +411,7 @@ export default function Success(props: SuccessProps) {
|
||||
!isCancelled &&
|
||||
(!isCancellationMode ? (
|
||||
<>
|
||||
<hr className="border-bookinglightest" />
|
||||
<hr className="border-bookinglightest dark:border-darkgray-300" />
|
||||
<div className="py-8 text-center last:pb-0">
|
||||
<span className="text-gray-900 ltr:mr-2 rtl:ml-2 dark:text-gray-50">
|
||||
{t("need_to_make_a_change")}
|
||||
@@ -448,7 +448,7 @@ export default function Success(props: SuccessProps) {
|
||||
))}
|
||||
{userIsOwner && !needsConfirmation && !isCancellationMode && !isCancelled && (
|
||||
<>
|
||||
<hr className="border-bookinglightest" />
|
||||
<hr className="border-bookinglightest dark:border-darkgray-300" />
|
||||
<div className="text-bookingdark align-center flex flex-row justify-center pt-8">
|
||||
<span className="flex self-center font-medium text-gray-700 ltr:mr-2 rtl:ml-2 dark:text-gray-50">
|
||||
{t("add_to_calendar")}
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import { useState } from "react";
|
||||
|
||||
import { useLocale } from "@calcom/lib/hooks/useLocale";
|
||||
import { trpc } from "@calcom/trpc/react";
|
||||
import { Icon } from "@calcom/ui/Icon";
|
||||
import { Shell } from "@calcom/ui/v2";
|
||||
import { Alert } from "@calcom/ui/v2/core/Alert";
|
||||
import Button from "@calcom/ui/v2/core/Button";
|
||||
import EmptyScreen from "@calcom/ui/v2/core/EmptyScreen";
|
||||
|
||||
import SkeletonLoaderTeamList from "@components/team/SkeletonloaderTeamList";
|
||||
import TeamCreateModal from "@components/team/TeamCreateModal";
|
||||
import TeamList from "@components/team/TeamList";
|
||||
|
||||
function Teams() {
|
||||
const { t } = useLocale();
|
||||
const [showCreateTeamModal, setShowCreateTeamModal] = useState(false);
|
||||
const [errorMessage, setErrorMessage] = useState("");
|
||||
|
||||
const { data, isLoading } = trpc.useQuery(["viewer.teams.list"], {
|
||||
onError: (e) => {
|
||||
setErrorMessage(e.message);
|
||||
},
|
||||
});
|
||||
|
||||
const teams = data?.filter((m) => m.accepted) || [];
|
||||
const invites = data?.filter((m) => !m.accepted) || [];
|
||||
|
||||
return (
|
||||
<Shell
|
||||
heading={t("teams")}
|
||||
subtitle={t("create_manage_teams_collaborative")}
|
||||
CTA={
|
||||
<Button type="button" onClick={() => setShowCreateTeamModal(true)}>
|
||||
<Icon.FiPlus className="inline-block h-3.5 w-3.5 text-white group-hover:text-black ltr:mr-2 rtl:ml-2" />
|
||||
{t("new")}
|
||||
</Button>
|
||||
}>
|
||||
<>
|
||||
{!!errorMessage && <Alert severity="error" title={errorMessage} />}
|
||||
{showCreateTeamModal && (
|
||||
<TeamCreateModal isOpen={showCreateTeamModal} onClose={() => setShowCreateTeamModal(false)} />
|
||||
)}
|
||||
{invites.length > 0 && (
|
||||
<div className="mb-4">
|
||||
<h1 className="mb-2 text-lg font-medium">{t("open_invitations")}</h1>
|
||||
<TeamList teams={invites} />
|
||||
</div>
|
||||
)}
|
||||
{isLoading && <SkeletonLoaderTeamList />}
|
||||
{!teams.length && !isLoading && (
|
||||
<EmptyScreen
|
||||
Icon={Icon.FiUsers}
|
||||
headline={t("no_teams")}
|
||||
description={t("no_teams_description")}
|
||||
buttonRaw={
|
||||
<Button color="secondary" onClick={() => setShowCreateTeamModal(true)}>
|
||||
{t("create_team")}
|
||||
</Button>
|
||||
}
|
||||
buttonOnClick={() => setShowCreateTeamModal(true)}
|
||||
/>
|
||||
)}
|
||||
{teams.length > 0 && <TeamList teams={teams} />}
|
||||
</>
|
||||
</Shell>
|
||||
);
|
||||
}
|
||||
|
||||
Teams.requiresLicense = false;
|
||||
|
||||
export default Teams;
|
||||
@@ -35,10 +35,10 @@ test.describe("Change username on settings", () => {
|
||||
const usernameInput = page.locator("[data-testid=username-input]");
|
||||
|
||||
await usernameInput.fill("demousernamex");
|
||||
|
||||
await page.click("[data-testid=update-username-btn]");
|
||||
await Promise.all([
|
||||
page.click("[data-testid=save-username]"),
|
||||
page.waitForResponse("**/viewer.updateProfile*"),
|
||||
page.click('button[type="submit"]'),
|
||||
]);
|
||||
|
||||
const newUpdatedUser = await prisma.user.findUniqueOrThrow({
|
||||
|
||||
@@ -2,13 +2,8 @@ import { expect, Page } from "@playwright/test";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
|
||||
// these tests need rewrite
|
||||
// eslint-disable-next-line playwright/no-skipped-test
|
||||
test.skip();
|
||||
|
||||
async function chooseEmbedType(page: Page, embedType: string) {
|
||||
const $embedTypeSelector = await page.locator(`[data-testid=${embedType}]`);
|
||||
$embedTypeSelector.click();
|
||||
function chooseEmbedType(page: Page, embedType: string) {
|
||||
page.locator(`[data-testid=${embedType}]`).click();
|
||||
}
|
||||
|
||||
async function gotToPreviewTab(page: Page) {
|
||||
@@ -111,7 +106,7 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
basePage: "/event-types",
|
||||
});
|
||||
|
||||
await chooseEmbedType(page, "inline");
|
||||
chooseEmbedType(page, "inline");
|
||||
|
||||
await expectToBeNavigatingToEmbedCodeAndPreviewDialog(page, {
|
||||
embedUrl,
|
||||
@@ -119,19 +114,18 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
basePage: "/event-types",
|
||||
});
|
||||
|
||||
/*await expectToContainValidCode(page, { embedType: "inline" });
|
||||
await expectToContainValidCode(page, { embedType: "inline" });
|
||||
|
||||
await gotToPreviewTab(page);
|
||||
|
||||
await expectToContainValidPreviewIframe(page, {
|
||||
embedType: "inline",
|
||||
calLink: `${pro.username}/30-min`,
|
||||
});*/
|
||||
});
|
||||
});
|
||||
|
||||
test("open Embed Dialog and choose floating-popup for First Event Type", async ({ page, users }) => {
|
||||
const [pro] = users.get();
|
||||
|
||||
const embedUrl = await clickFirstEventTypeEmbedButton(page);
|
||||
|
||||
await expectToBeNavigatingToEmbedTypesDialog(page, {
|
||||
@@ -139,20 +133,20 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
basePage: "/event-types",
|
||||
});
|
||||
|
||||
await chooseEmbedType(page, "floating-popup");
|
||||
chooseEmbedType(page, "floating-popup");
|
||||
|
||||
await expectToBeNavigatingToEmbedCodeAndPreviewDialog(page, {
|
||||
embedUrl,
|
||||
embedType: "floating-popup",
|
||||
basePage: "/event-types",
|
||||
});
|
||||
/*await expectToContainValidCode(page, { embedType: "floating-popup" });
|
||||
await expectToContainValidCode(page, { embedType: "floating-popup" });
|
||||
|
||||
await gotToPreviewTab(page);
|
||||
await expectToContainValidPreviewIframe(page, {
|
||||
embedType: "floating-popup",
|
||||
calLink: `${pro.username}/30-min`,
|
||||
});*/
|
||||
});
|
||||
});
|
||||
|
||||
test("open Embed Dialog and choose element-click for First Event Type", async ({ page, users }) => {
|
||||
@@ -164,20 +158,20 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
basePage: "/event-types",
|
||||
});
|
||||
|
||||
await chooseEmbedType(page, "element-click");
|
||||
chooseEmbedType(page, "element-click");
|
||||
|
||||
await expectToBeNavigatingToEmbedCodeAndPreviewDialog(page, {
|
||||
embedUrl,
|
||||
embedType: "element-click",
|
||||
basePage: "/event-types",
|
||||
});
|
||||
/*await expectToContainValidCode(page, { embedType: "element-click" });
|
||||
await expectToContainValidCode(page, { embedType: "element-click" });
|
||||
|
||||
await gotToPreviewTab(page);
|
||||
await expectToContainValidPreviewIframe(page, {
|
||||
embedType: "element-click",
|
||||
calLink: `${pro.username}/30-min`,
|
||||
});*/
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -200,7 +194,7 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
basePage,
|
||||
});
|
||||
|
||||
await chooseEmbedType(page, "inline");
|
||||
chooseEmbedType(page, "inline");
|
||||
|
||||
await expectToBeNavigatingToEmbedCodeAndPreviewDialog(page, {
|
||||
embedUrl,
|
||||
@@ -208,7 +202,7 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
embedType: "inline",
|
||||
});
|
||||
|
||||
/*await expectToContainValidCode(page, {
|
||||
await expectToContainValidCode(page, {
|
||||
embedType: "inline",
|
||||
});
|
||||
|
||||
@@ -217,7 +211,7 @@ test.describe("Embed Code Generator Tests", () => {
|
||||
await expectToContainValidPreviewIframe(page, {
|
||||
embedType: "inline",
|
||||
calLink: decodeURIComponent(embedUrl),
|
||||
});*/
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { expect } from "@playwright/test";
|
||||
|
||||
import { WEBAPP_URL } from "@calcom/lib/constants";
|
||||
|
||||
import { randomString } from "../lib/random";
|
||||
import { test } from "./lib/fixtures";
|
||||
|
||||
@@ -87,7 +89,7 @@ test.describe("Event Types tests", () => {
|
||||
);
|
||||
const href = await firstElement.getAttribute("href");
|
||||
if (!href) throw new Error("No href found for event type");
|
||||
const [eventTypeId] = href.split("/").reverse();
|
||||
const [eventTypeId] = new URL(WEBAPP_URL + href).pathname.split("/").reverse();
|
||||
const firstTitle = await page.locator(`[data-testid=event-type-title-${eventTypeId}]`).innerText();
|
||||
const firstFullSlug = await page.locator(`[data-testid=event-type-slug-${eventTypeId}]`).innerText();
|
||||
const firstSlug = firstFullSlug.split("/")[2];
|
||||
|
||||
@@ -3,6 +3,7 @@ import type Prisma from "@prisma/client";
|
||||
import { Prisma as PrismaType, UserPlan } from "@prisma/client";
|
||||
import { hash } from "bcryptjs";
|
||||
|
||||
import dayjs from "@calcom/dayjs";
|
||||
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
@@ -257,7 +258,7 @@ const createUser = async (
|
||||
password: await hashPassword(uname),
|
||||
emailVerified: new Date(),
|
||||
completedOnboarding: opts?.completedOnboarding ?? true,
|
||||
timeZone: opts?.timeZone ?? TimeZoneEnum.UK,
|
||||
timeZone: opts?.timeZone ?? dayjs.tz.guess(),
|
||||
locale: opts?.locale ?? "en",
|
||||
schedules:
|
||||
opts?.completedOnboarding ?? true
|
||||
|
||||
@@ -6,7 +6,7 @@ import { v4 as uuidv4 } from "uuid";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
import { createHttpServer, selectFirstAvailableTimeSlotNextMonth, todo, waitFor } from "./lib/testUtils";
|
||||
import { todo } from "./lib/testUtils";
|
||||
|
||||
declare let global: {
|
||||
E2E_EMAILS?: ({ text: string } | Record<string, unknown>)[];
|
||||
@@ -182,7 +182,7 @@ test.fixme("Integrations", () => {
|
||||
},
|
||||
token: {
|
||||
url: "https://zoom.us/oauth/token",
|
||||
verify({ requestHeaders, code }) {
|
||||
verify({ requestHeaders }) {
|
||||
const authorization = requestHeaders.get("authorization").replace("Basic ", "");
|
||||
const clientPair = Buffer.from(authorization, "base64").toString();
|
||||
const [clientId, clientSecret] = clientPair.split(":");
|
||||
@@ -361,75 +361,4 @@ test.fixme("Integrations", () => {
|
||||
todo("Can add CalDav Calendar");
|
||||
|
||||
todo("Can add Apple Calendar");
|
||||
|
||||
test("add webhook & test that creating an event triggers a webhook call", async ({
|
||||
page,
|
||||
users,
|
||||
}, testInfo) => {
|
||||
const webhookReceiver = createHttpServer();
|
||||
const user = await users.create();
|
||||
const [eventType] = user.eventTypes;
|
||||
await user.login();
|
||||
await page.goto(`/settings/developer`);
|
||||
|
||||
// --- add webhook
|
||||
await page.click('[data-testid="new_webhook"]');
|
||||
|
||||
await expect(page.locator(`[data-testid='WebhookDialogForm']`)).toBeVisible();
|
||||
|
||||
await page.fill('[name="subscriberUrl"]', webhookReceiver.url);
|
||||
|
||||
await page.fill('[name="secret"]', "secret");
|
||||
|
||||
await page.click("[type=submit]");
|
||||
|
||||
// dialog is closed
|
||||
await expect(page.locator(`[data-testid='WebhookDialogForm']`)).not.toBeVisible();
|
||||
// page contains the url
|
||||
expect(page.locator(`text='${webhookReceiver.url}'`)).toBeDefined();
|
||||
|
||||
// --- Book the first available day next month in the pro user's "30min"-event
|
||||
await page.goto(`/${user.username}/${eventType.slug}`);
|
||||
await selectFirstAvailableTimeSlotNextMonth(page);
|
||||
|
||||
// --- fill form
|
||||
await page.fill('[name="name"]', "Test Testson");
|
||||
await page.fill('[name="email"]', "test@example.com");
|
||||
await page.press('[name="email"]', "Enter");
|
||||
|
||||
// --- check that webhook was called
|
||||
await waitFor(() => {
|
||||
expect(webhookReceiver.requestList.length).toBe(1);
|
||||
});
|
||||
|
||||
const [request] = webhookReceiver.requestList;
|
||||
const body = request.body as any;
|
||||
|
||||
// remove dynamic properties that differs depending on where you run the tests
|
||||
const dynamic = "[redacted/dynamic]";
|
||||
body.createdAt = dynamic;
|
||||
body.payload.startTime = dynamic;
|
||||
body.payload.endTime = dynamic;
|
||||
body.payload.location = dynamic;
|
||||
for (const attendee of body.payload.attendees) {
|
||||
attendee.timeZone = dynamic;
|
||||
attendee.language = dynamic;
|
||||
}
|
||||
body.payload.organizer.email = dynamic;
|
||||
body.payload.organizer.timeZone = dynamic;
|
||||
body.payload.organizer.language = dynamic;
|
||||
body.payload.uid = dynamic;
|
||||
body.payload.bookingId = dynamic;
|
||||
body.payload.additionalInformation = dynamic;
|
||||
body.payload.requiresConfirmation = dynamic;
|
||||
body.payload.eventTypeId = dynamic;
|
||||
|
||||
// if we change the shape of our webhooks, we can simply update this by clicking `u`
|
||||
// console.log("BODY", body);
|
||||
// Text files shouldn't have platform specific suffixes
|
||||
testInfo.snapshotSuffix = "";
|
||||
expect(JSON.stringify(body)).toMatchSnapshot(`webhookResponse.txt`);
|
||||
|
||||
webhookReceiver.close();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import { expect } from "@playwright/test";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
import { createHttpServer, selectFirstAvailableTimeSlotNextMonth, waitFor } from "./lib/testUtils";
|
||||
|
||||
test("add webhook & test that creating an event triggers a webhook call", async ({
|
||||
page,
|
||||
users,
|
||||
}, testInfo) => {
|
||||
const webhookReceiver = createHttpServer();
|
||||
const user = await users.create();
|
||||
const [eventType] = user.eventTypes;
|
||||
await user.login();
|
||||
await page.goto(`/settings/developer/webhooks`);
|
||||
|
||||
// --- add webhook
|
||||
await page.click('[data-testid="new_webhook"]');
|
||||
|
||||
await page.fill('[name="subscriberUrl"]', webhookReceiver.url);
|
||||
|
||||
await page.fill('[name="secret"]', "secret");
|
||||
|
||||
await Promise.all([
|
||||
page.click("[type=submit]"),
|
||||
page.waitForNavigation({
|
||||
url: (url) => url.pathname.endsWith("/settings/developer/webhooks"),
|
||||
}),
|
||||
]);
|
||||
|
||||
// page contains the url
|
||||
expect(page.locator(`text='${webhookReceiver.url}'`)).toBeDefined();
|
||||
|
||||
// --- Book the first available day next month in the pro user's "30min"-event
|
||||
await page.goto(`/${user.username}/${eventType.slug}`);
|
||||
await selectFirstAvailableTimeSlotNextMonth(page);
|
||||
|
||||
// --- fill form
|
||||
await page.fill('[name="name"]', "Test Testson");
|
||||
await page.fill('[name="email"]', "test@example.com");
|
||||
await page.press('[name="email"]', "Enter");
|
||||
|
||||
// --- check that webhook was called
|
||||
await waitFor(() => {
|
||||
expect(webhookReceiver.requestList.length).toBe(1);
|
||||
});
|
||||
|
||||
const [request] = webhookReceiver.requestList;
|
||||
const body = request.body as any;
|
||||
|
||||
// remove dynamic properties that differs depending on where you run the tests
|
||||
const dynamic = "[redacted/dynamic]";
|
||||
body.createdAt = dynamic;
|
||||
body.payload.startTime = dynamic;
|
||||
body.payload.endTime = dynamic;
|
||||
body.payload.location = dynamic;
|
||||
for (const attendee of body.payload.attendees) {
|
||||
attendee.timeZone = dynamic;
|
||||
attendee.language = dynamic;
|
||||
}
|
||||
body.payload.organizer.email = dynamic;
|
||||
body.payload.organizer.timeZone = dynamic;
|
||||
body.payload.organizer.language = dynamic;
|
||||
body.payload.uid = dynamic;
|
||||
body.payload.bookingId = dynamic;
|
||||
body.payload.additionalInformation = dynamic;
|
||||
body.payload.requiresConfirmation = dynamic;
|
||||
body.payload.eventTypeId = dynamic;
|
||||
|
||||
// if we change the shape of our webhooks, we can simply update this by clicking `u`
|
||||
// console.log("BODY", body);
|
||||
// Text files shouldn't have platform specific suffixes
|
||||
testInfo.snapshotSuffix = "";
|
||||
expect(JSON.stringify(body)).toMatchSnapshot(`webhookResponse.txt`);
|
||||
|
||||
webhookReceiver.close();
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
{"triggerEvent":"BOOKING_CREATED","createdAt":"[redacted/dynamic]","payload":{"type":"30 min","title":"30 min between PRO and Test Testson","description":"","additionalNotes":"","customInputs":{},"startTime":"[redacted/dynamic]","endTime":"[redacted/dynamic]","organizer":{"name":"PRO","email":"[redacted/dynamic]","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"},"attendees":[{"email":"test@example.com","name":"Test Testson","timeZone":"[redacted/dynamic]","language":"[redacted/dynamic]"}],"location":"[redacted/dynamic]","destinationCalendar":null,"hideCalendarNotes":false,"requiresConfirmation":"[redacted/dynamic]","eventTypeId":"[redacted/dynamic]","uid":"[redacted/dynamic]","eventTitle":"30 min","eventDescription":null,"price":0,"currency":"usd","length":30,"bookingId":"[redacted/dynamic]","metadata":{},"status":"ACCEPTED","additionalInformation":"[redacted/dynamic]"}}
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "استخدم الرابط أدناه لإعادة تعيين كلمة المرور",
|
||||
"hey_there": "مرحبًا،",
|
||||
"forgot_your_password_calcom": "نسيت كلمة المرور الخاصة بك؟ - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | نوع الحدث",
|
||||
"delete_webhook_confirmation_message": "هل أنت متأكد أنك تريد حذف الويب هوك هذا؟ لن تتلقى بعد الآن بيانات اجتماعات Cal.com على عنوان URL محدد، وفي الوقت الفعلي، عندما تتم جدولة حدث أو يتم إلغاؤه.",
|
||||
"confirm_delete_webhook": "نعم، قم بحذف الويبهوك",
|
||||
"edit_webhook": "تعديل الويبهوك",
|
||||
@@ -143,6 +142,7 @@
|
||||
"view_public_page": "عرض الصفحة العامة",
|
||||
"sign_out": "تسجيل الخروج",
|
||||
"add_another": "أضف واحدًا آخر",
|
||||
"install_another": "تثبيت آخر",
|
||||
"until": "حتى",
|
||||
"powered_by": "مدعوم بواسطة",
|
||||
"unavailable": "غير متاح",
|
||||
@@ -157,6 +157,7 @@
|
||||
"checkbox": "مربع الاختيار",
|
||||
"is_required": "مطلوب",
|
||||
"required": "مطلوب",
|
||||
"optional": "اختياري",
|
||||
"input_type": "نوع الإدخال",
|
||||
"rejected": "مرفوض",
|
||||
"unconfirmed": "غير مؤكد",
|
||||
@@ -227,7 +228,9 @@
|
||||
"connect_your_calendar_instructions": "اربط التقويم لديك للتحقق تلقائيًا من الأوقات المشغولة والأحداث الجديدة أثناء جدولتها.",
|
||||
"set_up_later": "الإعداد لاحقًا",
|
||||
"current_time": "الوقت الحالي",
|
||||
"details": "التفاصيل",
|
||||
"welcome": "مرحبًا",
|
||||
"welcome_back": "أهلاً بك من جديد",
|
||||
"welcome_to_calcom": "مرحبًا بك في Cal.com",
|
||||
"welcome_instructions": "أخبرنا باسمك وبالمنطقة الزمنية التي توجد فيها. ستتمكن لاحقًا من تعديل هذا.",
|
||||
"connect_caldav": "الاتصال بخادم CalDav",
|
||||
@@ -240,6 +243,7 @@
|
||||
"when": "متى",
|
||||
"where": "أين",
|
||||
"add_to_calendar": "إضافة إلى التقويم",
|
||||
"add_another_calendar": "إضافة رزنامة أخرى",
|
||||
"other": "آخر",
|
||||
"emailed_you_and_attendees": "لقد أرسلنا إليك وإلى الحضور الآخرين دعوة للتقويم عبر البريد الإلكتروني تتضمن كل التفاصيل.",
|
||||
"emailed_you_and_attendees_recurring": "لقد أرسلنا إليك وإلى الحضور الآخرين دعوة تقويم عبر البريد الإلكتروني لأول هذه الأحداث المتكررة.",
|
||||
@@ -261,6 +265,8 @@
|
||||
"enter_new_password": "أدخل كلمة المرور الجديدة التي ترغب في تعيينها لحسابك.",
|
||||
"reset_password": "إعادة تعيين كلمة المرور",
|
||||
"change_your_password": "تغيير كلمة المرور",
|
||||
"show_password": "عرض كلمة المرور",
|
||||
"hide_password": "إخفاء كلمة المرور",
|
||||
"try_again": "حاول مجددًا",
|
||||
"request_is_expired": "انتهت صلاحية ذلك الطلب.",
|
||||
"reset_instructions": "أدخل عنوان البريد الإلكتروني المرتبط بحسابك وسنرسل إليك رابطًا لإعادة تعيين كلمة المرور.",
|
||||
@@ -289,7 +295,7 @@
|
||||
"reschedule_instead": "بدلًا من ذلك، يمكنك إعادة جدولته.",
|
||||
"event_is_in_the_past": "الحدث في الماضي",
|
||||
"cancelling_event_recurring": "إن هذا الحدث أحد الأمثلة على حدث متكرر.",
|
||||
"cancelling_all_recurring": "هذه هي الحالات المتبقية في الحدث المتكرر.",
|
||||
"cancelling_all_recurring": "هذه هي كل الأمثلة المتبقية في الحدث المتكرر.",
|
||||
"error_with_status_code_occured": "حدث خطأ برمز الحالة {{status}}.",
|
||||
"booking_already_cancelled": "تم إلغاء هذا الحجز بالفعل",
|
||||
"go_back_home": "العودة إلى الشاشة الرئيسية",
|
||||
@@ -306,6 +312,7 @@
|
||||
"recurring_bookings": "بمجرد أن يحجز شخص ما اجتماعًا متكررًا معك، سيظهر هنا.",
|
||||
"past_bookings": "ستظهر هنا عمليات حجزك السابقة.",
|
||||
"cancelled_bookings": "ستظهر هنا عمليات حجزك التي تم إلغاؤها.",
|
||||
"unconfirmed_bookings": "ستظهر هنا كل حجوزات غير المؤكدة بعد.",
|
||||
"on": "في",
|
||||
"and": "و",
|
||||
"calendar_shows_busy_between": "يظهر التقويم أنك مشغول بين",
|
||||
@@ -346,6 +353,7 @@
|
||||
"booking_cancelled": "تم إلغاء الحجز",
|
||||
"booking_rescheduled": "تمت إعادة جدولة الحجز",
|
||||
"booking_created": "تم إنشاء الحجز",
|
||||
"meeting_ended": "انتهى الاجتماع",
|
||||
"form_submitted": "تم إرسال النموذج",
|
||||
"event_triggers": "مشغلات الحدث",
|
||||
"subscriber_url": "عنوان URL للمشترك",
|
||||
@@ -488,6 +496,9 @@
|
||||
"lets_create_first_administrator_user": "لنقم بإنشاء المستخدم المسؤول الأول.",
|
||||
"new_member": "العضو الجديد",
|
||||
"invite": "دعوة",
|
||||
"add_team_members": "إضافة أعضاء الفريق",
|
||||
"add_team_members_description": "قم بدعوة الآخرين للانضمام إلى فريقك",
|
||||
"add_team_member": "إضافة عضو فريق",
|
||||
"invite_new_member": "دعوة عضو جديد",
|
||||
"invite_new_team_member": "دعوة شخص ما إلى فريقك.",
|
||||
"change_member_role": "تغيير دور العضو في الفريق",
|
||||
@@ -600,6 +611,7 @@
|
||||
"create_first_team_and_invite_others": "قم بإنشاء فريقك الأول ودعوة المستخدمين الآخرين للعمل معك.",
|
||||
"create_team_to_get_started": "قم بإنشاء فريق للبدء",
|
||||
"teams": "الفرق",
|
||||
"team": "فريق",
|
||||
"team_billing": "الفوترة الخاصة بالفريق",
|
||||
"upgrade_to_flexible_pro_title": "لقد قمنا بتغيير الفوترة الخاصة بالفرق",
|
||||
"upgrade_to_flexible_pro_message": "يوجد أعضاء في فريقك من دون مقاعد. قم بترقية خطة Pro خاصتك لتوفير المقاعد الناقصة.",
|
||||
@@ -620,7 +632,9 @@
|
||||
"billing": "الفوترة",
|
||||
"manage_your_billing_info": "قم بإدارة معلومات الفوترة لديك وإلغاء اشتراكك.",
|
||||
"availability": "الأوقات المتاحة",
|
||||
"edit_availability": "تعديل التوفر",
|
||||
"configure_availability": "اضبط الأوقات التي تكون متاحًا فيها للحجز.",
|
||||
"copy_times_to": "نسخ الأوقات إلى",
|
||||
"change_weekly_schedule": "تغيير جدولك الأسبوعي",
|
||||
"logo": "الشعار",
|
||||
"error": "خطأ",
|
||||
@@ -635,6 +649,7 @@
|
||||
"event_name_tooltip": "الاسم الذي سيظهر في التقاويم",
|
||||
"meeting_with_user": "الاجتماع مع {ATTENDEE}",
|
||||
"additional_inputs": "مدخلات إضافية",
|
||||
"additional_input_description": "يتطلب من المجدول إدخال مدخلات إضافية قبل تأكيد الحجز",
|
||||
"label": "العلامة",
|
||||
"placeholder": "العنصر النائب",
|
||||
"type": "النوع",
|
||||
@@ -649,6 +664,8 @@
|
||||
"disable_guests": "تعطيل خاصية الضيوف",
|
||||
"disable_guests_description": "قم بتعطيل إضافة مزيد من الضيوف أثناء الحجز.",
|
||||
"private_link": "إنشاء رابط خاص",
|
||||
"private_link_label": "رابط خاص",
|
||||
"private_link_hint": "سيتم إعادة توليد الرابط الخاص بك بعد كل استخدام",
|
||||
"copy_private_link": "نسخ الرابط الخاص",
|
||||
"private_link_description": "إنشاء عنوان URL خاص لمشاركته دون الكشف عن اسم المستخدم الخاص بك في Cal",
|
||||
"invitees_can_schedule": "يمكن للمدعوين الجدولة",
|
||||
@@ -690,16 +707,21 @@
|
||||
"delete_account_confirmation_message": "هل تريد بالتأكيد حذف حسابك على Cal.com؟ أي شخص شاركت رابط حسابك معه لن يستطيع بعد الآن الحجز باستخدامه، وستفقد أي تفضيلات حفظتها.",
|
||||
"integrations": "التكاملات",
|
||||
"apps": "التطبيقات",
|
||||
"category_apps": "تطبيقات {{category}}",
|
||||
"app_store": "متجر التطبيقات",
|
||||
"app_store_description": "الربط بين الناس، والتكنولوجيا، ومكان العمل.",
|
||||
"settings": "الإعدادات",
|
||||
"event_type_moved_successfully": "تم نقل نوع الحدث بنجاح",
|
||||
"next_step_text": "الخطوة التالية",
|
||||
"next_step": "تخطي الخطوة",
|
||||
"prev_step": "الخطوة السابقة",
|
||||
"install": "تثبيت",
|
||||
"installed": "تم التثبيت",
|
||||
"active_install_one": "{{count}} تثبيت نشط",
|
||||
"active_install_other": "{{count}} تثبيت نشط",
|
||||
"globally_install": "تم تثبيته حول العالم",
|
||||
"app_successfully_installed": "تم تثبيت التطبيق بنجاح",
|
||||
"app_could_not_be_installed": "تعذر تثبيت التطبيق",
|
||||
"disconnect": "فصل",
|
||||
"embed_your_calendar": "تضمين تقويمك في صفحة الويب لديك",
|
||||
"connect_your_favourite_apps": "الربط مع تطبيقاتك المفضلة.",
|
||||
@@ -710,6 +732,7 @@
|
||||
"connect_additional_calendar": "ربط رزنامة إضافية",
|
||||
"conferencing": "المؤتمرات عبر الفيديو",
|
||||
"calendar": "التقويم",
|
||||
"payments": "الدفعات",
|
||||
"not_installed": "لم يتم التثبيت",
|
||||
"error_password_mismatch": "كلمات المرور غير متطابقة.",
|
||||
"error_required_field": "هذا الحقل مطلوب.",
|
||||
@@ -743,9 +766,22 @@
|
||||
"import_from": "الاستيراد من",
|
||||
"access_token": "Access token",
|
||||
"visit_roadmap": "المخطط",
|
||||
"featured_categories": "الفئات البارزة",
|
||||
"popular_categories": "الفئات الشائعة",
|
||||
"number_apps_one": "تطبيق {{count}}",
|
||||
"number_apps_other": "تطبيقات {{count}}",
|
||||
"trending_apps": "التطبيقات الرائجة",
|
||||
"explore_apps": "تطبيقات {{category}}",
|
||||
"installed_apps": "التطبيقات المثبتة",
|
||||
"no_category_apps": "لا تطبيقات {{category}}",
|
||||
"no_category_apps_description_calendar": "أضف تطبيق رزنامة للتحقق من أي تضارب لمنع أي حجوزات مزدوجة",
|
||||
"no_category_apps_description_conferencing": "حاول إضافة تطبيق مؤتمرات لدمج مكالمة الفيديو مع عملائك",
|
||||
"no_category_apps_description_payment": "أضف تطبيق دفع لتسهيل التعاملات المالية بينك وبين عملائك",
|
||||
"no_category_apps_description_other": "أضف أي نوع آخر من التطبيقات للقيام بأي شيء",
|
||||
"installed_app_calendar_description": "اضبط الرزنامة (الرزنامات) للتحقق من أي تضارب لمنع الحجوزات المزدوجة.",
|
||||
"installed_app_conferencing_description": "أضف تطبيقات مؤتمرات الفيديو المفضلة لديك لاجتماعاتك",
|
||||
"installed_app_payment_description": "قم بإعداد أي خدمات معالجة دفع تود استخدامها عند طلب النقود من العملاء.",
|
||||
"installed_app_other_description": "جميع تطبيقاتك المثبتة من الفئات الأخرى.",
|
||||
"empty_installed_apps_headline": "لم يتم تثبيت أي تطبيق",
|
||||
"empty_installed_apps_description": "تعطيك التطبيقات القدرة على تعزيز سير عملك وتحسين الجدولة في حياتك بشدة.",
|
||||
"empty_installed_apps_button": "استكشف متجر التطبيقات (App Store)",
|
||||
@@ -766,6 +802,7 @@
|
||||
"remove": "إزالة",
|
||||
"add": "إضافة",
|
||||
"installed_one": "تم التثبيت",
|
||||
"installed_other": "{{count}} مثبت",
|
||||
"verify_wallet": "تأكيد المحفظة",
|
||||
"connect_metamask": "توصيل Metamask",
|
||||
"create_events_on": "إنشاء أحداث في:",
|
||||
@@ -783,6 +820,8 @@
|
||||
"set_to_default": "تعيين إلى الافتراضي",
|
||||
"new_schedule_btn": "جدول جديد",
|
||||
"add_new_schedule": "إضافة جدول جديد",
|
||||
"add_new_calendar": "إضافة رزنامة جديدة",
|
||||
"set_calendar": "حدد أين سيتم إضافة الأحداث الجديدة عند الحجز.",
|
||||
"delete_schedule": "حذف الجدول",
|
||||
"schedule_created_successfully": "تم إنشاء جدول {{scheduleName}} بنجاح",
|
||||
"availability_updated_successfully": "تم تحديث الأوقات المتاحة بنجاح",
|
||||
@@ -877,7 +916,7 @@
|
||||
"choose_ways_put_cal_site": "اختر واحدة من الطرق التالية لوضع Cal على موقعك الإلكتروني.",
|
||||
"setting_up_zapier": "إعداد دمج Zapier الخاص بك",
|
||||
"generate_api_key": "إنشاء مفتاح API",
|
||||
"generate_api_key_description": "إنشاء مفتاح API لاستخدامه مع Cal.com في",
|
||||
"generate_api_key_description": "إنشاء مفتاح API لاستخدامه مع Cal.com على",
|
||||
"your_unique_api_key": "مفتاح API الفريد الخاص بك",
|
||||
"copy_safe_api_key": "انسخ مفتاح API هذا واحفظه في مكان آمن. إذا فقدت هذا المفتاح، يتوجب عليك إنشاء مفتاح جديد.",
|
||||
"zapier_setup_instructions": "<0>سجل الدخول إلى حساب Zapier الخاص بك وأنشئ Zap جديد.</0><1>حدد Cal.com كتطبيق Trigger لديك. اختر أيضًا حدث Trigger.</1><2>اختر حسابك ثم أدخل مفتاح API الفريد الخاص بك.</2><3>اختبر Trigger الخاص بك.</3><4>أنت جاهز!</4>",
|
||||
@@ -936,6 +975,7 @@
|
||||
"workflows": "سير العمل",
|
||||
"new_workflow_btn": "سير عمل جديد",
|
||||
"add_new_workflow": "إضافة سير عمل جديد",
|
||||
"reschedule_event_trigger": "متى يتم إعادة جدولة الحدث",
|
||||
"trigger": "مشغل",
|
||||
"triggers": "مشغلين",
|
||||
"action": "إجراء",
|
||||
@@ -963,10 +1003,13 @@
|
||||
"change_username_standard_to_premium": "عند التغيير من اسم مستخدم قياسي إلى اسم مستخدم مميز، سيتم نقلك إلى الدفع للترقية.",
|
||||
"change_username_premium_to_standard": "عند التغيير من اسم مستخدم مميز إلى اسم مستخدم قياسي، سيتم نقلك إلى الدفع لخفض المستوى.",
|
||||
"go_to_stripe_billing": "الذهاب إلى الفوترة",
|
||||
"stripe_description": "يتطلب الدفع للحجز (0.5% + 0.10 يورو عمولة لكل معاملة)",
|
||||
"trial_expired": "انتهت صلاحية الفترة التجريبية الخاصة بك",
|
||||
"remove_app": "إزالة التطبيق",
|
||||
"yes_remove_app": "نعم، قم بإزالة التطبيق",
|
||||
"are_you_sure_you_want_to_remove_this_app": "هل أنت متأكد من أنك تريد إزالة هذا التطبيق؟",
|
||||
"app_removed_successfully": "تمت إزالة التطبيق بنجاح",
|
||||
"error_removing_app": "خطأ في إزالة التطبيق",
|
||||
"web_conference": "مؤتمر عبر الإنترنت",
|
||||
"number_for_sms_reminders": "رقم الهاتف (لتذكير الرسائل القصيرة)",
|
||||
"requires_confirmation": "يتطلب التأكيد",
|
||||
@@ -1022,20 +1065,205 @@
|
||||
"navigate": "تنقّل",
|
||||
"open": "فتح",
|
||||
"close": "إغلاق",
|
||||
"embed": "مُضمَن",
|
||||
"team_feature_teams": "هذه ميزة في Team. قم بالترقية إلى Team لرؤية توفر فريقك.",
|
||||
"team_feature_workflows": "هذه ميزة في Team. قم بالترقية إلى Team لأتمتة إشعارات الحدث والتذكير مع سير العمل.",
|
||||
"show_eventtype_on_profile": "إظهار على الملف الشخصي",
|
||||
"embed": "تضمين",
|
||||
"new_username": "اسم مستخدم جديد",
|
||||
"current_username": "اسم المستخدم الحالي",
|
||||
"example_1": "مثال 1",
|
||||
"example_2": "مثال 2",
|
||||
"additional_input_label": "تسمية الإدخال الاضافي",
|
||||
"company_size": "حجم الشركة",
|
||||
"what_help_needed": "ما الذي تحتاج إلى مساعدة بشأنه؟",
|
||||
"variable_format": "تنسيق المتغير",
|
||||
"webhook_subscriber_url_reserved": "رابط مشترك Webhook معرّف بالفعل",
|
||||
"custom_input_as_variable_info": "تجاهل جميع الأحرف الخاصة من تسمية الإدخال الإضافي (استخدم الأحرف والأرقام فقط)، واستخدم الحروف الكبيرة لجميع الأحرف واستبدل المساحات البيضاء بالشرط.",
|
||||
"using_additional_inputs_as_variables": "كيف أستخدم مدخلات إضافية كمتغيرات؟",
|
||||
"download_desktop_app": "تحميل تطبيق سطح المكتب",
|
||||
"set_ping_link": "تعيين رابط Ping",
|
||||
"rate_limit_exceeded": "تم تجاوز حد السعر",
|
||||
"when_something_happens": "عندما يحدث شيء ما",
|
||||
"action_is_performed": "يتم تنفيذ إجراء",
|
||||
"test_action": "إجراء الاختبار",
|
||||
"notification_sent": "تم إرسال الإشعار",
|
||||
"no_input": "لا مدخلات",
|
||||
"test_workflow_action": "اختبار إجراء سير العمل",
|
||||
"send_sms": "إرسال رسالة نصية",
|
||||
"send_sms_to_number": "هل أنت متأكد من رغبتك في إرسال رسالة قصيرة إلى {{number}}؟",
|
||||
"missing_connected_calendar": "لا يوجد تقويم افتراضي متصل",
|
||||
"connect_your_calendar_and_link": "يمكنك ربط رزنامتك من <1>هنا</1>.",
|
||||
"default_calendar_selected": "الرزنامة الافتراضية",
|
||||
"hide_from_profile": "إخفاء من الملف الشخصي",
|
||||
"event_setup_tab_title": "إعداد الحدث",
|
||||
"event_limit_tab_title": "الحدود",
|
||||
"event_limit_tab_description": "ما تواتر مرات الحجز",
|
||||
"event_advanced_tab_description": "إعدادات الرزنامة والمزيد...",
|
||||
"event_advanced_tab_title": "متقدم",
|
||||
"select_which_cal": "اختر أي رزنامة تريد إضافة الحجز إليها",
|
||||
"custom_event_name": "اسم حدث مخصص",
|
||||
"custom_event_name_description": "إنشاء أسماء أحداث مخصصة لعرضها في حدث الرزنامة",
|
||||
"2fa_required": "ثمة حاجة إلى المصادقة الثنائية",
|
||||
"incorrect_2fa": "رمز المصادقة الثنائية غير صحيح",
|
||||
"which_event_type_apply": "ما نوع الحدث الذي سينطبق عليه هذا؟",
|
||||
"no_workflows_description": "تعمل سير العمل على تمكين أتمتة إرسال الإشعارات والتذكيرات التي تمكنك من بناء عمليات حول الأحداث الخاصة بك.",
|
||||
"create_workflow": "إنشاء سير عمل",
|
||||
"do_this": "افعل هذا",
|
||||
"turn_off": "إيقاف التشغيل",
|
||||
"settings_updated_successfully": "تم تحديث الإعدادات بنجاح",
|
||||
"error_updating_settings": "خطأ في تحديث الإعدادات",
|
||||
"personal_cal_url": "عنوان Cal URL الخاص بي",
|
||||
"bio_hint": "اكتب بضع جمل عن نفسك، والتي ستظهر على صفحة عنوان Url الشخصية لديك.",
|
||||
"delete_account_modal_title": "حذف الحساب",
|
||||
"confirm_delete_account_modal": "هل أنت متأكد من أنك تريد حذف حساب Cal.com الخاص بك؟",
|
||||
"delete_my_account": "حذف حسابي",
|
||||
"start_of_week": "بداية الأسبوع",
|
||||
"select_calendars": "حدد الرزنامات التي تريد التحقق منها بحثًا عن تضاربات لمنع الحجوزات المزدوجة.",
|
||||
"check_for_conflicts": "التحقق من وجود تضاربات",
|
||||
"adding_events_to": "إضافة أحداث إلى",
|
||||
"follow_system_preferences": "اتبع تفضيلات النظام",
|
||||
"custom_brand_colors": "ألوان العلامة التجارية المخصصة",
|
||||
"customize_your_brand_colors": "تخصيص لون العلامة التجارية الخاصة بك في صفحة الحجز.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "إزالة أي علامات تجارية ذات صلة بـCal ، أي 'تم تشغيلها من قبل Cal.'",
|
||||
"profile_picture": "صورة الملف الشخصي",
|
||||
"upload": "تحميل",
|
||||
"web3": "الويب 3",
|
||||
"rainbow_token_gated": "هذا النوع من الأحداث موجود وراء بوابة رمز.",
|
||||
"rainbow_connect_wallet_gate": "قم بتوصيل محفظتك إذا كنت تملك <1>{{name}}</1> (<3>{{symbol}}</3>).",
|
||||
"rainbow_insufficient_balance": "لا تحتوي محفظتك المتصلة على ما يكفي من 1>{{symbol}}</1>.",
|
||||
"rainbow_sign_message_request": "قم بتوقيع طلب الرسالة على محفظتك.",
|
||||
"rainbow_signature_error": "خطأ في طلب التوقيع من محفظتك.",
|
||||
"token_address": "عنوان الرمز المميز",
|
||||
"blockchain": "البلوكشين",
|
||||
"old_password": "كلمة مرور قديمة",
|
||||
"secure_password": "كلمة المرور الجديدة الآمنة كلياً",
|
||||
"error_updating_password": "خطأ في تحديث كلمة المرور",
|
||||
"two_factor_auth": "المصادقة الثنائية",
|
||||
"recurring_event_tab_description": "إعداد جدول تكرار",
|
||||
"today": "اليوم",
|
||||
"appearance": "المظهر",
|
||||
"appearance_subtitle": "إدارة إعدادات مظهر الحجز الخاص بك",
|
||||
"my_account": "حسابي",
|
||||
"general": "عام",
|
||||
"calendars": "الرزنامات",
|
||||
"2fa_auth": "المصادقة الثنائية",
|
||||
"invoices": "الفواتير",
|
||||
"embeds": "التضمينات",
|
||||
"impersonation": "التقمّص",
|
||||
"users": "المستخدمون",
|
||||
"profile_description": "إدارة الإعدادات لملفك الرزنامة الشخصي لديك",
|
||||
"general_description": "إدارة الإعدادات للغة والمنطقة الزمنية الخاصة بك",
|
||||
"calendars_description": "اضبط كيفية تفاعل أنواع الأحداث لديك مع رزناماتك",
|
||||
"appearance_description": "إدارة إعدادات مظهر الحجز الخاص بك",
|
||||
"conferencing_description": "إدارة تطبيقات مؤتمرات الفيديو لديك لاجتماعاتك",
|
||||
"password_description": "إدارة إعدادات كلمات مرور حسابك",
|
||||
"2fa_description": "إدارة إعدادات كلمات مرور حسابك",
|
||||
"we_just_need_basic_info": "نحتاج فقط إلى بعض المعلومات الأساسية لإعداد ملفك الشخصي.",
|
||||
"skip": "تخطي",
|
||||
"do_this_later": "القيام بهذا لاحقاً",
|
||||
"set_availability_getting_started_subtitle_1": "تحديد الفترات الزمنية التي أنت فيها متاح",
|
||||
"set_availability_getting_started_subtitle_2": "يمكنك تخصيص كل هذا في وقت لاحق في صفحة التوفر.",
|
||||
"connect_calendars_from_app_store": "يمكنك إضافة المزيد من الرزنامات من متجر التطبيقات",
|
||||
"connect_conference_apps": "ربط تطبيقات المؤتمرات",
|
||||
"connect_calendar_apps": "ربط تطبيقات الرزنامة",
|
||||
"connect_payment_apps": "ربط تطبيقات الدفع",
|
||||
"connect_other_apps": "ربط تطبيقات أخرى",
|
||||
"current_step_of_total": "الخطوة {{currentStep}} من {{maxSteps}}",
|
||||
"add_variable": "إضافة متغير",
|
||||
"custom_phone_number": "رقم هاتف مخصص",
|
||||
"message_template": "نموذج رسالة",
|
||||
"email_subject": "موضوع الرسالة الإلكترونية",
|
||||
"add_dynamic_variables": "أضف متغيرات نص ديناميكية",
|
||||
"event_name_info": "اسم نوع الحدث",
|
||||
"event_date_info": "تاريخ الحدث",
|
||||
"event_time_info": "وقت بدء الحدث",
|
||||
"location_info": "موقع الحدث",
|
||||
"organizer_name_info": "اسمك",
|
||||
"additional_notes_info": "ملاحظات إضافية للحجز",
|
||||
"attendee_name_info": "اسم الشخص صاحب الحجز",
|
||||
"to": "إلى",
|
||||
"attendee_required_enter_number": "سيتطلب هذا من الحضور إدخال رقم هاتف عند الحجز",
|
||||
"workflow_turned_on_successfully": "تم تحديث سير العمل {{workflowName}} بنجاح {{offOn}}",
|
||||
"download_responses": "تحميل الردود",
|
||||
"create_your_first_form": "إنشاء أول استمارة",
|
||||
"create_your_first_form_description": "يمكنك من خلال استمارات التوجيه طرح أسئلة التأهيل والتوجيه إلى الشخص/نوع الحدث المناسب.",
|
||||
"create_your_first_webhook": "إنشاء أول Webhook",
|
||||
"create_your_first_webhook_description": "يمكنك باستخدام Webhooks تلقي بيانات الاجتماع في الوقت الفعلي عندما يحدث شيء ما في Cal.com.",
|
||||
"for_a_maximum_of": "كحد أقصى لـ",
|
||||
"event_one": "حدث",
|
||||
"event_other": "أحداث",
|
||||
"profile_team_description": "إدارة إعدادات الملف الشخصي لفريقك",
|
||||
"members_team_description": "المستخدمين الذين هم في المجموعة",
|
||||
"team_url": "عنوان URL للفريق",
|
||||
"delete_team": "حذف الفريق",
|
||||
"team_members": "أعضاء الفريق",
|
||||
"more": "المزيد",
|
||||
"more_page_footer": "نعتبر تطبيق الهاتف المحمول امتداداً لتطبيق الويب، لكن يرجعى العودة لتطبيق الويب إذا كنت تقوم بأي إجراءات معقدة.",
|
||||
"workflow_example_1": "إرسال رسائل نصية للحضور للتذكير قبل 24 ساعة من بدء الحدث",
|
||||
"workflow_example_2": "إرسال رسالة نصية مخصصة عند إعادة جدولة الحدث للحضور",
|
||||
"workflow_example_3": "إرسال بريد إلكتروني مخصص عندما يتم حجز حدث جديد للاستضافة",
|
||||
"workflow_example_4": "إرسال رسائل نصية للحضور للتذكير قبل 1 ساعة من بدء الحدث",
|
||||
"workflow_example_5": "إرسال رسالة نصية مخصصة عند إعادة جدولة الحدث للحضور",
|
||||
"workflow_example_6": "إرسال بريد إلكتروني مخصص عندما يتم حجز حدث جديد للاستضافة",
|
||||
"welcome_to_cal_header": "مرحبًا بك في Cal.com!",
|
||||
"edit_form_later_subtitle": "سوف تكون قادراً على تعديل هذا لاحقاً.",
|
||||
"connect_calendar_later": "سأقوم بتوصيل التقويم في وقت لاحق",
|
||||
"set_my_availability_later": "سأحدد متى أكون متوفراً لاحقاً",
|
||||
"problem_saving_user_profile": "حدثت مشكلة أثناء حفظ بياناتك. الرجاء المحاولة من جديد أو التواصل مع دعم العملاء.",
|
||||
"purchase_missing_seats": "شراء المقاعد الناقصة",
|
||||
"slot_length": "طول الفتحة",
|
||||
"booking_appearance": "مظهر الحجز",
|
||||
"appearance_team_description": "إدارة إعدادات مظهر الحجز الخاص بك",
|
||||
"only_owner_change": "يمكن فقط لمالك هذا الفريق إجراء تغييرات على حجز الفريق ",
|
||||
"team_disable_cal_branding_description": "إزالة أي علامات تجارية ذات صلة بـCal ، أي 'تم تشغيلها من قبل Cal'",
|
||||
"invited_by_team": "دعاك {{teamName}} للانضمام إلى فريقهم كـ{{role}}",
|
||||
"token_invalid_expired": "الرمز المميز إما غير صالح أو انتهت صلاحيته.",
|
||||
"exchange_add": "الربط مع Microsoft Exchange",
|
||||
"exchange_authentication": "طريقة المصادقة",
|
||||
"exchange_authentication_standard": "المصادقة الأساسية",
|
||||
"exchange_authentication_ntlm": "مصادقة NTLM",
|
||||
"exchange_compression": "ضغط وفق Gzip",
|
||||
"routing_forms_description": "يمكنك هنا رؤية جميع النماذج والمسارات التي أنشأتها.",
|
||||
"add_new_form": "إضافة استمارة جديدة",
|
||||
"form_description": "إنشاء استمارتك لتوجيه طالب حجز",
|
||||
"copy_link_to_form": "نسخ رابط إلى الاستمارة",
|
||||
"theme": "السمة",
|
||||
"theme_applies_note": "ينطبق هذا فقط على صفحات الحجز العامة الخاصة بك",
|
||||
"theme_light": "فاتح",
|
||||
"theme_dark": "داكن"
|
||||
"theme_dark": "داكن",
|
||||
"theme_system": "النظام الافتراضي",
|
||||
"add_a_team": "إضافة فريق",
|
||||
"saml_config": "إعدادات SAML",
|
||||
"add_webhook_description": "تلقي بيانات الاجتماع في الوقت الحقيقي حالما يحدث شيء ما في Cal.com",
|
||||
"triggers_when": "المشغلات عندما",
|
||||
"test_webhook": "الرجاء إجراء اختبار ping قبل الإنشاء.",
|
||||
"enable_webhook": "تمكين Webhook",
|
||||
"add_webhook": "إضافة Webhook",
|
||||
"webhook_edited_successfully": "تم حفظ Webhook",
|
||||
"webhooks_description": "تلقي بيانات الاجتماع في الوقت الحقيقي عندما يحدث شيء ما في Cal.com",
|
||||
"api_keys_description": "إنشاء مفاتيح API لاستخدامها للوصول إلى حسابك الخاص",
|
||||
"new_api_key": "مفتاح API جديد",
|
||||
"active": "نشط",
|
||||
"api_key_updated": "تم تحديث اسم مفتاح API",
|
||||
"api_key_update_failed": "خطأ في تحديث اسم مفتاح API",
|
||||
"embeds_title": "تضمين HTML iframe",
|
||||
"embeds_description": "تضمين جميع أنواع الأحداث لديك على موقعك على الإنترنت",
|
||||
"create_first_api_key": "إنشاء أول مفتاح API لك",
|
||||
"create_first_api_key_description": "تسمح مفاتيح API بتواصل التطبيقات الأخرى مع Cal.com",
|
||||
"back_to_signin": "العودة لتسجيل الدخول",
|
||||
"reset_link_sent": "تم إرسال رابط إعادة الضبط",
|
||||
"password_reset_email": "ثمة بريد إلكتروني في طريقه إلى {{email}} مع تعليمات لإعادة تعيين كلمة المرور الخاصة بك.",
|
||||
"password_updated": "تم تحديث كلمة المرور!",
|
||||
"pending_payment": "في انتظار الدفع",
|
||||
"confirmation_page_rainbow": "رمز بوابة الحدث الخاص بك مع الرموز أو NFTs على Ethereum وPolygon وغيرها.",
|
||||
"not_on_cal": "ليس على Cal.com",
|
||||
"no_calendar_installed": "لا يوجد رزنامة مثبتة",
|
||||
"no_calendar_installed_description": "لم تقم بعد بالاتصال بأي من التقويمات الخاصة بك",
|
||||
"add_a_calendar": "إضافة رزنامة",
|
||||
"change_email_hint": "قد تحتاج إلى تسجيل الخروج والعودة مجددًا لرؤية التغييرات التي تم تنفيذها",
|
||||
"confirm_password_change_email": "الرجاء تأكيد كلمة المرور الخاصة بك قبل تغيير عنوان بريدك الإلكتروني",
|
||||
"seats": "مقاعد",
|
||||
"every_app_published": "كل تطبيق منشور على متجر تطبيقات Cal.com هو مصدر مفتوح ويتم اختباره بشكل شامل عن طريق مراجعات الأقران. لا يدعم Cal.com أو يصادق على هذه التطبيقات ما لم يتم نشرها بواسطة Cal.com. إذا واجهت محتوى أو سلوك غير مناسب يرجى الإبلاغ عن ذلك.",
|
||||
"report_app": "الإبلاغ عن التطبيق"
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "Pro obnovení hesla použijte odkaz níž",
|
||||
"hey_there": "Zdravíme,",
|
||||
"forgot_your_password_calcom": "Zapomněli jste heslo? - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | Typ události",
|
||||
"delete_webhook_confirmation_message": "Opravdu chcete smazat tento webhook? Přestanete v reálném čase dostávat Cal.com data o schůzce na dané URL, pokud bude událost naplánována nebo zrušena.",
|
||||
"confirm_delete_webhook": "Ano, smazat webhook",
|
||||
"edit_webhook": "Upravit webhook",
|
||||
@@ -143,6 +142,7 @@
|
||||
"view_public_page": "Zobrazit veřejnou stránku",
|
||||
"sign_out": "Odhlásit se",
|
||||
"add_another": "Přidat další",
|
||||
"install_another": "Nainstalovat další",
|
||||
"until": "do",
|
||||
"powered_by": "běží na",
|
||||
"unavailable": "Nedostupný",
|
||||
@@ -157,6 +157,7 @@
|
||||
"checkbox": "Zaškrtávací pole",
|
||||
"is_required": "Je vyžadováno",
|
||||
"required": "Vyžadováno",
|
||||
"optional": "Nepovinné",
|
||||
"input_type": "Typ pole",
|
||||
"rejected": "Zamítnuto",
|
||||
"unconfirmed": "Nepotvrzeno",
|
||||
@@ -227,10 +228,12 @@
|
||||
"connect_your_calendar_instructions": "Připojit kalendář pro automatickou kontrolu časů, kdy nejste dostupní, a nově naplánovaných událostí.",
|
||||
"set_up_later": "Zprovoznit později",
|
||||
"current_time": "Aktuální čas",
|
||||
"details": "Podrobnosti",
|
||||
"welcome": "Vítejte",
|
||||
"welcome_back": "Vítejte zpět",
|
||||
"welcome_to_calcom": "Vítejte na webu Cal.com",
|
||||
"welcome_instructions": "Řekněte nám vaše jméno a v jaké časové zóně se pohybujete. Později můžete tyto informace změnit.",
|
||||
"connect_caldav": "Připojit se k CalDav serveru",
|
||||
"connect_caldav": "Připojit se k CalDav (Beta)u",
|
||||
"credentials_stored_and_encrypted": "Vaše přihlašovací údaje budou uloženy a zašifrovány.",
|
||||
"connect": "Připojit",
|
||||
"try_for_free": "Vyzkoušet zdarma",
|
||||
@@ -240,6 +243,7 @@
|
||||
"when": "Kdy",
|
||||
"where": "Kde",
|
||||
"add_to_calendar": "Přidat do kalendáře",
|
||||
"add_another_calendar": "Přidat další kalendář",
|
||||
"other": "Ostatní",
|
||||
"emailed_you_and_attendees": "Poslali jsme vám a ostatním účastníkům e-mail s pozvánkou a podrobnostmi.",
|
||||
"emailed_you_and_attendees_recurring": "Na první z těchto opakovaných událostí jsme vám a ostatním účastníkům poslali e-mail s pozvánkou a podrobnostmi.",
|
||||
@@ -261,6 +265,8 @@
|
||||
"enter_new_password": "Zadejte nové heslo, které chcete u účtu používat.",
|
||||
"reset_password": "Obnovit heslo",
|
||||
"change_your_password": "Změnit heslo",
|
||||
"show_password": "Zobrazit heslo",
|
||||
"hide_password": "Skrýt heslo",
|
||||
"try_again": "Zkusit znovu",
|
||||
"request_is_expired": "Platnost tohoto požadavku vypršela.",
|
||||
"reset_instructions": "Zadejte e-mailovou adresu přiřazenou k vašemu účtu. Pošleme vám na ni odkaz pro obnovení vašeho hesla.",
|
||||
@@ -306,6 +312,7 @@
|
||||
"recurring_bookings": "Jakmile si někdo s vámi zarezervuje opakovanou schůzku, zobrazí se to zde.",
|
||||
"past_bookings": "Vaše proběhlé rezervace se zobrazí zde.",
|
||||
"cancelled_bookings": "Vaše zrušené rezervace se zobrazí zde.",
|
||||
"unconfirmed_bookings": "Vaše nepotvrzené rezervace se zobrazí zde.",
|
||||
"on": "dne",
|
||||
"and": "a",
|
||||
"calendar_shows_busy_between": "Kalendář vás zobrazuje jako nedostupného mezi",
|
||||
@@ -346,6 +353,7 @@
|
||||
"booking_cancelled": "Rezervace zrušena",
|
||||
"booking_rescheduled": "Rezervace přesunuta",
|
||||
"booking_created": "Rezervace vytvořena",
|
||||
"meeting_ended": "Schůzka skončila",
|
||||
"form_submitted": "Formulář byl odeslán",
|
||||
"event_triggers": "Eventy na základě akce",
|
||||
"subscriber_url": "URL pro odběry",
|
||||
@@ -488,6 +496,9 @@
|
||||
"lets_create_first_administrator_user": "Pojďme vytvořit prvního uživatele administrátora.",
|
||||
"new_member": "Nový člen",
|
||||
"invite": "Pozvat",
|
||||
"add_team_members": "Přidat členy týmu",
|
||||
"add_team_members_description": "Pozvěte další, aby se připojili k vašemu týmu",
|
||||
"add_team_member": "Přidat člena týmu",
|
||||
"invite_new_member": "Pozvat nového člena",
|
||||
"invite_new_team_member": "Pozvat někoho do týmu.",
|
||||
"change_member_role": "Změnit roli člena týmu",
|
||||
@@ -600,6 +611,7 @@
|
||||
"create_first_team_and_invite_others": "Vytvořte svůj první tým a pozvěte další uživatele, aby s vámi spolupracovali.",
|
||||
"create_team_to_get_started": "Začněte vytvořením týmu",
|
||||
"teams": "Týmy",
|
||||
"team": "Tým",
|
||||
"team_billing": "Fakturace týmů",
|
||||
"upgrade_to_flexible_pro_title": "Změnili jsme fakturaci pro týmy",
|
||||
"upgrade_to_flexible_pro_message": "Ve vašem týmu jsou členové bez místa. Upgradujte svůj Pro plán pro pokrytí chybějících míst.",
|
||||
@@ -620,7 +632,9 @@
|
||||
"billing": "Fakturace",
|
||||
"manage_your_billing_info": "Spravujte své fakturační údaje a zrušte své předplatné.",
|
||||
"availability": "Dostupnost",
|
||||
"edit_availability": "Upravit dostupnost",
|
||||
"configure_availability": "Nastavit časy, kdy jste dostupní pro rezervace.",
|
||||
"copy_times_to": "Kopírovat časy do",
|
||||
"change_weekly_schedule": "Změnit týdenní pracovní dobu",
|
||||
"logo": "Logo",
|
||||
"error": "Chyba",
|
||||
@@ -635,6 +649,7 @@
|
||||
"event_name_tooltip": "Název, který se zobrazí v kalendářích",
|
||||
"meeting_with_user": "Schůzka s {ATTENDEE}",
|
||||
"additional_inputs": "Další pole",
|
||||
"additional_input_description": "Před potvrzením rezervace vyžadovat od plánující osoby zadání dalších vstupů",
|
||||
"label": "Popisek",
|
||||
"placeholder": "Placeholder",
|
||||
"type": "Typ",
|
||||
@@ -649,6 +664,8 @@
|
||||
"disable_guests": "Zakázat hosty",
|
||||
"disable_guests_description": "Zakázat přidávání dalších hostů během rezervace.",
|
||||
"private_link": "Vygenerovat soukromou adresu URL",
|
||||
"private_link_label": "Soukromý odkaz",
|
||||
"private_link_hint": "Váš soukromý odkaz se po každém použití obnoví",
|
||||
"copy_private_link": "Zkopírovat soukromý odkaz",
|
||||
"private_link_description": "Vygenerujte soukromou adresu URL, kterou budete sdílet, aniž byste odhalili své uživatelské jméno ve službě Cal",
|
||||
"invitees_can_schedule": "Pozvaní mohou plánovat",
|
||||
@@ -679,7 +696,7 @@
|
||||
"add_new_event_type": "Přidat nový typ události",
|
||||
"new_event_type_to_book_description": "Vytvořit nový typ události, ve které si lidé mohou rezervovat časy.",
|
||||
"length": "Doba trvání",
|
||||
"minimum_booking_notice": "Minimální upozornění na rezervaci",
|
||||
"minimum_booking_notice": "Minimální potřebná doba",
|
||||
"slot_interval": "Časové intervaly",
|
||||
"slot_interval_default": "Použít délku události (výchozí)",
|
||||
"delete_event_type_description": "Opravdu chcete smazat tento typ události? Všichni, se kterými jsme tento odkaz sdíleli, ho nebudou moct používat pro rezervování.",
|
||||
@@ -690,16 +707,21 @@
|
||||
"delete_account_confirmation_message": "Opravdu chcete odstranit svůj účet na Cal.com? Všichni, se kterými jste sdíleli odkaz na účet, přijdou o možnost vytvářet rezervace a vy přijdete o uložené předvolby.",
|
||||
"integrations": "Integrace",
|
||||
"apps": "Aplikace",
|
||||
"category_apps": "Aplikace ({{category}})",
|
||||
"app_store": "App Store",
|
||||
"app_store_description": "Propojování lidí, technologií a pracoviště.",
|
||||
"settings": "Nastavení",
|
||||
"event_type_moved_successfully": "Typ události byl úspěšně přesunut",
|
||||
"next_step_text": "Další krok",
|
||||
"next_step": "Přeskočit krok",
|
||||
"prev_step": "Předchozí krok",
|
||||
"install": "Nainstalovat",
|
||||
"installed": "Nainstalováno",
|
||||
"active_install_one": "Aktivní instalace: {{count}}",
|
||||
"active_install_other": "Aktivní instalace: {{count}}",
|
||||
"globally_install": "Nainstalováno globálně",
|
||||
"app_successfully_installed": "Aplikace byla nainstalována",
|
||||
"app_could_not_be_installed": "Aplikaci se nepodařilo nainstalovat",
|
||||
"disconnect": "Odpojit",
|
||||
"embed_your_calendar": "Vložte kalendář do vaší webové stránky",
|
||||
"connect_your_favourite_apps": "Připojte své oblíbené aplikace.",
|
||||
@@ -710,6 +732,7 @@
|
||||
"connect_additional_calendar": "Připojit další kalendář",
|
||||
"conferencing": "Konference",
|
||||
"calendar": "Kalendář",
|
||||
"payments": "Platby",
|
||||
"not_installed": "Nenainstalováno",
|
||||
"error_password_mismatch": "Hesla se neshodují.",
|
||||
"error_required_field": "Toto pole je povinné.",
|
||||
@@ -743,9 +766,22 @@
|
||||
"import_from": "Importovat z",
|
||||
"access_token": "Access token",
|
||||
"visit_roadmap": "Roadmapa",
|
||||
"featured_categories": "Doporučené kategorie",
|
||||
"popular_categories": "Oblíbené kategorie",
|
||||
"number_apps_one": "Aplikace: {{count}}",
|
||||
"number_apps_other": "Aplikace: {{count}}",
|
||||
"trending_apps": "Populární aplikace",
|
||||
"explore_apps": "Aplikace ({{category}})",
|
||||
"installed_apps": "Nainstalované aplikace",
|
||||
"no_category_apps": "Žádné aplikace ({{kategorie}})",
|
||||
"no_category_apps_description_calendar": "Přidejte aplikaci kalendáře, ať máte přehled v zájmu prevence dvojích rezervací",
|
||||
"no_category_apps_description_conferencing": "Zkuste přidat aplikaci pro konference, která umožní propojit videohovory s vašimi klienty",
|
||||
"no_category_apps_description_payment": "Přidejte platební aplikaci, která usnadní provádění transakcí mezi vámi a vašimi klienty",
|
||||
"no_category_apps_description_other": "Přidejte jakýkoli jiný typ aplikace pro nejrůznější činnosti",
|
||||
"installed_app_calendar_description": "Nastavte si kalendáře, ať můžete kontrolovat konflikty a zabránit tak dvojím rezervacím.",
|
||||
"installed_app_conferencing_description": "Přidejte své oblíbené aplikace pro videokonference pro vaše schůzky",
|
||||
"installed_app_payment_description": "Nakonfigurujte služby zpracování plateb, které se mají používat při strhávání plateb z klientů.",
|
||||
"installed_app_other_description": "Všechny vaše nainstalované aplikace z ostatních kategorií.",
|
||||
"empty_installed_apps_headline": "Nenainstalovány žádné aplikace",
|
||||
"empty_installed_apps_description": "Aplikace vám umožní zlepšit pracovní postupy a výrazně zkvalitnit plánování.",
|
||||
"empty_installed_apps_button": "Prozkoumejte App Store",
|
||||
@@ -766,6 +802,7 @@
|
||||
"remove": "Odebrat",
|
||||
"add": "Přidat",
|
||||
"installed_one": "Nainstalováno",
|
||||
"installed_other": "Nainstalováno: {{count}}",
|
||||
"verify_wallet": "Ověřit peněženku",
|
||||
"connect_metamask": "Připojit Metamask",
|
||||
"create_events_on": "Vytvořit události v:",
|
||||
@@ -783,6 +820,8 @@
|
||||
"set_to_default": "Nastavit jako výchozí",
|
||||
"new_schedule_btn": "Nový plán",
|
||||
"add_new_schedule": "Přidat nový plán",
|
||||
"add_new_calendar": "Přidání nového kalendáře",
|
||||
"set_calendar": "Nastavte, kam se mají při rezervaci přidávat nové události.",
|
||||
"delete_schedule": "Odstranit plán",
|
||||
"schedule_created_successfully": "Plán {{scheduleName}} byl úspěšně vytvořen",
|
||||
"availability_updated_successfully": "Dostupnost byla úspěšně aktualizována",
|
||||
@@ -809,7 +848,7 @@
|
||||
"booking_full": "Nejsou k dispozici žádná další místa",
|
||||
"api_keys": "Klíče API",
|
||||
"api_key": "Klíč API",
|
||||
"test_api_key": "Testovací klíč API",
|
||||
"test_api_key": "Otestovat klíč API",
|
||||
"test_passed": "Test se zdařil!",
|
||||
"test_failed": "Test se nezdařil",
|
||||
"provide_api_key": "Zadejte klíč API",
|
||||
@@ -883,7 +922,7 @@
|
||||
"zapier_setup_instructions": "<0>Přihlaste se ke svému účtu Zapier a vytvořte nový Zap.</0><1>Vyberte Cal.com jako svoji aplikaci Trigger. Také vyberte událost Trigger.</1><2>Vyberte si svůj účet a pak zadejte svůj unikátní klíč API.</2><3>Otestujte aplikaci Trigger.</3><4>Vše máte nastaveno!</4>",
|
||||
"install_zapier_app": "Nejdřív si z App Store nainstalujte aplikaci Zapier.",
|
||||
"connect_apple_server": "Připojit se k serveru Apple",
|
||||
"connect_caldav_server": "Připojit se k CalDav serveru",
|
||||
"connect_caldav_server": "Připojit se k CalDav (Beta)u",
|
||||
"calendar_url": "URL kalendáře",
|
||||
"apple_server_generate_password": "Vygenerujte si specifické heslo pro aplikaci Cal.com na",
|
||||
"credentials_stored_encrypted": "Vaše přihlašovací údaje budou uloženy a zašifrovány.",
|
||||
@@ -936,6 +975,7 @@
|
||||
"workflows": "Pracovní postupy",
|
||||
"new_workflow_btn": "Nový pracovní postup",
|
||||
"add_new_workflow": "Přidat nový pracovní postup",
|
||||
"reschedule_event_trigger": "pokud je událost přeložena na jiný termín",
|
||||
"trigger": "Spouštěč",
|
||||
"triggers": "Spouštěče",
|
||||
"action": "Akce",
|
||||
@@ -949,7 +989,7 @@
|
||||
"workflow_deleted_successfully": "Pracovní postup byl odstraněn",
|
||||
"how_long_before": "Za jak dlouho začne událost?",
|
||||
"day_timeUnit": "dny",
|
||||
"hour_timeUnit": "hodiny",
|
||||
"hour_timeUnit": "h",
|
||||
"minute_timeUnit": "min",
|
||||
"new_workflow_heading": "Vytvořte svůj první pracovní postup",
|
||||
"new_workflow_description": "Pracovní postupy umožňují automatizovat zasílání upomínek a oznámení.",
|
||||
@@ -963,10 +1003,13 @@
|
||||
"change_username_standard_to_premium": "Při změně uživatelského jména ze standardního na prémiové budete přesměrováni k platbě, po které přejdete na vyšší verzi.",
|
||||
"change_username_premium_to_standard": "Při změně uživatelského jména z prémiového na standardní budete přesměrováni k platbě, po které přejdete na nižší verzi.",
|
||||
"go_to_stripe_billing": "Přejít k fakturaci",
|
||||
"stripe_description": "Vyžadovat poplatek za rezervace (0,5 % + 0,10 € provize za transakci)",
|
||||
"trial_expired": "Vaše zkušební verze vypršela",
|
||||
"remove_app": "Odstranit aplikaci",
|
||||
"yes_remove_app": "Ano, odstranit aplikaci",
|
||||
"are_you_sure_you_want_to_remove_this_app": "Opravdu chcete odstranit tuto aplikaci?",
|
||||
"app_removed_successfully": "Aplikace odstraněna",
|
||||
"error_removing_app": "Chyba při odstraňování aplikace",
|
||||
"web_conference": "Webová konference",
|
||||
"number_for_sms_reminders": "Telefonní číslo (pro upomínky přes SMS)",
|
||||
"requires_confirmation": "Vyžaduje potvrzení",
|
||||
@@ -1022,18 +1065,205 @@
|
||||
"navigate": "Navigace",
|
||||
"open": "Otevřít",
|
||||
"close": "Zavřít",
|
||||
"team_feature_teams": "Toto je funkce Team. Pokud chcete zobrazit dostupnost svého týmu, upgradujte na verzi Team.",
|
||||
"team_feature_workflows": "Jedná se o funkci Team. Upgradujte na verzi Team, abyste mohli automatizovat upozornění na události a upomínky pomocí pracovních postupů.",
|
||||
"show_eventtype_on_profile": "Zobrazit na profilu",
|
||||
"embed": "Vložit",
|
||||
"new_username": "Nové uživatelské jméno",
|
||||
"current_username": "Současné uživatelské jméno",
|
||||
"example_1": "Příklad 1",
|
||||
"example_2": "Příklad 2",
|
||||
"additional_input_label": "Další vstupní štítek",
|
||||
"company_size": "Velikost společnosti",
|
||||
"what_help_needed": "S čím potřebujete pomoci?",
|
||||
"variable_format": "Formát proměnné",
|
||||
"webhook_subscriber_url_reserved": "Adresa URL odběratele webhooku je již definována",
|
||||
"custom_input_as_variable_info": "Ignorujte všechny speciální znaky doplňkového vstupního štítku (používejte pouze písmena a číslice), používejte velká písmena a bílé znaky nahraďte podtržítky.",
|
||||
"using_additional_inputs_as_variables": "Jak používat doplňkové vstupy jako proměnné?",
|
||||
"download_desktop_app": "Stáhněte si aplikaci pro stolní počítače",
|
||||
"set_ping_link": "Nastavení odkazu Ping",
|
||||
"rate_limit_exceeded": "Byl překročen limit rychlosti",
|
||||
"when_something_happens": "Pokud se něco stane",
|
||||
"action_is_performed": "Provede se akce",
|
||||
"test_action": "Testovací akce",
|
||||
"notification_sent": "Oznámení odesláno",
|
||||
"no_input": "Žádný vstup",
|
||||
"test_workflow_action": "Testovací akce pracovního postupu",
|
||||
"send_sms": "Odeslat SMS",
|
||||
"send_sms_to_number": "Opravdu chcete odeslat SMS na číslo {{number}}?",
|
||||
"missing_connected_calendar": "Není připojen žádný výchozí kalendář",
|
||||
"connect_your_calendar_and_link": "Svůj kalendář můžete připojit <1>zde</1>.",
|
||||
"default_calendar_selected": "Výchozí kalendář",
|
||||
"hide_from_profile": "Nezobrazovat v profilu",
|
||||
"event_setup_tab_title": "Nastavení události",
|
||||
"event_limit_tab_title": "Omezení",
|
||||
"event_limit_tab_description": "Jak často lze u vás provést rezervaci",
|
||||
"event_advanced_tab_description": "Nastavení kalendáře a další...",
|
||||
"event_advanced_tab_title": "Upřesnit",
|
||||
"select_which_cal": "Vyberte kalendář, do kterého se mají přidávat rezervace",
|
||||
"custom_event_name": "Vlastní název události",
|
||||
"custom_event_name_description": "Vytvářejte vlastní názvy událostí, které se budou zobrazovat v kalendáři událostí",
|
||||
"2fa_required": "Vyžadováno dvoufázové ověření",
|
||||
"incorrect_2fa": "Nesprávný kód dvoufázového ověření",
|
||||
"which_event_type_apply": "Na jaký typ události se to bude vztahovat?",
|
||||
"no_workflows_description": "Pracovní postupy umožňují jednoduchou automatizaci zasílání oznámení a připomínek, což vám umožní vytvářet procesy v souvislosti s vašimi událostmi.",
|
||||
"create_workflow": "Vytvoření pracovního postupu",
|
||||
"do_this": "Provést",
|
||||
"turn_off": "Vypnout",
|
||||
"settings_updated_successfully": "Nastavení aktualizováno",
|
||||
"error_updating_settings": "Chyba při aktualizaci nastavení",
|
||||
"personal_cal_url": "Moje osobní adresa URL Cal",
|
||||
"bio_hint": "Několik vět o vás. Obsah se zobrazí se na vaší osobní stránce URL.",
|
||||
"delete_account_modal_title": "Odstranit účet",
|
||||
"confirm_delete_account_modal": "Opravdu chcete odstranit svůj účet Cal.com?",
|
||||
"delete_my_account": "Odstranit můj účet",
|
||||
"start_of_week": "Začátek týdne",
|
||||
"select_calendars": "Vyberte kalendáře, u kterých chcete kontrolovat konflikty v zájmu prevence dvojích rezervací.",
|
||||
"check_for_conflicts": "Zkontrolovat konflikty",
|
||||
"adding_events_to": "Přidání událostí do:",
|
||||
"follow_system_preferences": "Řídit se předvolbami systému",
|
||||
"custom_brand_colors": "Vlastní barvy značky",
|
||||
"customize_your_brand_colors": "Vytvořte si rezervační stránku na míru v barvách své značky.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "Odstraní všechny značky související s Cal, tj. „Běží na platformě Cal.“",
|
||||
"profile_picture": "Profilový obrázek",
|
||||
"upload": "Nahrát",
|
||||
"web3": "Web3",
|
||||
"rainbow_token_gated": "Tento typ události je uzamčený tokenem.",
|
||||
"rainbow_connect_wallet_gate": "Propojte svoji peněženku, pokud vlastníte <1>{{name}}</1> (<3>{{symbol}}</3>).",
|
||||
"rainbow_insufficient_balance": "Vaše propojená peněženka neobsahuje dostatek <1>{{symbol}}</1>.",
|
||||
"rainbow_sign_message_request": "Podepište zprávu s žádostí ve své peněžence.",
|
||||
"rainbow_signature_error": "Chyba při vyžádání podpisu z vaší peněženky.",
|
||||
"token_address": "Adresa tokenu",
|
||||
"blockchain": "Blockchain",
|
||||
"old_password": "Staré heslo",
|
||||
"secure_password": "Vaše nové heslo se super zabezpečením",
|
||||
"error_updating_password": "Chyba při aktualizaci hesla",
|
||||
"two_factor_auth": "Dvoufázové ověření",
|
||||
"recurring_event_tab_description": "Nastavení opakujícího se plánu",
|
||||
"today": "dnes",
|
||||
"appearance": "Vzhled",
|
||||
"appearance_subtitle": "Správa nastavení vzhledu rezervace",
|
||||
"my_account": "Můj účet",
|
||||
"general": "Obecné",
|
||||
"calendars": "Kalendáře",
|
||||
"2fa_auth": "Dvoufázové ověření",
|
||||
"invoices": "Faktury",
|
||||
"embeds": "Vložení",
|
||||
"impersonation": "Ztotožnění",
|
||||
"users": "Uživatelé",
|
||||
"profile_description": "Správa nastavení profilu Cal",
|
||||
"general_description": "Správa nastavení jazyka a časového pásma",
|
||||
"calendars_description": "Konfigurace způsobu interkace typů událostí s kalendáři",
|
||||
"appearance_description": "Správa nastavení vzhledu rezervace",
|
||||
"conferencing_description": "Správa videokonferenčních aplikací pro vaše schůzky",
|
||||
"password_description": "Správa nastavení pro hesla vašich účtů",
|
||||
"2fa_description": "Správa nastavení pro hesla vašich účtů",
|
||||
"we_just_need_basic_info": "K nastavení vašeho profilu potřebujeme jen několik základních informací.",
|
||||
"skip": "Přeskočit",
|
||||
"do_this_later": "Provést později",
|
||||
"set_availability_getting_started_subtitle_1": "Definujte časová rozpětí, kdy budete k dispozici",
|
||||
"set_availability_getting_started_subtitle_2": "To vše si můžete přizpůsobit později na stránce dostupnosti.",
|
||||
"connect_calendars_from_app_store": "Další kalendáře můžete přidat přes obchod s aplikacemi",
|
||||
"connect_conference_apps": "Propojit konferenční aplikace",
|
||||
"connect_calendar_apps": "Propojit kalendářové aplikace",
|
||||
"connect_payment_apps": "Připojit platební aplikace",
|
||||
"connect_other_apps": "Připojit další aplikace",
|
||||
"current_step_of_total": "Krok {{currentStep}} / {{maxSteps}}",
|
||||
"add_variable": "Přidat proměnnou",
|
||||
"custom_phone_number": "Vlastní telefonní číslo",
|
||||
"message_template": "Šablona zprávy",
|
||||
"email_subject": "Předmět e-mailu",
|
||||
"add_dynamic_variables": "Přidání dynamických textových proměnných",
|
||||
"event_name_info": "Název typu události",
|
||||
"event_date_info": "Datum události",
|
||||
"event_time_info": "Čas začátku události",
|
||||
"location_info": "Místo události",
|
||||
"organizer_name_info": "Vaše jméno",
|
||||
"additional_notes_info": "Další poznámky k rezervaci",
|
||||
"attendee_name_info": "Jméno osoby provádějící rezervaci",
|
||||
"to": "Komu",
|
||||
"attendee_required_enter_number": "Při rezervaci je nutné, aby účastník zadal telefonní číslo",
|
||||
"workflow_turned_on_successfully": "Pracovní postup {{workflowName}} byl {{offOn}}",
|
||||
"download_responses": "Stáhnout odpovědi",
|
||||
"create_your_first_form": "Vytvořte svůj první formulář",
|
||||
"create_your_first_form_description": "Pomocí adresných formulářů můžete klást kvalifikační otázky a adresovat je správné osobě nebo typu události.",
|
||||
"create_your_first_webhook": "Vytvořte svůj první webhook",
|
||||
"create_your_first_webhook_description": "Pomocí webhooků můžete přijímat data o schůzkách v reálném čase, pokud v Cal.com dojde k nějaké akci.",
|
||||
"for_a_maximum_of": "Max. pro",
|
||||
"event_one": "událost",
|
||||
"event_other": "událostí",
|
||||
"profile_team_description": "Správa nastavení profilu vašeho týmu",
|
||||
"members_team_description": "Uživatelé, kteří jsou ve skupině",
|
||||
"team_url": "Adresa URL týmu",
|
||||
"delete_team": "Odstranit tým",
|
||||
"team_members": "Členové týmu",
|
||||
"more": "Více",
|
||||
"more_page_footer": "Mobilní aplikaci považujeme za rozšíření webové aplikace. Pokud provádíte složitější akce, vraťte se prosím k webové aplikaci.",
|
||||
"workflow_example_1": "Odeslat účastníkovi SMS upomínku 24 hodin před začátkem akce",
|
||||
"workflow_example_2": "Odeslat účastníkovi vlastní SMS, pokud je událost přeložena na jiný termín",
|
||||
"workflow_example_3": "Odeslat hostiteli vlastní e-mail, pokud je rezervována nová událost",
|
||||
"workflow_example_4": "Odeslat účastníkovi upomínku 1 hodinu před začátkem akce",
|
||||
"workflow_example_5": "Odeslat hostiteli vlastní e-mail, pokud je událost přeložena na jiný termín",
|
||||
"workflow_example_6": "Odeslat hostiteli vlastní SMS, pokud je rezervována nová událost",
|
||||
"welcome_to_cal_header": "Vítá vás Cal.com!",
|
||||
"edit_form_later_subtitle": "Toto budete moci upravit později.",
|
||||
"connect_calendar_later": "Připojím svůj kalendář později",
|
||||
"set_my_availability_later": "Svou dostupnost nastavím později",
|
||||
"problem_saving_user_profile": "Při ukládání vašich údajů došlo k problému. Zkuste to prosím znovu, případně se obraťte na zákaznickou podporu.",
|
||||
"purchase_missing_seats": "Koupit chybějící místa",
|
||||
"slot_length": "Délka slotu",
|
||||
"booking_appearance": "Vzhled rezervace",
|
||||
"appearance_team_description": "Správa nastavení vzhledu rezervace vašeho týmu",
|
||||
"only_owner_change": "Změny v rezervaci týmu může provádět pouze jeho vlastník ",
|
||||
"team_disable_cal_branding_description": "Odstraní všechny značky související s Cal, tj. „Běží na platformě Cal.“",
|
||||
"invited_by_team": "Tým {{teamName}} vás pozval do svého týmu na pozici {{role}}",
|
||||
"token_invalid_expired": "Token je buď neplatný, nebo jeho platnost vypršela.",
|
||||
"exchange_add": "Propojit s Microsoft Exchange",
|
||||
"exchange_authentication": "Metoda ověřování",
|
||||
"exchange_authentication_standard": "Základní ověření",
|
||||
"exchange_authentication_ntlm": "Ověřování NTLM",
|
||||
"exchange_compression": "Komprese GZip",
|
||||
"routing_forms_description": "Zde si můžete prohlédnout všechny vytvořené formuláře a trasy.",
|
||||
"add_new_form": "Přidat nový formulář",
|
||||
"form_description": "Vytvořte formulář, který adresujete rezervující osobě",
|
||||
"copy_link_to_form": "Kopírovat odkaz na formulář",
|
||||
"theme": "Motiv",
|
||||
"theme_applies_note": "Toto se týká pouze vašich veřejných stránek s rezervacemi",
|
||||
"theme_light": "Světlý",
|
||||
"theme_dark": "Tmavý"
|
||||
"theme_dark": "Tmavý",
|
||||
"theme_system": "Výchozí nastavení systému",
|
||||
"add_a_team": "Přidat tým",
|
||||
"saml_config": "Konfig. SAML",
|
||||
"add_webhook_description": "Přijímejte data o schůzkách v reálném čase, pokud v Cal.com dojde k nějaké akci",
|
||||
"triggers_when": "Spustí se, pokud:",
|
||||
"test_webhook": "Před vytvořením proveďte test ping.",
|
||||
"enable_webhook": "Povolit webhook",
|
||||
"add_webhook": "Přidat webhook",
|
||||
"webhook_edited_successfully": "Webhook uložen",
|
||||
"webhooks_description": "Přijímejte data o schůzkách v reálném čase, pokud v Cal.com dojde k nějaké akci",
|
||||
"api_keys_description": "Vygenerujte klíče API pro přístup k vlastnímu účtu",
|
||||
"new_api_key": "Nový klíč API",
|
||||
"active": "Aktivní",
|
||||
"api_key_updated": "Aktualizace názvu klíče API",
|
||||
"api_key_update_failed": "Chyba při aktualizaci názvu klíče API",
|
||||
"embeds_title": "Vložení rámce HTML iframe",
|
||||
"embeds_description": "Vložte všechny typy událostí na vaši webovou stránku",
|
||||
"create_first_api_key": "Vytvořte svůj první klíč API",
|
||||
"create_first_api_key_description": "Klíče API umožňují ostatním aplikacím komunikovat se službou Cal.com",
|
||||
"back_to_signin": "Zpět na přihlášení",
|
||||
"reset_link_sent": "Odkaz pro obnovení odeslán",
|
||||
"password_reset_email": "Na adresu {{email}} je na cestě e-mail s pokyny k obnovení hesla.",
|
||||
"password_updated": "Heslo aktualizováno!",
|
||||
"pending_payment": "Čeká se na platbu",
|
||||
"confirmation_page_rainbow": "Zamkněte svoji událost pomocí tokenů nebo NFT na platformách Ethereum, Polygon a dalších.",
|
||||
"not_on_cal": "Nenachází se na Cal.com",
|
||||
"no_calendar_installed": "Není nainstalován kalendář",
|
||||
"no_calendar_installed_description": "Ještě jste nepropojili žádný ze svých kalendářů",
|
||||
"add_a_calendar": "Přidejte kalendář",
|
||||
"change_email_hint": "Možná se budete muset odhlásit a znovu přihlásit, aby se změny projevily",
|
||||
"confirm_password_change_email": "Před změnou e-mailové adresy potvrďte své heslo",
|
||||
"seats": "míst",
|
||||
"every_app_published": "Každá aplikace zveřejněná v obchodě s aplikacemi na Cal.com má otevřený zdrojový kód a je důkladně testována prostřednictvím vzájemného hodnocení. Společnost Cal.com, Inc. nicméně tyto aplikace neschvaluje ani necertifikuje, pokud nejsou zveřejněny přímo společností Cal.com. Pokud se setkáte s nevhodným obsahem nebo chováním, nahlaste to.",
|
||||
"report_app": "Nahlásit aplikaci"
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "Benutzen Sie den Link, um Ihr Passwort zurückzusetzen",
|
||||
"hey_there": "Hallo,",
|
||||
"forgot_your_password_calcom": "Passwort vergessen? - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | Event-Typ",
|
||||
"delete_webhook_confirmation_message": "Sind Sie sicher, dass Sie diesen Webhook löschen möchten? Sie werden keine Cal.com Termindaten mehr erhalten.",
|
||||
"confirm_delete_webhook": "Ja, Webhook löschen",
|
||||
"edit_webhook": "Webhook bearbeiten",
|
||||
@@ -255,7 +254,7 @@
|
||||
"user_needs_to_confirm_or_reject_booking_recurring": "{{user}} muss noch die Terminreihe des wiederkehrenden Termins bestätigen oder ablehnen.",
|
||||
"meeting_is_scheduled": "Dieser Termin ist geplant",
|
||||
"meeting_is_scheduled_recurring": "Ihr wiederkehrender Termin wurde geplant",
|
||||
"submitted": "Ihr Termin wurde gebuhct",
|
||||
"submitted": "Ihr Termin wurde gebucht",
|
||||
"submitted_recurring": "Ihr wiederkehrender Termin wurde gebucht",
|
||||
"booking_submitted": "Ihre Buchung wurde versandt",
|
||||
"booking_submitted_recurring": "Ihre wiederkehrende Buchung wurde versandt",
|
||||
@@ -266,6 +265,8 @@
|
||||
"enter_new_password": "Geben Sie das neue Passwort ein.",
|
||||
"reset_password": "Passwort zurücksetzen",
|
||||
"change_your_password": "Passwort ändern",
|
||||
"show_password": "Passwort anzeigen",
|
||||
"hide_password": "Passwort verbergen",
|
||||
"try_again": "Erneut versuchen",
|
||||
"request_is_expired": "Diese Anfrage ist abgelaufen.",
|
||||
"reset_instructions": "Geben Sie die E-Mail-Adresse Ihres Kontos ein und wir senden Ihnen einen Link zum Zurücksetzen Ihres Passworts.",
|
||||
@@ -355,6 +356,7 @@
|
||||
"meeting_ended": "Meeting beendet",
|
||||
"form_submitted": "Formular gesendet",
|
||||
"event_triggers": "Ereignis-Auslöser",
|
||||
"subscriber_url": "Subscriber-URL",
|
||||
"create_new_webhook": "Einen neuen Webhook erstellen",
|
||||
"webhooks": "Webhooks",
|
||||
"team_webhooks": "Team Webhooks",
|
||||
@@ -465,7 +467,7 @@
|
||||
"host_phone_number": "Ihre Telefonnummer",
|
||||
"enter_phone_number": "Telefonnummer eingeben",
|
||||
"reschedule": "Neuplanen",
|
||||
"reschedule_this": "Stattdessen verschie",
|
||||
"reschedule_this": "Stattdessen verschieben",
|
||||
"book_a_team_member": "Teammitglied stattdessen buchen",
|
||||
"or": "ODER",
|
||||
"go_back": "Zurück",
|
||||
@@ -494,6 +496,9 @@
|
||||
"lets_create_first_administrator_user": "Lass uns als erstes den Administrator Benutzer anlegen.",
|
||||
"new_member": "Neues Mitglied",
|
||||
"invite": "Einladen",
|
||||
"add_team_members": "Teammitglieder hinzufügen",
|
||||
"add_team_members_description": "Lade andere ein, deinem Team beizutreten",
|
||||
"add_team_member": "Teammitglied hinzufügen",
|
||||
"invite_new_member": "Ein neues Mitglied einladen",
|
||||
"invite_new_team_member": "Laden Sie jemanden in Ihr Team ein.",
|
||||
"change_member_role": "Rolle des Teammitglieds ändern",
|
||||
@@ -629,6 +634,7 @@
|
||||
"availability": "Verfügbarkeit",
|
||||
"edit_availability": "Verfügbarkeit editieren",
|
||||
"configure_availability": "Konfigurieren Sie die Zeiten, in denen Sie für Buchungen zur Verfügung stehen.",
|
||||
"copy_times_to": "Zeiten kopieren in",
|
||||
"change_weekly_schedule": "Wöchentliche Termine ändern",
|
||||
"logo": "Logo",
|
||||
"error": "Fehler",
|
||||
@@ -643,6 +649,7 @@
|
||||
"event_name_tooltip": "Der Name, der in Kalendern angezeigt wird",
|
||||
"meeting_with_user": "Meeting mit {ATTENDEE}",
|
||||
"additional_inputs": "Zusätzliche Notizen",
|
||||
"additional_input_description": "Erfordert die Eingabe zusätzlicher Eingaben durch den Zeitplaner vor der Buchungsbestätigung",
|
||||
"label": "Bezeichnung",
|
||||
"placeholder": "Placeholder",
|
||||
"type": "Typ",
|
||||
@@ -725,6 +732,7 @@
|
||||
"connect_additional_calendar": "Zusätzlichen Kalender verbinden",
|
||||
"conferencing": "Videokonferenzen",
|
||||
"calendar": "Kalender",
|
||||
"payments": "Zahlungen",
|
||||
"not_installed": "Nicht installiert",
|
||||
"error_password_mismatch": "Passwörter stimmen nicht überein.",
|
||||
"error_required_field": "Dieses Feld ist erforderlich.",
|
||||
@@ -765,6 +773,15 @@
|
||||
"trending_apps": "Angesagte Apps",
|
||||
"explore_apps": "{{category}} Apps",
|
||||
"installed_apps": "Installierte Apps",
|
||||
"no_category_apps": "Keine {{category}} Apps",
|
||||
"no_category_apps_description_calendar": "Fügen Sie eine Kalender-App hinzu, in der nach Konflikten gesucht werden soll, um Doppelbuchungen zu vermeiden",
|
||||
"no_category_apps_description_conferencing": "Versuchen Sie, eine Konferenz-App hinzuzufügen, um Videoanrufe mit Ihren Kunden zu integrieren",
|
||||
"no_category_apps_description_payment": "Fügen Sie eine Zahlungs-App hinzu, um die Transaktion zwischen Ihnen und Ihren Kunden zu erleichtern",
|
||||
"no_category_apps_description_other": "Füge jede beliebige andere Art von App hinzu, um alle möglichen Dinge zu tun",
|
||||
"installed_app_calendar_description": "Legen Sie den/die Kalender in denen nach Konflikten gesucht werden sollen fest, um Doppelbuchungen zu vermeiden.",
|
||||
"installed_app_conferencing_description": "Fügen Sie Ihre liebsten Videokonferenz-Apps für Ihre Meetings hinzu",
|
||||
"installed_app_payment_description": "Konfigurieren Sie die Zahlungsdienstleister, welcher bei der Belastung Ihrer Kunden verwendet werden sollen.",
|
||||
"installed_app_other_description": "Alle von Ihnen installierten Apps aus anderen Kategorien.",
|
||||
"empty_installed_apps_headline": "Keine Apps installiert",
|
||||
"empty_installed_apps_description": "Apps erlauben es Ihnen, Ihren Arbeitsfluss zu verbessern und Ihre Planung viel besser zu gestalten.",
|
||||
"empty_installed_apps_button": "Entdecken Sie den App Store",
|
||||
@@ -803,6 +820,8 @@
|
||||
"set_to_default": "Auf Standard setzen",
|
||||
"new_schedule_btn": "Neuer Verfügbarkeitsplan",
|
||||
"add_new_schedule": "Neuen Verfügbarkeitsplan hinzufügen",
|
||||
"add_new_calendar": "Neuen Kalender hinzufügen",
|
||||
"set_calendar": "Legen Sie fest, wo neue Events hinzugefügt werden sollen, wenn Sie gebucht werden.",
|
||||
"delete_schedule": "Verfügbarkeitsplan löschen",
|
||||
"schedule_created_successfully": "{{scheduleName}} -Verfügbarkeitsplan erfolgreich erstellt",
|
||||
"availability_updated_successfully": "Verfügbarkeit erfolgreich aktualisiert",
|
||||
@@ -984,10 +1003,13 @@
|
||||
"change_username_standard_to_premium": "Da Sie von einem Standard- zu einem Premium-Benutzernamen wechseln, werden Sie zum Upgrade zur Kasse gebracht.",
|
||||
"change_username_premium_to_standard": "Da Sie von einem Premium- zu einem Standard-Benutzernamen wechseln, werden Sie zum Downgrade zur Kasse gebracht.",
|
||||
"go_to_stripe_billing": "Zur Abrechnung",
|
||||
"stripe_description": "Zahlung für Buchungen (0,5 % + 0,10 € Provision pro Transaktion)",
|
||||
"trial_expired": "Ihre Testversion ist abgelaufen",
|
||||
"remove_app": "App entfernen",
|
||||
"yes_remove_app": "Ja, App entfernen",
|
||||
"are_you_sure_you_want_to_remove_this_app": "Sind Sie sicher, dass Sie diese App entfernen möchten?",
|
||||
"app_removed_successfully": "App erfolgreich entfernt",
|
||||
"error_removing_app": "Fehler beim Entfernen der App",
|
||||
"web_conference": "Webkonferenz",
|
||||
"number_for_sms_reminders": "Telefonnummer (für SMS-Erinnerungen)",
|
||||
"requires_confirmation": "Erfordert Bestätigung",
|
||||
@@ -1043,6 +1065,8 @@
|
||||
"navigate": "Navigieren",
|
||||
"open": "Öffnen",
|
||||
"close": "Schließen",
|
||||
"team_feature_teams": "Dies ist eine Team-Funktion. Sie müssen auf die Team-Version upgraden, um die Verfügbarkeit Ihres Teams zu sehen.",
|
||||
"team_feature_workflows": "Dies ist eine Team-Funktion. Sie müssen auf die Team-Version upgraden, um Ihre Termin-Benachrichtigungen und Erinnerungen mit Workflows zu automatisieren.",
|
||||
"show_eventtype_on_profile": "Im Profil anzeigen",
|
||||
"embed": "Einbetten",
|
||||
"new_username": "Neuer Benutzername",
|
||||
@@ -1076,10 +1100,13 @@
|
||||
"event_limit_tab_description": "Wie oft können Sie gebucht werden",
|
||||
"event_advanced_tab_description": "Kalendereinstellungen & mehr...",
|
||||
"event_advanced_tab_title": "Erweitert",
|
||||
"select_which_cal": "Wählen Sie den Kalender aus, zu dem Buchungen hinzugefügt werden sollen",
|
||||
"custom_event_name": "Eigener Ereignisname",
|
||||
"custom_event_name_description": "Erstellen Sie benutzerdefinierte Ereignisnamen, die im Kalenderereignis angezeigt werden sollen",
|
||||
"2fa_required": "Zwei-Faktor-Authentifizierung erforderlich",
|
||||
"incorrect_2fa": "Falscher Zwei-Faktor-Authentifizierungscode",
|
||||
"which_event_type_apply": "Auf welchen Ereignistyp wird dies angewandt?",
|
||||
"no_workflows_description": "Workflows ermöglichen die einfache Automatisierung des Versands von Benachrichtigungen und Erinnerungen, so dass Sie Prozesse rund um Ihre Ereignisse erstellen können.",
|
||||
"create_workflow": "Einen Workflow erstellen",
|
||||
"do_this": "Mache dies",
|
||||
"turn_off": "Abschalten",
|
||||
@@ -1088,8 +1115,10 @@
|
||||
"personal_cal_url": "Meine persönliche Cal. com URL",
|
||||
"bio_hint": "Schreiben Sie eine kurze Beschreibung, welche auf Ihrer persönlichen Url-Seite erscheinen wird.",
|
||||
"delete_account_modal_title": "Account löschen",
|
||||
"confirm_delete_account_modal": "Sind Sie sicher, dass Sie Ihr Cal.com-Konto löschen möchten?",
|
||||
"delete_my_account": "Meinen Account löschen",
|
||||
"start_of_week": "Wochenstart",
|
||||
"select_calendars": "Wählen Sie aus, in welchen Kalendern Sie nach Konflikten suchen wollen, um Doppelbuchungen zu vermeiden.",
|
||||
"check_for_conflicts": "Auf Konflikte prüfen",
|
||||
"adding_events_to": "Ereignisse hinzufügen zu",
|
||||
"follow_system_preferences": "Systemeinstellungen folgen",
|
||||
@@ -1101,32 +1130,46 @@
|
||||
"upload": "Upload",
|
||||
"web3": "Web3",
|
||||
"rainbow_token_gated": "Dieser Ereignistyp ist durch einen Token geschützt.",
|
||||
"rainbow_connect_wallet_gate": "Verbinden Sie Ihre Wallet, falls Sie <1>{{name}}</1> (<3>{{symbol}}</3>) besitzen.",
|
||||
"rainbow_insufficient_balance": "Ihre verbundene Wallet enthält nicht genug <1>{{symbol}}</1>.",
|
||||
"rainbow_sign_message_request": "Unterschreiben Sie die Nachrichtenanfrage in Ihrer Wallet.",
|
||||
"rainbow_signature_error": "Fehler beim Anfordern der Signatur von Ihrer Wallet.",
|
||||
"token_address": "Adresse des Token",
|
||||
"blockchain": "Blockchain",
|
||||
"old_password": "Altes Passwort",
|
||||
"secure_password": "Ihr sicheres neues Passwort",
|
||||
"error_updating_password": "Fehler beim aktualisieren des Passworts",
|
||||
"two_factor_auth": "Zwei-Faktor-Authentifizierung",
|
||||
"recurring_event_tab_description": "Einen wiederholenden Zeitplan einrichten",
|
||||
"today": "heute",
|
||||
"appearance": "Aussehen",
|
||||
"appearance_subtitle": "Einstellungen für Ihre Buchungsdarstellung verwalten",
|
||||
"my_account": "Mein Account",
|
||||
"general": "Allgemein",
|
||||
"calendars": "Kalender",
|
||||
"2fa_auth": "2 Faktor-Authentifizierung",
|
||||
"invoices": "Rechnungen",
|
||||
"embeds": "Embeds",
|
||||
"impersonation": "Benutzeridentitätswechsel",
|
||||
"users": "Benutzer",
|
||||
"profile_description": "Einstellungen für Ihr Cal.com Profil verwalten",
|
||||
"general_description": "Einstellungen für Ihre Sprache und Zeitzone verwalten",
|
||||
"calendars_description": "Konfigurieren Sie, wie Ihre Event-Typen mit Ihren Kalendern interagieren sollen",
|
||||
"appearance_description": "Einstellungen für Ihre Buchungsdarstellung verwalten",
|
||||
"conferencing_description": "Verwalten Sie Ihre Video-Konferenz-Apps für Ihre Meetings",
|
||||
"password_description": "Einstellungen für Ihre Konto-Passwörter verwalten",
|
||||
"2fa_description": "Einstellungen für Ihre Konto-Passwörter verwalten",
|
||||
"we_just_need_basic_info": "Wir benötigen nur ein paar grundlegende Informationen, um Ihr Profil einzurichten.",
|
||||
"skip": "Überspringen",
|
||||
"do_this_later": "Später machen",
|
||||
"set_availability_getting_started_subtitle_1": "Zeiträume definieren, in denen Sie verfügbar sind",
|
||||
"set_availability_getting_started_subtitle_2": "Sie können dies alles später auf der Verfügbarkeitsseite anpassen.",
|
||||
"connect_calendars_from_app_store": "Sie können weitere Kalender aus dem App-Store hinzufügen",
|
||||
"connect_conference_apps": "Konferenz-Apps verbinden",
|
||||
"connect_calendar_apps": "Kalender-Apps verbinden",
|
||||
"connect_payment_apps": "Zahlungs-Apps verbinden",
|
||||
"connect_other_apps": "Andere Apps verbinden",
|
||||
"current_step_of_total": "Schritt {{currentStep}} von {{maxSteps}}",
|
||||
"add_variable": "Variable hinzufügen",
|
||||
"custom_phone_number": "Benutzerdefinierte Telefonnummer",
|
||||
"message_template": "Nachrichtenvorlage",
|
||||
@@ -1141,10 +1184,22 @@
|
||||
"attendee_name_info": "Der Name der Buchenden",
|
||||
"to": "An",
|
||||
"attendee_required_enter_number": "Hier muss der Teilnehmer bei der Buchung eine Telefonnummer eingeben",
|
||||
"workflow_turned_on_successfully": "{{workflowName}} Workflow erfolgreich {{offOn}}geschaltet",
|
||||
"download_responses": "Antworten herunterladen",
|
||||
"create_your_first_form": "Erstellen Sie Ihr erstes Formular",
|
||||
"create_your_first_form_description": "Mithilfe von Leitungsformularen können Sie qualifizierende Fragen stellen und zur richtigen Person oder zum richtigen Veranstaltungstyp weiterleiten.",
|
||||
"create_your_first_webhook": "Erstellen Sie Ihren ersten Webhook",
|
||||
"create_your_first_webhook_description": "Mit Webhooks können Sie Meetingdaten in Echtzeit erhalten, sobald etwas auf Cal.com passiert.",
|
||||
"for_a_maximum_of": "Für maximal",
|
||||
"event_one": "Ereignis",
|
||||
"event_other": "Ereignisse",
|
||||
"profile_team_description": "Einstellungen für Ihr Teamprofil verwalten",
|
||||
"members_team_description": "Benutzer in der Gruppe",
|
||||
"team_url": "Team-URL",
|
||||
"delete_team": "Team löschen",
|
||||
"team_members": "Teammitglieder",
|
||||
"more": "Mehr",
|
||||
"more_page_footer": "Wir betrachten die mobile Anwendung als Erweiterung der Web-Anwendung. Wenn Sie komplizierte Aktionen durchführen, verwenden Sie bitte die Web-Anwendung.",
|
||||
"workflow_example_1": "Eine Erinnerung per Mail an die Teilnehmer verschicken, 24 Stunden bevor das Event startet",
|
||||
"workflow_example_2": "Eine Erinnerung per SMS an die Teilnehmer verschicken, eine Stunde bevor das Event startet",
|
||||
"workflow_example_3": "Sende eine E-Mail an den Veranstalter, wenn das Ereignis abgebrochen wird",
|
||||
@@ -1156,15 +1211,59 @@
|
||||
"connect_calendar_later": "Ich verbinde meinen Kalender später",
|
||||
"set_my_availability_later": "Ich werde meine Verfügbarkeit später festlegen",
|
||||
"problem_saving_user_profile": "Beim Speichern Ihrer Daten ist ein Problem aufgetreten. Bitte versuchen Sie es erneut oder wenden Sie sich an den Support.",
|
||||
"purchase_missing_seats": "Fehlende Plätze kaufen",
|
||||
"slot_length": "Zeitfensterlänge",
|
||||
"booking_appearance": "Buchungsdarstellung",
|
||||
"appearance_team_description": "Einstellungen für die Buchungsdarstellung Ihres Teams verwalten",
|
||||
"only_owner_change": "Nur der Besitzer dieses Teams kann Änderungen an den Buchungen des Teams vornehmen ",
|
||||
"team_disable_cal_branding_description": "Entfernt alle Cal-bezogenen Markenzeichen, z.B. 'Powered by Cal.'",
|
||||
"invited_by_team": "{{teamName}} hat Sie eingeladen, dem Team als {{role}} beizutreten",
|
||||
"token_invalid_expired": "Token ist entweder ungültig oder abgelaufen.",
|
||||
"exchange_add": "Mit Microsoft Exchange verbinden",
|
||||
"exchange_authentication": "Authentifizierungsmethode",
|
||||
"exchange_authentication_standard": "Basisauthentifizierung",
|
||||
"exchange_authentication_ntlm": "NTLM-Authentifizierung",
|
||||
"exchange_compression": "GZip-Kompression",
|
||||
"routing_forms_description": "Hier sehen Sie alle Formulare und Weiterleitungen, die Sie erstellt haben.",
|
||||
"add_new_form": "Neues Formular hinzufügen",
|
||||
"form_description": "Erstellen Sie Ihr Formular, um einen Bucher zu anzuleiten",
|
||||
"copy_link_to_form": "Link in Formular kopieren",
|
||||
"theme": "Theme",
|
||||
"theme_applies_note": "Dies gilt nur für Ihre öffentlichen Buchungsseiten",
|
||||
"theme_light": "Hell",
|
||||
"theme_dark": "Dunkel",
|
||||
"theme_system": "Systemstandard",
|
||||
"add_a_team": "Team hinzufügen",
|
||||
"saml_config": "SAML-Konfiguration",
|
||||
"add_webhook_description": "Erhalten Sie Meetingdaten in Echtzeit, wenn etwas auf Cal.com passiert",
|
||||
"triggers_when": "Löst aus, wenn",
|
||||
"test_webhook": "Bitte führen Sie einen Pingtest vor der Erstellung durch.",
|
||||
"enable_webhook": "Webhook aktivieren",
|
||||
"add_webhook": "Webhook hinzufügen",
|
||||
"webhook_edited_successfully": "Webhook gespeichert",
|
||||
"webhooks_description": "Erhalten Sie Meetingdaten in Echtzeit, wenn etwas auf Cal.com passiert",
|
||||
"api_keys_description": "Generieren Sie API-Schlüssel für den Zugriff auf Ihren eigenen Account",
|
||||
"new_api_key": "Neuer API-Schlüssel",
|
||||
"active": "Aktiv",
|
||||
"api_key_updated": "API-Schlüsselname aktualisiert",
|
||||
"api_key_update_failed": "Fehler beim Aktualisieren des API-Schlüsselnamens",
|
||||
"embeds_title": "HTML iframe-Einbettung",
|
||||
"embeds_description": "Alle Event-Typen auf Ihrer Website einbetten",
|
||||
"create_first_api_key": "Erstellen Sie Ihren ersten API-Schlüssel",
|
||||
"create_first_api_key_description": "API-Schlüssel erlauben es anderen Apps, mit Cal.com zu kommunizieren",
|
||||
"back_to_signin": "Zurück zur Anmeldung",
|
||||
"reset_link_sent": "Zurücksetzungslink gesendet",
|
||||
"password_reset_email": "Eine E-Mail mit Anweisungen zum Zurücksetzen Ihres Passworts ist auf dem Weg zu {{email}}.",
|
||||
"password_updated": "Passwort aktualisiert!",
|
||||
"pending_payment": "Zahlung ausstehend"
|
||||
"pending_payment": "Zahlung ausstehend",
|
||||
"confirmation_page_rainbow": "Sperren Sie Ihr Ereignis hinter Tokens oder NFTs auf Ethereum, Polygon und mehr.",
|
||||
"not_on_cal": "Nicht auf Cal.com",
|
||||
"no_calendar_installed": "Kein Kalender installiert",
|
||||
"no_calendar_installed_description": "Sie haben noch keinen Ihrer Kalender verbunden",
|
||||
"add_a_calendar": "Kalender hinzufügen",
|
||||
"change_email_hint": "Möglicherweise müssen Sie sich aus- und einloggen, um jegliche Änderung wirksam zu machen",
|
||||
"confirm_password_change_email": "Bitte bestätigen Sie Ihr Passwort, bevor Sie Ihre E-Mail-Adresse ändern",
|
||||
"seats": "Plätze",
|
||||
"every_app_published": "Jede App, die im Cal.com App Store veröffentlicht wird, ist Open Source und gründlich durch Peer Reviews getestet. Cal.com, Inc. unterstützt oder zertifiziert diese Apps jedoch nicht, es sei denn, sie werden von Cal.com veröffentlicht. Wenn Sie auf unangemessene Inhalte oder Verhaltensweisen stoßen, melden Sie diese bitte.",
|
||||
"report_app": "App melden"
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "Use the link below to reset your password",
|
||||
"hey_there": "Hey there,",
|
||||
"forgot_your_password_calcom": "Forgot your password? - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | Event Type",
|
||||
"delete_webhook_confirmation_message": "Are you sure you want to delete this webhook? You will no longer receive Cal.com meeting data at a specified URL, in real-time, when an event is scheduled or canceled.",
|
||||
"confirm_delete_webhook": "Yes, delete webhook",
|
||||
"edit_webhook": "Edit Webhook",
|
||||
@@ -206,7 +205,6 @@
|
||||
"2fa_enter_six_digit_code": "Enter the six-digit code from your authenticator app below.",
|
||||
"create_an_account": "Create an account",
|
||||
"dont_have_an_account": "Don't have an account?",
|
||||
"i_dont_have_an_account": "I don't have an account",
|
||||
"2fa_code": "Two-Factor Code",
|
||||
"sign_in_account": "Sign in to your account",
|
||||
"sign_in": "Sign in",
|
||||
@@ -235,7 +233,7 @@
|
||||
"welcome_back": "Welcome back",
|
||||
"welcome_to_calcom": "Welcome to Cal.com",
|
||||
"welcome_instructions": "Tell us what to call you and let us know what timezone you’re in. You’ll be able to edit this later.",
|
||||
"connect_caldav": "Connect to CalDav Server",
|
||||
"connect_caldav": "Connect to CalDav (Beta)",
|
||||
"credentials_stored_and_encrypted": "Your credentials will be stored and encrypted.",
|
||||
"connect": "Connect",
|
||||
"try_for_free": "Try it for free",
|
||||
@@ -447,7 +445,7 @@
|
||||
"sunday": "Sunday",
|
||||
"all_booked_today": "All booked today.",
|
||||
"slots_load_fail": "Could not load the available time slots.",
|
||||
"additional_guests": "Add guest",
|
||||
"additional_guests": "Add guests",
|
||||
"your_name": "Your name",
|
||||
"email_address": "Email address",
|
||||
"enter_valid_email": "Please enter a valid email",
|
||||
@@ -634,7 +632,6 @@
|
||||
"billing": "Billing",
|
||||
"manage_your_billing_info": "Manage your billing information and cancel your subscription.",
|
||||
"availability": "Availability",
|
||||
"availability_title": "{{availabilityTitle}} | Availability",
|
||||
"edit_availability": "Edit availability",
|
||||
"configure_availability": "Configure times when you are available for bookings.",
|
||||
"copy_times_to": "Copy times to",
|
||||
@@ -843,7 +840,7 @@
|
||||
"redirect_url_upgrade_description": "In order to use this feature, you need to upgrade to a Pro account.",
|
||||
"duplicate": "Duplicate",
|
||||
"offer_seats": "Offer seats",
|
||||
"offer_seats_description": "Offer seats to bookings (This disables guests & opt in bookings)",
|
||||
"offer_seats_description": "Offer seats for booking. This automatically disables guest & opt-in bookings.",
|
||||
"seats_available": "Seats available",
|
||||
"number_of_seats": "Number of seats per booking",
|
||||
"enter_number_of_seats": "Enter number of seats",
|
||||
@@ -925,13 +922,13 @@
|
||||
"zapier_setup_instructions": "<0>Log into your Zapier account and create a new Zap.</0><1>Select Cal.com as your Trigger app. Also choose a Trigger event.</1><2>Choose your account and then enter your Unique API Key.</2><3>Test your Trigger.</3><4>You're set!</4>",
|
||||
"install_zapier_app": "Please first install the Zapier App in the app store.",
|
||||
"connect_apple_server": "Connect to Apple Server",
|
||||
"connect_caldav_server": "Connect to CalDav Server",
|
||||
"connect_caldav_server": "Connect to CalDav (Beta)",
|
||||
"calendar_url": "Calendar URL",
|
||||
"apple_server_generate_password": "Generate an app specific password to use with Cal.com at",
|
||||
"credentials_stored_encrypted": "Your credentials will be stored and encrypted.",
|
||||
"it_stored_encrypted": "It will be stored and encrypted.",
|
||||
"go_to_app_store": "Go to App Store",
|
||||
"calendar_error": "Something went wrong, try reconnecting your calendar with all necessary permissions",
|
||||
"calendar_error": "Try reconnecting your calendar with all necessary permissions",
|
||||
"set_your_phone_number": "Set a phone number for the meeting",
|
||||
"calendar_no_busy_slots": "There are no busy slots",
|
||||
"display_location_label": "Display on booking page",
|
||||
@@ -1197,12 +1194,12 @@
|
||||
"event_one": "event",
|
||||
"event_other": "events",
|
||||
"profile_team_description": "Manage settings for your team profile",
|
||||
"members_team_description": "Users that are in the group. Apes together strong!",
|
||||
"members_team_description": "Users that are in the group",
|
||||
"team_url": "Team URL",
|
||||
"delete_team": "Delete Team",
|
||||
"team_members": "Team members",
|
||||
"more": "More",
|
||||
"more_page_footer": "We view the mobile application as an extension of the web application. If you are performing any complication actions, please refer back to the web application.",
|
||||
"more_page_footer": "We view the mobile application as an extension of the web application. If you are performing any complicated actions, please refer back to the web application.",
|
||||
"workflow_example_1": "Send SMS reminder 24 hours before event starts to attendee",
|
||||
"workflow_example_2": "Send custom SMS when event is rescheduled to attendee",
|
||||
"workflow_example_3": "Send custom email when new event is booked to host",
|
||||
@@ -1256,14 +1253,47 @@
|
||||
"create_first_api_key_description": "API keys allow other apps to communicate with Cal.com",
|
||||
"back_to_signin": "Back to sign in",
|
||||
"reset_link_sent": "Reset link sent",
|
||||
"password_reset_email":"An email is on it’s way to {{email}} with instructions to reset your password.",
|
||||
"password_reset_leading":"If you did not receive the email soon, check that the email address you entered is correct, check your spam folder or reach out to support if the issue persists.",
|
||||
"password_updated":"Password updated!",
|
||||
"password_reset_email": "An email is on it’s way to {{email}} with instructions to reset your password.",
|
||||
"password_reset_leading": "If you don't receive an email soon, check that the email address you entered is correct, check your spam folder or reach out to support if the issue persists.",
|
||||
"password_updated": "Password updated!",
|
||||
"pending_payment": "Pending payment",
|
||||
"confirmation_page_rainbow": "Token gate your event with tokens or NFTs on Ethereum, Polygon, and more.",
|
||||
"not_on_cal": "Not on Cal.com",
|
||||
"no_calendar_installed": "No calendar installed",
|
||||
"no_calendar_installed_description": "You have not yet connected any of your calendars",
|
||||
"add_a_calendar": "Add a calendar",
|
||||
"change_email_hint": "You may need to log out and back in to see any change take effect"
|
||||
"change_email_hint": "You may need to log out and back in to see any change take effect",
|
||||
"confirm_password_change_email": "Please confirm your password before changing your email address",
|
||||
"seats": "seats",
|
||||
"every_app_published": "Every app published on the Cal.com App Store is open source and thoroughly tested via peer reviews. Nevertheless, Cal.com, Inc. does not endorse or certify these apps unless they are published by Cal.com. If you encounter inappropriate content or behaviour please report it.",
|
||||
"report_app": "Report app",
|
||||
"team_name_required": "Team name required",
|
||||
"how_additional_inputs_as_variables": "How to use Additional Inputs as Variables",
|
||||
"format": "Format",
|
||||
"uppercase_for_letters": "Use uppercase for all letters",
|
||||
"replace_whitespaces_underscores": "Replace whitespaces with underscores",
|
||||
"manage_billing": "Manage billing",
|
||||
"manage_billing_description": "Manage all things billing",
|
||||
"billing_freeplan_title": "You're currently on the FREE plan",
|
||||
"billing_freeplan_description": "We work better in teams. Extend your workflows with round-robin and collective events and make advanced routing forms",
|
||||
"billing_freeplan_cta": "Try now",
|
||||
"billing_manage_details_title": "View and manage your billing details",
|
||||
"billing_manage_details_description": "View and edit your billing details, as well as cancel your subscription.",
|
||||
"billing_portal": "Billing portal",
|
||||
"billing_help_title": "Need anything else?",
|
||||
"billing_help_description": "If you need any further help with billing, our support team are here to help.",
|
||||
"billing_help_cta": "Contact support",
|
||||
"ignore_special_characters": "Ignore special characters in your Additional Input label. Use only letters and numbers",
|
||||
"retry": "Retry",
|
||||
"fetching_calendars_error": "There was a problem fetching your calendars. Please <1>try again</1> or reach out to customer support.",
|
||||
"calendar_connection_fail": "Calendar connection failed",
|
||||
"booking_confirmation_success": "Booking confirmation succeeded",
|
||||
"booking_confirmation_fail": "Booking confirmation failed",
|
||||
"we_wont_show_again": "We won't show this again",
|
||||
"couldnt_update_timezone": "We couldn't update the timezone",
|
||||
"updated_timezone_to": "Updated timezone to {{formattedCurrentTz}}",
|
||||
"update_timezone": "Update timezone",
|
||||
"update_timezone_question": "Update Timezone?",
|
||||
"update_timezone_description": "It seems like your local timezone has changed to {{formattedCurrentTz}}. It's very important to have the correct timezone to prevent bookings at undesired times. Do you want to update it?",
|
||||
"dont_update": "Don't update"
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "Utilice el enlace de abajo para restablecer su contraseña",
|
||||
"hey_there": "Hola,",
|
||||
"forgot_your_password_calcom": "¿Has olvidado tu contraseña? - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | Tipo de Evento",
|
||||
"delete_webhook_confirmation_message": "¿Está seguro de que desea eliminar este webhook? Ya no recibirá datos de reuniones de Cal.com en una URL específica, en tiempo real, cuando se programe o cancele un evento.",
|
||||
"confirm_delete_webhook": "Sí, elimina este webhook",
|
||||
"edit_webhook": "Editar Webhook",
|
||||
@@ -143,6 +142,7 @@
|
||||
"view_public_page": "Ver página pública",
|
||||
"sign_out": "Cerrar sesión",
|
||||
"add_another": "Añadir otro",
|
||||
"install_another": "Instalar otro",
|
||||
"until": "hasta el",
|
||||
"powered_by": "con tecnología de",
|
||||
"unavailable": "Indisponible",
|
||||
@@ -157,6 +157,7 @@
|
||||
"checkbox": "Casilla",
|
||||
"is_required": "Es requerido",
|
||||
"required": "Requerido",
|
||||
"optional": "Opcional",
|
||||
"input_type": "Tipo de entrada",
|
||||
"rejected": "Rechazado",
|
||||
"unconfirmed": "Sin confirmar",
|
||||
@@ -227,7 +228,9 @@
|
||||
"connect_your_calendar_instructions": "Conecta tu calendario para comprobar automáticamente los horarios ocupados y los nuevos eventos a medida que se programan.",
|
||||
"set_up_later": "Configurar más tarde",
|
||||
"current_time": "Hora Actual",
|
||||
"details": "Detalles",
|
||||
"welcome": "Bienvenido",
|
||||
"welcome_back": "Bienvenido de nuevo",
|
||||
"welcome_to_calcom": "Bienvenido a Cal.com",
|
||||
"welcome_instructions": "Cuéntanos cómo llamarte y dinos la zona horaria en la que estás. Podrás editarla más tarde.",
|
||||
"connect_caldav": "Conectar con el servidor de CalDav",
|
||||
@@ -240,6 +243,7 @@
|
||||
"when": "Cuándo",
|
||||
"where": "Donde",
|
||||
"add_to_calendar": "Añadir al Calendario",
|
||||
"add_another_calendar": "Añadir otro calendario",
|
||||
"other": "Otro",
|
||||
"emailed_you_and_attendees": "Le enviamos a usted y a los demás asistentes una invitación al calendario con todos los detalles.",
|
||||
"emailed_you_and_attendees_recurring": "Le enviamos a usted y a los demás asistentes una invitación al calendario para el primero de estos eventos recurrentes.",
|
||||
@@ -261,6 +265,8 @@
|
||||
"enter_new_password": "Introduce la nueva contraseña que te gustaría para tu cuenta.",
|
||||
"reset_password": "Restablecer Contraseña",
|
||||
"change_your_password": "Cambiar Contraseña",
|
||||
"show_password": "Mostrar contraseña",
|
||||
"hide_password": "Ocultar contraseña",
|
||||
"try_again": "Volver a Intentarlo",
|
||||
"request_is_expired": "Esta solicitud ha caducado.",
|
||||
"reset_instructions": "Introduzca el email asociado a su cuenta y le enviaremos un enlace para restablecer su contraseña.",
|
||||
@@ -288,7 +294,7 @@
|
||||
"cannot_cancel_booking": "No puedes cancelar esta reserva",
|
||||
"reschedule_instead": "En su lugar, también puedes reprogramarlo.",
|
||||
"event_is_in_the_past": "El evento es en el pasado",
|
||||
"cancelling_event_recurring": "El evento es una instancia de un evento recurrente.",
|
||||
"cancelling_event_recurring": "El evento es un caso de un evento recurrente.",
|
||||
"cancelling_all_recurring": "Todas estas son instancias restantes en el evento recurrente.",
|
||||
"error_with_status_code_occured": "Se produjo un error con el código de estado {{status}}.",
|
||||
"booking_already_cancelled": "Esta reserva ya fue cancelada",
|
||||
@@ -306,6 +312,7 @@
|
||||
"recurring_bookings": "Tan pronto como alguien reserve una reunión recurrente con usted, aparecerá aquí.",
|
||||
"past_bookings": "Sus reservas pasadas aparecerán aquí.",
|
||||
"cancelled_bookings": "Las reservas canceladas aparecerán aquí.",
|
||||
"unconfirmed_bookings": "Las reservas sin confirmar aparecerán aquí.",
|
||||
"on": "en",
|
||||
"and": "y",
|
||||
"calendar_shows_busy_between": "Tu calendario te muestra como ocupado entre",
|
||||
@@ -346,6 +353,7 @@
|
||||
"booking_cancelled": "Reserva Cancelada",
|
||||
"booking_rescheduled": "Reserva habilitada",
|
||||
"booking_created": "Reserva Creada",
|
||||
"meeting_ended": "Reunión finalizada",
|
||||
"form_submitted": "Formulario enviado",
|
||||
"event_triggers": "Activadores de Eventos",
|
||||
"subscriber_url": "Url del suscriptor",
|
||||
@@ -488,6 +496,9 @@
|
||||
"lets_create_first_administrator_user": "Vamos a crear el primer usuario administrador.",
|
||||
"new_member": "Nuevo Miembro",
|
||||
"invite": "Invitación",
|
||||
"add_team_members": "Añadir miembros del equipo",
|
||||
"add_team_members_description": "Invite a otros para unirse a su equipo",
|
||||
"add_team_member": "Añadir miembro del equipo",
|
||||
"invite_new_member": "Invita a un Nuevo Miembro",
|
||||
"invite_new_team_member": "Invita a alguien a tu equipo.",
|
||||
"change_member_role": "Cambiar rol del miembro del equipo",
|
||||
@@ -600,6 +611,7 @@
|
||||
"create_first_team_and_invite_others": "Crea tu primer equipo e invita a otros usuarios a trabajar contigo.",
|
||||
"create_team_to_get_started": "Crea un equipo para empezar",
|
||||
"teams": "Equipos",
|
||||
"team": "Equipo",
|
||||
"team_billing": "Facturación del equipo",
|
||||
"upgrade_to_flexible_pro_title": "Hemos cambiado la facturación de los equipos",
|
||||
"upgrade_to_flexible_pro_message": "Hay miembros en tu equipo sin asiento. Actualiza al plan Pro para cubrir los asientos restantes.",
|
||||
@@ -620,7 +632,9 @@
|
||||
"billing": "Facturación",
|
||||
"manage_your_billing_info": "Gestione su información de facturación y cancele su suscripción.",
|
||||
"availability": "Disponibilidad",
|
||||
"edit_availability": "Editar disponibilidad",
|
||||
"configure_availability": "Configure los horarios en los que está disponible para realizar reservas.",
|
||||
"copy_times_to": "Copiar tiempos en",
|
||||
"change_weekly_schedule": "Cambia tu Horario Semanal",
|
||||
"logo": "Logotipo",
|
||||
"error": "Error",
|
||||
@@ -635,6 +649,7 @@
|
||||
"event_name_tooltip": "Nombre que aparecerá en los calendarios",
|
||||
"meeting_with_user": "Reunión con {ATTENDEE}",
|
||||
"additional_inputs": "Campos Adicionales",
|
||||
"additional_input_description": "Requerir que el planificador introduzca entradas adicionales antes de confirmar la reserva",
|
||||
"label": "Etiqueta",
|
||||
"placeholder": "Marcador de posición",
|
||||
"type": "Tipo",
|
||||
@@ -649,6 +664,8 @@
|
||||
"disable_guests": "Desactivar Invitados",
|
||||
"disable_guests_description": "Desactiva agregar invitados adicionales al hacer la reserva.",
|
||||
"private_link": "Generar una URL privada",
|
||||
"private_link_label": "Enlace privado",
|
||||
"private_link_hint": "Su enlace privado se regenera después de cada uso",
|
||||
"copy_private_link": "Copiar enlace privado",
|
||||
"private_link_description": "Generar una URL privada para compartir sin exponer su nombre de usuario de Cal",
|
||||
"invitees_can_schedule": "Los invitados pueden programar",
|
||||
@@ -690,16 +707,21 @@
|
||||
"delete_account_confirmation_message": "¿Estás seguro de que quieres eliminar tu cuenta de Cal.com? Cualquier persona con la que hayas compartido el enlace de tu cuenta ya no podrá reservar con ella y cualquier preferencia que hayas guardado se perderá.",
|
||||
"integrations": "Integraciones",
|
||||
"apps": "Aplicaciones",
|
||||
"category_apps": "Aplicaciones {{category}}",
|
||||
"app_store": "App Store",
|
||||
"app_store_description": "Conectar personas, tecnología y lugar de trabajo.",
|
||||
"settings": "Ajustes",
|
||||
"event_type_moved_successfully": "El tipo de evento se ha movido correctamente",
|
||||
"next_step_text": "Siguiente paso",
|
||||
"next_step": "Saltar paso",
|
||||
"prev_step": "Paso anterior",
|
||||
"install": "Instalar",
|
||||
"installed": "Instalado",
|
||||
"active_install_one": "{{count}} instalación activa",
|
||||
"active_install_other": "{{count}} instalaciones activas",
|
||||
"globally_install": "Instalada globalmente",
|
||||
"app_successfully_installed": "Aplicación instalada correctamente",
|
||||
"app_could_not_be_installed": "No se ha podido instalar la aplicación",
|
||||
"disconnect": "Desconectar",
|
||||
"embed_your_calendar": "Incorpora tu calendario en tu página web",
|
||||
"connect_your_favourite_apps": "Conecta tus aplicaciones favoritas.",
|
||||
@@ -710,6 +732,7 @@
|
||||
"connect_additional_calendar": "Conectar calendario adicional",
|
||||
"conferencing": "Conferencias",
|
||||
"calendar": "Calendario",
|
||||
"payments": "Pagos",
|
||||
"not_installed": "No instalado",
|
||||
"error_password_mismatch": "Las contraseñas no coinciden.",
|
||||
"error_required_field": "Este campo es requerido.",
|
||||
@@ -743,9 +766,22 @@
|
||||
"import_from": "Importar desde",
|
||||
"access_token": "Token de acceso",
|
||||
"visit_roadmap": "Hoja de ruta",
|
||||
"featured_categories": "Categorías destacadas",
|
||||
"popular_categories": "Categorías populares",
|
||||
"number_apps_one": "{{count}} aplicación",
|
||||
"number_apps_other": "{{count}} aplicaciones",
|
||||
"trending_apps": "Aplicaciones populares",
|
||||
"explore_apps": "Aplicaciones {{category}}",
|
||||
"installed_apps": "Aplicaciones instaladas",
|
||||
"no_category_apps": "No hay aplicaciones {{category}}",
|
||||
"no_category_apps_description_calendar": "Añada una aplicación de calendario para comprobar si hay conflictos y evitar reservas dobles",
|
||||
"no_category_apps_description_conferencing": "Intente añadir una aplicación de conferencias para integrar las videollamadas con sus clientes",
|
||||
"no_category_apps_description_payment": "Añada una aplicación de pagos para facilitar las transacciones con sus clientes",
|
||||
"no_category_apps_description_other": "Añada cualquier otro tipo de aplicación para hacer todo tipo de tareas",
|
||||
"installed_app_calendar_description": "Configure los calendarios para comprobar conflictos y evitar reservas dobles.",
|
||||
"installed_app_conferencing_description": "Añada sus aplicaciones de videoconferencia favoritas para sus reuniones",
|
||||
"installed_app_payment_description": "Configure qué servicios de procesamiento de pagos se van a usar al cobrarles a sus clientes.",
|
||||
"installed_app_other_description": "Todas sus aplicaciones instaladas de otras categorías.",
|
||||
"empty_installed_apps_headline": "No hay aplicaciones instaladas",
|
||||
"empty_installed_apps_description": "Las aplicaciones le permiten mejorar su flujo de trabajo y mejorar su tiempo calendario de forma significativa.",
|
||||
"empty_installed_apps_button": "Explore la tienda de aplicaciones",
|
||||
@@ -766,6 +802,7 @@
|
||||
"remove": "Eliminar",
|
||||
"add": "Añadir",
|
||||
"installed_one": "Instalado",
|
||||
"installed_other": "{{count}} instaladas",
|
||||
"verify_wallet": "Verificar billetera",
|
||||
"connect_metamask": "Conectar Metamask",
|
||||
"create_events_on": "Crear eventos en:",
|
||||
@@ -783,6 +820,8 @@
|
||||
"set_to_default": "Establecer como predeterminado",
|
||||
"new_schedule_btn": "Nuevo horario",
|
||||
"add_new_schedule": "Añadir un nuevo horario",
|
||||
"add_new_calendar": "Añadir un calendario nuevo",
|
||||
"set_calendar": "Especifique dónde se añadirán los eventos nuevos cuando haya reservado.",
|
||||
"delete_schedule": "Eliminar horario",
|
||||
"schedule_created_successfully": "Horario {{scheduleName}} creado con éxito",
|
||||
"availability_updated_successfully": "Disponibilidad actualizada correctamente",
|
||||
@@ -883,7 +922,7 @@
|
||||
"zapier_setup_instructions": "<0>Inicie sesión en su cuenta de Zapier y cree un nuevo Zap.</0><1>Seleccione Cal.com cómo su aplicación Trigger. También elija su evento Trigger.</1><2>Seleccione su cuenta e ingrese su Clave API única.</2><3>Pruebe su Trigger.</3><4>¡Listo!</4>",
|
||||
"install_zapier_app": "Instale primero la aplicación Zapier desde la App Store.",
|
||||
"connect_apple_server": "Conectar con Apple Server",
|
||||
"connect_caldav_server": "Conectar con CalDav Server",
|
||||
"connect_caldav_server": "Conectar con CalDav (Beta)",
|
||||
"calendar_url": "URL del calendario",
|
||||
"apple_server_generate_password": "Genere una contraseña específica de la aplicación para utilizarla con Cal.com en",
|
||||
"credentials_stored_encrypted": "Sus credenciales serán almacenadas y cifradas.",
|
||||
@@ -936,6 +975,7 @@
|
||||
"workflows": "Flujos de trabajo",
|
||||
"new_workflow_btn": "Flujo de trabajo nuevo",
|
||||
"add_new_workflow": "Agregar un flujo de trabajo nuevo",
|
||||
"reschedule_event_trigger": "cuando el evento se vuelva a programar",
|
||||
"trigger": "Desencadenante",
|
||||
"triggers": "Desencadenantes",
|
||||
"action": "Acción",
|
||||
@@ -963,10 +1003,13 @@
|
||||
"change_username_standard_to_premium": "Como estás cambiando de un nombre de usuario estándar a un nombre de usuario premium, se te llevará al pago para actualizarlo.",
|
||||
"change_username_premium_to_standard": "Como estás cambiando de un nombre de usuario premium a un nombre de usuario estándar, se te llevará al pago para bajar de categoría.",
|
||||
"go_to_stripe_billing": "Ir a facturación",
|
||||
"stripe_description": "Se requiere un pago para las reservas (0,5 % + 0,10 € de comisión por transacción)",
|
||||
"trial_expired": "Tu prueba ha caducado",
|
||||
"remove_app": "Eliminar aplicación",
|
||||
"yes_remove_app": "Sí, eliminar aplicación",
|
||||
"are_you_sure_you_want_to_remove_this_app": "¿Está seguro de que desea eliminar esta aplicación?",
|
||||
"app_removed_successfully": "Aplicación eliminada correctamente",
|
||||
"error_removing_app": "Error al eliminar la aplicación",
|
||||
"web_conference": "Conferencia web",
|
||||
"number_for_sms_reminders": "Número de teléfono (para recordatorios de SMS)",
|
||||
"requires_confirmation": "Requiere confirmación",
|
||||
@@ -1022,18 +1065,205 @@
|
||||
"navigate": "Navegar",
|
||||
"open": "Abrir",
|
||||
"close": "Cerrar",
|
||||
"team_feature_teams": "Esta es una función Team. Actualice a Team para ver la disponibilidad de su equipo.",
|
||||
"team_feature_workflows": "Esta es una función Team. Actualice a Team para automatizar los recordadorios y las notificaciones de eventos con flujos de trabajo.",
|
||||
"show_eventtype_on_profile": "Mostrar en perfil",
|
||||
"embed": "Insertar",
|
||||
"new_username": "Nombre de usuario nuevo",
|
||||
"current_username": "Nombre de usuario actual",
|
||||
"example_1": "Ejemplo 1",
|
||||
"example_2": "Ejemplo 2",
|
||||
"additional_input_label": "Etiqueta de entrada adicional",
|
||||
"company_size": "Tamaño de la empresa",
|
||||
"what_help_needed": "¿Con qué necesitas ayuda?",
|
||||
"variable_format": "Formato variable",
|
||||
"webhook_subscriber_url_reserved": "La URL del suscriptor de Webhook ya está definida",
|
||||
"custom_input_as_variable_info": "Ignore todos los caracteres especiales de la etiqueta de entrada adicional (utilice solo letras y números), utilice mayúsculas para todas las letras y sustituya los espacios en blanco por guiones bajos.",
|
||||
"using_additional_inputs_as_variables": "¿Cómo usar entradas adicionales como variables?",
|
||||
"download_desktop_app": "Descargar la aplicación de escritorio",
|
||||
"set_ping_link": "Establecer enlace Ping",
|
||||
"rate_limit_exceeded": "Límite de tasa excedido",
|
||||
"when_something_happens": "Cuando algo sucede",
|
||||
"action_is_performed": "Se realiza una acción",
|
||||
"test_action": "Probar acción",
|
||||
"notification_sent": "Notificación enviada",
|
||||
"no_input": "No hay entrada",
|
||||
"test_workflow_action": "Probar acción de flujo de trabajo",
|
||||
"send_sms": "Enviar SMS",
|
||||
"send_sms_to_number": "¿Está seguro de que desea enviar un SMS al {{number}}?",
|
||||
"missing_connected_calendar": "No hay ningún calendario predeterminado conectado",
|
||||
"connect_your_calendar_and_link": "Puede conectar su calendario desde <1>aquí</1>.",
|
||||
"default_calendar_selected": "Calendario predeterminado",
|
||||
"hide_from_profile": "Ocultar del perfil",
|
||||
"event_setup_tab_title": "Configuración del evento",
|
||||
"event_limit_tab_title": "Límites",
|
||||
"event_limit_tab_description": "Con qué frecuencia se puede reservar",
|
||||
"event_advanced_tab_description": "Ajustes del calendario y más...",
|
||||
"event_advanced_tab_title": "Avanzado",
|
||||
"select_which_cal": "Seleccione a qué calendario añadir reservas",
|
||||
"custom_event_name": "Nombre de evento personalizado",
|
||||
"custom_event_name_description": "Cree nombres de eventos personalizados para que el evento aparezca en el calendario",
|
||||
"2fa_required": "Se requiere autenticación de dos factores",
|
||||
"incorrect_2fa": "Código de autenticación de dos factores incorrecto",
|
||||
"which_event_type_apply": "¿A qué tipo de evento se aplicará esto?",
|
||||
"no_workflows_description": "Los flujos de trabajo permiten una automatización simple para enviar notificaciones y recordatorios que le permiten crear procesos para sus eventos.",
|
||||
"create_workflow": "Crear un flujo de trabajo",
|
||||
"do_this": "Hacer esto",
|
||||
"turn_off": "Apagar",
|
||||
"settings_updated_successfully": "Configuración actualizada correctamente",
|
||||
"error_updating_settings": "Error al actualizar la configuración",
|
||||
"personal_cal_url": "Mi URL personal de Cal",
|
||||
"bio_hint": "Algunas frases sobre usted, esta información aparecerá en la página de su URL personal.",
|
||||
"delete_account_modal_title": "Eliminar cuenta",
|
||||
"confirm_delete_account_modal": "¿Está seguro que desea eliminar su cuenta de Cal.com?",
|
||||
"delete_my_account": "Eliminar mi cuenta",
|
||||
"start_of_week": "Inicio de la semana",
|
||||
"select_calendars": "Seleccione los calendarios en los que desee comprobar conflictos para evitar reservas dobles.",
|
||||
"check_for_conflicts": "Comprobar conflictos",
|
||||
"adding_events_to": "Agregando eventos a",
|
||||
"follow_system_preferences": "Siga las preferencias del sistema",
|
||||
"custom_brand_colors": "Colores de marca personalizados",
|
||||
"customize_your_brand_colors": "Personalice el color de su propia marca en su página de reservas.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "Elimina cualquier marca relacionada con Cal, p. ej., \"Powered by Cal\".",
|
||||
"profile_picture": "Imagen de perfil",
|
||||
"upload": "Cargar",
|
||||
"web3": "Web3",
|
||||
"rainbow_token_gated": "Este tipo de evento tiene acceso cerrado por token.",
|
||||
"rainbow_connect_wallet_gate": "Conecte su billetera si posee <1>{{name}}</1> (<3>{{symbol}}</3>).",
|
||||
"rainbow_insufficient_balance": "Su billetera conectada no contiene suficiente <1>{{symbol}}</1>.",
|
||||
"rainbow_sign_message_request": "Firme la solicitud de mensaje en su billetera.",
|
||||
"rainbow_signature_error": "Error al solicitar la firma desde su billetera.",
|
||||
"token_address": "Dirección del token",
|
||||
"blockchain": "Blockchain",
|
||||
"old_password": "Contraseña anterior",
|
||||
"secure_password": "Su contraseña nueva supersegura",
|
||||
"error_updating_password": "Error al actualizar la contraseña",
|
||||
"two_factor_auth": "Autenticación de dos factores",
|
||||
"recurring_event_tab_description": "Configurar un calendario repetitivo",
|
||||
"today": "hoy",
|
||||
"appearance": "Aspecto",
|
||||
"appearance_subtitle": "Administre los ajustes para el aspecto de su reserva",
|
||||
"my_account": "Mi cuenta",
|
||||
"general": "General",
|
||||
"calendars": "Calendarios",
|
||||
"2fa_auth": "Autenticación de dos factores",
|
||||
"invoices": "Facturas",
|
||||
"embeds": "Inserciones",
|
||||
"impersonation": "Suplantación",
|
||||
"users": "Usuarios",
|
||||
"profile_description": "Administre los ajustes para su perfil de Cal",
|
||||
"general_description": "Administre los ajustes para su idioma y zona horaria",
|
||||
"calendars_description": "Configure cómo deben interactuar sus tipos de eventos con sus calendarios",
|
||||
"appearance_description": "Administre los ajustes para el aspecto de su reserva",
|
||||
"conferencing_description": "Administre sus aplicaciones de videoconferencia para sus reuniones",
|
||||
"password_description": "Administre los ajustes para las contraseñas de su cuenta",
|
||||
"2fa_description": "Administre los ajustes para las contraseñas de su cuenta",
|
||||
"we_just_need_basic_info": "Solo necesitamos información básica para configurar su perfil.",
|
||||
"skip": "Omitir",
|
||||
"do_this_later": "Hacerlo más tarde",
|
||||
"set_availability_getting_started_subtitle_1": "Defina los intervalos de tiempo en los que esté disponible",
|
||||
"set_availability_getting_started_subtitle_2": "Puede personalizar todo esto más tarde en la página de disponibilidad.",
|
||||
"connect_calendars_from_app_store": "Puede añadir más calendarios desde la tienda de aplicaciones",
|
||||
"connect_conference_apps": "Conectar aplicaciones de conferencia",
|
||||
"connect_calendar_apps": "Conectar aplicaciones de calendario",
|
||||
"connect_payment_apps": "Conectar aplicaciones de pago",
|
||||
"connect_other_apps": "Conectar otras aplicaciones",
|
||||
"current_step_of_total": "Paso {{currentStep}} de {{maxSteps}}",
|
||||
"add_variable": "Añadir variable",
|
||||
"custom_phone_number": "Número de teléfono personalizado",
|
||||
"message_template": "Plantilla de mensaje",
|
||||
"email_subject": "Asunto del correo electrónico",
|
||||
"add_dynamic_variables": "Añadir variables de texto dinámico",
|
||||
"event_name_info": "El nombre del tipo de evento",
|
||||
"event_date_info": "La fecha del evento",
|
||||
"event_time_info": "La hora de inicio del evento",
|
||||
"location_info": "La ubicación del evento",
|
||||
"organizer_name_info": "Tu Nombre",
|
||||
"additional_notes_info": "Las notas adicionales de la reserva",
|
||||
"attendee_name_info": "El nombre de la persona que reserva",
|
||||
"to": "Para",
|
||||
"attendee_required_enter_number": "Será necesario que el asistente introduzca un número de teléfono al reservar",
|
||||
"workflow_turned_on_successfully": "El flujo de trabajo {{workflowName}} se ajustó como {{offOn}} correctamente",
|
||||
"download_responses": "Descargar respuestas",
|
||||
"create_your_first_form": "Cree su primer formulario",
|
||||
"create_your_first_form_description": "Con los formularios de orientación puede hacer preguntas de cualificación y llegar a la persona o tipo de evento correctos.",
|
||||
"create_your_first_webhook": "Cree su primer Webhook",
|
||||
"create_your_first_webhook_description": "Con Webhooks puede recibir datos de reuniones en tiempo real cuando suceda algo en Cal.com.",
|
||||
"for_a_maximum_of": "Para un máximo de",
|
||||
"event_one": "evento",
|
||||
"event_other": "eventos",
|
||||
"profile_team_description": "Administre ajustes para el perfil de su equipo",
|
||||
"members_team_description": "Usuarios que están en el grupo",
|
||||
"team_url": "URL del equipo",
|
||||
"delete_team": "Eliminar equipo",
|
||||
"team_members": "Miembros del equipo",
|
||||
"more": "Más",
|
||||
"more_page_footer": "Vemos la aplicación móvil como una extensión de la aplicación web. Si está realizando acciones complicadas, consulte la aplicación web.",
|
||||
"workflow_example_1": "Enviar recordatorio por SMS al asistente 24 horas antes de que el evento comience",
|
||||
"workflow_example_2": "Enviar SMS personalizado al asistente cuando se cambie la planificación del evento",
|
||||
"workflow_example_3": "Enviar correo electrónico personalizado al anfitrión cuando se reserve un evento nuevo",
|
||||
"workflow_example_4": "Enviar recordatorio por correo electrónico al asistente 1 hora antes de que el evento comience",
|
||||
"workflow_example_5": "Enviar correo electrónico personalizado al anfitrión cuando se cambie la planificación del evento",
|
||||
"workflow_example_6": "Enviar SMS personalizado al anfitrión cuando se reserve un evento nuevo",
|
||||
"welcome_to_cal_header": "¡Bienvenido a Cal.com!",
|
||||
"edit_form_later_subtitle": "Podrá editarlo más tarde.",
|
||||
"connect_calendar_later": "Conectaré mi calendario más tarde",
|
||||
"set_my_availability_later": "Indicaré mi disponibilidad más tarde",
|
||||
"problem_saving_user_profile": "Se produjo un problema al guardar sus datos. Inténtelo de nuevo o póngase en contacto con el servicio de atención al cliente.",
|
||||
"purchase_missing_seats": "Comprar licencias que faltan",
|
||||
"slot_length": "Duración de franja",
|
||||
"booking_appearance": "Aspecto de la reserva",
|
||||
"appearance_team_description": "Administre los ajustes para el aspecto de la reserva de su equipo",
|
||||
"only_owner_change": "Solo el propietario de este equipo puede hacer cambios en la reserva del equipo ",
|
||||
"team_disable_cal_branding_description": "Elimina cualquier marca relacionada con Cal, p. ej., \"Powered by Cal\"",
|
||||
"invited_by_team": "{{teamName}} le invitó a unirse a su equipo como {{role}}",
|
||||
"token_invalid_expired": "El token no es válido o ha caducado.",
|
||||
"exchange_add": "Conectar a Microsoft Exchange",
|
||||
"exchange_authentication": "Método de autenticación",
|
||||
"exchange_authentication_standard": "Autenticación básica",
|
||||
"exchange_authentication_ntlm": "Autenticación NTLM",
|
||||
"exchange_compression": "Compresión GZip",
|
||||
"routing_forms_description": "Puede ver todos los formularios y rutas que ha creado aquí.",
|
||||
"add_new_form": "Añadir formulario nuevo",
|
||||
"form_description": "Cree su formulario para dirigirlo a un agente de reservas",
|
||||
"copy_link_to_form": "Copiar enlace al formulario",
|
||||
"theme": "Tema",
|
||||
"theme_applies_note": "Solo se aplica a sus páginas de reservas públicas",
|
||||
"theme_light": "Claro",
|
||||
"theme_dark": "Oscuro"
|
||||
"theme_dark": "Oscuro",
|
||||
"theme_system": "Predeterminado del sistema",
|
||||
"add_a_team": "Añadir un equipo",
|
||||
"saml_config": "Configuración de SAML",
|
||||
"add_webhook_description": "Reciba datos de reuniones en tiempo real cuando suceda algo en Cal.com",
|
||||
"triggers_when": "Se activa cuando",
|
||||
"test_webhook": "Antes de crear, ejecute una prueba de ping.",
|
||||
"enable_webhook": "Activar Webhook",
|
||||
"add_webhook": "Añadir Webhook",
|
||||
"webhook_edited_successfully": "Webhook guardado",
|
||||
"webhooks_description": "Reciba datos de reuniones en tiempo real cuando suceda algo en Cal.com",
|
||||
"api_keys_description": "Genere claves API para acceder a su propia cuenta",
|
||||
"new_api_key": "Clave API nueva",
|
||||
"active": "Activo",
|
||||
"api_key_updated": "Nombre de clave API actualizado",
|
||||
"api_key_update_failed": "Error al actualizar el nombre de la clave API",
|
||||
"embeds_title": "Inserción de iframe HTML",
|
||||
"embeds_description": "Inserte todos sus tipos de eventos en su sitio web",
|
||||
"create_first_api_key": "Cree su primera clave API",
|
||||
"create_first_api_key_description": "Las claves API permiten a otras aplicaciones comunicarse con Cal.com",
|
||||
"back_to_signin": "Volver a iniciar sesión",
|
||||
"reset_link_sent": "Enlace de restablecimiento enviado",
|
||||
"password_reset_email": "Se ha enviado un correo electrónico a {{email}} con instrucciones para restablecer su contraseña.",
|
||||
"password_updated": "¡Contraseña actualizada!",
|
||||
"pending_payment": "Pago pendiente",
|
||||
"confirmation_page_rainbow": "Cierre el acceso a su evento con tokens o NFT en Ethereum, Polygon, etc.",
|
||||
"not_on_cal": "No está en Cal.com",
|
||||
"no_calendar_installed": "No hay ningún calendario instalado",
|
||||
"no_calendar_installed_description": "Aún no ha conectado ninguno de sus calendarios",
|
||||
"add_a_calendar": "Añadir un calendario",
|
||||
"change_email_hint": "Quizá necesite cerrar sesión y volver a entrar para ver que el cambio surta efecto",
|
||||
"confirm_password_change_email": "Confirme su contraseña antes de cambiar su dirección de correo electrónico",
|
||||
"seats": "asientos",
|
||||
"every_app_published": "Las aplicaciones publicadas en la tienda de aplicaciones de Cal.com son de código abierto y se han sometido a pruebas exhaustivas con las reseñas de pares. Sin embargo, Cal.com, Inc. no avala ni certifica estas aplicaciones a menos que Cal.com las haya publicado expresamente. Si encuentra contenido o comportamiento inapropiados, informe de ello.",
|
||||
"report_app": "Denunciar aplicación"
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "Utilisez le lien ci-dessous pour réinitialiser votre mot de passe",
|
||||
"hey_there": "Salut,",
|
||||
"forgot_your_password_calcom": "Mot de passe oublié ? - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | Type d'événement",
|
||||
"delete_webhook_confirmation_message": "Êtes-vous sûr de vouloir supprimer ce webhook ? Vous ne recevrez plus les données de réunion de Cal.com à une URL spécifiée, en temps réel, lorsqu'un événement est programmé ou annulé.",
|
||||
"confirm_delete_webhook": "Oui, supprimer le webhook",
|
||||
"edit_webhook": "Modifier le webhook",
|
||||
@@ -143,6 +142,7 @@
|
||||
"view_public_page": "Voir la page publique",
|
||||
"sign_out": "Se déconnecter",
|
||||
"add_another": "Ajouter un autre",
|
||||
"install_another": "Installer un(e) autre",
|
||||
"until": "jusqu'au",
|
||||
"powered_by": "alimenté par",
|
||||
"unavailable": "Indisponible",
|
||||
@@ -157,6 +157,7 @@
|
||||
"checkbox": "Case à cocher",
|
||||
"is_required": "Est obligatoire",
|
||||
"required": "Obligatoire",
|
||||
"optional": "Facultatif",
|
||||
"input_type": "Type d'entrée",
|
||||
"rejected": "Refusé",
|
||||
"unconfirmed": "Non confirmé",
|
||||
@@ -227,7 +228,9 @@
|
||||
"connect_your_calendar_instructions": "Connectez votre calendrier pour vérifier automatiquement vos disponibilités au fur et à mesure qu'elles évoluent.",
|
||||
"set_up_later": "Configurer plus tard",
|
||||
"current_time": "Heure actuelle",
|
||||
"details": "Détails",
|
||||
"welcome": "Bienvenue",
|
||||
"welcome_back": "Bienvenue de nouveau",
|
||||
"welcome_to_calcom": "Bienvenue sur Cal.com",
|
||||
"welcome_instructions": "Dites-nous comment nous devons vous appeler et votre fuseau horaire. Vous pourrez modifier ceci plus tard.",
|
||||
"connect_caldav": "Se connecter au serveur CalDav",
|
||||
@@ -240,6 +243,7 @@
|
||||
"when": "Quand",
|
||||
"where": "Où",
|
||||
"add_to_calendar": "Ajouter au calendrier",
|
||||
"add_another_calendar": "Ajouter un autre calendrier",
|
||||
"other": "Autre",
|
||||
"emailed_you_and_attendees": "Nous vous avons envoyé, à vous et aux autres participants, une invitation par e-mail avec tous les détails.",
|
||||
"emailed_you_and_attendees_recurring": "Nous vous avons envoyé, à vous et aux autres participants, une invitation au calendrier pour le premier de ces événements récurrents.",
|
||||
@@ -261,6 +265,8 @@
|
||||
"enter_new_password": "Entrez le nouveau mot de passe que vous souhaitez pour votre compte.",
|
||||
"reset_password": "Réinitialiser le mot de passe",
|
||||
"change_your_password": "Modifiez votre mot de passe",
|
||||
"show_password": "Afficher le mot de passe",
|
||||
"hide_password": "Masquer le mot de passe",
|
||||
"try_again": "Réessayer",
|
||||
"request_is_expired": "Cette requête est expirée.",
|
||||
"reset_instructions": "Saisissez l'adresse e-mail associée à votre compte et nous vous enverrons un lien pour réinitialiser votre mot de passe.",
|
||||
@@ -306,6 +312,7 @@
|
||||
"recurring_bookings": "Dès que quelqu'un réserve une réunion récurrente avec vous, elle apparaît ici.",
|
||||
"past_bookings": "Vos réservations passées apparaîtront ici.",
|
||||
"cancelled_bookings": "Vos réservations annulées apparaîtront ici.",
|
||||
"unconfirmed_bookings": "Vos réservations non confirmées apparaîtront ici.",
|
||||
"on": "le",
|
||||
"and": "et",
|
||||
"calendar_shows_busy_between": "Votre calendrier vous montre comme étant occupé entre",
|
||||
@@ -346,6 +353,7 @@
|
||||
"booking_cancelled": "Réservation Annulée",
|
||||
"booking_rescheduled": "Réservation Modifiée",
|
||||
"booking_created": "Réservation Créée",
|
||||
"meeting_ended": "Réunion terminée",
|
||||
"form_submitted": "Formulaire envoyé",
|
||||
"event_triggers": "Déclencheurs d'événements",
|
||||
"subscriber_url": "URL de l'abonné",
|
||||
@@ -488,6 +496,9 @@
|
||||
"lets_create_first_administrator_user": "Nous allons créer le premier utilisateur administrateur.",
|
||||
"new_member": "Nouveau membre",
|
||||
"invite": "Inviter",
|
||||
"add_team_members": "Ajouter des membres de l'équipe",
|
||||
"add_team_members_description": "Invitez d'autres personnes à rejoindre votre équipe",
|
||||
"add_team_member": "Ajouter un membre de l'équipe",
|
||||
"invite_new_member": "Inviter un nouveau membre",
|
||||
"invite_new_team_member": "Invitez quelqu'un à rejoindre votre équipe.",
|
||||
"change_member_role": "Changer le rôle du membre de l'équipe",
|
||||
@@ -600,6 +611,7 @@
|
||||
"create_first_team_and_invite_others": "Créez votre première équipe et invitez d'autres utilisateurs à travailler avec vous.",
|
||||
"create_team_to_get_started": "Créer une équipe pour commencer",
|
||||
"teams": "Équipes",
|
||||
"team": "Equipe",
|
||||
"team_billing": "Facturation d'équipe",
|
||||
"upgrade_to_flexible_pro_title": "Nous avons modifié la facturation pour les équipes",
|
||||
"upgrade_to_flexible_pro_message": "Des membres dans votre équipe n'ont pas de place. Mettez à niveau votre offre pro pour couvrir les places manquantes.",
|
||||
@@ -620,7 +632,9 @@
|
||||
"billing": "Facturation",
|
||||
"manage_your_billing_info": "Gérez vos informations de facturation et annulez votre abonnement.",
|
||||
"availability": "Disponibilité",
|
||||
"edit_availability": "Modifier la disponibilité",
|
||||
"configure_availability": "Déclarez les moments où vous êtes disponible pour un rendez-vous.",
|
||||
"copy_times_to": "Copier les heures vers",
|
||||
"change_weekly_schedule": "Modifier votre planning hebdomadaire",
|
||||
"logo": "Logo",
|
||||
"error": "Erreur",
|
||||
@@ -635,6 +649,7 @@
|
||||
"event_name_tooltip": "Le nom qui apparaîtra dans les calendriers",
|
||||
"meeting_with_user": "Rendez-vous avec {ATTENDEE}",
|
||||
"additional_inputs": "Entrées additionnelles",
|
||||
"additional_input_description": "Exiger que le planificateur saisisse des entrées supplémentaires avant que la réservation soit confirmée",
|
||||
"label": "Libellé",
|
||||
"placeholder": "Placeholder",
|
||||
"type": "Type",
|
||||
@@ -649,6 +664,8 @@
|
||||
"disable_guests": "Désactiver les invité. es",
|
||||
"disable_guests_description": "Désactiver l'ajout d'invité·es supplémentaires lors de la réservation.",
|
||||
"private_link": "Générer une URL privée",
|
||||
"private_link_label": "Lien privé",
|
||||
"private_link_hint": "Votre lien privé sera régénéré après chaque utilisation",
|
||||
"copy_private_link": "Copier le lien privé",
|
||||
"private_link_description": "Générer une URL privée à partager sans exposer votre nom d'utilisateur Cal",
|
||||
"invitees_can_schedule": "Les invité·es peuvent réserver",
|
||||
@@ -690,16 +707,21 @@
|
||||
"delete_account_confirmation_message": "Êtes-vous sûr de vouloir supprimer votre compte Cal.com ? Toute personne avec qui vous avez partagé le lien de votre compte ne pourra plus réserver en utilisant ce lien et toutes les préférences que vous avez enregistrées seront perdues.",
|
||||
"integrations": "Intégrations",
|
||||
"apps": "Applications",
|
||||
"category_apps": "{{category}} applications",
|
||||
"app_store": "App Store",
|
||||
"app_store_description": "Connecter les personnes, la technologie et l'espace de travail.",
|
||||
"settings": "Paramètres",
|
||||
"event_type_moved_successfully": "Le type d'évènement a bien été déplacé",
|
||||
"next_step_text": "Prochaine étape",
|
||||
"next_step": "Passer l'étape",
|
||||
"prev_step": "Étape précédente",
|
||||
"install": "Installer",
|
||||
"installed": "Installé",
|
||||
"active_install_one": "{{count}} installation active",
|
||||
"active_install_other": "{{count}} installations actives",
|
||||
"globally_install": "Installation globale",
|
||||
"app_successfully_installed": "Application installée avec succès",
|
||||
"app_could_not_be_installed": "Impossible d'installer l'application",
|
||||
"disconnect": "Déconnecter",
|
||||
"embed_your_calendar": "Intégrez votre calendrier dans votre page web",
|
||||
"connect_your_favourite_apps": "Connectez vos applications favorites.",
|
||||
@@ -710,6 +732,7 @@
|
||||
"connect_additional_calendar": "Connecter un calendrier supplémentaire",
|
||||
"conferencing": "Conférence",
|
||||
"calendar": "Calendrier",
|
||||
"payments": "Paiements",
|
||||
"not_installed": "Non installé",
|
||||
"error_password_mismatch": "Les mots de passe ne correspondent pas.",
|
||||
"error_required_field": "Ce champ est requis.",
|
||||
@@ -743,9 +766,22 @@
|
||||
"import_from": "Importer depuis",
|
||||
"access_token": "Jeton d'accès",
|
||||
"visit_roadmap": "Feuille de route",
|
||||
"featured_categories": "Catégories en vedette",
|
||||
"popular_categories": "Catégories populaires",
|
||||
"number_apps_one": "{{count}} Application",
|
||||
"number_apps_other": "{{count}} Applications",
|
||||
"trending_apps": "Applications populaires",
|
||||
"explore_apps": "{{category}} applications",
|
||||
"installed_apps": "Applications installées",
|
||||
"no_category_apps": "Aucune application {{category}}",
|
||||
"no_category_apps_description_calendar": "Ajouter une application de calendrier pour vérifier les conflits et éviter les doubles réservations",
|
||||
"no_category_apps_description_conferencing": "Essayez d'ajouter une application de conférence pour interconnecter les appels vidéo avec vos clients",
|
||||
"no_category_apps_description_payment": "Ajouter une application de paiement pour faciliter les transactions entre vous et vos clients",
|
||||
"no_category_apps_description_other": "Ajouter n'importe quel autre type d'application pour faire toutes sortes de choses",
|
||||
"installed_app_calendar_description": "Définir le(s) calendrier(s) pour vérifier les conflits pour éviter les doubles réservations.",
|
||||
"installed_app_conferencing_description": "Ajoutez vos applications de vidéoconférence préférées pour vos réunions",
|
||||
"installed_app_payment_description": "Configurez les services de traitement de paiement à utiliser lors de la facturation de vos clients.",
|
||||
"installed_app_other_description": "Toutes vos applications installées à partir d'autres catégories.",
|
||||
"empty_installed_apps_headline": "Aucune application installée",
|
||||
"empty_installed_apps_description": "Les applications vous permettent d'améliorer votre flux de travail et votre processus de planification de façon significative.",
|
||||
"empty_installed_apps_button": "Explorer l'App Store",
|
||||
@@ -766,6 +802,7 @@
|
||||
"remove": "Supprimer",
|
||||
"add": "Ajouter",
|
||||
"installed_one": "Installé",
|
||||
"installed_other": "{{count}} installé(e)(s)",
|
||||
"verify_wallet": "Vérifier le portefeuille",
|
||||
"connect_metamask": "Connecter Metamask",
|
||||
"create_events_on": "Créer des événements le :",
|
||||
@@ -783,6 +820,8 @@
|
||||
"set_to_default": "Définir par défaut",
|
||||
"new_schedule_btn": "Nouveau programme",
|
||||
"add_new_schedule": "Ajouter un nouveau programme",
|
||||
"add_new_calendar": "Ajouter un nouveau calendrier",
|
||||
"set_calendar": "Définir où ajouter de nouveaux événements lors de la réservation.",
|
||||
"delete_schedule": "Supprimer le programme",
|
||||
"schedule_created_successfully": "{{scheduleName}} programme créé",
|
||||
"availability_updated_successfully": "{{scheduleName}} programme mis à jour",
|
||||
@@ -850,7 +889,7 @@
|
||||
"edit_booking": "Modifier la réservation",
|
||||
"reschedule_booking": "Reporter la réservation",
|
||||
"former_time": "Ancienne heure",
|
||||
"confirmation_page_gif": "Gif pour la page de confirmation",
|
||||
"confirmation_page_gif": "Ajouter un GIF à la page de confirmation",
|
||||
"search": "Rechercher",
|
||||
"impersonate": "Connexion autre",
|
||||
"user_impersonation_heading": "Connexion en tant qu'autre utilisateur",
|
||||
@@ -877,7 +916,7 @@
|
||||
"choose_ways_put_cal_site": "Choisissez une des façons suivantes de mettre Cal sur votre site.",
|
||||
"setting_up_zapier": "Configuration de votre intégration Zapier",
|
||||
"generate_api_key": "Générer une clé Api",
|
||||
"generate_api_key_description": "Générer une clé API à utiliser avec Cal.com à",
|
||||
"generate_api_key_description": "Générer une clé API à utiliser avec Cal.com sur",
|
||||
"your_unique_api_key": "Votre clé API unique",
|
||||
"copy_safe_api_key": "Copiez cette clé API et conservez-la dans un endroit sûr. Si vous perdez cette clé, vous devrez en générer une nouvelle.",
|
||||
"zapier_setup_instructions": "<0>Connectez-vous à votre compte Zapier et créez un nouveau Zap.</0><1>Sélectionnez Cal.com comme application Trigger. Choisissez également un événement Trigger. </1><2>Choisissez votre compte, puis saisissez votre clé API unique.</2><3>Testez votre Trigger.</3><4>Vous pouvez vous lancer !</4>",
|
||||
@@ -936,6 +975,7 @@
|
||||
"workflows": "Workflows",
|
||||
"new_workflow_btn": "Nouveau workflow",
|
||||
"add_new_workflow": "Ajouter un nouveau workflow",
|
||||
"reschedule_event_trigger": "lorsque l'événement est reprogrammé",
|
||||
"trigger": "Déclencheur",
|
||||
"triggers": "Déclencheurs",
|
||||
"action": "Action",
|
||||
@@ -963,10 +1003,13 @@
|
||||
"change_username_standard_to_premium": "Puisque vous passez d'un nom d'utilisateur standard à un premium, vous allez être envoyé au paiement pour valider le changement.",
|
||||
"change_username_premium_to_standard": "Puisque vous passez d'un nom d'utilisateur premium à un standard, vous allez être envoyé au paiement pour valider la rétrogradation.",
|
||||
"go_to_stripe_billing": "Accéder à la facturation",
|
||||
"stripe_description": "Nécessite un paiement pour les réservations (0,5% + 0,10 € de commission par transaction)",
|
||||
"trial_expired": "Votre période d'essai a expiré",
|
||||
"remove_app": "Supprimer l'application",
|
||||
"yes_remove_app": "Oui, supprimer l'application",
|
||||
"are_you_sure_you_want_to_remove_this_app": "Voulez-vous vraiment supprimer cette application ?",
|
||||
"app_removed_successfully": "Application supprimée avec succès",
|
||||
"error_removing_app": "Erreur lors de la suppression de l'application",
|
||||
"web_conference": "Conférence en ligne",
|
||||
"number_for_sms_reminders": "Numéro de téléphone (pour les rappels par SMS)",
|
||||
"requires_confirmation": "Nécessite une confirmation",
|
||||
@@ -1022,18 +1065,201 @@
|
||||
"navigate": "Naviguer",
|
||||
"open": "Ouvrir",
|
||||
"close": "Fermer",
|
||||
"team_feature_teams": "Ceci est une fonctionnalité d'équipe. Mettez à niveau vers l'équipe pour voir la disponibilité de votre équipe.",
|
||||
"team_feature_workflows": "Ceci est une fonctionnalité d'équipe. Mettez à niveau vers l'équipe pour automatiser vos notifications et rappels d'événements avec les flux de travail.",
|
||||
"show_eventtype_on_profile": "Afficher sur le profil",
|
||||
"embed": "Intégré",
|
||||
"new_username": "Nouveau nom d'utilisateur",
|
||||
"current_username": "Nom d'utilisateur actuel",
|
||||
"example_1": "Exemple 1",
|
||||
"example_2": "Exemple 2",
|
||||
"additional_input_label": "Libellé de saisie supplémentaire",
|
||||
"company_size": "Taille de l'entreprise",
|
||||
"what_help_needed": "Pour quoi avez-vous besoin d'aide ?",
|
||||
"variable_format": "Format des variables",
|
||||
"webhook_subscriber_url_reserved": "L'url d'abonné au Webhook est déjà définie",
|
||||
"custom_input_as_variable_info": "Ignorer tous les caractères spéciaux du libellé de saisie supplémentaire (utiliser uniquement des lettres et des chiffres), utiliser des majuscules pour toutes les lettres et remplacer les espaces par des tirets bas.",
|
||||
"using_additional_inputs_as_variables": "Comment utiliser des entrées supplémentaires en tant que variables ?",
|
||||
"download_desktop_app": "Télécharger l'application de bureau",
|
||||
"set_ping_link": "Définir le lien Ping",
|
||||
"rate_limit_exceeded": "Limite dépassée",
|
||||
"when_something_happens": "Quand quelque chose se passe",
|
||||
"action_is_performed": "Une action est effectuée",
|
||||
"test_action": "Tester l'action",
|
||||
"notification_sent": "Notification envoyée",
|
||||
"no_input": "Aucune entrée",
|
||||
"test_workflow_action": "Tester l'effet du workflow",
|
||||
"send_sms": "Envoyer un SMS",
|
||||
"send_sms_to_number": "Êtes-vous sûr de vouloir envoyer un SMS au {{number}}?",
|
||||
"missing_connected_calendar": "Aucun calendrier par défaut connecté",
|
||||
"connect_your_calendar_and_link": "Vous pouvez connecter votre calendrier depuis <1>ici</1>.",
|
||||
"default_calendar_selected": "Calendrier par défaut",
|
||||
"hide_from_profile": "Masquer du profil",
|
||||
"event_setup_tab_title": "Configuration de l'événement",
|
||||
"event_limit_tab_title": "Limites",
|
||||
"event_limit_tab_description": "Fréquence de réservation",
|
||||
"event_advanced_tab_description": "Paramètres du calendrier & plus...",
|
||||
"event_advanced_tab_title": "Avancé",
|
||||
"select_which_cal": "Sélectionnez le calendrier auquel ajouter des réservations",
|
||||
"custom_event_name": "Nom de l'événement personnalisé",
|
||||
"custom_event_name_description": "Créer des noms d'événements personnalisés à afficher sur l'événement du calendrier",
|
||||
"2fa_required": "Authentification en deux étapes requise",
|
||||
"incorrect_2fa": "Code d'authentification à deux facteurs incorrect",
|
||||
"which_event_type_apply": "À quel type d'événement s'appliquera-t-il ?",
|
||||
"no_workflows_description": "Les workflows permettent une automatisation simple pour envoyer des notifications et des rappels vous permettant de construire des processus autour de vos événements.",
|
||||
"create_workflow": "Créer un workflow",
|
||||
"do_this": "Effectuer ceci",
|
||||
"turn_off": "Désactiver",
|
||||
"settings_updated_successfully": "Paramètres mis à jour avec succès",
|
||||
"error_updating_settings": "Erreur lors de la mise à jour des paramètres",
|
||||
"personal_cal_url": "Mon URL Cal personnelle",
|
||||
"bio_hint": "Quelques phrases à propos de vous. Ces informations apparaîtront sur votre page d'url personnelle.",
|
||||
"delete_account_modal_title": "Supprimer le compte",
|
||||
"confirm_delete_account_modal": "Êtes-vous sûr de vouloir supprimer votre compte Cal.com ?",
|
||||
"delete_my_account": "Supprimer mon compte",
|
||||
"start_of_week": "Début de la semaine",
|
||||
"select_calendars": "Sélectionnez quels calendriers vous voulez vérifier les conflits pour éviter les doubles réservations.",
|
||||
"check_for_conflicts": "Vérifier les conflits",
|
||||
"adding_events_to": "Ajout d'événements à",
|
||||
"follow_system_preferences": "Suivre les préférences système",
|
||||
"custom_brand_colors": "Couleurs de marque personnalisées",
|
||||
"customize_your_brand_colors": "Personnalisez votre propre couleur de marque dans votre page de réservation.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "Supprime les marques associées à Cal, c'est-à-dire \"Powered by Cal.\"",
|
||||
"profile_picture": "Photo de profil",
|
||||
"upload": "Télécharger",
|
||||
"web3": "Web3",
|
||||
"rainbow_token_gated": "Ce type d'événement nécessite un jeton d'authentification.",
|
||||
"rainbow_connect_wallet_gate": "Connectez votre portefeuille si vous possédez <1>{{name}}</1> (<3>{{symbol}}</3>).",
|
||||
"rainbow_insufficient_balance": "Votre portefeuille connecté ne contient pas assez de <1>{{symbol}}</1>.",
|
||||
"rainbow_sign_message_request": "Signez la demande de message sur votre portefeuille.",
|
||||
"rainbow_signature_error": "Erreur lors de la demande de signature de votre portefeuille.",
|
||||
"token_address": "Adresse du jeton",
|
||||
"blockchain": "Blockchain",
|
||||
"old_password": "Ancien mot de passe",
|
||||
"secure_password": "Votre nouveau mot de passe super sécurisé",
|
||||
"error_updating_password": "Erreur lors de la mise à jour du mot de passe",
|
||||
"two_factor_auth": "Authentification en deux étapes",
|
||||
"recurring_event_tab_description": "Configurez un calendrier récurrent",
|
||||
"today": "aujourd'hui",
|
||||
"appearance": "Apparence",
|
||||
"appearance_subtitle": "Gérer les paramètres pour votre apparence de réservation",
|
||||
"my_account": "Mon compte",
|
||||
"general": "Général",
|
||||
"calendars": "Calendriers",
|
||||
"2fa_auth": "Authentification à deux facteurs",
|
||||
"invoices": "Factures",
|
||||
"embeds": "Intègre",
|
||||
"impersonation": "Identification",
|
||||
"users": "Utilisateurs",
|
||||
"profile_description": "Gérer les paramètres de votre profil cal",
|
||||
"general_description": "Gérer les paramètres pour votre langue et votre fuseau horaire",
|
||||
"calendars_description": "Configurez la manière dont vos types d'événements interagissent avec vos calendriers",
|
||||
"appearance_description": "Gérer les paramètres pour votre apparence de réservation",
|
||||
"conferencing_description": "Gérez vos applications de vidéoconférence pour vos réunions",
|
||||
"password_description": "Gérer les paramètres pour les mots de passe de votre compte",
|
||||
"2fa_description": "Gérer les paramètres pour les mots de passe de votre compte",
|
||||
"we_just_need_basic_info": "Nous avons juste besoin de quelques informations de base pour configurer votre profil.",
|
||||
"skip": "Ignorer",
|
||||
"do_this_later": "Effectuer ceci plus tard",
|
||||
"set_availability_getting_started_subtitle_1": "Définir les plages de temps lorsque vous êtes disponible",
|
||||
"set_availability_getting_started_subtitle_2": "Vous pouvez personnaliser tout cela plus tard dans la page de disponibilité.",
|
||||
"connect_calendars_from_app_store": "Vous pouvez ajouter plus de calendriers depuis l'App Store",
|
||||
"connect_conference_apps": "Connecter les applications de conférence",
|
||||
"connect_calendar_apps": "Connecter les applications de calendrier",
|
||||
"connect_payment_apps": "Connecter les applications de paiement",
|
||||
"connect_other_apps": "Connecter d'autres applications",
|
||||
"current_step_of_total": "Étape {{currentStep}} sur {{maxSteps}}",
|
||||
"add_variable": "Ajouter une variable",
|
||||
"custom_phone_number": "Numéro de téléphone personnalisé",
|
||||
"message_template": "Modèle de message",
|
||||
"email_subject": "Objet",
|
||||
"add_dynamic_variables": "Ajouter des variables de texte dynamiques",
|
||||
"event_name_info": "Nom du type d'événement",
|
||||
"event_date_info": "Date de l'évènement",
|
||||
"event_time_info": "Heure de début de l'événement",
|
||||
"location_info": "Lieu de l'événement",
|
||||
"organizer_name_info": "Votre nom",
|
||||
"additional_notes_info": "Notes supplémentaires de réservation",
|
||||
"attendee_name_info": "Nom de la personne réservant",
|
||||
"to": "À/vers",
|
||||
"attendee_required_enter_number": "Le participant devra entrer un numéro de téléphone lors de la réservation",
|
||||
"workflow_turned_on_successfully": "Le workflow {{workflowName}} a été {{offOn}} avec succès",
|
||||
"download_responses": "Télécharger les réponses",
|
||||
"create_your_first_form": "Créer votre premier formulaire",
|
||||
"create_your_first_form_description": "Grâce aux formulaires d'acheminement, vous pouvez poser des questions de qualification et vous diriger vers la personne ou le type d'événement appropriés.",
|
||||
"create_your_first_webhook": "Créez votre premier Webhook",
|
||||
"create_your_first_webhook_description": "Avec les Webhooks, vous pouvez recevoir des données de réunion en temps réel lorsque quelque chose se passe sur Cal.com.",
|
||||
"for_a_maximum_of": "Pour un maximum de",
|
||||
"event_one": "événement",
|
||||
"event_other": "événements",
|
||||
"profile_team_description": "Gérer les paramètres de votre profil d'équipe",
|
||||
"members_team_description": "Utilisateurs membres du groupe",
|
||||
"team_url": "URL de l'équipe",
|
||||
"delete_team": "Supprimer l'équipe",
|
||||
"team_members": "Membres de l'équipe",
|
||||
"more": "En savoir plus",
|
||||
"more_page_footer": "Nous considérons l'application mobile comme une extension de l'application web. Si vous effectuez des actions complexes, veuillez vous référer à l'application Web.",
|
||||
"workflow_example_1": "Envoyer un rappel SMS 24 heures avant le début de l'événement",
|
||||
"workflow_example_2": "Envoyer un SMS personnalisé lorsque l'événement est reprogrammé au participant",
|
||||
"workflow_example_3": "Envoyer un email personnalisé quand un nouvel événement est réservé à un hôte",
|
||||
"workflow_example_4": "Envoyer un rappel par e-mail 1 heure avant le début des événements",
|
||||
"workflow_example_5": "Envoyer un email personnalisé lorsque l'événement est reprogrammé à l'hôte",
|
||||
"workflow_example_6": "Envoyer un SMS personnalisé quand un nouvel événement est réservé à un hôte",
|
||||
"welcome_to_cal_header": "Bienvenue sur Cal.com !",
|
||||
"edit_form_later_subtitle": "Vous pourrez modifier cela plus tard.",
|
||||
"connect_calendar_later": "Je connecterai mon calendrier plus tard",
|
||||
"set_my_availability_later": "Je vais définir ma disponibilité plus tard",
|
||||
"problem_saving_user_profile": "Un problème est survenu lors de l'enregistrement de vos données. Veuillez réessayer ou contacter le service client.",
|
||||
"purchase_missing_seats": "Acheter des places manquantes",
|
||||
"slot_length": "Durée du créneau",
|
||||
"booking_appearance": "Apparence de réservation",
|
||||
"appearance_team_description": "Gérer les paramètres pour l'apparence de réservation de votre équipe",
|
||||
"only_owner_change": "Seul le propriétaire de cette équipe peut apporter des modifications à la réservation de l'équipe ",
|
||||
"team_disable_cal_branding_description": "Supprime les marques associées à Cal, c'est-à-dire \"Powered by Cal\"",
|
||||
"invited_by_team": "{{teamName}} vous a invité à rejoindre son équipe en tant que {{role}}",
|
||||
"token_invalid_expired": "Le jeton est invalide ou expiré.",
|
||||
"exchange_add": "Se connecter à Microsoft Exchange",
|
||||
"exchange_authentication": "Méthode d'authentification",
|
||||
"exchange_authentication_standard": "Authentification de base",
|
||||
"exchange_authentication_ntlm": "Authentification NTLM",
|
||||
"exchange_compression": "Compression GZip",
|
||||
"routing_forms_description": "Vous pouvez voir tous les formulaires et tous les parcours que vous avez créés ici.",
|
||||
"add_new_form": "Ajouter un nouveau formulaire",
|
||||
"form_description": "Créez votre formulaire pour acheminer un organisateur",
|
||||
"copy_link_to_form": "Copier le lien vers le formulaire",
|
||||
"theme": "Thème",
|
||||
"theme_applies_note": "Ceci s'applique uniquement à vos pages publiques de réservation",
|
||||
"theme_light": "Clair",
|
||||
"theme_dark": "Sombre"
|
||||
"theme_dark": "Sombre",
|
||||
"theme_system": "Défaut du système",
|
||||
"add_a_team": "Ajouter une équipe",
|
||||
"saml_config": "Configuration SAML",
|
||||
"add_webhook_description": "Recevoir les données de la réunion en temps réel lorsque quelque chose se passe sur Cal.com",
|
||||
"triggers_when": "Se déclenche quand",
|
||||
"enable_webhook": "Activer le Webhook",
|
||||
"add_webhook": "Ajouter un Webhook",
|
||||
"webhook_edited_successfully": "Webhook enregistré",
|
||||
"webhooks_description": "Recevez les données de la réunion en temps réel lorsque quelque chose se passe sur Cal.com",
|
||||
"api_keys_description": "Générer des clés API pour accéder à votre propre compte",
|
||||
"new_api_key": "Nouvelle clé API",
|
||||
"active": "Actif",
|
||||
"api_key_updated": "Nom de clé API mis à jour",
|
||||
"api_key_update_failed": "Erreur de mise à jour du nom de la clé API",
|
||||
"embeds_title": "Intégration d'iframe HTML",
|
||||
"embeds_description": "Intégrez tous vos types d'événements sur votre site web",
|
||||
"create_first_api_key": "Créez votre première clé API",
|
||||
"create_first_api_key_description": "Les clés API permettent à d'autres applications de communiquer avec Cal.com",
|
||||
"back_to_signin": "Retour à la connexion",
|
||||
"reset_link_sent": "Lien de réinitialisation envoyé",
|
||||
"password_updated": "Mot de passe mis à jour !",
|
||||
"pending_payment": "Paiement en attente",
|
||||
"not_on_cal": "Pas sur Cal.com",
|
||||
"no_calendar_installed": "Aucun calendrier installé",
|
||||
"no_calendar_installed_description": "Vous n'avez pas encore connecté vos calendriers",
|
||||
"add_a_calendar": "Ajouter au calendrier",
|
||||
"change_email_hint": "Vous devrez peut-être vous déconnecter et vous reconnecter pour que le changement prenne effet",
|
||||
"seats": "places",
|
||||
"every_app_published": "Chaque application publiée sur l'App Store de Cal.com est open source et soigneusement testée via les évaluations par les pairs. Néanmoins, Cal.com, Inc. n'approuve pas ou ne certifie pas ces applications à moins qu'elles ne soient publiées par Cal.com. Si vous rencontrez un contenu ou un comportement inapproprié, veuillez le signaler.",
|
||||
"report_app": "Signaler une application"
|
||||
}
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "נא להשתמש בקישור הבא כדי לאפס את הסיסמה",
|
||||
"hey_there": "שלום,",
|
||||
"forgot_your_password_calcom": "שכחתה את הסיסמה? - cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | סוג האירוע",
|
||||
"delete_webhook_confirmation_message": "בטוח שברצונך למחוק את ה-Webhook הזה? אם אירוע יבוטל או שייקבע עבורו מועד, לא תקבל יותר נתוני פגישות מ-Cal.com בזמן אמת או בכתובת ה-URL שציינת.",
|
||||
"confirm_delete_webhook": "כן, אני רוצה למחוק את ה-Webhook",
|
||||
"edit_webhook": "עריכת Webhook",
|
||||
@@ -809,7 +808,7 @@
|
||||
"booking_full": "אין יותר מקומות זמינים",
|
||||
"api_keys": "מפתחות ממשק תכנות יישומים (API)",
|
||||
"api_key": "מפתח ממשק תכנות יישומים (API)",
|
||||
"test_api_key": "בדיקת מפתח ממשק תכנות יישומים (API)",
|
||||
"test_api_key": "לבדוק מפתח ממשק תכנות יישומים (API)",
|
||||
"test_passed": "הבדיקה עברה בהצלחה!",
|
||||
"test_failed": "הבדיקה נכשלה",
|
||||
"provide_api_key": "נא לציין מפתח ממשק תכנות יישומים (API)",
|
||||
@@ -850,7 +849,7 @@
|
||||
"edit_booking": "עריכת ההזמנה",
|
||||
"reschedule_booking": "שנה/י את מועד ההזמנה",
|
||||
"former_time": "המועד הקודם",
|
||||
"confirmation_page_gif": "נא להוסיף קובץ GIF לדף האישור",
|
||||
"confirmation_page_gif": "להוסיף קובץ GIF לדף האישור",
|
||||
"search": "חיפוש",
|
||||
"impersonate": "התחזות",
|
||||
"user_impersonation_heading": "התחזות למשתמשים",
|
||||
|
||||
@@ -112,7 +112,6 @@
|
||||
"use_link_to_reset_password": "Usa il link qui sotto per reimpostare la tua password",
|
||||
"hey_there": "Ciao,",
|
||||
"forgot_your_password_calcom": "Hai dimenticato la password? - Cal.com",
|
||||
"event_type_title": "{{eventTypeTitle}} | Tipo di evento",
|
||||
"delete_webhook_confirmation_message": "Sei sicuro di voler eliminare questo webhook? Non riceverai più Cal.com dati di riunione in un URL specificato, in tempo reale, quando un evento è programmato o annullato.",
|
||||
"confirm_delete_webhook": "Sì, elimina webhook",
|
||||
"edit_webhook": "Modifica Webhook",
|
||||
@@ -143,6 +142,7 @@
|
||||
"view_public_page": "Visualizza la pagina pubblica",
|
||||
"sign_out": "Esci",
|
||||
"add_another": "Aggiungi un altro",
|
||||
"install_another": "Installa un altro",
|
||||
"until": "fino a",
|
||||
"powered_by": "offerto da",
|
||||
"unavailable": "Non Disponibile",
|
||||
@@ -157,6 +157,7 @@
|
||||
"checkbox": "Casella di controllo",
|
||||
"is_required": "È obbligatorio",
|
||||
"required": "Obbligatorio",
|
||||
"optional": "Facoltativo",
|
||||
"input_type": "Tipo di input",
|
||||
"rejected": "Rifiutato",
|
||||
"unconfirmed": "Non Confermato",
|
||||
@@ -227,7 +228,9 @@
|
||||
"connect_your_calendar_instructions": "Collega il tuo calendario per controllare automaticamente gli orari occupati e i nuovi eventi appena vengono pianificati.",
|
||||
"set_up_later": "Configura più tardi",
|
||||
"current_time": "Orario corrente",
|
||||
"details": "Dettagli",
|
||||
"welcome": "Benvenuto",
|
||||
"welcome_back": "Bentornato",
|
||||
"welcome_to_calcom": "Benvenuto in Cal.com",
|
||||
"welcome_instructions": "Dimmi come chiamarti e facci sapere in che timezone sei. Potrai cambiarlo dopo.",
|
||||
"connect_caldav": "Connetti al server CalDav",
|
||||
@@ -240,6 +243,7 @@
|
||||
"when": "Quando",
|
||||
"where": "Dove",
|
||||
"add_to_calendar": "Aggiungi al calendario",
|
||||
"add_another_calendar": "Aggiungi un altro calendario",
|
||||
"other": "Altro",
|
||||
"emailed_you_and_attendees": "Abbiamo inviato via email a te e agli altri partecipanti un invito al calendario con tutti i dettagli.",
|
||||
"emailed_you_and_attendees_recurring": "Abbiamo inviato via email a te e agli altri partecipanti un invito al primo di questi eventi ricorrenti.",
|
||||
@@ -261,6 +265,8 @@
|
||||
"enter_new_password": "Inserisci la nuova password che desideri per il tuo account.",
|
||||
"reset_password": "Reimposta Password",
|
||||
"change_your_password": "Cambia la tua password",
|
||||
"show_password": "Mostra password",
|
||||
"hide_password": "Nascondi password",
|
||||
"try_again": "Prova Di Nuovo",
|
||||
"request_is_expired": "Quella richiesta è scaduta.",
|
||||
"reset_instructions": "Inserisci l'indirizzo email associato al tuo account e ti invieremo un link per reimpostare la password.",
|
||||
@@ -306,6 +312,7 @@
|
||||
"recurring_bookings": "Quando qualcuno prenoterà una riunione ricorrente con te, la riunione apparirà qui.",
|
||||
"past_bookings": "Le tue prenotazioni passate appariranno qui.",
|
||||
"cancelled_bookings": "Le tue prenotazioni passate appariranno qui.",
|
||||
"unconfirmed_bookings": "Le tue prenotazioni non confermate appariranno qui.",
|
||||
"on": "su",
|
||||
"and": "e",
|
||||
"calendar_shows_busy_between": "Il tuo calendario ti mostra come occupato tra",
|
||||
@@ -346,6 +353,7 @@
|
||||
"booking_cancelled": "Prenotazione Annullata",
|
||||
"booking_rescheduled": "Prenotazione Riprogrammata",
|
||||
"booking_created": "Prenotazione Creata",
|
||||
"meeting_ended": "Riunione terminata",
|
||||
"form_submitted": "Modulo inviato",
|
||||
"event_triggers": "Attivatori Evento",
|
||||
"subscriber_url": "Url del Subscriber",
|
||||
@@ -488,6 +496,9 @@
|
||||
"lets_create_first_administrator_user": "Creiamo il primo utente amministratore.",
|
||||
"new_member": "Nuovo Membro",
|
||||
"invite": "Invita",
|
||||
"add_team_members": "Aggiungi membri del team",
|
||||
"add_team_members_description": "Invita altri utenti a unirsi al tuo team",
|
||||
"add_team_member": "Aggiungi un membro del team",
|
||||
"invite_new_member": "Invita un nuovo membro",
|
||||
"invite_new_team_member": "Invita qualcuno nel tuo team.",
|
||||
"change_member_role": "Cambia il ruolo del membro del team",
|
||||
@@ -600,6 +611,7 @@
|
||||
"create_first_team_and_invite_others": "Crea il tuo primo team e invita altri utenti a lavorare insieme a te.",
|
||||
"create_team_to_get_started": "Crea una squadra per iniziare",
|
||||
"teams": "Team",
|
||||
"team": "Team",
|
||||
"team_billing": "Fatturazione team",
|
||||
"upgrade_to_flexible_pro_title": "Abbiamo cambiato la fatturazione per i team",
|
||||
"upgrade_to_flexible_pro_message": "Alcuni membri del tuo team non hanno un posto. Aggiorna il tuo piano Pro per coprire i posti mancanti.",
|
||||
@@ -620,7 +632,9 @@
|
||||
"billing": "Fatturazione",
|
||||
"manage_your_billing_info": "Gestisci le tue informazioni di fatturazione e annulla il tuo abbonamento.",
|
||||
"availability": "Disponibilità",
|
||||
"edit_availability": "Modifica disponibilità",
|
||||
"configure_availability": "Cambia quando sei disponibile per le prenotazioni.",
|
||||
"copy_times_to": "Copia orari in",
|
||||
"change_weekly_schedule": "Cambia il tuo programma settimanale",
|
||||
"logo": "Logo",
|
||||
"error": "Errore",
|
||||
@@ -635,6 +649,7 @@
|
||||
"event_name_tooltip": "Il nome che apparirà nei calendari",
|
||||
"meeting_with_user": "Riunione con {ATTENDEE}",
|
||||
"additional_inputs": "Input Aggiuntivi",
|
||||
"additional_input_description": "Chiedi al creatore del programma di inserire dei campi di input aggiuntivi prima che la prenotazione sia confermata",
|
||||
"label": "Etichetta",
|
||||
"placeholder": "Segnaposto",
|
||||
"type": "Tipo",
|
||||
@@ -649,6 +664,8 @@
|
||||
"disable_guests": "Disabilita Ospiti",
|
||||
"disable_guests_description": "Disabilita l'aggiunta di ulteriori ospiti durante la prenotazione.",
|
||||
"private_link": "Genera URL privato",
|
||||
"private_link_label": "Link privato",
|
||||
"private_link_hint": "Il tuo link privato verrà rigenerato dopo ogni utilizzo",
|
||||
"copy_private_link": "Copia link privato",
|
||||
"private_link_description": "Genera un URL privato da condividere senza mostrare il tuo nome utente Cal",
|
||||
"invitees_can_schedule": "Gli inviti possono programmare",
|
||||
@@ -690,16 +707,21 @@
|
||||
"delete_account_confirmation_message": "Sei sicuro di voler eliminare il tuo account Cal.com? Chiunque abbia il link del tuo account non potrà più utilizzarlo per prenotare e le preferenze che hai salvato andranno perse.",
|
||||
"integrations": "Integrazioni",
|
||||
"apps": "App",
|
||||
"category_apps": "App {{category}}",
|
||||
"app_store": "App Store",
|
||||
"app_store_description": "Collegare persone, tecnologia e luogo di lavoro.",
|
||||
"settings": "Impostazioni",
|
||||
"event_type_moved_successfully": "Il tipo di evento è stato spostato con successo",
|
||||
"next_step_text": "Prossimo passo",
|
||||
"next_step": "Salta passo",
|
||||
"prev_step": "Passo precedente",
|
||||
"install": "Installa",
|
||||
"installed": "Installato",
|
||||
"active_install_one": "{{count}} installazione attiva",
|
||||
"active_install_other": "{{count}} installazioni attive",
|
||||
"globally_install": "Installato a livello globale",
|
||||
"app_successfully_installed": "App installata correttamente",
|
||||
"app_could_not_be_installed": "Impossibile installare l'app",
|
||||
"disconnect": "Disconnetti",
|
||||
"embed_your_calendar": "Incorpora il tuo calendario nella tua pagina web",
|
||||
"connect_your_favourite_apps": "Collega le tue app preferite.",
|
||||
@@ -710,6 +732,7 @@
|
||||
"connect_additional_calendar": "Collega calendario aggiuntivo",
|
||||
"conferencing": "Conferenze",
|
||||
"calendar": "Calendario",
|
||||
"payments": "Pagamenti",
|
||||
"not_installed": "Non installato",
|
||||
"error_password_mismatch": "Le password non corrispondono.",
|
||||
"error_required_field": "Questo campo è richiesto.",
|
||||
@@ -743,9 +766,22 @@
|
||||
"import_from": "Importa da",
|
||||
"access_token": "Token di accesso",
|
||||
"visit_roadmap": "Roadmap",
|
||||
"featured_categories": "Categorie in evidenza",
|
||||
"popular_categories": "Categorie popolari",
|
||||
"number_apps_one": "{{count}} App",
|
||||
"number_apps_other": "{{count}} App",
|
||||
"trending_apps": "App di tendenza",
|
||||
"explore_apps": "App {{category}}",
|
||||
"installed_apps": "App installate",
|
||||
"no_category_apps": "Nessuna app {{category}}",
|
||||
"no_category_apps_description_calendar": "Aggiungi un'app di calendario per controllare i conflitti ed evitare doppie prenotazioni",
|
||||
"no_category_apps_description_conferencing": "Prova ad aggiungere un'app di conferenza per integrare le videochiamate con i clienti",
|
||||
"no_category_apps_description_payment": "Aggiungi un'app di pagamento per facilitare le transazioni con i clienti",
|
||||
"no_category_apps_description_other": "Aggiungi qualsiasi altra app per fare altre attività",
|
||||
"installed_app_calendar_description": "Imposta uno o più calendari per controllare i conflitti ed evitare doppie prenotazioni.",
|
||||
"installed_app_conferencing_description": "Aggiungi le tue app di videoconferenza preferite per le tue riunioni",
|
||||
"installed_app_payment_description": "Configura quale servizio di elaborazione dei pagamenti usare per addebitare i clienti.",
|
||||
"installed_app_other_description": "Tutte le app installate appartenenti alle altre categorie.",
|
||||
"empty_installed_apps_headline": "Nessuna app installata",
|
||||
"empty_installed_apps_description": "Le app consentono di ottimizzare il flusso di lavoro e di migliorare significativamente la pianificazione.",
|
||||
"empty_installed_apps_button": "Esplora l'App Store",
|
||||
@@ -766,6 +802,7 @@
|
||||
"remove": "Rimuovi",
|
||||
"add": "Aggiungi",
|
||||
"installed_one": "Installato",
|
||||
"installed_other": "{{count}} installate",
|
||||
"verify_wallet": "Verifica Wallet",
|
||||
"connect_metamask": "Connetti Metamask",
|
||||
"create_events_on": "Crea eventi su:",
|
||||
@@ -783,6 +820,8 @@
|
||||
"set_to_default": "Impostazione predefinita",
|
||||
"new_schedule_btn": "Nuovo programma",
|
||||
"add_new_schedule": "Aggiungi un nuovo programma",
|
||||
"add_new_calendar": "Aggiungi un nuovo calendario",
|
||||
"set_calendar": "Imposta dove aggiungere nuovi eventi quando vi è una prenotazione.",
|
||||
"delete_schedule": "Elimina programma",
|
||||
"schedule_created_successfully": "Programma {{scheduleName}} creato",
|
||||
"availability_updated_successfully": "Disponibilità aggiornata con successo",
|
||||
@@ -811,7 +850,7 @@
|
||||
"api_key": "Chiave API",
|
||||
"test_api_key": "Prova chiave API",
|
||||
"test_passed": "Test superato!",
|
||||
"test_failed": "Test non superato",
|
||||
"test_failed": "Prova non superata",
|
||||
"provide_api_key": "Fornisci chiave API",
|
||||
"api_key_modal_subtitle": "Le chiavi API consentono di effettuare chiamate API per il proprio account.",
|
||||
"api_keys_subtitle": "Genera chiavi API da usare per accedere al tuo account.",
|
||||
@@ -936,6 +975,7 @@
|
||||
"workflows": "Flussi di lavoro",
|
||||
"new_workflow_btn": "Nuovo flusso di lavoro",
|
||||
"add_new_workflow": "Aggiungi un nuovo flusso di lavoro",
|
||||
"reschedule_event_trigger": "quando un evento viene riprogrammato",
|
||||
"trigger": "Attivatore",
|
||||
"triggers": "Attivatori",
|
||||
"action": "Azione",
|
||||
@@ -963,10 +1003,13 @@
|
||||
"change_username_standard_to_premium": "Poiché stai passando da un nome utente standard a un nome utente premium, sarai portato alla pagina di pagamento per effettuare l'upgrade.",
|
||||
"change_username_premium_to_standard": "Poiché stai passando da un nome utente premium a un nome utente standard, sarai portato alla pagina di pagamento per effettuare il downgrade.",
|
||||
"go_to_stripe_billing": "Vai alla fatturazione",
|
||||
"stripe_description": "Richiedi pagamento per le prenotazioni (0,5% + 0,10 € di commissione per transazione)",
|
||||
"trial_expired": "Il periodo di prova è scaduto",
|
||||
"remove_app": "Rimuovi app",
|
||||
"yes_remove_app": "Sì, rimuovi l'app",
|
||||
"are_you_sure_you_want_to_remove_this_app": "Rimuovere questa app?",
|
||||
"app_removed_successfully": "App rimossa correttamente",
|
||||
"error_removing_app": "Errore durante la rimozione dell'app",
|
||||
"web_conference": "Conferenza Web",
|
||||
"number_for_sms_reminders": "Numero di telefono (per promemoria via SMS)",
|
||||
"requires_confirmation": "Richiede conferma",
|
||||
@@ -1022,20 +1065,205 @@
|
||||
"navigate": "Esplora",
|
||||
"open": "Apri",
|
||||
"close": "Chiudi",
|
||||
"team_feature_teams": "Questa è una funzionalità del piano Team. Effettua l'upgrade al piano Team per vedere la disponibilità del tuo team.",
|
||||
"team_feature_workflows": "Questa è una funzionalità del piano Team. Effettua l'upgrade al piano Team per automatizzare le notifiche e i promemoria degli eventi per i flussi di lavoro.",
|
||||
"show_eventtype_on_profile": "Mostra nel profilo",
|
||||
"embed": "Incorpora",
|
||||
"new_username": "Nuovo nome utente",
|
||||
"current_username": "Nome utente corrente",
|
||||
"example_1": "Esempio 1",
|
||||
"example_2": "Esempio 2",
|
||||
"additional_input_label": "Etichetta del campo di input aggiuntivo",
|
||||
"company_size": "Dimensione dell'azienda",
|
||||
"what_help_needed": "Per cosa ti serve assistenza?",
|
||||
"variable_format": "Formato variabile",
|
||||
"webhook_subscriber_url_reserved": "URL dell'abbonato Webhook già definito",
|
||||
"custom_input_as_variable_info": "Ignora tutti i caratteri speciali dell'etichetta del campo di input aggiuntivo (usa solo lettere e numeri), usa solo lettere maiuscole e sostituisci gli spazi con il trattino basso.",
|
||||
"using_additional_inputs_as_variables": "Come usare i campi di input aggiuntivi come variabili?",
|
||||
"download_desktop_app": "Scarica app desktop",
|
||||
"set_ping_link": "Imposta link di Ping",
|
||||
"rate_limit_exceeded": "Limite di frequenza superato",
|
||||
"when_something_happens": "Quando succede qualcosa",
|
||||
"action_is_performed": "Viene eseguita un'azione",
|
||||
"test_action": "Azione di test",
|
||||
"notification_sent": "Notifica inviata",
|
||||
"no_input": "Nessun input",
|
||||
"test_workflow_action": "Verifica azione del flusso di lavoro",
|
||||
"send_sms": "Invia SMS",
|
||||
"send_sms_to_number": "Inviare un SMS a {{number}}?",
|
||||
"missing_connected_calendar": "Nessun calendario predefinito collegato",
|
||||
"connect_your_calendar_and_link": "Puoi collegare il tuo calendario <1>qui</1>.",
|
||||
"default_calendar_selected": "Calendario predefinito",
|
||||
"hide_from_profile": "Nascondi nel profilo",
|
||||
"event_setup_tab_title": "Impostazione evento",
|
||||
"event_limit_tab_title": "Limiti",
|
||||
"event_limit_tab_description": "Con quale frequenza è possibile fare una prenotazione",
|
||||
"event_advanced_tab_description": "Impostazioni calendario e altro...",
|
||||
"event_advanced_tab_title": "Avanzate",
|
||||
"select_which_cal": "Seleziona il calendario al quale aggiungere prenotazioni",
|
||||
"custom_event_name": "Nome evento personalizzato",
|
||||
"custom_event_name_description": "Crea nomi di eventi personalizzati da visualizzare nell'evento del calendario",
|
||||
"2fa_required": "Autenticazione a due fattori necessaria",
|
||||
"incorrect_2fa": "Codice di autenticazione a due fattori errato",
|
||||
"which_event_type_apply": "A quale tipo di evento si applicherà?",
|
||||
"no_workflows_description": "I flussi di lavoro attivano una semplice automazione per inviare notifiche e promemoria che ti consentono di creare processi per i tuoi eventi.",
|
||||
"create_workflow": "Crea un flusso di lavoro",
|
||||
"do_this": "Esegui questa operazione",
|
||||
"turn_off": "Disattiva",
|
||||
"settings_updated_successfully": "Impostazioni aggiornate correttamente",
|
||||
"error_updating_settings": "Errore durante l'aggiornamento delle impostazioni",
|
||||
"personal_cal_url": "Il mio URL personale di Cal",
|
||||
"bio_hint": "Scrivi qualcosa di te. Queste informazioni appariranno nella tua pagina personale.",
|
||||
"delete_account_modal_title": "Elimina account",
|
||||
"confirm_delete_account_modal": "Eliminare il tuo account Cal.com?",
|
||||
"delete_my_account": "Elimina il mio account",
|
||||
"start_of_week": "Inizio della settimana",
|
||||
"select_calendars": "Seleziona su quali calendari desideri controllare i conflitti per evitare doppie prenotazioni.",
|
||||
"check_for_conflicts": "Controlla conflitti",
|
||||
"adding_events_to": "Aggiungendo eventi a",
|
||||
"follow_system_preferences": "Segui le preferenze di sistema",
|
||||
"custom_brand_colors": "Colori del marchio personalizzati",
|
||||
"customize_your_brand_colors": "Personalizza la tua pagina di prenotazione con i colori del tuo marchio.",
|
||||
"pro": "Pro",
|
||||
"removes_cal_branding": "Rimuove tutte le scritte con il marchio Cal, ad es., 'Powered by Cal.'",
|
||||
"profile_picture": "Immagine del profilo",
|
||||
"upload": "Carica",
|
||||
"web3": "Web3",
|
||||
"rainbow_token_gated": "Questo tipo di evento è protetto da token.",
|
||||
"rainbow_connect_wallet_gate": "Collega il tuo portafoglio se possiedi <1>{{name}}</1> (<3>{{symbol}}</3>).",
|
||||
"rainbow_insufficient_balance": "Il portafoglio collegato non contiene abbastanza <1>{{symbol}}</1>.",
|
||||
"rainbow_sign_message_request": "Firma la richiesta di messaggio sul tuo portafoglio.",
|
||||
"rainbow_signature_error": "Errore durante la richiesta di firma al tuo portafoglio.",
|
||||
"token_address": "Indirizzo token",
|
||||
"blockchain": "Blockchain",
|
||||
"old_password": "Vecchia password",
|
||||
"secure_password": "La tua nuova password super sicura",
|
||||
"error_updating_password": "Errore durante l'aggiornamento della password",
|
||||
"two_factor_auth": "Autenticazione a due fattori",
|
||||
"recurring_event_tab_description": "Imposta una pianificazione ricorrente",
|
||||
"today": "oggi",
|
||||
"appearance": "Aspetto",
|
||||
"appearance_subtitle": "Gestisci le impostazioni di aspetto delle tue prenotazioni",
|
||||
"my_account": "Il mio account",
|
||||
"general": "Generale",
|
||||
"calendars": "Calendari",
|
||||
"2fa_auth": "Autenticazione a due fattori",
|
||||
"invoices": "Fatture",
|
||||
"embeds": "Incorpora",
|
||||
"impersonation": "Impersonificazione",
|
||||
"users": "Utenti",
|
||||
"profile_description": "Gestisci le impostazioni del tuo profilo cal",
|
||||
"general_description": "Gestisci le impostazioni di lingua e fuso orario",
|
||||
"calendars_description": "Configura come i tipi di eventi devono interagire con i tuoi calendari",
|
||||
"appearance_description": "Gestisci le impostazioni di aspetto delle tue prenotazioni",
|
||||
"conferencing_description": "Gestisci le app di videoconferenza per le tue riunioni",
|
||||
"password_description": "Gestisci le impostazioni relative alle password del tuo account",
|
||||
"2fa_description": "Gestisci le impostazioni relative alle password del tuo account",
|
||||
"we_just_need_basic_info": "Abbiamo bisogno di alcune informazioni di base per completare la configurazione del tuo profilo.",
|
||||
"skip": "Salta",
|
||||
"do_this_later": "Più tardi",
|
||||
"set_availability_getting_started_subtitle_1": "Definisci gli intervalli di tempo quando sei disponibile",
|
||||
"set_availability_getting_started_subtitle_2": "È possibile personalizzare queste impostazioni in un secondo tempo nella pagina della disponibilità.",
|
||||
"connect_calendars_from_app_store": "È possibile aggiungere altri calendari dall'App store",
|
||||
"connect_conference_apps": "Connetti app di conferenza",
|
||||
"connect_calendar_apps": "Connetti app di calendario",
|
||||
"connect_payment_apps": "Connetti app di pagamento",
|
||||
"connect_other_apps": "Connetti altre app",
|
||||
"current_step_of_total": "Passo {{currentStep}} di {{maxSteps}}",
|
||||
"add_variable": "Aggiungi variabile",
|
||||
"custom_phone_number": "Numero di telefono personalizzato",
|
||||
"message_template": "Modello di messaggio",
|
||||
"email_subject": "Oggetto e-mail",
|
||||
"add_dynamic_variables": "Aggiungi variabili di testo dinamico",
|
||||
"event_name_info": "Nome del tipo di evento",
|
||||
"event_date_info": "Data dell'evento",
|
||||
"event_time_info": "Ora di inizio dell'evento",
|
||||
"location_info": "Luogo dell'evento",
|
||||
"organizer_name_info": "Il tuo nome",
|
||||
"additional_notes_info": "Note aggiuntive sulla prenotazione",
|
||||
"attendee_name_info": "Nome della persona che prenota",
|
||||
"to": "A",
|
||||
"attendee_required_enter_number": "Al partecipante verrà richiesto di inserire un numero di telefono al momento della prenotazione",
|
||||
"workflow_turned_on_successfully": "Flusso di lavoro {{workflowName}} impostato come {{offOn}} correttamente",
|
||||
"download_responses": "Scarica risposte",
|
||||
"create_your_first_form": "Crea il tuo primo modulo",
|
||||
"create_your_first_form_description": "I Moduli di instradamento consentono di porre domande qualificanti agli utenti per indirizzarli a persone o eventi pertinenti.",
|
||||
"create_your_first_webhook": "Crea il tuo primo Webhook",
|
||||
"create_your_first_webhook_description": "I Webhook consentono di ricevere in tempo reale le informazioni sulle riunioni quando si verifica un evento su Cal.com.",
|
||||
"for_a_maximum_of": "Per un massimo di",
|
||||
"event_one": "evento",
|
||||
"event_other": "eventi",
|
||||
"profile_team_description": "Gestisci le impostazioni del profilo del tuo team",
|
||||
"members_team_description": "Utenti nel gruppo",
|
||||
"team_url": "URL del team",
|
||||
"delete_team": "Elimina team",
|
||||
"team_members": "Membri del team",
|
||||
"more": "Altro",
|
||||
"more_page_footer": "Consideriamo l'applicazione mobile un'estensione dell'applicazione web. Se si stanno eseguendo delle azioni complesse, si prega di fare riferimento all'applicazione web.",
|
||||
"workflow_example_1": "Invia al partecipante un promemoria via SMS 24 ore prima dell'inizio dell'evento",
|
||||
"workflow_example_2": "Invia al partecipante un SMS personalizzato se l'evento viene riprogrammato",
|
||||
"workflow_example_3": "Invia all'organizzatore un'e-mail personalizzata quando viene prenotato un nuovo evento",
|
||||
"workflow_example_4": "Invia al partecipante un promemoria via e-mail un'ora prima dell'inizio dell'evento",
|
||||
"workflow_example_5": "Invia all'organizzatore un'e-mail personalizzata se l'evento viene riprogrammato",
|
||||
"workflow_example_6": "Invia all'organizzatore un SMS personalizzato quando viene prenotato un nuovo evento",
|
||||
"welcome_to_cal_header": "Benvenuto in Cal.com!",
|
||||
"edit_form_later_subtitle": "Sarà possibile apportare modifiche in un secondo tempo.",
|
||||
"connect_calendar_later": "Connetterò il mio calendario più tardi",
|
||||
"set_my_availability_later": "Configurerò la mia disponibilità più tardi",
|
||||
"problem_saving_user_profile": "Si è verificato un problema durante il salvataggio dei dati. Riprovare o contattare l'assistenza clienti.",
|
||||
"purchase_missing_seats": "Acquista posti mancanti",
|
||||
"slot_length": "Durata della fascia oraria",
|
||||
"booking_appearance": "Aspetto prenotazione",
|
||||
"appearance_team_description": "Gestisci le impostazioni di aspetto delle prenotazioni del tuo team",
|
||||
"only_owner_change": "Solo il proprietario del team può apportare modifiche alla prenotazione del team ",
|
||||
"team_disable_cal_branding_description": "Rimuove tutte le scritte con il marchio Cal, ad es., 'Powered by Cal'",
|
||||
"invited_by_team": "{{teamName}} ti ha invitato a unirti al team come {{role}}",
|
||||
"token_invalid_expired": "Il token non è valido o scaduto.",
|
||||
"exchange_add": "Connetti a Microsoft Exchange",
|
||||
"exchange_authentication": "Metodo di autenticazione",
|
||||
"exchange_authentication_standard": "Autenticazione di base",
|
||||
"exchange_authentication_ntlm": "Autenticazione NTLM",
|
||||
"exchange_compression": "Compressione GZip",
|
||||
"routing_forms_description": "È possibile vedere qui tutti i moduli e tutti i percorsi che hai creato.",
|
||||
"add_new_form": "Aggiungi nuovo modulo",
|
||||
"form_description": "Crea un modulo per instradare le persone che prenotano",
|
||||
"copy_link_to_form": "Copia il link al modulo",
|
||||
"theme": "Tema",
|
||||
"theme_applies_note": "Si applica solo alle pagine di prenotazione pubbliche",
|
||||
"theme_light": "Chiaro",
|
||||
"theme_dark": "Scuro"
|
||||
"theme_dark": "Scuro",
|
||||
"theme_system": "Predefinito di sistema",
|
||||
"add_a_team": "Aggiungi un team",
|
||||
"saml_config": "Configurazione SAML",
|
||||
"add_webhook_description": "Ricevi in tempo reale le informazioni sulle riunioni quando si verifica un evento su Cal.com",
|
||||
"triggers_when": "Viene attivato quando",
|
||||
"test_webhook": "Eseguire un test del ping prima di creare.",
|
||||
"enable_webhook": "Abilita Webhook",
|
||||
"add_webhook": "Aggiungi Webhook",
|
||||
"webhook_edited_successfully": "Webhook salvato",
|
||||
"webhooks_description": "Ricevi in tempo reale le informazioni sulle riunioni quando si verifica un evento su Cal.com",
|
||||
"api_keys_description": "Genera chiavi API per accedere al tuo account",
|
||||
"new_api_key": "Nuova chiave API",
|
||||
"active": "Attivo",
|
||||
"api_key_updated": "Nome chiave API aggiornato",
|
||||
"api_key_update_failed": "Errore durante l'aggiornamento del nome della chiave API",
|
||||
"embeds_title": "iframe HTML incorporato",
|
||||
"embeds_description": "Incorpora tutti i tipi di eventi nel tuo sito web",
|
||||
"create_first_api_key": "Crea la tua prima chiave API",
|
||||
"create_first_api_key_description": "Le chiavi API consentono alle altre applicazioni di comunicare con Cal.com",
|
||||
"back_to_signin": "Torna alla pagina di accesso",
|
||||
"reset_link_sent": "Link per la reimpostazione inviato",
|
||||
"password_reset_email": "Un'e-mail con le istruzioni per reimpostare la password è stata inviata all'indirizzo {{email}}.",
|
||||
"password_updated": "Password aggiornata!",
|
||||
"pending_payment": "Pagamento in sospeso",
|
||||
"confirmation_page_rainbow": "Proteggi i tuoi eventi con token o NFT su Ethereum, Polygon e altri.",
|
||||
"not_on_cal": "Non presente su Cal.com",
|
||||
"no_calendar_installed": "Nessun calendario installato",
|
||||
"no_calendar_installed_description": "Non hai ancora collegato alcun calendario",
|
||||
"add_a_calendar": "Aggiungi un calendario",
|
||||
"change_email_hint": "Per rendere effettive le modifiche, potrebbe essere necessario disconnettersi e accedere nuovamente",
|
||||
"confirm_password_change_email": "Conferma la password prima di cambiare l'indirizzo e-mail",
|
||||
"seats": "posti",
|
||||
"every_app_published": "Tutte le applicazioni pubblicate nell'App store di Cal.com sono open source e sono state testate in modo approfondito attraverso una valutazione tra pari. Tuttavia, Cal.com, Inc. non promuove né certifica tali applicazioni, a meno che non siano pubblicate da Cal.com. Se notate contenuti o comportamenti inappropriati, vi preghiamo di segnalarceli.",
|
||||
"report_app": "Segnala app"
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user