Merge branch 'main' into v2/teams-billing

This commit is contained in:
Joe Au-Yeung 2022-11-01 13:50:43 -04:00
commit cc46f21d66
200 changed files with 1262 additions and 3355 deletions

View File

@ -5,7 +5,7 @@ name: 'Chromatic'
# Event for the workflow
on:
pull_request_target: # So we can test on forks
pull_request: # So we can test on forks
branches:
- main
paths:

View File

@ -1,6 +1,6 @@
name: Check types
on:
pull_request_target:
pull_request:
branches:
- main
paths:

View File

@ -2,7 +2,7 @@ name: E2E App-Store Apps
on:
push:
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
pull_request_target: # So we can test on forks
pull_request: # So we can test on forks
branches:
- main
paths-ignore:

View File

@ -1,6 +1,6 @@
name: E2E Embed tests and booking flow(for non-embed as well)
on:
pull_request_target: # So we can test on forks
pull_request: # So we can test on forks
branches:
- main
# Embed e2e - tests verify booking flow which is applicable to non-embed case also. So, don't ignore apps/web changes.

View File

@ -2,7 +2,7 @@ name: E2E test
on:
push:
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
pull_request_target: # So we can test on forks
pull_request: # So we can test on forks
branches:
- main
paths:

View File

@ -1,6 +1,6 @@
name: "Pull Request Labeler"
on:
- pull_request_target
- pull_request
concurrency:
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

View File

@ -1,6 +1,6 @@
name: Lint
on:
pull_request_target:
pull_request:
branches:
- main
paths:

View File

@ -2,7 +2,7 @@ name: Unit tests
on:
push:
branches: [fixes/e2e-consolidation] # TODO: Remove this after merged in main
pull_request_target: # So we can test on forks
pull_request: # So we can test on forks
branches:
- main
paths:

View File

@ -1,19 +0,0 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { App } from "@calcom/types/App";
import AppCard from "./AppCard";
export default function AllApps({ apps }: { apps: App[] }) {
const { t } = useLocale();
return (
<div className="mb-16">
<h2 className="mb-2 text-lg font-semibold text-gray-900">{t("all_apps")}</h2>
<div className="grid-col-1 grid grid-cols-1 gap-3 md:grid-cols-3">
{apps.map((app) => (
<AppCard key={app.name} app={app} />
))}
</div>
</div>
);
}

View File

@ -1,44 +0,0 @@
import Link from "next/link";
import { trpc } from "@calcom/trpc/react";
import { App } from "@calcom/types/App";
import Badge from "@calcom/ui/Badge";
import Button from "@calcom/ui/Button";
interface AppCardProps {
app: App;
}
export default function AppCard(props: AppCardProps) {
const { data: user } = trpc.useQuery(["viewer.me"]);
return (
<Link href={"/apps/" + props.app.slug}>
<a
className="block h-full rounded-sm border border-gray-300 p-5 hover:bg-neutral-50"
data-testid={`app-store-app-card-${props.app.slug}`}>
<div className="flex">
{
// eslint-disable-next-line @next/next/no-img-element
<img src={props.app.logo} alt={props.app.name + " Logo"} className="mb-4 h-12 w-12 rounded-sm" />
}
<Button
color="secondary"
className="ml-auto flex self-start"
onClick={() => {
console.log("The magic is supposed to happen here");
}}>
Add
</Button>
</div>
<div className="flex items-center">
<h3 className="font-medium">{props.app.name}</h3>
</div>
{/* TODO: add reviews <div className="flex text-sm text-gray-800">
<span>{props.rating} stars</span> <StarIcon className="ml-1 mt-0.5 h-4 w-4 text-yellow-600" />
<span className="pl-1 text-gray-500">{props.reviews} reviews</span>
</div> */}
<p className="mt-2 truncate text-sm text-gray-500">{props.app.description}</p>
</a>
</Link>
);
}

View File

@ -17,9 +17,9 @@ import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/Disconnect
import { QueryCell } from "@lib/QueryCell";
import AdditionalCalendarSelector from "@components/apps/AdditionalCalendarSelector";
import IntegrationListItem from "@components/apps/IntegrationListItem";
import SubHeadingTitleWithConnections from "@components/integrations/SubHeadingTitleWithConnections";
import AdditionalCalendarSelector from "@components/v2/apps/AdditionalCalendarSelector";
import IntegrationListItem from "@components/v2/apps/IntegrationListItem";
type Props = {
onChanged: () => unknown | Promise<unknown>;

View File

@ -1,43 +0,0 @@
import Image from "next/image";
import Link from "next/link";
import { useLocale } from "@calcom/lib/hooks/useLocale";
export default function AppStoreCategories({
categories,
}: {
categories: {
name: string;
count: number;
}[];
}) {
const { t } = useLocale();
return (
<div className="mb-16">
<h2 className="mb-2 text-lg font-semibold text-gray-900">{t("popular_categories")}</h2>
<div className="grid-col-1 grid w-full gap-3 overflow-scroll sm:grid-flow-col">
{categories.map((category) => (
<Link key={category.name} href={"/apps/categories/" + category.name}>
<a
data-testid={`app-store-category-${category.name}`}
className="relative flex rounded-sm bg-gray-100 px-6 py-4 sm:block">
<div className="min-w-24 -ml-5 text-center sm:ml-0">
<Image
alt={category.name}
width="352"
height="252"
layout="responsive"
src={"/app-store/" + category.name + ".svg"}
/>
</div>
<div className="self-center">
<h3 className="font-medium capitalize">{category.name}</h3>
<p className="text-sm text-gray-500">{category.count} apps</p>
</div>
</a>
</Link>
))}
</div>
</div>
);
}

View File

@ -2,6 +2,8 @@ import Link from "next/link";
import { useRouter } from "next/router";
import { ReactNode, useEffect, useState } from "react";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Icon } from "@calcom/ui";
import { ListItem, ListItemText, ListItemTitle } from "@calcom/ui/v2/core/List";
import classNames from "@lib/classNames";
@ -17,7 +19,9 @@ function IntegrationListItem(props: {
logo: string;
destination?: boolean;
separate?: boolean;
invalidCredential?: boolean;
}): JSX.Element {
const { t } = useLocale();
const router = useRouter();
const { hl } = router.query;
const [highlight, setHighlight] = useState(hl === props.slug);
@ -44,6 +48,15 @@ function IntegrationListItem(props: {
<Link href={"/apps/" + props.slug}>{props.name || title}</Link>
</ListItemTitle>
<ListItemText component="p">{props.description}</ListItemText>
{/* Alert error that key stopped working. */}
{props.invalidCredential && (
<div className="flex items-center space-x-2">
<Icon.FiAlertCircle className="w-8 text-red-500 sm:w-4" />
<ListItemText component="p" className="whitespace-pre-wrap text-red-500">
{t("invalid_credential")}
</ListItemText>
</div>
)}
</div>
<div>{props.actions}</div>
</div>

View File

@ -1,79 +0,0 @@
import Glide, { Options } from "@glidejs/glide";
import "@glidejs/glide/dist/css/glide.core.min.css";
import "@glidejs/glide/dist/css/glide.theme.min.css";
import { useEffect, useRef } from "react";
import { Icon } from "@calcom/ui/Icon";
const Slider = <T extends string | unknown>({
title = "",
className = "",
items,
itemKey = (item) => `${item}`,
renderItem,
options = {},
}: {
title?: string;
className?: string;
items: T[];
itemKey?: (item: T) => string;
renderItem?: (item: T) => JSX.Element;
options?: Options;
}) => {
const glide = useRef(null);
const slider = useRef<Glide.Properties | null>(null);
useEffect(() => {
if (glide.current) {
slider.current = new Glide(glide.current, {
type: "carousel",
...options,
}).mount();
}
return () => slider.current?.destroy();
}, [options]);
return (
<div className={`mb-2 ${className}`}>
<style jsx global>
{`
.glide__slide {
height: auto !important;
}
`}
</style>
<div className="glide" ref={glide}>
<div className="flex cursor-default">
{title && (
<div>
<h2 className="mt-0 mb-2 text-lg font-semibold text-gray-900">{title}</h2>
</div>
)}
<div className="glide__arrows ml-auto" data-glide-el="controls">
<button data-glide-dir="<" className="mr-4">
<Icon.FiArrowLeft className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
<button data-glide-dir=">">
<Icon.FiArrowRight className="h-5 w-5 text-gray-600 hover:text-black" />
</button>
</div>
</div>
<div className="glide__track" data-glide-el="track">
<ul className="glide__slides">
{items.map((item) => {
if (typeof renderItem !== "function") return null;
return (
<li key={itemKey(item)} className="glide__slide h-auto pl-0">
{renderItem(item)}
</li>
);
})}
</ul>
</div>
</div>
</div>
);
};
export default Slider;

View File

@ -1,30 +0,0 @@
import { useLocale } from "@calcom/lib/hooks/useLocale";
import type { App } from "@calcom/types/App";
import AppCard from "./AppCard";
import Slider from "./Slider";
const TrendingAppsSlider = <T extends App>({ items }: { items: T[] }) => {
const { t } = useLocale();
return (
<Slider<T>
className="mb-16"
title={t("trending_apps")}
items={items.filter((app) => !!app.trending)}
itemKey={(app) => app.name}
options={{
perView: 3,
breakpoints: {
768 /* and below */: {
perView: 1,
},
},
}}
renderItem={(app) => <AppCard app={app} />}
/>
);
};
/** @deprecated use v2 instead, located at packages/ui/v2/core/apps */
export default TrendingAppsSlider;

View File

@ -1,86 +0,0 @@
import { useRouter } from "next/router";
import { useForm } from "react-hook-form";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { trpc } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import { Dialog, DialogClose, DialogContent, DialogTrigger } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Form } from "@calcom/ui/form/fields";
import showToast from "@calcom/ui/v2/core/notifications";
import { HttpError } from "@lib/core/http/error";
export function NewScheduleButton({ name = "new-schedule" }: { name?: string }) {
const router = useRouter();
const { t } = useLocale();
const form = useForm<{
name: string;
}>();
const { register } = form;
const createMutation = trpc.useMutation("viewer.availability.schedule.create", {
onSuccess: async ({ schedule }) => {
await router.push("/availability/" + schedule.id);
showToast(t("schedule_created_successfully", { scheduleName: schedule.name }), "success");
},
onError: (err) => {
if (err instanceof HttpError) {
const message = `${err.statusCode}: ${err.message}`;
showToast(message, "error");
}
if (err.data?.code === "UNAUTHORIZED") {
const message = `${err.data.code}: You are not able to create this event`;
showToast(message, "error");
}
},
});
return (
<Dialog name={name} clearQueryParamsOnClose={["copy-schedule-id"]}>
<DialogTrigger asChild>
<Button data-testid={name} StartIcon={Icon.FiPlus}>
{t("new_schedule_btn")}
</Button>
</DialogTrigger>
<DialogContent>
<div className="mb-8">
<h3 className="text-lg font-bold leading-6 text-gray-900" id="modal-title">
{t("add_new_schedule")}
</h3>
</div>
<Form
form={form}
handleSubmit={(values) => {
createMutation.mutate(values);
}}>
<div className="mt-3 space-y-2">
<label htmlFor="label" className="block text-sm font-medium text-gray-700">
{t("name")}
</label>
<div className="mt-1">
<input
type="text"
id="name"
required
className="block w-full rounded-sm border-gray-300 text-sm"
placeholder={t("default_schedule_name")}
{...register("name")}
/>
</div>
</div>
<div className="mt-8 flex flex-row-reverse gap-x-2">
<Button type="submit" loading={createMutation.isLoading}>
{t("continue")}
</Button>
<DialogClose asChild>
<Button color="secondary">{t("cancel")}</Button>
</DialogClose>
</div>
</Form>
</DialogContent>
</Dialog>
);
}

View File

@ -1,350 +0,0 @@
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
import classNames from "classnames";
import React, { useCallback, useEffect, useMemo, useState } from "react";
import { Controller, useFieldArray, useFormContext } from "react-hook-form";
import { GroupBase, Props } from "react-select";
import dayjs, { Dayjs, ConfigType } from "@calcom/dayjs";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import Button from "@calcom/ui/Button";
import Dropdown, { DropdownMenuContent, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { defaultDayRange } from "@lib/availability";
import { weekdayNames } from "@lib/core/i18n/weekday";
import useMeQuery from "@lib/hooks/useMeQuery";
import { TimeRange } from "@lib/types/schedule";
import Select from "@components/ui/form/Select";
/** Begin Time Increments For Select */
const increment = 15;
type Option = {
readonly label: string;
readonly value: number;
};
/**
* Creates an array of times on a 15 minute interval from
* 00:00:00 (Start of day) to
* 23:45:00 (End of day with enough time for 15 min booking)
*/
const useOptions = () => {
// Get user so we can determine 12/24 hour format preferences
const query = useMeQuery();
const { timeFormat } = query.data || { timeFormat: null };
const [filteredOptions, setFilteredOptions] = useState<Option[]>([]);
const options = useMemo(() => {
const end = dayjs().utc().endOf("day");
let t: Dayjs = dayjs().utc().startOf("day");
const options: Option[] = [];
while (t.isBefore(end)) {
options.push({
value: t.toDate().valueOf(),
label: dayjs(t)
.utc()
.format(timeFormat === 12 ? "h:mma" : "HH:mm"),
});
t = t.add(increment, "minutes");
}
return options;
}, [timeFormat]);
const filter = useCallback(
({ offset, limit, current }: { offset?: ConfigType; limit?: ConfigType; current?: ConfigType }) => {
if (current) {
const currentOption = options.find((option) => option.value === dayjs(current).toDate().valueOf());
if (currentOption) setFilteredOptions([currentOption]);
} else
setFilteredOptions(
options.filter((option) => {
const time = dayjs(option.value);
return (!limit || time.isBefore(limit)) && (!offset || time.isAfter(offset));
})
);
},
[options]
);
return { options: filteredOptions, filter };
};
type TimeRangeFieldProps = {
name: string;
className?: string;
};
const LazySelect = ({
value,
min,
max,
...props
}: Omit<Props<Option, false, GroupBase<Option>>, "value"> & {
value: ConfigType;
min?: ConfigType;
max?: ConfigType;
}) => {
// Lazy-loaded options, otherwise adding a field has a noticable redraw delay.
const { options, filter } = useOptions();
useEffect(() => {
filter({ current: value });
}, [filter, value]);
return (
<Select
options={options}
onMenuOpen={() => {
if (min) filter({ offset: min });
if (max) filter({ limit: max });
}}
value={options.find((option) => option.value === dayjs(value).toDate().valueOf())}
onMenuClose={() => filter({ current: value })}
{...props}
/>
);
};
const TimeRangeField = ({ name, className }: TimeRangeFieldProps) => {
const { watch } = useFormContext();
const minEnd = watch(`${name}.start`);
const maxStart = watch(`${name}.end`);
return (
<div className={classNames("flex flex-grow items-center space-x-3", className)}>
<Controller
name={`${name}.start`}
render={({ field: { onChange, value } }) => {
return (
<LazySelect
className="w-[120px]"
value={value}
max={maxStart}
onChange={(option) => {
onChange(new Date(option?.value as number));
}}
/>
);
}}
/>
<span>-</span>
<Controller
name={`${name}.end`}
render={({ field: { onChange, value } }) => (
<LazySelect
className="flex-grow sm:w-[120px]"
value={value}
min={minEnd}
onChange={(option) => {
onChange(new Date(option?.value as number));
}}
/>
)}
/>
</div>
);
};
type ScheduleBlockProps = {
day: number;
weekday: string;
name: string;
};
const CopyTimes = ({ disabled, onApply }: { disabled: number[]; onApply: (selected: number[]) => void }) => {
const [selected, setSelected] = useState<number[]>([]);
const { i18n, t } = useLocale();
return (
<div className="m-4 space-y-2 py-4">
<p className="h6 text-xs font-medium uppercase text-neutral-400">Copy times to</p>
<ol className="space-y-2">
{weekdayNames(i18n.language).map((weekday, num) => (
<li key={weekday}>
<label className="flex w-full items-center justify-between">
<span>{weekday}</span>
<input
value={num}
defaultChecked={disabled.includes(num)}
disabled={disabled.includes(num)}
onChange={(e) => {
if (e.target.checked && !selected.includes(num)) {
setSelected(selected.concat([num]));
} else if (!e.target.checked && selected.includes(num)) {
setSelected(selected.slice(selected.indexOf(num), 1));
}
}}
type="checkbox"
className="inline-block rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500 disabled:text-neutral-400"
/>
</label>
</li>
))}
</ol>
<div className="pt-2">
<Button className="w-full justify-center" color="primary" onClick={() => onApply(selected)}>
{t("apply")}
</Button>
</div>
</div>
);
};
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
export const DayRanges = ({
name,
defaultValue = [defaultDayRange],
}: {
name: string;
defaultValue?: TimeRange[];
}) => {
const { setValue, watch } = useFormContext();
// XXX: Hack to make copying times work; `fields` is out of date until save.
const watcher = watch(name);
const { t } = useLocale();
const { fields, replace, append, remove } = useFieldArray({
name,
});
useEffect(() => {
if (defaultValue.length && !fields.length) {
replace(defaultValue);
}
}, [replace, defaultValue, fields.length]);
const handleAppend = () => {
// FIXME: Fix type-inference, can't get this to work. @see https://github.com/react-hook-form/react-hook-form/issues/4499
const nextRangeStart = dayjs((fields[fields.length - 1] as unknown as TimeRange).end);
const nextRangeEnd = dayjs(nextRangeStart).add(1, "hour");
if (nextRangeEnd.isBefore(nextRangeStart.endOf("day"))) {
return append({
start: nextRangeStart.toDate(),
end: nextRangeEnd.toDate(),
});
}
};
return (
<div className="space-y-2">
{fields.map((field, index) => (
<div key={field.id} className="flex items-center rtl:space-x-reverse">
<div className="flex flex-grow space-x-1 sm:flex-grow-0">
<TimeRangeField name={`${name}.${index}`} />
<Button
type="button"
size="icon"
color="minimal"
StartIcon={Icon.FiTrash}
onClick={() => remove(index)}
/>
</div>
{index === 0 && (
<div className="absolute top-2 right-0 text-right sm:relative sm:top-0 sm:flex-grow">
<Tooltip content={t("add_time_availability") as string}>
<Button
className="text-neutral-400"
type="button"
color="minimal"
size="icon"
StartIcon={Icon.FiPlus}
onClick={handleAppend}
/>
</Tooltip>
<Dropdown>
<Tooltip content={t("duplicate") as string}>
<DropdownMenuTrigger asChild>
<Button
type="button"
color="minimal"
size="icon"
StartIcon={Icon.FiCopy}
onClick={handleAppend}
/>
</DropdownMenuTrigger>
</Tooltip>
<DropdownMenuContent>
<CopyTimes
disabled={[parseInt(name.substring(name.lastIndexOf(".") + 1), 10)]}
onApply={(selected) =>
selected.forEach((day) => {
// TODO: Figure out why this is different?
// console.log(watcher, fields);
setValue(name.substring(0, name.lastIndexOf(".") + 1) + day, watcher);
})
}
/>
</DropdownMenuContent>
</Dropdown>
</div>
)}
</div>
))}
</div>
);
};
const ScheduleBlock = ({ name, day, weekday }: ScheduleBlockProps) => {
const { t } = useLocale();
const form = useFormContext();
const watchAvailable = form.watch(`${name}.${day}`, []);
return (
<fieldset className="relative flex flex-col justify-between space-y-2 py-5 sm:flex-row sm:space-y-0">
<label
className={classNames(
"flex space-x-2 rtl:space-x-reverse",
!watchAvailable.length ? "w-full" : "w-1/3"
)}>
<div className={classNames(!watchAvailable.length ? "w-1/3" : "w-full")}>
<input
type="checkbox"
checked={watchAvailable.length}
onChange={(e) => {
form.setValue(`${name}.${day}`, e.target.checked ? [defaultDayRange] : []);
}}
className="inline-block rounded-sm border-gray-300 text-neutral-900 focus:ring-neutral-500"
/>
<span className="ml-2 inline-block text-sm capitalize">{weekday}</span>
</div>
{!watchAvailable.length && (
<div className="flex-grow text-right text-sm text-gray-500 sm:flex-shrink">
{t("no_availability")}
</div>
)}
</label>
{!!watchAvailable.length && (
<div className="flex-grow">
<DayRanges name={`${name}.${day}`} defaultValue={[]} />
</div>
)}
</fieldset>
);
};
/**
* @deprecated
* use Component in "/packages/features/schedules/components/Schedule";
**/
const Schedule = ({ name }: { name: string }) => {
const { i18n } = useLocale();
return (
<fieldset className="divide-y divide-gray-200">
{weekdayNames(i18n.language).map((weekday, num) => (
<ScheduleBlock key={num} name={name} weekday={weekday} day={num} />
))}
</fieldset>
);
};
export default Schedule;

View File

@ -1,83 +0,0 @@
/**
* @deprecated modifications to this file should be v2 only
* Use `/packages/features/schedules/components/ScheduleListItem.tsx` instead
*/
import Link from "next/link";
import { Fragment } from "react";
import { availabilityAsString } from "@calcom/lib/availability";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Availability } from "@calcom/prisma/client";
import { inferQueryOutput } from "@calcom/trpc/react";
import { Button } from "@calcom/ui";
import Dropdown, { DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from "@calcom/ui/Dropdown";
import { Icon } from "@calcom/ui/Icon";
/**
* @deprecated modifications to this file should be v2 only
* Use `/packages/features/schedules/components/ScheduleListItem.tsx` instead
*/
export function ScheduleListItem({
schedule,
deleteFunction,
isDeleting = false,
}: {
schedule: inferQueryOutput<"viewer.availability.list">["schedules"][number];
deleteFunction: ({ scheduleId }: { scheduleId: number }) => void;
isDeleting: boolean;
}) {
const { t, i18n } = useLocale();
return (
<li key={schedule.id}>
<div className="flex items-center justify-between py-5 hover:bg-neutral-50 ltr:pl-4 rtl:pr-4 sm:ltr:pl-0 sm:rtl:pr-0">
<div className="group flex w-full items-center justify-between hover:bg-neutral-50 sm:px-6">
<Link href={"/availability/" + schedule.id}>
<a className="flex-grow truncate text-sm" title={schedule.name}>
<div>
<span className="truncate font-medium text-neutral-900">{schedule.name}</span>
{schedule.isDefault && (
<span className="ml-2 inline items-center rounded-sm bg-yellow-100 px-1.5 py-0.5 text-xs font-medium text-yellow-800">
{t("default")}
</span>
)}
</div>
<p className="mt-1 text-xs text-neutral-500">
{schedule.availability.map((availability: Availability) => (
<Fragment key={availability.id}>
{availabilityAsString(availability, { locale: i18n.language })}
<br />
</Fragment>
))}
</p>
</a>
</Link>
</div>
<Dropdown>
<DropdownMenuTrigger className="group mr-5 h-10 w-10 border border-transparent p-0 text-neutral-500 hover:border-gray-200">
<Icon.FiMoreHorizontal className="h-5 w-5 group-hover:text-gray-800" />
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem>
<Button
disabled={isDeleting}
onClick={() => {
deleteFunction({
scheduleId: schedule.id,
});
}}
type="button"
color="warn"
className="w-full font-normal"
StartIcon={isDeleting ? undefined : Icon.FiTrash}
loading={isDeleting}>
{isDeleting ? t("deleting") : t("delete_schedule")}
</Button>
</DropdownMenuItem>
</DropdownMenuContent>
</Dropdown>
</div>
</li>
);
}

View File

@ -1,8 +1,9 @@
import React from "react";
import { SkeletonText } from "@calcom/ui";
import { SkeletonText } from "@calcom/ui/v2";
import classNames from "@lib/classNames";
/** @deprecated Use `apps/web/components/v2/availability/SkeletonLoader.tsx` */
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
@ -20,26 +21,30 @@ function SkeletonItem() {
<li className="group flex w-full items-center justify-between px-2 py-[23px] sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex flex-col space-y-2">
<SkeletonText width="32" height="4" />
<SkeletonText width="32" height="2" />
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-2 w-32" />
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText width="12" height="6" />
<SkeletonText className="h-6 w-12" />
</div>
</div>
</li>
);
}
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 width="32" height="4" />
<SkeletonText width="4" height="4" />
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-4 w-4" />
</div>
</div>
</li>

View File

@ -58,7 +58,7 @@ const AvailableTimes: FC<AvailableTimesProps> = ({
{nameOfDay(i18n.language, Number(date.format("d")), "short")}
</span>
<span className="text-bookinglight font-medium">
, {date.toDate().toLocaleString(i18n.language, { month: "long", day: "numeric" })}
, {date.toDate().toLocaleString(i18n.language, { month: "long" })} {date.format(" D ")}
</span>
</div>
<div className="ml-auto">

View File

@ -70,7 +70,9 @@ const BookingDescription: FC<Props> = (props) => {
)}
/>
</div>
<EventTypeDescriptionSafeHTML eventType={eventType} />
<div className="max-w-[calc(100%_-_2rem)] flex-shrink break-words">
<EventTypeDescriptionSafeHTML eventType={eventType} />
</div>
</div>
)}
{eventType?.requiresConfirmation && (

View File

@ -11,11 +11,11 @@ import { getEveryFreqFor } from "@calcom/lib/recurringStrings";
import { inferQueryInput, inferQueryOutput, trpc } from "@calcom/trpc/react";
import { Dialog, DialogClose, DialogContent, DialogFooter, DialogHeader } from "@calcom/ui/Dialog";
import { Icon } from "@calcom/ui/Icon";
import { Tooltip } from "@calcom/ui/Tooltip";
import { TextArea } from "@calcom/ui/form/fields";
import Badge from "@calcom/ui/v2/core/Badge";
import Button from "@calcom/ui/v2/core/Button";
import MeetingTimeInTimezones from "@calcom/ui/v2/core/MeetingTimeInTimezones";
import Tooltip from "@calcom/ui/v2/core/Tooltip";
import showToast from "@calcom/ui/v2/core/notifications";
import useMeQuery from "@lib/hooks/useMeQuery";
@ -352,7 +352,7 @@ function BookingListItem(booking: BookingItemProps) {
</div>
{booking.description && (
<div
className="max-w-52 md:max-w-96 truncate text-sm text-gray-600"
className="max-w-10/12 sm:max-w-40 md:max-w-56 xl:max-w-80 lg:max-w-64 truncate text-sm text-gray-600"
title={booking.description}>
&quot;{booking.description}&quot;
</div>

View File

@ -1,8 +1,7 @@
import React from "react";
import { SkeletonText } from "@calcom/ui";
import { SkeletonText } from "@calcom/ui/v2";
/** @deprecated Use `apps/web/components/v2/bookings/SkeletonLoader.tsx` */
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:overflow-hidden">
@ -21,15 +20,15 @@ function SkeletonItem() {
<div className="flex-grow truncate text-sm">
<div className="flex">
<div className="flex flex-col space-y-2">
<SkeletonText width="16" height="5" />
<SkeletonText width="32" height="4" />
<SkeletonText className="h-5 w-16" />
<SkeletonText className="h-4 w-32" />
</div>
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText width="16" height="6" />
<SkeletonText width="32" height="6" />
<SkeletonText className="h-6 w-16" />
<SkeletonText className="h-6 w-32" />
</div>
</div>
</li>

View File

@ -812,7 +812,7 @@ const BookingPage = ({
<label
htmlFor="smsReminderNumber"
className="block text-sm font-medium text-gray-700 dark:text-white">
{t("number_for_sms_reminders")}
{t("number_sms_notifications")}
</label>
<div className="mt-1">
<PhoneInput<BookingFormValues>

View File

@ -4,6 +4,7 @@ import { NextSeo, NextSeoProps } from "next-seo";
import {
AppImageProps,
constructAppImage,
constructGenericImage,
constructMeetingImage,
MeetingImageProps,
} from "@calcom/lib/OgImages";
@ -72,12 +73,11 @@ const buildSeoMeta = (pageProps: {
export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
const defaultUrl = getBrowserInfo()?.url;
const image = getSeoImage("default");
const { title, description, siteName, canonical = defaultUrl, nextSeoProps = {}, app, meeting } = props;
const truncatedDescription = truncate(description, 24);
const longerTruncatedDescriptionOnWords = truncateOnWord(description, 148);
const image = getSeoImage("ogImage") + constructGenericImage({ title, description });
const truncatedDescription = truncateOnWord(description, 158);
const pageTitle = title + " | Cal.com";
let seoObject = buildSeoMeta({
@ -101,7 +101,7 @@ export const HeadSeo = (props: HeadSeoProps): JSX.Element => {
if (app) {
const pageImage =
getSeoImage("ogImage") + constructAppImage({ ...app, description: longerTruncatedDescriptionOnWords });
getSeoImage("ogImage") + constructAppImage({ ...app, description: truncatedDescription });
seoObject = buildSeoMeta({
title: pageTitle,
description: truncatedDescription,

View File

@ -17,27 +17,25 @@ interface Props {
export default function AuthContainer(props: React.PropsWithChildren<Props>) {
return (
<div className="flex min-h-screen flex-col justify-center border-gray-200 bg-neutral-50 py-12 sm:px-6 lg:px-8">
<div className="flex min-h-screen flex-col justify-center bg-[#f3f4f6] py-12 sm:px-6 lg:px-8">
<HeadSeo title={props.title} description={props.description} />
{props.showLogo && (
// eslint-disable-next-line @next/next/no-img-element
<img className="mb-auto h-4" src={LOGO} alt="Cal.com Logo" />
)}
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
{props.showLogo && (
// eslint-disable-next-line @next/next/no-img-element
<img className="mx-auto h-6" src={LOGO} alt="Cal.com Logo" />
)}
{props.heading && (
<h2 className="font-cal mt-6 text-center text-3xl text-neutral-900">{props.heading}</h2>
)}
{props.heading && <h2 className="font-cal text-center text-3xl text-neutral-900">{props.heading}</h2>}
</div>
{props.loading && (
<div className="absolute z-50 flex h-screen w-full items-center bg-gray-50">
<Loader />
</div>
)}
<div className="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="mx-2 rounded-md border border-gray-300 bg-white px-4 py-8 sm:px-10">
<div className="mb-auto mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-10 sm:px-10">
{props.children}
</div>
<div className="mt-4 text-center text-sm text-neutral-600">{props.footerText}</div>
<div className="mt-8 text-center text-sm text-neutral-600">{props.footerText}</div>
</div>
</div>
);

View File

@ -1,51 +0,0 @@
import classNames from "classnames";
import React from "react";
import { SkeletonText } from "@calcom/ui/v2";
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:mx-0 sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
</ul>
);
}
export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-2 py-[23px] sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex flex-col space-y-2">
<SkeletonText className="h-4 w-32" />
<SkeletonText className="h-2 w-32" />
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText className="h-6 w-12" />
</div>
</div>
</li>
);
}
export const SelectSkeletonLoader = ({ className }: { className?: string }) => {
return (
<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" />
<SkeletonText className="h-4 w-4" />
</div>
</div>
</li>
);
};

View File

@ -1,36 +0,0 @@
import React from "react";
import { SkeletonText } from "@calcom/ui/v2";
function SkeletonLoader() {
return (
<ul className="animate-pulse divide-y divide-neutral-200 rounded-md border border-gray-200 bg-white sm:overflow-hidden">
<SkeletonItem />
<SkeletonItem />
<SkeletonItem />
</ul>
);
}
export default SkeletonLoader;
function SkeletonItem() {
return (
<li className="group flex w-full items-center justify-between px-4 py-4 sm:px-6">
<div className="flex-grow truncate text-sm">
<div className="flex">
<div className="flex flex-col space-y-2">
<SkeletonText className="h-5 w-16" />
<SkeletonText className="h-4 w-32" />
</div>
</div>
</div>
<div className="mt-4 hidden flex-shrink-0 sm:mt-0 sm:ml-5 lg:flex">
<div className="flex justify-between space-x-2 rtl:space-x-reverse">
<SkeletonText className="h-6 w-16" />
<SkeletonText className="h-6 w-32" />
</div>
</div>
</li>
);
}

View File

@ -13,7 +13,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 { SelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader";
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
type AvailabilityOption = {
label: string;
@ -129,37 +129,41 @@ export const AvailabilityTab = () => {
/>
</div>
<div className="space-y-4 rounded border p-8 py-6 pt-2">
<div className="space-y-4 rounded border p-4 py-6 pt-2 md:p-8">
<ol className="table border-collapse text-sm">
{weekdayNames(i18n.language, 1, "long").map((day, index) => {
const isAvailable = !!filterDays(index).length;
return (
<li key={day} className="my-6 flex border-transparent last:mb-2">
<span className={classNames("w-32 font-medium", !isAvailable && "text-gray-500 opacity-50")}>
<span
className={classNames(
"w-20 font-medium sm:w-32",
!isAvailable && "text-gray-500 opacity-50"
)}>
{day}
</span>
{isLoading ? (
<SkeletonText className="block h-5 w-60" />
) : isAvailable ? (
<div className="space-y-3">
<div className="space-y-3 text-right">
{filterDays(index).map((dayRange, i) => (
<div key={i} className="flex items-center leading-4">
<span className="w-28">{format(dayRange.startTime)}</span>
<span className="">-</span>
<span className="w-16 sm:w-28 sm:text-left">{format(dayRange.startTime)}</span>
<span className="ml-4">-</span>
<div className="ml-6">{format(dayRange.endTime)}</div>
</div>
))}
</div>
) : (
<span className="text-gray-500 opacity-50 ">{t("unavailable")}</span>
<span className="ml-6 text-gray-500 opacity-50 sm:ml-0">{t("unavailable")}</span>
)}
</li>
);
})}
</ol>
<hr />
<div className="flex justify-between">
<span className="flex items-center text-sm text-gray-600">
<div className="flex flex-col justify-center gap-2 sm:flex-row sm:justify-between">
<span className="flex items-center justify-center text-sm text-gray-600 sm:justify-start">
<Icon.FiGlobe className="mr-2" />
{schedule?.timeZone || <SkeletonText className="block h-5 w-32" />}
</span>
@ -168,6 +172,7 @@ export const AvailabilityTab = () => {
color="minimal"
EndIcon={Icon.FiExternalLink}
target="_blank"
className="justify-center border sm:border-0"
rel="noopener noreferrer">
{t("edit_availability")}
</Button>

View File

@ -132,14 +132,14 @@ export const EventSetupTab = (
}
return (
<li key={location.type} className="mb-2 rounded-md border border-neutral-300 py-1.5 px-2">
<div className="flex justify-between">
<div className="flex max-w-full justify-between">
<div key={index} className="flex flex-grow items-center">
<img
src={eventLocationType.iconUrl}
className="h-6 w-6"
alt={`${eventLocationType.label} logo`}
/>
<span className="text-sm ltr:ml-2 rtl:mr-2">
<span className="truncate text-sm ltr:ml-2 rtl:mr-2">
{location[eventLocationType.defaultValueVariable] || eventLocationType.label}
</span>
</div>

View File

@ -23,6 +23,7 @@ import {
} from "@calcom/ui/v2";
import ConfirmationDialogContent from "@calcom/ui/v2/core/ConfirmationDialogContent";
import { Dialog } from "@calcom/ui/v2/core/Dialog";
import Divider from "@calcom/ui/v2/core/Divider";
import Dropdown, {
DropdownMenuContent,
DropdownMenuItem,
@ -45,6 +46,7 @@ type Props = {
installedAppsNumber: number;
enabledWorkflowsNumber: number;
formMethods: UseFormReturn<FormValues>;
isUpdateMutationLoading?: boolean;
};
function getNavigation(props: {
@ -111,6 +113,7 @@ function EventTypeSingleLayout({
enabledAppsNumber,
installedAppsNumber,
enabledWorkflowsNumber,
isUpdateMutationLoading,
formMethods,
}: Props) {
const utils = trpc.useContext();
@ -172,11 +175,11 @@ function EventTypeSingleLayout({
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">
<div className="hidden items-center rounded-md px-2 sm:flex sm:hover:bg-gray-100">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 hidden cursor-pointer self-center pr-2 sm:inline">
className="mt-2 hidden cursor-pointer self-center whitespace-nowrap pr-2 sm:inline">
{t("hide_from_profile")}
</Skeleton>
<Switch
@ -231,7 +234,7 @@ function EventTypeSingleLayout({
/>
</ButtonGroup>
<VerticalDivider />
<VerticalDivider className="hidden lg:block" />
<Dropdown>
<DropdownMenuTrigger className="block h-9 w-9 justify-center rounded-md border border-gray-200 bg-transparent text-gray-700 lg:hidden">
@ -264,12 +267,29 @@ function EventTypeSingleLayout({
{t("delete")}
</Button>
</DropdownMenuItem>
<Divider />
<div className="flex items-center rounded-md py-1.5 px-4 sm:hidden sm:hover:bg-gray-100">
<Skeleton
as={Label}
htmlFor="hiddenSwitch"
className="mt-2 inline cursor-pointer self-center pr-2 sm:hidden">
{t("hide_from_profile")}
</Skeleton>
<Switch
id="hiddenSwitch"
defaultChecked={formMethods.getValues("hidden")}
onCheckedChange={(e) => {
formMethods.setValue("hidden", e);
}}
/>
</div>
</DropdownMenuContent>
</Dropdown>
<div className="border-l-2 border-gray-300" />
<Button
className="ml-4 lg:ml-0"
type="submit"
loading={formMethods.formState.isSubmitting || isUpdateMutationLoading}
data-testid="update-eventtype"
form="event-type-form">
{t("save")}

View File

@ -1,42 +0,0 @@
import classNames from "classnames";
import React from "react";
import { LOGO } from "@calcom/lib/constants";
import Loader from "@components/Loader";
import { HeadSeo } from "@components/seo/head-seo";
interface Props {
title: string;
description: string;
footerText?: React.ReactNode | string;
showLogo?: boolean;
heading?: string;
loading?: boolean;
}
export default function AuthContainer(props: React.PropsWithChildren<Props>) {
return (
<div className="flex min-h-screen flex-col justify-center bg-[#f3f4f6] py-12 sm:px-6 lg:px-8">
<HeadSeo title={props.title} description={props.description} />
{props.showLogo && (
// eslint-disable-next-line @next/next/no-img-element
<img className="mb-auto h-4" src={LOGO} alt="Cal.com Logo" />
)}
<div className={classNames(props.showLogo ? "text-center" : "", "sm:mx-auto sm:w-full sm:max-w-md")}>
{props.heading && <h2 className="font-cal text-center text-3xl text-neutral-900">{props.heading}</h2>}
</div>
{props.loading && (
<div className="absolute z-50 flex h-screen w-full items-center bg-gray-50">
<Loader />
</div>
)}
<div className="mb-auto mt-8 sm:mx-auto sm:w-full sm:max-w-md">
<div className="border-1 mx-2 rounded-md border-gray-200 bg-white px-4 py-10 sm:px-10">
{props.children}
</div>
<div className="mt-8 text-center text-sm text-neutral-600">{props.footerText}</div>
</div>
</div>
);
}

View File

@ -1,6 +1,6 @@
{
"name": "@calcom/web",
"version": "2.1.1",
"version": "2.1.2",
"private": true,
"scripts": {
"analyze": "ANALYZE=true next build",

View File

@ -1,3 +1,4 @@
import MarkdownIt from "markdown-it";
import { GetStaticPaths, GetStaticPropsContext } from "next";
import { JSONObject } from "superjson/dist/types";
import { z } from "zod";
@ -8,6 +9,7 @@ import { getDefaultEvent, getGroupName, getUsernameList } from "@calcom/lib/defa
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { parseRecurringEvent } from "@calcom/lib/isRecurringEvent";
import prisma from "@calcom/prisma";
import { User } from "@calcom/prisma/client";
import { EventTypeMetaDataSchema } from "@calcom/prisma/zod-utils";
import { inferSSRProps } from "@lib/types/inferSSRProps";
@ -78,8 +80,27 @@ async function getUserPageProps(context: GetStaticPropsContext) {
brandColor: true,
darkBrandColor: true,
eventTypes: {
select: { id: true },
// Order by position is important to ensure that the event-type that's enabled is the first in the list because for Free user only first is allowed.
where: {
// Many-to-many relationship causes inclusion of the team events - cool -
// but to prevent these from being selected, make sure the teamId is NULL.
AND: [{ slug }, { teamId: null }],
},
select: {
title: true,
slug: true,
hidden: true,
recurringEvent: true,
length: true,
locations: true,
id: true,
description: true,
price: true,
currency: true,
requiresConfirmation: true,
schedulingType: true,
metadata: true,
seatsPerTimeSlot: true,
},
orderBy: [
{
position: "desc",
@ -92,57 +113,32 @@ async function getUserPageProps(context: GetStaticPropsContext) {
},
});
if (!user) return { notFound: true };
const md = new MarkdownIt("zero").enable([
//
"emphasis",
"list",
"newline",
"strikethrough",
]);
const eventTypes = await prisma.eventType.findMany({
where: {
slug,
OR: [{ userId: user.id }, { users: { some: { id: user.id } } }],
},
// Order is important to ensure that given a slug if there are duplicates, we choose the same event type consistently when showing in event-types list UI(in terms of ordering and disabled event types)
// TODO: If we can ensure that there are no duplicates for a [slug, userId] combination in existing data, this requirement might be avoided.
orderBy: [
{
position: "desc",
},
{
id: "asc",
},
],
select: {
title: true,
slug: true,
hidden: true,
recurringEvent: true,
length: true,
locations: true,
id: true,
description: true,
price: true,
currency: true,
requiresConfirmation: true,
schedulingType: true,
metadata: true,
seatsPerTimeSlot: true,
users: {
select: {
name: true,
username: true,
hideBranding: true,
brandColor: true,
darkBrandColor: true,
theme: true,
plan: true,
allowDynamicBooking: true,
timeZone: true,
if (!user || !user.eventTypes) return { notFound: true };
const [eventType]: (typeof user.eventTypes[number] & {
users: Pick<User, "name" | "username" | "hideBranding" | "plan" | "timeZone">[];
})[] = [
{
...user.eventTypes[0],
users: [
{
name: user.name,
username: user.username,
hideBranding: user.hideBranding,
plan: user.plan,
timeZone: user.timeZone,
},
},
],
},
});
if (!eventTypes) return { notFound: true };
const [eventType] = eventTypes;
];
if (!eventType) return { notFound: true };
@ -152,33 +148,21 @@ async function getUserPageProps(context: GetStaticPropsContext) {
metadata: EventTypeMetaDataSchema.parse(eventType.metadata || {}),
recurringEvent: parseRecurringEvent(eventType.recurringEvent),
locations: privacyFilteredLocations(locations),
users: eventType.users.map((user) => ({
name: user.name,
username: user.username,
hideBranding: user.hideBranding,
plan: user.plan,
timeZone: user.timeZone,
})),
descriptionAsSafeHTML: eventType.description ? md.render(eventType.description) : null,
});
const profile = eventType.users[0] || user;
return {
props: {
eventType: eventTypeObject,
profile: {
...eventType.users[0],
theme: user.theme,
name: user.name,
username: user.username,
hideBranding: user.hideBranding,
plan: user.plan,
timeZone: user.timeZone,
allowDynamicBooking: false,
weekStart: user.weekStart,
brandColor: user.brandColor,
darkBrandColor: user.darkBrandColor,
slug: `${profile.username}/${eventType.slug}`,
image: `${WEBAPP_URL}/${profile.username}/avatar.png`,
slug: `${user.username}/${eventType.slug}`,
image: `${WEBAPP_URL}/${user.username}/avatar.png`,
},
away: user?.away,
isDynamic: false,

View File

@ -3,7 +3,7 @@ import { NextApiRequest } from "next";
import type { SatoriOptions } from "satori";
import { z } from "zod";
import { Meeting, App } from "@calcom/lib/OgImages";
import { Meeting, App, Generic } from "@calcom/lib/OgImages";
const calFont = fetch(new URL("../../../../public/fonts/cal.ttf", import.meta.url)).then((res) =>
res.arrayBuffer()
@ -37,6 +37,12 @@ const appSchema = z.object({
slug: z.string(),
});
const genericSchema = z.object({
imageType: z.literal("generic"),
title: z.string(),
description: z.string(),
});
export default async function handler(req: NextApiRequest) {
const { searchParams } = new URL(`${req.url}`);
const imageType = searchParams.get("type");
@ -85,6 +91,17 @@ export default async function handler(req: NextApiRequest) {
});
return new ImageResponse(<App name={name} description={description} slug={slug} />, ogConfig);
}
case "generic": {
const { title, description } = genericSchema.parse({
title: searchParams.get("title"),
description: searchParams.get("description"),
imageType,
});
return new ImageResponse(<Generic title={title} description={description} />, ogConfig);
}
default:
return new Response("What you're looking for is not here..", { status: 404 });
}

View File

@ -1,6 +1,7 @@
import crypto from "crypto";
import type { NextApiRequest, NextApiResponse } from "next";
import { CAL_URL, WEBAPP_URL } from "@calcom/lib/constants";
import { getPlaceholderAvatar } from "@calcom/lib/getPlaceholderAvatar";
import prisma from "@calcom/prisma";
@ -11,6 +12,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
const username = req.query.username as string;
const teamname = req.query.teamname as string;
let identity;
let linksToThisRoute = false;
if (username) {
const user = await prisma.user.findUnique({
where: {
@ -26,6 +28,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
email: user?.email,
avatar: user?.avatar,
};
linksToThisRoute =
identity.avatar === `${CAL_URL}/${username}/avatar.png` ||
identity.avatar === `${WEBAPP_URL}/${username}/avatar.png`;
} else if (teamname) {
const team = await prisma.team.findUnique({
where: {
@ -40,6 +45,9 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
shouldDefaultBeNameBased: true,
avatar: team?.logo,
};
linksToThisRoute =
identity.avatar === `${CAL_URL}/team/${teamname}/avatar.png` ||
identity.avatar === `${WEBAPP_URL}/team/${teamname}/avatar.png`;
}
const emailMd5 = crypto
@ -47,7 +55,8 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
.update((identity?.email as string) || "guest@example.com")
.digest("hex");
const img = identity?.avatar;
if (!img) {
// If image isn't set or links to this route itself, use default avatar
if (!img || linksToThisRoute) {
let defaultSrc = defaultAvatarSrc({ md5: emailMd5 });
if (identity?.shouldDefaultBeNameBased) {
defaultSrc = getPlaceholderAvatar(null, identity.name);

View File

@ -12,7 +12,7 @@ import prisma from "@calcom/prisma";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import App from "@components/v2/apps/App";
import App from "@components/apps/App";
const components = {
a: ({ href = "", ...otherProps }: JSX.IntrinsicElements["a"]) => (

View File

@ -2,8 +2,8 @@ import { GetStaticPaths, InferGetStaticPropsType } from "next";
import { useSession } from "next-auth/react";
import { useRouter } from "next/router";
import { AppSetupPage } from "@calcom/app-store/_pages/setup";
import { getStaticProps } from "@calcom/app-store/_pages/setup/_getStaticProps";
import { AppSetupPage } from "@calcom/app-store/_pages/v2/setup";
export default function SetupInformation(props: InferGetStaticPropsType<typeof getStaticProps>) {
const router = useRouter();
@ -23,7 +23,7 @@ export default function SetupInformation(props: InferGetStaticPropsType<typeof g
});
}
return <AppSetupPage slug={`${slug}-V2`} {...props} />;
return <AppSetupPage slug={slug} {...props} />;
}
export const getStaticPaths: GetStaticPaths = async () => {

View File

@ -6,7 +6,6 @@ import { useRouter } from "next/router";
import { getAppRegistry } from "@calcom/app-store/_appRegistry";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import prisma from "@calcom/prisma";
import { Icon } from "@calcom/ui/Icon";
import { SkeletonText } from "@calcom/ui/v2";
import Shell from "@calcom/ui/v2/core/Shell";
import AppCard from "@calcom/ui/v2/core/apps/AppCard";

View File

@ -1,4 +1,3 @@
import { InferGetServerSidePropsType } from "next";
import { useRouter } from "next/router";
import z from "zod";
@ -10,7 +9,6 @@ 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";
import Button from "@calcom/ui/v2/core/Button";
import EmptyScreen from "@calcom/ui/v2/core/EmptyScreen";
@ -21,18 +19,21 @@ import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/Disconnect
import { QueryCell } from "@lib/QueryCell";
import { CalendarListContainer } from "@components/v2/apps/CalendarListContainer";
import IntegrationListItem from "@components/v2/apps/IntegrationListItem";
import { CalendarListContainer } from "@components/apps/CalendarListContainer";
import IntegrationListItem from "@components/apps/IntegrationListItem";
import SkeletonLoader from "@components/availability/SkeletonLoader";
function ConnectOrDisconnectIntegrationButton(props: {
credentialIds: number[];
type: App["type"];
isGlobal?: boolean;
installed?: boolean;
invalidCredentialIds?: number[];
}) {
const { type, credentialIds, isGlobal, installed, invalidCredentialIds } = props;
const { t } = useLocale();
const [credentialId] = props.credentialIds;
const type = props.type;
const [credentialId] = credentialIds;
const utils = trpc.useContext();
const handleOpenChange = () => {
utils.invalidateQueries(["viewer.integrations"]);
@ -49,6 +50,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
/>
);
}
return (
<DisconnectIntegration
credentialId={credentialId}
@ -58,7 +60,8 @@ function ConnectOrDisconnectIntegrationButton(props: {
/>
);
}
if (!props.installed) {
if (!installed) {
return (
<div className="flex items-center truncate">
<Alert severity="warning" title={t("not_installed")} />
@ -66,7 +69,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
);
}
/** We don't need to "Connect", just show that it's installed */
if (props.isGlobal) {
if (isGlobal) {
return (
<div className="truncate px-3 py-2">
<h3 className="text-sm font-medium text-gray-700">{t("default")}</h3>
@ -75,7 +78,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
}
return (
<InstallAppButton
type={props.type}
type={type}
render={(buttonProps) => (
<Button color="secondary" {...buttonProps} data-testid="integration-connection-button">
{t("install")}
@ -99,28 +102,32 @@ interface IntegrationsListProps {
const IntegrationsList = ({ data }: IntegrationsListProps) => {
return (
<List className="flex flex-col gap-6" noBorderTreatment>
{data.items.map((item) => (
<IntegrationListItem
name={item.name}
slug={item.slug}
key={item.title}
title={item.title}
logo={item.logo}
description={item.description}
separate={true}
actions={
<div className="flex w-16 justify-end">
<ConnectOrDisconnectIntegrationButton
credentialIds={item.credentialIds}
type={item.type}
isGlobal={item.isGlobal}
installed
/>
</div>
}>
<AppSettings slug={item.slug} />
</IntegrationListItem>
))}
{data.items
.filter((item) => item.invalidCredentialIds)
.map((item) => (
<IntegrationListItem
name={item.name}
slug={item.slug}
key={item.title}
title={item.title}
logo={item.logo}
description={item.description}
separate={true}
invalidCredential={item.invalidCredentialIds.length > 0}
actions={
<div className="flex w-16 justify-end">
<ConnectOrDisconnectIntegrationButton
credentialIds={item.credentialIds}
type={item.type}
isGlobal={item.isGlobal}
installed
invalidCredentialIds={item.invalidCredentialIds}
/>
</div>
}>
<AppSettings slug={item.slug} />
</IntegrationListItem>
))}
</List>
);
};

View File

@ -12,7 +12,7 @@ import { Button, Input, TextField } from "@calcom/ui/v2";
import { useLocale } from "@lib/hooks/useLocale";
import { HeadSeo } from "@components/seo/head-seo";
import AuthContainer from "@components/v2/ui/AuthContainer";
import AuthContainer from "@components/ui/AuthContainer";
type Props = {
id: string;

View File

@ -10,7 +10,7 @@ import { EmailField, Button } from "@calcom/ui/v2";
import { getSession } from "@lib/auth";
import { useLocale } from "@lib/hooks/useLocale";
import AuthContainer from "@components/v2/ui/AuthContainer";
import AuthContainer from "@components/ui/AuthContainer";
export default function ForgotPassword({ csrfToken }: { csrfToken: string }) {
const { t, i18n } = useLocale();

View File

@ -23,7 +23,7 @@ import { inferSSRProps } from "@lib/types/inferSSRProps";
import AddToHomescreen from "@components/AddToHomescreen";
import TwoFactor from "@components/auth/TwoFactor";
import AuthContainer from "@components/v2/ui/AuthContainer";
import AuthContainer from "@components/ui/AuthContainer";
import { IS_GOOGLE_LOGIN_ENABLED } from "@server/lib/constants";
import { ssrInit } from "@server/lib/ssr";

View File

@ -10,7 +10,7 @@ import Button from "@calcom/ui/v2/core/Button";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import AuthContainer from "@components/v2/ui/AuthContainer";
import AuthContainer from "@components/ui/AuthContainer";
import { ssrInit } from "@server/lib/ssr";

View File

@ -23,8 +23,8 @@ import { Skeleton, SkeletonText } from "@calcom/ui/v2/core/skeleton";
import { HttpError } from "@lib/core/http/error";
import { SelectSkeletonLoader } from "@components/availability/SkeletonLoader";
import EditableHeading from "@components/ui/EditableHeading";
import { SelectSkeletonLoader } from "@components/v2/availability/SkeletonLoader";
const querySchema = z.object({
schedule: stringOrNumber,
@ -42,7 +42,7 @@ export default function Availability({ schedule }: { schedule: number }) {
const router = useRouter();
const utils = trpc.useContext();
const me = useMeQuery();
const { timeFormat } = me.data || { timeFormat: null };
const { data, isLoading } = trpc.useQuery(["viewer.availability.schedule", { scheduleId: schedule }]);
const form = useForm<AvailabilityFormValues>();
@ -94,7 +94,7 @@ export default function Availability({ schedule }: { schedule: number }) {
data ? (
data.schedule.availability.map((availability) => (
<span key={availability.id}>
{availabilityAsString(availability, { locale: i18n.language })}
{availabilityAsString(availability, { locale: i18n.language, hour12: timeFormat === 12 })}
<br />
</span>
))

View File

@ -10,7 +10,7 @@ import { EmptyScreen, showToast } from "@calcom/ui/v2";
import { withQuery } from "@lib/QueryCell";
import { HttpError } from "@lib/core/http/error";
import SkeletonLoader from "@components/v2/availability/SkeletonLoader";
import SkeletonLoader from "@components/availability/SkeletonLoader";
export function AvailabilityList({ schedules }: inferQueryOutput<"viewer.availability.list">) {
const { t } = useLocale();

View File

@ -15,7 +15,7 @@ import BookingLayout from "@calcom/ui/v2/core/layouts/BookingLayout";
import { useInViewObserver } from "@lib/hooks/useInViewObserver";
import BookingListItem from "@components/booking/BookingListItem";
import SkeletonLoader from "@components/v2/bookings/SkeletonLoader";
import SkeletonLoader from "@components/booking/SkeletonLoader";
type BookingListingStatus = inferQueryInput<"viewer.bookings">["status"];
type BookingOutput = inferQueryOutput<"viewer.bookings">["bookings"][0];

View File

@ -14,8 +14,8 @@ import { collectPageParameters, telemetryEventTypes, useTelemetry } from "@calco
import { detectBrowserTimeFormat } from "@calcom/lib/timeFormat";
import { localStorage } from "@calcom/lib/webstorage";
import prisma, { bookingMinimalSelect } from "@calcom/prisma";
import { Button } from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import { Button } from "@calcom/ui/v2/core/Button";
import { getSession } from "@lib/auth";
import { inferSSRProps } from "@lib/types/inferSSRProps";

View File

@ -133,12 +133,14 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
message = `${err.data.code}: You are not able to update this event`;
}
if (err.data?.code === "PARSE_ERROR") {
if (err.data?.code === "PARSE_ERROR" || err.data?.code === "BAD_REQUEST") {
message = `${err.data.code}: ${err.message}`;
}
if (message) {
showToast(message, "error");
} else {
showToast(err.message, "error");
}
},
});
@ -218,6 +220,7 @@ const EventTypePage = (props: inferSSRProps<typeof getServerSideProps>) => {
enabledWorkflowsNumber={eventType.workflows.length}
eventType={eventType}
team={team}
isUpdateMutationLoading={updateMutation.isLoading}
formMethods={formMethods}
disableBorder={tabName === "apps" || tabName === "workflows"}
currentUserMembership={props.currentUserMembership}>
@ -460,7 +463,7 @@ export const getServerSideProps = async (context: GetServerSidePropsContext) =>
const eventType = {
...restEventType,
schedule: rawEventType.schedule?.id || rawEventType.users[0].defaultScheduleId,
schedule: rawEventType.schedule?.id || rawEventType.users[0]?.defaultScheduleId || null,
recurringEvent: parseRecurringEvent(restEventType.recurringEvent),
bookingLimits: parseBookingLimit(restEventType.bookingLimits),
locations: locations as unknown as LocationObject[],

View File

@ -1,741 +0,0 @@
// @@DEPRECATED, use new getting-started.tsx instead
import { zodResolver } from "@hookform/resolvers/zod";
import { Prisma } from "@prisma/client";
import classnames from "classnames";
import debounce from "lodash/debounce";
import omit from "lodash/omit";
import { NextPageContext } from "next";
import { useSession } from "next-auth/react";
import Head from "next/head";
import { useRouter } from "next/router";
import React, { useCallback, useEffect, useRef, useState } from "react";
import { useForm } from "react-hook-form";
import * as z from "zod";
import getApps from "@calcom/app-store/utils";
import dayjs from "@calcom/dayjs";
import { DEFAULT_SCHEDULE } from "@calcom/lib/availability";
import { DOCS_URL } from "@calcom/lib/constants";
import { fetchUsername } from "@calcom/lib/fetchUsername";
import { useLocale } from "@calcom/lib/hooks/useLocale";
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";
import Button from "@calcom/ui/Button";
import { Icon } from "@calcom/ui/Icon";
import TimezoneSelect from "@calcom/ui/form/TimezoneSelect";
import { Form } from "@calcom/ui/form/fields";
import showToast from "@calcom/ui/v2/core/notifications";
import { getSession } from "@lib/auth";
import { inferSSRProps } from "@lib/types/inferSSRProps";
import { Schedule as ScheduleType } from "@lib/types/schedule";
import { ClientSuspense } from "@components/ClientSuspense";
import Loader from "@components/Loader";
import Schedule from "@components/availability/Schedule";
import { CalendarListContainer } from "@components/integrations/CalendarListContainer";
import { UsernameAvailability } from "@components/ui/UsernameAvailability";
// Embed isn't applicable to onboarding, so ignore the rule
/* eslint-disable @calcom/eslint/avoid-web-storage */
export interface ScheduleFormValues {
schedule: ScheduleType;
}
let mutationComplete: ((err: Error | null) => void) | null;
export default function Onboarding(props: inferSSRProps<typeof getServerSideProps>) {
const { t } = useLocale();
const { user } = props;
const router = useRouter();
const utils = trpc.useContext();
const telemetry = useTelemetry();
const [hasErrors, setHasErrors] = useState(false);
const [errorMessage, setErrorMessage] = useState("");
const { data: eventTypes } = trpc.useQuery(["viewer.eventTypes.list"]);
const onSuccessMutation = async () => {
showToast(t("your_user_profile_updated_successfully"), "success");
setHasErrors(false); // dismiss any open errors
await utils.invalidateQueries(["viewer.me"]);
};
const onErrorMutation = (error: TRPCClientErrorLike<AppRouter>) => {
setHasErrors(true);
setErrorMessage(error.message);
document?.getElementsByTagName("main")[0]?.scrollTo({ top: 0, behavior: "smooth" });
};
const mutation = trpc.useMutation("viewer.updateProfile", {
onSuccess: async () => {
setSubmitting(true);
setEnteredName(nameRef.current?.value || "");
setInputUsernameValue(usernameRef.current?.value || "");
if (mutationComplete) {
mutationComplete(null);
mutationComplete = null;
}
setSubmitting(false);
},
onError: (err: TRPCClientErrorLike<AppRouter>) => {
setError(new Error(err.message));
if (mutationComplete) {
mutationComplete(new Error(err.message));
}
setSubmitting(false);
},
});
const DEFAULT_EVENT_TYPES = [
{
title: t("15min_meeting"),
slug: "15min",
length: 15,
},
{
title: t("30min_meeting"),
slug: "30min",
length: 30,
},
{
title: t("secret_meeting"),
slug: "secret",
length: 15,
hidden: true,
},
];
const [isSubmitting, setSubmitting] = React.useState(false);
const [enteredName, setEnteredName] = React.useState("");
const [currentUsername, setCurrentUsername] = useState(user.username || undefined);
const [inputUsernameValue, setInputUsernameValue] = useState(currentUsername);
const { status } = useSession();
const loading = status === "loading";
const [ready, setReady] = useState(false);
const [selectedImport, setSelectedImport] = useState("");
const [error, setError] = useState<Error | null>(null);
const updateUser = useCallback(
async (data: Prisma.UserUpdateInput) => {
const res = await fetch(`/api/user/${user.id}`, {
method: "PATCH",
body: JSON.stringify({ data: { ...data } }),
headers: {
"Content-Type": "application/json",
},
});
if (!res.ok) {
throw new Error((await res.json()).message);
}
const responseData = await res.json();
return responseData.data;
},
[user.id]
);
const createEventType = trpc.useMutation("viewer.eventTypes.create");
const createSchedule = trpc.useMutation("viewer.availability.schedule.create", {
onError: (error: TRPCClientErrorLike<AppRouter>) => {
throw new Error(error.message);
},
});
/** Name */
const nameRef = useRef<HTMLInputElement>(null);
/** Username */
const usernameRef = useRef<HTMLInputElement>(null!);
const bioRef = useRef<HTMLInputElement>(null);
/** End Name */
/** TimeZone */
const [selectedTimeZone, setSelectedTimeZone] = useState(dayjs.tz.guess());
/** End TimeZone */
/** Onboarding Steps */
const [currentStep, setCurrentStep] = useState(props.initialStep);
const handleConfirmStep = async () => {
try {
setSubmitting(true);
const onComplete = steps[currentStep]?.onComplete;
if (onComplete) {
await onComplete();
}
incrementStep();
setSubmitting(false);
} catch (error) {
setSubmitting(false);
setError(error as Error);
}
};
const debouncedHandleConfirmStep = debounce(handleConfirmStep, 850);
const handleSkipStep = () => {
incrementStep();
};
const incrementStep = () => {
const nextStep = currentStep + 1;
if (nextStep >= steps.length) {
completeOnboarding();
return;
}
setCurrentStep(nextStep);
};
const decrementStep = () => {
const previous = currentStep - 1;
if (previous < 0) {
return;
}
setCurrentStep(previous);
};
const goToStep = (step: number) => {
setCurrentStep(step);
};
/**
* Complete Onboarding finalizes the onboarding flow for a new user.
*
* Here, 3 event types are pre-created for the user as well.
* Set to the availability the user enter during the onboarding.
*
* If a user skips through the Onboarding flow,
* then the default availability is applied.
*/
const completeOnboarding = async () => {
setSubmitting(true);
if (eventTypes?.length === 0) {
await Promise.all(
DEFAULT_EVENT_TYPES.map(async (event) => {
return createEventType.mutate(event);
})
);
}
await updateUser({
completedOnboarding: true,
});
setSubmitting(false);
router.push("/event-types");
};
const schema = z.object({
token: z.string(),
});
const formMethods = useForm<{
token: string;
}>({ resolver: zodResolver(schema), mode: "onSubmit" });
// Should update username on user when being redirected from sign up and doing google/saml
useEffect(() => {
async function validateAndSave(username: string) {
const { data } = await fetchUsername(username);
// Only persist username if its available and not premium
// premium usernames are saved via stripe webhook
if (data.available && !data.premium) {
await updateUser({
username,
});
}
// Remove it from localStorage
window.localStorage.removeItem("username");
return;
}
// Looking for username on localStorage
const username = window.localStorage.getItem("username");
if (username) {
validateAndSave(username);
}
}, [updateUser]);
const availabilityForm = useForm({ defaultValues: { schedule: DEFAULT_SCHEDULE } });
const steps = [
{
id: t("welcome"),
title: t("welcome_to_calcom"),
description: t("welcome_instructions"),
Component: (
<>
{/** @NOTE: Hiding temporarily
* @URL related: https://github.com/calcom/cal.com/issues/3941
*/}
{/* {selectedImport == "" && (
<div className="mb-4 grid grid-cols-2 gap-x-4">
<Button color="secondary" onClick={() => setSelectedImport("calendly")}>
{t("import_from")} Calendly
</Button>
<Button color="secondary" onClick={() => setSelectedImport("savvycal")}>
{t("import_from")} SavvyCal
</Button>
</div>
)} */}
{/* {selectedImport && (
<div>
<h2 className="font-cal text-2xl text-gray-900">
{t("import_from")} {selectedImport === "calendly" ? "Calendly" : "SavvyCal"}
</h2>
<p className="mb-2 text-sm text-gray-500">
{t("you_will_need_to_generate")}. Find out how to do this{" "}
<a href={`${DOCS_URL}/import`}>here</a>.
</p>
<form
className="flex"
onSubmit={formMethods.handleSubmit(async (values) => {
// track the number of imports. Without personal data/payload
telemetry.event(telemetryEventTypes.importSubmitted, {
...collectPageParameters(),
selectedImport,
});
setSubmitting(true);
const response = await fetch(`/api/import/${selectedImport}`, {
method: "POST",
body: JSON.stringify({
token: values.token,
}),
headers: {
"Content-Type": "application/json",
},
});
if (response.status === 201) {
setSubmitting(false);
handleSkipStep();
} else {
await response.json().catch((e) => {
console.log("Error: response.json invalid: " + e);
setSubmitting(false);
});
}
})}>
{hasErrors && <Alert severity="error" title={errorMessage} />}
<input
onChange={async (e) => {
formMethods.setValue("token", e.target.value);
}}
type="text"
name="token"
id="token"
placeholder={t("access_token")}
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
/>
<Button type="submit" className="mt-1 ml-4 h-10">
{t("import")}
</Button>
</form>
</div>
)}
<div className="relative my-4">
<div className="absolute inset-0 flex items-center" aria-hidden="true">
<div className="w-full border-t border-gray-300" />
</div>
<div className="relative flex justify-center">
<span className="bg-white px-2 text-sm text-gray-500">or</span>
</div>
</div>
*/}
<form className="sm:mx-auto sm:w-full">
<section className="space-y-8">
<fieldset>
{user.username !== "" && (
<UsernameAvailability
currentUsername={currentUsername}
setCurrentUsername={setCurrentUsername}
inputUsernameValue={inputUsernameValue}
usernameRef={usernameRef}
setInputUsernameValue={setInputUsernameValue}
onSuccessMutation={onSuccessMutation}
onErrorMutation={onErrorMutation}
user={user}
/>
)}
</fieldset>
<fieldset>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
{t("full_name")}
</label>
<input
ref={nameRef}
type="text"
name="name"
id="name"
autoComplete="given-name"
placeholder={t("your_name")}
defaultValue={user.name ?? enteredName}
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
/>
</fieldset>
<fieldset>
<section className="flex justify-between">
<label htmlFor="timeZone" className="block text-sm font-medium text-gray-700">
{t("timezone")}
</label>
<p className="text-sm leading-tight text-gray-500 dark:text-white">
{t("current_time")}:&nbsp;
<span className="text-black">{dayjs().tz(selectedTimeZone).format("LT")}</span>
</p>
</section>
<TimezoneSelect
id="timeZone"
value={selectedTimeZone}
onChange={({ value }) => setSelectedTimeZone(value)}
className="mt-1 block w-full rounded-md border-gray-300 focus:border-blue-500 focus:ring-blue-500 sm:text-sm"
/>
</fieldset>
</section>
</form>
</>
),
hideConfirm: false,
confirmText: t("continue"),
showCancel: true,
cancelText: t("set_up_later"),
onComplete: async () => {
mutationComplete = null;
setError(null);
const mutationAsync = new Promise((resolve, reject) => {
mutationComplete = (err) => {
if (err) {
reject(err);
return;
}
resolve(null);
};
});
const userUpdateData = {
name: nameRef.current?.value,
username: usernameRef.current?.value,
timeZone: selectedTimeZone,
};
mutation.mutate(userUpdateData);
if (mutationComplete) {
await mutationAsync;
}
},
},
{
id: "connect-calendar",
title: t("connect_your_calendar"),
description: t("connect_your_calendar_instructions"),
Component: (
<section>
<ClientSuspense fallback={<Loader />}>
<CalendarListContainer heading={false} fromOnboarding={true} />
</ClientSuspense>
<footer className="flex flex-col space-y-6 py-6 sm:mx-auto sm:w-full">
<Button
className="justify-center"
EndIcon={Icon.FiArrowRight}
type="button"
onClick={handleConfirmStep}>
{t("continue")}
</Button>
</footer>
</section>
),
hideConfirm: true,
confirmText: t("continue"),
showCancel: true,
cancelText: t("continue_without_calendar"),
},
{
id: "set-availability",
title: t("set_availability"),
description: t("set_availability_instructions"),
Component: (
<Form<ScheduleFormValues>
className="mx-auto max-w-lg bg-white text-black dark:bg-opacity-5 dark:text-white"
form={availabilityForm}
handleSubmit={async (values) => {
try {
setSubmitting(true);
await createSchedule.mutate({
name: t("default_schedule_name"),
...values,
});
debouncedHandleConfirmStep();
setSubmitting(false);
} catch (error) {
if (error instanceof Error) {
setError(error);
}
}
}}>
<section>
<Schedule name="schedule" />
<footer className="flex flex-col space-y-6 py-6 sm:mx-auto sm:w-full">
<Button className="justify-center" EndIcon={Icon.FiArrowRight} type="submit">
{t("continue")}
</Button>
</footer>
</section>
</Form>
),
hideConfirm: true,
showCancel: false,
},
{
id: "profile",
title: t("nearly_there"),
description: t("nearly_there_instructions"),
Component: (
<form className="sm:mx-auto sm:w-full" id="ONBOARDING_STEP_4">
<section className="space-y-4">
<fieldset>
<label htmlFor="name" className="block text-sm font-medium text-gray-700">
{t("full_name")}
</label>
<input
ref={nameRef}
type="text"
name="name"
id="name"
autoComplete="given-name"
placeholder={t("your_name")}
defaultValue={user.name || enteredName}
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
/>
</fieldset>
<fieldset>
<label htmlFor="bio" className="block text-sm font-medium text-gray-700">
{t("about")}
</label>
<input
ref={bioRef}
type="text"
name="bio"
id="bio"
required
className="mt-1 block w-full rounded-sm border border-gray-300 px-3 py-2 focus:border-neutral-500 focus:outline-none focus:ring-neutral-500 sm:text-sm"
defaultValue={user.bio || undefined}
/>
<p className="mt-2 text-sm leading-tight text-gray-500 dark:text-white">
{t("few_sentences_about_yourself")}
</p>
</fieldset>
</section>
</form>
),
hideConfirm: false,
confirmText: t("finish"),
showCancel: true,
cancelText: t("set_up_later"),
onComplete: async () => {
try {
setSubmitting(true);
await updateUser({
bio: bioRef.current?.value,
});
setSubmitting(false);
} catch (error) {
setError(error as Error);
setSubmitting(false);
}
},
},
];
/** End Onboarding Steps */
useEffect(() => {
setReady(true);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
if (loading || !ready) {
return <div className="loader" />;
}
return (
<div className="bg-brand min-h-screen" data-testid="onboarding">
<Head>
<title>Cal.com - {t("getting_started")}</title>
<link rel="icon" href="/favicon.ico" />
</Head>
{isSubmitting && (
<div className="fixed z-10 flex h-full w-full flex-col content-center items-center justify-center bg-white bg-opacity-25">
<Loader />
</div>
)}
<div className="mx-auto px-4 py-24">
<article className="relative">
<section className="space-y-4 sm:mx-auto sm:w-full sm:max-w-lg">
<header>
<p className="font-cal mb-2 text-3xl tracking-wider text-white">{steps[currentStep].title}</p>
<p className="text-sm font-normal text-white">{steps[currentStep].description}</p>
</header>
<section className="space-y-2 pt-4">
<p className="text-xs font-medium text-gray-500 dark:text-white">
Step {currentStep + 1} of {steps.length}
</p>
{error && <Alert severity="error" message={error?.message} />}
<section className="flex w-full space-x-2 rtl:space-x-reverse">
{steps.map((s, index) => {
return index <= currentStep ? (
<div
key={`step-${index}`}
onClick={() => goToStep(index)}
className={classnames(
"h-1 w-1/4 bg-white",
index < currentStep ? "cursor-pointer" : ""
)}
/>
) : (
<div key={`step-${index}`} className="h-1 w-1/4 bg-white bg-opacity-25" />
);
})}
</section>
</section>
</section>
<section className="mx-auto mt-10 max-w-xl rounded-sm bg-white p-10">
{steps[currentStep].Component}
{!steps[currentStep].hideConfirm && (
<footer className="mt-8 flex flex-col space-y-6 sm:mx-auto sm:w-full">
<Button
className="justify-center"
disabled={isSubmitting}
onClick={debouncedHandleConfirmStep}
EndIcon={Icon.FiArrowRight}
data-testid={`continue-button-${currentStep}`}>
{steps[currentStep].confirmText}
</Button>
</footer>
)}
</section>
<section className="mx-auto max-w-xl py-8">
<div className="flex flex-row-reverse justify-between">
<button
disabled={isSubmitting}
onClick={handleSkipStep}
className="text-sm leading-tight text-gray-500 dark:text-white">
{t("next_step")}
</button>
{currentStep !== 0 && (
<button
disabled={isSubmitting}
onClick={decrementStep}
className="text-sm leading-tight text-gray-500 dark:text-white">
{t("prev_step")}
</button>
)}
</div>
</section>
</article>
</div>
</div>
);
}
export async function getServerSideProps(context: NextPageContext) {
const session = await getSession(context);
if (!session?.user?.id) {
return {
redirect: {
permanent: false,
destination: "/auth/login",
},
};
}
const user = await prisma.user.findFirst({
where: {
id: session.user.id,
},
select: {
id: true,
startTime: true,
endTime: true,
username: true,
name: true,
email: true,
bio: true,
avatar: true,
timeZone: true,
identityProvider: true,
completedOnboarding: true,
weekStart: true,
hideBranding: true,
theme: true,
plan: true,
brandColor: true,
darkBrandColor: true,
metadata: true,
timeFormat: true,
allowDynamicBooking: true,
selectedCalendars: {
select: {
externalId: true,
integration: true,
},
},
credentials: {
where: {
userId: session.user.id,
},
select: {
id: true,
type: true,
key: true,
userId: true,
appId: true,
},
},
schedules: {
where: {
userId: session.user.id,
},
select: {
id: true,
},
},
},
});
if (!user) {
throw new Error(`Signed in as ${session.user.id} but cannot be found in db`);
}
if (user.completedOnboarding) {
return {
redirect: {
permanent: false,
destination: "/event-types",
},
};
}
const integrations = getApps(user.credentials)
.filter((item) => item.type.endsWith("_calendar"))
.map((item) => omit(item, "key"));
const { schedules } = user;
const hasConfigureCalendar = integrations.some((integration) => integration.credential !== null);
const hasSchedules = schedules && schedules.length > 0;
return {
props: {
session,
user,
initialStep: hasSchedules ? (hasConfigureCalendar ? 2 : 3) : 0,
},
};
}

View File

@ -18,7 +18,7 @@ import DisconnectIntegration from "@calcom/ui/v2/modules/integrations/Disconnect
import { QueryCell } from "@lib/QueryCell";
import { CalendarSwitch } from "@components/v2/settings/CalendarSwitch";
import { CalendarSwitch } from "@components/settings/CalendarSwitch";
const SkeletonLoader = () => {
return (

View File

@ -158,9 +158,12 @@ const ProfileView = () => {
}>();
const { reset } = formMethods;
const formInitializedRef = useRef(false);
useEffect(() => {
if (user)
// The purpose of reset is to set the initial value obtained from tRPC.
// `user` would change for many reasons (e.g. when viewer.me automatically fetches on window re-focus(a react query feature))
if (user && !formInitializedRef.current) {
formInitializedRef.current = true;
reset({
avatar: user?.avatar || "",
username: user?.username || "",
@ -168,6 +171,7 @@ const ProfileView = () => {
email: user?.email || "",
bio: user?.bio || "",
});
}
}, [reset, user]);
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

View File

@ -8,8 +8,8 @@ import Meta from "@calcom/ui/v2/core/Meta";
import Switch from "@calcom/ui/v2/core/Switch";
import { getLayout } from "@calcom/ui/v2/core/layouts/SettingsLayout";
import DisableTwoFactorModal from "@components/v2/settings/DisableTwoFactorModal";
import EnableTwoFactorModal from "@components/v2/settings/EnableTwoFactorModal";
import DisableTwoFactorModal from "@components/settings/DisableTwoFactorModal";
import EnableTwoFactorModal from "@components/settings/EnableTwoFactorModal";
const TwoFactorAuthView = () => {
const utils = trpc.useContext();

View File

@ -297,7 +297,7 @@ export default function Success(props: SuccessProps) {
aria-hidden="true">
<div
className={classNames(
"inline-block transform overflow-hidden rounded-lg border sm:my-8 sm:max-w-xl",
"main inline-block transform overflow-hidden rounded-lg border sm:my-8 sm:max-w-xl",
isBackgroundTransparent ? "" : "dark:bg-darkgray-100 bg-white dark:border-neutral-700",
"px-8 pt-5 pb-4 text-left align-bottom transition-all sm:w-full sm:py-8 sm:align-middle"
)}
@ -427,6 +427,14 @@ export default function Success(props: SuccessProps) {
</>
);
})}
{bookingInfo?.smsReminderNumber && (
<>
<div className="mt-9 font-medium">{t("number_sms_notifications")}</div>
<div className="col-span-2 mb-2 mt-9">
<p>{bookingInfo.smsReminderNumber}</p>
</div>
</>
)}
</div>
</div>
{!needsConfirmation &&
@ -471,7 +479,7 @@ export default function Success(props: SuccessProps) {
{userIsOwner && !needsConfirmation && !isCancellationMode && !isCancelled && (
<>
<hr className="border-bookinglightest dark:border-darkgray-300" />
<div className="text-bookingdark align-center flex flex-row justify-center pt-8">
<div className="text-bookingdark align-center flex flex-row justify-center py-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")}
</span>
@ -578,7 +586,7 @@ export default function Success(props: SuccessProps) {
)}
{session === null && !(userIsOwner || props.hideBranding) && (
<>
<hr className="border-bookinglightest" />
<hr className="border-bookinglightest dark:border-darkgray-300" />
<div className="border-bookinglightest text-booking-lighter dark:border-darkgray-300 pt-8 text-center text-xs dark:text-white">
<a href="https://cal.com/signup">{t("create_booking_link_with_calcom")}</a>
@ -857,6 +865,7 @@ export async function getServerSideProps(context: GetServerSidePropsContext) {
uid: true,
description: true,
customInputs: true,
smsReminderNumber: true,
user: {
select: {
id: true,

View File

@ -51,7 +51,7 @@ function TeamPage({ team }: TeamPageProps) {
"dark:bg-darkgray-100 dark:border-darkgray-200 group relative rounded-sm border border-neutral-200 bg-white hover:bg-gray-50 dark:hover:border-neutral-600",
!isEmbed && "bg-white"
)}>
<Link href={`${team.slug}/${type.slug}`}>
<Link href={`/team/${team.slug}/${type.slug}`}>
<a className="flex justify-between px-6 py-4" data-testid="event-type-link">
<div className="flex-shrink">
<div className="flex flex-wrap items-center space-x-2">

View File

@ -3,6 +3,7 @@ import { expect } from "@playwright/test";
import { test } from "./lib/fixtures";
import {
bookFirstEvent,
bookOptinEvent,
bookTimeSlot,
selectFirstAvailableTimeSlotNextMonth,
selectSecondAvailableTimeSlotNextMonth,
@ -119,4 +120,21 @@ test.describe("pro user", () => {
await page.goto(`/${pro.username}`);
await bookFirstEvent(page);
});
test("can book an event that requires confirmation and then that booking can be accepted by organizer", async ({
page,
users,
}) => {
await bookOptinEvent(page);
const [pro] = users.get();
await pro.login();
await page.goto("/bookings/unconfirmed");
await Promise.all([
page.click('[data-testid="confirm"]'),
page.waitForResponse((response) => response.url().includes("/api/trpc/viewer.bookings.confirm")),
]);
// This is the only booking in there that needed confirmation and now it should be empty screen
await expect(page.locator('[data-testid="empty-screen"]')).toBeVisible();
});
});

View File

@ -105,11 +105,7 @@ export async function selectSecondAvailableTimeSlotNextMonth(page: Page) {
await page.locator('[data-testid="time"]').nth(1).click();
}
export async function bookFirstEvent(page: Page) {
// Click first event type
await page.click('[data-testid="event-type-link"]');
async function bookEventOnThisPage(page: Page) {
await selectFirstAvailableTimeSlotNextMonth(page);
await bookTimeSlot(page);
@ -122,6 +118,17 @@ export async function bookFirstEvent(page: Page) {
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
}
export async function bookOptinEvent(page: Page) {
await page.locator('[data-testid="event-type-link"]:has-text("Opt in")').click();
await bookEventOnThisPage(page);
}
export async function bookFirstEvent(page: Page) {
// Click first event type
await page.click('[data-testid="event-type-link"]');
await bookEventOnThisPage(page);
}
export const bookTimeSlot = async (page: Page) => {
// --- fill form
await page.fill('[name="name"]', "Test Testson");

View File

@ -2,3 +2,6 @@ User-agent: *
Disallow: /sandbox
Disallow: /api
Disallow: /static/locales
# Specifically allow access to OG Image api, otherwise eg Twitter won't render these images.
Allow: /api/social/og/image

View File

@ -313,7 +313,7 @@
"past_bookings": "ستظهر هنا عمليات حجزك السابقة.",
"cancelled_bookings": "ستظهر هنا عمليات حجزك التي تم إلغاؤها.",
"unconfirmed_bookings": "ستظهر هنا كل حجوزات غير المؤكدة بعد.",
"unconfirmed_bookings_tooltip": "حجز غير مؤكد",
"unconfirmed_bookings_tooltip": "الحجوزات غير المؤكدة",
"on": "في",
"and": "و",
"calendar_shows_busy_between": "يظهر التقويم أنك مشغول بين",
@ -768,19 +768,19 @@
"no_category_apps_description_calendar": "إضافة تطبيق تقويم للتحقق من أي تضارب لمنع أي حجوزات مزدوجة",
"no_category_apps_description_conferencing": "جرب إضافة تطبيق مؤتمرات لإنشاء مكالمات فيديو مع عملائك",
"no_category_apps_description_payment": "أضف تطبيق دفع لتسهيل التعاملات المالية بينك وبين عملائك",
"no_category_apps_description_analytics": "إضافة تطبيق تحليلي لصفحات الحجز الخاصة بك",
"no_category_apps_description_automation": "إضافة تطبيق أتمتة للاستخدام",
"no_category_apps_description_analytics": "إضافة تطبيق تحليلات لصفحات الحجز الخاصة بك",
"no_category_apps_description_automation": "إضافة تطبيق أتمتة لاستخدامه",
"no_category_apps_description_other": "أضف أي نوع آخر من التطبيقات للقيام بأي شيء",
"installed_app_calendar_description": "قم بتعيين التقويم (التقويمات) للتحقق من أي تضارب لمنع الحجوزات المزدوجة.",
"installed_app_conferencing_description": "أضف تطبيقات مؤتمرات الفيديو المفضلة لديك لاجتماعاتك",
"installed_app_payment_description": "قم بإعداد أي خدمات معالجة دفع تود استخدامها عند التحصيل من العملاء.",
"installed_app_analytics_description": كوين أي تطبيقات لاستخدامها مع صفحات الحجز لديك",
"installed_app_analytics_description": هيئة أي تطبيقات تستخدم لصفحات الحجز الخاصة بك",
"installed_app_other_description": "جميع تطبيقاتك المثبتة من الفئات الأخرى.",
"installed_app_automation_description": كوين تطبيقات الأتمتة التي ستُستخدم",
"installed_app_automation_description": هيئة تطبيقات الأتمتة التي سيتم استخدامها",
"analytics": "التحليلات",
"empty_installed_apps_headline": "لم يتم تثبيت أي تطبيق",
"empty_installed_apps_description": "تعطيك التطبيقات القدرة على تعزيز سير عملك وتحسين الجدولة في حياتك بشدة.",
"empty_installed_apps_button": "استكشف متجر التطبيقات (App Store)",
"empty_installed_apps_button": "استكشف App Store أو قم بالتثبيت من التطبيقات أدناه",
"manage_your_connected_apps": "إدارة تطبيقاتك المثبتة أو تغيير الإعدادات",
"browse_apps": "استعراض التطبيقات",
"features": "الميزات",
@ -1008,7 +1008,6 @@
"app_removed_successfully": "تمت إزالة التطبيق بنجاح",
"error_removing_app": "خطأ في إزالة التطبيق",
"web_conference": "مؤتمر عبر الإنترنت",
"number_for_sms_reminders": "رقم الهاتف (لتذكير الرسائل القصيرة)",
"requires_confirmation": "يتطلب التأكيد",
"nr_event_type_one": "{{count}} نوع حدث",
"nr_event_type_other": "{{count}} من أنواع الحدث",
@ -1301,16 +1300,16 @@
"require_additional_notes_description": "يتطلب ملء ملاحظات إضافية عند الحجز",
"email_address_action": "إرسال بريد إلكتروني إلى عنوان بريد إلكتروني محدد",
"after_event_trigger": "بعد انتهاء الحدث",
"how_long_after": "كم من الوقت بعد انتهاء الحدث؟",
"no_available_slots": "لا تتوفر شواغر",
"how_long_after": "كم من الوقت بعد انتهاء الحدث ؟",
"no_available_slots": "لا توجد فترات زمنية متاحة",
"time_available": "الوقت المتاح",
"install_new_calendar_app": "تثبيت تطبيق تقويم جديد",
"make_phone_number_required": "جعل رقم الهاتف مطلوب لحجز الحدث",
"dont_have_permission": ا صلاحيات كافية لديك للوصول إلى هذا المصدر.",
"saml_config": هيئة SAML",
"dont_have_permission": يس لديك الصلاحيات الكافية للوصول إلى هذا المصدر.",
"saml_config": سجيل الدخول لمرة واحدة",
"saml_description": "السماح لأعضاء الفريق بتسجيل الدخول باستخدام موفر الهوية",
"saml_config_deleted_successfully": "تم حذف تكوين SAML بنجاح",
"saml_config_updated_successfully": "تم تحديث تكوين SAML بنجاح",
"saml_config_updated_successfully": "تم تحديث تهيئة SAML بنجاح",
"saml_configuration": "تكوين SAML",
"delete_saml_configuration": "حذف تكوين SAML",
"delete_saml_configuration_confirmation_message": "هل تريد بالتأكيد حذف تكوين SAML؟ لن يتمكن بعد الآن أعضاء فريقك الذين يستخدمون تسجيل الدخول عبر SAML من الوصول إلى Cal.com.",
@ -1320,11 +1319,11 @@
"saml_configuration_placeholder": "يُرجى لصق بيانات تعريف SAML المقدمة من مزود الهوية هنا",
"saml_email_required": "يُرجى إدخال بريد إلكتروني حتى نتمكن من العثور على مزود هوية SAML الخاص بك",
"saml_sp_title": "تفاصيل موفر الخدمة",
"saml_sp_description": "سيطلب منك موفر الهوية (IdP) التفاصيل التالية لإكمال تكوين تطبيق SAML.",
"saml_sp_description": "سيطلب منك موفر الهوية (IdP) التفاصيل التالية لإكمال تهيئة تطبيق SAML.",
"saml_sp_acs_url": "رابط ACS",
"saml_sp_entity_id": "معرف الكيان SP",
"saml_sp_acs_url_copied": "تم نسخ رابط ACS!",
"saml_sp_entity_id_copied": "تم نسخ معرف كيان SP!",
"saml_btn_configure": كوين",
"saml_btn_configure": هيئة",
"add_calendar": "إضافة تقويم"
}

View File

@ -769,7 +769,7 @@
"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_analytics": "Přidejte analytickou aplikaci pro vaše rezervační stránky",
"no_category_apps_description_automation": "Přidat automatickou aplikaci k použití",
"no_category_apps_description_automation": "Přidejte aplikaci pro automatizaci",
"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",
@ -777,10 +777,10 @@
"installed_app_analytics_description": "Nakonfigurujte aplikace, které chcete použít pro své rezervační stránky",
"installed_app_other_description": "Všechny vaše nainstalované aplikace z ostatních kategorií.",
"installed_app_automation_description": "Konfigurovat aplikace pro automatizaci",
"analytics": "Analytici",
"analytics": "Analytické nástroje",
"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",
"empty_installed_apps_button": "Prozkoumejte App Store nebo nainstalujte aplikaci z následujícího seznamu",
"manage_your_connected_apps": "Spravovat nainstalované aplikace nebo změnit nastavení",
"browse_apps": "Procházet aplikace",
"features": "Funkce",
@ -834,7 +834,7 @@
"redirect_success_booking": "Přesměrovat při rezervaci ",
"you_are_being_redirected": "Budete přesměrováni na {{ url }} za $t(s, {\"count\": {{s}} }).",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "Přesměrovat na vlastní adresu URL po úspěšné objednávce",
"redirect_url_description": "Po úspěšné objednávce přesměrovat na vlastní adresu URL",
"duplicate": "Duplikovat",
"offer_seats": "Nabídka míst",
"offer_seats_description": "Nabídnout místa k rezervaci. Tím se vypnou rezervace hostů a volitelné rezervace.",
@ -1008,7 +1008,6 @@
"app_removed_successfully": "Aplikace byla odstraněna",
"error_removing_app": "Při odstraňování aplikace došlo k chybě",
"web_conference": "Webová konference",
"number_for_sms_reminders": "Telefonní číslo (pro upomínky přes SMS)",
"requires_confirmation": "Vyžaduje potvrzení",
"nr_event_type_one": "Typ události: {{count}}",
"nr_event_type_other": "Typy událostí: {{count}}",
@ -1165,7 +1164,7 @@
"connect_conference_apps": "Propojit konferenční aplikace",
"connect_calendar_apps": "Propojit kalendářové aplikace",
"connect_payment_apps": "Propojit platební aplikace",
"connect_automation_apps": "Připojit automatizační aplikace",
"connect_automation_apps": "Připojit aplikace pro automatizaci",
"connect_analytics_apps": "Připojit analytické aplikace",
"connect_other_apps": "Propojit další aplikace",
"current_step_of_total": "Krok {{currentStep}} / {{maxSteps}}",
@ -1241,7 +1240,7 @@
"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 si klíče API pro přístup k vlastnímu účtu",
"new_api_key": "Nový klíč API",
"active": "Aktivní",
"active": "aktivní",
"api_key_updated": "Název klíče API byl aktualizován",
"api_key_update_failed": "Při aktualizaci názvu klíče API došlo k chybě",
"embeds_title": "Vložení rámce HTML iframe",
@ -1276,7 +1275,7 @@
"manage_billing": "Spravovat fakturaci",
"manage_billing_description": "Spravovat vše ohledně fakturace",
"billing_freeplan_title": "Momentálně používáte tarif ZDARMA",
"billing_freeplan_description": "Pracujeme lépe v týmech. Rozšiřte své pracovní postupy s oběžníky a kolektivními událostmi a vytvořte pokročilé směrovací formuláře",
"billing_freeplan_description": "Lépe se pracuje v týmech. Rozšiřte své pracovní postupy o „round-robin“ a kolektivní události a vytvořte pokročilé směrovací formuláře",
"billing_freeplan_cta": "Vyzkoušet nyní",
"billing_manage_details_title": "Zobrazit a spravovat fakturační údaje",
"billing_manage_details_description": "Zobrazit a spravovat fakturační údaje, možnost zrušit předplatné.",
@ -1292,11 +1291,11 @@
"booking_confirmation_fail": "Potvrzení rezervace se nezdařilo",
"we_wont_show_again": "Znovu toto nezobrazovat",
"couldnt_update_timezone": "Časové pásmo nelze aktualizovat",
"updated_timezone_to": "Časové pásmo aktualizováno na {{formattedCurrentTz}}",
"updated_timezone_to": "Časové pásmo bylo aktualizováno na {{formattedCurrentTz}}",
"update_timezone": "Aktualizovat časové pásmo",
"update_timezone_question": "Aktualizovat časové pásmo?",
"update_timezone_description": "Zdá se, že se místní časové pásmo změnilo na {{formattedCurrentTz}}. Je velmi důležité mít správné časové pásmo, aby se zabránilo rezervacím v nežádoucích časech. Chcete jej aktualizovat?",
"dont_update": "Neaktualizovat",
"update_timezone_description": "Zdá se, že se místní časové pásmo změnilo na {{formattedCurrentTz}}. Je velmi důležité zvolit správné časové pásmo, aby se zabránilo rezervacím v nežádoucích časech. Chcete ho změnit?",
"dont_update": "Ne",
"require_additional_notes": "Požadovat další poznámky",
"require_additional_notes_description": "Požadovat vyplnění doplňujících poznámek při rezervaci",
"email_address_action": "odeslat e-mail na specifickou e-mailovou adresu",
@ -1306,9 +1305,9 @@
"time_available": "Dostupný čas",
"cant_find_the_right_video_app_visit_our_app_store": "Nemůžete najít správnou video aplikaci? Navštivte náš <1>App Store</1>.",
"install_new_calendar_app": "Nainstalovat novou aplikaci kalendáře",
"make_phone_number_required": "Požadovat telefonní číslo pro událost rezervace",
"make_phone_number_required": "Pro rezervaci události vyžadovat telefonní číslo",
"dont_have_permission": "Nemáte oprávnění k přístupu k tomuto dokumentu.",
"saml_config": "Konfigurace SAML",
"saml_config": "Single Sign-On",
"saml_description": "Povolit členům týmu přihlásit se pomocí poskytovatele identity",
"saml_config_deleted_successfully": "SAML konfigurace byla úspěšně smazána",
"saml_config_updated_successfully": "Konfigurace SAML byla úspěšně aktualizována",
@ -1324,8 +1323,8 @@
"saml_sp_description": "Váš poskytovatel identity (IdP) vás požádá, abyste dokončili konfiguraci SAML aplikace.",
"saml_sp_acs_url": "Adresa URL ACS",
"saml_sp_entity_id": "ID entity SP",
"saml_sp_acs_url_copied": "URL adresa ACS zkopírována!",
"saml_sp_entity_id_copied": "ID entity SP zkopírováno!",
"saml_sp_acs_url_copied": "URL adresa ACS byla zkopírována!",
"saml_sp_entity_id_copied": "ID entity SP bylo zkopírováno!",
"saml_btn_configure": "Konfigurovat",
"add_calendar": "Přidat kalendář"
}

View File

@ -1007,7 +1007,6 @@
"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",
"nr_event_type_one": "{{count}} Ereignistyp",
"nr_event_type_other": "{{count}} Ereignistypen",

View File

@ -140,6 +140,7 @@
"slide_zoom_drag_instructions": "Slide to zoom, drag to reposition",
"view_notifications": "View notifications",
"view_public_page": "View public page",
"copy_public_page_link": "Copy public page link",
"sign_out": "Sign out",
"add_another": "Add another",
"install_another": "Install another",
@ -1009,7 +1010,6 @@
"app_removed_successfully": "App removed successfully",
"error_removing_app": "Error removing app",
"web_conference": "Web conference",
"number_for_sms_reminders": "Phone number (for SMS reminders)",
"requires_confirmation": "Requires confirmation",
"nr_event_type_one": "{{count}} event type",
"nr_event_type_other": "{{count}} event types",
@ -1266,9 +1266,9 @@
"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",
"limit_booking_frequency":"Limit booking frequency",
"limit_booking_frequency_description":"Limit how many times this event can be booked",
"add_limit":"Add Limit",
"limit_booking_frequency": "Limit booking frequency",
"limit_booking_frequency_description": "Limit how many times this event can be booked",
"add_limit": "Add Limit",
"team_name_required": "Team name required",
"show_attendees": "Share attendee information between guests",
"how_additional_inputs_as_variables": "How to use Additional Inputs as Variables",
@ -1332,8 +1332,8 @@
"saml_sp_entity_id_copied": "SP Entity ID copied!",
"saml_btn_configure": "Configure",
"add_calendar": "Add Calendar",
"limit_future_bookings":"Limit future bookings",
"limit_future_bookings_description":"Limit how far in the future this event can be booked",
"limit_future_bookings": "Limit future bookings",
"limit_future_bookings_description": "Limit how far in the future this event can be booked",
"no_event_types": "No event types setup",
"no_event_types_description": "{{name}} has not setup any event types for you to book.",
"billing_frequency": "Billing Frequency",
@ -1349,5 +1349,9 @@
"team_name_taken": "This name is already taken",
"must_enter_team_name": "Must enter a team name",
"team_url_required": "Must enter a team URL",
"team_url_taken": "This URL is already taken"
"team_url_taken": "This URL is already taken",
"number_sms_notifications": "Phone number (SMS\u00a0notifications)",
"attendee_email_workflow": "Attendee email",
"attendee_email_info": "The person booking's email",
"invalid_credential": "Oh no! Looks like permission expired or was revoked. Please reinstall again."
}

View File

@ -1007,7 +1007,6 @@
"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",
"nr_event_type_one": "{{count}} tipo de evento",
"nr_event_type_other": "{{count}} tipos de eventos",

View File

@ -1007,7 +1007,6 @@
"app_removed_successfully": "Application supprimée",
"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",
"nr_event_type_one": "{{count}} type d'événement",
"nr_event_type_other": "{{count}} types d'événements",
@ -1275,14 +1274,14 @@
"manage_billing": "Gérer la facturation",
"manage_billing_description": "Gérer toute la facturation",
"billing_freeplan_title": "Vous êtes actuellement sur le forfait GRATUIT",
"billing_freeplan_description": "Nous travaillons mieux en équipe. Élargissez vos workflows avec rond-robin et événements collectifs et faites des formulaires de routage avancés",
"billing_freeplan_description": "Nous travaillons mieux en équipe. Élargissez vos flux de travail avec le tourniquet et les événements collectifs, et produisez des formulaires de routage avancés",
"billing_freeplan_cta": "Essayer maintenant",
"billing_manage_details_title": "Affichez et gérez vos informations de facturation",
"billing_manage_details_description": "Affichez et modifiez vos informations de facturation, et annulez votre abonnement.",
"billing_portal": "Portail de facturation",
"billing_help_title": "Besoin d'autre chose ?",
"billing_help_description": "Si vous avez besoin d'aide pour la facturation, notre équipe d'assistance est là pour vous aider.",
"billing_help_cta": "Contacter le support",
"billing_help_cta": "Contacter l'assistance",
"ignore_special_characters": "Ignorer les caractères spéciaux dans votre étiquette de saisie supplémentaire. Utilisez uniquement des lettres et des chiffres",
"retry": "Réessayer",
"fetching_calendars_error": "Un problème est survenu lors de la récupération de vos calendriers. Veuillez <1>réessayer</1> ou contacter le service client.",
@ -1291,10 +1290,10 @@
"booking_confirmation_fail": "Échec de la confirmation de la réservation",
"we_wont_show_again": "Nous n'afficherons plus ceci",
"couldnt_update_timezone": "Nous n'avons pas pu mettre à jour le fuseau horaire",
"updated_timezone_to": "Fuseau horaire mis à jour vers {{formattedCurrentTz}}",
"updated_timezone_to": "Fuseau horaire mis à jour. Nouveau : {{formattedCurrentTz}}",
"update_timezone": "Mettre à jour le fuseau horaire",
"update_timezone_question": "Mettre à jour le fuseau horaire ?",
"update_timezone_description": "Il semble que votre fuseau horaire local soit passé à {{formattedCurrentTz}}. Il est très important d'avoir le fuseau horaire correct pour éviter les réservations à des heures indésirables. Voulez-vous le mettre à jour ?",
"update_timezone_description": "Votre fuseau horaire local semble être passé sur {{formattedCurrentTz}}. Il est très important d'avoir le fuseau horaire correct pour éviter les réservations à des heures indésirables. Voulez-vous le mettre à jour ?",
"dont_update": "Ne pas mettre à jour",
"require_additional_notes": "Nécessite des notes supplémentaires",
"require_additional_notes_description": "Nécessite des notes supplémentaires à remplir lors de la réservation",
@ -1306,10 +1305,10 @@
"install_new_calendar_app": "Installer une nouvelle application de calendrier",
"make_phone_number_required": "Rendre le numéro de téléphone nécessaire pour la réservation de l'événement",
"dont_have_permission": "Vous n'avez pas la permission d'accéder à cette ressource.",
"saml_config": "Configuration SAML",
"saml_config": "Authentification unique",
"saml_description": "Autoriser les membres de l'équipe à se connecter à l'aide d'un fournisseur d'identité",
"saml_config_deleted_successfully": "Configuration SAML supprimée avec succès",
"saml_config_updated_successfully": "Configuration SAML mise à jour avec succès",
"saml_config_updated_successfully": "Configuration SAML mise à jour",
"saml_configuration": "Configuration SAML",
"delete_saml_configuration": "Supprimer la configuration SAML",
"delete_saml_configuration_confirmation_message": "Êtes-vous sûr de vouloir supprimer la configuration SAML ? Les membres de votre équipe qui utilisent la connexion SAML ne pourront plus accéder à Cal.com.",

View File

@ -779,7 +779,7 @@
"analytics": "ניתוח נתונים",
"empty_installed_apps_headline": "אין אפליקציות מותקנות",
"empty_installed_apps_description": "אפליקציות מאפשרות לך לשפר משמעותית את תהליכי העבודה שלך ואת תהליך קביעת המועדים.",
"empty_installed_apps_button": "נא לעיין ב-App Store",
"empty_installed_apps_button": "נא לעיין ב-App Store או להתקין מהאפליקציות שלהלן",
"manage_your_connected_apps": "ניהול האפליקציות המותקנות שלך או שינוי הגדרות",
"browse_apps": "עיון באפליקציות",
"features": "תכונות",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "האפליקציה הוסרה בהצלחה",
"error_removing_app": "שגיאה בהסרת האפליקציה",
"web_conference": "שיחת ועידה מקוונת",
"number_for_sms_reminders": "מספר טלפון (לתזכורות ב-SMS)",
"requires_confirmation": "נדרש אישור",
"nr_event_type_one": "סוג אירוע {{count}}",
"nr_event_type_other": "{{count}} סוגי אירועים",
@ -1275,7 +1274,7 @@
"manage_billing": "ניהול החיוב",
"manage_billing_description": "ניהול כל מה שקשור לחיובים",
"billing_freeplan_title": "החבילה הנוכחית שלך היא החבילה החינמית",
"billing_freeplan_description": "אנחנו עובדים טוב יותר בצוותים. הרחב/י את תהליכי העבודה שלך באמצעות סבבים ואירועים שיתופיים וצור/י טפסי ניתוב מתקדמים",
"billing_freeplan_description": "אנחנו עובדים טוב יותר בצוותים. הרחב/הרחיבי את תהליכי העבודה שלך באמצעות סבבים ואירועים שיתופיים וצור/י טפסי ניתוב מתקדמים",
"billing_freeplan_cta": "נסה/י כעת",
"billing_manage_details_title": "צפייה בפרטי החיוב שלך וניהולם",
"billing_manage_details_description": "צפה/י בפרטי החיוב שלך וערוך/ערכי אותם, וכן בטל/י את המינוי שלך.",
@ -1283,11 +1282,11 @@
"billing_help_title": "צריך/ה משהו נוסף?",
"billing_help_description": "אם דרוש לך סיוע נוסף בענייני חיוב, צוות התמיכה שלנו כאן כדי לעזור.",
"billing_help_cta": "פנייה לתמיכה",
"ignore_special_characters": "התעלמות מתווים מיוחדים בתווית הקלט הנוספת. שימוש באותיות ובספרות בלבד",
"ignore_special_characters": "להתעלם מתווים מיוחדים בתווית הקלט הנוספת. להשתמש באותיות ובספרות בלבד",
"retry": "ניסיון נוסף",
"fetching_calendars_error": "אירעה בעיה בטעינת לוחות השנה שלך. <1>נסה/י שוב</1> או פנה/י למחלקת תמיכת לקוחות.",
"fetching_calendars_error": "אירעה בעיה בטעינת לוחות השנה שלך. <1>נסה/י שוב</1> או פנה/י למחלקת תמיכת הלקוחות.",
"calendar_connection_fail": "החיבור ללוח השנה נכשל",
"booking_confirmation_success": "אישור ההזמנה בוצע בהצלחה",
"booking_confirmation_success": "ההזמנה אושרה בהצלחה",
"booking_confirmation_fail": "אישור ההזמנה נכשל",
"we_wont_show_again": "לא נציג זאת שוב",
"couldnt_update_timezone": "לא הצלחנו לעדכן את אזור הזמן",
@ -1296,9 +1295,9 @@
"update_timezone_question": "לעדכן את אזור הזמן?",
"update_timezone_description": "נראה שאזור הזמן המקומי שלך השתנה ל-{{formattedCurrentTz}}. מאוד חשוב שאזור הזמן המוגדר יהיה נכון על מנת למנוע הזמנות בשעות לא רצויות. האם ברצונך לעדכן את אזור הזמן?",
"dont_update": "לא לעדכן",
"require_additional_notes": "דרוש הערות נוספות",
"require_additional_notes_description": "דרישה למילוי הערות נוספות בעת ביצוע הזמנה",
"email_address_action": "שליחת דוא\"ל לכתובת מסוימת",
"require_additional_notes": "לדרוש הערות נוספות",
"require_additional_notes_description": "לדרוש למלא הערות נוספות בעת ביצוע הזמנה",
"email_address_action": "לשלוח דוא״ל לכתובת מסוימת",
"after_event_trigger": "לאחר סיום האירוע",
"how_long_after": "כמה זמן לאחר סיום האירוע?",
"no_available_slots": "אין חלונות זמן זמינים",
@ -1306,7 +1305,7 @@
"install_new_calendar_app": "התקנת האפליקציה החדשה של לוחות שנה",
"make_phone_number_required": "הגדרת מספר טלפון כפרט נדרש עבור סוג האירוע הזה",
"dont_have_permission": "אין לך הרשאת גישה אל המשאב הזה.",
"saml_config": "תצורת SAML",
"saml_config": "כניסה בודדת",
"saml_description": "אפשר/י לחברי הצוות להתחבר באמצעות ספק זהויות",
"saml_config_deleted_successfully": "תצורת SAML נמחקה בהצלחה",
"saml_config_updated_successfully": "תצורת SAML עודכנה בהצלחה",

View File

@ -779,7 +779,7 @@
"analytics": "Analisi",
"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",
"empty_installed_apps_button": "Esplora l'App Store o installa dalle app sottostanti",
"manage_your_connected_apps": "Gestisci le app installate o modifica le impostazioni",
"browse_apps": "Sfoglia app",
"features": "Funzionalità",
@ -833,7 +833,7 @@
"redirect_success_booking": "Reindirizza su prenotazione ",
"you_are_being_redirected": "Verrai reindirizzato a {{ url }} tra $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "Reindirizza a un URL personalizzato dopo una prenotazione correttamente effettuata",
"redirect_url_description": "Dopo una prenotazione, reindirizza a un URL personalizzato",
"duplicate": "Duplica",
"offer_seats": "Imposta posti",
"offer_seats_description": "Indica i posti disponibili (ciò disabiliterà le prenotazioni ospite e le prenotazioni opt-in).",
@ -1007,7 +1007,6 @@
"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",
"nr_event_type_one": "{{count}} tipo di evento",
"nr_event_type_other": "{{count}} tipi di evento",
@ -1240,7 +1239,7 @@
"webhooks_description": "Ricevi in tempo reale le informazioni sulle riunioni quando succede qualcosa su Cal.com",
"api_keys_description": "Genera chiavi API per accedere al tuo account",
"new_api_key": "Nuova chiave API",
"active": "Attivo",
"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",
@ -1264,7 +1263,7 @@
"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 siano pubblicate da Cal.com. Se noti contenuti o comportamenti inappropriati, ti invitiamo a segnalarceli.",
"report_app": "Segnala app",
"limit_booking_frequency": "Limita frequenza di prenotazione",
"limit_booking_frequency_description": "Limita quante volte è possibile prenotare questo evento",
"limit_booking_frequency_description": "Indica quante volte è possibile prenotare questo evento",
"add_limit": "Aggiungi limite",
"team_name_required": "Nome del team richiesto",
"show_attendees": "Condividi le informazioni dei partecipanti tra gli ospiti",
@ -1273,19 +1272,19 @@
"uppercase_for_letters": "Usa tutte lettere maiuscole",
"replace_whitespaces_underscores": "Sostituisci spazi con sottolineature",
"manage_billing": "Gestisci fatturazione",
"manage_billing_description": "Gestisci la fatturazione di tutti gli elementi",
"billing_freeplan_title": "Stai attualmente usando il piano GRATUITO",
"billing_freeplan_description": "Lavoriamo meglio in squadra. Estendi i tuoi flussi di lavoro con tavole rotonde ed eventi di gruppo e crea moduli di instradamento avanzati",
"manage_billing_description": "Gestisci tutto ciò che riguarda la fatturazione",
"billing_freeplan_title": "Attualmente stai usando il piano GRATUITO",
"billing_freeplan_description": "Si lavora meglio in squadra. Migliora i tuoi flussi di lavoro con eventi round robin o di gruppo e crea moduli di instradamento avanzati",
"billing_freeplan_cta": "Prova ora",
"billing_manage_details_title": "Visualizza e gestisci i tuoi dati di fatturazione",
"billing_manage_details_description": "Visualizza e modifica i tuoi dati di fatturazione e annulla il tuo abbonamento.",
"billing_portal": "Portale di fatturazione",
"billing_help_title": "Hai bisogno di altro?",
"billing_help_description": "Se hai bisogno di ulteriore aiuto per la fatturazione, il nostro team di supporto è qui per aiutarti.",
"billing_help_cta": "Contatta il supporto",
"ignore_special_characters": "Ignora caratteri speciali nelle etichette degli input aggiuntivi. Usa solo lettere e numeri",
"billing_help_cta": "Contatta l'assistenza",
"ignore_special_characters": "Ignora i caratteri speciali nelle etichette degli input aggiuntivi. Usa solo lettere e numeri",
"retry": "Riprova",
"fetching_calendars_error": "Si è verificato un problema durante il recupero dei calendari. Si prega di <1>riprovare</1> o di contattare l'assistenza clienti.",
"fetching_calendars_error": "Si è verificato un problema durante il recupero dei calendari. <1>Riprova</1> o contatta l'assistenza clienti.",
"calendar_connection_fail": "Impossibile connettersi al calendario",
"booking_confirmation_success": "Prenotazione confermata correttamente",
"booking_confirmation_fail": "Conferma prenotazione non riuscita",
@ -1294,7 +1293,7 @@
"updated_timezone_to": "Fuso orario aggiornato a {{formattedCurrentTz}}",
"update_timezone": "Aggiorna fuso orario",
"update_timezone_question": "Aggiornare il fuso orario?",
"update_timezone_description": "Sembra che il fuso orario locale sia stato cambiato in {{formattedCurrentTz}}. È molto importante che il fuso orario sia impostato correttamente per evitare prenotazioni in orari indesiderati. Aggiornare il fuso orario?",
"update_timezone_description": "Sembra che il fuso orario locale sia stato cambiato in {{formattedCurrentTz}}. È molto importante che il fuso orario sia impostato correttamente per evitare prenotazioni in orari indesiderati. Vuoi aggiornare il fuso orario?",
"dont_update": "Non aggiornare",
"require_additional_notes": "Richiedi note aggiuntive",
"require_additional_notes_description": "Chiedi di compilare le note aggiuntive al momento della prenotazione",
@ -1304,7 +1303,7 @@
"no_available_slots": "Nessuna fascia oraria disponibile",
"time_available": "Tempo disponibile",
"install_new_calendar_app": "Installa nuova app di calendario",
"make_phone_number_required": "Rendi tassativo il numero di telefono per prenotare un evento",
"make_phone_number_required": "Rendi obbligatorio il numero di telefono per prenotare un evento",
"dont_have_permission": "Non hai l'autorizzazione per accedere a questa risorsa.",
"saml_config": "Configurazione SAML",
"saml_description": "Consenti ai membri del team di effettuare l'accesso utilizzando un provider di identità",
@ -1318,7 +1317,7 @@
"saml_configuration_description": "Per aggiornare la configurazione SAML, incolla nella casella sottostante i metadati SAML forniti dal tuo provider di identità.",
"saml_configuration_placeholder": "Incolla qui i metadati SAML forniti dal tuo provider di identità",
"saml_email_required": "Inserisci un indirizzo email che consenta di individuare il tuo provider di identità SAML",
"saml_sp_title": "Dettagli del fornitore di servizi",
"saml_sp_title": "Dettagli del provider",
"saml_sp_description": "Il tuo provider di identità (IdP) ti chiederà le seguenti informazioni per completare la configurazione dell'applicazione SAML.",
"saml_sp_acs_url": "URL dell'ACS",
"saml_sp_entity_id": "ID Entità SP",

View File

@ -773,13 +773,13 @@
"installed_app_calendar_description": "ダブルブッキングを防ぐために、スケジュールの重複をチェックするカレンダーアプリを設定しましょう。",
"installed_app_conferencing_description": "会議用にお気に入りのビデオ会議アプリを追加しましょう",
"installed_app_payment_description": "顧客に請求をする際に使用する決済処理サービスを設定しましょう。",
"installed_app_analytics_description": "予約ページでどの分析アプリを使用するかを設定します",
"installed_app_analytics_description": "予約ページでどの分析アプリを使用するかを構成します",
"installed_app_other_description": "他のカテゴリーからインストールしたすべてのアプリ。",
"installed_app_automation_description": "どの自動化アプリを使用するかを設定します",
"installed_app_automation_description": "どの自動化アプリを使用するかを構成します",
"analytics": "分析",
"empty_installed_apps_headline": "アプリがインストールされていません",
"empty_installed_apps_description": "アプリを使えば、ワークフローの質を高め、スケジュールライフを大幅に改善することができます。",
"empty_installed_apps_button": "App Storeで探す",
"empty_installed_apps_button": "App Store で探す",
"manage_your_connected_apps": "インストール済みアプリの管理や設定の変更を行う",
"browse_apps": "アプリを閲覧",
"features": "特徴",
@ -829,11 +829,11 @@
"time_format": "時間の形式",
"12_hour": "12 時間",
"24_hour": "24 時間",
"24_hour_short": "24時間",
"24_hour_short": "24 時間",
"redirect_success_booking": "予約時にリダイレクト",
"you_are_being_redirected": "$t(second, {\"count\": {{seconds}} }) で {{ url }} にリダイレクトします",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "予約成功後、カスタム URL にリダイレクトします",
"redirect_url_description": "予約の成功後、カスタム URL へとリダイレクトします",
"duplicate": "複製",
"offer_seats": "座席を提供",
"offer_seats_description": "予約に座席を提供します。これにより、ゲストとオプトイン予約は自動的に無効化されます。",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "アプリは正常に削除されました",
"error_removing_app": "アプリの削除中にエラーが発生しました",
"web_conference": "ウェブ会議",
"number_for_sms_reminders": "電話番号SMS リマインダー用)",
"requires_confirmation": "確認が必要です",
"nr_event_type_one": "{{count}} 種類のイベントタイプ",
"nr_event_type_other": "{{count}} 種類のイベントタイプ",
@ -1164,8 +1163,8 @@
"connect_conference_apps": "会議アプリを接続",
"connect_calendar_apps": "カレンダーアプリを接続",
"connect_payment_apps": "決済アプリを接続",
"connect_automation_apps": "自動化アプリ接続する",
"connect_analytics_apps": "分析アプリ接続する",
"connect_automation_apps": "自動化アプリ接続する",
"connect_analytics_apps": "分析アプリ接続する",
"connect_other_apps": "その他のアプリを接続",
"current_step_of_total": "ステップ {{currentStep}}/{{maxSteps}}",
"add_variable": "変数を追加",
@ -1263,51 +1262,51 @@
"seats": "座席",
"every_app_published": "Cal.com の App Store で公開されているすべてのアプリはオープンソースであり、ピアレビューを介して入念なテストが実施されています。しかしながら、Cal.com, Inc. はこれらのアプリが Cal.com で公開されていない限りは推奨または認証を行っていません。不適切なコンテンツまたは行為を発見した場合には、ぜひご一報ください。",
"report_app": "アプリを報告",
"limit_booking_frequency": "予約頻度を制限する",
"limit_booking_frequency_description": "このイベントを予約できる回数を制限します",
"add_limit": "上限を追加",
"limit_booking_frequency": "予約の頻度に上限を設定する",
"limit_booking_frequency_description": "このイベントを予約できる回数に上限を設定します",
"add_limit": "上限を追加する",
"team_name_required": "チーム名が必要です",
"show_attendees": "ゲスト間で出席者情報を共有",
"show_attendees": "ゲスト間で出席者情報を共有する",
"how_additional_inputs_as_variables": "変数として追加の入力を使用する方法は?",
"format": "書式",
"uppercase_for_letters": "すべての文字に大文字を使用する",
"replace_whitespaces_underscores": "空白をアンダーバー「 _ 」に置き換える",
"manage_billing": "請求を管理する",
"manage_billing_description": "請求についてのあらゆることを管理する",
"manage_billing_description": "請求に関するすべての事柄を管理します",
"billing_freeplan_title": "あなたは現在、無料プランを利用しています",
"billing_freeplan_description": "チームだとより良い仕事ができます。ラウンドロビンや集合イベントでワークフローを拡張し、高度なルーティングフォームを作成しましょう",
"billing_freeplan_description": "チームで利用すれば、さらに業務効率が上がります。ラウンドロビンや共有イベントでワークフローを拡張し、高度なルーティングフォームを作成しましょう",
"billing_freeplan_cta": "今すぐ試す",
"billing_manage_details_title": "請求情報の詳細を表示・管理する",
"billing_manage_details_description": "請求情報の詳細の表示や編集、サブスクリプションのキャンセルを行います。",
"billing_portal": "請求ポータル",
"billing_help_title": "他にもお手伝いできることはありませんか?",
"billing_help_description": "請求に関してサポートが必要な場合には、サポートチームがお手伝いさせていただきます。",
"billing_help_cta": "サポートに連絡する",
"ignore_special_characters": "追加の入力ラベルに含まれている特殊文字はすべて無視し、アルファベットと数字のみを使用します",
"billing_help_cta": "サポートに問い合わせる",
"ignore_special_characters": "追加の入力ラベルに含まれている特殊文字はすべて無視し、アルファベットと数字のみを使用してください",
"retry": "再試行",
"fetching_calendars_error": "カレンダーの取得中に問題が発生しました。<1>もう一度試す</1>か、カスタマーサポートにご連絡ください。",
"fetching_calendars_error": "カレンダーの取得中にエラーが発生しました。<1>もう一度お試しいただく</1>か、カスタマーサポートまでお問い合わせください。",
"calendar_connection_fail": "カレンダーの接続に失敗しました",
"booking_confirmation_success": "予約の確認に成功しました",
"booking_confirmation_success": "正常に予約を確認しました",
"booking_confirmation_fail": "予約の確認に失敗しました",
"we_wont_show_again": "次回から表示しません",
"we_wont_show_again": "次回から表示しない",
"couldnt_update_timezone": "タイムゾーンを更新できませんでした",
"updated_timezone_to": "タイムゾーンを {{formattedCurrentTz}} に更新しました",
"update_timezone": "タイムゾーンを更新する",
"update_timezone_question": "タイムゾーンを更新しますか?",
"update_timezone_description": "あなたのローカルタイムゾーンが {{formattedCurrentTz}} に変更されたようです。希望しない時間に予約しないよう、タイムゾーンを正しく設定しておくことは非常に重要です。更新しますか?",
"update_timezone_description": "あなたのローカルタイムゾーンが {{formattedCurrentTz}} へと変更されたようです。望まない時間帯に予約が行われてしまうことがないよう、タイムゾーンを正しく設定しておくことは非常に重要です。更新しますか?",
"dont_update": "更新しない",
"require_additional_notes": "追加メモが必要です",
"require_additional_notes_description": "予約時には追加メモを記入する必要があります",
"email_address_action": "特定のメールアドレスにメールを送信",
"after_event_trigger": "イベント終了後",
"how_long_after": "イベント終了のどのくらい後に?",
"require_additional_notes": "追加メモが必要です",
"require_additional_notes_description": "予約を行う際には追加のメモを入力する必要があります",
"email_address_action": "特定のメールアドレスにメールを送信する",
"after_event_trigger": "イベント終了後",
"how_long_after": "イベント終了のどのくらい後にしますか",
"no_available_slots": "利用可能な時間帯がありません",
"time_available": "利用可能な時間帯",
"install_new_calendar_app": "新しいカレンダーアプリをインストール",
"make_phone_number_required": "イベントの予約に必要な電話番号を作成",
"dont_have_permission": "この資料へのアクセスが許可されていません。",
"saml_config": "SAML 設定",
"saml_description": "チームメンバーが ID プロバイダーを使用してログインすることを許可します",
"install_new_calendar_app": "新しいカレンダーアプリをインストールする",
"make_phone_number_required": "イベントの予約時に電話番号の入力を必須にする",
"dont_have_permission": "このリソースへのアクセスが許可されていません。",
"saml_config": "シングルサインオン",
"saml_description": "チームメンバーが ID プロバイダーを使用してログインすることを許可します",
"saml_config_deleted_successfully": "SAML の構成が正常に削除されました",
"saml_config_updated_successfully": "SAML の構成が正常に更新されました",
"saml_configuration": "SAML の構成",
@ -1318,11 +1317,11 @@
"saml_configuration_description": "SAML の構成を更新するには、以下のテキストボックスに ID プロバイダーから提供されている SAML のメタデータを貼り付けてください。",
"saml_configuration_placeholder": "ID プロバイダーから提供されている SAML のメタデータをこちらに貼り付けてください",
"saml_email_required": "SAML ID プロバイダーを見つけるために、メールアドレスを入力してください",
"saml_sp_title": "サービスプロバイダーの詳細",
"saml_sp_description": "ID プロバイダーIdPは、SAML アプリケーションの構成を完了するために、次の詳細情報を要求します。",
"saml_sp_title": "サービスプロバイダーの詳細情報",
"saml_sp_description": "ID プロバイダー (IdP) は、SAML アプリケーションの構成を完了するために以下の詳細情報を要求します。",
"saml_sp_acs_url": "ACS URL",
"saml_sp_entity_id": "SP エンティティ ID",
"saml_sp_acs_url_copied": "ACS URLをコピーしました!",
"saml_sp_acs_url_copied": "ACS URL をコピーしました!",
"saml_sp_entity_id_copied": "SP エンティティ ID をコピーしました!",
"saml_btn_configure": "設定する"
"saml_btn_configure": "構成する"
}

View File

@ -1007,7 +1007,6 @@
"app_removed_successfully": "앱이 제거됨",
"error_removing_app": "앱 제거 중 오류 발생",
"web_conference": "웹 화상 회의",
"number_for_sms_reminders": "전화번호(SMS 미리 알림)",
"requires_confirmation": "확인 필수",
"nr_event_type_one": "{{count}} 이벤트 타입",
"nr_event_type_other": "{{count}} 이벤트 타입",

View File

@ -1007,7 +1007,6 @@
"app_removed_successfully": "De app is verwijderd",
"error_removing_app": "Fout bij het verwijderen van de app",
"web_conference": "Webconferentie",
"number_for_sms_reminders": "Telefoonnummer (voor sms-herinneringen)",
"requires_confirmation": "Vereist bevestiging",
"nr_event_type_one": "{{count}} gebeurtenistype",
"nr_event_type_other": "{{count}} gebeurtenistypen",
@ -1272,18 +1271,18 @@
"format": "Formatteren",
"uppercase_for_letters": "Gebruik hoofdletters voor alle letters",
"replace_whitespaces_underscores": "Vervang spaties door onderstrepingstekens",
"manage_billing": "Facturatie beheren",
"manage_billing_description": "Beheren van alles wat met facturatie te maken heeft",
"manage_billing": "Facturering beheren",
"manage_billing_description": "Alles beheren wat met facturering te maken heeft",
"billing_freeplan_title": "U heeft momenteel het GRATIS abonnement",
"billing_freeplan_description": "We werken beter in teams. Breid uw werkstromen uit met Round Robin en collectieve gebeurtenissen en maak geavanceerde routeringsformulieren",
"billing_freeplan_cta": "Nu proberen",
"billing_manage_details_title": "Uw factureringsgegevens bekijken en beheren",
"billing_manage_details_description": "Bekijk en bewerk uw factureringsgegevens of zeg uw abonnement op.",
"billing_portal": "Facturatieportaal",
"billing_portal": "Factureringsportaal",
"billing_help_title": "Nog iets nodig?",
"billing_help_description": "Als u verder nog hulp nodig heeft bij de facturering, is ons ondersteuningsteam er om u te helpen.",
"billing_help_cta": "Contact opnemen met de ondersteuning",
"ignore_special_characters": "Negeer speciale tekens in uw label Extra invoer. Gebruik alleen letters en cijfers",
"ignore_special_characters": "Speciale tekens negeren in uw label Extra invoer. Alleen letters en cijfers gebruiken",
"retry": "Opnieuw proberen",
"fetching_calendars_error": "Er is een probleem opgetreden bij het ophalen van uw agenda's. <1>Probeer het opnieuw</1> of neem contact op met de klantenservice.",
"calendar_connection_fail": "Agendaverbinding mislukt",
@ -1305,8 +1304,8 @@
"time_available": "Tijd beschikbaar",
"install_new_calendar_app": "Nieuwe agenda-app installeren",
"make_phone_number_required": "Maak een telefoonnummer verplicht voor het boeken van een gebeurtenis",
"dont_have_permission": "U hewdt geen machtingen voor toegang tot dit hulpmiddel.",
"saml_config": "SAML-configuratie",
"dont_have_permission": "U heeft geen machting voor toegang tot dit hulpmiddel.",
"saml_config": "Eenmalige aanmelding",
"saml_description": "Teamleden toestaan in te loggen met behulp van een identiteitsprovider",
"saml_config_deleted_successfully": "SAML-configuratie verwijderd",
"saml_config_updated_successfully": "SAML-configuratie bijgewerkt",

View File

@ -779,7 +779,7 @@
"analytics": "Analityka",
"empty_installed_apps_headline": "Brak zainstalowanych aplikacji",
"empty_installed_apps_description": "Aplikacje umożliwiają znaczne usprawnienie przebiegu pracy i uproszczenie planowania.",
"empty_installed_apps_button": "Przeglądaj sklep App Store",
"empty_installed_apps_button": "Przeglądaj sklep App Store lub zainstaluj z poniższych aplikacji",
"manage_your_connected_apps": "Zarządzaj zainstalowanymi aplikacjami lub zmień ustawienia",
"browse_apps": "Przeglądaj aplikacje",
"features": "Funkcje",
@ -829,11 +829,11 @@
"time_format": "Format czasu",
"12_hour": "12 godzin",
"24_hour": "24 godzin",
"24_hour_short": "24 godz.",
"24_hour_short": "24 h",
"redirect_success_booking": "Przekieruj przy rezerwacji ",
"you_are_being_redirected": "Zostałeś przekierowany do {{ url }} w $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://example.com/przekieruj-do-moja-strona",
"redirect_url_description": "Przekieruj na niestandardowy adres URL po udanej rezerwacji",
"redirect_url_description": "Po udanej rezerwacji przekieruj na niestandardowy adres URL",
"duplicate": "Duplikat",
"offer_seats": "Zaproponuj miejsca",
"offer_seats_description": "Zaoferuj miejsca do zarezerwowania. To automatycznie wyłącza rezerwacje gości i rezerwacje wstępne.",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "Aplikacja została usunięta",
"error_removing_app": "Błąd podczas usuwania aplikacji",
"web_conference": "Konferencja internetowa",
"number_for_sms_reminders": "Numer telefonu (do przypomnień SMS)",
"requires_confirmation": "Wymaga potwierdzenia",
"nr_event_type_one": "Liczba typów wydarzenia: {{count}}",
"nr_event_type_other": "Liczba typów wydarzenia: {{count}}",
@ -1240,7 +1239,7 @@
"webhooks_description": "Otrzymuj dane ze spotkań w czasie rzeczywistym, gdy coś dzieje się w Cal.com",
"api_keys_description": "Wygeneruj klucze API umożliwiające dostęp do Twojego konta.",
"new_api_key": "Nowy klucz interfejsu API",
"active": "Aktywne",
"active": "aktywne",
"api_key_updated": "Nazwa klucza interfejsu API została zaktualizowana",
"api_key_update_failed": "Błąd podczas aktualizowania nazwy klucza interfejsu API",
"embeds_title": "Osadzanie elementów iframe HTML",
@ -1275,8 +1274,8 @@
"manage_billing": "Zarządzaj płatnościami",
"manage_billing_description": "Zarządzaj wszystkimi kwestiami dotyczącymi płatności",
"billing_freeplan_title": "Obecnie korzystasz z planu BEZPŁATNEGO",
"billing_freeplan_description": "Lepiej pracujemy zespołowo. Rozbuduj przepływy pracy o działania okrężne i wydarzenia zbiorowe, a także twórz zaawansowane formularze przekierowujące",
"billing_freeplan_cta": "Spróbuj teraz",
"billing_freeplan_description": "Lepiej pracujemy zespołowo. Rozbuduj przepływy pracy o działania cykliczne i wydarzenia zbiorowe, a także twórz zaawansowane formularze przekierowujące",
"billing_freeplan_cta": "Wypróbuj teraz",
"billing_manage_details_title": "Wyświetl swoje dane rozliczeniowe i zarządzaj nimi",
"billing_manage_details_description": "Wyświetl swoje dane rozliczeniowe i edytuj je, jak również anuluj subskrypcję.",
"billing_portal": "Portal rozliczeniowy",
@ -1301,12 +1300,12 @@
"email_address_action": "wyślij wiadomość e-mail na określony adres e-mail",
"after_event_trigger": "po zakończeniu wydarzenia",
"how_long_after": "Jak długo po zakończeniu wydarzenia?",
"no_available_slots": "Brak dostępnych miejsc",
"time_available": "Dostępne godziny",
"no_available_slots": "Brak dostępnych przedziałów",
"time_available": "Dostępny czas",
"install_new_calendar_app": "Zainstaluj nową aplikację kalendarza",
"make_phone_number_required": "Ustaw wymóg podania numeru telefonu w celu rezerwacji wydarzenia",
"dont_have_permission": "Nie masz uprawnień dostępu do tego zasobu.",
"saml_config": "Konfiguracja SAML",
"saml_config": "Pojedyncze logowanie",
"saml_description": "Zezwalaj członkom zespołu na logowanie za pomocą dostawcy tożsamości",
"saml_config_deleted_successfully": "Konfiguracja SAML usunięta pomyślnie",
"saml_config_updated_successfully": "Konfiguracja SAML została zaktualizowana",
@ -1318,11 +1317,11 @@
"saml_configuration_description": "Wklej metadane SAML od dostawcy tożsamości w polu tekstowym poniżej, aby zaktualizować konfigurację SAML.",
"saml_configuration_placeholder": "Wklej tutaj metadane SAML od dostawcy tożsamości",
"saml_email_required": "Wprowadź e-mail, abyśmy mogli znaleźć dostawcę tożsamości SAML",
"saml_sp_title": "Szczegóły dostawcy usług",
"saml_sp_title": "Szczegóły usługodawcy",
"saml_sp_description": "Twój dostawca tożsamości poprosi Cię o podanie następujących danych w celu dokończenia konfiguracji aplikacji SAML.",
"saml_sp_acs_url": "Adres URL usług kontroli dostępu",
"saml_sp_entity_id": "Identyfikator SP obiektu",
"saml_sp_acs_url_copied": "Skopiowano adres URL usług kontroli dostępu!",
"saml_sp_entity_id_copied": "Identyfikator obiektu SP skopiowany!",
"saml_sp_entity_id_copied": "Skopiowano identyfikator obiektu SP!",
"saml_btn_configure": "Konfiguruj"
}

View File

@ -1007,7 +1007,6 @@
"app_removed_successfully": "Aplicativo removido com sucesso",
"error_removing_app": "Erro ao remover o aplicativo",
"web_conference": "Conferência via web",
"number_for_sms_reminders": "Número de telefone (para lembretes de SMS)",
"requires_confirmation": "Requer confirmação",
"nr_event_type_one": "{{count}} tipo de evento",
"nr_event_type_other": "{{count}} tipos de evento",

View File

@ -767,19 +767,19 @@
"no_category_apps_description_calendar": "Adicione uma aplicação de calendário para verificar se existem conflitos e assim evitar sobreposição de marcações",
"no_category_apps_description_conferencing": "Experimente adicionar uma aplicação de conferência para integrar chamadas de vídeo com os seus clientes",
"no_category_apps_description_payment": "Adicione uma aplicação de pagamentos para facilitar a transação entre si e os seus clientes",
"no_category_apps_description_analytics": "Adicionar uma aplicação de análise para as suas páginas de reservas",
"no_category_apps_description_analytics": "Adicionar uma aplicação de análise às suas páginas de reservas",
"no_category_apps_description_automation": "Adicionar uma aplicação de automatização a utilizar",
"no_category_apps_description_other": "Adicione qualquer outro tipo de aplicações para fazer todos os tipos de coisas",
"installed_app_calendar_description": "Defina o(s) calendário(s) para verificar se existem conflitos e assim evitar marcações sobrepostas.",
"installed_app_conferencing_description": "Adicione as aplicações de videoconferência que prefere para as suas reuniões",
"installed_app_payment_description": "Configure os serviços de processamento de pagamentos que serão utilizados nas cobranças aos seus clientes.",
"installed_app_analytics_description": "Configurar quais as aplicações de análise a serem utilizadas nas suas páginas de reservas",
"installed_app_analytics_description": "Configurar as aplicações de análise a utilizar nas suas páginas de reservas",
"installed_app_other_description": "Todas as aplicações instaladas de outras categorias.",
"installed_app_automation_description": "Configurar quais as aplicações de automatização a utilizar",
"analytics": "Análise",
"installed_app_automation_description": "Configurar as aplicações de automatização a utilizar",
"analytics": "Estatísticas",
"empty_installed_apps_headline": "Nenhuma aplicação instalada",
"empty_installed_apps_description": "As aplicações permitem-lhe melhorar o seu fluxo de trabalho e os seus agendamentos, significativamente.",
"empty_installed_apps_button": "Explore a aplicação",
"empty_installed_apps_button": "Explore a App Store ou instale a partir das apps abaixo",
"manage_your_connected_apps": "Faça a gestão das suas aplicações instaladas ou altere as definições",
"browse_apps": "Pesquisar aplicações",
"features": "Funcionalidades",
@ -833,7 +833,7 @@
"redirect_success_booking": "Redireccionar ao reservar ",
"you_are_being_redirected": "Está a ser redireccionado para {{ url }} em $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://exemplo.com/redireccionar-para-pagina-de-sucesso",
"redirect_url_description": "Redirecionar para um endereço personalizado após uma reserva ser concluída com sucesso",
"redirect_url_description": "Redirecionar para um endereço personalizado após uma reserva concluída com sucesso",
"duplicate": "Duplicar",
"offer_seats": "Oferecer lugares",
"offer_seats_description": "Oferecer lugares para reservas. Isto desativa automaticamente os convidados e confirmações nas reservas.",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "Aplicação removida com sucesso",
"error_removing_app": "Erro ao remover a aplicação",
"web_conference": "Conferência web",
"number_for_sms_reminders": "Número de telefone (para lembretes SMS)",
"requires_confirmation": "Requer confirmação",
"nr_event_type_one": "{{count}} tipo de evento",
"nr_event_type_other": "{{count}} tipos de evento",
@ -1165,7 +1164,7 @@
"connect_calendar_apps": "Associe aplicações de calendário",
"connect_payment_apps": "Associe aplicações de pagamento",
"connect_automation_apps": "Associar aplicações de automatização",
"connect_analytics_apps": "Associar aplicações de análise",
"connect_analytics_apps": "Associar aplicações de estatística",
"connect_other_apps": "Associe outras aplicações",
"current_step_of_total": "Passo {{currentStep}} de {{maxSteps}}",
"add_variable": "Adicionar variável",
@ -1296,16 +1295,16 @@
"update_timezone_question": "Atualizar fuso horário?",
"update_timezone_description": "Parece que o seu fuso horário local mudou para {{formattedCurrentTz}}. É muito importante ter o fuso horário correto para evitar reservas em horários indesejados. Gostaria de atualizar o seu fuso horário?",
"dont_update": "Não atualizar",
"require_additional_notes": "Requerer notas adicionais",
"require_additional_notes_description": "Requerer que as notas adicionais sejam preenchidas na reserva",
"require_additional_notes": "Exigir notas adicionais",
"require_additional_notes_description": "Exigir que as notas adicionais sejam preenchidas na reserva",
"email_address_action": "enviar e-mail para um endereço de e-mail específico",
"after_event_trigger": "depois do fim do evento",
"how_long_after": "Quanto tempo após o fim do evento?",
"no_available_slots": "Sem vagas disponíveis",
"time_available": "Tempo disponível",
"install_new_calendar_app": "Instalar a nova aplicação de calendário",
"make_phone_number_required": "Tornar o número de telefone obrigatório para a reserva de um evento",
"dont_have_permission": "Não tem permissão para aceder a este recurso.",
"make_phone_number_required": "Tornar o número de telefone obrigatório na reserva de um evento",
"dont_have_permission": "Não tem permissão para aceder a esta funcionalidade.",
"saml_config": "Configuração SAML",
"saml_description": "Permitir que os membros da equipa iniciem sessão com recurso a um Fornecedor de identidade",
"saml_config_deleted_successfully": "Configuração de SAML eliminada com sucesso",

View File

@ -779,7 +779,7 @@
"analytics": "Analiză",
"empty_installed_apps_headline": "Nicio aplicație instalată",
"empty_installed_apps_description": "Aplicațiile vă permit să vă îmbunătățiți în mod semnificativ fluxul de lucru și activitățile legate de programări.",
"empty_installed_apps_button": "Explorați App Store",
"empty_installed_apps_button": "Accesați App Store sau instalați o aplicație de mai jos",
"manage_your_connected_apps": "Gestionați aplicațiile instalate sau modificați setările",
"browse_apps": "Răsfoiți aplicațiile",
"features": "Caracteristici",
@ -829,7 +829,7 @@
"time_format": "Format oră",
"12_hour": "12 ore",
"24_hour": "24 de ore",
"24_hour_short": "24h",
"24_hour_short": "24 h",
"redirect_success_booking": "Redirecționare la rezervare ",
"you_are_being_redirected": "Sunteți redirecționat către {{ url }} în $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "Aplicație ștearsă cu succes",
"error_removing_app": "Eroare la ștergerea aplicației",
"web_conference": "Conferință web",
"number_for_sms_reminders": "Număr de telefon (pentru mementouri SMS)",
"requires_confirmation": "Necesită confirmare",
"nr_event_type_one": "{{count}} tip de eveniment",
"nr_event_type_other": "{{count}} (de) tipuri de evenimente",
@ -1164,8 +1163,8 @@
"connect_conference_apps": "Conectați aplicațiile de conferință",
"connect_calendar_apps": "Conectați aplicațiile calendar",
"connect_payment_apps": "Conectați aplicațiile de plată",
"connect_automation_apps": "Conectează aplicații automate",
"connect_analytics_apps": "Conectează aplicații pentru analiză",
"connect_automation_apps": "Conectați aplicații de automatizare",
"connect_analytics_apps": "Conectați aplicații de analiză",
"connect_other_apps": "Conectați alte aplicații",
"current_step_of_total": "Pasul {{currentStep}} din {{maxSteps}}",
"add_variable": "Adăugare variabilă",
@ -1240,7 +1239,7 @@
"webhooks_description": "Primiți datele ședințelor în timp real atunci când se întâmplă ceva pe Cal.com",
"api_keys_description": "Generați chei API pentru accesarea propriului cont",
"new_api_key": "Cheie API nouă",
"active": "Activ",
"active": "activ",
"api_key_updated": "Denumire actualizată a cheii API",
"api_key_update_failed": "Eroare la actualizarea denumirii cheii API",
"embeds_title": "Încorporare iframe HTML",
@ -1264,18 +1263,18 @@
"every_app_published": "Fiecare aplicație publicată în magazinul de aplicații Cal.com este open-source și testată temeinic prin evaluări inter pares. Cu toate acestea, Cal.com, Inc. nu susține sau certifică aceste aplicații decât dacă sunt publicate de Cal.com. Dacă descoperiți conținut sau comportament necorespunzător, vă încurajăm să îl raportați.",
"report_app": "Raportați aplicația",
"limit_booking_frequency": "Limitați frecvența de rezervare",
"limit_booking_frequency_description": "Limitați de câte ori poate fi rezervat acest eveniment",
"limit_booking_frequency_description": "Limitați numărul de rezervări posibile pentru acest eveniment",
"add_limit": "Adăugați o limită",
"team_name_required": "Este necesar numele echipei",
"show_attendees": "Partajați informațiile despre participanți între invitați",
"show_attendees": "Distribuiți informațiile despre participanți între invitați",
"how_additional_inputs_as_variables": "Cum se utilizează intrările suplimentare ca variabile",
"format": "Format",
"uppercase_for_letters": "Utilizați majuscule pentru toate literele",
"replace_whitespaces_underscores": "Înlocuiți spațiile cu caractere de subliniere",
"manage_billing": "Gestionați facturarea",
"manage_billing_description": "Gestionați toate aspectele legate de facturare",
"billing_freeplan_title": "Momentan utilizați planul GRATUIT",
"billing_freeplan_description": "Lucrăm mai bine în echipe. Extindeți-vă fluxurile de lucru cu evenimente colective și cu alocare prin rotație și realizați formulare de rutare avansate",
"billing_freeplan_title": "Utilizați planul GRATUIT în prezent",
"billing_freeplan_description": "Lucrăm mai bine în echipe. Extindeți-vă fluxurile de lucru cu evenimente colective și cu alocare prin rotație și realizați formulare de direcționare avansate",
"billing_freeplan_cta": "Încercați acum",
"billing_manage_details_title": "Vizualizați și gestionați detaliile de facturare",
"billing_manage_details_description": "Vizualizați și modificați detaliile de facturare. Ca alternativă, vă puteți anula abonamentul.",
@ -1283,33 +1282,33 @@
"billing_help_title": "Aveți nevoie de altceva?",
"billing_help_description": "Dacă aveți nevoie de ajutor suplimentar pentru facturare, echipa noastră de asistență este aici pentru a vă ajuta.",
"billing_help_cta": "Contactați serviciul de asistență",
"ignore_special_characters": "Ignorați caracterele speciale din eticheta Intrări suplimentare. Utilizați doar litere și cifre",
"ignore_special_characters": "Ignorați caracterele speciale pe eticheta pentru intrări suplimentare. Utilizați doar litere și cifre",
"retry": "Reîncercați",
"fetching_calendars_error": "A survenit o problemă la obținerea calendarelor dvs. Vă rugăm să <1>reîncercați</1> sau să contactați serviciul de asistență.",
"fetching_calendars_error": "A survenit o problemă legată de recuperarea calendarelor dvs. <1>Încercați din nou</1> sau contactați serviciul de asistență pentru clienți.",
"calendar_connection_fail": "Conexiune calendar nereușită",
"booking_confirmation_success": "Confirmare rezervare reușită",
"booking_confirmation_fail": "Confirmare rezervare nereușită",
"we_wont_show_again": "Nu vom mai arăta acest mesaj",
"we_wont_show_again": "Nu vom mai afișa acest mesaj",
"couldnt_update_timezone": "Nu am putut actualiza fusul orar",
"updated_timezone_to": "Fusul orar a fost actualizat la {{formattedCurrentTz}}",
"update_timezone": "Actualizare fus orar",
"update_timezone_question": "Actualizați fusul orar?",
"update_timezone_description": "Se pare că fusul dvs. orar local a fost schimbat la {{formattedCurrentTz}}. Este foarte important să aveți fusul orar corect pentru a preveni rezervările efectuate la ore nedorite. Doriți să îl actualizați?",
"update_timezone_description": "Se pare că fusul dvs. orar local a fost modificat la {{formattedCurrentTz}}. Este foarte important să aveți fusul orar corect, pentru a evita rezervările la ore nedorite. Doriți să îl actualizați?",
"dont_update": "Nu actualizați",
"require_additional_notes": "Solicitați note suplimentare",
"require_additional_notes_description": "Solicitați completarea unor note suplimentare la rezervare",
"require_additional_notes": "Solicitați observații suplimentare",
"require_additional_notes_description": "Solicitați completarea unor observații suplimentare la momentul rezervării",
"email_address_action": "trimiteți e-mail către o anumită adresă de e-mail",
"after_event_trigger": "după finalul evenimentului",
"how_long_after": "La cât timp după finalul evenimentului?",
"no_available_slots": "Niciun interval disponibil",
"time_available": "Oră disponibilă",
"install_new_calendar_app": "Instalați aplicația nouă pentru calendar",
"install_new_calendar_app": "Instalați noua aplicație pentru calendar",
"make_phone_number_required": "Setați numărul de telefon ca fiind obligatoriu pentru rezervarea evenimentului",
"dont_have_permission": "Nu aveți permisiunea să accesați această resursă.",
"saml_config": "Configurare SAML",
"dont_have_permission": "Nu aveți permisiunea de a accesa această resursă.",
"saml_config": "Single Sign-On",
"saml_description": "Permiteți membrilor echipei să se conecteze utilizând un furnizor de identitate",
"saml_config_deleted_successfully": "Configurația SAML a fost ștearsă cu succes",
"saml_config_updated_successfully": "Configurație SAML actualizată cu succes",
"saml_config_updated_successfully": "Configurația SAML a fost actualizată cu succes",
"saml_configuration": "Configurație SAML",
"delete_saml_configuration": "Ștergeți configurația SAML",
"delete_saml_configuration_confirmation_message": "Sigur doriți să ștergeți configurația SAML? Membrii echipei dvs. care utilizează conectarea prin SAML nu vor mai putea accesa Cal.com.",
@ -1322,7 +1321,7 @@
"saml_sp_description": "Furnizorul dvs. de identitate (IdP) vă va solicita următoarele detalii pentru a finaliza configurarea aplicației SAML.",
"saml_sp_acs_url": "URL ACS",
"saml_sp_entity_id": "ID entitate SP",
"saml_sp_acs_url_copied": "URL ACS copiat!",
"saml_sp_entity_id_copied": "SP Entitate ID copiat!",
"saml_sp_acs_url_copied": "URL-ul ACS a fost copiat!",
"saml_sp_entity_id_copied": "ID-ul entității SP a fost copiat!",
"saml_btn_configure": "Configurare"
}

View File

@ -313,6 +313,7 @@
"past_bookings": "Здесь появятся ваши прошлые бронирования.",
"cancelled_bookings": "Здесь появятся ваши отменённые бронирования.",
"unconfirmed_bookings": "Здесь можно увидеть бронирования, которые вы еще не подтвердили.",
"unconfirmed_bookings_tooltip": "Неподтвержденные бронирования",
"on": "на",
"and": "и",
"calendar_shows_busy_between": "Ваш календарь показывает, что вы заняты между",
@ -766,18 +767,19 @@
"no_category_apps_description_calendar": "Добавьте приложение календаря, чтобы проверять наличие конфликтов для избежания двойного бронирования",
"no_category_apps_description_conferencing": "Попробуйте добавить приложение конференц-связи для интеграции видеозвонков с клиентами",
"no_category_apps_description_payment": "Добавьте платежное приложение, чтобы упростить транзакции с клиентами",
"no_category_apps_description_analytics": "Добавить на страницы бронирования аналитическое приложение",
"no_category_apps_description_automation": "Добавить приложение для автоматизации",
"no_category_apps_description_analytics": "Добавьте приложение аналитики для использования на страницах бронирования",
"no_category_apps_description_automation": "Добавьте приложение для автоматизации",
"no_category_apps_description_other": "Добавляйте всевозможные приложения для решения своих задач",
"installed_app_calendar_description": "Настройте проверку календарей на предмет конфликтов для избежания двойного бронирования.",
"installed_app_conferencing_description": "Добавьте приложения для видеоконференций, используемые для проведения онлайн-встреч",
"installed_app_payment_description": "Выберите службы обработки платежей для получения оплаты от клиентов.",
"installed_app_analytics_description": "Выбрать аналитические приложения для страниц бронирования",
"installed_app_analytics_description": "Выбор приложений аналитики для страниц бронирования",
"installed_app_other_description": "Все установленные приложения из других категорий.",
"installed_app_automation_description": "Выбрать приложения для автоматизации",
"installed_app_automation_description": "Выбор приложений для автоматизации",
"analytics": "Аналитика",
"empty_installed_apps_headline": "Нет установленных приложений",
"empty_installed_apps_description": "Приложения позволяют значительно улучшить рабочий процесс и жить по расписанию.",
"empty_installed_apps_button": "Перейти в App Store",
"empty_installed_apps_button": "Перейти в App Store или установить одно из приложений ниже",
"manage_your_connected_apps": "Управление установленными приложениями и изменение настроек",
"browse_apps": "Обзор приложений",
"features": "Возможности",
@ -827,11 +829,11 @@
"time_format": "Формат времени",
"12_hour": "12-часовой",
"24_hour": "24-часовой",
"24_hour_short": "24 часа",
"24_hour_short": "24ч",
"redirect_success_booking": "Перенаправление при бронировании ",
"you_are_being_redirected": "Перенаправление на {{ url }} произойдет через $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "Перенаправить на пользовательский URL после успешного оформления бронирования",
"redirect_url_description": "Перенаправить на пользовательский URL-адрес после успешного оформления бронирования",
"duplicate": "Создать копию",
"offer_seats": "Предложить места",
"offer_seats_description": "Предлагать места при бронировании (данная настройка автоматически отключает гостевой режим и бронирование с подтверждением).",
@ -1005,7 +1007,6 @@
"app_removed_successfully": "Приложение успешно удалено",
"error_removing_app": "Ошибка при удалении приложения",
"web_conference": "Веб-конференция",
"number_for_sms_reminders": "Номер телефона (для SMS-напоминаний)",
"requires_confirmation": "Требуется подтверждение",
"nr_event_type_one": "Типы событий: {{count}}",
"nr_event_type_other": "Типы событий: {{count}}",
@ -1163,7 +1164,7 @@
"connect_calendar_apps": "Подключить приложения календаря",
"connect_payment_apps": "Подключить платежные приложения",
"connect_automation_apps": "Подключить приложения для автоматизации",
"connect_analytics_apps": "Подключить аналитические приложения",
"connect_analytics_apps": "Подключить приложения аналитики",
"connect_other_apps": "Подключить другие приложения",
"current_step_of_total": "Шаг {{currentStep}} из {{maxSteps}}",
"add_variable": "Добавить переменную",
@ -1248,6 +1249,7 @@
"back_to_signin": "Вернуться на страницу входа",
"reset_link_sent": "Ссылка для сброса отправлена",
"password_reset_email": "В ближайшее время вы получите на {{email}} письмо с инструкциями по сбросу пароля.",
"password_reset_leading": "Если вы не получите письмо в ближайшее время, проверьте, верно ли введен адрес электронной почты, не попало ли письмо в спам и напишите в поддержку, если письмо так и не нашлось.",
"password_updated": "Пароль обновлен.",
"pending_payment": "Ожидает оплаты",
"confirmation_page_rainbow": "Используйте токены или NFT в Ethereum, Polygon и других сетях для ограничения доступа к мероприятиям.",
@ -1263,24 +1265,47 @@
"limit_booking_frequency": "Ограничить периодичность бронирования",
"limit_booking_frequency_description": "Ограничить количество бронирований этого события",
"add_limit": "Добавить ограничение",
"show_attendees": "Предоставить гостям информацию об участнике",
"team_name_required": "Требуется название команды",
"show_attendees": "Предоставлять гостям информацию об участниках",
"how_additional_inputs_as_variables": "Как использовать дополнительные данные в качестве переменных",
"format": "Формат",
"uppercase_for_letters": "Использовать только прописные буквы",
"replace_whitespaces_underscores": "Заменить пробелы подчеркиванием",
"manage_billing": "Управление расчетами",
"manage_billing_description": "Управление всеми расчетами",
"billing_freeplan_description": "В командах работается лучше. Добавьте в рабочие процессы циклические и коллективные события и создайте расширенные формы маршрутизации",
"manage_billing": "Управление биллингом",
"manage_billing_description": "Управление биллингом",
"billing_freeplan_title": "В настоящее время вы используете бесплатный тарифный план",
"billing_freeplan_description": "В командах работается лучше. Расширяйте свои рабочие процессы при помощи циклического перебора и коллективных событий, а также создавайте расширенные формы маршрутизации",
"billing_freeplan_cta": "Попробовать",
"billing_manage_details_title": "Просмотр и управление платежными данными",
"billing_manage_details_description": "Просмотр и редактирование платежных реквизитов, а также отмена подписки.",
"billing_portal": "Портал биллинга",
"billing_help_title": "Нужно что-нибудь еще?",
"billing_help_description": "Если вам нужна дальнейшая помощь с оплатой счета, наша служба поддержки готова помочь.",
"ignore_special_characters": "Игнорируйте специальные символы в метке дополнительной информации. Используйте только буквы и цифры",
"billing_help_cta": "Обратиться в службу поддержки",
"ignore_special_characters": "Не используйте специальные символы в метке дополнительного поля ввода. Используйте только буквы и цифры",
"retry": "Повторить",
"fetching_calendars_error": "Ошибка при загрузке календарей. <1>Попробуйте еще раз</1> или обратитесь в службу поддержки.",
"calendar_connection_fail": "Не удалось подключиться к календарю",
"booking_confirmation_success": "Бронирование подтверждено",
"booking_confirmation_fail": "Не удалось подтвердить бронирование",
"we_wont_show_again": "Мы больше не будем показывать эту информацию",
"couldnt_update_timezone": "Не удалось обновить часовой пояс",
"updated_timezone_to": "Установлен часовой пояс: {{formattedCurrentTz}}",
"update_timezone": "Обновить часовой пояс",
"require_additional_notes": "Запросить дополнительные примечания",
"no_available_slots": "Нет доступных слотов",
"update_timezone_question": "Обновить часовой пояс?",
"update_timezone_description": "Ваш часовой пояс изменился на {{formattedCurrentTz}}. Очень важно, чтобы часовой пояс был установлен правильно: это позволит избежать бронирования в нежелательное время. Обновить часовой пояс?",
"dont_update": "Не обновлять",
"require_additional_notes": "Требовать указания дополнительной информации",
"require_additional_notes_description": "Требовать указания дополнительной информации при бронировании",
"email_address_action": "отправить письмо на указанный адрес электронной почты",
"after_event_trigger": "после окончания события",
"how_long_after": "Спустя какое время после окончания события?",
"no_available_slots": "Нет доступных интервалов",
"time_available": "Доступное время",
"install_new_calendar_app": "Установить новое приложение календаря",
"make_phone_number_required": "Сделать номер телефона обязательным для бронирования",
"saml_config": "Конфигурация SAML",
"dont_have_permission": "У вас нет разрешения на доступ к этому ресурсу.",
"saml_config": "Единый вход",
"saml_description": "Разрешить членам команды вход с помощью поставщика удостоверений",
"saml_config_deleted_successfully": "Конфигурация SAML удалена",
"saml_config_updated_successfully": "Конфигурация SAML успешно обновлена",
@ -1292,5 +1317,11 @@
"saml_configuration_description": "Пожалуйста, вставьте метаданные SAML из вашего провайдера в текстовое поле ниже, чтобы обновить конфигурацию SAML.",
"saml_configuration_placeholder": "Пожалуйста, вставьте здесь метаданные SAML от вашего провайдера",
"saml_email_required": "Пожалуйста, введите адрес электронной почты, чтобы мы могли найти ваш SAML идентификатор провайдера",
"saml_sp_acs_url": "ACS URL"
"saml_sp_title": "Детали поставщика услуг",
"saml_sp_description": "Для того чтобы завершить настройку SAML-приложения, ваш поставщик удостоверений (IdP) запросит у вас следующую информацию.",
"saml_sp_acs_url": "URL-адрес ACS",
"saml_sp_entity_id": "Идентификатор сущности SP",
"saml_sp_acs_url_copied": "URL-адрес ACS скопирован.",
"saml_sp_entity_id_copied": "Идентификатор сущности SP скопирован.",
"saml_btn_configure": "Настроить"
}

View File

@ -780,7 +780,7 @@
"analytics": "Analitika",
"empty_installed_apps_headline": "Aplikacije nisu instalirane",
"empty_installed_apps_description": "Aplikacije omogućavaju da unapredite svoj rad i značajno poboljšate svoja zakazivanja.",
"empty_installed_apps_button": "Istražite App Store",
"empty_installed_apps_button": "Istražite App Store ili instalirajte neku od aplikacija u nastavku",
"manage_your_connected_apps": "Upravljajte instaliranim aplikacijama ili promenite postavke",
"browse_apps": "Pregledajte aplikacije",
"features": "Funkcije",
@ -834,7 +834,7 @@
"redirect_success_booking": "Preusmeri na rezervaciju ",
"you_are_being_redirected": "Preusmeravamo vas na {{ url }} za $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "Preusmeri na prilagođenu URL stranicu nakon uspešne rezervacije",
"redirect_url_description": "Preusmeri na prilagođenu URL adresu nakon uspešne rezervacije",
"duplicate": "Dupliraj",
"offer_seats": "Ponudi mesta",
"offer_seats_description": "Ponudite mesta za zakazivanje. Ovo automatski onemogućava gostujuća i opciona zakazivanja.",
@ -1008,7 +1008,6 @@
"app_removed_successfully": "Aplikacija je uspešno uklonjena",
"error_removing_app": "Greška prilikom uklanjanja aplikacije",
"web_conference": "Veb konferencija",
"number_for_sms_reminders": "Broj telefona (za SMS podsetnike)",
"requires_confirmation": "Potrebna je potvrda",
"nr_event_type_one": "{{count}} tip događaja",
"nr_event_type_other": "{{count}} tipova događaja",
@ -1241,7 +1240,7 @@
"webhooks_description": "Primajte podatke o sastanku u realnom vremenu kada se nešto dogodi na Cal.com",
"api_keys_description": "Generišite API ključeve za pristupanje svom nalogu",
"new_api_key": "Novi API ključ",
"active": "Aktivan",
"active": "aktivno",
"api_key_updated": "Ime API ključa je ažurirano",
"api_key_update_failed": "Greška pri ažuriranju imena API ključa",
"embeds_title": "HTML iframe ugradnja",
@ -1284,7 +1283,7 @@
"billing_help_title": "Treba li vam još nešto?",
"billing_help_description": "Ako vam treba pomoć sa naplatom, naš tim podrške je tu da pomogne.",
"billing_help_cta": "Obratite se podršci",
"ignore_special_characters": "Ignorišite specijalne karaktere u vašim oznakama dodatnih unosa. Koristite samo slova i brojeve",
"ignore_special_characters": "Ignorisati specijalne znakove u vašim oznakama Dodatnih unosa. Koristiti samo slova i brojeve",
"retry": "Pokušajte ponovo",
"fetching_calendars_error": "Došlo je do problema prilikom dobavljanja vaših kalendara. <1>Pokušajte ponovo</1> ili se obratite korisničkoj podršci.",
"calendar_connection_fail": "Povezivanje sa kalendarom nije uspelo",
@ -1294,21 +1293,21 @@
"couldnt_update_timezone": "Nismo uspeli da ažuriramo vremensku zonu",
"updated_timezone_to": "Vremenska zona je ažurirana na {{formattedCurrentTz}}",
"update_timezone": "Ažurirajte vremensku zonu",
"update_timezone_question": "Ažurirajte vremensku zonu?",
"update_timezone_question": "Ažurirati vremensku zonu?",
"update_timezone_description": "Izgleda da je vaša lokalna vremenska zona promenjena na {{formattedCurrentTz}}. Jako je važno da imate ispravnu vremensku zonu da biste sprečili rezervacije u neželjeno vreme. Da li želite da je ažurirate?",
"dont_update": "Nemoj da ažuriraš",
"dont_update": "Nemoj ažurirati",
"require_additional_notes": "Zahtevanje dodatnih napomena",
"require_additional_notes_description": "Zahtevajte da se popune dodatne napomene pre rezervisanja",
"email_address_action": "pošalji imejl na određene imejl adrese",
"after_event_trigger": "nakon što se događaj završi",
"how_long_after": "Koliko vremena nakon što se događaj završi?",
"no_available_slots": "Nema dostupnih mesta",
"no_available_slots": "Nema dostupnih termina",
"time_available": "Dostupno vreme",
"install_new_calendar_app": "Instalirajte novu aplikaciju kalendara",
"make_phone_number_required": "Postavite da je neophodan telefonski broj za rezervaciju događaja",
"dont_have_permission": "Nemate dozvolu da pristupite ovom resursu.",
"saml_config": "SAML konfiguracija",
"saml_description": "Dozvolite članu tima da se prijavi kao Identity Provider",
"saml_config": "Pojedinačno prijavljivanje",
"saml_description": "Dozvoliti članovima tima da se prijavljuju pomoću dobavljača identiteta",
"saml_config_deleted_successfully": "SAML konfiguracija uspešno izbrisana",
"saml_config_updated_successfully": "SAML konfiguracija je uspešno ažurirana",
"saml_configuration": "SAML konfiguracija",
@ -1320,11 +1319,11 @@
"saml_configuration_placeholder": "Molimo vas, nalepite SAML metadatu iz vašeg Identity Provider-a ovde",
"saml_email_required": "Molimo vas, unesite vašu e-poštu da bismo našli vaš SAML Identity Provider",
"saml_sp_title": "Informacije o pružaocu usluga",
"saml_sp_description": "Vaš Identity Provider (IdP) će vam tražiti dodatne informacije da biste završili konfigurisanje SAML prijave.",
"saml_sp_description": "Vaš Dobavljač identiteta (IdP) će od vas tražiti sledeće informacije da biste dovršili konfiguraciju SAML aplikacije.",
"saml_sp_acs_url": "ACS URL",
"saml_sp_entity_id": "SP entitet ID",
"saml_sp_entity_id": "ID SP entiteta",
"saml_sp_acs_url_copied": "ACS URL je kopiran!",
"saml_sp_entity_id_copied": "SP entitet ID je kopiran!",
"saml_btn_configure": "Konfiguracija",
"saml_sp_entity_id_copied": "ID SP entiteta je kopiran!",
"saml_btn_configure": "Konfiguriši",
"add_calendar": "Dodajte kalendar"
}

View File

@ -779,7 +779,7 @@
"analytics": "Analys",
"empty_installed_apps_headline": "Inga appar installerade",
"empty_installed_apps_description": "Med appar kan du förbättra ditt arbetsflöde och din schemaläggning avsevärt.",
"empty_installed_apps_button": "Utforska App Store",
"empty_installed_apps_button": "Utforska App Store eller installera från apparna nedan",
"manage_your_connected_apps": "Hantera dina installerade appar eller ändra inställningar",
"browse_apps": "Bläddra bland appar",
"features": "Egenskaper",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "Appen har tagits bort",
"error_removing_app": "Det gick inte att ta bort app",
"web_conference": "Webbkonferens",
"number_for_sms_reminders": "Telefonnummer (för SMS-påminnelser)",
"requires_confirmation": "Kräver bekräftelse",
"nr_event_type_one": "{{count}} händelsetyp",
"nr_event_type_other": "{{count}} händelsetyper",
@ -1240,7 +1239,7 @@
"webhooks_description": "Få mötesdata i realtid när något händer i Cal.com",
"api_keys_description": "Generera API-nycklar för att komma åt ditt eget konto",
"new_api_key": "Ny API-nyckel",
"active": "Aktiv",
"active": "aktiv",
"api_key_updated": "API-nyckelnamn uppdaterat",
"api_key_update_failed": "Fel vid uppdatering av API-nyckelnamn",
"embeds_title": "HTML iframe-inbäddning",
@ -1274,8 +1273,8 @@
"replace_whitespaces_underscores": "Ersätt blanktecken med understreck",
"manage_billing": "Hantera fakturering",
"manage_billing_description": "Hantera allt som faktureras",
"billing_freeplan_title": "Du har för närvarande GRATIS-prenumerationen",
"billing_freeplan_description": "Vi arbetar bättre i team. Utöka dina arbetsflöden med round-robin och kollektiva händelser och skapa avancerade routing-formulär",
"billing_freeplan_title": "Du använder för närvarande GRATIS-prenumerationen",
"billing_freeplan_description": "Vi arbetar bättre i team. Utöka dina arbetsflöden med round-robin och kollektiva händelser och skapa avancerade routingformulär",
"billing_freeplan_cta": "Testa nu",
"billing_manage_details_title": "Visa och hantera dina faktureringsuppgifter",
"billing_manage_details_description": "Visa och redigera dina faktureringsuppgifter samt avsluta din prenumeration.",
@ -1283,10 +1282,10 @@
"billing_help_title": "Behöver du något annat?",
"billing_help_description": "Om du behöver mer hjälp med fakturering finns vårt supportteam här för att hjälpa till.",
"billing_help_cta": "Kontakta support",
"ignore_special_characters": "Ignorera specialtecken i din Ytterligare Inmatningsetikett. Använd endast bokstäver och siffror",
"ignore_special_characters": "Ignorera specialtecken för etiketten Ytterligare indata. Använd endast bokstäver och siffror",
"retry": "Försök igen",
"fetching_calendars_error": "Det var ett problem med att hämta dina kalendrar. <1>Försök igen</1> eller kontakta kundtjänst.",
"calendar_connection_fail": "Kalenderanslutningen misslyckades",
"fetching_calendars_error": "Det gick inte att hämta dina kalendrar. <1>Försök igen</1> eller kontakta kundtjänst.",
"calendar_connection_fail": "Det gick inte att ansluta kalender",
"booking_confirmation_success": "Bokningsbekräftelsen lyckades",
"booking_confirmation_fail": "Bokningsbekräftelsen misslyckades",
"we_wont_show_again": "Vi kommer inte att visa detta igen",
@ -1294,7 +1293,7 @@
"updated_timezone_to": "Tidszon uppdaterad till {{formattedCurrentTz}}",
"update_timezone": "Uppdatera tidszon",
"update_timezone_question": "Uppdatera tidszon?",
"update_timezone_description": "Det verkar som om din lokala tidszon har ändrats till {{formattedCurrentTz}}. Det är mycket viktigt att ha rätt tidszon för att förhindra bokningar vid oönskade tider. Vill du uppdatera den?",
"update_timezone_description": "Det verkar som om din lokala tidszon har ändrats till {{formattedCurrentTz}}. Det är viktigt att ha rätt tidszon för att förhindra bokningar vid oönskade tider. Vill du uppdatera den?",
"dont_update": "Uppdatera inte",
"require_additional_notes": "Kräv ytterligare anteckningar",
"require_additional_notes_description": "Kräv ytterligare anteckningar som ska fyllas i vid bokning",
@ -1304,9 +1303,9 @@
"no_available_slots": "Inga tillgängliga tider",
"time_available": "Tid tillgänglig",
"install_new_calendar_app": "Installera ny kalenderapp",
"make_phone_number_required": "Gör telefonnummer krävs för att boka händelse",
"make_phone_number_required": "Gör telefonnummer obligatoriskt för att boka händelse",
"dont_have_permission": "Du har inte behörighet att komma åt denna resurs.",
"saml_config": "SAML-konfiguration",
"saml_config": "Enkel inloggning",
"saml_description": "Tillåt teammedlemmar att logga in med en identitetsleverantör",
"saml_config_deleted_successfully": "SAML-konfigurationen har tagits bort",
"saml_config_updated_successfully": "SAML-konfigurationen har uppdaterats",
@ -1318,11 +1317,11 @@
"saml_configuration_description": "Klistra in SAML-metadata från din identitetsleverantör i textrutan nedan för att uppdatera din SAML-konfiguration.",
"saml_configuration_placeholder": "Klistra in SAML-metadata från din identitetsleverantör här",
"saml_email_required": "Ange en e-postadress så att vi kan hitta din SAML identitetsleverantör",
"saml_sp_title": "Tjänsteleverantörsuppgifter",
"saml_sp_description": "Din identitetsleverantör (IdP) kommer att be dig om följande uppgifter för att slutföra SAML-applikationskonfigurationen.",
"saml_sp_title": "Information om tjänstleverantör",
"saml_sp_description": "Din identitetsleverantör (IdP) kommer att be dig om följande uppgifter för att slutföra SAML-programkonfigurationen.",
"saml_sp_acs_url": "ACS-URL",
"saml_sp_entity_id": "SP enhets-ID",
"saml_sp_acs_url_copied": "ACS-URL kopierad!",
"saml_sp_entity_id_copied": "SP enhets-ID kopierat!",
"saml_sp_entity_id": "ID för SP-enhet",
"saml_sp_acs_url_copied": "ACS-URL har kopierats!",
"saml_sp_entity_id_copied": "ID för SP-enhet har kopierats!",
"saml_btn_configure": "Konfigurera"
}

View File

@ -776,11 +776,11 @@
"installed_app_payment_description": "Müşterilerinizden ücret alırken hangi ödeme işleme hizmetlerinin kullanılacağını yapılandırın.",
"installed_app_analytics_description": "Rezervasyon sayfalarınız için hangi analiz uygulamalarını kullanacağınızı yapılandırın",
"installed_app_other_description": "Diğer kategorilerdeki tüm yüklü uygulamalarınız.",
"installed_app_automation_description": "Hangi otomasyon uygulamalarının kullanılacağını ayarlayın",
"installed_app_automation_description": "Kullanılacak otomasyon uygulamalarını yapılandırın",
"analytics": "Analizler",
"empty_installed_apps_headline": "Yüklü uygulama yok",
"empty_installed_apps_description": "Uygulamalar, iş akışınızı iyileştirmenize ve planlama sürecinizi önemli ölçüde geliştirmenize olanak tanır.",
"empty_installed_apps_button": "Uygulama Mağazasını Keşfedin",
"empty_installed_apps_button": "Uygulama Mağazasını Keşfedin ya da aşağıdaki uygulamaları Yükleyin",
"manage_your_connected_apps": "Yüklü uygulamalarınızı yönetin veya ayarları değiştirin",
"browse_apps": "Uygulamalara göz at",
"features": "Özellikler",
@ -1008,7 +1008,6 @@
"app_removed_successfully": "Uygulama başarıyla kaldırıldı",
"error_removing_app": "Uygulama kaldırılırken bir hata oluştu",
"web_conference": "Web konferansı",
"number_for_sms_reminders": "Telefon numarası (SMS hatırlatmaları için)",
"requires_confirmation": "Onay gerekli",
"nr_event_type_one": "{{count}} etkinlik türü",
"nr_event_type_other": "{{count}} etkinlik türü",
@ -1165,8 +1164,8 @@
"connect_conference_apps": "Konferans uygulamalarını bağlayın",
"connect_calendar_apps": "Takvim uygulamalarını bağlayın",
"connect_payment_apps": "Ödeme uygulamalarını bağlayın",
"connect_automation_apps": "Otomasyon uygulamalarını bağlayın",
"connect_analytics_apps": "Analiz uygulamalarını bağlayın",
"connect_automation_apps": "Otomasyon uygulamaları bağlayın",
"connect_analytics_apps": "Analiz uygulamaları bağlayın",
"connect_other_apps": "Diğer uygulamaları bağlayın",
"current_step_of_total": "Adım {{currentStep}} / {{maxSteps}}",
"add_variable": "Değişken ekle",
@ -1241,7 +1240,7 @@
"webhooks_description": "Cal.com'da bir etkinlik gerçekleştiğinde gerçek zamanlı olarak toplantı bilgilerini alın",
"api_keys_description": "Kendi hesabınıza erişmek için API anahtarları oluşturun",
"new_api_key": "Yeni API anahtarı",
"active": "Etkin",
"active": "etkin",
"api_key_updated": "API anahtarı adı güncellendi",
"api_key_update_failed": "API anahtarı adı güncellenirken bir hata oluştu",
"embeds_title": "HTML iframe yerleştirme",
@ -1277,38 +1276,38 @@
"manage_billing_description": "Faturalandırmayla ilgili her şeyi yönetin",
"billing_freeplan_title": "Şu anda ÜCRETSİZ plandasınız",
"billing_freeplan_description": "Ekip olarak daha iyi çalışıyoruz. Sıralı ve toplu etkinliklerle iş akışlarınızı genişletin ve gelişmiş yönlendirme formları oluşturun",
"billing_freeplan_cta": "Şimdi deneyin",
"billing_freeplan_cta": "Hemen deneyin",
"billing_manage_details_title": "Fatura bilgilerinizi görüntüleyin ve yönetin",
"billing_manage_details_description": "Fatura bilgilerinizi görüntüleyin ve düzenleyin, ayrıca aboneliğinizi iptal edin.",
"billing_portal": "Faturalandırma portalı",
"billing_help_title": "Başka bir şeye ihtiyacınız var mı?",
"billing_help_description": "Faturalandırma konusunda daha fazla yardıma ihtiyacınız olursa destek ekibimiz size yardımcı olmaya hazır.",
"billing_help_cta": "Destek ekibimize ulaşın",
"ignore_special_characters": "Ek Giriş Etiketinizdeki özel karakterleri yok sayın. Yalnızca harf ve sayı kullanın",
"ignore_special_characters": "Ek Girdi etiketinizdeki özel karakterleri yok sayın. Yalnızca harf ve sayı kullanın",
"retry": "Yeniden dene",
"fetching_calendars_error": "Takvimleriniz alınırken bir sorun oluştu. Lütfen <1>tekrar deneyin</1> veya müşteri desteği ile iletişme geçin.",
"fetching_calendars_error": "Takvimleriniz alınırken bir sorun oluştu. Lütfen <1>tekrar deneyin</1> veya müşteri destek ekibiyle iletişme geçin.",
"calendar_connection_fail": "Takvim bağlantısı yapılamadı",
"booking_confirmation_success": "Rezervasyon onayı başarılı",
"booking_confirmation_fail": "Rezervasyon onayı yapılamadı",
"we_wont_show_again": "Bunu tekrar göstermeyeceğiz",
"couldnt_update_timezone": "Saat dilimini güncelleyemiyoruz",
"couldnt_update_timezone": "Saat dilimini güncelleyemedik",
"updated_timezone_to": "Saat dilimi {{formattedCurrentTz}} olarak güncellendi",
"update_timezone": "Saat dilimini güncelle",
"update_timezone_question": "Saat Dilimi güncellensin mi?",
"update_timezone_description": "Görünüşe göre yerel saat diliminiz {{formattedCurrentTz}} olarak değiştirildi. İstenmeyen zamanlarda rezervasyon yapılmasını önlemek için doğru saat dilimini ayarlamak çok önemlidir. Saat dilimini güncellemek istiyor musunuz?",
"update_timezone_description": "Görünüşe göre yerel saat diliminiz {{formattedCurrentTz}} olarak değiştirilmiş. İstenmeyen zamanlarda rezervasyon yapılmasını önlemek için doğru saat dilimini ayarlamak çok önemlidir. Saat dilimini güncellemek ister misiniz?",
"dont_update": "Güncelleme",
"require_additional_notes": "Ek notlar isteyin",
"require_additional_notes_description": "Rezervasyon sırasında doldurulacak ek notlar isteyin",
"require_additional_notes": "Ek not isteyin",
"require_additional_notes_description": "Rezervasyon sırasında doldurulacak ek not isteyin",
"email_address_action": "belirli bir e-posta adresine e-posta gönder",
"after_event_trigger": "etkinlik sona erdikten sonra",
"how_long_after": "Etkinlik sona erdikten ne kadar sonra?",
"no_available_slots": "Uygun yer yok",
"time_available": "Uygun zaman",
"install_new_calendar_app": "Yeni takvim uygulamasını yükleyin",
"make_phone_number_required": "Rezervasyon etkinliği için telefon numarasını zorunlu hale getirin",
"make_phone_number_required": "Rezervasyon etkinliği için telefon numarasını gerekli hale getirin",
"dont_have_permission": "Bu kaynağa erişim izniniz yok.",
"saml_config": "SAML Yapılandırması",
"saml_description": "Ekip üyelerinin bir Kimlik Sağlayıcıyı kullanarak oturum açmalarına izin verin",
"saml_config": "Çoklu Oturum Açma",
"saml_description": "Ekip üyelerinin bir Kimlik Sağlayıcıyı kullanarak giriş yapmalarına izin verin",
"saml_config_deleted_successfully": "SAML yapılandırması başarıyla silindi",
"saml_config_updated_successfully": "SAML yapılandırması başarıyla güncellendi",
"saml_configuration": "SAML yapılandırması",
@ -1319,11 +1318,11 @@
"saml_configuration_description": "SAML yapılandırmanızı güncellemek için lütfen Kimlik Sağlayıcınızın gönderdiği SAML meta verilerini aşağıdaki metin kutusuna yapıştırın.",
"saml_configuration_placeholder": "Lütfen Kimlik Sağlayıcınızdan gelen SAML meta verilerini buraya yapıştırın",
"saml_email_required": "SAML Kimlik Sağlayıcınızı bulabilmemiz için lütfen bir e-posta girin",
"saml_sp_title": "Hizmet Sağlayıcı Ayrıntıları",
"saml_sp_title": "Hizmet Sağlayıcı Bilgileri",
"saml_sp_description": "Kimlik Sağlayıcınız (IdP), SAML uygulama yapılandırmasını tamamlamak için sizden aşağıdaki bilgileri isteyecektir.",
"saml_sp_acs_url": "ACS URL'si",
"saml_sp_entity_id": "SP Kayıt Kimliği",
"saml_sp_entity_id": "SP Varlık Kimliği",
"saml_sp_acs_url_copied": "ACS URL'si kopyalandı!",
"saml_sp_entity_id_copied": "SP Kayıt Kimliği kopyalandı!",
"saml_sp_entity_id_copied": "SP Varlık Kimliği kopyalandı!",
"saml_btn_configure": "Yapılandır"
}

View File

@ -767,19 +767,19 @@
"no_category_apps_description_calendar": "Додайте додаток для календаря, щоб перевіряти, чи немає конфліктів у розкладі, і уникати подвійних бронювань",
"no_category_apps_description_conferencing": "Спробуйте додати додаток для конференцій, щоб інтегрувати можливість відеорозмов зі своїми клієнтами",
"no_category_apps_description_payment": "Додайте платіжний додаток, щоб спростити трансакції між вами й вашими клієнтами",
"no_category_apps_description_analytics": "Включіть аналітичний додаток для своїх сторінок бронювання",
"no_category_apps_description_automation": "Включіть додаток для автоматизації",
"no_category_apps_description_analytics": "Включити аналітичний додаток на свої сторінки бронювання",
"no_category_apps_description_automation": "Включити додаток для автоматизації",
"no_category_apps_description_other": "Додавайте інші додатки з різноманітними функціями",
"installed_app_calendar_description": "Налаштуйте календар, щоб перевіряти, чи немає конфліктів у розкладі, і уникати подвійних бронювань.",
"installed_app_conferencing_description": "Додавайте улюблені додатки для відеоконференцій, щоб проводити наради",
"installed_app_payment_description": "Укажіть, які служби обробки платежів використовувати, коли клієнти вам платять.",
"installed_app_analytics_description": "Укажіть, які аналітичні додатки використовувати на сторінках бронювання",
"installed_app_analytics_description": "Вибрати аналітичні додатки для своїх сторінок бронювання",
"installed_app_other_description": "Усі ваші встановлені додатки з інших категорій.",
"installed_app_automation_description": "Укажіть, які додатки для автоматизації використовувати",
"installed_app_automation_description": "Вибрати додатки для автоматизації",
"analytics": "Аналітика",
"empty_installed_apps_headline": "Не встановлено жодних додатків",
"empty_installed_apps_description": "Додатки дають змогу оптимізувати робочий процес і спростити роботу з графіком.",
"empty_installed_apps_button": "Переглянути магазин додатків",
"empty_installed_apps_button": "Переглянути магазин додатків або встановити наведені нижче додатки",
"manage_your_connected_apps": "Керуйте встановленими додатками й налаштуваннями",
"browse_apps": "Огляд додатків",
"features": "Функції",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "Додаток вилучено",
"error_removing_app": "Не вдалося вилучити додаток",
"web_conference": "Вебконференція",
"number_for_sms_reminders": "Номер телефону (для SMS-нагадувань)",
"requires_confirmation": "Потрібне підтвердження",
"nr_event_type_one": "{{count}} тип заходу",
"nr_event_type_other": "Типів заходів: {{count}}",
@ -1240,7 +1239,7 @@
"webhooks_description": "Отримуйте дані про наради в реальному часі, коли в Cal.com щось відбувається",
"api_keys_description": "Створюйте ключі API, щоб отримувати доступ до свого облікового запису",
"new_api_key": "Новий ключ API",
"active": "Активний",
"active": "активний",
"api_key_updated": "Назву ключа API оновлено",
"api_key_update_failed": "Не вдалось оновити назву ключа API",
"embeds_title": "Вставка iframe HTML",
@ -1264,7 +1263,7 @@
"every_app_published": "Усі додатки, опубліковані в магазині Cal.com, мають відкритий код і ретельно перевіряються учасниками спільноти. Утім Cal.com, Inc. рекомендує та сертифікує лише додатки, опубліковані компанією Cal.com. Якщо ви натрапили на неприйнятний вміст або поведінку, надішліть нам скаргу.",
"report_app": "Поскаржитися на додаток",
"limit_booking_frequency": "Обмежити кількість бронювань",
"limit_booking_frequency_description": "Укажіть, яку максимальну кількість разів можна забронювати цей захід",
"limit_booking_frequency_description": "Указати максимальну кількість бронювань цього заходу",
"add_limit": "Додати ліміт",
"team_name_required": "Назва команди обов’язкова",
"show_attendees": "Поширювати інформацією про учасників серед гостей",
@ -1272,41 +1271,41 @@
"format": "Формат",
"uppercase_for_letters": "Використовувати всі великі літери",
"replace_whitespaces_underscores": "Заміняти пробіли підкресленнями",
"manage_billing": "Керування виставленням рахунків",
"manage_billing_description": "Керування всіма аспектами виставлення рахунків",
"manage_billing": "Керувати рахунками",
"manage_billing_description": "Керувати всіма моментами виставлення рахунків",
"billing_freeplan_title": "Зараз ви використовуєте БЕЗКОШТОВНИЙ план",
"billing_freeplan_description": "Ми краще працюємо в команді. Розширте свої робочі процеси за допомогою ротацій і колективних заходів та створюйте складніші форми переспрямовування",
"billing_freeplan_cta": "Спробуйте зараз",
"billing_freeplan_description": "Краще працювати в команді. Доповніть свої робочі процеси циклічними й колективними заходами та створюйте складніші форми переспрямовування.",
"billing_freeplan_cta": "Спробувати",
"billing_manage_details_title": "Переглядайте свої дані для виставлення рахунків і керуйте ними",
"billing_manage_details_description": "Тут ви можете переглянути й відредагувати свої дані для виставлення рахунків, а також скасувати підписку.",
"billing_portal": "Портал виставлення рахунків",
"billing_help_title": "Потрібно щось інше?",
"billing_help_description": "Якщо вам потрібна додаткова допомога з виставленням рахунків, наша служба підтримки готова її надати.",
"billing_help_cta": "Служба підтримки",
"ignore_special_characters": "Пропускати спеціальні символи в мітці додаткового поля. Використовувати лише літери та цифри",
"retry": "Повторити спробу",
"billing_help_cta": "Звернутися в службу підтримки",
"ignore_special_characters": "Пропускати спеціальні символи в підписі додаткового поля. Використовувати лише літери та цифри.",
"retry": "Повторити",
"fetching_calendars_error": "Під час отримання календарів виникла проблема. <1>Повторіть спробу</1> або зверніться в службу підтримки.",
"calendar_connection_fail": "Не вдалося підключитися до календаря",
"booking_confirmation_success": "Бронювання підтверджено",
"booking_confirmation_fail": "Не вдалося підтвердити бронювання",
"we_wont_show_again": "Ми більше не показуватимемо це повідомлення",
"couldnt_update_timezone": "Не вдалось оновити відомості про часовий пояс",
"updated_timezone_to": "Часовий пояс змінено на {{formattedCurrentTz}}",
"we_wont_show_again": "Ми більше не показуватимемо це",
"couldnt_update_timezone": "Не вдалось оновити часовий пояс",
"updated_timezone_to": "Часовий пояс оновлено на {{formattedCurrentTz}}",
"update_timezone": "Оновити часовий пояс",
"update_timezone_question": "Оновити часовий пояс?",
"update_timezone_description": "Схоже, ваш місцевий часовий пояс змінено на {{formattedCurrentTz}}. Щоб бронювання не випадали на небажаний час, дуже важливо вказати правильний часовий пояс. Змінити ці відомості?",
"dont_update": "Не змінювати",
"require_additional_notes": "Вимагати додаткових нотаток",
"require_additional_notes_description": "Під час бронювання потрібно обов’язково ввести додаткові нотатки",
"email_address_action": "надсилати електронний лист на певну адресу",
"update_timezone_description": "Схоже, ваш місцевий часовий пояс змінився на {{formattedCurrentTz}}. Щоб бронювання не випадали на небажаний час, дуже важливо вказати правильний часовий пояс. Оновити часовий пояс?",
"dont_update": "Не оновлювати",
"require_additional_notes": "Вимагати додаткові примітки",
"require_additional_notes_description": "Вимагати введення додаткових приміток під час бронювання",
"email_address_action": "надсилати електронний лист на вказану адресу",
"after_event_trigger": "після завершення заходу",
"how_long_after": "За який час після завершення заходу?",
"how_long_after": "Через який час після завершення заходу?",
"no_available_slots": "Немає доступних вікон",
"time_available": "Доступний час",
"install_new_calendar_app": "Установити новий додаток календаря",
"make_phone_number_required": "Зробити введення телефонного номера обов’язковим для бронювання заходу",
"dont_have_permission": "У вас немає дозволів на доступ до цього ресурсу.",
"saml_config": "Конфігурація SAML",
"make_phone_number_required": "Вимагати введення номера телефону під час бронювання заходу",
"dont_have_permission": "У вас немає дозволу на доступ до цього ресурсу.",
"saml_config": "Єдиний вхід",
"saml_description": "Дозволити учасникам команди входити за допомогою постачальника посвідчень",
"saml_config_deleted_successfully": "Конфігурацію SAML видалено",
"saml_config_updated_successfully": "Конфігурацію SAML оновлено",
@ -1319,10 +1318,10 @@
"saml_configuration_placeholder": "Вставте тут метадані SAML від свого постачальника посвідчень",
"saml_email_required": "Введіть електронну адресу, щоб ми могли знайти вашого постачальника посвідчень SAML",
"saml_sp_title": "Дані постачальника послуг",
"saml_sp_description": "Ваш постачальник посвідчень попросить вас надати наведені далі відомості, щоб завершити налаштування додатку SAML.",
"saml_sp_description": "Ваш постачальник посвідчень (IdP) попросить вас надати наведені нижче дані, щоб завершити налаштування додатка SAML.",
"saml_sp_acs_url": "URL-адреса ACS",
"saml_sp_entity_id": "Ідентифікатор сутності постачальника послуг",
"saml_sp_entity_id": "ID сутності SP",
"saml_sp_acs_url_copied": "URL-адресу ACS скопійовано!",
"saml_sp_entity_id_copied": "Ідентифікатор сутності постачальника послуг скопійовано!",
"saml_sp_entity_id_copied": "ID сутності SP скопійовано!",
"saml_btn_configure": "Налаштувати"
}

View File

@ -773,13 +773,13 @@
"installed_app_calendar_description": "Đặt (các) lịch làm nhiệm vụ kiểm tra xung đột nhằm ngăn tình trạng đặt lịch trùng.",
"installed_app_conferencing_description": "Thêm ứng dụng hội nghị video mà bạn yêu thích để phục vụ cho các cuộc họp",
"installed_app_payment_description": "Cấu hình loại dịch vụ xử lí thanh toán nào sẽ được dùng đến khi thu phí khách hàng.",
"installed_app_analytics_description": "Cấu hình ứng dụng phân tích nào cần dùng cho những trang lịch hẹn",
"installed_app_analytics_description": "Cấu hình ứng dụng phân tích nào để sử dụng cho những trang lịch hẹn của bạn",
"installed_app_other_description": "Tất cả những ứng dụng bạn đã cài từ những danh mục khác.",
"installed_app_automation_description": "Cấu hình ứng dụng tự động hoá nên dùng",
"analytics": "Công cụ phân tích",
"analytics": "Phân tích",
"empty_installed_apps_headline": "Chưa cài ứng dụng nào",
"empty_installed_apps_description": "Ứng dụng cho phép bạn cải thiện tiến độ công việc và cải thiện đáng kể việc sắp xếp tổ chức.",
"empty_installed_apps_button": "Khám phá App Store",
"empty_installed_apps_button": "Khám phá App Store hoặc Cài đặt các ứng dụng dưới đây",
"manage_your_connected_apps": "Quản lý các ứng dụng đã cài đặt của bạn hoặc thay đổi cài đặt",
"browse_apps": "Xem ứng dụng",
"features": "Tính năng",
@ -829,7 +829,7 @@
"time_format": "Định dạng thời gian",
"12_hour": "12 tiếng",
"24_hour": "24 tiếng",
"24_hour_short": "24h",
"24_hour_short": "24 giờ",
"redirect_success_booking": "Chuyển hướng khi đặt lịch hẹn ",
"you_are_being_redirected": "Bạn sẽ được chuyển hướng đến {{ url }} sau $t(second, {\"count\": {{seconds}} }).",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "Ứng dụng được gỡ bỏ thành công",
"error_removing_app": "Lỗi khi gỡ bỏ ứng dụng",
"web_conference": "Hội nghị web",
"number_for_sms_reminders": "Số điện thoại (để gửi lời nhắc SMS)",
"requires_confirmation": "Yêu cầu xác nhận",
"nr_event_type_one": "{{count}} loại sự kiện",
"nr_event_type_other": "{{count}} loại sự kiện",
@ -1240,7 +1239,7 @@
"webhooks_description": "Nhận dữ liệu cuộc họp theo thời gian thực khi có hoạt động diễn ra ở Cal.com",
"api_keys_description": "Tạo khóa API để dùng cho việc truy cập tài khoản chính mình",
"new_api_key": "Khoá API mới",
"active": "Đang hoạt động",
"active": "đang hoạt động",
"api_key_updated": "Đã cập nhật tên của khoá API",
"api_key_update_failed": "Lỗi khi cập nhật tên của khoá API",
"embeds_title": "Nhúng iframe HTML",
@ -1267,7 +1266,7 @@
"limit_booking_frequency_description": "Giới hạn số lần có thể đặt lịch hẹn cho sự kiện này",
"add_limit": "Thêm giới hạn",
"team_name_required": "Cần có tên nhóm",
"show_attendees": "Chia sẻ thông tin người tham gia cho khách biết",
"show_attendees": "Chia sẻ thông tin người tham gia cho khách",
"how_additional_inputs_as_variables": "Cách sử dụng Đầu vào bổ sung làm Tham số",
"format": "Định dạng",
"uppercase_for_letters": "In hoa hết tất cả các chữ cái",
@ -1288,7 +1287,7 @@
"fetching_calendars_error": "Có vấn đề khi tìm dữ liệu của bạn. Vui lòng <1>thử lại</1> hoặc liên hệ với bộ phận hỗ trợ khách hàng.",
"calendar_connection_fail": "Kết nối lịch bất thành",
"booking_confirmation_success": "Xác nhận lịch hẹn thành công",
"booking_confirmation_fail": "Xác nhận lịch hẹn bất thành",
"booking_confirmation_fail": "Xác nhận lịch hẹn không thành công",
"we_wont_show_again": "Chúng tôi sẽ không hiển thị cái này nữa",
"couldnt_update_timezone": "Chúng tôi không thể cập nhật múi giờ",
"updated_timezone_to": "Múi giờ được cập nhật sang {{formattedCurrentTz}}",
@ -1304,9 +1303,9 @@
"no_available_slots": "Không còn khoảng thời gian trống",
"time_available": "Thời gian trống",
"install_new_calendar_app": "Cài ứng dụng lịch mới",
"make_phone_number_required": "Đặt số điện thoại là điều kiện bắt buộc khi đặt lịch hẹn sự kiện",
"make_phone_number_required": "Yêu cầu điện thoại khi đặt lịch hẹn sự kiện",
"dont_have_permission": "Bạn không có quyền truy cập tài nguyên này.",
"saml_config": "Cấu hình SAML",
"saml_config": "Cấu hình Đăng nhập một lần",
"saml_description": "Cho phép thành viên nhóm đăng nhập bằng một Nhà cung cấp danh tính",
"saml_config_deleted_successfully": "Đã xóa cấu hình SAML thành công",
"saml_config_updated_successfully": "Đã cập nhật cấu hình SAML thành công",
@ -1322,7 +1321,7 @@
"saml_sp_description": "Nhà cung cấp danh tính (IdP) của bạn sẽ hỏi bạn một số chi tiết sau để hoàn thành việc cấu hình ứng dụng SAML.",
"saml_sp_acs_url": "URL ACS",
"saml_sp_entity_id": "ID của thực thể SP",
"saml_sp_acs_url_copied": "URL ACS đã sao chép!",
"saml_sp_entity_id_copied": "ID của thực thể SP đã sao chép!",
"saml_sp_acs_url_copied": "URL ACS đã được sao chép!",
"saml_sp_entity_id_copied": "ID của thực thể SP đã được sao chép!",
"saml_btn_configure": "Cấu hình"
}

View File

@ -779,7 +779,7 @@
"analytics": "分析",
"empty_installed_apps_headline": "未安装任何应用",
"empty_installed_apps_description": "通过应用可以显著增强工作流程和改善日程安排生活。",
"empty_installed_apps_button": "浏览 App Store",
"empty_installed_apps_button": "浏览 App Store 或从以下应用中选择安装",
"manage_your_connected_apps": "管理已安装的应用或更改设置",
"browse_apps": "浏览应用",
"features": "功能",
@ -829,11 +829,11 @@
"time_format": "时间格式",
"12_hour": "12 小时",
"24_hour": "24 小时",
"24_hour_short": "24 小时",
"24_hour_short": "24 小时",
"redirect_success_booking": "预约时重定向",
"you_are_being_redirected": "您将在 $t(second, {\"count\": {{seconds}} }) 秒后重定向到 {{ url }}。",
"external_redirect_url": "https://example.com/redirect-to-my-success-page",
"redirect_url_description": "成功预约后重定向到自定义 URL",
"redirect_url_description": "成功预约后重定向到自定义链接",
"duplicate": "复制",
"offer_seats": "提供位置",
"offer_seats_description": "提供位置以供预约 (这将自动禁止访客和选择加入预约)。",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "应用已成功删除",
"error_removing_app": "删除应用时出错",
"web_conference": "网络会议",
"number_for_sms_reminders": "电话号码(用于短信提醒)",
"requires_confirmation": "需要确认",
"nr_event_type_one": "{{count}} 种活动类型",
"nr_event_type_other": "{{count}} 种活动类型",
@ -1275,7 +1274,7 @@
"manage_billing": "管理计费",
"manage_billing_description": "管理所有计费事项",
"billing_freeplan_title": "您目前正在使用免费计划",
"billing_freeplan_description": "我们在团队中可以更好地工作。通过轮流模式和集体活动扩展工作流程,并制作高级途径表格",
"billing_freeplan_description": "团队合作效率高。通过轮流模式和集体活动可拓展工作流程,并可制作高级途径表格",
"billing_freeplan_cta": "立即试用",
"billing_manage_details_title": "查看和管理您的账单详情",
"billing_manage_details_description": "查看和编辑您的账单详情,以及取消您的订阅。",
@ -1291,25 +1290,25 @@
"booking_confirmation_fail": "预约确认失败",
"we_wont_show_again": "我们不会再显示此内容",
"couldnt_update_timezone": "我们无法更新时区",
"updated_timezone_to": "将时区更新为 {{formattedCurrentTz}}",
"updated_timezone_to": "将时区更新为 {{formattedCurrentTz}}",
"update_timezone": "更新时区",
"update_timezone_question": "更新时区?",
"update_timezone_description": "似乎您的本地时区已更改为 {{formattedCurrentTz}}。设置正确的时区以防止时间不当的预约非常重要。您是否要更新?",
"update_timezone_description": "您的本地时区似乎已更改为 {{formattedCurrentTz}}。请务必设置正确的时区以防止预约不当的时间。是否要更新?",
"dont_update": "不更新",
"require_additional_notes": "需要附加备注",
"require_additional_notes_description": "预约时需要填写附加备注",
"email_address_action": "发送电子邮件到指定的电子邮件地址",
"email_address_action": "将电子邮件发送到指定的电子邮件地址",
"after_event_trigger": "活动结束后",
"how_long_after": "活动结束后多久?",
"no_available_slots": "没有可用的时间段",
"time_available": "可用时间",
"install_new_calendar_app": "安装新的日历应用",
"make_phone_number_required": "使电话号码成为预约活动时的必需项",
"make_phone_number_required": "预约活动时要求提供电话号码",
"dont_have_permission": "您无权访问此资源。",
"saml_config": "SAML 配置",
"saml_config": "单点登录",
"saml_description": "允许团队成员使用身份提供程序登录",
"saml_config_deleted_successfully": "SAML 配置删除成功",
"saml_config_updated_successfully": "SAML 配置更新成功",
"saml_config_updated_successfully": "SAML 配置已成功更新",
"saml_configuration": "SAML 配置",
"delete_saml_configuration": "删除 SAML 配置",
"delete_saml_configuration_confirmation_message": "您确定要删除 SAML 配置吗您使用SAML 登录的团队成员将无法再访问 Cal.com。",
@ -1320,9 +1319,9 @@
"saml_email_required": "请输入您的邮箱,以便我们找到您的 SAML 身份提供商",
"saml_sp_title": "服务提供商详细信息",
"saml_sp_description": "您的身份提供程序 (IdP) 将要求您提供以下详细信息以完成 SAML 应用程序配置。",
"saml_sp_acs_url": "ACS URL",
"saml_sp_acs_url": "ACS 链接",
"saml_sp_entity_id": "SP 实体 ID",
"saml_sp_acs_url_copied": "ACS URL 已复制!",
"saml_sp_acs_url_copied": "ACS 链接已复制!",
"saml_sp_entity_id_copied": "SP 实体 ID 已复制!",
"saml_btn_configure": "配置"
}

View File

@ -779,7 +779,7 @@
"analytics": "分析",
"empty_installed_apps_headline": "沒有已安裝的應用程式",
"empty_installed_apps_description": "應用程式使您能夠增強您的工作流程並顯著改善您的日程安排。",
"empty_installed_apps_button": "探索應用商店",
"empty_installed_apps_button": "探索 App Store 或從下列應用程式中選擇安裝",
"manage_your_connected_apps": "管理已安裝的應用程式或是變更設定",
"browse_apps": "瀏覽應用程式",
"features": "功能",
@ -1007,7 +1007,6 @@
"app_removed_successfully": "已成功移除應用程式",
"error_removing_app": "移除應用程式時發生錯誤",
"web_conference": "網路會議",
"number_for_sms_reminders": "電話號碼 (用於簡訊提醒)",
"requires_confirmation": "需要確認",
"nr_event_type_one": "{{count}} 種活動類型",
"nr_event_type_other": "{{count}} 種活動類型",
@ -1164,8 +1163,8 @@
"connect_conference_apps": "連接會議應用程式",
"connect_calendar_apps": "連接行事曆應用程式",
"connect_payment_apps": "連接付款應用程式",
"connect_automation_apps": "連自動化應用程式",
"connect_analytics_apps": "連分析應用程式",
"connect_automation_apps": "連自動化應用程式",
"connect_analytics_apps": "連分析應用程式",
"connect_other_apps": "連接其他應用程式",
"current_step_of_total": "第 {{currentStep}} 步,共 {{maxSteps}} 步",
"add_variable": "新增變數",
@ -1240,7 +1239,7 @@
"webhooks_description": "當 Cal.com 上有活動進行時,即時收到會議資料",
"api_keys_description": "產生用於存取您帳號的 API 金鑰",
"new_api_key": "新 API 金鑰",
"active": "使用中",
"active": "用中",
"api_key_updated": "API 金鑰名稱已更新",
"api_key_update_failed": "更新 API 金鑰名稱時發生錯誤",
"embeds_title": "已嵌入 HTML iframe",
@ -1272,33 +1271,33 @@
"format": "格式",
"uppercase_for_letters": "一律使用大寫字母",
"replace_whitespaces_underscores": "以底線取代空格",
"manage_billing": "管理帳單",
"manage_billing_description": "管理全部帳單",
"manage_billing": "管理付費",
"manage_billing_description": "管理所有付費事項",
"billing_freeplan_title": "您目前使用的是免費方案",
"billing_freeplan_description": "團隊合作效率佳。使用循環制和集體活動來擴您的工作流程,並製作進階版引導表單",
"billing_freeplan_description": "團隊合作效率佳。使用循環制和集體活動來擴您的工作流程,並製作進階版引導表單",
"billing_freeplan_cta": "立即試用",
"billing_manage_details_title": "檢視與管理付費細節",
"billing_manage_details_description": "檢視與編輯付費細節,以及取消訂閱。",
"billing_portal": "帳單入口網站",
"billing_portal": "付費入口網站",
"billing_help_title": "還需要什麼嗎?",
"billing_help_description": "若您需要關於付費的進一步協助,我們的支援團隊會隨時幫助您。",
"billing_help_cta": "聯絡支援",
"ignore_special_characters": "忽略額外輸入標籤中的所有特殊字元。只使用字母和數字",
"retry": "重試",
"fetching_calendars_error": "擷取行事曆時發生問題。請<1>再試一次</1>,或聯絡客服支援團隊。",
"calendar_connection_fail": "行事曆連失敗",
"calendar_connection_fail": "行事曆連失敗",
"booking_confirmation_success": "預約確認成功",
"booking_confirmation_fail": "預約確認失敗",
"we_wont_show_again": "我們不會再顯示此訊息",
"couldnt_update_timezone": "我們無法更新時區",
"updated_timezone_to": "已將時區更新為{{formattedCurrentTz}}",
"update_timezone": "更新時區",
"update_timezone_question": "更新時區?",
"update_timezone_question": "是否要更新時區?",
"update_timezone_description": "您的當地時區似乎已變更為{{formattedCurrentTz}}。請務必設定正確的時區,才能避免預約錯誤的時間。您要更新時區嗎?",
"dont_update": "不要更新",
"require_additional_notes": "需要額外備註",
"require_additional_notes_description": "預約時需填寫額外備註",
"email_address_action": "傳送電子郵件給特定的電子郵件地址",
"email_address_action": "寄電子郵件至特定的電子郵件地址",
"after_event_trigger": "活動結束過後",
"how_long_after": "活動結束過後多久?",
"no_available_slots": "沒有可用的時段",
@ -1318,11 +1317,11 @@
"saml_configuration_description": "如果要更新 SAML 設定,請從 Identity Provider 貼 SAML Metadata 到以下文字框。",
"saml_configuration_placeholder": "請把 Identity Provider 的 SAML Metadata 貼到這裡。",
"saml_email_required": "請輸入讓我們可以找到 SAML Identity Provider 的電子郵件",
"saml_sp_title": "服務供應方詳",
"saml_sp_title": "服務供應方詳細資訊",
"saml_sp_description": "您的 Identity Provider (IdP) 會要求您提供下列詳細資料,以完成 SAML 應用程式設定。",
"saml_sp_acs_url": "ACS 網址",
"saml_sp_entity_id": "SP 實體 ID",
"saml_sp_acs_url_copied": "ACS 網址已複製",
"saml_sp_acs_url_copied": "已複製ACS 網址!",
"saml_sp_entity_id_copied": "已複製 SP 實體 ID",
"saml_btn_configure": "設定"
}

View File

@ -6,7 +6,5 @@ items:
- /api/app-store/_APP_DIR_/3.jpg
---
<Slider items={items} />
{description}

View File

@ -3,7 +3,7 @@ import Link from "next/link";
import { inferQueryOutput } from "@calcom/trpc/react";
import { Switch } from "@calcom/ui/v2";
import OmniInstallAppButton from "@calcom/web/components/v2/apps/OmniInstallAppButton";
import OmniInstallAppButton from "@calcom/web/components/apps/OmniInstallAppButton";
import { SetAppDataGeneric } from "../EventTypeAppContext";
import { eventTypeAppCardZod } from "../eventTypeAppCardZod";
@ -26,8 +26,8 @@ export default function AppCard({
const [animationRef] = useAutoAnimate<HTMLDivElement>();
return (
<div ref={animationRef} className="mb-4 mt-2 rounded-md border border-gray-200 p-8 text-sm">
<div className="flex w-full">
<div ref={animationRef} className="mb-4 mt-2 rounded-md border border-gray-200 p-4 text-sm sm:p-8">
<div className="flex w-full flex-col gap-2 sm:flex-row sm:gap-0">
{/* Don't know why but w-[42px] isn't working, started happening when I started using next/dynamic */}
<Link href={"/apps/" + app.slug}>
<a className="mr-3 h-auto w-10 rounded-sm">

View File

@ -1,20 +0,0 @@
import { GetStaticPropsContext } from "next";
export const AppSetupPageMap = {
zapier: import("../../../zapier/pages/setup/_getStaticProps"),
};
export const getStaticProps = async (ctx: GetStaticPropsContext) => {
const { slug } = ctx.params || {};
if (typeof slug !== "string") return { notFound: true } as const;
if (!(slug in AppSetupPageMap)) return { props: {} };
const page = await AppSetupPageMap[slug as keyof typeof AppSetupPageMap];
if (!page.getStaticProps) return { props: {} };
const props = await page.getStaticProps(ctx);
return props;
};

View File

@ -1,19 +0,0 @@
import dynamic from "next/dynamic";
import { DynamicComponent } from "../../../_components/DynamicComponent";
export const AppSetupMap = {
"apple-calendar-V2": dynamic(() => import("../../../applecalendar/pages/v2/setup")),
"exchange-V2": dynamic(() => import("../../../exchangecalendar/pages/v2/setup")),
"exchange2013-calendar-V2": dynamic(() => import("../../../exchange2013calendar/pages/v2/setup")),
"exchange2016-calendar-V2": dynamic(() => import("../../../exchange2016calendar/pages/v2/setup")),
"caldav-calendar-V2": dynamic(() => import("../../../caldavcalendar/pages/v2/setup")),
"zapier-V2": dynamic(() => import("../../../zapier/pages/v2/setup")),
"closecom-V2": dynamic(() => import("../../../closecomothercalendar/pages/v2/setup")),
};
export const AppSetupPage = (props: { slug: string }) => {
return <DynamicComponent<typeof AppSetupMap> componentMap={AppSetupMap} {...props} />;
};
export default AppSetupPage;

View File

@ -1,13 +1,12 @@
import { Credential } from "@prisma/client";
import logger from "@calcom/lib/logger";
import type { Calendar } from "@calcom/types/Calendar";
import { CredentialPayload } from "@calcom/types/Credential";
import appStore from "..";
const log = logger.getChildLogger({ prefix: ["CalendarManager"] });
export const getCalendar = (credential: Credential | null): Calendar | null => {
export const getCalendar = (credential: CredentialPayload | null): Calendar | null => {
if (!credential || !credential.key) return null;
const { type: calendarType } = credential;
const calendarApp = appStore[calendarType.split("_").join("") as keyof typeof appStore];

View File

@ -3,6 +3,4 @@ items:
- /api/app-store/applecalendar/1.jpg
---
<Slider items={items} />
Apple calendar runs both the macOS and iOS mobile operating systems. Offering online cloud backup of calendars using Apples iCloud service, it can sync with Google Calendar and Microsoft Exchange Server. Users can schedule events in their day that include time, location, duration, and extra notes.

View File

@ -1,9 +1,8 @@
import { Credential } from "@prisma/client";
import CalendarService from "@calcom/lib/CalendarService";
import { CredentialPayload } from "@calcom/types/Credential";
export default class AppleCalendarService extends CalendarService {
constructor(credential: Credential) {
constructor(credential: CredentialPayload) {
super(credential, "apple_calendar", "https://caldav.icloud.com");
}
}

View File

@ -4,9 +4,7 @@ import { useForm } from "react-hook-form";
import { Toaster } from "react-hot-toast";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Alert } from "@calcom/ui/Alert";
import Button from "@calcom/ui/Button";
import { Form, TextField } from "@calcom/ui/form/fields";
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
export default function AppleCalendarSetup() {
const { t } = useLocale();

View File

@ -1,101 +0,0 @@
import { useRouter } from "next/router";
import { useState } from "react";
import { useForm } from "react-hook-form";
import { Toaster } from "react-hot-toast";
import { useLocale } from "@calcom/lib/hooks/useLocale";
import { Alert, Button, Form, TextField } from "@calcom/ui/v2";
export default function AppleCalendarSetup() {
const { t } = useLocale();
const router = useRouter();
const form = useForm({
defaultValues: {
username: "",
password: "",
},
});
const [errorMessage, setErrorMessage] = useState("");
return (
<div className="flex h-screen bg-gray-200">
<div className="m-auto rounded bg-white p-5 md:w-[560px] md:p-10">
<div className="flex flex-col space-y-5 md:flex-row md:space-y-0 md:space-x-5">
<div>
{/* eslint-disable @next/next/no-img-element */}
<img
src="/api/app-store/applecalendar/icon.svg"
alt="Apple Calendar"
className="h-12 w-12 max-w-2xl"
/>
</div>
<div>
<h1 className="text-gray-600">{t("connect_apple_server")}</h1>
<div className="mt-1 text-sm">
{t("apple_server_generate_password")}{" "}
<a
className="text-indigo-400"
href="https://appleid.apple.com/account/manage"
target="_blank"
rel="noopener noreferrer">
https://appleid.apple.com/account/manage
</a>
. {t("credentials_stored_encrypted")}
</div>
<div className="my-2 mt-3">
<Form
form={form}
handleSubmit={async (values) => {
setErrorMessage("");
const res = await fetch("/api/integrations/applecalendar/add", {
method: "POST",
body: JSON.stringify(values),
headers: {
"Content-Type": "application/json",
},
});
const json = await res.json();
if (!res.ok) {
setErrorMessage(json?.message || t("something_went_wrong"));
} else {
router.push(json.url);
}
}}>
<fieldset className="space-y-2" disabled={form.formState.isSubmitting}>
<TextField
required
type="text"
{...form.register("username")}
label="Apple ID"
placeholder="appleid@domain.com"
/>
<TextField
required
type="password"
{...form.register("password")}
label={t("password")}
placeholder="•••••••••••••"
autoComplete="password"
/>
</fieldset>
{errorMessage && <Alert severity="error" title={errorMessage} className="my-4" />}
<div className="mt-5 justify-end space-x-2 sm:mt-4 sm:flex">
<Button type="button" color="secondary" onClick={() => router.back()}>
{t("cancel")}
</Button>
<Button type="submit" loading={form.formState.isSubmitting}>
{t("save")}
</Button>
</div>
</Form>
</div>
</div>
</div>
</div>
<Toaster position="bottom-right" />
</div>
);
}

View File

@ -30,6 +30,7 @@ import { metadata as qr_code_meta } from "./qr_code/_metadata";
import { metadata as rainbow_meta } from "./rainbow/_metadata";
import { metadata as raycast_meta } from "./raycast/_metadata";
import { metadata as riverside_meta } from "./riverside/_metadata";
import { metadata as sirius_video_meta } from "./sirius_video/_metadata";
import { metadata as slackmessaging_meta } from "./slackmessaging/_metadata";
import { metadata as stripepayment_meta } from "./stripepayment/_metadata";
import { metadata as tandemvideo_meta } from "./tandemvideo/_metadata";
@ -70,6 +71,7 @@ export const appStoreMetadata = {
rainbow: rainbow_meta,
raycast: raycast_meta,
riverside: riverside_meta,
sirius_video: sirius_video_meta,
slackmessaging: slackmessaging_meta,
stripepayment: stripepayment_meta,
tandemvideo: tandemvideo_meta,

View File

@ -29,6 +29,7 @@ export const apiHandlers = {
rainbow: import("./rainbow/api"),
raycast: import("./raycast/api"),
riverside: import("./riverside/api"),
sirius_video: import("./sirius_video/api"),
slackmessaging: import("./slackmessaging/api"),
stripepayment: import("./stripepayment/api"),
tandemvideo: import("./tandemvideo/api"),

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